From f96283903424f97cf6adf7a33ada6eb35bf1809e Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 9 Aug 2016 12:58:54 +0300 Subject: [PATCH 001/558] Rename package: org.jetbrains.plugin -> org.jetbrains.debugger.memory --- action/ShowClassesWithDiff.java | 22 + action/ShowClassesWithInstanceAction.java | 22 + action/ShowInstancesByClassAction.java | 57 +++ component/MemoryViewManager.java | 80 +++ component/MemoryViewManagerState.java | 14 + event/MemoryViewManagerListener.java | 11 + sizeof/RetainedSizeFetcher.java | 109 ++++ sizeof/SizeOfEvalStrategy.java | 21 + toolwindow/MemoryViewCondition.java | 45 ++ toolwindow/MemoryViewToolWindowFactory.java | 149 ++++++ utils/AbstractTableColumnDescriptor.java | 21 + utils/AbstractTableModelWithColumns.java | 37 ++ utils/DebugCommand.java | 9 + utils/InstanceJavaValue.java | 31 ++ utils/InstanceValueDescriptor.java | 46 ++ utils/KeyboardUtils.java | 18 + utils/NamesUtils.java | 10 + utils/SingleAlarmWithMutableDelay.java | 23 + view/ClassesFilteredView.java | 199 ++++++++ view/ClassesTable.java | 307 +++++++++++ view/InstancesWindow.java | 531 ++++++++++++++++++++ 21 files changed, 1762 insertions(+) create mode 100644 action/ShowClassesWithDiff.java create mode 100644 action/ShowClassesWithInstanceAction.java create mode 100644 action/ShowInstancesByClassAction.java create mode 100644 component/MemoryViewManager.java create mode 100644 component/MemoryViewManagerState.java create mode 100644 event/MemoryViewManagerListener.java create mode 100644 sizeof/RetainedSizeFetcher.java create mode 100644 sizeof/SizeOfEvalStrategy.java create mode 100644 toolwindow/MemoryViewCondition.java create mode 100644 toolwindow/MemoryViewToolWindowFactory.java create mode 100644 utils/AbstractTableColumnDescriptor.java create mode 100644 utils/AbstractTableModelWithColumns.java create mode 100644 utils/DebugCommand.java create mode 100644 utils/InstanceJavaValue.java create mode 100644 utils/InstanceValueDescriptor.java create mode 100644 utils/KeyboardUtils.java create mode 100644 utils/NamesUtils.java create mode 100644 utils/SingleAlarmWithMutableDelay.java create mode 100644 view/ClassesFilteredView.java create mode 100644 view/ClassesTable.java create mode 100644 view/InstancesWindow.java diff --git a/action/ShowClassesWithDiff.java b/action/ShowClassesWithDiff.java new file mode 100644 index 000000000000..78081dc76baf --- /dev/null +++ b/action/ShowClassesWithDiff.java @@ -0,0 +1,22 @@ +package org.jetbrains.debugger.memory.action; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.project.Project; +import org.jetbrains.debugger.memory.component.MemoryViewManager; + +public class ShowClassesWithDiff extends ToggleAction { + @Override + public boolean isSelected(AnActionEvent e) { + Project project = e.getProject(); + return project != null && MemoryViewManager.getInstance(e.getProject()).isNeedShowDiffOnly(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + Project project = e.getProject(); + if (project != null) { + MemoryViewManager.getInstance(project).setShowDiffOnly(state); + } + } +} diff --git a/action/ShowClassesWithInstanceAction.java b/action/ShowClassesWithInstanceAction.java new file mode 100644 index 000000000000..9e30ffda8230 --- /dev/null +++ b/action/ShowClassesWithInstanceAction.java @@ -0,0 +1,22 @@ +package org.jetbrains.debugger.memory.action; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.project.Project; +import org.jetbrains.debugger.memory.component.MemoryViewManager; + +public class ShowClassesWithInstanceAction extends ToggleAction { + @Override + public boolean isSelected(AnActionEvent e) { + Project project = e.getProject(); + return project != null && MemoryViewManager.getInstance(project).isNeedShowInstancesOnly(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + Project project = e.getProject(); + if (project != null) { + MemoryViewManager.getInstance(project).setShowWithInstancesOnly(state); + } + } +} diff --git a/action/ShowInstancesByClassAction.java b/action/ShowInstancesByClassAction.java new file mode 100644 index 000000000000..8a96fc9891d2 --- /dev/null +++ b/action/ShowInstancesByClassAction.java @@ -0,0 +1,57 @@ +package org.jetbrains.debugger.memory.action; + +import com.intellij.debugger.ui.impl.watch.NodeDescriptorProvider; +import com.intellij.debugger.ui.tree.NodeDescriptor; +import com.intellij.debugger.ui.tree.ValueDescriptor; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XDebuggerManager; +import com.intellij.xdebugger.frame.XValue; +import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase; +import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.Value; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.view.InstancesWindow; + +public class ShowInstancesByClassAction extends XDebuggerTreeActionBase { + @Override + protected boolean isEnabled(@NotNull XValueNodeImpl node, @NotNull AnActionEvent e) { + ObjectReference ref = getObjectReference(node); + if(ref != null) { + String text = String.format("Show %s Objects...", StringUtil.getShortName(ref.referenceType().name())); + e.getPresentation().setText(text); + } + + return ref != null; + } + + @Override + protected void perform(XValueNodeImpl node, @NotNull String nodeName, AnActionEvent e) { + Project project = e.getProject(); + if (project != null) { + XDebugSession debugSession = XDebuggerManager.getInstance(project).getCurrentSession(); + ObjectReference ref = getObjectReference(node); + if(debugSession != null && ref != null){ + new InstancesWindow(project, debugSession, ref.referenceType()).show(); + } + } + } + + @Nullable + private ObjectReference getObjectReference(@NotNull XValueNodeImpl node) { + XValue valueContainer = node.getValueContainer(); + if (valueContainer instanceof NodeDescriptorProvider) { + NodeDescriptor descriptor = ((NodeDescriptorProvider) valueContainer).getDescriptor(); + if (descriptor instanceof ValueDescriptor) { + Value value = ((ValueDescriptor)descriptor).getValue(); + return value instanceof ObjectReference ? (ObjectReference) value : null; + } + } + + return null; + } +} diff --git a/component/MemoryViewManager.java b/component/MemoryViewManager.java new file mode 100644 index 000000000000..d7de9e532c95 --- /dev/null +++ b/component/MemoryViewManager.java @@ -0,0 +1,80 @@ +package org.jetbrains.debugger.memory.component; + +import com.intellij.openapi.components.*; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.util.EventDispatcher; +import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; +import org.jetbrains.debugger.memory.toolwindow.MemoryViewToolWindowFactory; +import org.jetbrains.annotations.NotNull; + +@State(name = "MemoryViewSettings", storages = @Storage(StoragePathMacros.WORKSPACE_FILE)) +public class MemoryViewManager extends AbstractProjectComponent + implements PersistentStateComponent { + private final EventDispatcher myDispatcher = + EventDispatcher.create(MemoryViewManagerListener.class); + private MemoryViewManagerState myState = new MemoryViewManagerState(); + + public static MemoryViewManager getInstance(Project project) { + return project.getComponent(MemoryViewManager.class); + } + + protected MemoryViewManager(Project project) { + super(project); + } + + @NotNull + @Override + public MemoryViewManagerState getState() { + return new MemoryViewManagerState(myState); + } + + @Override + public void loadState(MemoryViewManagerState state) { + if (state == null) { + state = new MemoryViewManagerState(); + } + + myState = state; + fireStateChanged(); + } + + public void setShowDiffOnly(boolean value) { + if (myState.isShowWithDiffOnly != value) { + myState.isShowWithDiffOnly = value; + fireStateChanged(); + } + } + + public void setShowWithInstancesOnly(boolean value) { + if (myState.isShowWithInstancesOnly != value) { + myState.isShowWithInstancesOnly = value; + fireStateChanged(); + } + } + + public boolean isNeedShowDiffOnly() { + return myState.isShowWithDiffOnly; + } + + public boolean isNeedShowInstancesOnly() { + return myState.isShowWithInstancesOnly; + } + + public void addMemoryViewManagerListener(MemoryViewManagerListener listener) { + myDispatcher.addListener(listener); + } + + public void removeMemoryViewManagerListener(MemoryViewManagerListener listener) { + myDispatcher.removeListener(listener); + } + + public ToolWindow getToolWindow() { + return ToolWindowManager.getInstance(myProject).getToolWindow(MemoryViewToolWindowFactory.TOOL_WINDOW_ID); + } + + private void fireStateChanged() { + myDispatcher.getMulticaster().stateChanged(new MemoryViewManagerState(myState)); + } +} diff --git a/component/MemoryViewManagerState.java b/component/MemoryViewManagerState.java new file mode 100644 index 000000000000..0694133b5842 --- /dev/null +++ b/component/MemoryViewManagerState.java @@ -0,0 +1,14 @@ +package org.jetbrains.debugger.memory.component; + +public final class MemoryViewManagerState { + public boolean isShowWithInstancesOnly = true; + public boolean isShowWithDiffOnly = false; + + MemoryViewManagerState() { + } + + MemoryViewManagerState(MemoryViewManagerState other) { + isShowWithInstancesOnly = other.isShowWithInstancesOnly; + isShowWithDiffOnly = other.isShowWithDiffOnly; + } +} diff --git a/event/MemoryViewManagerListener.java b/event/MemoryViewManagerListener.java new file mode 100644 index 000000000000..1c7e1cf8f031 --- /dev/null +++ b/event/MemoryViewManagerListener.java @@ -0,0 +1,11 @@ +package org.jetbrains.debugger.memory.event; + +import org.jetbrains.debugger.memory.component.MemoryViewManagerState; +import org.jetbrains.annotations.NotNull; + +import java.util.EventListener; + +@FunctionalInterface +public interface MemoryViewManagerListener extends EventListener { + void stateChanged(@NotNull MemoryViewManagerState state); +} diff --git a/sizeof/RetainedSizeFetcher.java b/sizeof/RetainedSizeFetcher.java new file mode 100644 index 000000000000..78a9f66079ba --- /dev/null +++ b/sizeof/RetainedSizeFetcher.java @@ -0,0 +1,109 @@ +package org.jetbrains.debugger.memory.sizeof; + +import com.sun.jdi.*; + +import java.util.Collection; +import java.util.concurrent.TimeUnit; + +public class RetainedSizeFetcher extends SizeOfEvalStrategy { + private final static long REFERENCE_SIZE = 4; + private final static long CHAR_SIZE = 2; + private final static long INT_SIZE = 4; + private final static long FLOAT_SIZE = 4; + private final static long DOUBLE_SIZE = 8; + private final static long BYTE_SIZE = 1; + private final static long BOOLEAN_SIZE = 1; // ? + private final static long LONG_SIZE = 8; + private final static long SHORT_SIZE = 2; + + private final boolean myCheckReferringObjects; + + public RetainedSizeFetcher(boolean checkReferringObjects) { + myCheckReferringObjects = checkReferringObjects; + } + + private static long getPrimitiveTypeSize(PrimitiveValue value) { + if (value instanceof IntegerValue) { + return INT_SIZE; + } + if (value instanceof LongValue) { + return LONG_SIZE; + } + if (value instanceof DoubleValue) { + return DOUBLE_SIZE; + } + if (value instanceof FloatValue) { + return FLOAT_SIZE; + } + if (value instanceof CharValue) { + return CHAR_SIZE; + } + if (value instanceof ByteValue) { + return BYTE_SIZE; + } + if (value instanceof BooleanValue) { + return BOOLEAN_SIZE; + } + + return SHORT_SIZE; + } + + private long getValueSize(Value value, Collection visited) { + if (value == null) { + return 0; + } + + if (value instanceof PrimitiveValue) { + return getPrimitiveTypeSize((PrimitiveValue) value); + } else { + ObjectReference ref = (ObjectReference) value; + if (visited.contains(ref)) { + return 0; + } + + if (myCheckReferringObjects) { + long startTime = System.nanoTime(); + int needReferringObjectCount = visited.size() + 1; + Collection referringObjects = ref.referringObjects(needReferringObjectCount); + long duration = System.nanoTime() - startTime; + System.out.println("referringObjects(" + needReferringObjectCount + ") duration = " + duration + "ns "+ + "(" + TimeUnit.NANOSECONDS.toMillis(duration) + " ms)."); + if (referringObjects.stream().anyMatch(objectReference -> !visited.contains(objectReference))) { + return 0; + } + } + + return sizeOfImpl(ref, visited, false); + } + } + + @Override + protected long sizeOfImpl(ObjectReference ref, Collection visited, boolean isRoot) { + visited.add(ref); + long result = 0; + + if (ref instanceof ArrayReference) { + ArrayReference arrRef = (ArrayReference) ref; + int length = arrRef.length(); + for (int i = 0; i < length; i++) { + result += getValueSize(arrRef.getValue(i), visited); + } + } + + for (Field field : ref.referenceType().allFields()) { + Value val = ref.getValue(field); + if (field.isStatic()) { + continue; + } + + result += REFERENCE_SIZE; + if (val == null) { + continue; + } + + result += getValueSize(val, visited); + } + + return result; + } +} diff --git a/sizeof/SizeOfEvalStrategy.java b/sizeof/SizeOfEvalStrategy.java new file mode 100644 index 000000000000..962909f3fbaf --- /dev/null +++ b/sizeof/SizeOfEvalStrategy.java @@ -0,0 +1,21 @@ +package org.jetbrains.debugger.memory.sizeof; + +import com.sun.jdi.ObjectReference; + +import java.util.Collection; +import java.util.HashSet; +import java.util.concurrent.TimeUnit; + +abstract class SizeOfEvalStrategy { + public long sizeOf(ObjectReference ref) { + long startTime = System.nanoTime(); + long result = sizeOfImpl(ref, new HashSet<>(), true); + long duration = System.nanoTime() - startTime; + System.out.printf("org.jetbrains.plugin.sizeof [%d] == %d. time = %d ns (%d ms).%n", + ref.uniqueID(), result, duration, TimeUnit.NANOSECONDS.toMillis(duration)); + + return result; + } + + protected abstract long sizeOfImpl(ObjectReference ref, Collection visited, boolean isRoot); +} diff --git a/toolwindow/MemoryViewCondition.java b/toolwindow/MemoryViewCondition.java new file mode 100644 index 000000000000..9bcf07002d4f --- /dev/null +++ b/toolwindow/MemoryViewCondition.java @@ -0,0 +1,45 @@ +package org.jetbrains.debugger.memory.toolwindow; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.util.messages.MessageBusConnection; +import com.intellij.xdebugger.XDebugProcess; +import com.intellij.xdebugger.XDebuggerManager; +import com.intellij.xdebugger.XDebuggerManagerListener; +import icons.MemoryViewIcons; +import org.jetbrains.debugger.memory.component.MemoryViewManager; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.util.Arrays; + +public class MemoryViewCondition implements Condition { + @Override + public boolean value(Project project) { + MessageBusConnection connection = project.getMessageBus().connect(project); + connection.subscribe(XDebuggerManager.TOPIC, new XDebuggerManagerListener() { + @Override + public void processStarted(@NotNull XDebugProcess debugProcess) { + updateIcon(debugProcess.getSession().getProject(), true); + } + + @Override + public void processStopped(@NotNull XDebugProcess debugProcess) { + Project project = debugProcess.getSession().getProject(); + boolean enabled = Arrays.stream(XDebuggerManager.getInstance(project) + .getDebugSessions()).anyMatch(session -> !session.getDebugProcess().equals(debugProcess)); + updateIcon(project, enabled); + } + + private void updateIcon(@NotNull Project project, boolean enabled) { + ToolWindow toolWindow = MemoryViewManager.getInstance(project).getToolWindow(); + if (toolWindow != null) { + SwingUtilities.invokeLater(() -> + toolWindow.setIcon(enabled ? MemoryViewIcons.MAIN_ENABLED : MemoryViewIcons.MAIN_DISABLED)); + } + } + }); + return true; + } +} diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java new file mode 100644 index 000000000000..f258eb633050 --- /dev/null +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -0,0 +1,149 @@ +package org.jetbrains.debugger.memory.toolwindow; + +import com.intellij.execution.Executor; +import com.intellij.execution.ui.RunContentDescriptor; +import com.intellij.execution.ui.RunContentManager; +import com.intellij.execution.ui.RunContentWithExecutorListener; +import com.intellij.openapi.actionSystem.ActionGroup; +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowFactory; +import com.intellij.openapi.wm.ex.ToolWindowEx; +import com.intellij.openapi.wm.ex.ToolWindowManagerAdapter; +import com.intellij.openapi.wm.impl.ToolWindowImpl; +import com.intellij.ui.components.JBLabel; +import com.intellij.util.containers.HashMap; +import com.intellij.util.messages.MessageBusConnection; +import com.intellij.xdebugger.XDebugProcess; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XDebuggerManager; +import com.intellij.xdebugger.XDebuggerManagerListener; +import org.jetbrains.debugger.memory.view.ClassesFilteredView; +import org.jetbrains.debugger.memory.component.MemoryViewManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; + +public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware { + public final static String TOOL_WINDOW_ID = "Memory View"; + + private final JComponent myEmptyContent; + + private final HashMap myMemoryViews = new HashMap<>(); + + { + myEmptyContent = new JBLabel("Run debugging to see loaded classes", SwingConstants.CENTER); + } + + @Override + public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) { + MessageBusConnection connection = project.getMessageBus().connect(project); + connection.subscribe(XDebuggerManager.TOPIC, new MyDebuggerStatusChangedListener()); + connection.subscribe(RunContentManager.TOPIC, new RunContentWithExecutorListener() { + @Override + public void contentSelected(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor) { + SwingUtilities.invokeLater(() -> updateCurrentMemoryView(project, toolWindow)); + } + + @Override + public void contentRemoved(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor) { + SwingUtilities.invokeLater(() -> updateCurrentMemoryView(project, toolWindow)); + } + }); + + ((ToolWindowImpl) toolWindow).getToolWindowManager() + .addToolWindowManagerListener(new ToolWindowManagerAdapter() { + @Override + public void stateChanged() { + if (!myMemoryViews.isEmpty()) { + SwingUtilities.invokeLater(() -> + myMemoryViews.values().forEach(classesFilteredView -> + classesFilteredView.setNeedReloadClasses(toolWindow.isVisible()))); + } + } + }); + + ActionGroup group = new DefaultActionGroup( + ActionManager.getInstance().getAction("MemoryView.ShowOnlyWithInstances"), + ActionManager.getInstance().getAction("MemoryView.ShowOnlyWithDiff") + ); + ((ToolWindowEx) toolWindow).setAdditionalGearActions(group); + toolWindow.getComponent().setLayout(new BorderLayout()); + + for (XDebugSession session : XDebuggerManager.getInstance(project).getDebugSessions()) { + addNewSession(session); + } + + updateCurrentMemoryView(project, toolWindow); + } + + private void addNewSession(@NotNull XDebugSession session) { + ToolWindow toolWindow = getToolWindow(session.getProject()); + if (!myMemoryViews.containsKey(session)) { + ClassesFilteredView newView = new ClassesFilteredView(session); + newView.setNeedReloadClasses(toolWindow != null && toolWindow.isVisible()); + myMemoryViews.put(session, newView); + } + } + + private void removeSession(@NotNull XDebugSession session) { + myMemoryViews.remove(session); + } + + private void updateCurrentMemoryView(@NotNull Project project, @NotNull ToolWindow toolWindow) { + if (!project.isDisposed()) { + XDebugSession session = XDebuggerManager.getInstance(project).getCurrentSession(); + if (session != null && myMemoryViews.containsKey(session)) { + ClassesFilteredView view = myMemoryViews.get(session); + replaceToolWindowContent(toolWindow, view); + return; + } + } + + replaceToolWindowContent(toolWindow, myEmptyContent); + } + + private void replaceToolWindowContent(@NotNull ToolWindow toolWindow, JComponent comp) { + JComponent toolWindowComp = toolWindow.getComponent(); + toolWindowComp.removeAll(); + toolWindowComp.add(comp); + toolWindowComp.repaint(); + } + + @Nullable + private ToolWindow getToolWindow(@NotNull Project project) { + return MemoryViewManager.getInstance(project).getToolWindow(); + } + + private final class MyDebuggerStatusChangedListener implements XDebuggerManagerListener { + @Override + public void processStarted(@NotNull XDebugProcess xDebugProcess) { + SwingUtilities.invokeLater(() -> { + XDebugSession session = xDebugProcess.getSession(); + addNewSession(session); + updateView(session); + }); + } + + @Override + public void processStopped(@NotNull XDebugProcess xDebugProcess) { + SwingUtilities.invokeLater(() -> { + XDebugSession session = xDebugProcess.getSession(); + removeSession(session); + updateView(session); + }); + } + + private void updateView(@NotNull XDebugSession debugSession) { + ToolWindow toolWindow = getToolWindow(debugSession.getProject()); + if (toolWindow != null) { + updateCurrentMemoryView(debugSession.getProject(), toolWindow); + } + } + } +} diff --git a/utils/AbstractTableColumnDescriptor.java b/utils/AbstractTableColumnDescriptor.java new file mode 100644 index 000000000000..3abb30a98e89 --- /dev/null +++ b/utils/AbstractTableColumnDescriptor.java @@ -0,0 +1,21 @@ +package org.jetbrains.debugger.memory.utils; + +public abstract class AbstractTableColumnDescriptor implements AbstractTableModelWithColumns.TableColumnDescriptor { + private final String myName; + private final Class myClass; + protected AbstractTableColumnDescriptor(String name, Class elementClass) { + myName = name; + myClass = elementClass; + } + + @Override + public Class getColumnClass() { + return myClass; + } + + + @Override + public String getName() { + return myName; + } +} diff --git a/utils/AbstractTableModelWithColumns.java b/utils/AbstractTableModelWithColumns.java new file mode 100644 index 000000000000..d0a67129078c --- /dev/null +++ b/utils/AbstractTableModelWithColumns.java @@ -0,0 +1,37 @@ +package org.jetbrains.debugger.memory.utils; + +import javax.swing.table.AbstractTableModel; + +public abstract class AbstractTableModelWithColumns extends AbstractTableModel { + + private final TableColumnDescriptor[] myColumnDescriptors; + public AbstractTableModelWithColumns(TableColumnDescriptor[] descriptors) { + myColumnDescriptors = descriptors; + } + + @Override + public Class getColumnClass(int columnIndex) { + return myColumnDescriptors[columnIndex].getColumnClass(); + } + + @Override + public String getColumnName(int column) { + return myColumnDescriptors[column].getName(); + } + + @Override + public int getColumnCount() { + return myColumnDescriptors.length; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + return myColumnDescriptors[columnIndex].getValue(rowIndex); + } + + interface TableColumnDescriptor { + Class getColumnClass(); + Object getValue(int ix); + String getName(); + } +} diff --git a/utils/DebugCommand.java b/utils/DebugCommand.java new file mode 100644 index 000000000000..3076fb3d3ebc --- /dev/null +++ b/utils/DebugCommand.java @@ -0,0 +1,9 @@ +package org.jetbrains.debugger.memory.utils; + +import com.intellij.debugger.engine.managerThread.DebuggerCommand; + +public interface DebugCommand extends DebuggerCommand { + @Override + default void commandCancelled() { + } +} diff --git a/utils/InstanceJavaValue.java b/utils/InstanceJavaValue.java new file mode 100644 index 000000000000..0c8046e6c989 --- /dev/null +++ b/utils/InstanceJavaValue.java @@ -0,0 +1,31 @@ +package org.jetbrains.debugger.memory.utils; + +import com.intellij.debugger.engine.JavaValue; +import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; +import com.intellij.debugger.ui.impl.watch.NodeManagerImpl; +import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl; +import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class InstanceJavaValue extends JavaValue { + public InstanceJavaValue(JavaValue javaValue, + @NotNull ValueDescriptorImpl valueDescriptor, + @NotNull EvaluationContextImpl evaluationContext, + NodeManagerImpl nodeManager, + boolean contextSet) { + super(javaValue, valueDescriptor, evaluationContext, nodeManager, contextSet); + } + + @Nullable + @Override + public String getEvaluationExpression() { + ObjectReference ref = ((ObjectReference)getDescriptor().getValue()); + return NamesUtils.getUniqueName(ref); + } + + @Override + public boolean canNavigateToSource() { + return false; + } +} diff --git a/utils/InstanceValueDescriptor.java b/utils/InstanceValueDescriptor.java new file mode 100644 index 000000000000..2c1b65f4f7c9 --- /dev/null +++ b/utils/InstanceValueDescriptor.java @@ -0,0 +1,46 @@ +package org.jetbrains.debugger.memory.utils; + +import com.intellij.debugger.DebuggerContext; +import com.intellij.debugger.engine.ContextUtil; +import com.intellij.debugger.engine.evaluation.EvaluateException; +import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; +import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl; +import com.intellij.openapi.project.Project; +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiElementFactory; +import com.intellij.psi.PsiExpression; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.Value; + +public class InstanceValueDescriptor extends ValueDescriptorImpl { + + public InstanceValueDescriptor(Project project, Value value) { + super(project, value); + } + + @Override + public String calcValueName() { + ObjectReference ref = ((ObjectReference)getValue()); + return NamesUtils.getUniqueName(ref); + } + + @Override + public Value calcValue(EvaluationContextImpl evaluationContext) throws EvaluateException { + return getValue(); + } + + @Override + public boolean isShowIdLabel() { + return false; + } + + @Override + public PsiExpression getDescriptorEvaluation(DebuggerContext debuggerContext) throws EvaluateException { + PsiElementFactory elementFactory = JavaPsiFacade.getInstance(myProject).getElementFactory(); + ObjectReference ref = ((ObjectReference)getValue()); + String name = NamesUtils.getUniqueName(ref).replace("@", ""); + String presentation = String.format("%s_DebugLabel", name); + + return elementFactory.createExpressionFromText(presentation, ContextUtil.getContextElement(debuggerContext)); + } +} diff --git a/utils/KeyboardUtils.java b/utils/KeyboardUtils.java new file mode 100644 index 000000000000..d7818f4bccf5 --- /dev/null +++ b/utils/KeyboardUtils.java @@ -0,0 +1,18 @@ +package org.jetbrains.debugger.memory.utils; + +import java.awt.event.KeyEvent; + +public class KeyboardUtils { + public static boolean isEnterKey(int keyCode) { + return keyCode == KeyEvent.VK_ENTER; + } + + public static boolean isArrowKey(int keyCode) { + return keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_LEFT || + keyCode == KeyEvent.VK_DOWN || keyCode == KeyEvent.VK_RIGHT; + } + + public static boolean isCharacter(int keyCode) { + return KeyEvent.getKeyText(keyCode).length() == 1; + } +} diff --git a/utils/NamesUtils.java b/utils/NamesUtils.java new file mode 100644 index 000000000000..8b4363ae1cd1 --- /dev/null +++ b/utils/NamesUtils.java @@ -0,0 +1,10 @@ +package org.jetbrains.debugger.memory.utils; + +import com.sun.jdi.ObjectReference; + +class NamesUtils { + static String getUniqueName(ObjectReference ref) { + String name = ref.referenceType().name().replace("[]", "Array"); + return String.format("%s@%d", name.substring(name.lastIndexOf('.') + 1), ref.uniqueID()); + } +} diff --git a/utils/SingleAlarmWithMutableDelay.java b/utils/SingleAlarmWithMutableDelay.java new file mode 100644 index 000000000000..43687ce17139 --- /dev/null +++ b/utils/SingleAlarmWithMutableDelay.java @@ -0,0 +1,23 @@ +package org.jetbrains.debugger.memory.utils; + +import com.intellij.util.Alarm; +import org.jetbrains.annotations.NotNull; + +public class SingleAlarmWithMutableDelay extends Alarm { + private final Runnable myTask; + private volatile int myDelay; + public SingleAlarmWithMutableDelay(@NotNull Runnable task) { + myTask = task; + } + + public void setDelay(int value) { + myDelay = value; + } + + public void cancelAndRequest() { + if(!isDisposed()) { + cancelAllRequests(); + addRequest(myTask, myDelay); + } + } +} diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java new file mode 100644 index 000000000000..a9a42a79c42b --- /dev/null +++ b/view/ClassesFilteredView.java @@ -0,0 +1,199 @@ +package org.jetbrains.debugger.memory.view; + +import com.intellij.debugger.DebuggerManager; +import com.intellij.debugger.engine.DebugProcess; +import com.intellij.debugger.engine.DebuggerManagerThreadImpl; +import com.intellij.debugger.engine.SuspendContextImpl; +import com.intellij.debugger.engine.events.SuspendContextCommandImpl; +import com.intellij.debugger.impl.DebuggerManagerImpl; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.ui.DocumentAdapter; +import com.intellij.ui.DoubleClickListener; +import com.intellij.ui.SearchTextField; +import com.intellij.ui.components.JBScrollPane; +import com.intellij.util.ui.components.BorderLayoutPanel; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XDebugSessionListener; +import com.sun.jdi.ReferenceType; +import com.sun.jdi.VirtualMachine; +import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; +import org.jetbrains.debugger.memory.component.MemoryViewManager; +import org.jetbrains.debugger.memory.component.MemoryViewManagerState; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.utils.KeyboardUtils; +import org.jetbrains.debugger.memory.utils.SingleAlarmWithMutableDelay; + +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.util.List; +import java.util.concurrent.TimeUnit; + +public class ClassesFilteredView extends BorderLayoutPanel { + private static final Logger LOG = Logger.getInstance(ClassesFilteredView.class); + private final static double DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT = 0.5; + + private final Project myProject; + private final XDebugSession myDebugSession; + private final DebugProcess myDebugProcess; + private final SingleAlarmWithMutableDelay mySingleAlarm; + + private final SearchTextField myFilterTextField = new SearchTextField(false); + private final ClassesTable myTable; + + private boolean myNeedReloadClasses = false; + + public ClassesFilteredView(@NotNull XDebugSession debugSession) { + super(); + + myProject = debugSession.getProject(); + myDebugSession = debugSession; + myDebugProcess = DebuggerManager.getInstance(myProject) + .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); + + MemoryViewManagerState memoryViewManagerState = MemoryViewManager.getInstance(myProject).getState(); + + myTable = new ClassesTable(memoryViewManagerState.isShowWithDiffOnly, + memoryViewManagerState.isShowWithInstancesOnly); + + myTable.addKeyListener(new KeyAdapter() { + @Override + public void keyReleased(KeyEvent e) { + int keyCode = e.getKeyCode(); + if (KeyboardUtils.isEnterKey(keyCode)) { + handleClassSelection(myTable.getSelectedClass()); + } else if (!KeyboardUtils.isArrowKey(keyCode) && KeyboardUtils.isCharacter(keyCode)) { + SwingUtilities.invokeLater(myFilterTextField::requestFocusInWindow); + String text = myFilterTextField.getText(); + myFilterTextField.setText(text + KeyEvent.getKeyText(keyCode).toLowerCase()); + } + } + }); + + new DoubleClickListener() { + @Override + protected boolean onDoubleClick(MouseEvent event) { + handleClassSelection(myTable.getSelectedClass()); + return true; + } + }.installOn(myTable); + + final MemoryViewManagerListener memoryViewManagerListener = state -> { + myTable.setFilteringByDiffNonZero(state.isShowWithDiffOnly); + myTable.setFilteringByInstanceExists(state.isShowWithInstancesOnly); + }; + + MemoryViewManager.getInstance(myProject).addMemoryViewManagerListener(memoryViewManagerListener); + + myDebugSession.addSessionListener(new XDebugSessionListener() { + @Override + public void sessionResumed() { + SwingUtilities.invokeLater(myTable::hideContent); + } + + @Override + public void sessionStopped() { + debugSession.removeSessionListener(this); + MemoryViewManager.getInstance(myProject).removeMemoryViewManagerListener(memoryViewManagerListener); + } + + @Override + public void sessionPaused() { + SwingUtilities.invokeLater(myTable::showContent); + if (myNeedReloadClasses) { + updateClassesAndCounts(); + } + } + }); + + myFilterTextField.addDocumentListener(new DocumentAdapter() { + @Override + protected void textChanged(DocumentEvent e) { + myTable.setFilterPattern(myFilterTextField.getText()); + } + }); + + mySingleAlarm = new SingleAlarmWithMutableDelay(() -> { + SuspendContextImpl suspendContext = getSuspendContext(); + if (suspendContext != null) { + SwingUtilities.invokeLater(() -> myTable.setBusy(true)); + + ((DebuggerManagerThreadImpl) myDebugProcess.getManagerThread()) + .schedule(new MyUpdateClassesCommand(suspendContext)); + } + }); + + JBScrollPane scroll = new JBScrollPane(myTable); + addToTop(myFilterTextField); + addToCenter(scroll); + } + + public void setNeedReloadClasses(boolean value) { + if (myNeedReloadClasses != value) { + myNeedReloadClasses = value; + if (myNeedReloadClasses) { + updateClassesAndCounts(); + } + } + } + + private void handleClassSelection(@Nullable ReferenceType ref) { + if (ref != null && myDebugSession.isSuspended()) { + InstancesWindow window = new InstancesWindow(myProject, myDebugSession, ref); + window.show(); + } + } + + private SuspendContextImpl getSuspendContext() { + return DebuggerManagerImpl.getInstanceEx(myProject).getContext().getSuspendContext(); + } + + private void updateClassesAndCounts() { + if (myDebugProcess.isAttached()) { + mySingleAlarm.cancelAndRequest(); + } + } + + private final class MyUpdateClassesCommand extends SuspendContextCommandImpl { + MyUpdateClassesCommand(@Nullable SuspendContextImpl suspendContext) { + super(suspendContext); + } + + @Override + public Priority getPriority() { + return Priority.LOWEST; + } + + @Override + public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { + loadClassesAndCounts(); + } + + @Override + public void commandCancelled() { + } + } + + private void loadClassesAndCounts() { + assert DebuggerManager.getInstance(myProject).isDebuggerManagerThread(); + myDebugProcess.getVirtualMachineProxy(); + List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); + + if (classes.isEmpty()) { + return; + } + + VirtualMachine vm = classes.get(0).virtualMachine(); + long start = System.nanoTime(); + long[] counts = vm.instanceCounts(classes); + long delay = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); + mySingleAlarm.setDelay((int) (DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT * delay)); + + LOG.info(String.format("Instances query time = %d ms. Count = %d%n", delay, classes.size())); + SwingUtilities.invokeLater(() -> myTable.setClassesAndCounts(classes, counts)); + } +} diff --git a/view/ClassesTable.java b/view/ClassesTable.java new file mode 100644 index 000000000000..495350a048d5 --- /dev/null +++ b/view/ClassesTable.java @@ -0,0 +1,307 @@ +package org.jetbrains.debugger.memory.view; + +import com.intellij.icons.AllIcons; +import com.intellij.ui.ColoredTableCellRenderer; +import com.intellij.ui.table.JBTable; +import com.intellij.util.ui.JBUI; +import com.sun.jdi.ReferenceType; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.utils.AbstractTableColumnDescriptor; +import org.jetbrains.debugger.memory.utils.AbstractTableModelWithColumns; + +import javax.swing.*; +import javax.swing.table.TableColumn; +import javax.swing.table.TableRowSorter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +class ClassesTable extends JBTable { + private static final int CLASSES_COLUMN_PREFERRED_WIDTH = 250; + private static final int COUNT_COLUMN_MIN_WIDTH = 80; + private static final int COUNT_COLUMN_MAX_WIDTH = 100; + private static final int DIFF_COLUMN_MIN_WIDTH = 80; + private static final int DIFF_COLUMN_MAX_WIDTH = 100; + + private static final String EMPTY_TABLE_CONTENT = "The application is running"; + + private final DiffViewTableModel myModel = new DiffViewTableModel(); + private final UnknownDiffValue myUnknownValue = new UnknownDiffValue(); + + private boolean myOnlyWithDiff; + private boolean myOnlyWithInstances; + + private String myFilteringPattern = ""; + private ConcurrentHashMap myCounts = new ConcurrentHashMap<>(); + + private volatile List myElems = Collections.unmodifiableList(new ArrayList<>()); + + ClassesTable(boolean onlyWithDiff, boolean onlyWithInstances) { + setModel(myModel); + + myOnlyWithDiff = onlyWithDiff; + myOnlyWithInstances = onlyWithInstances; + + getEmptyText().setText(EMPTY_TABLE_CONTENT); + + TableColumn classesColumn = getColumnModel().getColumn(DiffViewTableModel.CLASSNAME_COLUMN_INDEX); + TableColumn countColumn = getColumnModel().getColumn(DiffViewTableModel.COUNT_COLUMN_INDEX); + TableColumn diffColumn = getColumnModel().getColumn(DiffViewTableModel.DIFF_COLUMN_INDEX); + + setAutoResizeMode(AUTO_RESIZE_ALL_COLUMNS); + classesColumn.setPreferredWidth(JBUI.scale(CLASSES_COLUMN_PREFERRED_WIDTH)); + classesColumn.setResizable(false); + + countColumn.setMinWidth(JBUI.scale(COUNT_COLUMN_MIN_WIDTH)); + countColumn.setMaxWidth(JBUI.scale(COUNT_COLUMN_MAX_WIDTH)); + countColumn.setResizable(false); + + diffColumn.setMinWidth(JBUI.scale(DIFF_COLUMN_MIN_WIDTH)); + diffColumn.setMaxWidth(JBUI.scale(DIFF_COLUMN_MAX_WIDTH)); + diffColumn.setResizable(false); + + setDefaultRenderer(ReferenceType.class, new ColoredTableCellRenderer() { + @Override + protected void customizeCellRenderer(JTable table, @Nullable Object value, + boolean isSelected, boolean hasFocus, int row, int column) { + String presentation = value == null ? "null" : ((ReferenceType) value).name(); + append(presentation); + setTransparentIconBackground(true); + setIcon(AllIcons.Nodes.Class); + } + }); + + setDefaultRenderer(Integer.class, new ColoredTableCellRenderer() { + @Override + protected void customizeCellRenderer(JTable table, @Nullable Object value, + boolean selected, boolean hasFocus, int row, int column) { + if (value != null) { + int val = (Integer) value; + append(String.format("%s%d", val > 0 ? "+" : "", val)); + setTextAlign(SwingConstants.RIGHT); + } + } + }); + + TableRowSorter sorter = new TableRowSorter<>(myModel); + sorter.setRowFilter(new RowFilter() { + @Override + public boolean include(Entry entry) { + int ix = entry.getIdentifier(); + ReferenceType ref = myElems.get(ix); + DiffValue diff = myCounts.getOrDefault(ref, myUnknownValue); + if (myOnlyWithDiff && diff.diff() == 0 || + myOnlyWithInstances && !diff.hasInstance()) { + return false; + } + + String name = ref.name().toLowerCase(); + String pattern = myFilteringPattern; + return name.contains(pattern) || match(pattern.toLowerCase(), name.substring(name.lastIndexOf('.') + 1)); + } + }); + + List myDefaultSortingKeys = Arrays.asList( + new RowSorter.SortKey(DiffViewTableModel.DIFF_COLUMN_INDEX, SortOrder.DESCENDING), + new RowSorter.SortKey(DiffViewTableModel.COUNT_COLUMN_INDEX, SortOrder.DESCENDING), + new RowSorter.SortKey(DiffViewTableModel.CLASSNAME_COLUMN_INDEX, SortOrder.ASCENDING) + ); + sorter.setSortKeys(myDefaultSortingKeys); + setRowSorter(sorter); + setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + } + + @Nullable + ReferenceType getSelectedClass() { + int selectedRow = getSelectedRow(); + if (selectedRow != -1) { + int ix = convertRowIndexToModel(selectedRow); + return myElems.get(ix); + } + + return null; + } + + void setBusy(boolean value) { + setPaintBusy(value); + } + + void setFilterPattern(String pattern) { + if (!myFilteringPattern.equals(pattern)) { + myFilteringPattern = pattern; + getRowSorter().allRowsChanged(); + } + } + + void setFilteringByInstanceExists(boolean value) { + if (value != myOnlyWithInstances) { + myOnlyWithInstances = value; + getRowSorter().allRowsChanged(); + } + } + + void setFilteringByDiffNonZero(boolean value) { + if (myOnlyWithDiff != value) { + myOnlyWithDiff = value; + getRowSorter().allRowsChanged(); + } + } + + void setClassesAndCounts(List classes, long[] counts) { + ReferenceType selectedClass = myModel.getSelectedClassBeforeHided(); + + final int newSelectedIndex = classes.indexOf(selectedClass); + myElems = Collections.unmodifiableList(new ArrayList<>(classes)); + for (int i = 0, size = myElems.size(); i < size; i++) { + ReferenceType ref = myElems.get(i); + myCounts.put(ref, myCounts.getOrDefault(ref, myUnknownValue).update(counts[i])); + } + + getRowSorter().allRowsChanged(); + + if (newSelectedIndex != -1) { + changeSelection(convertRowIndexToView(newSelectedIndex), + DiffViewTableModel.CLASSNAME_COLUMN_INDEX, false, false); + } + + setBusy(false); + } + + void hideContent() { + myModel.hide(); + } + + void showContent() { + myModel.show(); + } + + // TODO: use jb filter + private boolean match(String pattern, String value) { + int i = 0; + int j = 0; + while (j < value.length()) { + if (i < pattern.length() && value.charAt(j) == pattern.charAt(i)) { + i++; + } + j++; + } + + return i == pattern.length(); + } + + private class DiffViewTableModel extends AbstractTableModelWithColumns { + private final static int CLASSNAME_COLUMN_INDEX = 0; + private final static int COUNT_COLUMN_INDEX = 1; + private final static int DIFF_COLUMN_INDEX = 2; + + // Workaround: save selection after content of classes table has been hided + private ReferenceType selectedClassWhenHided = null; + private boolean isHide = false; + + DiffViewTableModel() { + super(new AbstractTableColumnDescriptor[]{ + new AbstractTableColumnDescriptor("Class", ReferenceType.class) { + @Override + public Object getValue(int ix) { + return myElems.get(ix); + } + }, + new AbstractTableColumnDescriptor("Count", Long.class) { + @Override + public Object getValue(int ix) { + return myCounts.getOrDefault(myElems.get(ix), myUnknownValue).myCurrentCount; + } + }, + new AbstractTableColumnDescriptor("Diff", Integer.class) { + @Override + public Object getValue(int ix) { + return myCounts.getOrDefault(myElems.get(ix), myUnknownValue).diff(); + } + } + }); + } + + ReferenceType getSelectedClassBeforeHided() { + return selectedClassWhenHided; + } + + void hide() { + if (!isHide) { + selectedClassWhenHided = getSelectedClass(); + isHide = true; + getRowSorter().allRowsChanged(); + } + } + + void show() { + if (isHide) { + isHide = false; + getRowSorter().allRowsChanged(); + } + } + + @Override + public int getRowCount() { + return isHide ? 0 : myElems.size(); + } + } + + private static class UnknownDiffValue extends DiffValue { + UnknownDiffValue() { + super(0); + } + + @Override + boolean hasInstance() { + return true; + } + + @Override + DiffValue update(long count) { + return new DiffValue(count, count); + } + + @Override + public String toString() { + return "?"; + } + } + + private static class DiffValue { + private final long myOldCount; + private final long myCurrentCount; + + DiffValue(long count) { + this(count, count); + } + + DiffValue(long old, long current) { + myCurrentCount = current; + myOldCount = old; + } + + DiffValue update(long count) { + return new DiffValue(myCurrentCount, count); + } + + boolean hasInstance() { + return myCurrentCount > 0; + } + + int diff() { + return (int) (myCurrentCount - myOldCount); + } + + @Override + public String toString() { + if (myOldCount == myCurrentCount) { + return String.valueOf(myOldCount); + } + + long diff = diff(); + return String.format("%d (%s%d)", myCurrentCount, diff > 0 ? "+" : "-", Math.abs(diff)); + } + } +} diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java new file mode 100644 index 000000000000..05d8fed6e311 --- /dev/null +++ b/view/InstancesWindow.java @@ -0,0 +1,531 @@ +package org.jetbrains.debugger.memory.view; + +import com.intellij.debugger.DebuggerManager; +import com.intellij.debugger.engine.*; +import com.intellij.debugger.engine.evaluation.EvaluateException; +import com.intellij.debugger.engine.evaluation.EvaluationContext; +import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; +import com.intellij.debugger.engine.evaluation.TextWithImportsImpl; +import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator; +import com.intellij.debugger.engine.events.DebuggerContextCommandImpl; +import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl; +import com.intellij.debugger.ui.impl.watch.MessageDescriptor; +import com.intellij.debugger.ui.impl.watch.NodeManagerImpl; +import com.intellij.debugger.ui.tree.NodeDescriptor; +import com.intellij.debugger.ui.tree.render.CachedEvaluator; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.actionSystem.ex.AnActionListener; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.PsiClass; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.ui.JBColor; +import com.intellij.ui.JBProgressBar; +import com.intellij.ui.components.JBLabel; +import com.intellij.ui.components.JBPanel; +import com.intellij.ui.components.JBScrollPane; +import com.intellij.util.ui.JBDimension; +import com.intellij.util.ui.JBUI; +import com.intellij.util.ui.UIUtil; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XDebugSessionListener; +import com.intellij.xdebugger.XExpression; +import com.intellij.xdebugger.frame.*; +import com.intellij.xdebugger.frame.presentation.XValuePresentation; +import com.intellij.xdebugger.impl.XDebugSessionImpl; +import com.intellij.xdebugger.impl.XSourcePositionImpl; +import com.intellij.xdebugger.impl.actions.XDebuggerActionBase; +import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl; +import com.intellij.xdebugger.impl.evaluate.quick.XDebuggerTreeCreator; +import com.intellij.xdebugger.impl.frame.XValueMarkers; +import com.intellij.xdebugger.impl.ui.XDebuggerExpressionEditor; +import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants; +import com.intellij.xdebugger.impl.ui.tree.ValueMarkup; +import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; +import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState; +import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase; +import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; +import com.sun.jdi.BooleanValue; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; +import com.sun.jdi.Value; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.java.debugger.JavaDebuggerEditorsProvider; +import org.jetbrains.debugger.memory.utils.DebugCommand; +import org.jetbrains.debugger.memory.utils.InstanceJavaValue; +import org.jetbrains.debugger.memory.utils.InstanceValueDescriptor; + +import javax.swing.*; +import javax.swing.tree.TreeNode; +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import static java.lang.Math.min; + +public class InstancesWindow extends DialogWrapper { + private static final int DEFAULT_WINDOW_WIDTH = 700; + private static final int DEFAULT_WINDOW_HEIGHT = 400; + private static final int FILTERING_BUTTON_ADDITIONAL_WIDTH = 30; + private static final int BORDER_LAYOUT_DEFAULT_GAP = 5; + + private final Project myProject; + private final XDebugSession myDebugSession; + private final ReferenceType myReferenceType; + private MyInstancesView myInstancesView; + + public InstancesWindow(@NotNull Project project, + @NotNull XDebugSession session, + @NotNull ReferenceType referenceType) { + super(project, false); + myProject = project; + myDebugSession = session; + myReferenceType = referenceType; + setTitle("Instances of " + referenceType.name()); + myDebugSession.addSessionListener(new XDebugSessionListener() { + @Override + public void sessionStopped() { + SwingUtilities.invokeLater(() -> close(OK_EXIT_CODE)); + } + }, myDisposable); + setModal(false); + init(); + JRootPane root = myInstancesView.getRootPane(); + root.setDefaultButton(myInstancesView.myFilterButton); + } + + @NotNull + @Override + protected String getDimensionServiceKey() { + return "#org.jetbrains.plugin.view.InstancesWindow"; + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + myInstancesView = new MyInstancesView(); + myInstancesView.setPreferredSize( + new JBDimension(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT)); + return myInstancesView; + } + + @Nullable + @Override + protected JComponent createSouthPanel() { + JComponent comp = super.createSouthPanel(); + if (comp != null) { + comp.add(myInstancesView.myProgressPanel, BorderLayout.WEST); + } + + return comp; + } + + @Override + protected void dispose() { + super.dispose(); + myInstancesView.dispose(); + } + + @NotNull + @Override + protected Action[] createActions() { + return new Action[]{new DialogWrapperExitAction("Close", CLOSE_EXIT_CODE)}; + } + + private class MyInstancesView extends JBPanel implements Disposable { + private static final int MAX_TREE_NODE_COUNT = 1000; + private static final int FILTERING_CHUNK_SIZE = 30; + + private final XDebuggerTree myInstancesTree; + private final XDebuggerExpressionEditor myFilterConditionEditor; + private final XDebugSessionListener myDebugSessionListener = new MySessionListener(); + + private final MyCachedEvaluator myEvaluator = new MyCachedEvaluator(); + private final MyNodeManager myNodeManager = new MyNodeManager(myProject); + + private final JBProgressBar myFilteringProgressBar = new JBProgressBar(); + private final JButton myFilterButton = new JButton("Filter"); + private final JComponent myProgressPanel = new JBPanel<>(new BorderLayout()); + + private final AnActionListener.Adapter myActionListener = new MyActionListener(); + private final Object myFilteringTaskLock = new Object(); + + private volatile MyFilteringWorker myFilteringTask = null; + + MyInstancesView() { + super(new BorderLayout(0, JBUI.scale(BORDER_LAYOUT_DEFAULT_GAP))); + + XValueMarkers markers = getValueMarkers(); + ActionManager.getInstance().addAnActionListener(myActionListener, InstancesWindow.this.myDisposable); + myDebugSession.addSessionListener(myDebugSessionListener, InstancesWindow.this.myDisposable); + JavaDebuggerEditorsProvider editorsProvider = new JavaDebuggerEditorsProvider(); + + myFilterConditionEditor = new XDebuggerExpressionEditor(myProject, editorsProvider, "FilterExpression", + null, XExpressionImpl.EMPTY_EXPRESSION, false, true, true); + new SwingWorker() { + @Override + protected Void doInBackground() throws Exception { + ApplicationManager.getApplication().runReadAction(() -> { + final PsiClass psiClass = DebuggerUtils.findClass(myReferenceType.name(), + myProject, GlobalSearchScope.allScope(myProject)); + XSourcePositionImpl position = XSourcePositionImpl.createByElement(psiClass); + SwingUtilities.invokeLater(() -> myFilterConditionEditor.setSourcePosition(position)); + }); + return null; + } + }.execute(); + + myFilterButton.setBorder(BorderFactory.createEmptyBorder()); + Dimension progressbarPreferredSize = myFilterConditionEditor.getEditorComponent().getPreferredSize(); + progressbarPreferredSize.width = JBUI.scale(FILTERING_BUTTON_ADDITIONAL_WIDTH) + + myFilterButton.getPreferredSize().width; + myFilterButton.setPreferredSize(progressbarPreferredSize); + + JBPanel filteringPane = new JBPanel(new BorderLayout(JBUI.scale(BORDER_LAYOUT_DEFAULT_GAP), 0)); + + filteringPane.add(new JBLabel("Condition:"), BorderLayout.WEST); + filteringPane.add(myFilterConditionEditor.getComponent(), BorderLayout.CENTER); + filteringPane.add(myFilterButton, BorderLayout.EAST); + + JLabel stopFilteringButton = + new JLabel(UIUtil.isUnderDarcula() ? AllIcons.Actions.Clean : AllIcons.Actions.CleanLight); + stopFilteringButton.setOpaque(true); + stopFilteringButton.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + cancelFilteringTask(); + } + }); + + myProgressPanel.add(myFilteringProgressBar, BorderLayout.CENTER); + myProgressPanel.add(stopFilteringButton, BorderLayout.EAST); + + XDebuggerTreeCreator treeCreator = + new XDebuggerTreeCreator(myProject, editorsProvider, null, markers); + myInstancesTree = (XDebuggerTree) treeCreator.createTree(Pair.pair(new XNamedValue("root") { + @Override + public void computeChildren(@NotNull XCompositeNode node) { + updateInstances(); + } + + @Override + public void computePresentation(@NotNull XValueNode xValueNode, @NotNull XValuePlace xValuePlace) { + xValueNode.setPresentation(null, "", "", true); + } + }, "root")); + + myInstancesTree.setRootVisible(false); + myInstancesTree.getRoot().setLeaf(false); + myInstancesTree.setExpandableItemsEnabled(true); + + myFilterButton.addActionListener(e -> + myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree))); + + JBScrollPane treeScrollPane = new JBScrollPane(myInstancesTree); + add(filteringPane, BorderLayout.NORTH); + add(treeScrollPane, BorderLayout.CENTER); + + } + + @Override + public void dispose() { + ActionManager.getInstance().removeAnActionListener(myActionListener); + myDebugSession.removeSessionListener(myDebugSessionListener); + cancelFilteringTask(); + Disposer.dispose(myInstancesTree); + } + + private void updateInstances() { + DebugProcess debugProcess = DebuggerManager.getInstance(myProject) + .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); + + cancelFilteringTask(); + + debugProcess.getManagerThread().invokeCommand((DebugCommand) () -> { + List instances = myReferenceType.instances(0); + EvaluationContextImpl evaluationContext = ((DebugProcessImpl) debugProcess) + .getDebuggerContext().createEvaluationContext(); + + if(evaluationContext != null) { + synchronized (myFilteringTaskLock) { + myFilteringTask = new MyFilteringWorker(instances, evaluationContext, createEvaluator()); + myFilteringTask.execute(); + } + } + }); + } + + private void showProgressPane(int progressBarMaximum) { + myFilteringProgressBar.setMinimum(0); + myFilteringProgressBar.setMaximum(progressBarMaximum); + myProgressPanel.setVisible(true); + } + + private void hideProgressPane() { + myProgressPanel.setVisible(false); + } + + @Nullable + private ExpressionEvaluator createEvaluator() { + ExpressionEvaluator evaluator = null; + + XExpression expression = myFilterConditionEditor.getExpression(); + if (expression != null && !expression.getExpression().isEmpty()) { + try { + myEvaluator.setReferenceExpression(TextWithImportsImpl. + fromXExpression(myFilterConditionEditor.getExpression())); + evaluator = myEvaluator.getEvaluator(); + } catch (EvaluateException ignored) { + } + } + + return evaluator; + } + + private void cancelFilteringTask() { + if (myFilteringTask != null) { + synchronized (myFilteringTaskLock) { + if (myFilteringTask != null) { + myFilteringTask.cancel(false); + myFilteringTask = null; + } + } + } + } + + private XValueMarkers getValueMarkers() { + return myDebugSession instanceof XDebugSessionImpl + ? ((XDebugSessionImpl) myDebugSession).getValueMarkers() + : null; + } + + private class MySessionListener implements XDebugSessionListener { + private final XValueChildrenList myRunningAppChildNode = new XValueChildrenList(); + + private volatile XDebuggerTreeState myTreeState = null; + + { + myRunningAppChildNode.add(new XNamedValue("") { + @Override + public void computePresentation(@NotNull XValueNode node, @NotNull XValuePlace place) { + node.setPresentation(XDebuggerUIConstants.INFORMATION_MESSAGE_ICON, new XValuePresentation() { + @NotNull + @Override + public String getSeparator() { + return ""; + } + + @Override + public void renderValue(@NotNull XValueTextRenderer renderer) { + renderer.renderValue("The application is running"); + } + }, false); + } + }); + } + + @Override + public void sessionResumed() { + SwingUtilities.invokeLater(() -> { + myTreeState = XDebuggerTreeState.saveState(myInstancesTree); + myInstancesTree.getRoot().clearChildren(); + ((XValueNodeImpl) myInstancesTree.getRoot()).addChildren(myRunningAppChildNode, true); + }); + } + + @Override + public void sessionPaused() { + SwingUtilities.invokeLater(() -> { + myInstancesTree.setRootVisible(false); + myInstancesTree.rebuildAndRestore(myTreeState); + }); + } + } + + private class MyActionListener extends AnActionListener.Adapter { + @Override + public void afterActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) { + if (dataContext.getData(PlatformDataKeys.CONTEXT_COMPONENT) == myInstancesView.myInstancesTree && + (isAddToWatchesAction(action) || isEvaluateExpressionAction(action))) { + myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree)); + } + } + + @Override + public void beforeActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) { + if (dataContext.getData(PlatformDataKeys.CONTEXT_COMPONENT) == myInstancesView.myInstancesTree && + (isAddToWatchesAction(action) || isEvaluateExpressionAction(action))) { + XValueNodeImpl selectedNode = XDebuggerTreeActionBase.getSelectedNode(dataContext); + XValueMarkers markers = getValueMarkers(); + + if (markers != null && selectedNode != null) { + + TreeNode currentNode = selectedNode; + while (!myInstancesTree.getRoot().equals(currentNode.getParent())) { + currentNode = currentNode.getParent(); + } + + XValue valueContainer = ((XValueNodeImpl) currentNode).getValueContainer(); + + String expression = valueContainer.getEvaluationExpression(); + if (expression != null) { + markers.markValue(valueContainer, + new ValueMarkup(expression.replace("@", ""), new JBColor(0, 0), null)); + } + } + } + } + + private boolean isAddToWatchesAction(AnAction action) { + String className = action.getClass().getSimpleName(); + return action instanceof XDebuggerTreeActionBase && className.equals("XAddToWatchesAction"); + } + + private boolean isEvaluateExpressionAction(AnAction action) { + String className = action.getClass().getSimpleName(); + return action instanceof XDebuggerActionBase && className.equals("EvaluateAction"); + } + } + + private final class MyCachedEvaluator extends CachedEvaluator { + @Override + @NotNull + protected String getClassName() { + return myReferenceType.name(); + } + + ExpressionEvaluator getEvaluator() throws EvaluateException { + return getEvaluator(myProject); + } + } + + private class MyFilteringWorker extends SwingWorker { + private final List myReferences; + private final EvaluationContextImpl myEvaluationContext; + private final ExpressionEvaluator myExpressionEvaluator; + private volatile boolean myDebuggerTaskCompleted; + + MyFilteringWorker(@NotNull List refs, + @NotNull EvaluationContextImpl evaluationContext, + @Nullable ExpressionEvaluator evaluator) { + myReferences = refs; + myEvaluationContext = evaluationContext; + myExpressionEvaluator = evaluator; + } + + @Override + protected Void doInBackground() throws Exception { + DebugProcessImpl debugProcess = (DebugProcessImpl) DebuggerManager.getInstance(myProject) + .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); + + SwingUtilities.invokeLater(() -> showProgressPane(myReferences.size())); + + AtomicInteger totalChildren = new AtomicInteger(0); + for (int i = 0, size = myReferences.size(); i < size && !isCancelled(); i += FILTERING_CHUNK_SIZE) { + myDebuggerTaskCompleted = false; + final int chunkBegin = i; + debugProcess.getManagerThread().schedule(new DebuggerContextCommandImpl(debugProcess.getDebuggerContext()) { + @Override + public Priority getPriority() { + return Priority.LOWEST; + } + + @Override + public void threadAction(@NotNull SuspendContextImpl suspendContext) { + XValueChildrenList children = new XValueChildrenList(FILTERING_CHUNK_SIZE); + int endOfChunk = min(chunkBegin + FILTERING_CHUNK_SIZE, size); + for (int j = chunkBegin; j < endOfChunk + && !isCancelled() && totalChildren.get() < MAX_TREE_NODE_COUNT; j++) { + ObjectReference ref = myReferences.get(j); + if (myExpressionEvaluator != null && isSatisfy(myExpressionEvaluator, ref) != MyFilteringResult.MATCH) { + continue; + } + + JavaValue val = new InstanceJavaValue(null, new InstanceValueDescriptor(myProject, ref), + myEvaluationContext, myNodeManager, true); + children.add(val); + } + + if (children.size() > 0) { + totalChildren.addAndGet(children.size()); + SwingUtilities.invokeLater(() -> + ((XValueNodeImpl) myInstancesTree.getRoot()).addChildren(children, false)); + } + + SwingUtilities.invokeLater(() -> myFilteringProgressBar.setValue(endOfChunk)); + + synchronized (MyFilteringWorker.this) { + myDebuggerTaskCompleted = true; + MyFilteringWorker.this.notify(); + } + } + }); + debugProcess.getManagerThread().invokeCommand((DebugCommand) () -> { + + }); + + synchronized (this) { + while (!myDebuggerTaskCompleted) { + wait(); + } + } + if (totalChildren.get() >= MAX_TREE_NODE_COUNT) { + break; + } + } + + SwingUtilities.invokeLater(() -> { + ((XValueNodeImpl) myInstancesTree.getRoot()).addChildren(XValueChildrenList.EMPTY, true); + hideProgressPane(); + }); + + return null; + } + + private MyFilteringResult isSatisfy(@NotNull ExpressionEvaluator evaluator, @NotNull Value value) { + try { + Value evaluate = evaluator.evaluate(myEvaluationContext.createEvaluationContext(value)); + if (evaluate instanceof BooleanValue && ((BooleanValue) evaluate).value()) { + return MyFilteringResult.MATCH; + } + } catch (EvaluateException e) { + return MyFilteringResult.EVAL_ERROR; + } + + return MyFilteringResult.NO_MATCH; + } + } + } + + private final static class MyNodeManager extends NodeManagerImpl { + MyNodeManager(Project project) { + super(project, null); + } + + @Override + public DebuggerTreeNodeImpl createNode(final NodeDescriptor descriptor, EvaluationContext evaluationContext) { + return new DebuggerTreeNodeImpl(null, descriptor); + } + + @Override + public DebuggerTreeNodeImpl createMessageNode(MessageDescriptor descriptor) { + return new DebuggerTreeNodeImpl(null, descriptor); + } + + @Override + public DebuggerTreeNodeImpl createMessageNode(String message) { + return new DebuggerTreeNodeImpl(null, new MessageDescriptor(message)); + } + } + + private enum MyFilteringResult { + MATCH, NO_MATCH, EVAL_ERROR + } +} From 58ac0267080e7b5fbae8c52e6900e5d551a661f2 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Tue, 9 Aug 2016 13:53:34 +0300 Subject: [PATCH 002/558] removed table borders --- view/ClassesFilteredView.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index a9a42a79c42b..5b78834bcc11 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -8,10 +8,7 @@ import com.intellij.debugger.engine.events.SuspendContextCommandImpl; import com.intellij.debugger.impl.DebuggerManagerImpl; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; -import com.intellij.ui.DocumentAdapter; -import com.intellij.ui.DoubleClickListener; -import com.intellij.ui.SearchTextField; -import com.intellij.ui.components.JBScrollPane; +import com.intellij.ui.*; import com.intellij.util.ui.components.BorderLayoutPanel; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; @@ -127,7 +124,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { } }); - JBScrollPane scroll = new JBScrollPane(myTable); + JScrollPane scroll = ScrollPaneFactory.createScrollPane(myTable, SideBorder.TOP); addToTop(myFilterTextField); addToCenter(scroll); } From 01d2b8cce13c58a500cd9b1fa2b1db1ae45cbd69 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 9 Aug 2016 18:49:25 +0300 Subject: [PATCH 003/558] Fix ArrayOufOfRangeException after debugger step. Hide content of table when it is updating --- view/ClassesFilteredView.java | 1 - view/ClassesTable.java | 30 +++++++++++++++++------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 5b78834bcc11..8325b38bfe50 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -100,7 +100,6 @@ public class ClassesFilteredView extends BorderLayoutPanel { @Override public void sessionPaused() { - SwingUtilities.invokeLater(myTable::showContent); if (myNeedReloadClasses) { updateClassesAndCounts(); } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 495350a048d5..f4124b8d020d 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -151,7 +151,6 @@ class ClassesTable extends JBTable { void setClassesAndCounts(List classes, long[] counts) { ReferenceType selectedClass = myModel.getSelectedClassBeforeHided(); - final int newSelectedIndex = classes.indexOf(selectedClass); myElems = Collections.unmodifiableList(new ArrayList<>(classes)); for (int i = 0, size = myElems.size(); i < size; i++) { @@ -159,9 +158,9 @@ class ClassesTable extends JBTable { myCounts.put(ref, myCounts.getOrDefault(ref, myUnknownValue).update(counts[i])); } - getRowSorter().allRowsChanged(); + showContent(); - if (newSelectedIndex != -1) { + if (newSelectedIndex != -1 && !myModel.isHided()) { changeSelection(convertRowIndexToView(newSelectedIndex), DiffViewTableModel.CLASSNAME_COLUMN_INDEX, false, false); } @@ -173,7 +172,7 @@ class ClassesTable extends JBTable { myModel.hide(); } - void showContent() { + private void showContent() { myModel.show(); } @@ -197,8 +196,8 @@ class ClassesTable extends JBTable { private final static int DIFF_COLUMN_INDEX = 2; // Workaround: save selection after content of classes table has been hided - private ReferenceType selectedClassWhenHided = null; - private boolean isHide = false; + private ReferenceType mySelectedClassWhenHided = null; + private boolean myIsWithContent = true; DiffViewTableModel() { super(new AbstractTableColumnDescriptor[]{ @@ -224,27 +223,32 @@ class ClassesTable extends JBTable { } ReferenceType getSelectedClassBeforeHided() { - return selectedClassWhenHided; + return mySelectedClassWhenHided; } void hide() { - if (!isHide) { - selectedClassWhenHided = getSelectedClass(); - isHide = true; + if (myIsWithContent) { + mySelectedClassWhenHided = getSelectedClass(); + myIsWithContent = false; + clearSelection(); getRowSorter().allRowsChanged(); } } void show() { - if (isHide) { - isHide = false; + if (!myIsWithContent) { + myIsWithContent = true; getRowSorter().allRowsChanged(); } } + boolean isHided() { + return !myIsWithContent; + } + @Override public int getRowCount() { - return isHide ? 0 : myElems.size(); + return myIsWithContent ? myElems.size() : 0; } } From 49fcb9f0117279ed5acb6ee52007f8aec57c5090 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 9 Aug 2016 19:26:55 +0300 Subject: [PATCH 004/558] Fix classes loading on first breakpoint. Remove redundant methods toString --- view/ClassesTable.java | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index f4124b8d020d..f2c4c67b81e9 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -197,7 +197,7 @@ class ClassesTable extends JBTable { // Workaround: save selection after content of classes table has been hided private ReferenceType mySelectedClassWhenHided = null; - private boolean myIsWithContent = true; + private boolean myIsWithContent = false; DiffViewTableModel() { super(new AbstractTableColumnDescriptor[]{ @@ -266,11 +266,6 @@ class ClassesTable extends JBTable { DiffValue update(long count) { return new DiffValue(count, count); } - - @Override - public String toString() { - return "?"; - } } private static class DiffValue { @@ -297,15 +292,5 @@ class ClassesTable extends JBTable { int diff() { return (int) (myCurrentCount - myOldCount); } - - @Override - public String toString() { - if (myOldCount == myCurrentCount) { - return String.valueOf(myOldCount); - } - - long diff = diff(); - return String.format("%d (%s%d)", myCurrentCount, diff > 0 ? "+" : "-", Math.abs(diff)); - } } } From 2ab97c7d97dce4737ad1a50abfc4ae8bf2e2e4f7 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 10 Aug 2016 15:01:39 +0300 Subject: [PATCH 005/558] IDEA-159603 Save history for filtering expression --- view/ExpressionEditorWithHistory.java | 75 +++++++++++++++++++++++++++ view/InstancesWindow.java | 28 +++++----- 2 files changed, 88 insertions(+), 15 deletions(-) create mode 100644 view/ExpressionEditorWithHistory.java diff --git a/view/ExpressionEditorWithHistory.java b/view/ExpressionEditorWithHistory.java new file mode 100644 index 000000000000..7894d61ec5e2 --- /dev/null +++ b/view/ExpressionEditorWithHistory.java @@ -0,0 +1,75 @@ +package org.jetbrains.debugger.memory.view; + +import com.intellij.codeInsight.lookup.LookupManager; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CustomShortcutSet; +import com.intellij.openapi.editor.ex.util.EditorUtil; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.PopupStep; +import com.intellij.openapi.ui.popup.util.BaseListPopupStep; +import com.intellij.ui.ColoredListCellRenderer; +import com.intellij.ui.popup.list.ListPopupImpl; +import com.intellij.xdebugger.XExpression; +import com.intellij.xdebugger.XSourcePosition; +import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; +import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl; +import com.intellij.xdebugger.impl.ui.XDebuggerExpressionEditor; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.List; + +class ExpressionEditorWithHistory extends XDebuggerExpressionEditor { + ExpressionEditorWithHistory(@NotNull Project project, + @NotNull XDebuggerEditorsProvider debuggerEditorsProvider, + @Nullable @NonNls String historyId, + @Nullable XSourcePosition sourcePosition, + @Nullable Disposable parentDisposable) { + super(project, debuggerEditorsProvider, historyId, sourcePosition, XExpressionImpl.EMPTY_EXPRESSION, + false, true, true); + + new AnAction("InstancesWindow.ShowHistory") { + @Override + public void actionPerformed(AnActionEvent e) { + showHistory(); + } + + @Override + public void update(AnActionEvent e) { + e.getPresentation().setEnabled(LookupManager.getActiveLookup(getEditor()) == null); + } + }.registerCustomShortcutSet(CustomShortcutSet.fromString("DOWN"), getComponent(), parentDisposable); + } + + private void showHistory() { + List expressions = getRecentExpressions(); + if (!expressions.isEmpty()) { + ListPopupImpl historyPopup = new ListPopupImpl(new BaseListPopupStep(null, expressions) { + @Override + public PopupStep onChosen(XExpression selectedValue, boolean finalChoice) { + setExpression(selectedValue); + requestFocusInEditor(); + return FINAL_CHOICE; + } + }) { + @Override + protected ListCellRenderer getListElementRenderer() { + return new ColoredListCellRenderer() { + @Override + protected void customizeCellRenderer(@NotNull JList list, XExpression value, int index, + boolean selected, boolean hasFocus) { + append(value.getExpression()); + } + }; + } + }; + + historyPopup.getList().setFont(EditorUtil.getEditorFont()); + historyPopup.showUnderneathOf(getEditorComponent()); + } + } +} diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 05d8fed6e311..5c56b551386e 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -40,7 +40,6 @@ import com.intellij.xdebugger.frame.presentation.XValuePresentation; import com.intellij.xdebugger.impl.XDebugSessionImpl; import com.intellij.xdebugger.impl.XSourcePositionImpl; import com.intellij.xdebugger.impl.actions.XDebuggerActionBase; -import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl; import com.intellij.xdebugger.impl.evaluate.quick.XDebuggerTreeCreator; import com.intellij.xdebugger.impl.frame.XValueMarkers; import com.intellij.xdebugger.impl.ui.XDebuggerExpressionEditor; @@ -56,10 +55,10 @@ import com.sun.jdi.ReferenceType; import com.sun.jdi.Value; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.java.debugger.JavaDebuggerEditorsProvider; import org.jetbrains.debugger.memory.utils.DebugCommand; import org.jetbrains.debugger.memory.utils.InstanceJavaValue; import org.jetbrains.debugger.memory.utils.InstanceValueDescriptor; +import org.jetbrains.java.debugger.JavaDebuggerEditorsProvider; import javax.swing.*; import javax.swing.tree.TreeNode; @@ -141,6 +140,7 @@ public class InstancesWindow extends DialogWrapper { } private class MyInstancesView extends JBPanel implements Disposable { + private static final String HISTORY_ID_PREFIX = "filtering"; private static final int MAX_TREE_NODE_COUNT = 1000; private static final int FILTERING_CHUNK_SIZE = 30; @@ -168,8 +168,8 @@ public class InstancesWindow extends DialogWrapper { myDebugSession.addSessionListener(myDebugSessionListener, InstancesWindow.this.myDisposable); JavaDebuggerEditorsProvider editorsProvider = new JavaDebuggerEditorsProvider(); - myFilterConditionEditor = new XDebuggerExpressionEditor(myProject, editorsProvider, "FilterExpression", - null, XExpressionImpl.EMPTY_EXPRESSION, false, true, true); + myFilterConditionEditor = new ExpressionEditorWithHistory(myProject, editorsProvider, + HISTORY_ID_PREFIX + myReferenceType.name(), null, InstancesWindow.this.myDisposable); new SwingWorker() { @Override protected Void doInBackground() throws Exception { @@ -225,13 +225,17 @@ public class InstancesWindow extends DialogWrapper { myInstancesTree.getRoot().setLeaf(false); myInstancesTree.setExpandableItemsEnabled(true); - myFilterButton.addActionListener(e -> - myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree))); + myFilterButton.addActionListener(e -> { + String expression = myFilterConditionEditor.getExpression().getExpression(); + if(!expression.isEmpty()) { + myFilterConditionEditor.saveTextInHistory(); + } + myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree)); + }); JBScrollPane treeScrollPane = new JBScrollPane(myInstancesTree); add(filteringPane, BorderLayout.NORTH); add(treeScrollPane, BorderLayout.CENTER); - } @Override @@ -350,14 +354,6 @@ public class InstancesWindow extends DialogWrapper { } private class MyActionListener extends AnActionListener.Adapter { - @Override - public void afterActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) { - if (dataContext.getData(PlatformDataKeys.CONTEXT_COMPONENT) == myInstancesView.myInstancesTree && - (isAddToWatchesAction(action) || isEvaluateExpressionAction(action))) { - myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree)); - } - } - @Override public void beforeActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) { if (dataContext.getData(PlatformDataKeys.CONTEXT_COMPONENT) == myInstancesView.myInstancesTree && @@ -379,6 +375,8 @@ public class InstancesWindow extends DialogWrapper { markers.markValue(valueContainer, new ValueMarkup(expression.replace("@", ""), new JBColor(0, 0), null)); } + + myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree)); } } } From 2c22abb615b967958c805531593dba999e04c2ed Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 9 Aug 2016 16:58:55 +0300 Subject: [PATCH 006/558] Disable grid in classes table --- view/ClassesTable.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index f2c4c67b81e9..cfd08d70b597 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -62,6 +62,8 @@ class ClassesTable extends JBTable { diffColumn.setMaxWidth(JBUI.scale(DIFF_COLUMN_MAX_WIDTH)); diffColumn.setResizable(false); + setShowGrid(false); + setDefaultRenderer(ReferenceType.class, new ColoredTableCellRenderer() { @Override protected void customizeCellRenderer(JTable table, @Nullable Object value, From 5304dfd04873d48cf54c0c4660a4ab2e8bd67919 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 9 Aug 2016 17:12:23 +0300 Subject: [PATCH 007/558] Add action for popup menu --- action/ShowInstancesFromClassesViewAction.java | 10 ++++++++++ view/ClassesTable.java | 11 +++++++++++ 2 files changed, 21 insertions(+) create mode 100644 action/ShowInstancesFromClassesViewAction.java diff --git a/action/ShowInstancesFromClassesViewAction.java b/action/ShowInstancesFromClassesViewAction.java new file mode 100644 index 000000000000..272b9d8625e2 --- /dev/null +++ b/action/ShowInstancesFromClassesViewAction.java @@ -0,0 +1,10 @@ +package org.jetbrains.debugger.memory.action; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; + +public class ShowInstancesFromClassesViewAction extends AnAction { + @Override + public void actionPerformed(AnActionEvent e) { + } +} diff --git a/view/ClassesTable.java b/view/ClassesTable.java index cfd08d70b597..5a81dcc8a286 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -12,6 +12,8 @@ import org.jetbrains.debugger.memory.utils.AbstractTableModelWithColumns; import javax.swing.*; import javax.swing.table.TableColumn; import javax.swing.table.TableRowSorter; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -64,6 +66,15 @@ class ClassesTable extends JBTable { setShowGrid(false); + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if(SwingUtilities.isRightMouseButton(e) && e.getClickCount() == 1) { + System.out.println("right clicked"); + } + } + }); + setDefaultRenderer(ReferenceType.class, new ColoredTableCellRenderer() { @Override protected void customizeCellRenderer(JTable table, @Nullable Object value, From cfc4a639ee704e04c8ea6494f5a0a4cef84b9467 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 10 Aug 2016 16:09:22 +0300 Subject: [PATCH 008/558] IDEA-159449 Show popup on right click on class --- view/ClassesFilteredView.java | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 8325b38bfe50..89a83e9f640d 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -6,6 +6,9 @@ import com.intellij.debugger.engine.DebuggerManagerThreadImpl; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; import com.intellij.debugger.impl.DebuggerManagerImpl; +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.ActionPopupMenu; +import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.ui.*; @@ -14,16 +17,17 @@ import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; import com.sun.jdi.ReferenceType; import com.sun.jdi.VirtualMachine; -import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; -import org.jetbrains.debugger.memory.component.MemoryViewManager; -import org.jetbrains.debugger.memory.component.MemoryViewManagerState; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.component.MemoryViewManager; +import org.jetbrains.debugger.memory.component.MemoryViewManagerState; +import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; import org.jetbrains.debugger.memory.utils.KeyboardUtils; import org.jetbrains.debugger.memory.utils.SingleAlarmWithMutableDelay; import javax.swing.*; import javax.swing.event.DocumentEvent; +import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; @@ -123,6 +127,17 @@ public class ClassesFilteredView extends BorderLayoutPanel { } }); + myTable.addMouseListener(new PopupHandler() { + @Override + public void invokePopup(Component comp, int x, int y) { + DefaultActionGroup group = (DefaultActionGroup) ActionManager.getInstance().getAction("ClassesView.PopupActionGroup"); + ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu("ClassesView.PopupActionGroup", group); + if (popupMenu != null) { + popupMenu.getComponent().show(comp, x, y); + } + } + }); + JScrollPane scroll = ScrollPaneFactory.createScrollPane(myTable, SideBorder.TOP); addToTop(myFilterTextField); addToCenter(scroll); From 0678c79cba9a6f4452881c8c9a8f7b4401ea5d3f Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 10 Aug 2016 17:51:15 +0300 Subject: [PATCH 009/558] IDEA-159449 Open instances window after choosing item in popup menu --- action/ClassesActionBase.java | 35 +++++++++++++++++++ action/ShowInstancesByClassAction.java | 2 +- .../ShowInstancesFromClassesViewAction.java | 20 +++++++++-- view/ClassesFilteredView.java | 19 +++++----- view/ClassesTable.java | 15 ++++++-- view/InstancesWindow.java | 8 ++--- 6 files changed, 80 insertions(+), 19 deletions(-) create mode 100644 action/ClassesActionBase.java diff --git a/action/ClassesActionBase.java b/action/ClassesActionBase.java new file mode 100644 index 000000000000..5d8ce09b6441 --- /dev/null +++ b/action/ClassesActionBase.java @@ -0,0 +1,35 @@ +package org.jetbrains.debugger.memory.action; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.view.ClassesTable; + +import java.awt.*; + +abstract class ClassesActionBase extends AnAction { + @Override + public void update(AnActionEvent e) { + e.getPresentation().setEnabled(isEnabled(e)); + } + + @Override + public void actionPerformed(AnActionEvent e) { + perform(e); + } + + protected boolean isEnabled(AnActionEvent e) { + Project project = e.getProject(); + return project != null && !project.isDisposed(); + } + + protected abstract void perform(AnActionEvent e); + + @Nullable + ClassesTable getTable(AnActionEvent e) { + Component component = e.getData(PlatformDataKeys.CONTEXT_COMPONENT); + return component == null ? null : (ClassesTable) component; + } +} diff --git a/action/ShowInstancesByClassAction.java b/action/ShowInstancesByClassAction.java index 8a96fc9891d2..7bc2b3c19a8f 100644 --- a/action/ShowInstancesByClassAction.java +++ b/action/ShowInstancesByClassAction.java @@ -36,7 +36,7 @@ public class ShowInstancesByClassAction extends XDebuggerTreeActionBase { XDebugSession debugSession = XDebuggerManager.getInstance(project).getCurrentSession(); ObjectReference ref = getObjectReference(node); if(debugSession != null && ref != null){ - new InstancesWindow(project, debugSession, ref.referenceType()).show(); + new InstancesWindow(debugSession, ref.referenceType()).show(); } } } diff --git a/action/ShowInstancesFromClassesViewAction.java b/action/ShowInstancesFromClassesViewAction.java index 272b9d8625e2..5faf9bba1731 100644 --- a/action/ShowInstancesFromClassesViewAction.java +++ b/action/ShowInstancesFromClassesViewAction.java @@ -1,10 +1,24 @@ package org.jetbrains.debugger.memory.action; -import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.sun.jdi.ReferenceType; +import org.jetbrains.debugger.memory.view.ClassesTable; +import org.jetbrains.debugger.memory.view.InstancesWindow; -public class ShowInstancesFromClassesViewAction extends AnAction { +public class ShowInstancesFromClassesViewAction extends ClassesActionBase { @Override - public void actionPerformed(AnActionEvent e) { + protected boolean isEnabled(AnActionEvent e) { + return super.isEnabled(e); + } + + @Override + protected void perform(AnActionEvent e) { + ClassesTable classesView = getTable(e); + if(classesView != null){ + ReferenceType selectedClass = classesView.getSelectedClass(); + if(selectedClass != null) { + new InstancesWindow(classesView.getDebugSession(), selectedClass).show(); + } + } } } diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 89a83e9f640d..7c967fffc129 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -6,9 +6,9 @@ import com.intellij.debugger.engine.DebuggerManagerThreadImpl; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; import com.intellij.debugger.impl.DebuggerManagerImpl; +import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.ActionPopupMenu; -import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.ui.*; @@ -58,7 +58,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { MemoryViewManagerState memoryViewManagerState = MemoryViewManager.getInstance(myProject).getState(); - myTable = new ClassesTable(memoryViewManagerState.isShowWithDiffOnly, + myTable = new ClassesTable(myDebugSession, memoryViewManagerState.isShowWithDiffOnly, memoryViewManagerState.isShowWithInstancesOnly); myTable.addKeyListener(new KeyAdapter() { @@ -130,10 +130,9 @@ public class ClassesFilteredView extends BorderLayoutPanel { myTable.addMouseListener(new PopupHandler() { @Override public void invokePopup(Component comp, int x, int y) { - DefaultActionGroup group = (DefaultActionGroup) ActionManager.getInstance().getAction("ClassesView.PopupActionGroup"); - ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu("ClassesView.PopupActionGroup", group); - if (popupMenu != null) { - popupMenu.getComponent().show(comp, x, y); + ActionPopupMenu menu = createContextMenu(); + if (menu != null) { + menu.getComponent().show(comp, x, y); } } }); @@ -154,8 +153,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { private void handleClassSelection(@Nullable ReferenceType ref) { if (ref != null && myDebugSession.isSuspended()) { - InstancesWindow window = new InstancesWindow(myProject, myDebugSession, ref); - window.show(); + new InstancesWindow(myDebugSession, ref).show(); } } @@ -189,6 +187,11 @@ public class ClassesFilteredView extends BorderLayoutPanel { } } + private ActionPopupMenu createContextMenu() { + ActionGroup group = (ActionGroup) ActionManager.getInstance().getAction("ClassesView.PopupActionGroup"); + return ActionManager.getInstance().createActionPopupMenu("ClassesView.PopupActionGroup", group); + } + private void loadClassesAndCounts() { assert DebuggerManager.getInstance(myProject).isDebuggerManagerThread(); myDebugProcess.getVirtualMachineProxy(); diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 5a81dcc8a286..515c44581291 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -4,7 +4,9 @@ import com.intellij.icons.AllIcons; import com.intellij.ui.ColoredTableCellRenderer; import com.intellij.ui.table.JBTable; import com.intellij.util.ui.JBUI; +import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.utils.AbstractTableColumnDescriptor; import org.jetbrains.debugger.memory.utils.AbstractTableModelWithColumns; @@ -20,7 +22,7 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.ConcurrentHashMap; -class ClassesTable extends JBTable { +public class ClassesTable extends JBTable { private static final int CLASSES_COLUMN_PREFERRED_WIDTH = 250; private static final int COUNT_COLUMN_MIN_WIDTH = 80; private static final int COUNT_COLUMN_MAX_WIDTH = 100; @@ -31,6 +33,7 @@ class ClassesTable extends JBTable { private final DiffViewTableModel myModel = new DiffViewTableModel(); private final UnknownDiffValue myUnknownValue = new UnknownDiffValue(); + private final XDebugSession myDebugSession; private boolean myOnlyWithDiff; private boolean myOnlyWithInstances; @@ -40,9 +43,10 @@ class ClassesTable extends JBTable { private volatile List myElems = Collections.unmodifiableList(new ArrayList<>()); - ClassesTable(boolean onlyWithDiff, boolean onlyWithInstances) { + ClassesTable(@NotNull XDebugSession session, boolean onlyWithDiff, boolean onlyWithInstances) { setModel(myModel); + myDebugSession = session; myOnlyWithDiff = onlyWithDiff; myOnlyWithInstances = onlyWithInstances; @@ -126,8 +130,13 @@ class ClassesTable extends JBTable { setSelectionMode(ListSelectionModel.SINGLE_SELECTION); } + @NotNull + public XDebugSession getDebugSession() { + return myDebugSession; + } + @Nullable - ReferenceType getSelectedClass() { + public ReferenceType getSelectedClass() { int selectedRow = getSelectedRow(); if (selectedRow != -1) { int ix = convertRowIndexToModel(selectedRow); diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 5c56b551386e..27b88adf8c57 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -81,11 +81,11 @@ public class InstancesWindow extends DialogWrapper { private final ReferenceType myReferenceType; private MyInstancesView myInstancesView; - public InstancesWindow(@NotNull Project project, - @NotNull XDebugSession session, + public InstancesWindow(@NotNull XDebugSession session, @NotNull ReferenceType referenceType) { - super(project, false); - myProject = project; + super(session.getProject(), false); + + myProject = session.getProject(); myDebugSession = session; myReferenceType = referenceType; setTitle("Instances of " + referenceType.name()); From 8a119d6e3e667e960bcca0ba3cf148c8c2268de5 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 10 Aug 2016 18:45:13 +0300 Subject: [PATCH 010/558] IDEA-159449 Review fixes --- action/ClassesActionBase.java | 15 +++++--- .../ShowInstancesFromClassesViewAction.java | 12 +++--- view/ClassesTable.java | 38 ++++++++++--------- 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/action/ClassesActionBase.java b/action/ClassesActionBase.java index 5d8ce09b6441..51e2c3a550a5 100644 --- a/action/ClassesActionBase.java +++ b/action/ClassesActionBase.java @@ -2,13 +2,12 @@ package org.jetbrains.debugger.memory.action; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.project.Project; +import com.intellij.xdebugger.XDebugSession; +import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.view.ClassesTable; -import java.awt.*; - abstract class ClassesActionBase extends AnAction { @Override public void update(AnActionEvent e) { @@ -28,8 +27,12 @@ abstract class ClassesActionBase extends AnAction { protected abstract void perform(AnActionEvent e); @Nullable - ClassesTable getTable(AnActionEvent e) { - Component component = e.getData(PlatformDataKeys.CONTEXT_COMPONENT); - return component == null ? null : (ClassesTable) component; + ReferenceType getSelectedClass(AnActionEvent e) { + return e.getData(ClassesTable.SELECTED_CLASS_KEY); + } + + @Nullable + XDebugSession getDebugSession(AnActionEvent e) { + return e.getData(ClassesTable.DEBUG_SESSION_KEY); } } diff --git a/action/ShowInstancesFromClassesViewAction.java b/action/ShowInstancesFromClassesViewAction.java index 5faf9bba1731..9034d583091a 100644 --- a/action/ShowInstancesFromClassesViewAction.java +++ b/action/ShowInstancesFromClassesViewAction.java @@ -1,8 +1,8 @@ package org.jetbrains.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; -import org.jetbrains.debugger.memory.view.ClassesTable; import org.jetbrains.debugger.memory.view.InstancesWindow; public class ShowInstancesFromClassesViewAction extends ClassesActionBase { @@ -13,12 +13,10 @@ public class ShowInstancesFromClassesViewAction extends ClassesActionBase { @Override protected void perform(AnActionEvent e) { - ClassesTable classesView = getTable(e); - if(classesView != null){ - ReferenceType selectedClass = classesView.getSelectedClass(); - if(selectedClass != null) { - new InstancesWindow(classesView.getDebugSession(), selectedClass).show(); - } + XDebugSession debugSession = getDebugSession(e); + ReferenceType selectedClass = getSelectedClass(e); + if (debugSession != null && selectedClass != null) { + new InstancesWindow(debugSession, selectedClass).show(); } } } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 515c44581291..9c4834096abe 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -1,11 +1,14 @@ package org.jetbrains.debugger.memory.view; import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.DataKey; +import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.ui.ColoredTableCellRenderer; import com.intellij.ui.table.JBTable; import com.intellij.util.ui.JBUI; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.utils.AbstractTableColumnDescriptor; @@ -14,15 +17,15 @@ import org.jetbrains.debugger.memory.utils.AbstractTableModelWithColumns; import javax.swing.*; import javax.swing.table.TableColumn; import javax.swing.table.TableRowSorter; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.ConcurrentHashMap; -public class ClassesTable extends JBTable { +public class ClassesTable extends JBTable implements DataProvider { + public static final DataKey SELECTED_CLASS_KEY = DataKey.create("ClassesTable.SelectedClass"); + public static final DataKey DEBUG_SESSION_KEY = DataKey.create("ClassesTable.DebugSession"); private static final int CLASSES_COLUMN_PREFERRED_WIDTH = 250; private static final int COUNT_COLUMN_MIN_WIDTH = 80; private static final int COUNT_COLUMN_MAX_WIDTH = 100; @@ -70,15 +73,6 @@ public class ClassesTable extends JBTable { setShowGrid(false); - addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if(SwingUtilities.isRightMouseButton(e) && e.getClickCount() == 1) { - System.out.println("right clicked"); - } - } - }); - setDefaultRenderer(ReferenceType.class, new ColoredTableCellRenderer() { @Override protected void customizeCellRenderer(JTable table, @Nullable Object value, @@ -130,13 +124,8 @@ public class ClassesTable extends JBTable { setSelectionMode(ListSelectionModel.SINGLE_SELECTION); } - @NotNull - public XDebugSession getDebugSession() { - return myDebugSession; - } - @Nullable - public ReferenceType getSelectedClass() { + ReferenceType getSelectedClass() { int selectedRow = getSelectedRow(); if (selectedRow != -1) { int ix = convertRowIndexToModel(selectedRow); @@ -212,6 +201,19 @@ public class ClassesTable extends JBTable { return i == pattern.length(); } + @Nullable + @Override + public Object getData(@NonNls String dataId) { + if(SELECTED_CLASS_KEY.is(dataId)) { + return getSelectedClass(); + } + if(DEBUG_SESSION_KEY.is(dataId)) { + return myDebugSession; + } + + return null; + } + private class DiffViewTableModel extends AbstractTableModelWithColumns { private final static int CLASSNAME_COLUMN_INDEX = 0; private final static int COUNT_COLUMN_INDEX = 1; From 76c0040d7451c1db16955e83fe2dca0c99a242f3 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 11 Aug 2016 12:26:32 +0300 Subject: [PATCH 011/558] Save settings of toolwindow per application --- action/ShowClassesWithDiff.java | 5 ++--- action/ShowClassesWithInstanceAction.java | 5 ++--- component/MemoryViewManager.java | 19 ++++++++----------- toolwindow/MemoryViewCondition.java | 2 +- toolwindow/MemoryViewToolWindowFactory.java | 2 +- view/ClassesFilteredView.java | 6 +++--- 6 files changed, 17 insertions(+), 22 deletions(-) diff --git a/action/ShowClassesWithDiff.java b/action/ShowClassesWithDiff.java index 78081dc76baf..8ca30ec247b8 100644 --- a/action/ShowClassesWithDiff.java +++ b/action/ShowClassesWithDiff.java @@ -8,15 +8,14 @@ import org.jetbrains.debugger.memory.component.MemoryViewManager; public class ShowClassesWithDiff extends ToggleAction { @Override public boolean isSelected(AnActionEvent e) { - Project project = e.getProject(); - return project != null && MemoryViewManager.getInstance(e.getProject()).isNeedShowDiffOnly(); + return MemoryViewManager.getInstance().isNeedShowDiffOnly(); } @Override public void setSelected(AnActionEvent e, boolean state) { Project project = e.getProject(); if (project != null) { - MemoryViewManager.getInstance(project).setShowDiffOnly(state); + MemoryViewManager.getInstance().setShowDiffOnly(state); } } } diff --git a/action/ShowClassesWithInstanceAction.java b/action/ShowClassesWithInstanceAction.java index 9e30ffda8230..17681f97dfb9 100644 --- a/action/ShowClassesWithInstanceAction.java +++ b/action/ShowClassesWithInstanceAction.java @@ -8,15 +8,14 @@ import org.jetbrains.debugger.memory.component.MemoryViewManager; public class ShowClassesWithInstanceAction extends ToggleAction { @Override public boolean isSelected(AnActionEvent e) { - Project project = e.getProject(); - return project != null && MemoryViewManager.getInstance(project).isNeedShowInstancesOnly(); + return MemoryViewManager.getInstance().isNeedShowInstancesOnly(); } @Override public void setSelected(AnActionEvent e, boolean state) { Project project = e.getProject(); if (project != null) { - MemoryViewManager.getInstance(project).setShowWithInstancesOnly(state); + MemoryViewManager.getInstance().setShowWithInstancesOnly(state); } } } diff --git a/component/MemoryViewManager.java b/component/MemoryViewManager.java index d7de9e532c95..d6d0f93c42d8 100644 --- a/component/MemoryViewManager.java +++ b/component/MemoryViewManager.java @@ -1,27 +1,24 @@ package org.jetbrains.debugger.memory.component; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.util.EventDispatcher; +import org.jetbrains.annotations.NotNull; import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; import org.jetbrains.debugger.memory.toolwindow.MemoryViewToolWindowFactory; -import org.jetbrains.annotations.NotNull; -@State(name = "MemoryViewSettings", storages = @Storage(StoragePathMacros.WORKSPACE_FILE)) -public class MemoryViewManager extends AbstractProjectComponent +@State(name = "MemoryViewSettings", storages = @Storage("memory.view.xml")) +public class MemoryViewManager extends ApplicationComponent.Adapter implements PersistentStateComponent { private final EventDispatcher myDispatcher = EventDispatcher.create(MemoryViewManagerListener.class); private MemoryViewManagerState myState = new MemoryViewManagerState(); - public static MemoryViewManager getInstance(Project project) { - return project.getComponent(MemoryViewManager.class); - } - - protected MemoryViewManager(Project project) { - super(project); + public static MemoryViewManager getInstance() { + return ApplicationManager.getApplication().getComponent(MemoryViewManager.class); } @NotNull @@ -70,8 +67,8 @@ public class MemoryViewManager extends AbstractProjectComponent myDispatcher.removeListener(listener); } - public ToolWindow getToolWindow() { - return ToolWindowManager.getInstance(myProject).getToolWindow(MemoryViewToolWindowFactory.TOOL_WINDOW_ID); + public ToolWindow getToolWindow(Project project) { + return ToolWindowManager.getInstance(project).getToolWindow(MemoryViewToolWindowFactory.TOOL_WINDOW_ID); } private void fireStateChanged() { diff --git a/toolwindow/MemoryViewCondition.java b/toolwindow/MemoryViewCondition.java index 9bcf07002d4f..e50a25d2decf 100644 --- a/toolwindow/MemoryViewCondition.java +++ b/toolwindow/MemoryViewCondition.java @@ -33,7 +33,7 @@ public class MemoryViewCondition implements Condition { } private void updateIcon(@NotNull Project project, boolean enabled) { - ToolWindow toolWindow = MemoryViewManager.getInstance(project).getToolWindow(); + ToolWindow toolWindow = MemoryViewManager.getInstance().getToolWindow(project); if (toolWindow != null) { SwingUtilities.invokeLater(() -> toolWindow.setIcon(enabled ? MemoryViewIcons.MAIN_ENABLED : MemoryViewIcons.MAIN_DISABLED)); diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java index f258eb633050..1d8807bbaabd 100644 --- a/toolwindow/MemoryViewToolWindowFactory.java +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -117,7 +117,7 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware @Nullable private ToolWindow getToolWindow(@NotNull Project project) { - return MemoryViewManager.getInstance(project).getToolWindow(); + return MemoryViewManager.getInstance().getToolWindow(project); } private final class MyDebuggerStatusChangedListener implements XDebuggerManagerListener { diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 7c967fffc129..d17e096e46cd 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -56,7 +56,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { myDebugProcess = DebuggerManager.getInstance(myProject) .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); - MemoryViewManagerState memoryViewManagerState = MemoryViewManager.getInstance(myProject).getState(); + MemoryViewManagerState memoryViewManagerState = MemoryViewManager.getInstance().getState(); myTable = new ClassesTable(myDebugSession, memoryViewManagerState.isShowWithDiffOnly, memoryViewManagerState.isShowWithInstancesOnly); @@ -88,7 +88,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { myTable.setFilteringByInstanceExists(state.isShowWithInstancesOnly); }; - MemoryViewManager.getInstance(myProject).addMemoryViewManagerListener(memoryViewManagerListener); + MemoryViewManager.getInstance().addMemoryViewManagerListener(memoryViewManagerListener); myDebugSession.addSessionListener(new XDebugSessionListener() { @Override @@ -99,7 +99,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { @Override public void sessionStopped() { debugSession.removeSessionListener(this); - MemoryViewManager.getInstance(myProject).removeMemoryViewManagerListener(memoryViewManagerListener); + MemoryViewManager.getInstance().removeMemoryViewManagerListener(memoryViewManagerListener); } @Override From 1784b2cf75c749307bc9bfbf30ce9867d0a7ac52 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 11 Aug 2016 13:15:46 +0300 Subject: [PATCH 012/558] Remove redundant invokeLater call --- toolwindow/MemoryViewToolWindowFactory.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java index 1d8807bbaabd..ae43aaebf52f 100644 --- a/toolwindow/MemoryViewToolWindowFactory.java +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -61,9 +61,8 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware @Override public void stateChanged() { if (!myMemoryViews.isEmpty()) { - SwingUtilities.invokeLater(() -> - myMemoryViews.values().forEach(classesFilteredView -> - classesFilteredView.setNeedReloadClasses(toolWindow.isVisible()))); + myMemoryViews.values().forEach(classesFilteredView -> + classesFilteredView.setNeedReloadClasses(toolWindow.isVisible())); } } }); From d1dc2ee62a61d4af71b8f24ba7750b6cd9e1cdc9 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 11 Aug 2016 13:29:21 +0300 Subject: [PATCH 013/558] Run updating of classes after toolwindow was opened only when session has been continued --- view/ClassesFilteredView.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index d17e096e46cd..43a02b99e23e 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -46,6 +46,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { private final SearchTextField myFilterTextField = new SearchTextField(false); private final ClassesTable myTable; + private volatile SuspendContextImpl myLastSuspendContext; private boolean myNeedReloadClasses = false; public ClassesFilteredView(@NotNull XDebugSession debugSession) { @@ -118,12 +119,12 @@ public class ClassesFilteredView extends BorderLayoutPanel { }); mySingleAlarm = new SingleAlarmWithMutableDelay(() -> { - SuspendContextImpl suspendContext = getSuspendContext(); - if (suspendContext != null) { + myLastSuspendContext = getSuspendContext(); + if (myLastSuspendContext != null) { SwingUtilities.invokeLater(() -> myTable.setBusy(true)); ((DebuggerManagerThreadImpl) myDebugProcess.getManagerThread()) - .schedule(new MyUpdateClassesCommand(suspendContext)); + .schedule(new MyUpdateClassesCommand(myLastSuspendContext)); } }); @@ -146,7 +147,10 @@ public class ClassesFilteredView extends BorderLayoutPanel { if (myNeedReloadClasses != value) { myNeedReloadClasses = value; if (myNeedReloadClasses) { - updateClassesAndCounts(); + SuspendContextImpl suspendContext = getSuspendContext(); + if (suspendContext != null && !suspendContext.equals(myLastSuspendContext)) { + updateClassesAndCounts(); + } } } } From cc6d07b69f3595e770ae7f332cbc52ee821881ef Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 11 Aug 2016 13:43:19 +0300 Subject: [PATCH 014/558] Make condition for ToolWindowFactory as its inner class --- toolwindow/MemoryViewCondition.java | 45 --------------------- toolwindow/MemoryViewToolWindowFactory.java | 36 ++++++++++++++++- 2 files changed, 34 insertions(+), 47 deletions(-) delete mode 100644 toolwindow/MemoryViewCondition.java diff --git a/toolwindow/MemoryViewCondition.java b/toolwindow/MemoryViewCondition.java deleted file mode 100644 index e50a25d2decf..000000000000 --- a/toolwindow/MemoryViewCondition.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.jetbrains.debugger.memory.toolwindow; - -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; -import com.intellij.openapi.wm.ToolWindow; -import com.intellij.util.messages.MessageBusConnection; -import com.intellij.xdebugger.XDebugProcess; -import com.intellij.xdebugger.XDebuggerManager; -import com.intellij.xdebugger.XDebuggerManagerListener; -import icons.MemoryViewIcons; -import org.jetbrains.debugger.memory.component.MemoryViewManager; -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; -import java.util.Arrays; - -public class MemoryViewCondition implements Condition { - @Override - public boolean value(Project project) { - MessageBusConnection connection = project.getMessageBus().connect(project); - connection.subscribe(XDebuggerManager.TOPIC, new XDebuggerManagerListener() { - @Override - public void processStarted(@NotNull XDebugProcess debugProcess) { - updateIcon(debugProcess.getSession().getProject(), true); - } - - @Override - public void processStopped(@NotNull XDebugProcess debugProcess) { - Project project = debugProcess.getSession().getProject(); - boolean enabled = Arrays.stream(XDebuggerManager.getInstance(project) - .getDebugSessions()).anyMatch(session -> !session.getDebugProcess().equals(debugProcess)); - updateIcon(project, enabled); - } - - private void updateIcon(@NotNull Project project, boolean enabled) { - ToolWindow toolWindow = MemoryViewManager.getInstance().getToolWindow(project); - if (toolWindow != null) { - SwingUtilities.invokeLater(() -> - toolWindow.setIcon(enabled ? MemoryViewIcons.MAIN_ENABLED : MemoryViewIcons.MAIN_DISABLED)); - } - } - }); - return true; - } -} diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java index ae43aaebf52f..9260030988f1 100644 --- a/toolwindow/MemoryViewToolWindowFactory.java +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -21,13 +21,15 @@ import com.intellij.xdebugger.XDebugProcess; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebuggerManager; import com.intellij.xdebugger.XDebuggerManagerListener; -import org.jetbrains.debugger.memory.view.ClassesFilteredView; -import org.jetbrains.debugger.memory.component.MemoryViewManager; +import icons.MemoryViewIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.component.MemoryViewManager; +import org.jetbrains.debugger.memory.view.ClassesFilteredView; import javax.swing.*; import java.awt.*; +import java.util.Arrays; public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware { public final static String TOOL_WINDOW_ID = "Memory View"; @@ -119,6 +121,36 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware return MemoryViewManager.getInstance().getToolWindow(project); } + public static class Condition implements com.intellij.openapi.util.Condition { + @Override + public boolean value(Project project) { + MessageBusConnection connection = project.getMessageBus().connect(project); + connection.subscribe(XDebuggerManager.TOPIC, new XDebuggerManagerListener() { + @Override + public void processStarted(@NotNull XDebugProcess debugProcess) { + updateIcon(debugProcess.getSession().getProject(), true); + } + + @Override + public void processStopped(@NotNull XDebugProcess debugProcess) { + Project project = debugProcess.getSession().getProject(); + boolean enabled = Arrays.stream(XDebuggerManager.getInstance(project) + .getDebugSessions()).anyMatch(session -> !session.getDebugProcess().equals(debugProcess)); + updateIcon(project, enabled); + } + + private void updateIcon(@NotNull Project project, boolean enabled) { + ToolWindow toolWindow = MemoryViewManager.getInstance().getToolWindow(project); + if (toolWindow != null) { + SwingUtilities.invokeLater(() -> + toolWindow.setIcon(enabled ? MemoryViewIcons.MAIN_ENABLED : MemoryViewIcons.MAIN_DISABLED)); + } + } + }); + return true; + } + } + private final class MyDebuggerStatusChangedListener implements XDebuggerManagerListener { @Override public void processStarted(@NotNull XDebugProcess xDebugProcess) { From 82398a3abd529537cd4b2c4d1218e03c38b99dea Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 11 Aug 2016 18:01:55 +0300 Subject: [PATCH 015/558] Rename dimension service key for instances window --- view/InstancesWindow.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 27b88adf8c57..790c9e9368aa 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -104,7 +104,7 @@ public class InstancesWindow extends DialogWrapper { @NotNull @Override protected String getDimensionServiceKey() { - return "#org.jetbrains.plugin.view.InstancesWindow"; + return "#org.jetbrains.debugger.memory.view.InstancesWindow"; } @Nullable From ba07eeb58c266c1dd2870792de8425873aaa7eff Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 11 Aug 2016 18:02:17 +0300 Subject: [PATCH 016/558] Annotate getToolWindow method in manager --- component/MemoryViewManager.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/component/MemoryViewManager.java b/component/MemoryViewManager.java index d6d0f93c42d8..42949b2bc59c 100644 --- a/component/MemoryViewManager.java +++ b/component/MemoryViewManager.java @@ -7,6 +7,7 @@ import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.util.EventDispatcher; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; import org.jetbrains.debugger.memory.toolwindow.MemoryViewToolWindowFactory; @@ -67,6 +68,7 @@ public class MemoryViewManager extends ApplicationComponent.Adapter myDispatcher.removeListener(listener); } + @Nullable public ToolWindow getToolWindow(Project project) { return ToolWindowManager.getInstance(project).getToolWindow(MemoryViewToolWindowFactory.TOOL_WINDOW_ID); } From 33e9abf776e58d49b2e3d03719ed24191f16159d Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 11 Aug 2016 18:11:37 +0300 Subject: [PATCH 017/558] Small refactor InstancesWindow class --- utils/DebugCommand.java | 9 ---- view/InstancesWindow.java | 105 ++++++++++++++++++++++---------------- 2 files changed, 60 insertions(+), 54 deletions(-) delete mode 100644 utils/DebugCommand.java diff --git a/utils/DebugCommand.java b/utils/DebugCommand.java deleted file mode 100644 index 3076fb3d3ebc..000000000000 --- a/utils/DebugCommand.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.jetbrains.debugger.memory.utils; - -import com.intellij.debugger.engine.managerThread.DebuggerCommand; - -public interface DebugCommand extends DebuggerCommand { - @Override - default void commandCancelled() { - } -} diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 790c9e9368aa..640f3e0c031c 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -1,7 +1,10 @@ package org.jetbrains.debugger.memory.view; import com.intellij.debugger.DebuggerManager; -import com.intellij.debugger.engine.*; +import com.intellij.debugger.engine.DebugProcessImpl; +import com.intellij.debugger.engine.DebuggerUtils; +import com.intellij.debugger.engine.JavaValue; +import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.EvaluationContext; import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; @@ -55,7 +58,6 @@ import com.sun.jdi.ReferenceType; import com.sun.jdi.Value; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.utils.DebugCommand; import org.jetbrains.debugger.memory.utils.InstanceJavaValue; import org.jetbrains.debugger.memory.utils.InstanceValueDescriptor; import org.jetbrains.java.debugger.JavaDebuggerEditorsProvider; @@ -104,7 +106,7 @@ public class InstancesWindow extends DialogWrapper { @NotNull @Override protected String getDimensionServiceKey() { - return "#org.jetbrains.debugger.memory.view.InstancesWindow"; + return "#org.jetbrains.plugin.view.InstancesWindow"; } @Nullable @@ -170,18 +172,7 @@ public class InstancesWindow extends DialogWrapper { myFilterConditionEditor = new ExpressionEditorWithHistory(myProject, editorsProvider, HISTORY_ID_PREFIX + myReferenceType.name(), null, InstancesWindow.this.myDisposable); - new SwingWorker() { - @Override - protected Void doInBackground() throws Exception { - ApplicationManager.getApplication().runReadAction(() -> { - final PsiClass psiClass = DebuggerUtils.findClass(myReferenceType.name(), - myProject, GlobalSearchScope.allScope(myProject)); - XSourcePositionImpl position = XSourcePositionImpl.createByElement(psiClass); - SwingUtilities.invokeLater(() -> myFilterConditionEditor.setSourcePosition(position)); - }); - return null; - } - }.execute(); + setSourcePositionForEditor(); myFilterButton.setBorder(BorderFactory.createEmptyBorder()); Dimension progressbarPreferredSize = myFilterConditionEditor.getEditorComponent().getPreferredSize(); @@ -209,25 +200,15 @@ public class InstancesWindow extends DialogWrapper { XDebuggerTreeCreator treeCreator = new XDebuggerTreeCreator(myProject, editorsProvider, null, markers); - myInstancesTree = (XDebuggerTree) treeCreator.createTree(Pair.pair(new XNamedValue("root") { - @Override - public void computeChildren(@NotNull XCompositeNode node) { - updateInstances(); - } - - @Override - public void computePresentation(@NotNull XValueNode xValueNode, @NotNull XValuePlace xValuePlace) { - xValueNode.setPresentation(null, "", "", true); - } - }, "root")); + myInstancesTree = (XDebuggerTree) treeCreator.createTree(getTreeRootDescriptor()); myInstancesTree.setRootVisible(false); myInstancesTree.getRoot().setLeaf(false); myInstancesTree.setExpandableItemsEnabled(true); myFilterButton.addActionListener(e -> { String expression = myFilterConditionEditor.getExpression().getExpression(); - if(!expression.isEmpty()) { + if (!expression.isEmpty()) { myFilterConditionEditor.saveTextInHistory(); } myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree)); @@ -247,20 +228,28 @@ public class InstancesWindow extends DialogWrapper { } private void updateInstances() { - DebugProcess debugProcess = DebuggerManager.getInstance(myProject) + DebugProcessImpl debugProcess = (DebugProcessImpl) DebuggerManager.getInstance(myProject) .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); cancelFilteringTask(); - debugProcess.getManagerThread().invokeCommand((DebugCommand) () -> { - List instances = myReferenceType.instances(0); - EvaluationContextImpl evaluationContext = ((DebugProcessImpl) debugProcess) - .getDebuggerContext().createEvaluationContext(); + debugProcess.getManagerThread().schedule(new DebuggerContextCommandImpl(debugProcess.getDebuggerContext()) { + @Override + public Priority getPriority() { + return Priority.LOWEST; + } - if(evaluationContext != null) { - synchronized (myFilteringTaskLock) { - myFilteringTask = new MyFilteringWorker(instances, evaluationContext, createEvaluator()); - myFilteringTask.execute(); + @Override + public void threadAction(@NotNull SuspendContextImpl suspendContext) { + List instances = myReferenceType.instances(0); + EvaluationContextImpl evaluationContext = debugProcess + .getDebuggerContext().createEvaluationContext(); + + if(evaluationContext != null) { + synchronized (myFilteringTaskLock) { + myFilteringTask = new MyFilteringWorker(instances, evaluationContext, createEvaluator()); + myFilteringTask.execute(); + } } } }); @@ -284,7 +273,7 @@ public class InstancesWindow extends DialogWrapper { if (expression != null && !expression.getExpression().isEmpty()) { try { myEvaluator.setReferenceExpression(TextWithImportsImpl. - fromXExpression(myFilterConditionEditor.getExpression())); + fromXExpression(expression)); evaluator = myEvaluator.getEvaluator(); } catch (EvaluateException ignored) { } @@ -310,6 +299,36 @@ public class InstancesWindow extends DialogWrapper { : null; } + private void setSourcePositionForEditor() { + new SwingWorker() { + @Override + protected Void doInBackground() throws Exception { + ApplicationManager.getApplication().runReadAction(() -> { + final PsiClass psiClass = DebuggerUtils.findClass(myReferenceType.name(), + myProject, GlobalSearchScope.allScope(myProject)); + XSourcePositionImpl position = XSourcePositionImpl.createByElement(psiClass); + SwingUtilities.invokeLater(() -> myFilterConditionEditor.setSourcePosition(position)); + }); + return null; + } + }.execute(); + } + + @NotNull + private Pair getTreeRootDescriptor() { + return Pair.pair(new XValue() { + @Override + public void computeChildren(@NotNull XCompositeNode node) { + updateInstances(); + } + + @Override + public void computePresentation(@NotNull XValueNode node, @NotNull XValuePlace place) { + node.setPresentation(null, "", "", true); + } + }, "root"); + } + private class MySessionListener implements XDebugSessionListener { private final XValueChildrenList myRunningAppChildNode = new XValueChildrenList(); @@ -339,6 +358,7 @@ public class InstancesWindow extends DialogWrapper { public void sessionResumed() { SwingUtilities.invokeLater(() -> { myTreeState = XDebuggerTreeState.saveState(myInstancesTree); + cancelFilteringTask(); myInstancesTree.getRoot().clearChildren(); ((XValueNodeImpl) myInstancesTree.getRoot()).addChildren(myRunningAppChildNode, true); }); @@ -346,10 +366,7 @@ public class InstancesWindow extends DialogWrapper { @Override public void sessionPaused() { - SwingUtilities.invokeLater(() -> { - myInstancesTree.setRootVisible(false); - myInstancesTree.rebuildAndRestore(myTreeState); - }); + SwingUtilities.invokeLater(() -> myInstancesTree.rebuildAndRestore(myTreeState)); } } @@ -408,7 +425,8 @@ public class InstancesWindow extends DialogWrapper { private final List myReferences; private final EvaluationContextImpl myEvaluationContext; private final ExpressionEvaluator myExpressionEvaluator; - private volatile boolean myDebuggerTaskCompleted; + + private volatile boolean myDebuggerTaskCompleted = false; MyFilteringWorker(@NotNull List refs, @NotNull EvaluationContextImpl evaluationContext, @@ -465,9 +483,6 @@ public class InstancesWindow extends DialogWrapper { } } }); - debugProcess.getManagerThread().invokeCommand((DebugCommand) () -> { - - }); synchronized (this) { while (!myDebuggerTaskCompleted) { From 02342c3d2b379d84fab5ef08c5128b21b40f2d89 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 11 Aug 2016 18:34:19 +0300 Subject: [PATCH 018/558] Fix race condition when old task cancelled, and new task starts executing --- view/InstancesWindow.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 640f3e0c031c..916fbc62755a 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -286,7 +286,7 @@ public class InstancesWindow extends DialogWrapper { if (myFilteringTask != null) { synchronized (myFilteringTaskLock) { if (myFilteringTask != null) { - myFilteringTask.cancel(false); + myFilteringTask.cancel(true); myFilteringTask = null; } } @@ -436,6 +436,12 @@ public class InstancesWindow extends DialogWrapper { myExpressionEvaluator = evaluator; } + @Override + protected void done() { + ((XValueNodeImpl) myInstancesTree.getRoot()).addChildren(XValueChildrenList.EMPTY, true); + hideProgressPane(); + } + @Override protected Void doInBackground() throws Exception { DebugProcessImpl debugProcess = (DebugProcessImpl) DebuggerManager.getInstance(myProject) @@ -484,9 +490,9 @@ public class InstancesWindow extends DialogWrapper { } }); - synchronized (this) { + synchronized (MyFilteringWorker.this) { while (!myDebuggerTaskCompleted) { - wait(); + MyFilteringWorker.this.wait(); } } if (totalChildren.get() >= MAX_TREE_NODE_COUNT) { @@ -494,11 +500,6 @@ public class InstancesWindow extends DialogWrapper { } } - SwingUtilities.invokeLater(() -> { - ((XValueNodeImpl) myInstancesTree.getRoot()).addChildren(XValueChildrenList.EMPTY, true); - hideProgressPane(); - }); - return null; } From 3e84814fa0448d67ee9ebdd4d622caf2072a243d Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 11 Aug 2016 19:25:44 +0300 Subject: [PATCH 019/558] Fix NPE when instances window closing while filtering is running --- view/InstancesWindow.java | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 916fbc62755a..e64f71e50236 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -51,6 +51,7 @@ import com.intellij.xdebugger.impl.ui.tree.ValueMarkup; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState; import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase; +import com.intellij.xdebugger.impl.ui.tree.nodes.XDebuggerTreeNode; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.sun.jdi.BooleanValue; import com.sun.jdi.ObjectReference; @@ -245,7 +246,7 @@ public class InstancesWindow extends DialogWrapper { EvaluationContextImpl evaluationContext = debugProcess .getDebuggerContext().createEvaluationContext(); - if(evaluationContext != null) { + if (evaluationContext != null) { synchronized (myFilteringTaskLock) { myFilteringTask = new MyFilteringWorker(instances, evaluationContext, createEvaluator()); myFilteringTask.execute(); @@ -299,6 +300,13 @@ public class InstancesWindow extends DialogWrapper { : null; } + private void addChildrenToTree(XValueChildrenList children, boolean last) { + XDebuggerTreeNode root = myInstancesTree.getRoot(); + if(root != null) { + ((XValueNodeImpl)root).addChildren(children, last); + } + } + private void setSourcePositionForEditor() { new SwingWorker() { @Override @@ -359,8 +367,12 @@ public class InstancesWindow extends DialogWrapper { SwingUtilities.invokeLater(() -> { myTreeState = XDebuggerTreeState.saveState(myInstancesTree); cancelFilteringTask(); - myInstancesTree.getRoot().clearChildren(); - ((XValueNodeImpl) myInstancesTree.getRoot()).addChildren(myRunningAppChildNode, true); + + XDebuggerTreeNode root = myInstancesTree.getRoot(); + if(root != null) { + root.clearChildren(); + addChildrenToTree(myRunningAppChildNode, true); + } }); } @@ -438,7 +450,7 @@ public class InstancesWindow extends DialogWrapper { @Override protected void done() { - ((XValueNodeImpl) myInstancesTree.getRoot()).addChildren(XValueChildrenList.EMPTY, true); + addChildrenToTree(XValueChildrenList.EMPTY, true); hideProgressPane(); } @@ -461,7 +473,7 @@ public class InstancesWindow extends DialogWrapper { @Override public void threadAction(@NotNull SuspendContextImpl suspendContext) { - XValueChildrenList children = new XValueChildrenList(FILTERING_CHUNK_SIZE); + XValueChildrenList children = new XValueChildrenList(); int endOfChunk = min(chunkBegin + FILTERING_CHUNK_SIZE, size); for (int j = chunkBegin; j < endOfChunk && !isCancelled() && totalChildren.get() < MAX_TREE_NODE_COUNT; j++) { @@ -477,8 +489,7 @@ public class InstancesWindow extends DialogWrapper { if (children.size() > 0) { totalChildren.addAndGet(children.size()); - SwingUtilities.invokeLater(() -> - ((XValueNodeImpl) myInstancesTree.getRoot()).addChildren(children, false)); + SwingUtilities.invokeLater(() -> addChildrenToTree(children, false)); } SwingUtilities.invokeLater(() -> myFilteringProgressBar.setValue(endOfChunk)); @@ -505,11 +516,12 @@ public class InstancesWindow extends DialogWrapper { private MyFilteringResult isSatisfy(@NotNull ExpressionEvaluator evaluator, @NotNull Value value) { try { - Value evaluate = evaluator.evaluate(myEvaluationContext.createEvaluationContext(value)); - if (evaluate instanceof BooleanValue && ((BooleanValue) evaluate).value()) { + Value result = evaluator.evaluate(myEvaluationContext.createEvaluationContext(value)); + if (result instanceof BooleanValue && ((BooleanValue) result).value()) { return MyFilteringResult.MATCH; } } catch (EvaluateException e) { + System.out.println(e.getMessage()); return MyFilteringResult.EVAL_ERROR; } From 99a587a82e4b80dd08d14c2cf01fea366aab2b18 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 12 Aug 2016 15:20:30 +0300 Subject: [PATCH 020/558] Fix bug with usage of project which already disposed --- toolwindow/MemoryViewToolWindowFactory.java | 13 ++++++++----- view/InstancesWindow.java | 9 +++++---- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java index 9260030988f1..88f425b3d312 100644 --- a/toolwindow/MemoryViewToolWindowFactory.java +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -62,12 +62,12 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware .addToolWindowManagerListener(new ToolWindowManagerAdapter() { @Override public void stateChanged() { - if (!myMemoryViews.isEmpty()) { + if (!myMemoryViews.isEmpty() && !toolWindow.isDisposed()) { myMemoryViews.values().forEach(classesFilteredView -> classesFilteredView.setNeedReloadClasses(toolWindow.isVisible())); } } - }); + }, project); ActionGroup group = new DefaultActionGroup( ActionManager.getInstance().getAction("MemoryView.ShowOnlyWithInstances"), @@ -171,9 +171,12 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware } private void updateView(@NotNull XDebugSession debugSession) { - ToolWindow toolWindow = getToolWindow(debugSession.getProject()); - if (toolWindow != null) { - updateCurrentMemoryView(debugSession.getProject(), toolWindow); + Project project = debugSession.getProject(); + if (!project.isDisposed()) { + ToolWindow toolWindow = getToolWindow(project); + if (toolWindow != null) { + updateCurrentMemoryView(project, toolWindow); + } } } } diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index e64f71e50236..8d8610d6fd1c 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -225,6 +225,7 @@ public class InstancesWindow extends DialogWrapper { ActionManager.getInstance().removeAnActionListener(myActionListener); myDebugSession.removeSessionListener(myDebugSessionListener); cancelFilteringTask(); + Disposer.dispose(myInstancesTree); } @@ -301,9 +302,9 @@ public class InstancesWindow extends DialogWrapper { } private void addChildrenToTree(XValueChildrenList children, boolean last) { - XDebuggerTreeNode root = myInstancesTree.getRoot(); - if(root != null) { - ((XValueNodeImpl)root).addChildren(children, last); + XDebuggerTreeNode root = myInstancesTree.getRoot(); + if (root != null) { + ((XValueNodeImpl) root).addChildren(children, last); } } @@ -369,7 +370,7 @@ public class InstancesWindow extends DialogWrapper { cancelFilteringTask(); XDebuggerTreeNode root = myInstancesTree.getRoot(); - if(root != null) { + if (root != null) { root.clearChildren(); addChildrenToTree(myRunningAppChildNode, true); } From 2bbabfb8289e51b6ecaf8a6ca2a82dd32fa7b7b0 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 12 Aug 2016 16:09:11 +0300 Subject: [PATCH 021/558] Remove a icon of class from classes table item --- view/ClassesTable.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 9c4834096abe..88a8af377fcb 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -1,9 +1,9 @@ package org.jetbrains.debugger.memory.view; -import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.DataKey; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.ui.ColoredTableCellRenderer; +import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.table.JBTable; import com.intellij.util.ui.JBUI; import com.intellij.xdebugger.XDebugSession; @@ -78,9 +78,7 @@ public class ClassesTable extends JBTable implements DataProvider { protected void customizeCellRenderer(JTable table, @Nullable Object value, boolean isSelected, boolean hasFocus, int row, int column) { String presentation = value == null ? "null" : ((ReferenceType) value).name(); - append(presentation); - setTransparentIconBackground(true); - setIcon(AllIcons.Nodes.Class); + append(String.format(" %s", presentation), SimpleTextAttributes.REGULAR_ATTRIBUTES); } }); From 5ea55355f47dd132dc3e3681529f4437e971e977 Mon Sep 17 00:00:00 2001 From: roenke Date: Mon, 15 Aug 2016 16:03:19 +0300 Subject: [PATCH 022/558] Small fixes before 0.0.8 --- view/ClassesFilteredView.java | 64 ++++++++++++++++++++--------------- view/ClassesTable.java | 6 ++-- view/InstancesWindow.java | 20 ++++++----- 3 files changed, 50 insertions(+), 40 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 43a02b99e23e..6652553c6d51 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -47,7 +47,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { private final ClassesTable myTable; private volatile SuspendContextImpl myLastSuspendContext; - private boolean myNeedReloadClasses = false; + private volatile boolean myNeedReloadClasses = false; public ClassesFilteredView(@NotNull XDebugSession debugSession) { super(); @@ -95,6 +95,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { @Override public void sessionResumed() { SwingUtilities.invokeLater(myTable::hideContent); + mySingleAlarm.cancelAllRequests(); } @Override @@ -121,10 +122,11 @@ public class ClassesFilteredView extends BorderLayoutPanel { mySingleAlarm = new SingleAlarmWithMutableDelay(() -> { myLastSuspendContext = getSuspendContext(); if (myLastSuspendContext != null) { - SwingUtilities.invokeLater(() -> myTable.setBusy(true)); - - ((DebuggerManagerThreadImpl) myDebugProcess.getManagerThread()) - .schedule(new MyUpdateClassesCommand(myLastSuspendContext)); + SwingUtilities.invokeLater(() -> { + myTable.setBusy(true); + ((DebuggerManagerThreadImpl) myDebugProcess.getManagerThread()) + .schedule(new MyUpdateClassesCommand(myLastSuspendContext)); + }); } }); @@ -171,7 +173,13 @@ public class ClassesFilteredView extends BorderLayoutPanel { } } + private ActionPopupMenu createContextMenu() { + ActionGroup group = (ActionGroup) ActionManager.getInstance().getAction("ClassesView.PopupActionGroup"); + return ActionManager.getInstance().createActionPopupMenu("ClassesView.PopupActionGroup", group); + } + private final class MyUpdateClassesCommand extends SuspendContextCommandImpl { + MyUpdateClassesCommand(@Nullable SuspendContextImpl suspendContext) { super(suspendContext); } @@ -183,35 +191,35 @@ public class ClassesFilteredView extends BorderLayoutPanel { @Override public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { - loadClassesAndCounts(); + myDebugProcess.getVirtualMachineProxy(); + List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); + + if (classes.isEmpty()) { + return; + } + + VirtualMachine vm = classes.get(0).virtualMachine(); + long start = System.nanoTime(); + long[] counts = vm.instanceCounts(classes); + long delay = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); + mySingleAlarm.setDelay((int) (DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT * delay)); + + LOG.info(String.format("Instances query time = %d ms. Count = %d", delay, classes.size())); + SwingUtilities.invokeLater(() -> { + if (contextIsValid()) { + myTable.setClassesAndCounts(classes, counts); + } + + myTable.setBusy(false); + }); } @Override public void commandCancelled() { } - } - private ActionPopupMenu createContextMenu() { - ActionGroup group = (ActionGroup) ActionManager.getInstance().getAction("ClassesView.PopupActionGroup"); - return ActionManager.getInstance().createActionPopupMenu("ClassesView.PopupActionGroup", group); - } - - private void loadClassesAndCounts() { - assert DebuggerManager.getInstance(myProject).isDebuggerManagerThread(); - myDebugProcess.getVirtualMachineProxy(); - List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); - - if (classes.isEmpty()) { - return; + private boolean contextIsValid() { + return ClassesFilteredView.this.getSuspendContext() == getSuspendContext(); } - - VirtualMachine vm = classes.get(0).virtualMachine(); - long start = System.nanoTime(); - long[] counts = vm.instanceCounts(classes); - long delay = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); - mySingleAlarm.setDelay((int) (DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT * delay)); - - LOG.info(String.format("Instances query time = %d ms. Count = %d%n", delay, classes.size())); - SwingUtilities.invokeLater(() -> myTable.setClassesAndCounts(classes, counts)); } } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 88a8af377fcb..51420e56de68 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -173,8 +173,6 @@ public class ClassesTable extends JBTable implements DataProvider { changeSelection(convertRowIndexToView(newSelectedIndex), DiffViewTableModel.CLASSNAME_COLUMN_INDEX, false, false); } - - setBusy(false); } void hideContent() { @@ -202,10 +200,10 @@ public class ClassesTable extends JBTable implements DataProvider { @Nullable @Override public Object getData(@NonNls String dataId) { - if(SELECTED_CLASS_KEY.is(dataId)) { + if (SELECTED_CLASS_KEY.is(dataId)) { return getSelectedClass(); } - if(DEBUG_SESSION_KEY.is(dataId)) { + if (DEBUG_SESSION_KEY.is(dataId)) { return myDebugSession; } diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 8d8610d6fd1c..8d9cefcf6b1d 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -176,10 +176,10 @@ public class InstancesWindow extends DialogWrapper { setSourcePositionForEditor(); myFilterButton.setBorder(BorderFactory.createEmptyBorder()); - Dimension progressbarPreferredSize = myFilterConditionEditor.getEditorComponent().getPreferredSize(); - progressbarPreferredSize.width = JBUI.scale(FILTERING_BUTTON_ADDITIONAL_WIDTH) + + Dimension filteringButtonSize = myFilterConditionEditor.getEditorComponent().getPreferredSize(); + filteringButtonSize.width = JBUI.scale(FILTERING_BUTTON_ADDITIONAL_WIDTH) + myFilterButton.getPreferredSize().width; - myFilterButton.setPreferredSize(progressbarPreferredSize); + myFilterButton.setPreferredSize(filteringButtonSize); JBPanel filteringPane = new JBPanel(new BorderLayout(JBUI.scale(BORDER_LAYOUT_DEFAULT_GAP), 0)); @@ -261,10 +261,12 @@ public class InstancesWindow extends DialogWrapper { myFilteringProgressBar.setMinimum(0); myFilteringProgressBar.setMaximum(progressBarMaximum); myProgressPanel.setVisible(true); + myProgressPanel.repaint(); } private void hideProgressPane() { myProgressPanel.setVisible(false); + myProgressPanel.repaint(); } @Nullable @@ -463,7 +465,7 @@ public class InstancesWindow extends DialogWrapper { SwingUtilities.invokeLater(() -> showProgressPane(myReferences.size())); AtomicInteger totalChildren = new AtomicInteger(0); - for (int i = 0, size = myReferences.size(); i < size && !isCancelled(); i += FILTERING_CHUNK_SIZE) { + for (int i = 0, size = myReferences.size(); i < size; i += FILTERING_CHUNK_SIZE) { myDebuggerTaskCompleted = false; final int chunkBegin = i; debugProcess.getManagerThread().schedule(new DebuggerContextCommandImpl(debugProcess.getDebuggerContext()) { @@ -476,8 +478,7 @@ public class InstancesWindow extends DialogWrapper { public void threadAction(@NotNull SuspendContextImpl suspendContext) { XValueChildrenList children = new XValueChildrenList(); int endOfChunk = min(chunkBegin + FILTERING_CHUNK_SIZE, size); - for (int j = chunkBegin; j < endOfChunk - && !isCancelled() && totalChildren.get() < MAX_TREE_NODE_COUNT; j++) { + for (int j = chunkBegin; j < endOfChunk && totalChildren.get() < MAX_TREE_NODE_COUNT; j++) { ObjectReference ref = myReferences.get(j); if (myExpressionEvaluator != null && isSatisfy(myExpressionEvaluator, ref) != MyFilteringResult.MATCH) { continue; @@ -490,7 +491,11 @@ public class InstancesWindow extends DialogWrapper { if (children.size() > 0) { totalChildren.addAndGet(children.size()); - SwingUtilities.invokeLater(() -> addChildrenToTree(children, false)); + SwingUtilities.invokeLater(() -> { + if(MyFilteringWorker.this == myFilteringTask) { + addChildrenToTree(children, false); + } + }); } SwingUtilities.invokeLater(() -> myFilteringProgressBar.setValue(endOfChunk)); @@ -522,7 +527,6 @@ public class InstancesWindow extends DialogWrapper { return MyFilteringResult.MATCH; } } catch (EvaluateException e) { - System.out.println(e.getMessage()); return MyFilteringResult.EVAL_ERROR; } From 859c43f86a5886f016424b5c7deef86f05195193 Mon Sep 17 00:00:00 2001 From: roenke Date: Mon, 15 Aug 2016 19:40:48 +0300 Subject: [PATCH 023/558] IDEA-159837 Add default focus on text editor for filtering --- view/InstancesWindow.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 8d9cefcf6b1d..77e55c68ffbd 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -25,6 +25,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.psi.PsiClass; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.JBColor; @@ -35,6 +36,7 @@ import com.intellij.ui.components.JBScrollPane; import com.intellij.util.ui.JBDimension; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.update.UiNotifyConnector; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; import com.intellij.xdebugger.XExpression; @@ -218,6 +220,11 @@ public class InstancesWindow extends DialogWrapper { JBScrollPane treeScrollPane = new JBScrollPane(myInstancesTree); add(filteringPane, BorderLayout.NORTH); add(treeScrollPane, BorderLayout.CENTER); + + JComponent focusedComponent = myFilterConditionEditor.getEditorComponent(); + UiNotifyConnector.doWhenFirstShown(focusedComponent, () -> + IdeFocusManager.findInstanceByComponent(focusedComponent) + .requestFocus(focusedComponent, true)); } @Override @@ -492,7 +499,7 @@ public class InstancesWindow extends DialogWrapper { if (children.size() > 0) { totalChildren.addAndGet(children.size()); SwingUtilities.invokeLater(() -> { - if(MyFilteringWorker.this == myFilteringTask) { + if (MyFilteringWorker.this == myFilteringTask) { addChildrenToTree(children, false); } }); From 5e982277057f4c70e131038ebf109b8e3080ad45 Mon Sep 17 00:00:00 2001 From: roenke Date: Mon, 15 Aug 2016 20:26:32 +0300 Subject: [PATCH 024/558] IDEA-159835 Add basic implementation for "Jump to source" context menu action (arrays not working) --- action/JumpToTypeSourceAction.java | 41 +++++++++++++++++++ ...ff.java => ShowClassesWithDiffAction.java} | 2 +- 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 action/JumpToTypeSourceAction.java rename action/{ShowClassesWithDiff.java => ShowClassesWithDiffAction.java} (90%) diff --git a/action/JumpToTypeSourceAction.java b/action/JumpToTypeSourceAction.java new file mode 100644 index 000000000000..909ba5bc0112 --- /dev/null +++ b/action/JumpToTypeSourceAction.java @@ -0,0 +1,41 @@ +package org.jetbrains.debugger.memory.action; + +import com.intellij.codeInsight.navigation.NavigationUtil; +import com.intellij.debugger.engine.DebuggerUtils; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiClass; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.xdebugger.XDebugSession; +import com.sun.jdi.ReferenceType; +import org.jetbrains.annotations.Nullable; + +public class JumpToTypeSourceAction extends ClassesActionBase { + + @Override + protected boolean isEnabled(AnActionEvent e) { + final PsiClass psiClass = getPsiClass(e); + + return super.isEnabled(e) && psiClass != null && psiClass.isPhysical(); + } + + @Override + protected void perform(AnActionEvent e) { + final PsiClass psiClass = getPsiClass(e); + if(psiClass != null) { + NavigationUtil.openFileWithPsiElement(psiClass, true, true); + } + } + + @Nullable + private PsiClass getPsiClass(AnActionEvent e) { + ReferenceType selectedClass = getSelectedClass(e); + XDebugSession session = getDebugSession(e); + if(selectedClass == null || session == null) { + return null; + } + + Project project = session.getProject(); + return DebuggerUtils.findClass(selectedClass.name(), project, GlobalSearchScope.allScope(project)); + } +} diff --git a/action/ShowClassesWithDiff.java b/action/ShowClassesWithDiffAction.java similarity index 90% rename from action/ShowClassesWithDiff.java rename to action/ShowClassesWithDiffAction.java index 8ca30ec247b8..f12c349e0593 100644 --- a/action/ShowClassesWithDiff.java +++ b/action/ShowClassesWithDiffAction.java @@ -5,7 +5,7 @@ import com.intellij.openapi.actionSystem.ToggleAction; import com.intellij.openapi.project.Project; import org.jetbrains.debugger.memory.component.MemoryViewManager; -public class ShowClassesWithDiff extends ToggleAction { +public class ShowClassesWithDiffAction extends ToggleAction { @Override public boolean isSelected(AnActionEvent e) { return MemoryViewManager.getInstance().isNeedShowDiffOnly(); From 6b38a494908ac1352cccbf03156fa4b52e97d2d1 Mon Sep 17 00:00:00 2001 From: roenke Date: Mon, 15 Aug 2016 23:29:13 +0300 Subject: [PATCH 025/558] IDEA-159835 Add ability to jump to source code of type of array element --- action/JumpToTypeSourceAction.java | 36 +++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/action/JumpToTypeSourceAction.java b/action/JumpToTypeSourceAction.java index 909ba5bc0112..01632ac1a70a 100644 --- a/action/JumpToTypeSourceAction.java +++ b/action/JumpToTypeSourceAction.java @@ -7,7 +7,8 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.PsiClass; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.xdebugger.XDebugSession; -import com.sun.jdi.ReferenceType; +import com.sun.jdi.*; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class JumpToTypeSourceAction extends ClassesActionBase { @@ -22,8 +23,8 @@ public class JumpToTypeSourceAction extends ClassesActionBase { @Override protected void perform(AnActionEvent e) { final PsiClass psiClass = getPsiClass(e); - if(psiClass != null) { - NavigationUtil.openFileWithPsiElement(psiClass, true, true); + if (psiClass != null) { + NavigationUtil.activateFileWithPsiElement(psiClass); } } @@ -31,11 +32,34 @@ public class JumpToTypeSourceAction extends ClassesActionBase { private PsiClass getPsiClass(AnActionEvent e) { ReferenceType selectedClass = getSelectedClass(e); XDebugSession session = getDebugSession(e); - if(selectedClass == null || session == null) { + if (selectedClass == null || session == null) { return null; } - Project project = session.getProject(); - return DebuggerUtils.findClass(selectedClass.name(), project, GlobalSearchScope.allScope(project)); + ReferenceType targetClass = getObjectType(selectedClass); + if (targetClass != null) { + Project project = session.getProject(); + return DebuggerUtils + .findClass(targetClass.name(), project, GlobalSearchScope.allScope(project)); + } + + return null; + } + + @Nullable + private ReferenceType getObjectType(@NotNull ReferenceType ref) { + if (!(ref instanceof ArrayType)) { + return ref; + } + + try { + Type elementType = ((ArrayType) ref).componentType(); + if (elementType instanceof ReferenceType) { + return getObjectType((ReferenceType) elementType); + } + } catch (ClassNotLoadedException ignored) { + } + + return null; } } From 70ddce0c14c669f80c6a9cea9bb9c61ae132c2db Mon Sep 17 00:00:00 2001 From: roenke Date: Tue, 16 Aug 2016 11:25:54 +0300 Subject: [PATCH 026/558] IDEA-159835 Jump to type of arrays element (for multidimensional arrays) --- action/JumpToTypeSourceAction.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/action/JumpToTypeSourceAction.java b/action/JumpToTypeSourceAction.java index 01632ac1a70a..6e72daadb9ff 100644 --- a/action/JumpToTypeSourceAction.java +++ b/action/JumpToTypeSourceAction.java @@ -11,6 +11,8 @@ import com.sun.jdi.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.List; + public class JumpToTypeSourceAction extends ClassesActionBase { @Override @@ -52,12 +54,11 @@ public class JumpToTypeSourceAction extends ClassesActionBase { return ref; } - try { - Type elementType = ((ArrayType) ref).componentType(); - if (elementType instanceof ReferenceType) { - return getObjectType((ReferenceType) elementType); - } - } catch (ClassNotLoadedException ignored) { + String elementTypeName = ref.name().replace("[]", ""); + VirtualMachine vm = ref.virtualMachine(); + final List referenceTypes = vm.classesByName(elementTypeName); + if (referenceTypes.size() == 1) { + return referenceTypes.get(0); } return null; From 815829aadd8e9d1936ecca209a822c3a79c0e760 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 17 Aug 2016 13:31:38 +0300 Subject: [PATCH 027/558] Simplify information node when session has been resumed in instances window --- view/InstancesWindow.java | 34 ++++++---------------------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 77e55c68ffbd..855310e06a0e 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -30,6 +30,7 @@ import com.intellij.psi.PsiClass; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.JBColor; import com.intellij.ui.JBProgressBar; +import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBPanel; import com.intellij.ui.components.JBScrollPane; @@ -41,7 +42,6 @@ import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; import com.intellij.xdebugger.XExpression; import com.intellij.xdebugger.frame.*; -import com.intellij.xdebugger.frame.presentation.XValuePresentation; import com.intellij.xdebugger.impl.XDebugSessionImpl; import com.intellij.xdebugger.impl.XSourcePositionImpl; import com.intellij.xdebugger.impl.actions.XDebuggerActionBase; @@ -348,41 +348,19 @@ public class InstancesWindow extends DialogWrapper { } private class MySessionListener implements XDebugSessionListener { - private final XValueChildrenList myRunningAppChildNode = new XValueChildrenList(); - private volatile XDebuggerTreeState myTreeState = null; - { - myRunningAppChildNode.add(new XNamedValue("") { - @Override - public void computePresentation(@NotNull XValueNode node, @NotNull XValuePlace place) { - node.setPresentation(XDebuggerUIConstants.INFORMATION_MESSAGE_ICON, new XValuePresentation() { - @NotNull - @Override - public String getSeparator() { - return ""; - } - - @Override - public void renderValue(@NotNull XValueTextRenderer renderer) { - renderer.renderValue("The application is running"); - } - }, false); - } - }); - } - @Override public void sessionResumed() { SwingUtilities.invokeLater(() -> { myTreeState = XDebuggerTreeState.saveState(myInstancesTree); cancelFilteringTask(); - XDebuggerTreeNode root = myInstancesTree.getRoot(); - if (root != null) { - root.clearChildren(); - addChildrenToTree(myRunningAppChildNode, true); - } + myInstancesTree.getRoot().clearChildren(); + XCompositeNode root = (XCompositeNode) myInstancesTree.getRoot(); + + root.setMessage("The application is running", XDebuggerUIConstants.INFORMATION_MESSAGE_ICON, + SimpleTextAttributes.REGULAR_ATTRIBUTES, null); }); } From 34126d2fff5f8a34223d3088f8cf661f5ff7790a Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 17 Aug 2016 15:43:02 +0300 Subject: [PATCH 028/558] Disable button "Filter" during filtration --- view/InstancesWindow.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 855310e06a0e..3bea4064c631 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -214,6 +214,8 @@ public class InstancesWindow extends DialogWrapper { if (!expression.isEmpty()) { myFilterConditionEditor.saveTextInHistory(); } + + myFilterButton.setEnabled(false); myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree)); }); @@ -439,6 +441,7 @@ public class InstancesWindow extends DialogWrapper { @Override protected void done() { addChildrenToTree(XValueChildrenList.EMPTY, true); + myFilterButton.setEnabled(true); hideProgressPane(); } From 52dd2b5e85c90c667d6e6a0ba22a6622e18ec297 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 17 Aug 2016 19:48:01 +0300 Subject: [PATCH 029/558] IDEA-159836 Add statistics for nodes/error amount to instances view --- view/FilteringProgressView.java | 117 ++++++++++++++++++++++++++++++++ view/InstancesWindow.java | 86 ++++++++++++----------- 2 files changed, 159 insertions(+), 44 deletions(-) create mode 100644 view/FilteringProgressView.java diff --git a/view/FilteringProgressView.java b/view/FilteringProgressView.java new file mode 100644 index 000000000000..30cd478a7e81 --- /dev/null +++ b/view/FilteringProgressView.java @@ -0,0 +1,117 @@ +package org.jetbrains.debugger.memory.view; + +import com.intellij.icons.AllIcons; +import com.intellij.ui.JBProgressBar; +import com.intellij.ui.components.JBLabel; +import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.components.BorderLayoutPanel; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.view.InstancesWindow.FilteringCompletionReason; + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +class FilteringProgressView extends BorderLayoutPanel { + private final static String LIMIT_REACHED_LABEL = "Limit has been reached"; + private final static String INTERRUPTED_LABEL = "Filtering has been interrupted"; + + private final JBProgressBar myProgressBar = new JBProgressBar(); + private final BorderLayoutPanel myProgressPanel = new BorderLayoutPanel(); + private final JBLabel myStopButton = new JBLabel(UIUtil.isUnderDarcula() + ? AllIcons.Actions.Clean : AllIcons.Actions.CleanLight); + private final JBLabel myProgressText = new JBLabel(); + + private int myProceedCount = 0; + private int myErrorCount = 0; + private int myMatchedCount = 0; + + private boolean myIsInProcess = false; + + @Nullable + private FilteringCompletionReason myCompletionReason = null; + + FilteringProgressView() { + myStopButton.setOpaque(true); + myProgressPanel.addToCenter(myProgressBar); + myProgressPanel.addToRight(myStopButton); + addToTop(myProgressText); + addToCenter(myProgressPanel); + } + + void addStopActionListener(@NotNull Runnable listener) { + myStopButton.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + listener.run(); + } + }); + } + + void updateProgress(int proceedCount, int matchedCount, int errorCount) { + if (myIsInProcess) { + myProceedCount += proceedCount; + myMatchedCount += matchedCount; + myErrorCount += errorCount; + myProgressBar.setValue(myProceedCount); + myProgressText.setText(getDescription()); + } + } + + void start(int totalCount) { + reset(); + myIsInProcess = true; + myProgressBar.setMinimum(0); + myProgressBar.setMaximum(totalCount); + setProgressBarVisible(true); + } + + void complete(@NotNull FilteringCompletionReason reason) { + if (!myIsInProcess) { + throw new IllegalStateException("First you need to start progress"); + } + + myIsInProcess = false; + myCompletionReason = reason; + myProgressText.setText(getDescription()); + setProgressBarVisible(false); + } + + private void reset() { + myIsInProcess = false; + myErrorCount = 0; + myProceedCount = 0; + myMatchedCount = 0; + myCompletionReason = null; + } + + private void setProgressBarVisible(boolean value) { + myProgressPanel.setVisible(value); + } + + private String getDescription() { + int total = myProgressBar.getMaximum(); + String itemsInfo = String.format("Items: %d / %d", myMatchedCount, total); + if (myIsInProcess || myCompletionReason == null) { + return itemsInfo; + } + + switch (myCompletionReason) { + case ALL_CHECKED: + break; + case INTERRUPTED: + itemsInfo = String.format("%s (%s)", itemsInfo, INTERRUPTED_LABEL); + break; + case LIMIT_REACHED: + itemsInfo = String.format("%s (%s)", itemsInfo, LIMIT_REACHED_LABEL); + break; + } + + if (!myIsInProcess && myErrorCount != 0) { + String errors = String.format("Errors: %d", myErrorCount); + return String.format("%s
%s", itemsInfo, errors); + } + + return itemsInfo; + } +} diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 3bea4064c631..8d21bbdbf92c 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -16,7 +16,6 @@ import com.intellij.debugger.ui.impl.watch.MessageDescriptor; import com.intellij.debugger.ui.impl.watch.NodeManagerImpl; import com.intellij.debugger.ui.tree.NodeDescriptor; import com.intellij.debugger.ui.tree.render.CachedEvaluator; -import com.intellij.icons.AllIcons; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.AnActionListener; @@ -29,14 +28,12 @@ import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.psi.PsiClass; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.JBColor; -import com.intellij.ui.JBProgressBar; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBPanel; import com.intellij.ui.components.JBScrollPane; import com.intellij.util.ui.JBDimension; import com.intellij.util.ui.JBUI; -import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.update.UiNotifyConnector; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; @@ -68,8 +65,6 @@ import org.jetbrains.java.debugger.JavaDebuggerEditorsProvider; import javax.swing.*; import javax.swing.tree.TreeNode; import java.awt.*; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -106,6 +101,10 @@ public class InstancesWindow extends DialogWrapper { root.setDefaultButton(myInstancesView.myFilterButton); } + enum FilteringCompletionReason { + ALL_CHECKED, INTERRUPTED, LIMIT_REACHED + } + @NotNull @Override protected String getDimensionServiceKey() { @@ -126,7 +125,7 @@ public class InstancesWindow extends DialogWrapper { protected JComponent createSouthPanel() { JComponent comp = super.createSouthPanel(); if (comp != null) { - comp.add(myInstancesView.myProgressPanel, BorderLayout.WEST); + comp.add(myInstancesView.myProgress, BorderLayout.WEST); } return comp; @@ -156,9 +155,8 @@ public class InstancesWindow extends DialogWrapper { private final MyCachedEvaluator myEvaluator = new MyCachedEvaluator(); private final MyNodeManager myNodeManager = new MyNodeManager(myProject); - private final JBProgressBar myFilteringProgressBar = new JBProgressBar(); private final JButton myFilterButton = new JButton("Filter"); - private final JComponent myProgressPanel = new JBPanel<>(new BorderLayout()); + private final FilteringProgressView myProgress = new FilteringProgressView(); private final AnActionListener.Adapter myActionListener = new MyActionListener(); private final Object myFilteringTaskLock = new Object(); @@ -189,17 +187,7 @@ public class InstancesWindow extends DialogWrapper { filteringPane.add(myFilterConditionEditor.getComponent(), BorderLayout.CENTER); filteringPane.add(myFilterButton, BorderLayout.EAST); - JLabel stopFilteringButton = - new JLabel(UIUtil.isUnderDarcula() ? AllIcons.Actions.Clean : AllIcons.Actions.CleanLight); - stopFilteringButton.setOpaque(true); - stopFilteringButton.addMouseListener(new MouseAdapter() { - public void mousePressed(MouseEvent e) { - cancelFilteringTask(); - } - }); - - myProgressPanel.add(myFilteringProgressBar, BorderLayout.CENTER); - myProgressPanel.add(stopFilteringButton, BorderLayout.EAST); + myProgress.addStopActionListener(this::cancelFilteringTask); XDebuggerTreeCreator treeCreator = new XDebuggerTreeCreator(myProject, editorsProvider, null, markers); @@ -259,6 +247,10 @@ public class InstancesWindow extends DialogWrapper { if (evaluationContext != null) { synchronized (myFilteringTaskLock) { myFilteringTask = new MyFilteringWorker(instances, evaluationContext, createEvaluator()); + SwingUtilities.invokeLater(() -> { + myProgress.start(instances.size()); + myFilteringTask.execute(); + }); myFilteringTask.execute(); } } @@ -266,18 +258,6 @@ public class InstancesWindow extends DialogWrapper { }); } - private void showProgressPane(int progressBarMaximum) { - myFilteringProgressBar.setMinimum(0); - myFilteringProgressBar.setMaximum(progressBarMaximum); - myProgressPanel.setVisible(true); - myProgressPanel.repaint(); - } - - private void hideProgressPane() { - myProgressPanel.setVisible(false); - myProgressPanel.repaint(); - } - @Nullable private ExpressionEvaluator createEvaluator() { ExpressionEvaluator evaluator = null; @@ -430,6 +410,9 @@ public class InstancesWindow extends DialogWrapper { private volatile boolean myDebuggerTaskCompleted = false; + @NotNull + private volatile FilteringCompletionReason myCompletionReason = FilteringCompletionReason.INTERRUPTED; + MyFilteringWorker(@NotNull List refs, @NotNull EvaluationContextImpl evaluationContext, @Nullable ExpressionEvaluator evaluator) { @@ -440,9 +423,11 @@ public class InstancesWindow extends DialogWrapper { @Override protected void done() { - addChildrenToTree(XValueChildrenList.EMPTY, true); - myFilterButton.setEnabled(true); - hideProgressPane(); + SwingUtilities.invokeLater(() -> { + addChildrenToTree(XValueChildrenList.EMPTY, true); + myFilterButton.setEnabled(true); + myProgress.complete(myCompletionReason); + }); } @Override @@ -450,10 +435,10 @@ public class InstancesWindow extends DialogWrapper { DebugProcessImpl debugProcess = (DebugProcessImpl) DebuggerManager.getInstance(myProject) .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); - SwingUtilities.invokeLater(() -> showProgressPane(myReferences.size())); - AtomicInteger totalChildren = new AtomicInteger(0); - for (int i = 0, size = myReferences.size(); i < size; i += FILTERING_CHUNK_SIZE) { + AtomicInteger totalViewed = new AtomicInteger(0); + for (int i = 0, size = myReferences.size(); i < size && totalChildren.get() < MAX_TREE_NODE_COUNT; + i += FILTERING_CHUNK_SIZE) { myDebuggerTaskCompleted = false; final int chunkBegin = i; debugProcess.getManagerThread().schedule(new DebuggerContextCommandImpl(debugProcess.getDebuggerContext()) { @@ -466,19 +451,28 @@ public class InstancesWindow extends DialogWrapper { public void threadAction(@NotNull SuspendContextImpl suspendContext) { XValueChildrenList children = new XValueChildrenList(); int endOfChunk = min(chunkBegin + FILTERING_CHUNK_SIZE, size); + int errorCount = 0; for (int j = chunkBegin; j < endOfChunk && totalChildren.get() < MAX_TREE_NODE_COUNT; j++) { ObjectReference ref = myReferences.get(j); - if (myExpressionEvaluator != null && isSatisfy(myExpressionEvaluator, ref) != MyFilteringResult.MATCH) { - continue; + totalViewed.incrementAndGet(); + if (myExpressionEvaluator != null) { + MyFilteringResult comparison = isSatisfy(myExpressionEvaluator, ref); + if(comparison == MyFilteringResult.EVAL_ERROR) { + errorCount++; + } + + if(comparison != MyFilteringResult.MATCH) { + continue; + } } JavaValue val = new InstanceJavaValue(null, new InstanceValueDescriptor(myProject, ref), myEvaluationContext, myNodeManager, true); children.add(val); + totalChildren.incrementAndGet(); } if (children.size() > 0) { - totalChildren.addAndGet(children.size()); SwingUtilities.invokeLater(() -> { if (MyFilteringWorker.this == myFilteringTask) { addChildrenToTree(children, false); @@ -486,7 +480,10 @@ public class InstancesWindow extends DialogWrapper { }); } - SwingUtilities.invokeLater(() -> myFilteringProgressBar.setValue(endOfChunk)); + final int childrenSize = children.size(); + final int finalErrorsCount = errorCount; + SwingUtilities.invokeLater(() -> + myProgress.updateProgress(endOfChunk - chunkBegin, childrenSize, finalErrorsCount)); synchronized (MyFilteringWorker.this) { myDebuggerTaskCompleted = true; @@ -500,11 +497,12 @@ public class InstancesWindow extends DialogWrapper { MyFilteringWorker.this.wait(); } } - if (totalChildren.get() >= MAX_TREE_NODE_COUNT) { - break; - } } + myCompletionReason = totalViewed.get() == myReferences.size() + ? FilteringCompletionReason.ALL_CHECKED + : FilteringCompletionReason.LIMIT_REACHED; + return null; } From 2a1d8b9e477bb90cae1d8a9781bbe7ddec76b3f6 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 17 Aug 2016 20:47:26 +0300 Subject: [PATCH 030/558] Show errors after successfully filtered items --- view/ErrorsValueGroup.java | 60 ++++++++++++++++++++++++++++++++++++++ view/InstancesWindow.java | 36 ++++++++++++++--------- 2 files changed, 82 insertions(+), 14 deletions(-) create mode 100644 view/ErrorsValueGroup.java diff --git a/view/ErrorsValueGroup.java b/view/ErrorsValueGroup.java new file mode 100644 index 000000000000..6d5a368318bc --- /dev/null +++ b/view/ErrorsValueGroup.java @@ -0,0 +1,60 @@ +package org.jetbrains.debugger.memory.view; + +import com.intellij.icons.AllIcons; +import com.intellij.util.containers.HashMap; +import com.intellij.xdebugger.frame.XCompositeNode; +import com.intellij.xdebugger.frame.XNamedValue; +import com.intellij.xdebugger.frame.XValueChildrenList; +import com.intellij.xdebugger.frame.XValueGroup; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +class ErrorsValueGroup extends XValueGroup { + private final Map> myErrorMessage2ValueMap = new HashMap<>(); + ErrorsValueGroup(@NotNull String name) { + super(name); + } + + void addErrorValue(@NotNull String message, @NotNull XNamedValue value) { + List lst; + if(!myErrorMessage2ValueMap.containsKey(message)) { + myErrorMessage2ValueMap.put(message, new ArrayList<>()); + } + + lst = myErrorMessage2ValueMap.get(message); + lst.add(value); + } + + @Nullable + @Override + public Icon getIcon() { + return AllIcons.General.Error; + } + + @Override + public void computeChildren(@NotNull XCompositeNode node) { + XValueChildrenList lst = new XValueChildrenList(); + myErrorMessage2ValueMap.keySet().forEach(s -> lst.addTopGroup(new MyErrorsValueGroup(s))); + node.addChildren(lst, true); + } + + private class MyErrorsValueGroup extends XValueGroup { + + @Override + public void computeChildren(@NotNull XCompositeNode node) { + XValueChildrenList lst = new XValueChildrenList(); + String name = getName(); + myErrorMessage2ValueMap.get(name).forEach(lst::add); + node.addChildren(lst, true); + } + + MyErrorsValueGroup(@NotNull String name) { + super(name); + } + } +} diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 8d21bbdbf92c..3ae81705ec5b 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -145,7 +145,7 @@ public class InstancesWindow extends DialogWrapper { private class MyInstancesView extends JBPanel implements Disposable { private static final String HISTORY_ID_PREFIX = "filtering"; - private static final int MAX_TREE_NODE_COUNT = 1000; + private static final int MAX_TREE_NODE_COUNT = 2000; private static final int FILTERING_CHUNK_SIZE = 30; private final XDebuggerTree myInstancesTree; @@ -407,6 +407,7 @@ public class InstancesWindow extends DialogWrapper { private final List myReferences; private final EvaluationContextImpl myEvaluationContext; private final ExpressionEvaluator myExpressionEvaluator; + private final ErrorsValueGroup myErrorsGroup = new ErrorsValueGroup("Errors"); private volatile boolean myDebuggerTaskCompleted = false; @@ -423,11 +424,11 @@ public class InstancesWindow extends DialogWrapper { @Override protected void done() { - SwingUtilities.invokeLater(() -> { - addChildrenToTree(XValueChildrenList.EMPTY, true); - myFilterButton.setEnabled(true); - myProgress.complete(myCompletionReason); - }); + XValueChildrenList lst = new XValueChildrenList(); + lst.addBottomGroup(myErrorsGroup); + addChildrenToTree(lst, true); + myFilterButton.setEnabled(true); + myProgress.complete(myCompletionReason); } @Override @@ -449,26 +450,33 @@ public class InstancesWindow extends DialogWrapper { @Override public void threadAction(@NotNull SuspendContextImpl suspendContext) { + // TODO: need to rewrite this XValueChildrenList children = new XValueChildrenList(); int endOfChunk = min(chunkBegin + FILTERING_CHUNK_SIZE, size); int errorCount = 0; for (int j = chunkBegin; j < endOfChunk && totalChildren.get() < MAX_TREE_NODE_COUNT; j++) { ObjectReference ref = myReferences.get(j); totalViewed.incrementAndGet(); + Pair comparison = null; if (myExpressionEvaluator != null) { - MyFilteringResult comparison = isSatisfy(myExpressionEvaluator, ref); - if(comparison == MyFilteringResult.EVAL_ERROR) { + comparison = isSatisfy(myExpressionEvaluator, ref); + if (comparison.first == MyFilteringResult.EVAL_ERROR) { errorCount++; } - if(comparison != MyFilteringResult.MATCH) { + if (comparison.first == MyFilteringResult.NO_MATCH) { continue; } } JavaValue val = new InstanceJavaValue(null, new InstanceValueDescriptor(myProject, ref), myEvaluationContext, myNodeManager, true); - children.add(val); + if (comparison == null || comparison.first == MyFilteringResult.MATCH) { + children.add(val); + } else { + myErrorsGroup.addErrorValue(comparison.second, val); + } + totalChildren.incrementAndGet(); } @@ -506,17 +514,17 @@ public class InstancesWindow extends DialogWrapper { return null; } - private MyFilteringResult isSatisfy(@NotNull ExpressionEvaluator evaluator, @NotNull Value value) { + private Pair isSatisfy(@NotNull ExpressionEvaluator evaluator, @NotNull Value value) { try { Value result = evaluator.evaluate(myEvaluationContext.createEvaluationContext(value)); if (result instanceof BooleanValue && ((BooleanValue) result).value()) { - return MyFilteringResult.MATCH; + return Pair.create(MyFilteringResult.MATCH, ""); } } catch (EvaluateException e) { - return MyFilteringResult.EVAL_ERROR; + return Pair.create(MyFilteringResult.EVAL_ERROR, e.getMessage()); } - return MyFilteringResult.NO_MATCH; + return Pair.create(MyFilteringResult.NO_MATCH, ""); } } } From 29c91c11795d443a40b36a919b2958e3371c31f4 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 17 Aug 2016 21:08:32 +0300 Subject: [PATCH 031/558] Show "Errors" node only if errors occurred --- view/ErrorsValueGroup.java | 7 ++++++- view/InstancesWindow.java | 11 ++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/view/ErrorsValueGroup.java b/view/ErrorsValueGroup.java index 6d5a368318bc..598b66e91e2a 100644 --- a/view/ErrorsValueGroup.java +++ b/view/ErrorsValueGroup.java @@ -16,13 +16,14 @@ import java.util.Map; class ErrorsValueGroup extends XValueGroup { private final Map> myErrorMessage2ValueMap = new HashMap<>(); + ErrorsValueGroup(@NotNull String name) { super(name); } void addErrorValue(@NotNull String message, @NotNull XNamedValue value) { List lst; - if(!myErrorMessage2ValueMap.containsKey(message)) { + if (!myErrorMessage2ValueMap.containsKey(message)) { myErrorMessage2ValueMap.put(message, new ArrayList<>()); } @@ -43,6 +44,10 @@ class ErrorsValueGroup extends XValueGroup { node.addChildren(lst, true); } + boolean isEmpty() { + return myErrorMessage2ValueMap.isEmpty(); + } + private class MyErrorsValueGroup extends XValueGroup { @Override diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 3ae81705ec5b..0bb1c79dee2d 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -424,9 +424,14 @@ public class InstancesWindow extends DialogWrapper { @Override protected void done() { - XValueChildrenList lst = new XValueChildrenList(); - lst.addBottomGroup(myErrorsGroup); - addChildrenToTree(lst, true); + if (!myErrorsGroup.isEmpty()) { + XValueChildrenList lst = new XValueChildrenList(); + lst.addBottomGroup(myErrorsGroup); + addChildrenToTree(lst, true); + } else { + addChildrenToTree(XValueChildrenList.EMPTY, true); + } + myFilterButton.setEnabled(true); myProgress.complete(myCompletionReason); } From c7e4450e89beabf39e6516247a4c08dfa21f3b17 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 17 Aug 2016 21:12:27 +0300 Subject: [PATCH 032/558] Move ErrorsValueGroup to util package --- {view => utils}/ErrorsValueGroup.java | 16 ++++++++-------- view/InstancesWindow.java | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) rename {view => utils}/ErrorsValueGroup.java (86%) diff --git a/view/ErrorsValueGroup.java b/utils/ErrorsValueGroup.java similarity index 86% rename from view/ErrorsValueGroup.java rename to utils/ErrorsValueGroup.java index 598b66e91e2a..922a70cf9af3 100644 --- a/view/ErrorsValueGroup.java +++ b/utils/ErrorsValueGroup.java @@ -1,4 +1,4 @@ -package org.jetbrains.debugger.memory.view; +package org.jetbrains.debugger.memory.utils; import com.intellij.icons.AllIcons; import com.intellij.util.containers.HashMap; @@ -14,14 +14,14 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -class ErrorsValueGroup extends XValueGroup { +public class ErrorsValueGroup extends XValueGroup { private final Map> myErrorMessage2ValueMap = new HashMap<>(); - ErrorsValueGroup(@NotNull String name) { + public ErrorsValueGroup(@NotNull String name) { super(name); } - void addErrorValue(@NotNull String message, @NotNull XNamedValue value) { + public void addErrorValue(@NotNull String message, @NotNull XNamedValue value) { List lst; if (!myErrorMessage2ValueMap.containsKey(message)) { myErrorMessage2ValueMap.put(message, new ArrayList<>()); @@ -31,6 +31,10 @@ class ErrorsValueGroup extends XValueGroup { lst.add(value); } + public boolean isEmpty() { + return myErrorMessage2ValueMap.isEmpty(); + } + @Nullable @Override public Icon getIcon() { @@ -44,10 +48,6 @@ class ErrorsValueGroup extends XValueGroup { node.addChildren(lst, true); } - boolean isEmpty() { - return myErrorMessage2ValueMap.isEmpty(); - } - private class MyErrorsValueGroup extends XValueGroup { @Override diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 0bb1c79dee2d..7dcfe0aace8e 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -58,6 +58,7 @@ import com.sun.jdi.ReferenceType; import com.sun.jdi.Value; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.utils.ErrorsValueGroup; import org.jetbrains.debugger.memory.utils.InstanceJavaValue; import org.jetbrains.debugger.memory.utils.InstanceValueDescriptor; import org.jetbrains.java.debugger.JavaDebuggerEditorsProvider; From 02dfd7d3a4d40a25d5b17a6dc59017cc18fdd909 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 18 Aug 2016 12:16:28 +0300 Subject: [PATCH 033/558] Rename dimension service key for instances window --- view/InstancesWindow.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 7dcfe0aace8e..3b1aedf5272f 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -109,7 +109,7 @@ public class InstancesWindow extends DialogWrapper { @NotNull @Override protected String getDimensionServiceKey() { - return "#org.jetbrains.plugin.view.InstancesWindow"; + return "#org.jetbrains.debugger.memory.view.InstancesWindow"; } @Nullable From 1ea174714599a4f2a68110fe48ae18e85656f02c Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 18 Aug 2016 13:31:36 +0300 Subject: [PATCH 034/558] Move logic with expression editor to ExpressionEditorWithHistory --- view/ExpressionEditorWithHistory.java | 38 ++++++++++++++++++++------- view/InstancesWindow.java | 26 ++---------------- 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/view/ExpressionEditorWithHistory.java b/view/ExpressionEditorWithHistory.java index 7894d61ec5e2..d7b78130b8c6 100644 --- a/view/ExpressionEditorWithHistory.java +++ b/view/ExpressionEditorWithHistory.java @@ -1,22 +1,26 @@ package org.jetbrains.debugger.memory.view; import com.intellij.codeInsight.lookup.LookupManager; +import com.intellij.debugger.engine.DebuggerUtils; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CustomShortcutSet; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.PopupStep; import com.intellij.openapi.ui.popup.util.BaseListPopupStep; +import com.intellij.psi.PsiClass; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.ColoredListCellRenderer; import com.intellij.ui.popup.list.ListPopupImpl; import com.intellij.xdebugger.XExpression; -import com.intellij.xdebugger.XSourcePosition; import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; +import com.intellij.xdebugger.impl.XSourcePositionImpl; import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl; import com.intellij.xdebugger.impl.ui.XDebuggerExpressionEditor; -import org.jetbrains.annotations.NonNls; +import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -24,13 +28,16 @@ import javax.swing.*; import java.util.List; class ExpressionEditorWithHistory extends XDebuggerExpressionEditor { - ExpressionEditorWithHistory(@NotNull Project project, - @NotNull XDebuggerEditorsProvider debuggerEditorsProvider, - @Nullable @NonNls String historyId, - @Nullable XSourcePosition sourcePosition, - @Nullable Disposable parentDisposable) { - super(project, debuggerEditorsProvider, historyId, sourcePosition, XExpressionImpl.EMPTY_EXPRESSION, - false, true, true); + private static final String HISTORY_ID_PREFIX = "filtering"; + + ExpressionEditorWithHistory(final @NotNull Project project, + final @NotNull ReferenceType referenceType, + final @NotNull XDebuggerEditorsProvider debuggerEditorsProvider, + final @Nullable Disposable parentDisposable) { + super(project, debuggerEditorsProvider, HISTORY_ID_PREFIX + referenceType.name(), null, + XExpressionImpl.EMPTY_EXPRESSION, false, true, true); + + new AnAction("InstancesWindow.ShowHistory") { @Override @@ -43,6 +50,19 @@ class ExpressionEditorWithHistory extends XDebuggerExpressionEditor { e.getPresentation().setEnabled(LookupManager.getActiveLookup(getEditor()) == null); } }.registerCustomShortcutSet(CustomShortcutSet.fromString("DOWN"), getComponent(), parentDisposable); + + new SwingWorker() { + @Override + protected Void doInBackground() throws Exception { + ApplicationManager.getApplication().runReadAction(() -> { + final PsiClass psiClass = DebuggerUtils.findClass(referenceType.name(), + project, GlobalSearchScope.allScope(project)); + XSourcePositionImpl position = XSourcePositionImpl.createByElement(psiClass); + SwingUtilities.invokeLater(() -> setSourcePosition(position)); + }); + return null; + } + }.execute(); } private void showHistory() { diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 3b1aedf5272f..8255f8ece80b 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -2,7 +2,6 @@ package org.jetbrains.debugger.memory.view; import com.intellij.debugger.DebuggerManager; import com.intellij.debugger.engine.DebugProcessImpl; -import com.intellij.debugger.engine.DebuggerUtils; import com.intellij.debugger.engine.JavaValue; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.evaluation.EvaluateException; @@ -19,14 +18,11 @@ import com.intellij.debugger.ui.tree.render.CachedEvaluator; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.AnActionListener; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.openapi.wm.IdeFocusManager; -import com.intellij.psi.PsiClass; -import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.JBColor; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBLabel; @@ -40,7 +36,6 @@ import com.intellij.xdebugger.XDebugSessionListener; import com.intellij.xdebugger.XExpression; import com.intellij.xdebugger.frame.*; import com.intellij.xdebugger.impl.XDebugSessionImpl; -import com.intellij.xdebugger.impl.XSourcePositionImpl; import com.intellij.xdebugger.impl.actions.XDebuggerActionBase; import com.intellij.xdebugger.impl.evaluate.quick.XDebuggerTreeCreator; import com.intellij.xdebugger.impl.frame.XValueMarkers; @@ -145,7 +140,6 @@ public class InstancesWindow extends DialogWrapper { } private class MyInstancesView extends JBPanel implements Disposable { - private static final String HISTORY_ID_PREFIX = "filtering"; private static final int MAX_TREE_NODE_COUNT = 2000; private static final int FILTERING_CHUNK_SIZE = 30; @@ -172,9 +166,8 @@ public class InstancesWindow extends DialogWrapper { myDebugSession.addSessionListener(myDebugSessionListener, InstancesWindow.this.myDisposable); JavaDebuggerEditorsProvider editorsProvider = new JavaDebuggerEditorsProvider(); - myFilterConditionEditor = new ExpressionEditorWithHistory(myProject, editorsProvider, - HISTORY_ID_PREFIX + myReferenceType.name(), null, InstancesWindow.this.myDisposable); - setSourcePositionForEditor(); + myFilterConditionEditor = new ExpressionEditorWithHistory(myProject, myReferenceType, + editorsProvider, InstancesWindow.this.myDisposable); myFilterButton.setBorder(BorderFactory.createEmptyBorder()); Dimension filteringButtonSize = myFilterConditionEditor.getEditorComponent().getPreferredSize(); @@ -300,21 +293,6 @@ public class InstancesWindow extends DialogWrapper { } } - private void setSourcePositionForEditor() { - new SwingWorker() { - @Override - protected Void doInBackground() throws Exception { - ApplicationManager.getApplication().runReadAction(() -> { - final PsiClass psiClass = DebuggerUtils.findClass(myReferenceType.name(), - myProject, GlobalSearchScope.allScope(myProject)); - XSourcePositionImpl position = XSourcePositionImpl.createByElement(psiClass); - SwingUtilities.invokeLater(() -> myFilterConditionEditor.setSourcePosition(position)); - }); - return null; - } - }.execute(); - } - @NotNull private Pair getTreeRootDescriptor() { return Pair.pair(new XValue() { From 856d7de4a2fd17bb61364d0190d947d45b3db6a0 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 18 Aug 2016 13:45:36 +0300 Subject: [PATCH 035/558] Fix bug in actions: "Evaluate expression" and "Add to watches" --- view/InstancesWindow.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 8255f8ece80b..794597e190d4 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -354,7 +354,8 @@ public class InstancesWindow extends DialogWrapper { new ValueMarkup(expression.replace("@", ""), new JBColor(0, 0), null)); } - myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree)); + SwingUtilities.invokeLater(() -> + myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree))); } } } From b65f2864b6593056195ff79ae080114e3985daf5 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 18 Aug 2016 14:07:25 +0300 Subject: [PATCH 036/558] IDEA-159447 Add warning that filtering may lead to side effects --- view/InstancesWindow.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 794597e190d4..8f62e098de9a 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -30,6 +30,7 @@ import com.intellij.ui.components.JBPanel; import com.intellij.ui.components.JBScrollPane; import com.intellij.util.ui.JBDimension; import com.intellij.util.ui.JBUI; +import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.update.UiNotifyConnector; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; @@ -176,10 +177,15 @@ public class InstancesWindow extends DialogWrapper { myFilterButton.setPreferredSize(filteringButtonSize); JBPanel filteringPane = new JBPanel(new BorderLayout(JBUI.scale(BORDER_LAYOUT_DEFAULT_GAP), 0)); + JBLabel sideEffectsWarning = new JBLabel("Warning: filtering may have side effects", SwingConstants.RIGHT); + sideEffectsWarning.setBorder(JBUI.Borders.empty(1, 0, 0, 0)); + sideEffectsWarning.setComponentStyle(UIUtil.ComponentStyle.SMALL); + sideEffectsWarning.setFontColor(UIUtil.FontColor.BRIGHTER); filteringPane.add(new JBLabel("Condition:"), BorderLayout.WEST); filteringPane.add(myFilterConditionEditor.getComponent(), BorderLayout.CENTER); filteringPane.add(myFilterButton, BorderLayout.EAST); + filteringPane.add(sideEffectsWarning, BorderLayout.SOUTH); myProgress.addStopActionListener(this::cancelFilteringTask); From 7cd8fba0c707d12b83f552df045a3fd98d88a292 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 18 Aug 2016 16:12:48 +0300 Subject: [PATCH 037/558] Fix race condition when "done" executed earlier then all progress update tasks --- view/InstancesWindow.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 8f62e098de9a..d51667f4f8b4 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -419,7 +419,7 @@ public class InstancesWindow extends DialogWrapper { } myFilterButton.setEnabled(true); - myProgress.complete(myCompletionReason); + SwingUtilities.invokeLater(() -> myProgress.complete(myCompletionReason)); } @Override From 16d739b89a900e784dabe1bc8cf81143950d7de1 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 18 Aug 2016 16:13:14 +0300 Subject: [PATCH 038/558] Replace "Items" label to "Shown" --- view/FilteringProgressView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/FilteringProgressView.java b/view/FilteringProgressView.java index 30cd478a7e81..7f17f6b9a8d7 100644 --- a/view/FilteringProgressView.java +++ b/view/FilteringProgressView.java @@ -91,7 +91,7 @@ class FilteringProgressView extends BorderLayoutPanel { private String getDescription() { int total = myProgressBar.getMaximum(); - String itemsInfo = String.format("Items: %d / %d", myMatchedCount, total); + String itemsInfo = String.format("Shown: %d / %d", myMatchedCount, total); if (myIsInProcess || myCompletionReason == null) { return itemsInfo; } From ddb098cfc624d167c26a57e1a10a369eaeb45903 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 18 Aug 2016 16:49:21 +0300 Subject: [PATCH 039/558] Add array length to node label in instances tree --- utils/InstanceValueDescriptor.java | 9 +++++++-- utils/NamesUtils.java | 22 +++++++++++++++++++--- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/utils/InstanceValueDescriptor.java b/utils/InstanceValueDescriptor.java index 2c1b65f4f7c9..101d30714643 100644 --- a/utils/InstanceValueDescriptor.java +++ b/utils/InstanceValueDescriptor.java @@ -9,6 +9,7 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiElementFactory; import com.intellij.psi.PsiExpression; +import com.sun.jdi.ArrayReference; import com.sun.jdi.ObjectReference; import com.sun.jdi.Value; @@ -20,7 +21,11 @@ public class InstanceValueDescriptor extends ValueDescriptorImpl { @Override public String calcValueName() { - ObjectReference ref = ((ObjectReference)getValue()); + ObjectReference ref = ((ObjectReference) getValue()); + if (ref instanceof ArrayReference) { + ArrayReference arrayReference = (ArrayReference) ref; + return NamesUtils.getArrayUniqueName(arrayReference); + } return NamesUtils.getUniqueName(ref); } @@ -37,7 +42,7 @@ public class InstanceValueDescriptor extends ValueDescriptorImpl { @Override public PsiExpression getDescriptorEvaluation(DebuggerContext debuggerContext) throws EvaluateException { PsiElementFactory elementFactory = JavaPsiFacade.getInstance(myProject).getElementFactory(); - ObjectReference ref = ((ObjectReference)getValue()); + ObjectReference ref = ((ObjectReference) getValue()); String name = NamesUtils.getUniqueName(ref).replace("@", ""); String presentation = String.format("%s_DebugLabel", name); diff --git a/utils/NamesUtils.java b/utils/NamesUtils.java index 8b4363ae1cd1..d7fdd334bf7d 100644 --- a/utils/NamesUtils.java +++ b/utils/NamesUtils.java @@ -1,10 +1,26 @@ package org.jetbrains.debugger.memory.utils; +import com.intellij.openapi.util.text.StringUtil; +import com.sun.jdi.ArrayReference; import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; + +import java.util.regex.Pattern; class NamesUtils { - static String getUniqueName(ObjectReference ref) { - String name = ref.referenceType().name().replace("[]", "Array"); - return String.format("%s@%d", name.substring(name.lastIndexOf('.') + 1), ref.uniqueID()); + @NotNull + static String getUniqueName(@NotNull ObjectReference ref) { + String shortName = StringUtil.getShortName(ref.referenceType().name()); + String name = shortName.replace("[]", "Array"); + return String.format("%s@%d", name, ref.uniqueID()); + } + + @NotNull + static String getArrayUniqueName(@NotNull ArrayReference ref) { + String shortName = StringUtil.getShortName(ref.referenceType().name()); + int length = ref.length(); + + String name = shortName.replaceFirst(Pattern.quote("[]"), String.format("[%d]", length)); + return String.format("%s@%d", name, ref.uniqueID()); } } From 6f1c5f237780c9c64abce5973778801ff8305f29 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 18 Aug 2016 17:39:58 +0300 Subject: [PATCH 040/558] Fix ArrayIndexOutOfBoundsException in classes table --- view/ClassesTable.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 51420e56de68..ec2dd8a944a4 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -170,7 +170,8 @@ public class ClassesTable extends JBTable implements DataProvider { showContent(); if (newSelectedIndex != -1 && !myModel.isHided()) { - changeSelection(convertRowIndexToView(newSelectedIndex), + int ix = convertRowIndexToView(newSelectedIndex); + changeSelection(ix, DiffViewTableModel.CLASSNAME_COLUMN_INDEX, false, false); } } @@ -251,7 +252,7 @@ public class ClassesTable extends JBTable implements DataProvider { mySelectedClassWhenHided = getSelectedClass(); myIsWithContent = false; clearSelection(); - getRowSorter().allRowsChanged(); + fireTableDataChanged(); } } From 8d25d88e49eb22ed00d36e837f54e07226d4b1f7 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 18 Aug 2016 19:47:33 +0300 Subject: [PATCH 041/558] Update version: 0.9 --- view/ExpressionEditorWithHistory.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/view/ExpressionEditorWithHistory.java b/view/ExpressionEditorWithHistory.java index d7b78130b8c6..ab34a0972d00 100644 --- a/view/ExpressionEditorWithHistory.java +++ b/view/ExpressionEditorWithHistory.java @@ -37,8 +37,6 @@ class ExpressionEditorWithHistory extends XDebuggerExpressionEditor { super(project, debuggerEditorsProvider, HISTORY_ID_PREFIX + referenceType.name(), null, XExpressionImpl.EMPTY_EXPRESSION, false, true, true); - - new AnAction("InstancesWindow.ShowHistory") { @Override public void actionPerformed(AnActionEvent e) { From 3acee07a6990af8f8f12def9c4cd5dc5552a6a29 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 19 Aug 2016 12:57:01 +0300 Subject: [PATCH 042/558] Hide progress panel while session is running --- view/InstancesWindow.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index d51667f4f8b4..fa02e715f144 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -328,12 +328,16 @@ public class InstancesWindow extends DialogWrapper { root.setMessage("The application is running", XDebuggerUIConstants.INFORMATION_MESSAGE_ICON, SimpleTextAttributes.REGULAR_ATTRIBUTES, null); + myProgress.setVisible(false); }); } @Override public void sessionPaused() { - SwingUtilities.invokeLater(() -> myInstancesTree.rebuildAndRestore(myTreeState)); + SwingUtilities.invokeLater(() -> { + myProgress.setVisible(true); + myInstancesTree.rebuildAndRestore(myTreeState); + }); } } From c782ee412f187201b503a4cf0d43650614aeec8d Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Mon, 22 Aug 2016 11:59:42 +0300 Subject: [PATCH 043/558] Fix filtering condition for classes --- view/ClassesTable.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index ec2dd8a944a4..a3d6f37c4392 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -107,8 +107,8 @@ public class ClassesTable extends JBTable implements DataProvider { } String name = ref.name().toLowerCase(); - String pattern = myFilteringPattern; - return name.contains(pattern) || match(pattern.toLowerCase(), name.substring(name.lastIndexOf('.') + 1)); + String pattern = myFilteringPattern.toLowerCase(); + return name.contains(pattern) || match(pattern, name.substring(name.lastIndexOf('.') + 1)); } }); From aa1e91aa1979a5fddc203131e23c78d08dd068f4 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Mon, 22 Aug 2016 13:15:59 +0300 Subject: [PATCH 044/558] Use jb matcher, instead of custom. Highlight matched text fragments --- view/ClassesTable.java | 45 +++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index a3d6f37c4392..b1f9ed46766b 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -2,9 +2,14 @@ package org.jetbrains.debugger.memory.view; import com.intellij.openapi.actionSystem.DataKey; import com.intellij.openapi.actionSystem.DataProvider; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.codeStyle.MinusculeMatcher; +import com.intellij.psi.codeStyle.NameUtil; import com.intellij.ui.ColoredTableCellRenderer; import com.intellij.ui.SimpleTextAttributes; +import com.intellij.ui.speedSearch.SpeedSearchUtil; import com.intellij.ui.table.JBTable; +import com.intellij.util.containers.FList; import com.intellij.util.ui.JBUI; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; @@ -41,6 +46,7 @@ public class ClassesTable extends JBTable implements DataProvider { private boolean myOnlyWithDiff; private boolean myOnlyWithInstances; + private MinusculeMatcher myMatcher = NameUtil.buildMatcher("*").build(); private String myFilteringPattern = ""; private ConcurrentHashMap myCounts = new ConcurrentHashMap<>(); @@ -78,7 +84,20 @@ public class ClassesTable extends JBTable implements DataProvider { protected void customizeCellRenderer(JTable table, @Nullable Object value, boolean isSelected, boolean hasFocus, int row, int column) { String presentation = value == null ? "null" : ((ReferenceType) value).name(); - append(String.format(" %s", presentation), SimpleTextAttributes.REGULAR_ATTRIBUTES); + + append(" "); + if (value != null && isSelected) { + FList textRanges = myMatcher.matchingFragments(presentation); + if(textRanges != null) { + SimpleTextAttributes attributes = new SimpleTextAttributes(getBackground(), getForeground(), null, + SimpleTextAttributes.STYLE_SEARCH_MATCH); + SpeedSearchUtil.appendColoredFragments(this, presentation, textRanges, + SimpleTextAttributes.REGULAR_ATTRIBUTES, attributes); + return; + } + } + + append(String.format("%s", presentation), SimpleTextAttributes.REGULAR_ATTRIBUTES); } }); @@ -101,14 +120,9 @@ public class ClassesTable extends JBTable implements DataProvider { int ix = entry.getIdentifier(); ReferenceType ref = myElems.get(ix); DiffValue diff = myCounts.getOrDefault(ref, myUnknownValue); - if (myOnlyWithDiff && diff.diff() == 0 || - myOnlyWithInstances && !diff.hasInstance()) { - return false; - } + return !(myOnlyWithDiff && diff.diff() == 0 || myOnlyWithInstances && !diff.hasInstance()) + && myMatcher.matches(ref.name()); - String name = ref.name().toLowerCase(); - String pattern = myFilteringPattern.toLowerCase(); - return name.contains(pattern) || match(pattern, name.substring(name.lastIndexOf('.') + 1)); } }); @@ -140,6 +154,7 @@ public class ClassesTable extends JBTable implements DataProvider { void setFilterPattern(String pattern) { if (!myFilteringPattern.equals(pattern)) { myFilteringPattern = pattern; + myMatcher = NameUtil.buildMatcher("*" + pattern).build(); getRowSorter().allRowsChanged(); } } @@ -184,20 +199,6 @@ public class ClassesTable extends JBTable implements DataProvider { myModel.show(); } - // TODO: use jb filter - private boolean match(String pattern, String value) { - int i = 0; - int j = 0; - while (j < value.length()) { - if (i < pattern.length() && value.charAt(j) == pattern.charAt(i)) { - i++; - } - j++; - } - - return i == pattern.length(); - } - @Nullable @Override public Object getData(@NonNls String dataId) { From 0f7f09f0fc7b2e9c7ec740bdcf03140ac1f12276 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Mon, 22 Aug 2016 19:09:02 +0300 Subject: [PATCH 045/558] Support android plugin for IDEA Ultimate. Make DiffValue mutable --- utils/AndroidUtil.java | 12 +++ view/ClassesFilteredView.java | 40 +++++++--- view/ClassesTable.java | 146 ++++++++++++++++++++++++---------- 3 files changed, 145 insertions(+), 53 deletions(-) create mode 100644 utils/AndroidUtil.java diff --git a/utils/AndroidUtil.java b/utils/AndroidUtil.java new file mode 100644 index 000000000000..e74911112527 --- /dev/null +++ b/utils/AndroidUtil.java @@ -0,0 +1,12 @@ +package org.jetbrains.debugger.memory.utils; + +import com.sun.jdi.VirtualMachine; +import org.jetbrains.annotations.NotNull; + +public class AndroidUtil { + public static final int ANDROID_INSTANCES_COUNT_BATCH_SIZE = 500; + + public static boolean isAndroidVM(@NotNull VirtualMachine vm) { + return vm.name().toLowerCase().contains("dalvik"); + } +} diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 6652553c6d51..434bd8ee7366 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -12,6 +12,7 @@ import com.intellij.openapi.actionSystem.ActionPopupMenu; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.ui.*; +import com.intellij.util.SmartList; import com.intellij.util.ui.components.BorderLayoutPanel; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; @@ -22,6 +23,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.MemoryViewManager; import org.jetbrains.debugger.memory.component.MemoryViewManagerState; import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; +import org.jetbrains.debugger.memory.utils.AndroidUtil; import org.jetbrains.debugger.memory.utils.KeyboardUtils; import org.jetbrains.debugger.memory.utils.SingleAlarmWithMutableDelay; @@ -31,12 +33,15 @@ import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; +import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; public class ClassesFilteredView extends BorderLayoutPanel { private static final Logger LOG = Logger.getInstance(ClassesFilteredView.class); private final static double DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT = 0.5; + private final static int DEFAULT_BATCH_SIZE = Integer.MAX_VALUE; private final Project myProject; private final XDebugSession myDebugSession; @@ -199,17 +204,34 @@ public class ClassesFilteredView extends BorderLayoutPanel { } VirtualMachine vm = classes.get(0).virtualMachine(); - long start = System.nanoTime(); - long[] counts = vm.instanceCounts(classes); - long delay = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); - mySingleAlarm.setDelay((int) (DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT * delay)); + SwingUtilities.invokeAndWait(() -> myTable.setClasses(classes)); + int batchSize = AndroidUtil.isAndroidVM(vm) + ? AndroidUtil.ANDROID_INSTANCES_COUNT_BATCH_SIZE + : DEFAULT_BATCH_SIZE; + + List chunks = new SmartList<>(); + int size = classes.size(); + for (int begin = 0, end = Math.min(batchSize, size); + begin != size && contextIsValid(); + begin = end, end = Math.min(end + batchSize, size)) { + final List batch = classes.subList(begin, end); + + long start = System.nanoTime(); + final long[] counts = vm.instanceCounts(batch); + long delay = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); + + chunks.add(counts); + + mySingleAlarm.setDelay((int) (DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT * delay)); + LOG.info(String.format("Instances query time = %d ms. Count = %d", delay, batch.size())); + } + + long[] counts = chunks.size() == 1 ? chunks.get(0) : IntStream.range(0, chunks.size()).boxed() + .flatMapToLong(integer -> Arrays.stream(chunks.get(integer))) + .toArray(); - LOG.info(String.format("Instances query time = %d ms. Count = %d", delay, classes.size())); SwingUtilities.invokeLater(() -> { - if (contextIsValid()) { - myTable.setClassesAndCounts(classes, counts); - } - + myTable.updateInstanceCounts(classes, counts); myTable.setBusy(false); }); } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index b1f9ed46766b..d6a85043b638 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -79,39 +79,9 @@ public class ClassesTable extends JBTable implements DataProvider { setShowGrid(false); - setDefaultRenderer(ReferenceType.class, new ColoredTableCellRenderer() { - @Override - protected void customizeCellRenderer(JTable table, @Nullable Object value, - boolean isSelected, boolean hasFocus, int row, int column) { - String presentation = value == null ? "null" : ((ReferenceType) value).name(); + setDefaultRenderer(ReferenceType.class, new MyClassTableCellRenderer()); - append(" "); - if (value != null && isSelected) { - FList textRanges = myMatcher.matchingFragments(presentation); - if(textRanges != null) { - SimpleTextAttributes attributes = new SimpleTextAttributes(getBackground(), getForeground(), null, - SimpleTextAttributes.STYLE_SEARCH_MATCH); - SpeedSearchUtil.appendColoredFragments(this, presentation, textRanges, - SimpleTextAttributes.REGULAR_ATTRIBUTES, attributes); - return; - } - } - - append(String.format("%s", presentation), SimpleTextAttributes.REGULAR_ATTRIBUTES); - } - }); - - setDefaultRenderer(Integer.class, new ColoredTableCellRenderer() { - @Override - protected void customizeCellRenderer(JTable table, @Nullable Object value, - boolean selected, boolean hasFocus, int row, int column) { - if (value != null) { - int val = (Integer) value; - append(String.format("%s%d", val > 0 ? "+" : "", val)); - setTextAlign(SwingConstants.RIGHT); - } - } - }); + setDefaultRenderer(Long.class, new MyNumberTableCellRenderer()); TableRowSorter sorter = new TableRowSorter<>(myModel); sorter.setRowFilter(new RowFilter() { @@ -173,14 +143,11 @@ public class ClassesTable extends JBTable implements DataProvider { } } - void setClassesAndCounts(List classes, long[] counts) { + void setClasses(@NotNull List classes) { ReferenceType selectedClass = myModel.getSelectedClassBeforeHided(); final int newSelectedIndex = classes.indexOf(selectedClass); myElems = Collections.unmodifiableList(new ArrayList<>(classes)); - for (int i = 0, size = myElems.size(); i < size; i++) { - ReferenceType ref = myElems.get(i); - myCounts.put(ref, myCounts.getOrDefault(ref, myUnknownValue).update(counts[i])); - } + myCounts.values().forEach(DiffValue::makeObsolete); showContent(); @@ -191,6 +158,15 @@ public class ClassesTable extends JBTable implements DataProvider { } } + void updateInstanceCounts(@NotNull List refs, @NotNull long[] counts) { + for (int i = 0, size = refs.size(); i < size; i++) { + ReferenceType ref = refs.get(i); + myCounts.put(ref, myCounts.getOrDefault(ref, myUnknownValue).update(counts[i])); + } + + getRowSorter().allRowsChanged(); + } + void hideContent() { myModel.hide(); } @@ -235,7 +211,7 @@ public class ClassesTable extends JBTable implements DataProvider { return myCounts.getOrDefault(myElems.get(ix), myUnknownValue).myCurrentCount; } }, - new AbstractTableColumnDescriptor("Diff", Integer.class) { + new AbstractTableColumnDescriptor("Diff", Long.class) { @Override public Object getValue(int ix) { return myCounts.getOrDefault(myElems.get(ix), myUnknownValue).diff(); @@ -274,6 +250,18 @@ public class ClassesTable extends JBTable implements DataProvider { } } + /** + * State transmissions for DiffValue and UnknownDiffValue + * unknown -> diff + * diff -> diff + * diff -> obsolete + * obsolete -> diff + * + * State descriptions: + * Unknown - instances count never executed + * Diff - actual value + * Obsolete - query for updating value is executing right now + */ private static class UnknownDiffValue extends DiffValue { UnknownDiffValue() { super(0); @@ -284,15 +272,25 @@ public class ClassesTable extends JBTable implements DataProvider { return true; } + @Override + boolean isObsolete() { + return true; + } + + @Override + void makeObsolete() { + } + @Override DiffValue update(long count) { - return new DiffValue(count, count); + return new DiffValue(count); } } private static class DiffValue { - private final long myOldCount; - private final long myCurrentCount; + private long myOldCount; + private long myCurrentCount; + private boolean myIsObsolete = false; DiffValue(long count) { this(count, count); @@ -304,15 +302,75 @@ public class ClassesTable extends JBTable implements DataProvider { } DiffValue update(long count) { - return new DiffValue(myCurrentCount, count); + myOldCount = myCurrentCount; + myIsObsolete = false; + myCurrentCount = count; + return this; + } + + void makeObsolete() { + myIsObsolete = true; + } + + boolean isObsolete() { + return myIsObsolete; } boolean hasInstance() { return myCurrentCount > 0; } - int diff() { - return (int) (myCurrentCount - myOldCount); + long diff() { + return myCurrentCount - myOldCount; + } + } + + private class MyNumberTableCellRenderer extends ColoredTableCellRenderer { + @Override + protected void customizeCellRenderer(JTable table, @Nullable Object value, + boolean selected, boolean hasFocus, int row, int column) { + int ix = convertRowIndexToModel(row); + if(myCounts.getOrDefault(myElems.get(ix), myUnknownValue).isObsolete() || value == null) { + return; + } + + long val = (long) value; + if (column == DiffViewTableModel.COUNT_COLUMN_INDEX) { + renderCount(val); + } else { + renderDiff(val); + } + } + + private void renderDiff(long diff) { + append(String.format("%s%d", diff > 0 ? "+" : "", diff)); + setTextAlign(SwingConstants.RIGHT); + } + + private void renderCount(long count) { + append(String.valueOf(count)); + } + } + + private class MyClassTableCellRenderer extends ColoredTableCellRenderer { + @Override + protected void customizeCellRenderer(JTable table, @Nullable Object value, + boolean isSelected, boolean hasFocus, int row, int column) { + String presentation = value == null ? "null" : ((ReferenceType) value).name(); + + append(" "); + if (value != null && isSelected) { + FList textRanges = myMatcher.matchingFragments(presentation); + if (textRanges != null) { + SimpleTextAttributes attributes = new SimpleTextAttributes(getBackground(), getForeground(), null, + SimpleTextAttributes.STYLE_SEARCH_MATCH); + SpeedSearchUtil.appendColoredFragments(this, presentation, textRanges, + SimpleTextAttributes.REGULAR_ATTRIBUTES, attributes); + return; + } + } + + append(String.format("%s", presentation), SimpleTextAttributes.REGULAR_ATTRIBUTES); } } } From 6c4a6a8b5211078575466747d537834669d69185 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 23 Aug 2016 13:12:56 +0300 Subject: [PATCH 046/558] Show list of classes only when loading completed --- view/ClassesFilteredView.java | 12 ++++----- view/ClassesTable.java | 50 +++++++++-------------------------- 2 files changed, 17 insertions(+), 45 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 434bd8ee7366..d0c70a68b879 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -196,15 +196,13 @@ public class ClassesFilteredView extends BorderLayoutPanel { @Override public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { - myDebugProcess.getVirtualMachineProxy(); - List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); + final List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); if (classes.isEmpty()) { return; } VirtualMachine vm = classes.get(0).virtualMachine(); - SwingUtilities.invokeAndWait(() -> myTable.setClasses(classes)); int batchSize = AndroidUtil.isAndroidVM(vm) ? AndroidUtil.ANDROID_INSTANCES_COUNT_BATCH_SIZE : DEFAULT_BATCH_SIZE; @@ -214,10 +212,10 @@ public class ClassesFilteredView extends BorderLayoutPanel { for (int begin = 0, end = Math.min(batchSize, size); begin != size && contextIsValid(); begin = end, end = Math.min(end + batchSize, size)) { - final List batch = classes.subList(begin, end); + List batch = classes.subList(begin, end); long start = System.nanoTime(); - final long[] counts = vm.instanceCounts(batch); + long[] counts = vm.instanceCounts(batch); long delay = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); chunks.add(counts); @@ -226,12 +224,12 @@ public class ClassesFilteredView extends BorderLayoutPanel { LOG.info(String.format("Instances query time = %d ms. Count = %d", delay, batch.size())); } - long[] counts = chunks.size() == 1 ? chunks.get(0) : IntStream.range(0, chunks.size()).boxed() + final long[] counts = chunks.size() == 1 ? chunks.get(0) : IntStream.range(0, chunks.size()).boxed() .flatMapToLong(integer -> Arrays.stream(chunks.get(integer))) .toArray(); SwingUtilities.invokeLater(() -> { - myTable.updateInstanceCounts(classes, counts); + myTable.setClassesAndUpdateCounts(classes, counts); myTable.setBusy(false); }); } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index d6a85043b638..1ffac18c9f53 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -143,11 +143,16 @@ public class ClassesTable extends JBTable implements DataProvider { } } - void setClasses(@NotNull List classes) { + void setClassesAndUpdateCounts(@NotNull List classes, @NotNull long[] counts) { + assert classes.size() == counts.length; ReferenceType selectedClass = myModel.getSelectedClassBeforeHided(); - final int newSelectedIndex = classes.indexOf(selectedClass); + int newSelectedIndex = classes.indexOf(selectedClass); myElems = Collections.unmodifiableList(new ArrayList<>(classes)); - myCounts.values().forEach(DiffValue::makeObsolete); + + for (int i = 0, size = classes.size(); i < size; i++) { + ReferenceType ref = classes.get(i); + myCounts.put(ref, myCounts.getOrDefault(ref, myUnknownValue).update(counts[i])); + } showContent(); @@ -158,15 +163,6 @@ public class ClassesTable extends JBTable implements DataProvider { } } - void updateInstanceCounts(@NotNull List refs, @NotNull long[] counts) { - for (int i = 0, size = refs.size(); i < size; i++) { - ReferenceType ref = refs.get(i); - myCounts.put(ref, myCounts.getOrDefault(ref, myUnknownValue).update(counts[i])); - } - - getRowSorter().allRowsChanged(); - } - void hideContent() { myModel.hide(); } @@ -254,13 +250,10 @@ public class ClassesTable extends JBTable implements DataProvider { * State transmissions for DiffValue and UnknownDiffValue * unknown -> diff * diff -> diff - * diff -> obsolete - * obsolete -> diff - * + *

* State descriptions: * Unknown - instances count never executed * Diff - actual value - * Obsolete - query for updating value is executing right now */ private static class UnknownDiffValue extends DiffValue { UnknownDiffValue() { @@ -272,15 +265,6 @@ public class ClassesTable extends JBTable implements DataProvider { return true; } - @Override - boolean isObsolete() { - return true; - } - - @Override - void makeObsolete() { - } - @Override DiffValue update(long count) { return new DiffValue(count); @@ -290,7 +274,6 @@ public class ClassesTable extends JBTable implements DataProvider { private static class DiffValue { private long myOldCount; private long myCurrentCount; - private boolean myIsObsolete = false; DiffValue(long count) { this(count, count); @@ -303,19 +286,10 @@ public class ClassesTable extends JBTable implements DataProvider { DiffValue update(long count) { myOldCount = myCurrentCount; - myIsObsolete = false; myCurrentCount = count; return this; } - void makeObsolete() { - myIsObsolete = true; - } - - boolean isObsolete() { - return myIsObsolete; - } - boolean hasInstance() { return myCurrentCount > 0; } @@ -329,12 +303,12 @@ public class ClassesTable extends JBTable implements DataProvider { @Override protected void customizeCellRenderer(JTable table, @Nullable Object value, boolean selected, boolean hasFocus, int row, int column) { - int ix = convertRowIndexToModel(row); - if(myCounts.getOrDefault(myElems.get(ix), myUnknownValue).isObsolete() || value == null) { + if (value == null) { return; } long val = (long) value; + setTextAlign(SwingConstants.RIGHT); if (column == DiffViewTableModel.COUNT_COLUMN_INDEX) { renderCount(val); } else { @@ -344,11 +318,11 @@ public class ClassesTable extends JBTable implements DataProvider { private void renderDiff(long diff) { append(String.format("%s%d", diff > 0 ? "+" : "", diff)); - setTextAlign(SwingConstants.RIGHT); } private void renderCount(long count) { append(String.valueOf(count)); + } } From 442ab0aeed0114b6bb3727972ee3b917c548dc77 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 23 Aug 2016 15:03:37 +0300 Subject: [PATCH 047/558] Set limit for instances count for android --- utils/AndroidUtil.java | 3 ++- view/ClassesFilteredView.java | 2 +- view/InstancesWindow.java | 14 +++++++++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/utils/AndroidUtil.java b/utils/AndroidUtil.java index e74911112527..112297fd4a96 100644 --- a/utils/AndroidUtil.java +++ b/utils/AndroidUtil.java @@ -4,7 +4,8 @@ import com.sun.jdi.VirtualMachine; import org.jetbrains.annotations.NotNull; public class AndroidUtil { - public static final int ANDROID_INSTANCES_COUNT_BATCH_SIZE = 500; + public static final int ANDROID_COUNT_BY_CLASSES_BATCH_SIZE = 500; + public static final int ANDROID_INSTANCES_LIMIT = 30000; public static boolean isAndroidVM(@NotNull VirtualMachine vm) { return vm.name().toLowerCase().contains("dalvik"); diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index d0c70a68b879..e6736331928c 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -204,7 +204,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { VirtualMachine vm = classes.get(0).virtualMachine(); int batchSize = AndroidUtil.isAndroidVM(vm) - ? AndroidUtil.ANDROID_INSTANCES_COUNT_BATCH_SIZE + ? AndroidUtil.ANDROID_COUNT_BY_CLASSES_BATCH_SIZE : DEFAULT_BATCH_SIZE; List chunks = new SmartList<>(); diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index fa02e715f144..0ca5d928230b 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -54,6 +54,7 @@ import com.sun.jdi.ReferenceType; import com.sun.jdi.Value; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.utils.AndroidUtil; import org.jetbrains.debugger.memory.utils.ErrorsValueGroup; import org.jetbrains.debugger.memory.utils.InstanceJavaValue; import org.jetbrains.debugger.memory.utils.InstanceValueDescriptor; @@ -72,6 +73,7 @@ public class InstancesWindow extends DialogWrapper { private static final int DEFAULT_WINDOW_HEIGHT = 400; private static final int FILTERING_BUTTON_ADDITIONAL_WIDTH = 30; private static final int BORDER_LAYOUT_DEFAULT_GAP = 5; + private static final int DEFAULT_INSTANCES_COUNT = 0; // All private final Project myProject; private final XDebugSession myDebugSession; @@ -240,10 +242,20 @@ public class InstancesWindow extends DialogWrapper { @Override public void threadAction(@NotNull SuspendContextImpl suspendContext) { - List instances = myReferenceType.instances(0); + int limit = AndroidUtil.isAndroidVM(myReferenceType.virtualMachine()) + ? AndroidUtil.ANDROID_INSTANCES_LIMIT + : DEFAULT_INSTANCES_COUNT; + List instances = myReferenceType.instances(limit); + EvaluationContextImpl evaluationContext = debugProcess .getDebuggerContext().createEvaluationContext(); + //noinspection StatementWithEmptyBody + if (AndroidUtil.isAndroidVM(myReferenceType.virtualMachine()) + && instances.size() == AndroidUtil.ANDROID_INSTANCES_LIMIT) { + // TODO: show warning + } + if (evaluationContext != null) { synchronized (myFilteringTaskLock) { myFilteringTask = new MyFilteringWorker(instances, evaluationContext, createEvaluator()); From 7f2dbc028332939f1922a4904e53c474c19b45ef Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 23 Aug 2016 16:09:59 +0300 Subject: [PATCH 048/558] Show warning if vm is dalvik and limitation for instances has been exceeded --- view/InstancesWindow.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 0ca5d928230b..b7eb2c39a70b 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -87,7 +87,7 @@ public class InstancesWindow extends DialogWrapper { myProject = session.getProject(); myDebugSession = session; myReferenceType = referenceType; - setTitle("Instances of " + referenceType.name()); + addWarningMessage(null); myDebugSession.addSessionListener(new XDebugSessionListener() { @Override public void sessionStopped() { @@ -104,6 +104,11 @@ public class InstancesWindow extends DialogWrapper { ALL_CHECKED, INTERRUPTED, LIMIT_REACHED } + private void addWarningMessage(@Nullable String message) { + String warning = message == null ? "" : String.format(". Warning: %s", message); + setTitle(String.format("Instances of %s%s", myReferenceType.name(), warning)); + } + @NotNull @Override protected String getDimensionServiceKey() { @@ -250,10 +255,9 @@ public class InstancesWindow extends DialogWrapper { EvaluationContextImpl evaluationContext = debugProcess .getDebuggerContext().createEvaluationContext(); - //noinspection StatementWithEmptyBody if (AndroidUtil.isAndroidVM(myReferenceType.virtualMachine()) && instances.size() == AndroidUtil.ANDROID_INSTANCES_LIMIT) { - // TODO: show warning + addWarningMessage("Not all instances will be shown (android restriction)"); } if (evaluationContext != null) { @@ -263,7 +267,6 @@ public class InstancesWindow extends DialogWrapper { myProgress.start(instances.size()); myFilteringTask.execute(); }); - myFilteringTask.execute(); } } } From 15d4e9f73fe0ea265ffcb5bf1efec3286397a21d Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 23 Aug 2016 19:14:22 +0300 Subject: [PATCH 049/558] Fix bug when "Show Instances" action enabled when no selected class --- action/ShowInstancesFromClassesViewAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/action/ShowInstancesFromClassesViewAction.java b/action/ShowInstancesFromClassesViewAction.java index 9034d583091a..ebbda9b9435a 100644 --- a/action/ShowInstancesFromClassesViewAction.java +++ b/action/ShowInstancesFromClassesViewAction.java @@ -8,7 +8,7 @@ import org.jetbrains.debugger.memory.view.InstancesWindow; public class ShowInstancesFromClassesViewAction extends ClassesActionBase { @Override protected boolean isEnabled(AnActionEvent e) { - return super.isEnabled(e); + return super.isEnabled(e) && getSelectedClass(e) != null ; } @Override From 99d19c88d23390f1fc9cdf9328288b468952763c Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 23 Aug 2016 19:31:46 +0300 Subject: [PATCH 050/558] Hide grid in selected element of classes table --- view/ClassesTable.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 1ffac18c9f53..1c2ced59bf9c 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -10,6 +10,7 @@ import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.speedSearch.SpeedSearchUtil; import com.intellij.ui.table.JBTable; import com.intellij.util.containers.FList; +import com.intellij.util.ui.JBDimension; import com.intellij.util.ui.JBUI; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; @@ -78,9 +79,9 @@ public class ClassesTable extends JBTable implements DataProvider { diffColumn.setResizable(false); setShowGrid(false); + setIntercellSpacing(new JBDimension(0, 0)); setDefaultRenderer(ReferenceType.class, new MyClassTableCellRenderer()); - setDefaultRenderer(Long.class, new MyNumberTableCellRenderer()); TableRowSorter sorter = new TableRowSorter<>(myModel); From 716d52fe42ca1cd6af460a01c85d2a550897eb59 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 23 Aug 2016 20:10:36 +0300 Subject: [PATCH 051/558] Add context menu actions for tracking of instances --- action/ClassesActionBase.java | 2 +- action/tracking/HashBasedTrackingAction.java | 11 +++++++++++ action/tracking/IdentityBasedTrackingAction.java | 11 +++++++++++ .../tracking/RetainReferenceBasedTrackingAction.java | 12 ++++++++++++ 4 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 action/tracking/HashBasedTrackingAction.java create mode 100644 action/tracking/IdentityBasedTrackingAction.java create mode 100644 action/tracking/RetainReferenceBasedTrackingAction.java diff --git a/action/ClassesActionBase.java b/action/ClassesActionBase.java index 51e2c3a550a5..cdd3a50dd892 100644 --- a/action/ClassesActionBase.java +++ b/action/ClassesActionBase.java @@ -8,7 +8,7 @@ import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.view.ClassesTable; -abstract class ClassesActionBase extends AnAction { +public abstract class ClassesActionBase extends AnAction { @Override public void update(AnActionEvent e) { e.getPresentation().setEnabled(isEnabled(e)); diff --git a/action/tracking/HashBasedTrackingAction.java b/action/tracking/HashBasedTrackingAction.java new file mode 100644 index 000000000000..f0813c177eb6 --- /dev/null +++ b/action/tracking/HashBasedTrackingAction.java @@ -0,0 +1,11 @@ +package org.jetbrains.debugger.memory.action.tracking; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import org.jetbrains.debugger.memory.action.ClassesActionBase; + +public class HashBasedTrackingAction extends ClassesActionBase { + @Override + protected void perform(AnActionEvent e) { + + } +} diff --git a/action/tracking/IdentityBasedTrackingAction.java b/action/tracking/IdentityBasedTrackingAction.java new file mode 100644 index 000000000000..577f47090bc4 --- /dev/null +++ b/action/tracking/IdentityBasedTrackingAction.java @@ -0,0 +1,11 @@ +package org.jetbrains.debugger.memory.action.tracking; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import org.jetbrains.debugger.memory.action.ClassesActionBase; + +public class IdentityBasedTrackingAction extends ClassesActionBase { + @Override + protected void perform(AnActionEvent e) { + + } +} diff --git a/action/tracking/RetainReferenceBasedTrackingAction.java b/action/tracking/RetainReferenceBasedTrackingAction.java new file mode 100644 index 000000000000..095a79965b6a --- /dev/null +++ b/action/tracking/RetainReferenceBasedTrackingAction.java @@ -0,0 +1,12 @@ +package org.jetbrains.debugger.memory.action.tracking; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import org.jetbrains.debugger.memory.action.ClassesActionBase; + +public class RetainReferenceBasedTrackingAction extends ClassesActionBase { + + @Override + protected void perform(AnActionEvent e) { + + } +} From 90b8f9ea8f226bad0ceb262b771b57ba2e8e474a Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 24 Aug 2016 11:15:58 +0300 Subject: [PATCH 052/558] Add persistent project component, for storing classes which were attached for tracking --- action/ClassesActionBase.java | 2 +- action/tracking/HashBasedTrackingAction.java | 10 ++- .../tracking/IdentityBasedTrackingAction.java | 9 +- .../RetainReferenceBasedTrackingAction.java | 10 ++- component/InstancesTracker.java | 89 +++++++++++++++++++ 5 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 component/InstancesTracker.java diff --git a/action/ClassesActionBase.java b/action/ClassesActionBase.java index cdd3a50dd892..e534614afd83 100644 --- a/action/ClassesActionBase.java +++ b/action/ClassesActionBase.java @@ -27,7 +27,7 @@ public abstract class ClassesActionBase extends AnAction { protected abstract void perform(AnActionEvent e); @Nullable - ReferenceType getSelectedClass(AnActionEvent e) { + protected ReferenceType getSelectedClass(AnActionEvent e) { return e.getData(ClassesTable.SELECTED_CLASS_KEY); } diff --git a/action/tracking/HashBasedTrackingAction.java b/action/tracking/HashBasedTrackingAction.java index f0813c177eb6..fa53a12e63ff 100644 --- a/action/tracking/HashBasedTrackingAction.java +++ b/action/tracking/HashBasedTrackingAction.java @@ -1,11 +1,19 @@ package org.jetbrains.debugger.memory.action.tracking; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.Project; +import com.sun.jdi.ReferenceType; import org.jetbrains.debugger.memory.action.ClassesActionBase; +import org.jetbrains.debugger.memory.component.InstancesTracker; +import org.jetbrains.debugger.memory.component.InstancesTracker.TrackingType; public class HashBasedTrackingAction extends ClassesActionBase { @Override protected void perform(AnActionEvent e) { - + Project project = e.getProject(); + ReferenceType selectedClass = getSelectedClass(e); + if (project != null && selectedClass != null) { + InstancesTracker.getInstance(project).add(selectedClass, TrackingType.HASH); + } } } diff --git a/action/tracking/IdentityBasedTrackingAction.java b/action/tracking/IdentityBasedTrackingAction.java index 577f47090bc4..cbed9a55ee5d 100644 --- a/action/tracking/IdentityBasedTrackingAction.java +++ b/action/tracking/IdentityBasedTrackingAction.java @@ -1,11 +1,18 @@ package org.jetbrains.debugger.memory.action.tracking; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.Project; +import com.sun.jdi.ReferenceType; import org.jetbrains.debugger.memory.action.ClassesActionBase; +import org.jetbrains.debugger.memory.component.InstancesTracker; public class IdentityBasedTrackingAction extends ClassesActionBase { @Override protected void perform(AnActionEvent e) { - + Project project = e.getProject(); + ReferenceType selectedClass = getSelectedClass(e); + if (project != null && selectedClass != null) { + InstancesTracker.getInstance(project).add(selectedClass, InstancesTracker.TrackingType.IDENTITY); + } } } diff --git a/action/tracking/RetainReferenceBasedTrackingAction.java b/action/tracking/RetainReferenceBasedTrackingAction.java index 095a79965b6a..ad7441506ed2 100644 --- a/action/tracking/RetainReferenceBasedTrackingAction.java +++ b/action/tracking/RetainReferenceBasedTrackingAction.java @@ -1,12 +1,18 @@ package org.jetbrains.debugger.memory.action.tracking; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.Project; +import com.sun.jdi.ReferenceType; import org.jetbrains.debugger.memory.action.ClassesActionBase; +import org.jetbrains.debugger.memory.component.InstancesTracker; public class RetainReferenceBasedTrackingAction extends ClassesActionBase { - @Override protected void perform(AnActionEvent e) { - + Project project = e.getProject(); + ReferenceType selectedClass = getSelectedClass(e); + if (project != null && selectedClass != null) { + InstancesTracker.getInstance(project).add(selectedClass, InstancesTracker.TrackingType.RETAIN); + } } } diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java new file mode 100644 index 000000000000..5a8c932a5af0 --- /dev/null +++ b/component/InstancesTracker.java @@ -0,0 +1,89 @@ +package org.jetbrains.debugger.memory.component; + +import com.intellij.openapi.components.*; +import com.intellij.openapi.project.Project; +import com.intellij.util.xmlb.annotations.AbstractCollection; +import com.intellij.util.xmlb.annotations.Attribute; +import com.intellij.util.xmlb.annotations.Tag; +import com.sun.jdi.ReferenceType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Vector; + +@State(name = "InstancesTracker", storages = @Storage(StoragePathMacros.WORKSPACE_FILE)) +public class InstancesTracker extends AbstractProjectComponent + implements PersistentStateComponent { + private MyState myState = new MyState(); + + public InstancesTracker(Project project) { + super(project); + } + + public static InstancesTracker getInstance(@NotNull Project project) { + return project.getComponent(InstancesTracker.class); + } + + public enum TrackingType { + IDENTITY, HASH, RETAIN + } + + public void add(@NotNull ReferenceType ref, @NotNull TrackingType type) { + myState.classes.add(new TrackingClassState(ref.name(), type)); + } + + @SuppressWarnings("unused") + public boolean remove(@NotNull ReferenceType ref, @NotNull TrackingType type) { + return myState.classes.removeIf(state -> state.className.equals(ref.name()) && type.equals(state.trackingType)); + } + + @Nullable + @Override + public MyState getState() { + return new MyState(myState); + } + + @Override + public void loadState(MyState state) { + myState = new MyState(state); + } + + static class MyState { + @NotNull + @AbstractCollection(surroundWithTag = false, elementTypes = {TrackingClassState.class}) + final List classes = new Vector<>(); + + MyState() { + } + + MyState(@NotNull MyState state) { + for (TrackingClassState classState : state.classes) { + TrackingClassState newState = new TrackingClassState(); + newState.className = classState.className; + newState.trackingType = classState.trackingType; + classes.add(newState); + } + } + } + + @Tag("class-state") + private static class TrackingClassState { + @NotNull + @Attribute("name") + String className =""; + + @Nullable + @Attribute("type") + TrackingType trackingType; + + @SuppressWarnings("WeakerAccess") + public TrackingClassState() { + } + + TrackingClassState(@NotNull String name, @NotNull TrackingType type) { + className = name; + trackingType = type; + } + } +} From 027a81370264e37e20a715dae7ea3ccbefd56383 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 24 Aug 2016 11:44:33 +0300 Subject: [PATCH 053/558] Allow use only single method for tracking of instances of some class --- component/InstancesTracker.java | 70 ++++++++++++++--------------- event/InstancesTrackerListener.java | 14 ++++++ 2 files changed, 48 insertions(+), 36 deletions(-) create mode 100644 event/InstancesTrackerListener.java diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java index 5a8c932a5af0..8546d9ca954b 100644 --- a/component/InstancesTracker.java +++ b/component/InstancesTracker.java @@ -2,19 +2,23 @@ package org.jetbrains.debugger.memory.component; import com.intellij.openapi.components.*; import com.intellij.openapi.project.Project; +import com.intellij.util.EventDispatcher; +import com.intellij.util.containers.HashMap; import com.intellij.util.xmlb.annotations.AbstractCollection; -import com.intellij.util.xmlb.annotations.Attribute; -import com.intellij.util.xmlb.annotations.Tag; import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.event.InstancesTrackerListener; -import java.util.List; -import java.util.Vector; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; @State(name = "InstancesTracker", storages = @Storage(StoragePathMacros.WORKSPACE_FILE)) public class InstancesTracker extends AbstractProjectComponent implements PersistentStateComponent { + private final EventDispatcher myDispatcher = + EventDispatcher.create(InstancesTrackerListener.class); private MyState myState = new MyState(); public InstancesTracker(Project project) { @@ -29,13 +33,30 @@ public class InstancesTracker extends AbstractProjectComponent IDENTITY, HASH, RETAIN } - public void add(@NotNull ReferenceType ref, @NotNull TrackingType type) { - myState.classes.add(new TrackingClassState(ref.name(), type)); + public Map getTrackingClasses() { + Map copy = new HashMap<>(myState.classes); + return Collections.unmodifiableMap(copy); } - @SuppressWarnings("unused") - public boolean remove(@NotNull ReferenceType ref, @NotNull TrackingType type) { - return myState.classes.removeIf(state -> state.className.equals(ref.name()) && type.equals(state.trackingType)); + public void add(@NotNull ReferenceType ref, @NotNull TrackingType type) { + String name = ref.name(); + if(type.equals(myState.classes.getOrDefault(name, null))) { + return; + } + + myState.classes.put(ref.name(), type); + myDispatcher.getMulticaster().classAdded(name, type); + } + + public boolean remove(@NotNull ReferenceType ref) { + String name = ref.name(); + TrackingType removed = myState.classes.remove(name); + if(removed != null) { + myDispatcher.getMulticaster().classRemoved(name); + return true; + } + + return false; } @Nullable @@ -51,39 +72,16 @@ public class InstancesTracker extends AbstractProjectComponent static class MyState { @NotNull - @AbstractCollection(surroundWithTag = false, elementTypes = {TrackingClassState.class}) - final List classes = new Vector<>(); + @AbstractCollection(surroundWithTag = false, elementTypes = {Map.Entry.class}) + final Map classes = new ConcurrentHashMap<>(); MyState() { } MyState(@NotNull MyState state) { - for (TrackingClassState classState : state.classes) { - TrackingClassState newState = new TrackingClassState(); - newState.className = classState.className; - newState.trackingType = classState.trackingType; - classes.add(newState); + for (Map.Entry classState : state.classes.entrySet()) { + classes.put(classState.getKey(), classState.getValue()); } } } - - @Tag("class-state") - private static class TrackingClassState { - @NotNull - @Attribute("name") - String className =""; - - @Nullable - @Attribute("type") - TrackingType trackingType; - - @SuppressWarnings("WeakerAccess") - public TrackingClassState() { - } - - TrackingClassState(@NotNull String name, @NotNull TrackingType type) { - className = name; - trackingType = type; - } - } } diff --git a/event/InstancesTrackerListener.java b/event/InstancesTrackerListener.java new file mode 100644 index 000000000000..bc0fd49f301d --- /dev/null +++ b/event/InstancesTrackerListener.java @@ -0,0 +1,14 @@ +package org.jetbrains.debugger.memory.event; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.debugger.memory.component.InstancesTracker; + +import java.util.EventListener; + +public interface InstancesTrackerListener extends EventListener{ + default void classAdded(@NotNull String name, @NotNull InstancesTracker.TrackingType type) { + } + + default void classRemoved(@NotNull String name) { + } +} From 85f958daf550823a3a715e9a3660a79113bcc0e2 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 24 Aug 2016 13:09:03 +0300 Subject: [PATCH 054/558] Make classes view disposable, and highlight classes for which tracking enabled --- component/InstancesTracker.java | 6 +++ component/MemoryViewManager.java | 9 ++--- toolwindow/MemoryViewToolWindowFactory.java | 6 ++- utils/SingleAlarmWithMutableDelay.java | 6 ++- view/ClassesFilteredView.java | 16 +++++--- view/ClassesTable.java | 41 ++++++++++++++++++--- 6 files changed, 66 insertions(+), 18 deletions(-) diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java index 8546d9ca954b..6b1c84a540bf 100644 --- a/component/InstancesTracker.java +++ b/component/InstancesTracker.java @@ -1,5 +1,6 @@ package org.jetbrains.debugger.memory.component; +import com.intellij.openapi.Disposable; import com.intellij.openapi.components.*; import com.intellij.openapi.project.Project; import com.intellij.util.EventDispatcher; @@ -48,6 +49,7 @@ public class InstancesTracker extends AbstractProjectComponent myDispatcher.getMulticaster().classAdded(name, type); } + @SuppressWarnings("unused") public boolean remove(@NotNull ReferenceType ref) { String name = ref.name(); TrackingType removed = myState.classes.remove(name); @@ -59,6 +61,10 @@ public class InstancesTracker extends AbstractProjectComponent return false; } + public void addTrackerListener(@NotNull InstancesTrackerListener listener, @NotNull Disposable parentDisposable) { + myDispatcher.addListener(listener, parentDisposable); + } + @Nullable @Override public MyState getState() { diff --git a/component/MemoryViewManager.java b/component/MemoryViewManager.java index 42949b2bc59c..1af153b035a6 100644 --- a/component/MemoryViewManager.java +++ b/component/MemoryViewManager.java @@ -1,5 +1,6 @@ package org.jetbrains.debugger.memory.component; +import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.*; import com.intellij.openapi.project.Project; @@ -60,12 +61,8 @@ public class MemoryViewManager extends ApplicationComponent.Adapter return myState.isShowWithInstancesOnly; } - public void addMemoryViewManagerListener(MemoryViewManagerListener listener) { - myDispatcher.addListener(listener); - } - - public void removeMemoryViewManagerListener(MemoryViewManagerListener listener) { - myDispatcher.removeListener(listener); + public void addMemoryViewManagerListener(MemoryViewManagerListener listener, @NotNull Disposable parentDisposable) { + myDispatcher.addListener(listener, parentDisposable); } @Nullable diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java index 88f425b3d312..2b8680786822 100644 --- a/toolwindow/MemoryViewToolWindowFactory.java +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -9,6 +9,7 @@ import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowFactory; import com.intellij.openapi.wm.ex.ToolWindowEx; @@ -93,7 +94,10 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware } private void removeSession(@NotNull XDebugSession session) { - myMemoryViews.remove(session); + ClassesFilteredView removed = myMemoryViews.remove(session); + if(removed != null) { + Disposer.dispose(removed); + } } private void updateCurrentMemoryView(@NotNull Project project, @NotNull ToolWindow toolWindow) { diff --git a/utils/SingleAlarmWithMutableDelay.java b/utils/SingleAlarmWithMutableDelay.java index 43687ce17139..75cb937c187c 100644 --- a/utils/SingleAlarmWithMutableDelay.java +++ b/utils/SingleAlarmWithMutableDelay.java @@ -1,12 +1,16 @@ package org.jetbrains.debugger.memory.utils; +import com.intellij.openapi.Disposable; import com.intellij.util.Alarm; +import com.intellij.util.config.ToggleBooleanProperty; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class SingleAlarmWithMutableDelay extends Alarm { private final Runnable myTask; private volatile int myDelay; - public SingleAlarmWithMutableDelay(@NotNull Runnable task) { + public SingleAlarmWithMutableDelay(@NotNull Runnable task, @NotNull Disposable parentDisposable) { + super(parentDisposable); myTask = task; } diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index e6736331928c..e32f299b437f 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -6,11 +6,13 @@ import com.intellij.debugger.engine.DebuggerManagerThreadImpl; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; import com.intellij.debugger.impl.DebuggerManagerImpl; +import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.ActionPopupMenu; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; import com.intellij.ui.*; import com.intellij.util.SmartList; import com.intellij.util.ui.components.BorderLayoutPanel; @@ -38,7 +40,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; -public class ClassesFilteredView extends BorderLayoutPanel { +public class ClassesFilteredView extends BorderLayoutPanel implements Disposable { private static final Logger LOG = Logger.getInstance(ClassesFilteredView.class); private final static double DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT = 0.5; private final static int DEFAULT_BATCH_SIZE = Integer.MAX_VALUE; @@ -66,6 +68,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { myTable = new ClassesTable(myDebugSession, memoryViewManagerState.isShowWithDiffOnly, memoryViewManagerState.isShowWithInstancesOnly); + Disposer.register(this, myTable); myTable.addKeyListener(new KeyAdapter() { @Override @@ -94,7 +97,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { myTable.setFilteringByInstanceExists(state.isShowWithInstancesOnly); }; - MemoryViewManager.getInstance().addMemoryViewManagerListener(memoryViewManagerListener); + MemoryViewManager.getInstance().addMemoryViewManagerListener(memoryViewManagerListener, this); myDebugSession.addSessionListener(new XDebugSessionListener() { @Override @@ -106,7 +109,6 @@ public class ClassesFilteredView extends BorderLayoutPanel { @Override public void sessionStopped() { debugSession.removeSessionListener(this); - MemoryViewManager.getInstance().removeMemoryViewManagerListener(memoryViewManagerListener); } @Override @@ -115,7 +117,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { updateClassesAndCounts(); } } - }); + }, this); myFilterTextField.addDocumentListener(new DocumentAdapter() { @Override @@ -133,7 +135,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { .schedule(new MyUpdateClassesCommand(myLastSuspendContext)); }); } - }); + }, this); myTable.addMouseListener(new PopupHandler() { @Override @@ -183,6 +185,10 @@ public class ClassesFilteredView extends BorderLayoutPanel { return ActionManager.getInstance().createActionPopupMenu("ClassesView.PopupActionGroup", group); } + @Override + public void dispose() { + } + private final class MyUpdateClassesCommand extends SuspendContextCommandImpl { MyUpdateClassesCommand(@Nullable SuspendContextImpl suspendContext) { diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 1c2ced59bf9c..983f812ec7f2 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -1,35 +1,38 @@ package org.jetbrains.debugger.memory.view; +import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.DataKey; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.util.TextRange; import com.intellij.psi.codeStyle.MinusculeMatcher; import com.intellij.psi.codeStyle.NameUtil; import com.intellij.ui.ColoredTableCellRenderer; +import com.intellij.ui.JBColor; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.speedSearch.SpeedSearchUtil; import com.intellij.ui.table.JBTable; import com.intellij.util.containers.FList; +import com.intellij.util.containers.HashMap; import com.intellij.util.ui.JBDimension; import com.intellij.util.ui.JBUI; +import com.intellij.util.ui.UIUtil; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.component.InstancesTracker; +import org.jetbrains.debugger.memory.event.InstancesTrackerListener; import org.jetbrains.debugger.memory.utils.AbstractTableColumnDescriptor; import org.jetbrains.debugger.memory.utils.AbstractTableModelWithColumns; import javax.swing.*; import javax.swing.table.TableColumn; import javax.swing.table.TableRowSorter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; -public class ClassesTable extends JBTable implements DataProvider { +public class ClassesTable extends JBTable implements DataProvider, Disposable { public static final DataKey SELECTED_CLASS_KEY = DataKey.create("ClassesTable.SelectedClass"); public static final DataKey DEBUG_SESSION_KEY = DataKey.create("ClassesTable.DebugSession"); private static final int CLASSES_COLUMN_PREFERRED_WIDTH = 250; @@ -43,6 +46,7 @@ public class ClassesTable extends JBTable implements DataProvider { private final DiffViewTableModel myModel = new DiffViewTableModel(); private final UnknownDiffValue myUnknownValue = new UnknownDiffValue(); private final XDebugSession myDebugSession; + private final Map myTrackedClasses; private boolean myOnlyWithDiff; private boolean myOnlyWithInstances; @@ -62,6 +66,22 @@ public class ClassesTable extends JBTable implements DataProvider { getEmptyText().setText(EMPTY_TABLE_CONTENT); + InstancesTracker tracker = InstancesTracker.getInstance(myDebugSession.getProject()); + tracker.addTrackerListener(new InstancesTrackerListener() { + @Override + public void classAdded(@NotNull String name, @NotNull InstancesTracker.TrackingType type) { + myTrackedClasses.put(name, type); + getRowSorter().allRowsChanged(); + } + + @Override + public void classRemoved(@NotNull String name) { + myTrackedClasses.remove(name); + getRowSorter().allRowsChanged(); + } + }, this); + myTrackedClasses = new HashMap<>(tracker.getTrackingClasses()); + TableColumn classesColumn = getColumnModel().getColumn(DiffViewTableModel.CLASSNAME_COLUMN_INDEX); TableColumn countColumn = getColumnModel().getColumn(DiffViewTableModel.COUNT_COLUMN_INDEX); TableColumn diffColumn = getColumnModel().getColumn(DiffViewTableModel.DIFF_COLUMN_INDEX); @@ -185,6 +205,10 @@ public class ClassesTable extends JBTable implements DataProvider { return null; } + @Override + public void dispose() { + } + private class DiffViewTableModel extends AbstractTableModelWithColumns { private final static int CLASSNAME_COLUMN_INDEX = 0; private final static int COUNT_COLUMN_INDEX = 1; @@ -308,6 +332,10 @@ public class ClassesTable extends JBTable implements DataProvider { return; } + String className = ((ReferenceType) getValueAt(row, DiffViewTableModel.CLASSNAME_COLUMN_INDEX)).name(); + if (!selected && myTrackedClasses.containsKey(className)) { + setBackground(UIUtil.toAlpha(JBColor.yellow, 20)); + } long val = (long) value; setTextAlign(SwingConstants.RIGHT); if (column == DiffViewTableModel.COUNT_COLUMN_INDEX) { @@ -332,6 +360,9 @@ public class ClassesTable extends JBTable implements DataProvider { protected void customizeCellRenderer(JTable table, @Nullable Object value, boolean isSelected, boolean hasFocus, int row, int column) { String presentation = value == null ? "null" : ((ReferenceType) value).name(); + if (value != null && !isSelected && myTrackedClasses.containsKey(presentation)) { + setBackground(UIUtil.toAlpha(JBColor.yellow, 20)); + } append(" "); if (value != null && isSelected) { From e95da174d13b8c505cd9d5e9dc3f775e0b5e8114 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 24 Aug 2016 13:43:39 +0300 Subject: [PATCH 055/558] Add action for disabling of instance tracking, hide tracking action when class not selected --- action/tracking/DisableTrackingAction.java | 26 ++++++++++++++++++++++ action/tracking/TrackingActionGroup.java | 13 +++++++++++ component/InstancesTracker.java | 5 ++++- 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 action/tracking/DisableTrackingAction.java create mode 100644 action/tracking/TrackingActionGroup.java diff --git a/action/tracking/DisableTrackingAction.java b/action/tracking/DisableTrackingAction.java new file mode 100644 index 000000000000..a492115e8d5b --- /dev/null +++ b/action/tracking/DisableTrackingAction.java @@ -0,0 +1,26 @@ +package org.jetbrains.debugger.memory.action.tracking; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.Project; +import com.sun.jdi.ReferenceType; +import org.jetbrains.debugger.memory.action.ClassesActionBase; +import org.jetbrains.debugger.memory.component.InstancesTracker; + +public class DisableTrackingAction extends ClassesActionBase { + @Override + protected boolean isEnabled(AnActionEvent e) { + Project project = e.getProject(); + ReferenceType selectedClass = getSelectedClass(e); + return super.isEnabled(e) && selectedClass != null && + project != null && InstancesTracker.getInstance(project).isTracked(selectedClass.name()); + } + + @Override + protected void perform(AnActionEvent e) { + Project project = e.getProject(); + ReferenceType selectedClass = getSelectedClass(e); + if(project != null && selectedClass != null) { + InstancesTracker.getInstance(project).remove(selectedClass); + } + } +} diff --git a/action/tracking/TrackingActionGroup.java b/action/tracking/TrackingActionGroup.java new file mode 100644 index 000000000000..fd586f3125dc --- /dev/null +++ b/action/tracking/TrackingActionGroup.java @@ -0,0 +1,13 @@ +package org.jetbrains.debugger.memory.action.tracking; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.project.DumbAware; +import org.jetbrains.debugger.memory.view.ClassesTable; + +public class TrackingActionGroup extends DefaultActionGroup implements DumbAware{ + @Override + public void update(AnActionEvent e) { + e.getPresentation().setVisible(e.getData(ClassesTable.SELECTED_CLASS_KEY) != null); + } +} diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java index 6b1c84a540bf..879a4c2bc9fc 100644 --- a/component/InstancesTracker.java +++ b/component/InstancesTracker.java @@ -34,6 +34,10 @@ public class InstancesTracker extends AbstractProjectComponent IDENTITY, HASH, RETAIN } + public boolean isTracked(@NotNull String className) { + return myState.classes.containsKey(className); + } + public Map getTrackingClasses() { Map copy = new HashMap<>(myState.classes); return Collections.unmodifiableMap(copy); @@ -49,7 +53,6 @@ public class InstancesTracker extends AbstractProjectComponent myDispatcher.getMulticaster().classAdded(name, type); } - @SuppressWarnings("unused") public boolean remove(@NotNull ReferenceType ref) { String name = ref.name(); TrackingType removed = myState.classes.remove(name); From 2c09a549a862c22b8a20c4f4737592e943c9facd Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 24 Aug 2016 15:00:13 +0300 Subject: [PATCH 056/558] Add basic implementation for algorithms for tracking of instances --- tracking/HashBasedTracking.java | 20 ++++++++++++++++++++ tracking/IdBasedTracking.java | 20 ++++++++++++++++++++ tracking/InstanceTrackingStrategy.java | 25 +++++++++++++++++++++++++ tracking/RetainReferencesTracking.java | 24 ++++++++++++++++++++++++ 4 files changed, 89 insertions(+) create mode 100644 tracking/HashBasedTracking.java create mode 100644 tracking/IdBasedTracking.java create mode 100644 tracking/InstanceTrackingStrategy.java create mode 100644 tracking/RetainReferencesTracking.java diff --git a/tracking/HashBasedTracking.java b/tracking/HashBasedTracking.java new file mode 100644 index 000000000000..0394202d4664 --- /dev/null +++ b/tracking/HashBasedTracking.java @@ -0,0 +1,20 @@ +package org.jetbrains.debugger.memory.tracking; + +import com.sun.jdi.ObjectReference; +import org.apache.commons.lang.NotImplementedException; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class HashBasedTracking extends InstanceTrackingStrategy { + + public HashBasedTracking(@NotNull List initialInstances) { + + } + + @NotNull + @Override + protected List updateImpl(@NotNull List references) { + throw new NotImplementedException(); + } +} diff --git a/tracking/IdBasedTracking.java b/tracking/IdBasedTracking.java new file mode 100644 index 000000000000..070aad0f2e2c --- /dev/null +++ b/tracking/IdBasedTracking.java @@ -0,0 +1,20 @@ +package org.jetbrains.debugger.memory.tracking; + +import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.stream.Collectors; + +public class IdBasedTracking extends InstanceTrackingStrategy { + private long myLastId = -1; + public IdBasedTracking(@NotNull List initialInstances) { + myLastId = initialInstances.stream().map(ObjectReference::uniqueID).max(Long::compare).orElse(-1L); + } + + @NotNull + @Override + protected List updateImpl(@NotNull List references) { + return references.stream().filter(reference -> reference.uniqueID() > myLastId).collect(Collectors.toList()); + } +} diff --git a/tracking/InstanceTrackingStrategy.java b/tracking/InstanceTrackingStrategy.java new file mode 100644 index 000000000000..0a9451d57cd0 --- /dev/null +++ b/tracking/InstanceTrackingStrategy.java @@ -0,0 +1,25 @@ +package org.jetbrains.debugger.memory.tracking; + +import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public abstract class InstanceTrackingStrategy { + @NotNull + private List myNewInstances = new ArrayList<>(); + + @NotNull + public final List getNewInstances() { + return Collections.unmodifiableList(myNewInstances); + } + + public final void update(@NotNull List references) { + myNewInstances = updateImpl(references); + } + + @NotNull + protected abstract List updateImpl(@NotNull List references); +} diff --git a/tracking/RetainReferencesTracking.java b/tracking/RetainReferencesTracking.java new file mode 100644 index 000000000000..d213e8a2417a --- /dev/null +++ b/tracking/RetainReferencesTracking.java @@ -0,0 +1,24 @@ +package org.jetbrains.debugger.memory.tracking; + +import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class RetainReferencesTracking extends InstanceTrackingStrategy { + private final IdBasedTracking myIdTracker; + @SuppressWarnings("unused") + private List myHardRefs; + public RetainReferencesTracking(@NotNull List initialInstances) { + myHardRefs = initialInstances; + myIdTracker = new IdBasedTracking(initialInstances); + } + + @NotNull + @Override + protected List updateImpl(@NotNull List references) { + myIdTracker.update(references); + myHardRefs = references; + return myIdTracker.getNewInstances(); + } +} From 5d4010f769b55c4db3bde9038f0c733352c1ec3a Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 24 Aug 2016 16:02:08 +0300 Subject: [PATCH 057/558] Fix error in id-based tracking --- tracking/IdBasedTracking.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tracking/IdBasedTracking.java b/tracking/IdBasedTracking.java index 070aad0f2e2c..4394e54d6bc7 100644 --- a/tracking/IdBasedTracking.java +++ b/tracking/IdBasedTracking.java @@ -9,12 +9,20 @@ import java.util.stream.Collectors; public class IdBasedTracking extends InstanceTrackingStrategy { private long myLastId = -1; public IdBasedTracking(@NotNull List initialInstances) { - myLastId = initialInstances.stream().map(ObjectReference::uniqueID).max(Long::compare).orElse(-1L); + myLastId = getMaxId(initialInstances); } @NotNull @Override protected List updateImpl(@NotNull List references) { - return references.stream().filter(reference -> reference.uniqueID() > myLastId).collect(Collectors.toList()); + List result = references.stream() + .filter(reference -> reference.uniqueID() > myLastId) + .collect(Collectors.toList()); + myLastId = Math.max(myLastId, getMaxId(result)); + return result; + } + + private long getMaxId(@NotNull List references) { + return references.stream().map(ObjectReference::uniqueID).max(Long::compare).orElse(-1L); } } From 68f75b23ae0009c92a30cdfacdac359b726bb11b Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 24 Aug 2016 16:40:34 +0300 Subject: [PATCH 058/558] Add toggle ability for instances tracking actions --- action/tracking/HashBasedTrackingAction.java | 18 +++---- .../tracking/IdentityBasedTrackingAction.java | 16 ++----- .../tracking/InstancesTrackingActionBase.java | 47 +++++++++++++++++++ .../RetainReferenceBasedTrackingAction.java | 16 ++----- component/InstancesTracker.java | 8 +++- event/InstancesTrackerListener.java | 2 +- view/ClassesTable.java | 2 +- 7 files changed, 72 insertions(+), 37 deletions(-) create mode 100644 action/tracking/InstancesTrackingActionBase.java diff --git a/action/tracking/HashBasedTrackingAction.java b/action/tracking/HashBasedTrackingAction.java index fa53a12e63ff..afac0f214808 100644 --- a/action/tracking/HashBasedTrackingAction.java +++ b/action/tracking/HashBasedTrackingAction.java @@ -1,19 +1,13 @@ package org.jetbrains.debugger.memory.action.tracking; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; -import com.sun.jdi.ReferenceType; -import org.jetbrains.debugger.memory.action.ClassesActionBase; -import org.jetbrains.debugger.memory.component.InstancesTracker; +import org.jetbrains.annotations.NotNull; import org.jetbrains.debugger.memory.component.InstancesTracker.TrackingType; -public class HashBasedTrackingAction extends ClassesActionBase { +public class HashBasedTrackingAction extends InstancesTrackingActionBase { + @NotNull @Override - protected void perform(AnActionEvent e) { - Project project = e.getProject(); - ReferenceType selectedClass = getSelectedClass(e); - if (project != null && selectedClass != null) { - InstancesTracker.getInstance(project).add(selectedClass, TrackingType.HASH); - } + protected TrackingType getType() { + return TrackingType.HASH; } } + diff --git a/action/tracking/IdentityBasedTrackingAction.java b/action/tracking/IdentityBasedTrackingAction.java index cbed9a55ee5d..6b04df12181d 100644 --- a/action/tracking/IdentityBasedTrackingAction.java +++ b/action/tracking/IdentityBasedTrackingAction.java @@ -1,18 +1,12 @@ package org.jetbrains.debugger.memory.action.tracking; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; -import com.sun.jdi.ReferenceType; -import org.jetbrains.debugger.memory.action.ClassesActionBase; +import org.jetbrains.annotations.NotNull; import org.jetbrains.debugger.memory.component.InstancesTracker; -public class IdentityBasedTrackingAction extends ClassesActionBase { +public class IdentityBasedTrackingAction extends InstancesTrackingActionBase { + @NotNull @Override - protected void perform(AnActionEvent e) { - Project project = e.getProject(); - ReferenceType selectedClass = getSelectedClass(e); - if (project != null && selectedClass != null) { - InstancesTracker.getInstance(project).add(selectedClass, InstancesTracker.TrackingType.IDENTITY); - } + protected InstancesTracker.TrackingType getType() { + return InstancesTracker.TrackingType.IDENTITY; } } diff --git a/action/tracking/InstancesTrackingActionBase.java b/action/tracking/InstancesTrackingActionBase.java new file mode 100644 index 000000000000..e7c904db694d --- /dev/null +++ b/action/tracking/InstancesTrackingActionBase.java @@ -0,0 +1,47 @@ +package org.jetbrains.debugger.memory.action.tracking; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.project.Project; +import com.sun.jdi.ReferenceType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.component.InstancesTracker; +import org.jetbrains.debugger.memory.view.ClassesTable; + +import static org.jetbrains.debugger.memory.component.InstancesTracker.TrackingType; + +abstract class InstancesTrackingActionBase extends ToggleAction { + @Override + public void setSelected(AnActionEvent e, boolean state) { + TrackingType type = getType(); + ReferenceType selectedClass = getSelectedClass(e); + Project project = e.getProject(); + if (selectedClass != null && project != null) { + InstancesTracker tracker = InstancesTracker.getInstance(project); + TrackingType currentType = tracker.getTrackingType(selectedClass.name()); + if (state) { + tracker.add(selectedClass, type); + } else if(type.equals(currentType)) { + tracker.remove(selectedClass); + } + } + } + + @Override + public boolean isSelected(AnActionEvent e) { + TrackingType type = getType(); + ReferenceType selectedClass = getSelectedClass(e); + Project project = e.getProject(); + return selectedClass != null && project != null && + type.equals(InstancesTracker.getInstance(project).getTrackingType(selectedClass.name())); + } + + @NotNull + protected abstract TrackingType getType(); + + @Nullable + private ReferenceType getSelectedClass(AnActionEvent e) { + return e.getData(ClassesTable.SELECTED_CLASS_KEY); + } +} diff --git a/action/tracking/RetainReferenceBasedTrackingAction.java b/action/tracking/RetainReferenceBasedTrackingAction.java index ad7441506ed2..b0d118470227 100644 --- a/action/tracking/RetainReferenceBasedTrackingAction.java +++ b/action/tracking/RetainReferenceBasedTrackingAction.java @@ -1,18 +1,12 @@ package org.jetbrains.debugger.memory.action.tracking; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; -import com.sun.jdi.ReferenceType; -import org.jetbrains.debugger.memory.action.ClassesActionBase; +import org.jetbrains.annotations.NotNull; import org.jetbrains.debugger.memory.component.InstancesTracker; -public class RetainReferenceBasedTrackingAction extends ClassesActionBase { +public class RetainReferenceBasedTrackingAction extends InstancesTrackingActionBase { + @NotNull @Override - protected void perform(AnActionEvent e) { - Project project = e.getProject(); - ReferenceType selectedClass = getSelectedClass(e); - if (project != null && selectedClass != null) { - InstancesTracker.getInstance(project).add(selectedClass, InstancesTracker.TrackingType.RETAIN); - } + protected InstancesTracker.TrackingType getType() { + return InstancesTracker.TrackingType.RETAIN; } } diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java index 879a4c2bc9fc..29594c125a78 100644 --- a/component/InstancesTracker.java +++ b/component/InstancesTracker.java @@ -38,6 +38,12 @@ public class InstancesTracker extends AbstractProjectComponent return myState.classes.containsKey(className); } + @Nullable + public TrackingType getTrackingType(@NotNull String className) { + return myState.classes.getOrDefault(className, null); + } + + public Map getTrackingClasses() { Map copy = new HashMap<>(myState.classes); return Collections.unmodifiableMap(copy); @@ -50,7 +56,7 @@ public class InstancesTracker extends AbstractProjectComponent } myState.classes.put(ref.name(), type); - myDispatcher.getMulticaster().classAdded(name, type); + myDispatcher.getMulticaster().classChanged(name, type); } public boolean remove(@NotNull ReferenceType ref) { diff --git a/event/InstancesTrackerListener.java b/event/InstancesTrackerListener.java index bc0fd49f301d..ed03afbc4173 100644 --- a/event/InstancesTrackerListener.java +++ b/event/InstancesTrackerListener.java @@ -6,7 +6,7 @@ import org.jetbrains.debugger.memory.component.InstancesTracker; import java.util.EventListener; public interface InstancesTrackerListener extends EventListener{ - default void classAdded(@NotNull String name, @NotNull InstancesTracker.TrackingType type) { + default void classChanged(@NotNull String name, @NotNull InstancesTracker.TrackingType type) { } default void classRemoved(@NotNull String name) { diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 983f812ec7f2..830803917450 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -69,7 +69,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { InstancesTracker tracker = InstancesTracker.getInstance(myDebugSession.getProject()); tracker.addTrackerListener(new InstancesTrackerListener() { @Override - public void classAdded(@NotNull String name, @NotNull InstancesTracker.TrackingType type) { + public void classChanged(@NotNull String name, @NotNull InstancesTracker.TrackingType type) { myTrackedClasses.put(name, type); getRowSorter().allRowsChanged(); } From 45fc3d16385fa10c98aed6d7cf31631c48f8ae71 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 24 Aug 2016 18:47:39 +0300 Subject: [PATCH 059/558] Add static creator method for strategy classes --- tracking/HashBasedTracking.java | 4 ++-- ...acking.java => IdentityBasedTracking.java} | 4 ++-- tracking/InstanceTrackingStrategy.java | 21 ++++++++++++++++--- tracking/RetainReferencesTracking.java | 8 +++---- 4 files changed, 26 insertions(+), 11 deletions(-) rename tracking/{IdBasedTracking.java => IdentityBasedTracking.java} (84%) diff --git a/tracking/HashBasedTracking.java b/tracking/HashBasedTracking.java index 0394202d4664..b3c02ce934d1 100644 --- a/tracking/HashBasedTracking.java +++ b/tracking/HashBasedTracking.java @@ -6,9 +6,9 @@ import org.jetbrains.annotations.NotNull; import java.util.List; -public class HashBasedTracking extends InstanceTrackingStrategy { +class HashBasedTracking extends InstanceTrackingStrategy { - public HashBasedTracking(@NotNull List initialInstances) { + HashBasedTracking(@NotNull List initialInstances) { } diff --git a/tracking/IdBasedTracking.java b/tracking/IdentityBasedTracking.java similarity index 84% rename from tracking/IdBasedTracking.java rename to tracking/IdentityBasedTracking.java index 4394e54d6bc7..78ef6670576a 100644 --- a/tracking/IdBasedTracking.java +++ b/tracking/IdentityBasedTracking.java @@ -6,9 +6,9 @@ import org.jetbrains.annotations.NotNull; import java.util.List; import java.util.stream.Collectors; -public class IdBasedTracking extends InstanceTrackingStrategy { +class IdentityBasedTracking extends InstanceTrackingStrategy { private long myLastId = -1; - public IdBasedTracking(@NotNull List initialInstances) { + IdentityBasedTracking(@NotNull List initialInstances) { myLastId = getMaxId(initialInstances); } diff --git a/tracking/InstanceTrackingStrategy.java b/tracking/InstanceTrackingStrategy.java index 0a9451d57cd0..558ed9dc3943 100644 --- a/tracking/InstanceTrackingStrategy.java +++ b/tracking/InstanceTrackingStrategy.java @@ -7,16 +7,31 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -public abstract class InstanceTrackingStrategy { +import static org.jetbrains.debugger.memory.component.InstancesTracker.*; + +abstract class InstanceTrackingStrategy { @NotNull private List myNewInstances = new ArrayList<>(); + public static InstanceTrackingStrategy create(@NotNull TrackingType type, @NotNull List init) { + switch (type) { + case IDENTITY: + return new IdentityBasedTracking(init); + case HASH: + return new HashBasedTracking(init); + case RETAIN: + return new RetainReferencesTracking(init); + } + + throw new UnsupportedOperationException("Such TrackingType not found"); + } + @NotNull - public final List getNewInstances() { + final List getNewInstances() { return Collections.unmodifiableList(myNewInstances); } - public final void update(@NotNull List references) { + final void update(@NotNull List references) { myNewInstances = updateImpl(references); } diff --git a/tracking/RetainReferencesTracking.java b/tracking/RetainReferencesTracking.java index d213e8a2417a..1830dd4d58de 100644 --- a/tracking/RetainReferencesTracking.java +++ b/tracking/RetainReferencesTracking.java @@ -5,13 +5,13 @@ import org.jetbrains.annotations.NotNull; import java.util.List; -public class RetainReferencesTracking extends InstanceTrackingStrategy { - private final IdBasedTracking myIdTracker; +class RetainReferencesTracking extends InstanceTrackingStrategy { + private final IdentityBasedTracking myIdTracker; @SuppressWarnings("unused") private List myHardRefs; - public RetainReferencesTracking(@NotNull List initialInstances) { + RetainReferencesTracking(@NotNull List initialInstances) { myHardRefs = initialInstances; - myIdTracker = new IdBasedTracking(initialInstances); + myIdTracker = new IdentityBasedTracking(initialInstances); } @NotNull From 86e3ba494cc24aa51c7545a3eff29f83862761d0 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 24 Aug 2016 19:58:55 +0300 Subject: [PATCH 060/558] Change InstancesWindow structure, use InstanceProvider and class name instead of ReferenceType --- action/ShowInstancesByClassAction.java | 4 ++- .../ShowInstancesFromClassesViewAction.java | 2 +- utils/InstancesProvider.java | 11 +++++++ view/ClassesFilteredView.java | 2 +- view/ClassesTable.java | 28 +++++++++------- view/ExpressionEditorWithHistory.java | 7 ++-- view/InstancesWindow.java | 32 ++++++++++--------- 7 files changed, 53 insertions(+), 33 deletions(-) create mode 100644 utils/InstancesProvider.java diff --git a/action/ShowInstancesByClassAction.java b/action/ShowInstancesByClassAction.java index 7bc2b3c19a8f..451af6f9b1f4 100644 --- a/action/ShowInstancesByClassAction.java +++ b/action/ShowInstancesByClassAction.java @@ -12,6 +12,7 @@ import com.intellij.xdebugger.frame.XValue; import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; import com.sun.jdi.Value; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -36,7 +37,8 @@ public class ShowInstancesByClassAction extends XDebuggerTreeActionBase { XDebugSession debugSession = XDebuggerManager.getInstance(project).getCurrentSession(); ObjectReference ref = getObjectReference(node); if(debugSession != null && ref != null){ - new InstancesWindow(debugSession, ref.referenceType()).show(); + ReferenceType referenceType = ref.referenceType(); + new InstancesWindow(debugSession, referenceType::instances, referenceType.name()).show(); } } } diff --git a/action/ShowInstancesFromClassesViewAction.java b/action/ShowInstancesFromClassesViewAction.java index ebbda9b9435a..a03c5ec1f288 100644 --- a/action/ShowInstancesFromClassesViewAction.java +++ b/action/ShowInstancesFromClassesViewAction.java @@ -16,7 +16,7 @@ public class ShowInstancesFromClassesViewAction extends ClassesActionBase { XDebugSession debugSession = getDebugSession(e); ReferenceType selectedClass = getSelectedClass(e); if (debugSession != null && selectedClass != null) { - new InstancesWindow(debugSession, selectedClass).show(); + new InstancesWindow(debugSession, selectedClass::instances, selectedClass.name()).show(); } } } diff --git a/utils/InstancesProvider.java b/utils/InstancesProvider.java new file mode 100644 index 000000000000..1a717474b1b8 --- /dev/null +++ b/utils/InstancesProvider.java @@ -0,0 +1,11 @@ +package org.jetbrains.debugger.memory.utils; + +import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface InstancesProvider { + @NotNull + List getInstances(int limit); +} diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index e32f299b437f..820405f3449f 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -166,7 +166,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private void handleClassSelection(@Nullable ReferenceType ref) { if (ref != null && myDebugSession.isSuspended()) { - new InstancesWindow(myDebugSession, ref).show(); + new InstancesWindow(myDebugSession, ref::instances, ref.name()).show(); } } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 830803917450..249d4c64106c 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -12,7 +12,6 @@ import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.speedSearch.SpeedSearchUtil; import com.intellij.ui.table.JBTable; import com.intellij.util.containers.FList; -import com.intellij.util.containers.HashMap; import com.intellij.util.ui.JBDimension; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; @@ -46,14 +45,14 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private final DiffViewTableModel myModel = new DiffViewTableModel(); private final UnknownDiffValue myUnknownValue = new UnknownDiffValue(); private final XDebugSession myDebugSession; - private final Map myTrackedClasses; + private final Map myCounts = new ConcurrentHashMap<>(); + private final InstancesTracker myInstancesTracker; private boolean myOnlyWithDiff; - private boolean myOnlyWithInstances; + private boolean myOnlyWithInstances; private MinusculeMatcher myMatcher = NameUtil.buildMatcher("*").build(); private String myFilteringPattern = ""; - private ConcurrentHashMap myCounts = new ConcurrentHashMap<>(); private volatile List myElems = Collections.unmodifiableList(new ArrayList<>()); @@ -63,6 +62,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { myDebugSession = session; myOnlyWithDiff = onlyWithDiff; myOnlyWithInstances = onlyWithInstances; + myInstancesTracker = InstancesTracker.getInstance(myDebugSession.getProject()); getEmptyText().setText(EMPTY_TABLE_CONTENT); @@ -70,17 +70,14 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { tracker.addTrackerListener(new InstancesTrackerListener() { @Override public void classChanged(@NotNull String name, @NotNull InstancesTracker.TrackingType type) { - myTrackedClasses.put(name, type); getRowSorter().allRowsChanged(); } @Override public void classRemoved(@NotNull String name) { - myTrackedClasses.remove(name); getRowSorter().allRowsChanged(); } }, this); - myTrackedClasses = new HashMap<>(tracker.getTrackingClasses()); TableColumn classesColumn = getColumnModel().getColumn(DiffViewTableModel.CLASSNAME_COLUMN_INDEX); TableColumn countColumn = getColumnModel().getColumn(DiffViewTableModel.COUNT_COLUMN_INDEX); @@ -209,6 +206,15 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { public void dispose() { } + private boolean needHighlightAsTracked(boolean isSelected, int row) { + if(isSelected){ + return false; + } + + ReferenceType ref = (ReferenceType) getValueAt(row, DiffViewTableModel.CLASSNAME_COLUMN_INDEX); + return myInstancesTracker.isTracked(ref.name()); + } + private class DiffViewTableModel extends AbstractTableModelWithColumns { private final static int CLASSNAME_COLUMN_INDEX = 0; private final static int COUNT_COLUMN_INDEX = 1; @@ -327,15 +333,15 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private class MyNumberTableCellRenderer extends ColoredTableCellRenderer { @Override protected void customizeCellRenderer(JTable table, @Nullable Object value, - boolean selected, boolean hasFocus, int row, int column) { + boolean isSelected, boolean hasFocus, int row, int column) { if (value == null) { return; } - String className = ((ReferenceType) getValueAt(row, DiffViewTableModel.CLASSNAME_COLUMN_INDEX)).name(); - if (!selected && myTrackedClasses.containsKey(className)) { + if (needHighlightAsTracked(isSelected, row)) { setBackground(UIUtil.toAlpha(JBColor.yellow, 20)); } + long val = (long) value; setTextAlign(SwingConstants.RIGHT); if (column == DiffViewTableModel.COUNT_COLUMN_INDEX) { @@ -360,7 +366,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { protected void customizeCellRenderer(JTable table, @Nullable Object value, boolean isSelected, boolean hasFocus, int row, int column) { String presentation = value == null ? "null" : ((ReferenceType) value).name(); - if (value != null && !isSelected && myTrackedClasses.containsKey(presentation)) { + if (needHighlightAsTracked(isSelected, row)) { setBackground(UIUtil.toAlpha(JBColor.yellow, 20)); } diff --git a/view/ExpressionEditorWithHistory.java b/view/ExpressionEditorWithHistory.java index ab34a0972d00..d11e0e100873 100644 --- a/view/ExpressionEditorWithHistory.java +++ b/view/ExpressionEditorWithHistory.java @@ -20,7 +20,6 @@ import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; import com.intellij.xdebugger.impl.XSourcePositionImpl; import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl; import com.intellij.xdebugger.impl.ui.XDebuggerExpressionEditor; -import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -31,10 +30,10 @@ class ExpressionEditorWithHistory extends XDebuggerExpressionEditor { private static final String HISTORY_ID_PREFIX = "filtering"; ExpressionEditorWithHistory(final @NotNull Project project, - final @NotNull ReferenceType referenceType, + final @NotNull String className, final @NotNull XDebuggerEditorsProvider debuggerEditorsProvider, final @Nullable Disposable parentDisposable) { - super(project, debuggerEditorsProvider, HISTORY_ID_PREFIX + referenceType.name(), null, + super(project, debuggerEditorsProvider, HISTORY_ID_PREFIX + className, null, XExpressionImpl.EMPTY_EXPRESSION, false, true, true); new AnAction("InstancesWindow.ShowHistory") { @@ -53,7 +52,7 @@ class ExpressionEditorWithHistory extends XDebuggerExpressionEditor { @Override protected Void doInBackground() throws Exception { ApplicationManager.getApplication().runReadAction(() -> { - final PsiClass psiClass = DebuggerUtils.findClass(referenceType.name(), + final PsiClass psiClass = DebuggerUtils.findClass(className, project, GlobalSearchScope.allScope(project)); XSourcePositionImpl position = XSourcePositionImpl.createByElement(psiClass); SwingUtilities.invokeLater(() -> setSourcePosition(position)); diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index b7eb2c39a70b..bbf13da7a553 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -50,14 +50,10 @@ import com.intellij.xdebugger.impl.ui.tree.nodes.XDebuggerTreeNode; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.sun.jdi.BooleanValue; import com.sun.jdi.ObjectReference; -import com.sun.jdi.ReferenceType; import com.sun.jdi.Value; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.utils.AndroidUtil; -import org.jetbrains.debugger.memory.utils.ErrorsValueGroup; -import org.jetbrains.debugger.memory.utils.InstanceJavaValue; -import org.jetbrains.debugger.memory.utils.InstanceValueDescriptor; +import org.jetbrains.debugger.memory.utils.*; import org.jetbrains.java.debugger.JavaDebuggerEditorsProvider; import javax.swing.*; @@ -77,16 +73,20 @@ public class InstancesWindow extends DialogWrapper { private final Project myProject; private final XDebugSession myDebugSession; - private final ReferenceType myReferenceType; + private final InstancesProvider myInstancesProvider; + private final String myClassName; private MyInstancesView myInstancesView; public InstancesWindow(@NotNull XDebugSession session, - @NotNull ReferenceType referenceType) { + @NotNull InstancesProvider provider, + @NotNull String className) { super(session.getProject(), false); myProject = session.getProject(); myDebugSession = session; - myReferenceType = referenceType; + myInstancesProvider = provider; + myClassName = className; + addWarningMessage(null); myDebugSession.addSessionListener(new XDebugSessionListener() { @Override @@ -106,7 +106,7 @@ public class InstancesWindow extends DialogWrapper { private void addWarningMessage(@Nullable String message) { String warning = message == null ? "" : String.format(". Warning: %s", message); - setTitle(String.format("Instances of %s%s", myReferenceType.name(), warning)); + setTitle(String.format("Instances of %s%s", myClassName, warning)); } @NotNull @@ -164,6 +164,8 @@ public class InstancesWindow extends DialogWrapper { private final AnActionListener.Adapter myActionListener = new MyActionListener(); private final Object myFilteringTaskLock = new Object(); + private boolean myIsAndroidVM = false; + private volatile MyFilteringWorker myFilteringTask = null; MyInstancesView() { @@ -174,7 +176,7 @@ public class InstancesWindow extends DialogWrapper { myDebugSession.addSessionListener(myDebugSessionListener, InstancesWindow.this.myDisposable); JavaDebuggerEditorsProvider editorsProvider = new JavaDebuggerEditorsProvider(); - myFilterConditionEditor = new ExpressionEditorWithHistory(myProject, myReferenceType, + myFilterConditionEditor = new ExpressionEditorWithHistory(myProject, myClassName, editorsProvider, InstancesWindow.this.myDisposable); myFilterButton.setBorder(BorderFactory.createEmptyBorder()); @@ -247,16 +249,16 @@ public class InstancesWindow extends DialogWrapper { @Override public void threadAction(@NotNull SuspendContextImpl suspendContext) { - int limit = AndroidUtil.isAndroidVM(myReferenceType.virtualMachine()) + myIsAndroidVM = AndroidUtil.isAndroidVM(debugProcess.getVirtualMachineProxy().getVirtualMachine()); + int limit = myIsAndroidVM ? AndroidUtil.ANDROID_INSTANCES_LIMIT : DEFAULT_INSTANCES_COUNT; - List instances = myReferenceType.instances(limit); + List instances = myInstancesProvider.getInstances(limit); EvaluationContextImpl evaluationContext = debugProcess .getDebuggerContext().createEvaluationContext(); - if (AndroidUtil.isAndroidVM(myReferenceType.virtualMachine()) - && instances.size() == AndroidUtil.ANDROID_INSTANCES_LIMIT) { + if (myIsAndroidVM && instances.size() == AndroidUtil.ANDROID_INSTANCES_LIMIT) { addWarningMessage("Not all instances will be shown (android restriction)"); } @@ -400,7 +402,7 @@ public class InstancesWindow extends DialogWrapper { @Override @NotNull protected String getClassName() { - return myReferenceType.name(); + return myClassName; } ExpressionEvaluator getEvaluator() throws EvaluateException { From 6a9e73f17d1258188291f8f4b548f32c4b950272 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 24 Aug 2016 20:00:06 +0300 Subject: [PATCH 061/558] Add "Show New Instances" action to context menu of Classes view --- action/ShowNewInstancesAction.java | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 action/ShowNewInstancesAction.java diff --git a/action/ShowNewInstancesAction.java b/action/ShowNewInstancesAction.java new file mode 100644 index 000000000000..c4cbbe8a283e --- /dev/null +++ b/action/ShowNewInstancesAction.java @@ -0,0 +1,21 @@ +package org.jetbrains.debugger.memory.action; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.Project; +import com.sun.jdi.ReferenceType; +import org.jetbrains.debugger.memory.component.InstancesTracker; + +public class ShowNewInstancesAction extends ClassesActionBase { + @Override + protected boolean isEnabled(AnActionEvent e) { + Project project = e.getProject(); + ReferenceType selectedClass = getSelectedClass(e); + return super.isEnabled(e) && selectedClass != null && + project != null && InstancesTracker.getInstance(project).isTracked(selectedClass.name()); + } + + @Override + protected void perform(AnActionEvent e) { + + } +} From 8ff814ac61dc88278e1864f8b5dffeb1a80fd370 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 24 Aug 2016 20:44:42 +0300 Subject: [PATCH 062/558] Add basic implementation for new instances tracking --- action/ShowNewInstancesAction.java | 11 +++++++++- tracking/InstanceTrackingStrategy.java | 6 +++--- view/ClassesFilteredView.java | 28 +++++++++++++++++++++++++- view/ClassesTable.java | 22 +++++++++++++++++++- 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/action/ShowNewInstancesAction.java b/action/ShowNewInstancesAction.java index c4cbbe8a283e..7261022a63ca 100644 --- a/action/ShowNewInstancesAction.java +++ b/action/ShowNewInstancesAction.java @@ -2,8 +2,12 @@ package org.jetbrains.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.Project; +import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; import org.jetbrains.debugger.memory.component.InstancesTracker; +import org.jetbrains.debugger.memory.utils.InstancesProvider; +import org.jetbrains.debugger.memory.view.ClassesTable; +import org.jetbrains.debugger.memory.view.InstancesWindow; public class ShowNewInstancesAction extends ClassesActionBase { @Override @@ -16,6 +20,11 @@ public class ShowNewInstancesAction extends ClassesActionBase { @Override protected void perform(AnActionEvent e) { - + XDebugSession session = getDebugSession(e); + ReferenceType selectedClass = getSelectedClass(e); + InstancesProvider provider = e.getData(ClassesTable.NEW_INSTANCES_PROVIDER_KEY); + if(selectedClass != null && provider != null && session != null) { + new InstancesWindow(session, provider, selectedClass.name()).show(); + } } } diff --git a/tracking/InstanceTrackingStrategy.java b/tracking/InstanceTrackingStrategy.java index 558ed9dc3943..3a37afc3cee6 100644 --- a/tracking/InstanceTrackingStrategy.java +++ b/tracking/InstanceTrackingStrategy.java @@ -9,7 +9,7 @@ import java.util.List; import static org.jetbrains.debugger.memory.component.InstancesTracker.*; -abstract class InstanceTrackingStrategy { +public abstract class InstanceTrackingStrategy { @NotNull private List myNewInstances = new ArrayList<>(); @@ -27,11 +27,11 @@ abstract class InstanceTrackingStrategy { } @NotNull - final List getNewInstances() { + public final List getNewInstances() { return Collections.unmodifiableList(myNewInstances); } - final void update(@NotNull List references) { + public final void update(@NotNull List references) { myNewInstances = updateImpl(references); } diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 820405f3449f..3be9ce3ba2b2 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -18,13 +18,16 @@ import com.intellij.util.SmartList; import com.intellij.util.ui.components.BorderLayoutPanel; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; +import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import com.sun.jdi.VirtualMachine; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.component.MemoryViewManager; import org.jetbrains.debugger.memory.component.MemoryViewManagerState; import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; +import org.jetbrains.debugger.memory.tracking.InstanceTrackingStrategy; import org.jetbrains.debugger.memory.utils.AndroidUtil; import org.jetbrains.debugger.memory.utils.KeyboardUtils; import org.jetbrains.debugger.memory.utils.SingleAlarmWithMutableDelay; @@ -37,6 +40,8 @@ import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; @@ -52,10 +57,14 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private final SearchTextField myFilterTextField = new SearchTextField(false); private final ClassesTable myTable; + private final InstancesTracker myInstancesTracker; + private final Map myTrackedClasses = new ConcurrentHashMap<>(); + private volatile SuspendContextImpl myLastSuspendContext; private volatile boolean myNeedReloadClasses = false; + public ClassesFilteredView(@NotNull XDebugSession debugSession) { super(); @@ -63,11 +72,12 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myDebugSession = debugSession; myDebugProcess = DebuggerManager.getInstance(myProject) .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); + myInstancesTracker = InstancesTracker.getInstance(myProject); MemoryViewManagerState memoryViewManagerState = MemoryViewManager.getInstance().getState(); myTable = new ClassesTable(myDebugSession, memoryViewManagerState.isShowWithDiffOnly, - memoryViewManagerState.isShowWithInstancesOnly); + memoryViewManagerState.isShowWithInstancesOnly, this); Disposer.register(this, myTable); myTable.addKeyListener(new KeyAdapter() { @@ -152,6 +162,11 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable addToCenter(scroll); } + @Nullable + InstanceTrackingStrategy getStrategy(@NotNull ReferenceType ref) { + return myTrackedClasses.getOrDefault(ref, null); + } + public void setNeedReloadClasses(boolean value) { if (myNeedReloadClasses != value) { myNeedReloadClasses = value; @@ -203,6 +218,17 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { final List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); + for(ReferenceType ref : classes) { + InstancesTracker.TrackingType type = myInstancesTracker.getTrackingType(ref.name()); + if(type != null && !myTrackedClasses.containsKey(ref)) { + List instances = ref.instances(0); + myTrackedClasses.put(ref, InstanceTrackingStrategy.create(type, instances)); + } + } + + for(Map.Entry entry : myTrackedClasses.entrySet()) { + entry.getValue().update(entry.getKey().instances(0)); + } if (classes.isEmpty()) { return; diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 249d4c64106c..d5ee323f2ffc 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -16,14 +16,17 @@ import com.intellij.util.ui.JBDimension; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import com.intellij.xdebugger.XDebugSession; +import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.event.InstancesTrackerListener; +import org.jetbrains.debugger.memory.tracking.InstanceTrackingStrategy; import org.jetbrains.debugger.memory.utils.AbstractTableColumnDescriptor; import org.jetbrains.debugger.memory.utils.AbstractTableModelWithColumns; +import org.jetbrains.debugger.memory.utils.InstancesProvider; import javax.swing.*; import javax.swing.table.TableColumn; @@ -34,6 +37,8 @@ import java.util.concurrent.ConcurrentHashMap; public class ClassesTable extends JBTable implements DataProvider, Disposable { public static final DataKey SELECTED_CLASS_KEY = DataKey.create("ClassesTable.SelectedClass"); public static final DataKey DEBUG_SESSION_KEY = DataKey.create("ClassesTable.DebugSession"); + public static final DataKey NEW_INSTANCES_PROVIDER_KEY = + DataKey.create("ClassesTable.NewInstances"); private static final int CLASSES_COLUMN_PREFERRED_WIDTH = 250; private static final int COUNT_COLUMN_MIN_WIDTH = 80; private static final int COUNT_COLUMN_MAX_WIDTH = 100; @@ -47,6 +52,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private final XDebugSession myDebugSession; private final Map myCounts = new ConcurrentHashMap<>(); private final InstancesTracker myInstancesTracker; + private final ClassesFilteredView myParent; private boolean myOnlyWithDiff; @@ -56,13 +62,16 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private volatile List myElems = Collections.unmodifiableList(new ArrayList<>()); - ClassesTable(@NotNull XDebugSession session, boolean onlyWithDiff, boolean onlyWithInstances) { + // TODO: parent must be unknown in this context + ClassesTable(@NotNull XDebugSession session, boolean onlyWithDiff, boolean onlyWithInstances, + @NotNull ClassesFilteredView parent) { setModel(myModel); myDebugSession = session; myOnlyWithDiff = onlyWithDiff; myOnlyWithInstances = onlyWithInstances; myInstancesTracker = InstancesTracker.getInstance(myDebugSession.getProject()); + myParent = parent; getEmptyText().setText(EMPTY_TABLE_CONTENT); @@ -198,6 +207,17 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { if (DEBUG_SESSION_KEY.is(dataId)) { return myDebugSession; } + if(NEW_INSTANCES_PROVIDER_KEY.is(dataId)) { + ReferenceType selectedClass = getSelectedClass(); + if(selectedClass != null) { + InstanceTrackingStrategy strategy = myParent.getStrategy(selectedClass); + if(strategy != null) { + List newInstances = strategy.getNewInstances(); + return (InstancesProvider) limit -> newInstances; + } + + } + } return null; } From b380268d909819fc9554a272f4248e1edb585c8d Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 26 Aug 2016 13:03:08 +0300 Subject: [PATCH 063/558] Add ability to navigate in classes table when writing filter expression --- view/ClassesFilteredView.java | 49 +++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index e6736331928c..d8d70341c13c 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -48,7 +48,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { private final DebugProcess myDebugProcess; private final SingleAlarmWithMutableDelay mySingleAlarm; - private final SearchTextField myFilterTextField = new SearchTextField(false); + private final SearchTextField myFilterTextField = new FilterTextField(); private final ClassesTable myTable; private volatile SuspendContextImpl myLastSuspendContext; @@ -81,6 +81,31 @@ public class ClassesFilteredView extends BorderLayoutPanel { } }); + myFilterTextField.addKeyboardListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + dispatch(e); + } + + @Override + public void keyReleased(KeyEvent e) { + dispatch(e); + } + + private void dispatch(KeyEvent e) { + if (KeyboardUtils.isArrowKey(e.getKeyCode())) { + myTable.dispatchEvent(e); + } + } + }); + + myFilterTextField.addDocumentListener(new DocumentAdapter() { + @Override + protected void textChanged(DocumentEvent e) { + myTable.setFilterPattern(myFilterTextField.getText()); + } + }); + new DoubleClickListener() { @Override protected boolean onDoubleClick(MouseEvent event) { @@ -117,13 +142,6 @@ public class ClassesFilteredView extends BorderLayoutPanel { } }); - myFilterTextField.addDocumentListener(new DocumentAdapter() { - @Override - protected void textChanged(DocumentEvent e) { - myTable.setFilterPattern(myFilterTextField.getText()); - } - }); - mySingleAlarm = new SingleAlarmWithMutableDelay(() -> { myLastSuspendContext = getSuspendContext(); if (myLastSuspendContext != null) { @@ -242,4 +260,19 @@ public class ClassesFilteredView extends BorderLayoutPanel { return ClassesFilteredView.this.getSuspendContext() == getSuspendContext(); } } + + private class FilterTextField extends SearchTextField { + FilterTextField() { + super(false); + } + + @Override + protected void showPopup() { + } + + @Override + protected boolean hasIconsOutsideOfTextField() { + return false; + } + } } From 0fb33f883cddd1c770fa97368e3844edbc4bdb51 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 26 Aug 2016 13:31:27 +0300 Subject: [PATCH 064/558] Support backspace and uppercase characters in table with classes --- utils/KeyboardUtils.java | 4 ++++ view/ClassesFilteredView.java | 9 ++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/utils/KeyboardUtils.java b/utils/KeyboardUtils.java index d7818f4bccf5..367c8f567261 100644 --- a/utils/KeyboardUtils.java +++ b/utils/KeyboardUtils.java @@ -12,6 +12,10 @@ public class KeyboardUtils { keyCode == KeyEvent.VK_DOWN || keyCode == KeyEvent.VK_RIGHT; } + public static boolean isBackSpace(int keyCode) { + return keyCode == KeyEvent.VK_BACK_SPACE; + } + public static boolean isCharacter(int keyCode) { return KeyEvent.getKeyText(keyCode).length() == 1; } diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index d8d70341c13c..f39b26261bbd 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -73,10 +73,13 @@ public class ClassesFilteredView extends BorderLayoutPanel { int keyCode = e.getKeyCode(); if (KeyboardUtils.isEnterKey(keyCode)) { handleClassSelection(myTable.getSelectedClass()); - } else if (!KeyboardUtils.isArrowKey(keyCode) && KeyboardUtils.isCharacter(keyCode)) { - SwingUtilities.invokeLater(myFilterTextField::requestFocusInWindow); + } else if (KeyboardUtils.isCharacter(keyCode) || KeyboardUtils.isBackSpace(keyCode)) { String text = myFilterTextField.getText(); - myFilterTextField.setText(text + KeyEvent.getKeyText(keyCode).toLowerCase()); + String newText = KeyboardUtils.isBackSpace(keyCode) + ? text.substring(0, text.length() - 1) + : text + e.getKeyChar(); + myFilterTextField.setText(newText); + FocusManager.getCurrentManager().focusNextComponent(myFilterTextField); } } }); From 7e706e667341546454daf7165d73dbe955f5938d Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 26 Aug 2016 13:36:29 +0300 Subject: [PATCH 065/558] Dispatch to classes table only up/down/enter keys for keyboard events --- utils/KeyboardUtils.java | 5 ++--- view/ClassesFilteredView.java | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/utils/KeyboardUtils.java b/utils/KeyboardUtils.java index 367c8f567261..57899d96122e 100644 --- a/utils/KeyboardUtils.java +++ b/utils/KeyboardUtils.java @@ -7,9 +7,8 @@ public class KeyboardUtils { return keyCode == KeyEvent.VK_ENTER; } - public static boolean isArrowKey(int keyCode) { - return keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_LEFT || - keyCode == KeyEvent.VK_DOWN || keyCode == KeyEvent.VK_RIGHT; + public static boolean isUpDownKey(int keyCode) { + return keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN; } public static boolean isBackSpace(int keyCode) { diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index f39b26261bbd..193bd4160f77 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -96,7 +96,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { } private void dispatch(KeyEvent e) { - if (KeyboardUtils.isArrowKey(e.getKeyCode())) { + if (KeyboardUtils.isUpDownKey(e.getKeyCode()) || KeyboardUtils.isEnterKey(e.getKeyCode())) { myTable.dispatchEvent(e); } } From 27cba431be75e4f14ec0abf358c172430e87ecf1 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 26 Aug 2016 15:56:14 +0300 Subject: [PATCH 066/558] Implement tracking for new instances trough hash codes on target vm --- tracking/HashBasedTracking.java | 78 +++++++++++++++++++++++--- tracking/IdentityBasedTracking.java | 4 +- tracking/InstanceTrackingStrategy.java | 16 ++++-- tracking/RetainReferencesTracking.java | 6 +- view/ClassesFilteredView.java | 4 +- 5 files changed, 91 insertions(+), 17 deletions(-) diff --git a/tracking/HashBasedTracking.java b/tracking/HashBasedTracking.java index b3c02ce934d1..4fa1c6863cbf 100644 --- a/tracking/HashBasedTracking.java +++ b/tracking/HashBasedTracking.java @@ -1,20 +1,84 @@ package org.jetbrains.debugger.memory.tracking; -import com.sun.jdi.ObjectReference; -import org.apache.commons.lang.NotImplementedException; +import com.intellij.debugger.engine.SuspendContextImpl; +import com.intellij.debugger.engine.jdi.ThreadReferenceProxy; +import com.sun.jdi.*; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import java.util.List; +import java.util.*; +import java.util.stream.Collectors; class HashBasedTracking extends InstanceTrackingStrategy { + private final ReferenceType myClassType; + private Set myHashesSet; - HashBasedTracking(@NotNull List initialInstances) { - + HashBasedTracking(@NotNull SuspendContextImpl suspendContext, + @NotNull ReferenceType classType, + @NotNull List initialInstances) { + myClassType = classType; + myHashesSet = toSetOfHashes(evalHashCodes(suspendContext, initialInstances)); } @NotNull @Override - protected List updateImpl(@NotNull List references) { - throw new NotImplementedException(); + protected List updateImpl(@NotNull SuspendContextImpl suspendContext, + @NotNull List references) { + Map> ref2hash = evalHashCodes(suspendContext, references); + List newInstances = new ArrayList<>(); + + for(ObjectReference ref : references) { + Optional hash = ref2hash.get(ref); + if(hash.isPresent() && myHashesSet.contains(hash.get())) { + newInstances.add(ref); + } + } + + return newInstances; + } + + @NotNull + private Map> evalHashCodes(@NotNull SuspendContextImpl suspendContext, + @NotNull List references) { + Method hashCodeMethod = getHashCodeMethod(myClassType); + Map> result = new HashMap<>(); + ThreadReferenceProxy threadProxy = suspendContext.getThread(); + ThreadReference thread = threadProxy != null ? threadProxy.getThreadReference() : null; + if(thread == null) { + return result; + } + + for (ObjectReference ref : references) { + Optional hash = Optional.empty(); + try { + Value value = ref.invokeMethod(thread, hashCodeMethod, Collections.emptyList(), 0); + hash = Optional.of(((IntegerValue)value).value()); + } catch (InvalidTypeException | ClassNotLoadedException | + IncompatibleThreadStateException | InvocationException ignored) { + } + + result.put(ref, hash); + } + + return result; + } + + private Set toSetOfHashes(@NotNull Map> ref2hashCode) { + return ref2hashCode.values().stream() + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toCollection(TreeSet::new)); + } + + @Nullable + private Method getHashCodeMethod(@Nullable ReferenceType referenceType) { + if (referenceType == null) { + return null; + } + + return referenceType.methodsByName("hashCode").stream() + .filter(method -> method.argumentTypeNames().isEmpty()) + .findFirst() + .orElseGet(null); } } diff --git a/tracking/IdentityBasedTracking.java b/tracking/IdentityBasedTracking.java index 78ef6670576a..d3d84615a505 100644 --- a/tracking/IdentityBasedTracking.java +++ b/tracking/IdentityBasedTracking.java @@ -1,5 +1,6 @@ package org.jetbrains.debugger.memory.tracking; +import com.intellij.debugger.engine.SuspendContextImpl; import com.sun.jdi.ObjectReference; import org.jetbrains.annotations.NotNull; @@ -14,7 +15,8 @@ class IdentityBasedTracking extends InstanceTrackingStrategy { @NotNull @Override - protected List updateImpl(@NotNull List references) { + protected List updateImpl(@NotNull SuspendContextImpl suspendContext, + @NotNull List references) { List result = references.stream() .filter(reference -> reference.uniqueID() > myLastId) .collect(Collectors.toList()); diff --git a/tracking/InstanceTrackingStrategy.java b/tracking/InstanceTrackingStrategy.java index 3a37afc3cee6..86a1f4a4f2e2 100644 --- a/tracking/InstanceTrackingStrategy.java +++ b/tracking/InstanceTrackingStrategy.java @@ -1,6 +1,8 @@ package org.jetbrains.debugger.memory.tracking; +import com.intellij.debugger.engine.SuspendContextImpl; import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -13,12 +15,15 @@ public abstract class InstanceTrackingStrategy { @NotNull private List myNewInstances = new ArrayList<>(); - public static InstanceTrackingStrategy create(@NotNull TrackingType type, @NotNull List init) { + public static InstanceTrackingStrategy create(@NotNull ReferenceType referenceType, + @NotNull SuspendContextImpl suspendContext, + @NotNull TrackingType type, + @NotNull List init) { switch (type) { case IDENTITY: return new IdentityBasedTracking(init); case HASH: - return new HashBasedTracking(init); + return new HashBasedTracking(suspendContext, referenceType, init); case RETAIN: return new RetainReferencesTracking(init); } @@ -31,10 +36,11 @@ public abstract class InstanceTrackingStrategy { return Collections.unmodifiableList(myNewInstances); } - public final void update(@NotNull List references) { - myNewInstances = updateImpl(references); + public final void update(@NotNull SuspendContextImpl suspendContext, @NotNull List references) { + myNewInstances = updateImpl(suspendContext, references); } @NotNull - protected abstract List updateImpl(@NotNull List references); + protected abstract List updateImpl(@NotNull SuspendContextImpl suspendContext, + @NotNull List references); } diff --git a/tracking/RetainReferencesTracking.java b/tracking/RetainReferencesTracking.java index 1830dd4d58de..f5a20d72ce63 100644 --- a/tracking/RetainReferencesTracking.java +++ b/tracking/RetainReferencesTracking.java @@ -1,5 +1,6 @@ package org.jetbrains.debugger.memory.tracking; +import com.intellij.debugger.engine.SuspendContextImpl; import com.sun.jdi.ObjectReference; import org.jetbrains.annotations.NotNull; @@ -16,8 +17,9 @@ class RetainReferencesTracking extends InstanceTrackingStrategy { @NotNull @Override - protected List updateImpl(@NotNull List references) { - myIdTracker.update(references); + protected List updateImpl(@NotNull SuspendContextImpl suspendContext, + @NotNull List references) { + myIdTracker.update(suspendContext, references); myHardRefs = references; return myIdTracker.getNewInstances(); } diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 3be9ce3ba2b2..5bbdcc659169 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -222,12 +222,12 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable InstancesTracker.TrackingType type = myInstancesTracker.getTrackingType(ref.name()); if(type != null && !myTrackedClasses.containsKey(ref)) { List instances = ref.instances(0); - myTrackedClasses.put(ref, InstanceTrackingStrategy.create(type, instances)); + myTrackedClasses.put(ref, InstanceTrackingStrategy.create(ref, suspendContext, type, instances)); } } for(Map.Entry entry : myTrackedClasses.entrySet()) { - entry.getValue().update(entry.getKey().instances(0)); + entry.getValue().update(suspendContext, entry.getKey().instances(0)); } if (classes.isEmpty()) { From 51a0ddf223dfe783e6b81198d3dfb523fda266cd Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 26 Aug 2016 16:26:51 +0300 Subject: [PATCH 067/558] Fix bugs in hashCode-based instances tracker --- tracking/HashBasedTracking.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tracking/HashBasedTracking.java b/tracking/HashBasedTracking.java index 4fa1c6863cbf..f21014e818fb 100644 --- a/tracking/HashBasedTracking.java +++ b/tracking/HashBasedTracking.java @@ -29,11 +29,12 @@ class HashBasedTracking extends InstanceTrackingStrategy { for(ObjectReference ref : references) { Optional hash = ref2hash.get(ref); - if(hash.isPresent() && myHashesSet.contains(hash.get())) { + if(hash.isPresent() && !myHashesSet.contains(hash.get())) { newInstances.add(ref); } } + myHashesSet = toSetOfHashes(ref2hash); return newInstances; } @@ -67,7 +68,7 @@ class HashBasedTracking extends InstanceTrackingStrategy { return ref2hashCode.values().stream() .filter(Optional::isPresent) .map(Optional::get) - .collect(Collectors.toCollection(TreeSet::new)); + .collect(Collectors.toCollection(HashSet::new)); } @Nullable From f932c38e1d2fc7d50945ff6ff29b56465fc76bc8 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 26 Aug 2016 17:16:39 +0300 Subject: [PATCH 068/558] Add UI support for tracking of constructors calls --- action/tracking/HashBasedTrackingAction.java | 2 +- .../tracking/IdentityBasedTrackingAction.java | 6 +-- .../tracking/InstancesTrackingActionBase.java | 2 +- .../RetainReferenceBasedTrackingAction.java | 6 +-- .../tracking/TrackInstanceCreationAction.java | 12 ++++++ component/InstancesTracker.java | 13 +----- event/InstancesTrackerListener.java | 4 +- tracking/InstanceTrackingStrategy.java | 2 - tracking/TrackingType.java | 29 +++++++++++++ view/ClassesFilteredView.java | 17 +++++--- view/ClassesTable.java | 41 +++++++++++-------- 11 files changed, 87 insertions(+), 47 deletions(-) create mode 100644 action/tracking/TrackInstanceCreationAction.java create mode 100644 tracking/TrackingType.java diff --git a/action/tracking/HashBasedTrackingAction.java b/action/tracking/HashBasedTrackingAction.java index afac0f214808..8a8c29c2444d 100644 --- a/action/tracking/HashBasedTrackingAction.java +++ b/action/tracking/HashBasedTrackingAction.java @@ -1,7 +1,7 @@ package org.jetbrains.debugger.memory.action.tracking; import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.component.InstancesTracker.TrackingType; +import org.jetbrains.debugger.memory.tracking.TrackingType; public class HashBasedTrackingAction extends InstancesTrackingActionBase { @NotNull diff --git a/action/tracking/IdentityBasedTrackingAction.java b/action/tracking/IdentityBasedTrackingAction.java index 6b04df12181d..f43f65952cf1 100644 --- a/action/tracking/IdentityBasedTrackingAction.java +++ b/action/tracking/IdentityBasedTrackingAction.java @@ -1,12 +1,12 @@ package org.jetbrains.debugger.memory.action.tracking; import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.component.InstancesTracker; +import org.jetbrains.debugger.memory.tracking.TrackingType; public class IdentityBasedTrackingAction extends InstancesTrackingActionBase { @NotNull @Override - protected InstancesTracker.TrackingType getType() { - return InstancesTracker.TrackingType.IDENTITY; + protected TrackingType getType() { + return TrackingType.IDENTITY; } } diff --git a/action/tracking/InstancesTrackingActionBase.java b/action/tracking/InstancesTrackingActionBase.java index e7c904db694d..aacd946e7beb 100644 --- a/action/tracking/InstancesTrackingActionBase.java +++ b/action/tracking/InstancesTrackingActionBase.java @@ -9,7 +9,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.view.ClassesTable; -import static org.jetbrains.debugger.memory.component.InstancesTracker.TrackingType; +import org.jetbrains.debugger.memory.tracking.TrackingType; abstract class InstancesTrackingActionBase extends ToggleAction { @Override diff --git a/action/tracking/RetainReferenceBasedTrackingAction.java b/action/tracking/RetainReferenceBasedTrackingAction.java index b0d118470227..bf86f08209f2 100644 --- a/action/tracking/RetainReferenceBasedTrackingAction.java +++ b/action/tracking/RetainReferenceBasedTrackingAction.java @@ -1,12 +1,12 @@ package org.jetbrains.debugger.memory.action.tracking; import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.component.InstancesTracker; +import org.jetbrains.debugger.memory.tracking.TrackingType; public class RetainReferenceBasedTrackingAction extends InstancesTrackingActionBase { @NotNull @Override - protected InstancesTracker.TrackingType getType() { - return InstancesTracker.TrackingType.RETAIN; + protected TrackingType getType() { + return TrackingType.RETAIN; } } diff --git a/action/tracking/TrackInstanceCreationAction.java b/action/tracking/TrackInstanceCreationAction.java new file mode 100644 index 000000000000..e8cfc3f9c9d8 --- /dev/null +++ b/action/tracking/TrackInstanceCreationAction.java @@ -0,0 +1,12 @@ +package org.jetbrains.debugger.memory.action.tracking; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.debugger.memory.tracking.TrackingType; + +public class TrackInstanceCreationAction extends InstancesTrackingActionBase{ + @NotNull + @Override + protected TrackingType getType() { + return TrackingType.CREATION; + } +} diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java index 29594c125a78..77145325508f 100644 --- a/component/InstancesTracker.java +++ b/component/InstancesTracker.java @@ -4,14 +4,13 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.components.*; import com.intellij.openapi.project.Project; import com.intellij.util.EventDispatcher; -import com.intellij.util.containers.HashMap; import com.intellij.util.xmlb.annotations.AbstractCollection; import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.event.InstancesTrackerListener; +import org.jetbrains.debugger.memory.tracking.TrackingType; -import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -30,10 +29,6 @@ public class InstancesTracker extends AbstractProjectComponent return project.getComponent(InstancesTracker.class); } - public enum TrackingType { - IDENTITY, HASH, RETAIN - } - public boolean isTracked(@NotNull String className) { return myState.classes.containsKey(className); } @@ -43,12 +38,6 @@ public class InstancesTracker extends AbstractProjectComponent return myState.classes.getOrDefault(className, null); } - - public Map getTrackingClasses() { - Map copy = new HashMap<>(myState.classes); - return Collections.unmodifiableMap(copy); - } - public void add(@NotNull ReferenceType ref, @NotNull TrackingType type) { String name = ref.name(); if(type.equals(myState.classes.getOrDefault(name, null))) { diff --git a/event/InstancesTrackerListener.java b/event/InstancesTrackerListener.java index ed03afbc4173..0e8924e17657 100644 --- a/event/InstancesTrackerListener.java +++ b/event/InstancesTrackerListener.java @@ -1,12 +1,12 @@ package org.jetbrains.debugger.memory.event; import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.component.InstancesTracker; +import org.jetbrains.debugger.memory.tracking.TrackingType; import java.util.EventListener; public interface InstancesTrackerListener extends EventListener{ - default void classChanged(@NotNull String name, @NotNull InstancesTracker.TrackingType type) { + default void classChanged(@NotNull String name, @NotNull TrackingType type) { } default void classRemoved(@NotNull String name) { diff --git a/tracking/InstanceTrackingStrategy.java b/tracking/InstanceTrackingStrategy.java index 86a1f4a4f2e2..1fc0543fad2e 100644 --- a/tracking/InstanceTrackingStrategy.java +++ b/tracking/InstanceTrackingStrategy.java @@ -9,8 +9,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import static org.jetbrains.debugger.memory.component.InstancesTracker.*; - public abstract class InstanceTrackingStrategy { @NotNull private List myNewInstances = new ArrayList<>(); diff --git a/tracking/TrackingType.java b/tracking/TrackingType.java new file mode 100644 index 000000000000..bbdb16794b3e --- /dev/null +++ b/tracking/TrackingType.java @@ -0,0 +1,29 @@ +package org.jetbrains.debugger.memory.tracking; + +import com.intellij.ui.JBColor; +import org.jetbrains.annotations.NotNull; + +public enum TrackingType { + IDENTITY("Id Based Tracking", JBColor.yellow), + HASH("Hash Based Tracking", JBColor.yellow), + RETAIN("Retaining Reference Object Tracking", JBColor.yellow), + CREATION("Track Constructors", JBColor.orange); + + private final String myDescription; + private final JBColor myHighlighting; + + TrackingType(@NotNull String description, @NotNull JBColor highlighting) { + myDescription = description; + myHighlighting = highlighting; + } + + @NotNull + public JBColor color() { + return myHighlighting; + } + + @NotNull + public String description() { + return myDescription; + } +} diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 5bbdcc659169..241d134c9bd3 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -28,6 +28,7 @@ import org.jetbrains.debugger.memory.component.MemoryViewManager; import org.jetbrains.debugger.memory.component.MemoryViewManagerState; import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; import org.jetbrains.debugger.memory.tracking.InstanceTrackingStrategy; +import org.jetbrains.debugger.memory.tracking.TrackingType; import org.jetbrains.debugger.memory.utils.AndroidUtil; import org.jetbrains.debugger.memory.utils.KeyboardUtils; import org.jetbrains.debugger.memory.utils.SingleAlarmWithMutableDelay; @@ -218,15 +219,19 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { final List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); - for(ReferenceType ref : classes) { - InstancesTracker.TrackingType type = myInstancesTracker.getTrackingType(ref.name()); - if(type != null && !myTrackedClasses.containsKey(ref)) { - List instances = ref.instances(0); - myTrackedClasses.put(ref, InstanceTrackingStrategy.create(ref, suspendContext, type, instances)); + for (ReferenceType ref : classes) { + TrackingType type = myInstancesTracker.getTrackingType(ref.name()); + if (type != null && !myTrackedClasses.containsKey(ref)) { + if (type == TrackingType.CREATION) { + + } else { + List instances = ref.instances(0); + myTrackedClasses.put(ref, InstanceTrackingStrategy.create(ref, suspendContext, type, instances)); + } } } - for(Map.Entry entry : myTrackedClasses.entrySet()) { + for (Map.Entry entry : myTrackedClasses.entrySet()) { entry.getValue().update(suspendContext, entry.getKey().instances(0)); } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index d5ee323f2ffc..baccc02d1d05 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -7,7 +7,6 @@ import com.intellij.openapi.util.TextRange; import com.intellij.psi.codeStyle.MinusculeMatcher; import com.intellij.psi.codeStyle.NameUtil; import com.intellij.ui.ColoredTableCellRenderer; -import com.intellij.ui.JBColor; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.speedSearch.SpeedSearchUtil; import com.intellij.ui.table.JBTable; @@ -24,6 +23,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.event.InstancesTrackerListener; import org.jetbrains.debugger.memory.tracking.InstanceTrackingStrategy; +import org.jetbrains.debugger.memory.tracking.TrackingType; import org.jetbrains.debugger.memory.utils.AbstractTableColumnDescriptor; import org.jetbrains.debugger.memory.utils.AbstractTableModelWithColumns; import org.jetbrains.debugger.memory.utils.InstancesProvider; @@ -78,7 +78,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { InstancesTracker tracker = InstancesTracker.getInstance(myDebugSession.getProject()); tracker.addTrackerListener(new InstancesTrackerListener() { @Override - public void classChanged(@NotNull String name, @NotNull InstancesTracker.TrackingType type) { + public void classChanged(@NotNull String name, @NotNull TrackingType type) { getRowSorter().allRowsChanged(); } @@ -207,11 +207,11 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { if (DEBUG_SESSION_KEY.is(dataId)) { return myDebugSession; } - if(NEW_INSTANCES_PROVIDER_KEY.is(dataId)) { + if (NEW_INSTANCES_PROVIDER_KEY.is(dataId)) { ReferenceType selectedClass = getSelectedClass(); - if(selectedClass != null) { + if (selectedClass != null) { InstanceTrackingStrategy strategy = myParent.getStrategy(selectedClass); - if(strategy != null) { + if (strategy != null) { List newInstances = strategy.getNewInstances(); return (InstancesProvider) limit -> newInstances; } @@ -226,13 +226,10 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { public void dispose() { } - private boolean needHighlightAsTracked(boolean isSelected, int row) { - if(isSelected){ - return false; - } - + @Nullable + private TrackingType getTrackingType(int row) { ReferenceType ref = (ReferenceType) getValueAt(row, DiffViewTableModel.CLASSNAME_COLUMN_INDEX); - return myInstancesTracker.isTracked(ref.name()); + return myInstancesTracker.getTrackingType(ref.name()); } private class DiffViewTableModel extends AbstractTableModelWithColumns { @@ -324,6 +321,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private static class DiffValue { private long myOldCount; + private long myCurrentCount; DiffValue(long count) { @@ -348,9 +346,11 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { long diff() { return myCurrentCount - myOldCount; } + } private class MyNumberTableCellRenderer extends ColoredTableCellRenderer { + @Override protected void customizeCellRenderer(JTable table, @Nullable Object value, boolean isSelected, boolean hasFocus, int row, int column) { @@ -358,8 +358,9 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { return; } - if (needHighlightAsTracked(isSelected, row)) { - setBackground(UIUtil.toAlpha(JBColor.yellow, 20)); + TrackingType trackingType = getTrackingType(row); + if (!isSelected && trackingType != null) { + setBackground(UIUtil.toAlpha(trackingType.color(), 20)); } long val = (long) value; @@ -379,15 +380,18 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { append(String.valueOf(count)); } + } private class MyClassTableCellRenderer extends ColoredTableCellRenderer { + @Override protected void customizeCellRenderer(JTable table, @Nullable Object value, boolean isSelected, boolean hasFocus, int row, int column) { String presentation = value == null ? "null" : ((ReferenceType) value).name(); - if (needHighlightAsTracked(isSelected, row)) { - setBackground(UIUtil.toAlpha(JBColor.yellow, 20)); + TrackingType trackingType = getTrackingType(row); + if (trackingType != null && !isSelected) { + setBackground(UIUtil.toAlpha(trackingType.color(), 20)); } append(" "); @@ -398,11 +402,14 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { SimpleTextAttributes.STYLE_SEARCH_MATCH); SpeedSearchUtil.appendColoredFragments(this, presentation, textRanges, SimpleTextAttributes.REGULAR_ATTRIBUTES, attributes); - return; } + } else { + append(String.format("%s", presentation), SimpleTextAttributes.REGULAR_ATTRIBUTES); } - append(String.format("%s", presentation), SimpleTextAttributes.REGULAR_ATTRIBUTES); + if (trackingType != null) { + append(String.format(" (%s)", trackingType.description())); + } } } } From db2a1a6e8baf4600dc25a08fa357e0fb00ae2040 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Mon, 29 Aug 2016 17:23:00 +0300 Subject: [PATCH 069/558] Add basic implementation for tracking instances through constructors calls --- tracking/ConstructorInstancesTracker.java | 96 +++++++++++++++++++++++ tracking/InstanceTrackingStrategy.java | 2 +- tracking/TrackerForNewInstances.java | 9 +++ view/ClassesFilteredView.java | 20 +++-- view/ClassesTable.java | 5 +- 5 files changed, 122 insertions(+), 10 deletions(-) create mode 100644 tracking/ConstructorInstancesTracker.java create mode 100644 tracking/TrackerForNewInstances.java diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java new file mode 100644 index 000000000000..801df0caaf29 --- /dev/null +++ b/tracking/ConstructorInstancesTracker.java @@ -0,0 +1,96 @@ +package org.jetbrains.debugger.memory.tracking; + +import com.intellij.debugger.engine.DebugProcessImpl; +import com.intellij.debugger.engine.SuspendContextImpl; +import com.intellij.debugger.engine.evaluation.EvaluateException; +import com.intellij.debugger.engine.events.SuspendContextCommandImpl; +import com.intellij.debugger.ui.breakpoints.JavaLineBreakpointType; +import com.intellij.debugger.ui.breakpoints.LineBreakpoint; +import com.intellij.openapi.project.Project; +import com.intellij.xdebugger.breakpoints.XBreakpoint; +import com.intellij.xdebugger.impl.XDebuggerManagerImpl; +import com.intellij.xdebugger.impl.breakpoints.LineBreakpointState; +import com.intellij.xdebugger.impl.breakpoints.XLineBreakpointImpl; +import com.sun.jdi.Location; +import com.sun.jdi.Method; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; +import com.sun.jdi.event.LocatableEvent; +import com.sun.jdi.request.BreakpointRequest; +import com.sun.jdi.request.EventRequest; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.java.debugger.breakpoints.properties.JavaLineBreakpointProperties; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +public class ConstructorInstancesTracker implements TrackerForNewInstances { + private final HashSet myNewObjects = new HashSet<>(); + + public ConstructorInstancesTracker(@NotNull ReferenceType ref, + @NotNull DebugProcessImpl debugProcess) { + Project project = debugProcess.getProject(); + JavaLineBreakpointType breakPointType = new JavaLineBreakpointType(); + + XBreakpoint bpn = new XLineBreakpointImpl<>(breakPointType, + ((XDebuggerManagerImpl) XDebuggerManagerImpl.getInstance(project)).getBreakpointManager(), + new JavaLineBreakpointProperties(), + new LineBreakpointState<>()); + MyConstructorBreakpoints myLineBreakpoint = new MyConstructorBreakpoints(project, bpn); + myLineBreakpoint.createRequestForPreparedClass(debugProcess, ref); + } + + public void obsolete() { + myNewObjects.clear(); + } + + @Override + public List getNewInstances() { + return new ArrayList<>(myNewObjects); + } + + private final class MyConstructorBreakpoints extends LineBreakpoint { + private final List myRequests = new ArrayList<>(); + + MyConstructorBreakpoints(Project project, XBreakpoint xBreakpoint) { + super(project, xBreakpoint); + } + + @Override + protected void createRequestForPreparedClass(DebugProcessImpl debugProcess, ReferenceType classType) { + for (Method cons : classType.methodsByName("")) { + Location loc = cons.location(); + BreakpointRequest breakpointRequest = debugProcess.getRequestsManager().createBreakpointRequest(this, loc); + breakpointRequest.enable(); + myRequests.add(breakpointRequest); + } + } + + void disable(@NotNull DebugProcessImpl debugProcess) { + myRequests.forEach(EventRequest::disable); + myRequests.clear(); + debugProcess.getRequestsManager().deleteRequest(this); + } + + @Override + public void reload() { + } + + @Override + public boolean processLocatableEvent(SuspendContextCommandImpl action, LocatableEvent event) + throws EventProcessingException { + try { + SuspendContextImpl suspendContext = action.getSuspendContext(); + if (suspendContext != null) { + ObjectReference thisRef = getThisObject(suspendContext, event); + System.out.println("new object " + thisRef.uniqueID() + " added."); + myNewObjects.add(thisRef); + } + } catch (EvaluateException e) { + e.printStackTrace(); + } + return false; + } + } +} diff --git a/tracking/InstanceTrackingStrategy.java b/tracking/InstanceTrackingStrategy.java index 1fc0543fad2e..48497b16dc43 100644 --- a/tracking/InstanceTrackingStrategy.java +++ b/tracking/InstanceTrackingStrategy.java @@ -9,7 +9,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -public abstract class InstanceTrackingStrategy { +public abstract class InstanceTrackingStrategy implements TrackerForNewInstances { @NotNull private List myNewInstances = new ArrayList<>(); diff --git a/tracking/TrackerForNewInstances.java b/tracking/TrackerForNewInstances.java new file mode 100644 index 000000000000..4ea69620c6a8 --- /dev/null +++ b/tracking/TrackerForNewInstances.java @@ -0,0 +1,9 @@ +package org.jetbrains.debugger.memory.tracking; + +import com.sun.jdi.ObjectReference; + +import java.util.List; + +public interface TrackerForNewInstances { + List getNewInstances(); +} diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 241d134c9bd3..9b65325c8d69 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -2,6 +2,7 @@ package org.jetbrains.debugger.memory.view; import com.intellij.debugger.DebuggerManager; import com.intellij.debugger.engine.DebugProcess; +import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.DebuggerManagerThreadImpl; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; @@ -27,7 +28,9 @@ import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.component.MemoryViewManager; import org.jetbrains.debugger.memory.component.MemoryViewManagerState; import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; +import org.jetbrains.debugger.memory.tracking.ConstructorInstancesTracker; import org.jetbrains.debugger.memory.tracking.InstanceTrackingStrategy; +import org.jetbrains.debugger.memory.tracking.TrackerForNewInstances; import org.jetbrains.debugger.memory.tracking.TrackingType; import org.jetbrains.debugger.memory.utils.AndroidUtil; import org.jetbrains.debugger.memory.utils.KeyboardUtils; @@ -60,12 +63,11 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private final ClassesTable myTable; private final InstancesTracker myInstancesTracker; private final Map myTrackedClasses = new ConcurrentHashMap<>(); - + private final Map myConstructorTrackedClasses = new ConcurrentHashMap<>(); private volatile SuspendContextImpl myLastSuspendContext; private volatile boolean myNeedReloadClasses = false; - public ClassesFilteredView(@NotNull XDebugSession debugSession) { super(); @@ -113,6 +115,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myDebugSession.addSessionListener(new XDebugSessionListener() { @Override public void sessionResumed() { + myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::obsolete); SwingUtilities.invokeLater(myTable::hideContent); mySingleAlarm.cancelAllRequests(); } @@ -164,8 +167,12 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } @Nullable - InstanceTrackingStrategy getStrategy(@NotNull ReferenceType ref) { - return myTrackedClasses.getOrDefault(ref, null); + TrackerForNewInstances getStrategy(@NotNull ReferenceType ref) { + if(myTrackedClasses.containsKey(ref)) { + return myTrackedClasses.get(ref); + } + + return myConstructorTrackedClasses.getOrDefault(ref, null); } public void setNeedReloadClasses(boolean value) { @@ -221,9 +228,10 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable final List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); for (ReferenceType ref : classes) { TrackingType type = myInstancesTracker.getTrackingType(ref.name()); - if (type != null && !myTrackedClasses.containsKey(ref)) { + if (type != null && !myTrackedClasses.containsKey(ref) && !myConstructorTrackedClasses.containsKey(ref)) { if (type == TrackingType.CREATION) { - + myConstructorTrackedClasses.put(ref, + new ConstructorInstancesTracker(ref, (DebugProcessImpl) myDebugProcess)); } else { List instances = ref.instances(0); myTrackedClasses.put(ref, InstanceTrackingStrategy.create(ref, suspendContext, type, instances)); diff --git a/view/ClassesTable.java b/view/ClassesTable.java index baccc02d1d05..513138c25be1 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -22,7 +22,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.event.InstancesTrackerListener; -import org.jetbrains.debugger.memory.tracking.InstanceTrackingStrategy; +import org.jetbrains.debugger.memory.tracking.TrackerForNewInstances; import org.jetbrains.debugger.memory.tracking.TrackingType; import org.jetbrains.debugger.memory.utils.AbstractTableColumnDescriptor; import org.jetbrains.debugger.memory.utils.AbstractTableModelWithColumns; @@ -210,12 +210,11 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { if (NEW_INSTANCES_PROVIDER_KEY.is(dataId)) { ReferenceType selectedClass = getSelectedClass(); if (selectedClass != null) { - InstanceTrackingStrategy strategy = myParent.getStrategy(selectedClass); + TrackerForNewInstances strategy = myParent.getStrategy(selectedClass); if (strategy != null) { List newInstances = strategy.getNewInstances(); return (InstancesProvider) limit -> newInstances; } - } } From cf329ee9a42e9e26a1140b3535731d1f28ac8113 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 19 Aug 2016 15:23:49 +0300 Subject: [PATCH 070/558] Fix empty text in classes table. When session suspended, need to change empty text for "Nothing to show" --- view/ClassesFilteredView.java | 9 ++++++++- view/ClassesTable.java | 4 ---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 193bd4160f77..357b4312d292 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -42,6 +42,9 @@ public class ClassesFilteredView extends BorderLayoutPanel { private static final Logger LOG = Logger.getInstance(ClassesFilteredView.class); private final static double DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT = 0.5; private final static int DEFAULT_BATCH_SIZE = Integer.MAX_VALUE; + private static final String EMPTY_TABLE_CONTENT_WHEN_RUNNED = "The application is running"; + private static final String EMPTY_TABLE_CONTENT_WHEN_SUSPENDED = "Nothing to show"; + private final Project myProject; private final XDebugSession myDebugSession; @@ -127,7 +130,10 @@ public class ClassesFilteredView extends BorderLayoutPanel { myDebugSession.addSessionListener(new XDebugSessionListener() { @Override public void sessionResumed() { - SwingUtilities.invokeLater(myTable::hideContent); + SwingUtilities.invokeLater(() -> { + myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNED); + myTable.hideContent(); + }); mySingleAlarm.cancelAllRequests(); } @@ -139,6 +145,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { @Override public void sessionPaused() { + SwingUtilities.invokeLater(() -> myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_SUSPENDED)); if (myNeedReloadClasses) { updateClassesAndCounts(); } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 1c2ced59bf9c..8fae7b9020ca 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -38,8 +38,6 @@ public class ClassesTable extends JBTable implements DataProvider { private static final int DIFF_COLUMN_MIN_WIDTH = 80; private static final int DIFF_COLUMN_MAX_WIDTH = 100; - private static final String EMPTY_TABLE_CONTENT = "The application is running"; - private final DiffViewTableModel myModel = new DiffViewTableModel(); private final UnknownDiffValue myUnknownValue = new UnknownDiffValue(); private final XDebugSession myDebugSession; @@ -60,8 +58,6 @@ public class ClassesTable extends JBTable implements DataProvider { myOnlyWithDiff = onlyWithDiff; myOnlyWithInstances = onlyWithInstances; - getEmptyText().setText(EMPTY_TABLE_CONTENT); - TableColumn classesColumn = getColumnModel().getColumn(DiffViewTableModel.CLASSNAME_COLUMN_INDEX); TableColumn countColumn = getColumnModel().getColumn(DiffViewTableModel.COUNT_COLUMN_INDEX); TableColumn diffColumn = getColumnModel().getColumn(DiffViewTableModel.DIFF_COLUMN_INDEX); From b135d8c9e8a2dac3d04a4b539a2a7e746a6551ce Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Mon, 29 Aug 2016 19:05:39 +0300 Subject: [PATCH 071/558] IDEA-160276 Fix diff column value when class loaded during last step --- view/ClassesTable.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 8fae7b9020ca..b209f2004d48 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -144,11 +144,15 @@ public class ClassesTable extends JBTable implements DataProvider { assert classes.size() == counts.length; ReferenceType selectedClass = myModel.getSelectedClassBeforeHided(); int newSelectedIndex = classes.indexOf(selectedClass); + boolean isInitialized = !myElems.isEmpty(); myElems = Collections.unmodifiableList(new ArrayList<>(classes)); for (int i = 0, size = classes.size(); i < size; i++) { ReferenceType ref = classes.get(i); - myCounts.put(ref, myCounts.getOrDefault(ref, myUnknownValue).update(counts[i])); + DiffValue oldValue = isInitialized && !myCounts.containsKey(ref) + ? new DiffValue(0, 0) + : myCounts.getOrDefault(ref, myUnknownValue); + myCounts.put(ref, oldValue.update(counts[i])); } showContent(); From 5913bbd203277a9c2f521cb9d2dbeee5b7030c13 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 30 Aug 2016 13:41:28 +0300 Subject: [PATCH 072/558] Dont track subclasses --- tracking/ConstructorInstancesTracker.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index 801df0caaf29..faf6eecca577 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -27,9 +27,11 @@ import java.util.List; public class ConstructorInstancesTracker implements TrackerForNewInstances { private final HashSet myNewObjects = new HashSet<>(); + private final ReferenceType myReference; public ConstructorInstancesTracker(@NotNull ReferenceType ref, @NotNull DebugProcessImpl debugProcess) { + myReference = ref; Project project = debugProcess.getProject(); JavaLineBreakpointType breakPointType = new JavaLineBreakpointType(); @@ -84,8 +86,9 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances { SuspendContextImpl suspendContext = action.getSuspendContext(); if (suspendContext != null) { ObjectReference thisRef = getThisObject(suspendContext, event); - System.out.println("new object " + thisRef.uniqueID() + " added."); - myNewObjects.add(thisRef); + if (myReference.equals(thisRef.referenceType())) { + myNewObjects.add(thisRef); + } } } catch (EvaluateException e) { e.printStackTrace(); From b95c15b8724d290708cbc3eae4f8558457ac4372 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 30 Aug 2016 15:20:53 +0300 Subject: [PATCH 073/558] Check that object is not collected before hash calculation --- tracking/HashBasedTracking.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tracking/HashBasedTracking.java b/tracking/HashBasedTracking.java index f21014e818fb..83955ce354d9 100644 --- a/tracking/HashBasedTracking.java +++ b/tracking/HashBasedTracking.java @@ -45,15 +45,22 @@ class HashBasedTracking extends InstanceTrackingStrategy { Map> result = new HashMap<>(); ThreadReferenceProxy threadProxy = suspendContext.getThread(); ThreadReference thread = threadProxy != null ? threadProxy.getThreadReference() : null; - if(thread == null) { + if(thread == null || hashCodeMethod == null) { return result; } for (ObjectReference ref : references) { Optional hash = Optional.empty(); try { - Value value = ref.invokeMethod(thread, hashCodeMethod, Collections.emptyList(), 0); - hash = Optional.of(((IntegerValue)value).value()); + if(!ref.isCollected()) { + ref.disableCollection(); + if(!ref.isCollected()) { + Value value = ref.invokeMethod(thread, hashCodeMethod, Collections.emptyList(), + ObjectReference.INVOKE_NONVIRTUAL); + hash = Optional.of(((IntegerValue)value).value()); + } + ref.enableCollection(); + } } catch (InvalidTypeException | ClassNotLoadedException | IncompatibleThreadStateException | InvocationException ignored) { } From 63bf235d073b726cbb7e4d4b60643a4fbb9e8581 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 31 Aug 2016 15:22:31 +0300 Subject: [PATCH 074/558] Don't update classes counts if context changed --- view/ClassesFilteredView.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 357b4312d292..b47def68e716 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -238,7 +238,7 @@ public class ClassesFilteredView extends BorderLayoutPanel { List chunks = new SmartList<>(); int size = classes.size(); for (int begin = 0, end = Math.min(batchSize, size); - begin != size && contextIsValid(); + begin != size && isContextValid(); begin = end, end = Math.min(end + batchSize, size)) { List batch = classes.subList(begin, end); @@ -252,21 +252,21 @@ public class ClassesFilteredView extends BorderLayoutPanel { LOG.info(String.format("Instances query time = %d ms. Count = %d", delay, batch.size())); } - final long[] counts = chunks.size() == 1 ? chunks.get(0) : IntStream.range(0, chunks.size()).boxed() - .flatMapToLong(integer -> Arrays.stream(chunks.get(integer))) - .toArray(); + if(isContextValid()) { + final long[] counts = chunks.size() == 1 ? chunks.get(0) : IntStream.range(0, chunks.size()).boxed() + .flatMapToLong(integer -> Arrays.stream(chunks.get(integer))) + .toArray(); + SwingUtilities.invokeLater(() -> myTable.setClassesAndUpdateCounts(classes, counts)); + } - SwingUtilities.invokeLater(() -> { - myTable.setClassesAndUpdateCounts(classes, counts); - myTable.setBusy(false); - }); + SwingUtilities.invokeLater(() -> myTable.setBusy(false)); } @Override public void commandCancelled() { } - private boolean contextIsValid() { + private boolean isContextValid() { return ClassesFilteredView.this.getSuspendContext() == getSuspendContext(); } } From 1b1e74e1e51bb212f78a987850c99832a9e38d9f Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 31 Aug 2016 17:53:12 +0300 Subject: [PATCH 075/558] Select first class in table if selection has been lost after filtering --- view/ClassesTable.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index b209f2004d48..4f44f5e32a36 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -123,6 +123,9 @@ public class ClassesTable extends JBTable implements DataProvider { myFilteringPattern = pattern; myMatcher = NameUtil.buildMatcher("*" + pattern).build(); getRowSorter().allRowsChanged(); + if(getSelectedClass() == null && getRowCount() > 0) { + getSelectionModel().setSelectionInterval(0, 0); + } } } From 4816300443ac17530a0897c2bf8e17000b65f0c7 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 1 Sep 2016 13:43:57 +0300 Subject: [PATCH 076/558] Save execution stack for tracked instances --- component/CreationPositionTracker.java | 57 +++++++++++++++++++++++ tracking/ConstructorInstancesTracker.java | 21 ++++++++- view/ClassesFilteredView.java | 3 +- 3 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 component/CreationPositionTracker.java diff --git a/component/CreationPositionTracker.java b/component/CreationPositionTracker.java new file mode 100644 index 000000000000..2fc414e18a93 --- /dev/null +++ b/component/CreationPositionTracker.java @@ -0,0 +1,57 @@ +package org.jetbrains.debugger.memory.component; + +import com.intellij.debugger.jdi.StackFrameProxyImpl; +import com.intellij.openapi.components.AbstractProjectComponent; +import com.intellij.openapi.project.Project; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XDebugSessionListener; +import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class CreationPositionTracker extends AbstractProjectComponent { + private final Map>> mySession2Reference2Stack + = new ConcurrentHashMap<>(); + + public CreationPositionTracker(Project project) { + super(project); + } + + public static CreationPositionTracker getInstance(@NotNull Project project) { + return project.getComponent(CreationPositionTracker.class); + } + + @Nullable + public List getStack(@NotNull XDebugSession session, @NotNull ObjectReference ref) { + Map> ref2Stack = mySession2Reference2Stack.getOrDefault(session, null); + List stack = ref2Stack != null ? ref2Stack.getOrDefault(ref, null) : null; + return stack != null ? Collections.unmodifiableList(stack) : null; + } + + public void addStack(@NotNull XDebugSession session, @NotNull ObjectReference ref, @NotNull List stack) { + if (!mySession2Reference2Stack.containsKey(session)) { + mySession2Reference2Stack.put(session, new ConcurrentHashMap<>()); + session.addSessionListener(new XDebugSessionListener() { + @Override + public void sessionStopped() { + mySession2Reference2Stack.remove(session); + } + }); + } + + mySession2Reference2Stack.get(session).put(ref, stack); + } + + public void releaseBySession(@NotNull XDebugSession session) { + if(!mySession2Reference2Stack.containsKey(session)) { + return; + } + + mySession2Reference2Stack.put(session, new ConcurrentHashMap<>()); + } +} diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index faf6eecca577..d85bf0b2ebd6 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -1,12 +1,16 @@ package org.jetbrains.debugger.memory.tracking; +import com.intellij.debugger.DebuggerManager; import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; +import com.intellij.debugger.jdi.StackFrameProxyImpl; +import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; import com.intellij.debugger.ui.breakpoints.JavaLineBreakpointType; import com.intellij.debugger.ui.breakpoints.LineBreakpoint; import com.intellij.openapi.project.Project; +import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.breakpoints.XBreakpoint; import com.intellij.xdebugger.impl.XDebuggerManagerImpl; import com.intellij.xdebugger.impl.breakpoints.LineBreakpointState; @@ -19,6 +23,7 @@ import com.sun.jdi.event.LocatableEvent; import com.sun.jdi.request.BreakpointRequest; import com.sun.jdi.request.EventRequest; import org.jetbrains.annotations.NotNull; +import org.jetbrains.debugger.memory.component.CreationPositionTracker; import org.jetbrains.java.debugger.breakpoints.properties.JavaLineBreakpointProperties; import java.util.ArrayList; @@ -28,11 +33,15 @@ import java.util.List; public class ConstructorInstancesTracker implements TrackerForNewInstances { private final HashSet myNewObjects = new HashSet<>(); private final ReferenceType myReference; + private final XDebugSession myDebugSession; + private final CreationPositionTracker myPositionTracker; public ConstructorInstancesTracker(@NotNull ReferenceType ref, - @NotNull DebugProcessImpl debugProcess) { + @NotNull XDebugSession debugSession) { myReference = ref; - Project project = debugProcess.getProject(); + myDebugSession = debugSession; + myPositionTracker = CreationPositionTracker.getInstance(debugSession.getProject()); + Project project = debugSession.getProject(); JavaLineBreakpointType breakPointType = new JavaLineBreakpointType(); XBreakpoint bpn = new XLineBreakpointImpl<>(breakPointType, @@ -40,11 +49,14 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances { new JavaLineBreakpointProperties(), new LineBreakpointState<>()); MyConstructorBreakpoints myLineBreakpoint = new MyConstructorBreakpoints(project, bpn); + DebugProcessImpl debugProcess = (DebugProcessImpl) DebuggerManager.getInstance(project) + .getDebugProcess(debugSession.getDebugProcess().getProcessHandler()); myLineBreakpoint.createRequestForPreparedClass(debugProcess, ref); } public void obsolete() { myNewObjects.clear(); + myPositionTracker.releaseBySession(myDebugSession); } @Override @@ -88,6 +100,11 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances { ObjectReference thisRef = getThisObject(suspendContext, event); if (myReference.equals(thisRef.referenceType())) { myNewObjects.add(thisRef); + ThreadReferenceProxyImpl threadReferenceProxy = suspendContext.getThread(); + List stack = threadReferenceProxy == null ? null : threadReferenceProxy.frames(); + if(stack != null) { + myPositionTracker.addStack(myDebugSession, thisRef, stack); + } } } } catch (EvaluateException e) { diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 9b65325c8d69..813450c818d6 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -2,7 +2,6 @@ package org.jetbrains.debugger.memory.view; import com.intellij.debugger.DebuggerManager; import com.intellij.debugger.engine.DebugProcess; -import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.DebuggerManagerThreadImpl; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; @@ -231,7 +230,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable if (type != null && !myTrackedClasses.containsKey(ref) && !myConstructorTrackedClasses.containsKey(ref)) { if (type == TrackingType.CREATION) { myConstructorTrackedClasses.put(ref, - new ConstructorInstancesTracker(ref, (DebugProcessImpl) myDebugProcess)); + new ConstructorInstancesTracker(ref, myDebugSession)); } else { List instances = ref.instances(0); myTrackedClasses.put(ref, InstanceTrackingStrategy.create(ref, suspendContext, type, instances)); From 2b3bc73a0db59d1fe021a4ab52e44734b8677901 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 1 Sep 2016 13:45:23 +0300 Subject: [PATCH 077/558] Add action for showing a stacktrace for specific instance --- action/DebuggerTreeAction.java | 28 ++++++++++++ action/ShowInstancesByClassAction.java | 23 +--------- .../tracking/GetAllocationSourceAction.java | 43 +++++++++++++++++++ 3 files changed, 72 insertions(+), 22 deletions(-) create mode 100644 action/DebuggerTreeAction.java create mode 100644 action/tracking/GetAllocationSourceAction.java diff --git a/action/DebuggerTreeAction.java b/action/DebuggerTreeAction.java new file mode 100644 index 000000000000..c46d72c5ff97 --- /dev/null +++ b/action/DebuggerTreeAction.java @@ -0,0 +1,28 @@ +package org.jetbrains.debugger.memory.action; + +import com.intellij.debugger.ui.impl.watch.NodeDescriptorProvider; +import com.intellij.debugger.ui.tree.NodeDescriptor; +import com.intellij.debugger.ui.tree.ValueDescriptor; +import com.intellij.xdebugger.frame.XValue; +import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase; +import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.Value; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public abstract class DebuggerTreeAction extends XDebuggerTreeActionBase { + @Nullable + protected ObjectReference getObjectReference(@NotNull XValueNodeImpl node) { + XValue valueContainer = node.getValueContainer(); + if (valueContainer instanceof NodeDescriptorProvider) { + NodeDescriptor descriptor = ((NodeDescriptorProvider) valueContainer).getDescriptor(); + if (descriptor instanceof ValueDescriptor) { + Value value = ((ValueDescriptor)descriptor).getValue(); + return value instanceof ObjectReference ? (ObjectReference) value : null; + } + } + + return null; + } +} diff --git a/action/ShowInstancesByClassAction.java b/action/ShowInstancesByClassAction.java index 451af6f9b1f4..f4ccf9271eea 100644 --- a/action/ShowInstancesByClassAction.java +++ b/action/ShowInstancesByClassAction.java @@ -1,24 +1,17 @@ package org.jetbrains.debugger.memory.action; -import com.intellij.debugger.ui.impl.watch.NodeDescriptorProvider; -import com.intellij.debugger.ui.tree.NodeDescriptor; -import com.intellij.debugger.ui.tree.ValueDescriptor; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebuggerManager; -import com.intellij.xdebugger.frame.XValue; -import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; -import com.sun.jdi.Value; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.view.InstancesWindow; -public class ShowInstancesByClassAction extends XDebuggerTreeActionBase { +public class ShowInstancesByClassAction extends DebuggerTreeAction { @Override protected boolean isEnabled(@NotNull XValueNodeImpl node, @NotNull AnActionEvent e) { ObjectReference ref = getObjectReference(node); @@ -42,18 +35,4 @@ public class ShowInstancesByClassAction extends XDebuggerTreeActionBase { } } } - - @Nullable - private ObjectReference getObjectReference(@NotNull XValueNodeImpl node) { - XValue valueContainer = node.getValueContainer(); - if (valueContainer instanceof NodeDescriptorProvider) { - NodeDescriptor descriptor = ((NodeDescriptorProvider) valueContainer).getDescriptor(); - if (descriptor instanceof ValueDescriptor) { - Value value = ((ValueDescriptor)descriptor).getValue(); - return value instanceof ObjectReference ? (ObjectReference) value : null; - } - } - - return null; - } } diff --git a/action/tracking/GetAllocationSourceAction.java b/action/tracking/GetAllocationSourceAction.java new file mode 100644 index 000000000000..9ec6531eebb6 --- /dev/null +++ b/action/tracking/GetAllocationSourceAction.java @@ -0,0 +1,43 @@ +package org.jetbrains.debugger.memory.action.tracking; + +import com.intellij.debugger.jdi.StackFrameProxyImpl; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.Project; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XDebuggerManager; +import com.intellij.xdebugger.frame.XExecutionStack; +import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; +import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.action.DebuggerTreeAction; +import org.jetbrains.debugger.memory.component.CreationPositionTracker; + +import java.util.List; + +public class GetAllocationSourceAction extends DebuggerTreeAction { + @Override + public void update(AnActionEvent e) { + e.getPresentation().setVisible(getStack(e) != null); + } + + @Override + protected void perform(XValueNodeImpl node, @NotNull String nodeName, AnActionEvent e) { + List stack = getStack(e); + System.out.println(stack != null); + } + + @Nullable + private List getStack(AnActionEvent e) { + Project project = e.getProject(); + XValueNodeImpl selectedNode = getSelectedNode(e.getDataContext()); + ObjectReference ref = selectedNode != null ? getObjectReference(selectedNode) : null; + if(project == null || ref == null) { + return null; + } + + // TODO: Wrong way: session may be not current + XDebugSession session = XDebuggerManager.getInstance(project).getCurrentSession(); + return session == null ? null : CreationPositionTracker.getInstance(project).getStack(session, ref); + } +} From 959890afb5814529bd3f91f826777a8e41b3bd90 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 1 Sep 2016 16:32:14 +0300 Subject: [PATCH 078/558] Add basic implementation for stack frame selector --- .../tracking/GetAllocationSourceAction.java | 16 ++-- component/CreationPositionTracker.java | 12 +-- tracking/ConstructorInstancesTracker.java | 22 +++-- utils/StackFrameDescriptor.java | 29 ++++++ view/StackFrameSelector.java | 88 +++++++++++++++++++ 5 files changed, 150 insertions(+), 17 deletions(-) create mode 100644 utils/StackFrameDescriptor.java create mode 100644 view/StackFrameSelector.java diff --git a/action/tracking/GetAllocationSourceAction.java b/action/tracking/GetAllocationSourceAction.java index 9ec6531eebb6..cfc3cd9d89b0 100644 --- a/action/tracking/GetAllocationSourceAction.java +++ b/action/tracking/GetAllocationSourceAction.java @@ -1,17 +1,19 @@ package org.jetbrains.debugger.memory.action.tracking; -import com.intellij.debugger.jdi.StackFrameProxyImpl; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.Project; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebuggerManager; -import com.intellij.xdebugger.frame.XExecutionStack; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.sun.jdi.ObjectReference; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.action.DebuggerTreeAction; import org.jetbrains.debugger.memory.component.CreationPositionTracker; +import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; +import org.jetbrains.debugger.memory.view.StackFrameSelector; import java.util.List; @@ -23,12 +25,16 @@ public class GetAllocationSourceAction extends DebuggerTreeAction { @Override protected void perform(XValueNodeImpl node, @NotNull String nodeName, AnActionEvent e) { - List stack = getStack(e); - System.out.println(stack != null); + Project project = e.getProject(); + List stack = getStack(e); + Editor editor = project == null ? null : FileEditorManager.getInstance(project).getSelectedTextEditor(); + if(project != null && stack != null && editor != null) { + new StackFrameSelector(project, stack, editor).show(); + } } @Nullable - private List getStack(AnActionEvent e) { + private List getStack(AnActionEvent e) { Project project = e.getProject(); XValueNodeImpl selectedNode = getSelectedNode(e.getDataContext()); ObjectReference ref = selectedNode != null ? getObjectReference(selectedNode) : null; diff --git a/component/CreationPositionTracker.java b/component/CreationPositionTracker.java index 2fc414e18a93..cb16fcd98a3a 100644 --- a/component/CreationPositionTracker.java +++ b/component/CreationPositionTracker.java @@ -1,6 +1,5 @@ package org.jetbrains.debugger.memory.component; -import com.intellij.debugger.jdi.StackFrameProxyImpl; import com.intellij.openapi.components.AbstractProjectComponent; import com.intellij.openapi.project.Project; import com.intellij.xdebugger.XDebugSession; @@ -8,6 +7,7 @@ import com.intellij.xdebugger.XDebugSessionListener; import com.sun.jdi.ObjectReference; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; import java.util.Collections; import java.util.List; @@ -15,7 +15,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class CreationPositionTracker extends AbstractProjectComponent { - private final Map>> mySession2Reference2Stack + private final Map>> mySession2Reference2Stack = new ConcurrentHashMap<>(); public CreationPositionTracker(Project project) { @@ -27,13 +27,13 @@ public class CreationPositionTracker extends AbstractProjectComponent { } @Nullable - public List getStack(@NotNull XDebugSession session, @NotNull ObjectReference ref) { - Map> ref2Stack = mySession2Reference2Stack.getOrDefault(session, null); - List stack = ref2Stack != null ? ref2Stack.getOrDefault(ref, null) : null; + public List getStack(@NotNull XDebugSession session, @NotNull ObjectReference ref) { + Map> ref2Stack = mySession2Reference2Stack.getOrDefault(session, null); + List stack = ref2Stack != null ? ref2Stack.getOrDefault(ref, null) : null; return stack != null ? Collections.unmodifiableList(stack) : null; } - public void addStack(@NotNull XDebugSession session, @NotNull ObjectReference ref, @NotNull List stack) { + public void addStack(@NotNull XDebugSession session, @NotNull ObjectReference ref, @NotNull List stack) { if (!mySession2Reference2Stack.containsKey(session)) { mySession2Reference2Stack.put(session, new ConcurrentHashMap<>()); session.addSessionListener(new XDebugSessionListener() { diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index d85bf0b2ebd6..7c1dee6fd3e2 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -15,20 +15,20 @@ import com.intellij.xdebugger.breakpoints.XBreakpoint; import com.intellij.xdebugger.impl.XDebuggerManagerImpl; import com.intellij.xdebugger.impl.breakpoints.LineBreakpointState; import com.intellij.xdebugger.impl.breakpoints.XLineBreakpointImpl; -import com.sun.jdi.Location; -import com.sun.jdi.Method; -import com.sun.jdi.ObjectReference; -import com.sun.jdi.ReferenceType; +import com.sun.jdi.*; import com.sun.jdi.event.LocatableEvent; import com.sun.jdi.request.BreakpointRequest; import com.sun.jdi.request.EventRequest; import org.jetbrains.annotations.NotNull; import org.jetbrains.debugger.memory.component.CreationPositionTracker; +import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; import org.jetbrains.java.debugger.breakpoints.properties.JavaLineBreakpointProperties; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; public class ConstructorInstancesTracker implements TrackerForNewInstances { private final HashSet myNewObjects = new HashSet<>(); @@ -102,8 +102,18 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances { myNewObjects.add(thisRef); ThreadReferenceProxyImpl threadReferenceProxy = suspendContext.getThread(); List stack = threadReferenceProxy == null ? null : threadReferenceProxy.frames(); - if(stack != null) { - myPositionTracker.addStack(myDebugSession, thisRef, stack); + + + if (stack != null) { + List stackFrameDescriptors = stack.stream().map(frame -> { + try { + Location loc = frame.location(); + return new StackFrameDescriptor(loc.declaringType().name(), frame.getIndexFromBottom(), loc.lineNumber()); + } catch (EvaluateException e) { + return null; + } + }).filter(Objects::nonNull).collect(Collectors.toList()); + myPositionTracker.addStack(myDebugSession, thisRef, stackFrameDescriptors); } } } diff --git a/utils/StackFrameDescriptor.java b/utils/StackFrameDescriptor.java new file mode 100644 index 000000000000..866edf73161c --- /dev/null +++ b/utils/StackFrameDescriptor.java @@ -0,0 +1,29 @@ +package org.jetbrains.debugger.memory.utils; + +import org.jetbrains.annotations.NotNull; + +public class StackFrameDescriptor { + private final String myFilePath; + private int myFrameLevel; + private int myLineNumber; + + public StackFrameDescriptor(@NotNull String path, int frameLevel, int line) { + myFilePath = path.replace('\\', '.'); + myFrameLevel = frameLevel; + myLineNumber = line; + } + + @NotNull + public String path() { + return myFilePath; + } + + public int line() { + return myLineNumber; + } + + @Override + public String toString() { + return String.format("%d. %s:%d", myFrameLevel, myFilePath, myLineNumber); + } +} diff --git a/view/StackFrameSelector.java b/view/StackFrameSelector.java new file mode 100644 index 000000000000..6f80becbdc56 --- /dev/null +++ b/view/StackFrameSelector.java @@ -0,0 +1,88 @@ +package org.jetbrains.debugger.memory.view; + +import com.intellij.codeInsight.navigation.NavigationUtil; +import com.intellij.debugger.engine.DebuggerUtils; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.LogicalPosition; +import com.intellij.openapi.editor.ScrollType; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.wm.IdeFocusManager; +import com.intellij.psi.PsiClass; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.ui.DoubleClickListener; +import com.intellij.ui.components.JBList; +import com.intellij.ui.components.JBScrollPane; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; + +import javax.swing.*; +import java.awt.event.MouseEvent; +import java.util.List; + +public class StackFrameSelector extends DialogWrapper { + private final Project myProject; + private final List myStackFrame; + private final Editor myEditor; + + public StackFrameSelector(@NotNull Project project, + @NotNull List stack, + @NotNull Editor editor) { + super(project, false); + myStackFrame = stack; + myProject = project; + myEditor = editor; + setModal(false); + init(); + } + + @Nullable + @Override + protected JComponent createNorthPanel() { + return null; + } + + @Nullable + @Override + protected JComponent createSouthPanel() { + return null; + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + JBList frameList = new JBList(new AbstractListModel() { + @Override + public int getSize() { + return myStackFrame.size(); + } + + @Override + public Object getElementAt(int index) { + return myStackFrame.get(index); + } + }); + + new DoubleClickListener() { + @Override + protected boolean onDoubleClick(MouseEvent event) { + StackFrameDescriptor selectedValue = (StackFrameDescriptor) frameList.getSelectedValue(); + if (selectedValue != null) { + PsiClass psiClass = DebuggerUtils.findClass(selectedValue.path(), myProject, GlobalSearchScope.allScope(myProject)); + if (psiClass != null) { + NavigationUtil.openFileWithPsiElement(psiClass, true, false); + myEditor.getCaretModel().removeSecondaryCarets(); + myEditor.getCaretModel().moveToLogicalPosition(new LogicalPosition(selectedValue.line() - 1, 0, false)); + myEditor.getScrollingModel().scrollToCaret(ScrollType.CENTER); + myEditor.getSelectionModel().removeSelection(); + IdeFocusManager.getGlobalInstance().requestFocus(myEditor.getContentComponent(), true); + } + } + return false; + } + }.installOn(frameList); + + return new JBScrollPane(frameList); + } +} From cf71634697e689e6166c59f1c2e48ca8085c25e2 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 1 Sep 2016 17:09:48 +0300 Subject: [PATCH 079/558] IDEA-160618 Set 500000 as limitation for instances loading --- view/InstancesWindow.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index b7eb2c39a70b..466a5639de1c 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -73,7 +73,7 @@ public class InstancesWindow extends DialogWrapper { private static final int DEFAULT_WINDOW_HEIGHT = 400; private static final int FILTERING_BUTTON_ADDITIONAL_WIDTH = 30; private static final int BORDER_LAYOUT_DEFAULT_GAP = 5; - private static final int DEFAULT_INSTANCES_COUNT = 0; // All + private static final int DEFAULT_INSTANCES_LIMIT = 500000; // All private final Project myProject; private final XDebugSession myDebugSession; @@ -249,22 +249,23 @@ public class InstancesWindow extends DialogWrapper { public void threadAction(@NotNull SuspendContextImpl suspendContext) { int limit = AndroidUtil.isAndroidVM(myReferenceType.virtualMachine()) ? AndroidUtil.ANDROID_INSTANCES_LIMIT - : DEFAULT_INSTANCES_COUNT; - List instances = myReferenceType.instances(limit); + : DEFAULT_INSTANCES_LIMIT; + List instances = myReferenceType.instances(limit + 1); EvaluationContextImpl evaluationContext = debugProcess .getDebuggerContext().createEvaluationContext(); - if (AndroidUtil.isAndroidVM(myReferenceType.virtualMachine()) - && instances.size() == AndroidUtil.ANDROID_INSTANCES_LIMIT) { - addWarningMessage("Not all instances will be shown (android restriction)"); + if (instances.size() > limit) { + addWarningMessage(String.format("Not all instances will be loaded (only %d)", limit)); + instances = instances.subList(0, limit); } + final int progressSize = instances.size(); if (evaluationContext != null) { synchronized (myFilteringTaskLock) { myFilteringTask = new MyFilteringWorker(instances, evaluationContext, createEvaluator()); SwingUtilities.invokeLater(() -> { - myProgress.start(instances.size()); + myProgress.start(progressSize); myFilteringTask.execute(); }); } From fe91e3b0b893932b6f6a4f32b3a228cc487d5432 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 1 Sep 2016 20:30:48 +0300 Subject: [PATCH 080/558] Improve UI and logic for instances tracking --- tracking/ConstructorInstancesTracker.java | 68 ++++++++++++++-------- tracking/HashBasedTracking.java | 36 +++++++++++- tracking/InstanceTrackingStrategy.java | 10 +++- tracking/TrackerForNewInstances.java | 3 + tracking/TrackingType.java | 5 +- utils/LowestPriorityCommand.java | 20 +++++++ view/ClassesFilteredView.java | 70 +++++++++++++++++------ view/ClassesTable.java | 38 ++++++------ 8 files changed, 186 insertions(+), 64 deletions(-) create mode 100644 utils/LowestPriorityCommand.java diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index 7c1dee6fd3e2..5d8e4ad9169d 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -9,32 +9,39 @@ import com.intellij.debugger.jdi.StackFrameProxyImpl; import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; import com.intellij.debugger.ui.breakpoints.JavaLineBreakpointType; import com.intellij.debugger.ui.breakpoints.LineBreakpoint; +import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.breakpoints.XBreakpoint; import com.intellij.xdebugger.impl.XDebuggerManagerImpl; import com.intellij.xdebugger.impl.breakpoints.LineBreakpointState; import com.intellij.xdebugger.impl.breakpoints.XLineBreakpointImpl; -import com.sun.jdi.*; +import com.sun.jdi.Location; +import com.sun.jdi.Method; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; import com.sun.jdi.event.LocatableEvent; import com.sun.jdi.request.BreakpointRequest; import com.sun.jdi.request.EventRequest; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.CreationPositionTracker; import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; import org.jetbrains.java.debugger.breakpoints.properties.JavaLineBreakpointProperties; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.stream.Collectors; -public class ConstructorInstancesTracker implements TrackerForNewInstances { - private final HashSet myNewObjects = new HashSet<>(); +public class ConstructorInstancesTracker implements TrackerForNewInstances, Disposable { private final ReferenceType myReference; private final XDebugSession myDebugSession; private final CreationPositionTracker myPositionTracker; + private final DebugProcessImpl myDebugProcess; + private final MyConstructorBreakpoints myBreakpoint; + @Nullable + private HashSet myNewObjects = null; + @NotNull + private HashSet myTrackedObjects = new HashSet<>(); public ConstructorInstancesTracker(@NotNull ReferenceType ref, @NotNull XDebugSession debugSession) { @@ -42,29 +49,46 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances { myDebugSession = debugSession; myPositionTracker = CreationPositionTracker.getInstance(debugSession.getProject()); Project project = debugSession.getProject(); + myDebugProcess = (DebugProcessImpl) DebuggerManager.getInstance(project) + .getDebugProcess(debugSession.getDebugProcess().getProcessHandler()); JavaLineBreakpointType breakPointType = new JavaLineBreakpointType(); XBreakpoint bpn = new XLineBreakpointImpl<>(breakPointType, ((XDebuggerManagerImpl) XDebuggerManagerImpl.getInstance(project)).getBreakpointManager(), new JavaLineBreakpointProperties(), new LineBreakpointState<>()); - MyConstructorBreakpoints myLineBreakpoint = new MyConstructorBreakpoints(project, bpn); - DebugProcessImpl debugProcess = (DebugProcessImpl) DebuggerManager.getInstance(project) - .getDebugProcess(debugSession.getDebugProcess().getProcessHandler()); - myLineBreakpoint.createRequestForPreparedClass(debugProcess, ref); + myBreakpoint= new MyConstructorBreakpoints(project, bpn); + myBreakpoint.createRequestForPreparedClass(myDebugProcess, ref); } public void obsolete() { - myNewObjects.clear(); + myNewObjects = null; myPositionTracker.releaseBySession(myDebugSession); } - @Override - public List getNewInstances() { - return new ArrayList<>(myNewObjects); + public void commitTracked() { + myNewObjects = myTrackedObjects; + myTrackedObjects = new HashSet<>(); } - private final class MyConstructorBreakpoints extends LineBreakpoint { + @NotNull + @Override + public List getNewInstances() { + return myNewObjects == null ? Collections.EMPTY_LIST : new ArrayList<>(myNewObjects); + } + + @Override + public boolean isReady() { + return myNewObjects != null; + } + + @Override + public void dispose() { + myBreakpoint.dispose(); + } + + private final class MyConstructorBreakpoints extends LineBreakpoint + implements Disposable { private final List myRequests = new ArrayList<>(); MyConstructorBreakpoints(Project project, XBreakpoint xBreakpoint) { @@ -81,14 +105,11 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances { } } - void disable(@NotNull DebugProcessImpl debugProcess) { + @Override + public void dispose() { myRequests.forEach(EventRequest::disable); myRequests.clear(); - debugProcess.getRequestsManager().deleteRequest(this); - } - - @Override - public void reload() { + myDebugProcess.getRequestsManager().deleteRequest(this); } @Override @@ -99,11 +120,10 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances { if (suspendContext != null) { ObjectReference thisRef = getThisObject(suspendContext, event); if (myReference.equals(thisRef.referenceType())) { - myNewObjects.add(thisRef); + myTrackedObjects.add(thisRef); ThreadReferenceProxyImpl threadReferenceProxy = suspendContext.getThread(); List stack = threadReferenceProxy == null ? null : threadReferenceProxy.frames(); - if (stack != null) { List stackFrameDescriptors = stack.stream().map(frame -> { try { diff --git a/tracking/HashBasedTracking.java b/tracking/HashBasedTracking.java index 83955ce354d9..856ee5cfe4f9 100644 --- a/tracking/HashBasedTracking.java +++ b/tracking/HashBasedTracking.java @@ -12,12 +12,16 @@ import java.util.stream.Collectors; class HashBasedTracking extends InstanceTrackingStrategy { private final ReferenceType myClassType; private Set myHashesSet; + private MyState myState = MyState.WAIT_HASHES; - HashBasedTracking(@NotNull SuspendContextImpl suspendContext, + HashBasedTracking(@Nullable SuspendContextImpl suspendContext, @NotNull ReferenceType classType, @NotNull List initialInstances) { myClassType = classType; - myHashesSet = toSetOfHashes(evalHashCodes(suspendContext, initialInstances)); + if(suspendContext != null) { + myHashesSet = toSetOfHashes(evalHashCodes(suspendContext, initialInstances)); + myState = myState.next(); + } } @NotNull @@ -35,9 +39,15 @@ class HashBasedTracking extends InstanceTrackingStrategy { } myHashesSet = toSetOfHashes(ref2hash); + myState = myState.next(); return newInstances; } + @Override + public boolean isReady() { + return myState == MyState.READY; + } + @NotNull private Map> evalHashCodes(@NotNull SuspendContextImpl suspendContext, @NotNull List references) { @@ -89,4 +99,26 @@ class HashBasedTracking extends InstanceTrackingStrategy { .findFirst() .orElseGet(null); } + + // WAIT_HASHES -> WAIT_UPDATE -> READY + enum MyState { + WAIT_HASHES { + @Override + public MyState next() { + return WAIT_UPDATE; + } + }, WAIT_UPDATE { + @Override + public MyState next() { + return READY; + } + }, READY { + @Override + public MyState next() { + return this; + } + }; + + public abstract MyState next(); + } } diff --git a/tracking/InstanceTrackingStrategy.java b/tracking/InstanceTrackingStrategy.java index 48497b16dc43..9385105e4380 100644 --- a/tracking/InstanceTrackingStrategy.java +++ b/tracking/InstanceTrackingStrategy.java @@ -4,6 +4,7 @@ import com.intellij.debugger.engine.SuspendContextImpl; import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; @@ -12,9 +13,10 @@ import java.util.List; public abstract class InstanceTrackingStrategy implements TrackerForNewInstances { @NotNull private List myNewInstances = new ArrayList<>(); + private boolean myIsReady = false; public static InstanceTrackingStrategy create(@NotNull ReferenceType referenceType, - @NotNull SuspendContextImpl suspendContext, + @Nullable SuspendContextImpl suspendContext, @NotNull TrackingType type, @NotNull List init) { switch (type) { @@ -36,6 +38,12 @@ public abstract class InstanceTrackingStrategy implements TrackerForNewInstances public final void update(@NotNull SuspendContextImpl suspendContext, @NotNull List references) { myNewInstances = updateImpl(suspendContext, references); + myIsReady = true; + } + + @Override + public boolean isReady() { + return myIsReady; } @NotNull diff --git a/tracking/TrackerForNewInstances.java b/tracking/TrackerForNewInstances.java index 4ea69620c6a8..d28b930a7b77 100644 --- a/tracking/TrackerForNewInstances.java +++ b/tracking/TrackerForNewInstances.java @@ -1,9 +1,12 @@ package org.jetbrains.debugger.memory.tracking; import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; import java.util.List; public interface TrackerForNewInstances { + @NotNull List getNewInstances(); + boolean isReady(); } diff --git a/tracking/TrackingType.java b/tracking/TrackingType.java index bbdb16794b3e..a000daaade40 100644 --- a/tracking/TrackingType.java +++ b/tracking/TrackingType.java @@ -4,7 +4,8 @@ import com.intellij.ui.JBColor; import org.jetbrains.annotations.NotNull; public enum TrackingType { - IDENTITY("Id Based Tracking", JBColor.yellow), + + IDENTITY("Id Based Tracking",JBColor.yellow), HASH("Hash Based Tracking", JBColor.yellow), RETAIN("Retaining Reference Object Tracking", JBColor.yellow), CREATION("Track Constructors", JBColor.orange); @@ -26,4 +27,6 @@ public enum TrackingType { public String description() { return myDescription; } + + public static JBColor inactiveColor() { return JBColor.GRAY; } } diff --git a/utils/LowestPriorityCommand.java b/utils/LowestPriorityCommand.java new file mode 100644 index 000000000000..214cc5681f66 --- /dev/null +++ b/utils/LowestPriorityCommand.java @@ -0,0 +1,20 @@ +package org.jetbrains.debugger.memory.utils; + +import com.intellij.debugger.engine.SuspendContextImpl; +import com.intellij.debugger.engine.events.SuspendContextCommandImpl; +import org.jetbrains.annotations.Nullable; + +public class LowestPriorityCommand extends SuspendContextCommandImpl { + protected LowestPriorityCommand(@Nullable SuspendContextImpl suspendContext) { + super(suspendContext); + } + + @Override + protected void commandCancelled() { + } + + @Override + public Priority getPriority() { + return Priority.LOWEST; + } +} diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 813450c818d6..af41691039db 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -4,7 +4,6 @@ import com.intellij.debugger.DebuggerManager; import com.intellij.debugger.engine.DebugProcess; import com.intellij.debugger.engine.DebuggerManagerThreadImpl; import com.intellij.debugger.engine.SuspendContextImpl; -import com.intellij.debugger.engine.events.SuspendContextCommandImpl; import com.intellij.debugger.impl.DebuggerManagerImpl; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.ActionGroup; @@ -26,6 +25,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.component.MemoryViewManager; import org.jetbrains.debugger.memory.component.MemoryViewManagerState; +import org.jetbrains.debugger.memory.event.InstancesTrackerListener; import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; import org.jetbrains.debugger.memory.tracking.ConstructorInstancesTracker; import org.jetbrains.debugger.memory.tracking.InstanceTrackingStrategy; @@ -33,6 +33,7 @@ import org.jetbrains.debugger.memory.tracking.TrackerForNewInstances; import org.jetbrains.debugger.memory.tracking.TrackingType; import org.jetbrains.debugger.memory.utils.AndroidUtil; import org.jetbrains.debugger.memory.utils.KeyboardUtils; +import org.jetbrains.debugger.memory.utils.LowestPriorityCommand; import org.jetbrains.debugger.memory.utils.SingleAlarmWithMutableDelay; import javax.swing.*; @@ -75,7 +76,27 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myDebugProcess = DebuggerManager.getInstance(myProject) .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); myInstancesTracker = InstancesTracker.getInstance(myProject); + myInstancesTracker.addTrackerListener(new InstancesTrackerListener() { + @Override + public void classChanged(@NotNull String name, @NotNull TrackingType type) { + ReferenceType ref = myTable.getClassByName(name); + if (ref != null) { + ((DebuggerManagerThreadImpl) myDebugProcess.getManagerThread()) + .schedule(new LowestPriorityCommand(getSuspendContext()) { + @Override + public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { + trackClass(ref, type, suspendContext); + } + }); + } + } + @Override + public void classRemoved(@NotNull String name) { + myTrackedClasses.keySet().removeIf(referenceType -> name.equals(referenceType.name())); + myTable.getRowSorter().allRowsChanged(); + } + }, this); MemoryViewManagerState memoryViewManagerState = MemoryViewManager.getInstance().getState(); myTable = new ClassesTable(myDebugSession, memoryViewManagerState.isShowWithDiffOnly, @@ -127,6 +148,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void sessionPaused() { if (myNeedReloadClasses) { + myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::commitTracked); updateClassesAndCounts(); } } @@ -167,7 +189,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Nullable TrackerForNewInstances getStrategy(@NotNull ReferenceType ref) { - if(myTrackedClasses.containsKey(ref)) { + if (myTrackedClasses.containsKey(ref)) { return myTrackedClasses.get(ref); } @@ -186,6 +208,20 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } } + private void trackClass(@NotNull ReferenceType ref, + @NotNull TrackingType type, + @Nullable SuspendContextImpl suspendContext) { + LOG.assertTrue(DebuggerManager.getInstance(myProject).isDebuggerManagerThread()); + if (type == TrackingType.CREATION) { + myConstructorTrackedClasses.put(ref, + new ConstructorInstancesTracker(ref, myDebugSession)); + } else { + List instances = ref.instances(0); + myTrackedClasses.put(ref, InstanceTrackingStrategy.create(ref, suspendContext, type, instances)); + } + } + + private void handleClassSelection(@Nullable ReferenceType ref) { if (ref != null && myDebugSession.isSuspended()) { new InstancesWindow(myDebugSession, ref::instances, ref.name()).show(); @@ -207,34 +243,34 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable return ActionManager.getInstance().createActionPopupMenu("ClassesView.PopupActionGroup", group); } + boolean isTrackingActive(@NotNull ReferenceType ref) { + TrackerForNewInstances strategy = myConstructorTrackedClasses.getOrDefault(ref, null); + if (strategy != null) { + return strategy.isReady(); + } + + strategy = myTrackedClasses.getOrDefault(ref, null); + return strategy != null && strategy.isReady(); + + } + @Override public void dispose() { } - private final class MyUpdateClassesCommand extends SuspendContextCommandImpl { + private final class MyUpdateClassesCommand extends LowestPriorityCommand { MyUpdateClassesCommand(@Nullable SuspendContextImpl suspendContext) { super(suspendContext); } - @Override - public Priority getPriority() { - return Priority.LOWEST; - } - @Override public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { final List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); for (ReferenceType ref : classes) { TrackingType type = myInstancesTracker.getTrackingType(ref.name()); if (type != null && !myTrackedClasses.containsKey(ref) && !myConstructorTrackedClasses.containsKey(ref)) { - if (type == TrackingType.CREATION) { - myConstructorTrackedClasses.put(ref, - new ConstructorInstancesTracker(ref, myDebugSession)); - } else { - List instances = ref.instances(0); - myTrackedClasses.put(ref, InstanceTrackingStrategy.create(ref, suspendContext, type, instances)); - } + trackClass(ref, type, suspendContext); } } @@ -278,10 +314,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable }); } - @Override - public void commandCancelled() { - } - private boolean contextIsValid() { return ClassesFilteredView.this.getSuspendContext() == getSuspendContext(); } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 513138c25be1..664584ad6bb5 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -7,6 +7,7 @@ import com.intellij.openapi.util.TextRange; import com.intellij.psi.codeStyle.MinusculeMatcher; import com.intellij.psi.codeStyle.NameUtil; import com.intellij.ui.ColoredTableCellRenderer; +import com.intellij.ui.JBColor; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.speedSearch.SpeedSearchUtil; import com.intellij.ui.table.JBTable; @@ -21,7 +22,6 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.InstancesTracker; -import org.jetbrains.debugger.memory.event.InstancesTrackerListener; import org.jetbrains.debugger.memory.tracking.TrackerForNewInstances; import org.jetbrains.debugger.memory.tracking.TrackingType; import org.jetbrains.debugger.memory.utils.AbstractTableColumnDescriptor; @@ -75,19 +75,6 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { getEmptyText().setText(EMPTY_TABLE_CONTENT); - InstancesTracker tracker = InstancesTracker.getInstance(myDebugSession.getProject()); - tracker.addTrackerListener(new InstancesTrackerListener() { - @Override - public void classChanged(@NotNull String name, @NotNull TrackingType type) { - getRowSorter().allRowsChanged(); - } - - @Override - public void classRemoved(@NotNull String name) { - getRowSorter().allRowsChanged(); - } - }, this); - TableColumn classesColumn = getColumnModel().getColumn(DiffViewTableModel.CLASSNAME_COLUMN_INDEX); TableColumn countColumn = getColumnModel().getColumn(DiffViewTableModel.COUNT_COLUMN_INDEX); TableColumn diffColumn = getColumnModel().getColumn(DiffViewTableModel.DIFF_COLUMN_INDEX); @@ -144,6 +131,17 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { return null; } + @Nullable + ReferenceType getClassByName(@NotNull String name) { + for(ReferenceType ref : myElems) { + if(name.equals(ref.name())) { + return ref; + } + } + + return null; + } + void setBusy(boolean value) { setPaintBusy(value); } @@ -211,7 +209,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { ReferenceType selectedClass = getSelectedClass(); if (selectedClass != null) { TrackerForNewInstances strategy = myParent.getStrategy(selectedClass); - if (strategy != null) { + if (strategy != null && strategy.isReady()) { List newInstances = strategy.getNewInstances(); return (InstancesProvider) limit -> newInstances; } @@ -359,7 +357,10 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { TrackingType trackingType = getTrackingType(row); if (!isSelected && trackingType != null) { - setBackground(UIUtil.toAlpha(trackingType.color(), 20)); + JBColor color = myParent.isTrackingActive(myElems.get(convertRowIndexToModel(row))) + ? trackingType.color() + : TrackingType.inactiveColor(); + setBackground(UIUtil.toAlpha(color, 20)); } long val = (long) value; @@ -390,7 +391,10 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { String presentation = value == null ? "null" : ((ReferenceType) value).name(); TrackingType trackingType = getTrackingType(row); if (trackingType != null && !isSelected) { - setBackground(UIUtil.toAlpha(trackingType.color(), 20)); + JBColor color = myParent.isTrackingActive(myElems.get(convertRowIndexToModel(row))) + ? trackingType.color() + : TrackingType.inactiveColor(); + setBackground(UIUtil.toAlpha(color, 20)); } append(" "); From 6ce7e15e60310ed49a75a13a660e559092ea7569 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 1 Sep 2016 23:45:07 +0300 Subject: [PATCH 081/558] Fix order of updating for new trackers --- tracking/ConstructorInstancesTracker.java | 5 +++++ view/ClassesFilteredView.java | 9 +++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index 5d8e4ad9169d..5cc111eadfbd 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -57,6 +57,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp ((XDebuggerManagerImpl) XDebuggerManagerImpl.getInstance(project)).getBreakpointManager(), new JavaLineBreakpointProperties(), new LineBreakpointState<>()); + myBreakpoint= new MyConstructorBreakpoints(project, bpn); myBreakpoint.createRequestForPreparedClass(myDebugProcess, ref); } @@ -105,6 +106,10 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp } } + @Override + public void reload() { + } + @Override public void dispose() { myRequests.forEach(EventRequest::disable); diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index af41691039db..c29fd1d083a7 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -267,6 +267,11 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { final List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); + + for (Map.Entry entry : myTrackedClasses.entrySet()) { + entry.getValue().update(suspendContext, entry.getKey().instances(0)); + } + for (ReferenceType ref : classes) { TrackingType type = myInstancesTracker.getTrackingType(ref.name()); if (type != null && !myTrackedClasses.containsKey(ref) && !myConstructorTrackedClasses.containsKey(ref)) { @@ -274,10 +279,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } } - for (Map.Entry entry : myTrackedClasses.entrySet()) { - entry.getValue().update(suspendContext, entry.getKey().instances(0)); - } - if (classes.isEmpty()) { return; } From a76d9cba063fbaa78baa8049ad4f95d459c35f2d Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 2 Sep 2016 12:06:28 +0300 Subject: [PATCH 082/558] Dispose constructor trackers correctly --- tracking/ConstructorInstancesTracker.java | 24 ++++++++++++----------- view/ClassesFilteredView.java | 11 ++++++++--- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index 5cc111eadfbd..eaf5a4511e1e 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -4,6 +4,7 @@ import com.intellij.debugger.DebuggerManager; import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.evaluation.EvaluateException; +import com.intellij.debugger.engine.events.DebuggerCommandImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; import com.intellij.debugger.jdi.StackFrameProxyImpl; import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; @@ -11,6 +12,7 @@ import com.intellij.debugger.ui.breakpoints.JavaLineBreakpointType; import com.intellij.debugger.ui.breakpoints.LineBreakpoint; import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.breakpoints.XBreakpoint; import com.intellij.xdebugger.impl.XDebuggerManagerImpl; @@ -22,7 +24,6 @@ import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import com.sun.jdi.event.LocatableEvent; import com.sun.jdi.request.BreakpointRequest; -import com.sun.jdi.request.EventRequest; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.CreationPositionTracker; @@ -37,7 +38,6 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp private final XDebugSession myDebugSession; private final CreationPositionTracker myPositionTracker; private final DebugProcessImpl myDebugProcess; - private final MyConstructorBreakpoints myBreakpoint; @Nullable private HashSet myNewObjects = null; @NotNull @@ -58,8 +58,9 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp new JavaLineBreakpointProperties(), new LineBreakpointState<>()); - myBreakpoint= new MyConstructorBreakpoints(project, bpn); - myBreakpoint.createRequestForPreparedClass(myDebugProcess, ref); + MyConstructorBreakpoints breakpoint = new MyConstructorBreakpoints(project, bpn); + breakpoint.createRequestForPreparedClass(myDebugProcess, ref); + Disposer.register(this, breakpoint); } public void obsolete() { @@ -85,12 +86,10 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp @Override public void dispose() { - myBreakpoint.dispose(); } private final class MyConstructorBreakpoints extends LineBreakpoint implements Disposable { - private final List myRequests = new ArrayList<>(); MyConstructorBreakpoints(Project project, XBreakpoint xBreakpoint) { super(project, xBreakpoint); @@ -102,7 +101,6 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp Location loc = cons.location(); BreakpointRequest breakpointRequest = debugProcess.getRequestsManager().createBreakpointRequest(this, loc); breakpointRequest.enable(); - myRequests.add(breakpointRequest); } } @@ -112,9 +110,12 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp @Override public void dispose() { - myRequests.forEach(EventRequest::disable); - myRequests.clear(); - myDebugProcess.getRequestsManager().deleteRequest(this); + myDebugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { + @Override + protected void action() throws Exception { + myDebugProcess.getRequestsManager().deleteRequest(MyConstructorBreakpoints.this); + } + }); } @Override @@ -133,7 +134,8 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp List stackFrameDescriptors = stack.stream().map(frame -> { try { Location loc = frame.location(); - return new StackFrameDescriptor(loc.declaringType().name(), frame.getIndexFromBottom(), loc.lineNumber()); + return new StackFrameDescriptor(loc.declaringType().name(), + frame.getIndexFromBottom(), loc.lineNumber()); } catch (EvaluateException e) { return null; } diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index c29fd1d083a7..43c1bb4fbea5 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -213,15 +213,20 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Nullable SuspendContextImpl suspendContext) { LOG.assertTrue(DebuggerManager.getInstance(myProject).isDebuggerManagerThread()); if (type == TrackingType.CREATION) { - myConstructorTrackedClasses.put(ref, - new ConstructorInstancesTracker(ref, myDebugSession)); + ConstructorInstancesTracker old = myConstructorTrackedClasses.getOrDefault(ref, null); + if (old != null) { + Disposer.dispose(old); + } + + ConstructorInstancesTracker tracker = new ConstructorInstancesTracker(ref, myDebugSession); + Disposer.register(ClassesFilteredView.this, tracker); + myConstructorTrackedClasses.put(ref, tracker); } else { List instances = ref.instances(0); myTrackedClasses.put(ref, InstanceTrackingStrategy.create(ref, suspendContext, type, instances)); } } - private void handleClassSelection(@Nullable ReferenceType ref) { if (ref != null && myDebugSession.isSuspended()) { new InstancesWindow(myDebugSession, ref::instances, ref.name()).show(); From b6a24d0379976f0c6083ea56444f61dbc499a8da Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 2 Sep 2016 19:13:47 +0300 Subject: [PATCH 083/558] Add navigation for stack frames for new instances --- .../tracking/GetAllocationSourceAction.java | 14 ++++++--- view/StackFrameSelector.java | 31 +++++++++---------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/action/tracking/GetAllocationSourceAction.java b/action/tracking/GetAllocationSourceAction.java index cfc3cd9d89b0..555d6309d3e5 100644 --- a/action/tracking/GetAllocationSourceAction.java +++ b/action/tracking/GetAllocationSourceAction.java @@ -1,9 +1,9 @@ package org.jetbrains.debugger.memory.action.tracking; +import com.intellij.debugger.DebuggerManager; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.Project; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebuggerManager; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; @@ -27,9 +27,13 @@ public class GetAllocationSourceAction extends DebuggerTreeAction { protected void perform(XValueNodeImpl node, @NotNull String nodeName, AnActionEvent e) { Project project = e.getProject(); List stack = getStack(e); - Editor editor = project == null ? null : FileEditorManager.getInstance(project).getSelectedTextEditor(); - if(project != null && stack != null && editor != null) { - new StackFrameSelector(project, stack, editor).show(); + if(project != null && stack != null) { + XDebugSession session = XDebuggerManager.getInstance(project).getCurrentSession(); + if(session != null) { + GlobalSearchScope searchScope = DebuggerManager.getInstance(project) + .getDebugProcess(session.getDebugProcess().getProcessHandler()).getSearchScope(); + new StackFrameSelector(project, stack, searchScope).show(); + } } } diff --git a/view/StackFrameSelector.java b/view/StackFrameSelector.java index 6f80becbdc56..52df1c30baeb 100644 --- a/view/StackFrameSelector.java +++ b/view/StackFrameSelector.java @@ -1,13 +1,10 @@ package org.jetbrains.debugger.memory.view; -import com.intellij.codeInsight.navigation.NavigationUtil; import com.intellij.debugger.engine.DebuggerUtils; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.LogicalPosition; -import com.intellij.openapi.editor.ScrollType; +import com.intellij.execution.filters.OpenFileHyperlinkInfo; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.psi.PsiClass; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.DoubleClickListener; @@ -24,15 +21,15 @@ import java.util.List; public class StackFrameSelector extends DialogWrapper { private final Project myProject; private final List myStackFrame; - private final Editor myEditor; + private final GlobalSearchScope mySearchScope; public StackFrameSelector(@NotNull Project project, @NotNull List stack, - @NotNull Editor editor) { + @NotNull GlobalSearchScope searchScope) { super(project, false); - myStackFrame = stack; myProject = project; - myEditor = editor; + myStackFrame = stack; + mySearchScope = searchScope; setModal(false); init(); } @@ -69,14 +66,14 @@ public class StackFrameSelector extends DialogWrapper { protected boolean onDoubleClick(MouseEvent event) { StackFrameDescriptor selectedValue = (StackFrameDescriptor) frameList.getSelectedValue(); if (selectedValue != null) { - PsiClass psiClass = DebuggerUtils.findClass(selectedValue.path(), myProject, GlobalSearchScope.allScope(myProject)); - if (psiClass != null) { - NavigationUtil.openFileWithPsiElement(psiClass, true, false); - myEditor.getCaretModel().removeSecondaryCarets(); - myEditor.getCaretModel().moveToLogicalPosition(new LogicalPosition(selectedValue.line() - 1, 0, false)); - myEditor.getScrollingModel().scrollToCaret(ScrollType.CENTER); - myEditor.getSelectionModel().removeSelection(); - IdeFocusManager.getGlobalInstance().requestFocus(myEditor.getContentComponent(), true); + PsiClass psiClass = DebuggerUtils.findClass(selectedValue.path(), myProject, mySearchScope); + if(psiClass != null) { + ApplicationManager.getApplication().runReadAction(() -> { + OpenFileHyperlinkInfo info = + new OpenFileHyperlinkInfo(myProject, + psiClass.getContainingFile().getVirtualFile(), selectedValue.line() - 1); + info.navigate(myProject); + }); } } return false; From 186ba9408c9145a9b63ad36d0c9070408e1750d4 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 2 Sep 2016 20:05:26 +0300 Subject: [PATCH 084/558] Add basic implementation for tracking unloaded classes --- component/InstancesTracker.java | 14 ++++++++-- tracking/ConstructorInstancesTracker.java | 9 ++++++- view/ClassesFilteredView.java | 33 +++++++++++++++++------ 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java index 77145325508f..13e1d5705883 100644 --- a/component/InstancesTracker.java +++ b/component/InstancesTracker.java @@ -11,8 +11,10 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.event.InstancesTrackerListener; import org.jetbrains.debugger.memory.tracking.TrackingType; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; @State(name = "InstancesTracker", storages = @Storage(StoragePathMacros.WORKSPACE_FILE)) public class InstancesTracker extends AbstractProjectComponent @@ -38,9 +40,17 @@ public class InstancesTracker extends AbstractProjectComponent return myState.classes.getOrDefault(className, null); } + @NotNull + public List getClassesByTrackingType(@NotNull TrackingType type) { + return myState.classes.entrySet().stream() + .filter(entry -> entry.getValue().equals(type)) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + } + public void add(@NotNull ReferenceType ref, @NotNull TrackingType type) { String name = ref.name(); - if(type.equals(myState.classes.getOrDefault(name, null))) { + if (type.equals(myState.classes.getOrDefault(name, null))) { return; } @@ -51,7 +61,7 @@ public class InstancesTracker extends AbstractProjectComponent public boolean remove(@NotNull ReferenceType ref) { String name = ref.name(); TrackingType removed = myState.classes.remove(name); - if(removed != null) { + if (removed != null) { myDispatcher.getMulticaster().classRemoved(name); return true; } diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index eaf5a4511e1e..777936f8114e 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -1,6 +1,7 @@ package org.jetbrains.debugger.memory.tracking; import com.intellij.debugger.DebuggerManager; +import com.intellij.debugger.engine.DebugProcess; import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.evaluation.EvaluateException; @@ -59,7 +60,8 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp new LineBreakpointState<>()); MyConstructorBreakpoints breakpoint = new MyConstructorBreakpoints(project, bpn); - breakpoint.createRequestForPreparedClass(myDebugProcess, ref); + myDebugProcess.getRequestsManager().callbackOnPrepareClasses(breakpoint, myReference.name()); + Disposer.register(this, breakpoint); } @@ -95,6 +97,11 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp super(project, xBreakpoint); } + @Override + public void processClassPrepare(DebugProcess debugProcess, ReferenceType classType) { + createRequestForPreparedClass(myDebugProcess, classType); + } + @Override protected void createRequestForPreparedClass(DebugProcessImpl debugProcess, ReferenceType classType) { for (Method cons : classType.methodsByName("")) { diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 43c1bb4fbea5..a492ab0d54e9 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -1,9 +1,9 @@ package org.jetbrains.debugger.memory.view; import com.intellij.debugger.DebuggerManager; -import com.intellij.debugger.engine.DebugProcess; -import com.intellij.debugger.engine.DebuggerManagerThreadImpl; +import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.SuspendContextImpl; +import com.intellij.debugger.engine.events.DebuggerCommandImpl; import com.intellij.debugger.impl.DebuggerManagerImpl; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.ActionGroup; @@ -56,7 +56,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private final Project myProject; private final XDebugSession myDebugSession; - private final DebugProcess myDebugProcess; + private final DebugProcessImpl myDebugProcess; private final SingleAlarmWithMutableDelay mySingleAlarm; private final SearchTextField myFilterTextField = new SearchTextField(false); @@ -73,15 +73,15 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myProject = debugSession.getProject(); myDebugSession = debugSession; - myDebugProcess = DebuggerManager.getInstance(myProject) + myDebugProcess = (DebugProcessImpl) DebuggerManager.getInstance(myProject) .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); myInstancesTracker = InstancesTracker.getInstance(myProject); - myInstancesTracker.addTrackerListener(new InstancesTrackerListener() { + InstancesTrackerListener instancesTrackerListener = new InstancesTrackerListener() { @Override public void classChanged(@NotNull String name, @NotNull TrackingType type) { ReferenceType ref = myTable.getClassByName(name); if (ref != null) { - ((DebuggerManagerThreadImpl) myDebugProcess.getManagerThread()) + myDebugProcess.getManagerThread() .schedule(new LowestPriorityCommand(getSuspendContext()) { @Override public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { @@ -96,7 +96,24 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myTrackedClasses.keySet().removeIf(referenceType -> name.equals(referenceType.name())); myTable.getRowSorter().allRowsChanged(); } - }, this); + }; + + myDebugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { + @Override + protected void action() throws Exception { + List constructorTrackerClasses = myInstancesTracker.getClassesByTrackingType(TrackingType.CREATION); + myInstancesTracker.addTrackerListener(instancesTrackerListener, ClassesFilteredView.this); + for (String className : constructorTrackerClasses) { + List classes = myDebugProcess.getVirtualMachineProxy().classesByName(className); + if (classes.isEmpty()) { + // TODO: track class preparation + } else { + // TODO: foreach add breakpoint + } + } + } + }); + MemoryViewManagerState memoryViewManagerState = MemoryViewManager.getInstance().getState(); myTable = new ClassesTable(myDebugSession, memoryViewManagerState.isShowWithDiffOnly, @@ -166,7 +183,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable if (myLastSuspendContext != null) { SwingUtilities.invokeLater(() -> { myTable.setBusy(true); - ((DebuggerManagerThreadImpl) myDebugProcess.getManagerThread()) + myDebugProcess.getManagerThread() .schedule(new MyUpdateClassesCommand(myLastSuspendContext)); }); } From 5b9a24adc7814de1071d041232f9e36909591666 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Mon, 5 Sep 2016 15:16:01 +0300 Subject: [PATCH 085/558] Start tracking new instances when class loaded instead of first breakpoint after loading --- component/InstancesTracker.java | 6 ++++ tracking/ClassPreparedListener.java | 44 +++++++++++++++++++++++ tracking/ConstructorInstancesTracker.java | 8 +---- view/ClassesFilteredView.java | 36 ++++++++++--------- 4 files changed, 71 insertions(+), 23 deletions(-) create mode 100644 tracking/ClassPreparedListener.java diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java index 13e1d5705883..74c725a98815 100644 --- a/component/InstancesTracker.java +++ b/component/InstancesTracker.java @@ -4,6 +4,7 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.components.*; import com.intellij.openapi.project.Project; import com.intellij.util.EventDispatcher; +import com.intellij.util.containers.HashMap; import com.intellij.util.xmlb.annotations.AbstractCollection; import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; @@ -40,6 +41,11 @@ public class InstancesTracker extends AbstractProjectComponent return myState.classes.getOrDefault(className, null); } + @NotNull + public Map getTrackedClasses() { + return new HashMap<>(myState.classes); + } + @NotNull public List getClassesByTrackingType(@NotNull TrackingType type) { return myState.classes.entrySet().stream() diff --git a/tracking/ClassPreparedListener.java b/tracking/ClassPreparedListener.java new file mode 100644 index 000000000000..f102d4905e30 --- /dev/null +++ b/tracking/ClassPreparedListener.java @@ -0,0 +1,44 @@ +package org.jetbrains.debugger.memory.tracking; + +import com.intellij.debugger.DebuggerManager; +import com.intellij.debugger.engine.DebugProcess; +import com.intellij.debugger.engine.DebugProcessImpl; +import com.intellij.debugger.requests.ClassPrepareRequestor; +import com.intellij.openapi.project.Project; +import com.intellij.xdebugger.XDebugSession; +import com.sun.jdi.ReferenceType; +import com.sun.jdi.request.ClassPrepareRequest; +import org.jetbrains.annotations.NotNull; + +public abstract class ClassPreparedListener { + private final XDebugSession myDebugSession; + private final Project myProject; + + protected ClassPreparedListener(@NotNull String className, + @NotNull XDebugSession debugSession) { + myProject = debugSession.getProject(); + myDebugSession = debugSession; + DebugProcessImpl debugProcess = getDebugProcess(); + ClassPrepareRequestor request = new MyClassPreparedRequest(); + ClassPrepareRequest classPrepareRequest = debugProcess.getRequestsManager() + .createClassPrepareRequest(request, className); + if (classPrepareRequest != null) { + classPrepareRequest.enable(); + } + } + + public abstract void onClassPrepared(@NotNull ReferenceType referenceType); + + private DebugProcessImpl getDebugProcess() { + return (DebugProcessImpl) DebuggerManager.getInstance(myProject) + .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); + } + + private final class MyClassPreparedRequest implements ClassPrepareRequestor { + @Override + public void processClassPrepare(DebugProcess debuggerProcess, ReferenceType referenceType) { + getDebugProcess().getRequestsManager().deleteRequest(this); + onClassPrepared(referenceType); + } + } +} diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index 777936f8114e..123e7db18a9d 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -1,7 +1,6 @@ package org.jetbrains.debugger.memory.tracking; import com.intellij.debugger.DebuggerManager; -import com.intellij.debugger.engine.DebugProcess; import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.evaluation.EvaluateException; @@ -60,7 +59,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp new LineBreakpointState<>()); MyConstructorBreakpoints breakpoint = new MyConstructorBreakpoints(project, bpn); - myDebugProcess.getRequestsManager().callbackOnPrepareClasses(breakpoint, myReference.name()); + breakpoint.createRequestForPreparedClass(myDebugProcess, myReference); Disposer.register(this, breakpoint); } @@ -97,11 +96,6 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp super(project, xBreakpoint); } - @Override - public void processClassPrepare(DebugProcess debugProcess, ReferenceType classType) { - createRequestForPreparedClass(myDebugProcess, classType); - } - @Override protected void createRequestForPreparedClass(DebugProcessImpl debugProcess, ReferenceType classType) { for (Method cons : classType.methodsByName("")) { diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index a492ab0d54e9..3f6937ed15cd 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -27,10 +27,7 @@ import org.jetbrains.debugger.memory.component.MemoryViewManager; import org.jetbrains.debugger.memory.component.MemoryViewManagerState; import org.jetbrains.debugger.memory.event.InstancesTrackerListener; import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; -import org.jetbrains.debugger.memory.tracking.ConstructorInstancesTracker; -import org.jetbrains.debugger.memory.tracking.InstanceTrackingStrategy; -import org.jetbrains.debugger.memory.tracking.TrackerForNewInstances; -import org.jetbrains.debugger.memory.tracking.TrackingType; +import org.jetbrains.debugger.memory.tracking.*; import org.jetbrains.debugger.memory.utils.AndroidUtil; import org.jetbrains.debugger.memory.utils.KeyboardUtils; import org.jetbrains.debugger.memory.utils.LowestPriorityCommand; @@ -99,18 +96,32 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable }; myDebugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { + @Override + public Priority getPriority() { + return Priority.LOWEST; + } + @Override protected void action() throws Exception { - List constructorTrackerClasses = myInstancesTracker.getClassesByTrackingType(TrackingType.CREATION); - myInstancesTracker.addTrackerListener(instancesTrackerListener, ClassesFilteredView.this); - for (String className : constructorTrackerClasses) { + for(Map.Entry entry : myInstancesTracker.getTrackedClasses().entrySet()) { + TrackingType type = entry.getValue(); + String className = entry.getKey(); List classes = myDebugProcess.getVirtualMachineProxy().classesByName(className); if (classes.isEmpty()) { - // TODO: track class preparation + new ClassPreparedListener(className, myDebugSession) { + @Override + public void onClassPrepared(@NotNull ReferenceType referenceType) { + trackClass(referenceType, type, getSuspendContext()); + } + }; } else { - // TODO: foreach add breakpoint + for(ReferenceType ref : classes) { + trackClass(ref, type, getSuspendContext()); + } } } + + myInstancesTracker.addTrackerListener(instancesTrackerListener, ClassesFilteredView.this); } }); @@ -294,13 +305,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable entry.getValue().update(suspendContext, entry.getKey().instances(0)); } - for (ReferenceType ref : classes) { - TrackingType type = myInstancesTracker.getTrackingType(ref.name()); - if (type != null && !myTrackedClasses.containsKey(ref) && !myConstructorTrackedClasses.containsKey(ref)) { - trackClass(ref, type, suspendContext); - } - } - if (classes.isEmpty()) { return; } From 3b4a97b8b398ba0472c357dab27391e74113ba6e Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Mon, 5 Sep 2016 22:55:31 +0300 Subject: [PATCH 086/558] Support hashCode based tracking for arrays too --- tracking/HashBasedTracking.java | 74 +++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 18 deletions(-) diff --git a/tracking/HashBasedTracking.java b/tracking/HashBasedTracking.java index 856ee5cfe4f9..80723075b04e 100644 --- a/tracking/HashBasedTracking.java +++ b/tracking/HashBasedTracking.java @@ -2,15 +2,20 @@ package org.jetbrains.debugger.memory.tracking; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.jdi.ThreadReferenceProxy; +import com.intellij.openapi.diagnostic.Logger; import com.sun.jdi.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; class HashBasedTracking extends InstanceTrackingStrategy { + private static final Logger LOG = Logger.getInstance(HashBasedTracking.class); + private final ReferenceType myClassType; + private MyHashCodeMethodWrapper myHashCodeWrapper; private Set myHashesSet; private MyState myState = MyState.WAIT_HASHES; @@ -18,7 +23,7 @@ class HashBasedTracking extends InstanceTrackingStrategy { @NotNull ReferenceType classType, @NotNull List initialInstances) { myClassType = classType; - if(suspendContext != null) { + if (suspendContext != null) { myHashesSet = toSetOfHashes(evalHashCodes(suspendContext, initialInstances)); myState = myState.next(); } @@ -31,9 +36,9 @@ class HashBasedTracking extends InstanceTrackingStrategy { Map> ref2hash = evalHashCodes(suspendContext, references); List newInstances = new ArrayList<>(); - for(ObjectReference ref : references) { + for (ObjectReference ref : references) { Optional hash = ref2hash.get(ref); - if(hash.isPresent() && !myHashesSet.contains(hash.get())) { + if (hash.isPresent() && !myState.equals(MyState.WAIT_HASHES) && !myHashesSet.contains(hash.get())) { newInstances.add(ref); } } @@ -51,33 +56,37 @@ class HashBasedTracking extends InstanceTrackingStrategy { @NotNull private Map> evalHashCodes(@NotNull SuspendContextImpl suspendContext, @NotNull List references) { - Method hashCodeMethod = getHashCodeMethod(myClassType); + MyHashCodeMethodWrapper hashCodeCaller = getHashCodeMethod(myClassType); Map> result = new HashMap<>(); ThreadReferenceProxy threadProxy = suspendContext.getThread(); ThreadReference thread = threadProxy != null ? threadProxy.getThreadReference() : null; - if(thread == null || hashCodeMethod == null) { + if (thread == null || hashCodeCaller == null) { + LOG.warn(thread == null ? "Thread is null" : "hashCode not found"); return result; } + long start = System.nanoTime(); for (ObjectReference ref : references) { Optional hash = Optional.empty(); try { - if(!ref.isCollected()) { + if (!ref.isCollected()) { ref.disableCollection(); - if(!ref.isCollected()) { - Value value = ref.invokeMethod(thread, hashCodeMethod, Collections.emptyList(), - ObjectReference.INVOKE_NONVIRTUAL); - hash = Optional.of(((IntegerValue)value).value()); + if (!ref.isCollected()) { + hash = hashCodeCaller.eval(ref, suspendContext, thread); } ref.enableCollection(); } } catch (InvalidTypeException | ClassNotLoadedException | - IncompatibleThreadStateException | InvocationException ignored) { + IncompatibleThreadStateException | InvocationException e) { + LOG.warn("Hash code evaluation failed", e); } result.put(ref, hash); } + long duration = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); + LOG.info(String.format("Hash code evaluation: %d ms. Count = %d", duration, references.size())); + return result; } @@ -89,15 +98,38 @@ class HashBasedTracking extends InstanceTrackingStrategy { } @Nullable - private Method getHashCodeMethod(@Nullable ReferenceType referenceType) { - if (referenceType == null) { - return null; + private MyHashCodeMethodWrapper getHashCodeMethod(@NotNull ReferenceType referenceType) { + if (myHashCodeWrapper != null) { + return myHashCodeWrapper; } - return referenceType.methodsByName("hashCode").stream() - .filter(method -> method.argumentTypeNames().isEmpty()) - .findFirst() - .orElseGet(null); + final List referenceTypes = referenceType.virtualMachine().classesByName("java.util.Objects"); + if (!referenceTypes.isEmpty()) { + ClassType objectsClass = (ClassType) referenceTypes.get(0); + Method hashCodeFromObjects = objectsClass.methodsByName("hashCode").stream() + .filter(method -> method.argumentTypeNames().size() == 1 + && "java.lang.Object".equals(method.argumentTypeNames().get(0))) + .findFirst() + .orElse(null); + + myHashCodeWrapper = (ref, suspendContext, thread) -> { + final Value value = objectsClass.invokeMethod(thread, hashCodeFromObjects, + Collections.singletonList(ref), ClassType.INVOKE_SINGLE_THREADED); + return Optional.of(((IntegerValue) value).value()); + }; + } else { + final Method hashCode = referenceType.methodsByName("hashCode").stream() + .filter(method -> method.argumentTypeNames().isEmpty()) + .findFirst() + .orElse(null); + + myHashCodeWrapper = hashCode == null ? null : (ref, suspendContext, thread) -> { + final Value value = ref.invokeMethod(thread, hashCode, Collections.emptyList(), ClassType.INVOKE_SINGLE_THREADED); + return Optional.of(((IntegerValue) value).value()); + }; + } + + return myHashCodeWrapper; } // WAIT_HASHES -> WAIT_UPDATE -> READY @@ -121,4 +153,10 @@ class HashBasedTracking extends InstanceTrackingStrategy { public abstract MyState next(); } + + private interface MyHashCodeMethodWrapper { + Optional eval(@NotNull ObjectReference ref, @NotNull SuspendContextImpl suspendContext, + @NotNull ThreadReference thread) throws InvocationException, InvalidTypeException, + ClassNotLoadedException, IncompatibleThreadStateException; + } } From 2a2401046dd2185adce917ae0329a7f94ff3ff95 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 6 Sep 2016 13:17:56 +0300 Subject: [PATCH 087/558] Replace dialog window with popup window when "Jump To Allocation" action performed --- .../tracking/GetAllocationSourceAction.java | 4 +- component/CreationPositionTracker.java | 3 +- view/StackFramePopup.java | 90 +++++++++++++++++++ 3 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 view/StackFramePopup.java diff --git a/action/tracking/GetAllocationSourceAction.java b/action/tracking/GetAllocationSourceAction.java index 555d6309d3e5..e396d989ae6e 100644 --- a/action/tracking/GetAllocationSourceAction.java +++ b/action/tracking/GetAllocationSourceAction.java @@ -13,7 +13,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.action.DebuggerTreeAction; import org.jetbrains.debugger.memory.component.CreationPositionTracker; import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; -import org.jetbrains.debugger.memory.view.StackFrameSelector; +import org.jetbrains.debugger.memory.view.StackFramePopup; import java.util.List; @@ -32,7 +32,7 @@ public class GetAllocationSourceAction extends DebuggerTreeAction { if(session != null) { GlobalSearchScope searchScope = DebuggerManager.getInstance(project) .getDebugProcess(session.getDebugProcess().getProcessHandler()).getSearchScope(); - new StackFrameSelector(project, stack, searchScope).show(); + new StackFramePopup(project, stack, searchScope).show(); } } } diff --git a/component/CreationPositionTracker.java b/component/CreationPositionTracker.java index cb16fcd98a3a..6045d9089b41 100644 --- a/component/CreationPositionTracker.java +++ b/component/CreationPositionTracker.java @@ -33,7 +33,8 @@ public class CreationPositionTracker extends AbstractProjectComponent { return stack != null ? Collections.unmodifiableList(stack) : null; } - public void addStack(@NotNull XDebugSession session, @NotNull ObjectReference ref, @NotNull List stack) { + public void addStack(@NotNull XDebugSession session, @NotNull ObjectReference ref, + @NotNull List stack) { if (!mySession2Reference2Stack.containsKey(session)) { mySession2Reference2Stack.put(session, new ConcurrentHashMap<>()); session.addSessionListener(new XDebugSessionListener() { diff --git a/view/StackFramePopup.java b/view/StackFramePopup.java new file mode 100644 index 000000000000..8e517c469756 --- /dev/null +++ b/view/StackFramePopup.java @@ -0,0 +1,90 @@ +package org.jetbrains.debugger.memory.view; + +import com.intellij.debugger.engine.DebuggerUtils; +import com.intellij.execution.filters.OpenFileHyperlinkInfo; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.OpenFileDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.JBPopup; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.psi.PsiClass; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.ui.components.JBList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.debugger.memory.utils.KeyboardUtils; +import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; + +import javax.swing.*; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.util.List; + +public class StackFramePopup { + private final Project myProject; + private final List myStackFrame; + private final GlobalSearchScope myScope; + + public StackFramePopup(@NotNull Project project, + @NotNull List stack, + @NotNull GlobalSearchScope searchScope) { + myProject = project; + myStackFrame = stack; + myScope = searchScope; + } + + public void show() { + JBList list = createStackList(); + JBPopup popup = JBPopupFactory.getInstance().createListPopupBuilder(list) + .setTitle("Select stack frame") + .setAutoSelectIfEmpty(true) + .setResizable(false) + .createPopup(); + popup.showInFocusCenter(); + } + + private JBList createStackList() { + JBList frameList = new JBList(new AbstractListModel() { + @Override + public int getSize() { + return myStackFrame.size(); + } + + @Override + public Object getElementAt(int index) { + return myStackFrame.get(index); + } + }); + + frameList.addKeyListener(new KeyAdapter() { + @Override + public void keyReleased(KeyEvent e) { + if (KeyboardUtils.isEnterKey(e.getKeyCode())) { + navigateToSelectedFrame(frameList, true); + } + } + }); + + frameList.addListSelectionListener(e -> navigateToSelectedFrame(frameList, false)); + + return frameList; + } + + private void navigateToSelectedFrame(@NotNull JBList frameList, boolean focusEditor) { + StackFrameDescriptor frame = (StackFrameDescriptor) frameList.getSelectedValue(); + if (frame != null) { + PsiClass psiClass = DebuggerUtils.findClass(frame.path(), myProject, myScope); + if (psiClass != null) { + ApplicationManager.getApplication().runReadAction(() -> { + OpenFileHyperlinkInfo info = + new OpenFileHyperlinkInfo(myProject, psiClass.getContainingFile().getVirtualFile(), + frame.line() - 1); + OpenFileDescriptor descriptor = info.getDescriptor(); + if (descriptor != null) { + FileEditorManager.getInstance(myProject).openTextEditor(descriptor, focusEditor); + } + }); + } + } + } +} From b077d49315746e2534c14efca854ed7c32049943 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 6 Sep 2016 13:55:29 +0300 Subject: [PATCH 088/558] New renderer for list with stack frame --- tracking/ConstructorInstancesTracker.java | 5 ++-- utils/StackFrameDescriptor.java | 29 ++++++++++++++++------- view/StackFramePopup.java | 11 +++++++++ 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index 123e7db18a9d..1fd7fa93dc64 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -135,8 +135,9 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp List stackFrameDescriptors = stack.stream().map(frame -> { try { Location loc = frame.location(); - return new StackFrameDescriptor(loc.declaringType().name(), - frame.getIndexFromBottom(), loc.lineNumber()); + String typeName = loc.declaringType().name(); + String methodName = loc.method().name(); + return new StackFrameDescriptor(typeName, methodName, loc.lineNumber()); } catch (EvaluateException e) { return null; } diff --git a/utils/StackFrameDescriptor.java b/utils/StackFrameDescriptor.java index 866edf73161c..7d1ca9d6ebef 100644 --- a/utils/StackFrameDescriptor.java +++ b/utils/StackFrameDescriptor.java @@ -1,15 +1,16 @@ package org.jetbrains.debugger.memory.utils; +import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; public class StackFrameDescriptor { private final String myFilePath; - private int myFrameLevel; - private int myLineNumber; + private final String myMethodName; + private final int myLineNumber; - public StackFrameDescriptor(@NotNull String path, int frameLevel, int line) { + public StackFrameDescriptor(@NotNull String path, @NotNull String methodName, int line) { myFilePath = path.replace('\\', '.'); - myFrameLevel = frameLevel; + myMethodName = methodName; myLineNumber = line; } @@ -18,12 +19,22 @@ public class StackFrameDescriptor { return myFilePath; } + @NotNull + public String methodName() { + return myMethodName; + } + + @NotNull + public String className() { + return StringUtil.getShortName(myFilePath); + } + + @NotNull + public String packageName() { + return StringUtil.getPackageName(myFilePath); + } + public int line() { return myLineNumber; } - - @Override - public String toString() { - return String.format("%d. %s:%d", myFrameLevel, myFilePath, myLineNumber); - } } diff --git a/view/StackFramePopup.java b/view/StackFramePopup.java index 8e517c469756..45f9d6df09da 100644 --- a/view/StackFramePopup.java +++ b/view/StackFramePopup.java @@ -10,6 +10,8 @@ import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.psi.PsiClass; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.ui.ColoredListCellRenderer; +import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBList; import org.jetbrains.annotations.NotNull; import org.jetbrains.debugger.memory.utils.KeyboardUtils; @@ -56,6 +58,15 @@ public class StackFramePopup { } }); + frameList.setCellRenderer(new ColoredListCellRenderer() { + @Override + protected void customizeCellRenderer(@NotNull JList list, + StackFrameDescriptor value, int index, boolean isSelected, boolean hasFocus) { + append(String.format("%s:%d, %s", value.methodName(), value.line(), value.className())); + append(String.format(" (%s)", value.packageName()), SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES); + } + }); + frameList.addKeyListener(new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { From e672175654d7356040946596220779adb10e398c Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 6 Sep 2016 16:20:49 +0300 Subject: [PATCH 089/558] Remove redundant code. Not show empty parens if package is empty in stack popup --- component/InstancesTracker.java | 10 ---- view/StackFramePopup.java | 6 ++- view/StackFrameSelector.java | 85 --------------------------------- 3 files changed, 5 insertions(+), 96 deletions(-) delete mode 100644 view/StackFrameSelector.java diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java index 74c725a98815..28c416bec251 100644 --- a/component/InstancesTracker.java +++ b/component/InstancesTracker.java @@ -12,10 +12,8 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.event.InstancesTrackerListener; import org.jetbrains.debugger.memory.tracking.TrackingType; -import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; @State(name = "InstancesTracker", storages = @Storage(StoragePathMacros.WORKSPACE_FILE)) public class InstancesTracker extends AbstractProjectComponent @@ -46,14 +44,6 @@ public class InstancesTracker extends AbstractProjectComponent return new HashMap<>(myState.classes); } - @NotNull - public List getClassesByTrackingType(@NotNull TrackingType type) { - return myState.classes.entrySet().stream() - .filter(entry -> entry.getValue().equals(type)) - .map(Map.Entry::getKey) - .collect(Collectors.toList()); - } - public void add(@NotNull ReferenceType ref, @NotNull TrackingType type) { String name = ref.name(); if (type.equals(myState.classes.getOrDefault(name, null))) { diff --git a/view/StackFramePopup.java b/view/StackFramePopup.java index 45f9d6df09da..196efcd47fd9 100644 --- a/view/StackFramePopup.java +++ b/view/StackFramePopup.java @@ -13,6 +13,7 @@ import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.ColoredListCellRenderer; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBList; +import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.debugger.memory.utils.KeyboardUtils; import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; @@ -63,7 +64,10 @@ public class StackFramePopup { protected void customizeCellRenderer(@NotNull JList list, StackFrameDescriptor value, int index, boolean isSelected, boolean hasFocus) { append(String.format("%s:%d, %s", value.methodName(), value.line(), value.className())); - append(String.format(" (%s)", value.packageName()), SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES); + String packageName = value.packageName(); + if(!StringUtils.isEmpty(packageName)) { + append(String.format(" (%s)", value.packageName()), SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES); + } } }); diff --git a/view/StackFrameSelector.java b/view/StackFrameSelector.java deleted file mode 100644 index 52df1c30baeb..000000000000 --- a/view/StackFrameSelector.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.jetbrains.debugger.memory.view; - -import com.intellij.debugger.engine.DebuggerUtils; -import com.intellij.execution.filters.OpenFileHyperlinkInfo; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.psi.PsiClass; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.ui.DoubleClickListener; -import com.intellij.ui.components.JBList; -import com.intellij.ui.components.JBScrollPane; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; - -import javax.swing.*; -import java.awt.event.MouseEvent; -import java.util.List; - -public class StackFrameSelector extends DialogWrapper { - private final Project myProject; - private final List myStackFrame; - private final GlobalSearchScope mySearchScope; - - public StackFrameSelector(@NotNull Project project, - @NotNull List stack, - @NotNull GlobalSearchScope searchScope) { - super(project, false); - myProject = project; - myStackFrame = stack; - mySearchScope = searchScope; - setModal(false); - init(); - } - - @Nullable - @Override - protected JComponent createNorthPanel() { - return null; - } - - @Nullable - @Override - protected JComponent createSouthPanel() { - return null; - } - - @Nullable - @Override - protected JComponent createCenterPanel() { - JBList frameList = new JBList(new AbstractListModel() { - @Override - public int getSize() { - return myStackFrame.size(); - } - - @Override - public Object getElementAt(int index) { - return myStackFrame.get(index); - } - }); - - new DoubleClickListener() { - @Override - protected boolean onDoubleClick(MouseEvent event) { - StackFrameDescriptor selectedValue = (StackFrameDescriptor) frameList.getSelectedValue(); - if (selectedValue != null) { - PsiClass psiClass = DebuggerUtils.findClass(selectedValue.path(), myProject, mySearchScope); - if(psiClass != null) { - ApplicationManager.getApplication().runReadAction(() -> { - OpenFileHyperlinkInfo info = - new OpenFileHyperlinkInfo(myProject, - psiClass.getContainingFile().getVirtualFile(), selectedValue.line() - 1); - info.navigate(myProject); - }); - } - } - return false; - } - }.installOn(frameList); - - return new JBScrollPane(frameList); - } -} From 62e401700f50b136f3a52f63c86b21dc1d0cd4e4 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 6 Sep 2016 17:34:27 +0300 Subject: [PATCH 090/558] Show label "The application is running" by default in table with classes --- view/ClassesFilteredView.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 91094adce788..e6f1b09fde6a 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -53,7 +53,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private static final String EMPTY_TABLE_CONTENT_WHEN_RUNNED = "The application is running"; private static final String EMPTY_TABLE_CONTENT_WHEN_SUSPENDED = "Nothing to show"; - private final Project myProject; private final XDebugSession myDebugSession; private final DebugProcessImpl myDebugProcess; @@ -132,6 +131,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myTable = new ClassesTable(myDebugSession, memoryViewManagerState.isShowWithDiffOnly, memoryViewManagerState.isShowWithInstancesOnly, this); + myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNED); Disposer.register(this, myTable); myTable.addKeyListener(new KeyAdapter() { @@ -195,7 +195,10 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void sessionResumed() { myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::obsolete); - SwingUtilities.invokeLater(myTable::hideContent); + SwingUtilities.invokeLater(() -> { + myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNED); + myTable.hideContent(); + }); mySingleAlarm.cancelAllRequests(); } From 8f89ed371a4ccd443f835819e1e11d72e5f04c31 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 6 Sep 2016 18:54:45 +0300 Subject: [PATCH 091/558] Fix bug when breakpoint added into parents' constructors --- tracking/ConstructorInstancesTracker.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index 1fd7fa93dc64..923bb64a926a 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -98,11 +98,11 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp @Override protected void createRequestForPreparedClass(DebugProcessImpl debugProcess, ReferenceType classType) { - for (Method cons : classType.methodsByName("")) { + classType.methods().stream().filter(Method::isConstructor).forEach(cons -> { Location loc = cons.location(); BreakpointRequest breakpointRequest = debugProcess.getRequestsManager().createBreakpointRequest(this, loc); breakpointRequest.enable(); - } + }); } @Override From 747fc289533dfe6e06a51dd3c8d389efbebeec5b Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 6 Sep 2016 18:56:01 +0300 Subject: [PATCH 092/558] Fix bug when "Show New Instances" action is enabled, but tracker is not ready --- action/ShowNewInstancesAction.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/action/ShowNewInstancesAction.java b/action/ShowNewInstancesAction.java index 7261022a63ca..08ea5b76b6e3 100644 --- a/action/ShowNewInstancesAction.java +++ b/action/ShowNewInstancesAction.java @@ -1,10 +1,8 @@ package org.jetbrains.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; -import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.utils.InstancesProvider; import org.jetbrains.debugger.memory.view.ClassesTable; import org.jetbrains.debugger.memory.view.InstancesWindow; @@ -12,10 +10,10 @@ import org.jetbrains.debugger.memory.view.InstancesWindow; public class ShowNewInstancesAction extends ClassesActionBase { @Override protected boolean isEnabled(AnActionEvent e) { - Project project = e.getProject(); + XDebugSession session = getDebugSession(e); ReferenceType selectedClass = getSelectedClass(e); - return super.isEnabled(e) && selectedClass != null && - project != null && InstancesTracker.getInstance(project).isTracked(selectedClass.name()); + InstancesProvider provider = e.getData(ClassesTable.NEW_INSTANCES_PROVIDER_KEY); + return super.isEnabled(e) && session != null && selectedClass != null && provider != null; } @Override From 8db6df92fc0d0a2ec1991edc0466cd56f012bf07 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 6 Sep 2016 19:01:52 +0300 Subject: [PATCH 093/558] Fix race when item in popup with stack was closed earlier then navigation to source --- view/StackFramePopup.java | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/view/StackFramePopup.java b/view/StackFramePopup.java index 196efcd47fd9..64a70330e53d 100644 --- a/view/StackFramePopup.java +++ b/view/StackFramePopup.java @@ -15,12 +15,9 @@ import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBList; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.utils.KeyboardUtils; import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; import javax.swing.*; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; import java.util.List; public class StackFramePopup { @@ -38,11 +35,14 @@ public class StackFramePopup { public void show() { JBList list = createStackList(); + JBPopup popup = JBPopupFactory.getInstance().createListPopupBuilder(list) .setTitle("Select stack frame") .setAutoSelectIfEmpty(true) .setResizable(false) + .setItemChoosenCallback(() -> navigateToSelectedFrame(list, true)) .createPopup(); + popup.showInFocusCenter(); } @@ -65,21 +65,12 @@ public class StackFramePopup { StackFrameDescriptor value, int index, boolean isSelected, boolean hasFocus) { append(String.format("%s:%d, %s", value.methodName(), value.line(), value.className())); String packageName = value.packageName(); - if(!StringUtils.isEmpty(packageName)) { + if (!StringUtils.isEmpty(packageName)) { append(String.format(" (%s)", value.packageName()), SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES); } } }); - frameList.addKeyListener(new KeyAdapter() { - @Override - public void keyReleased(KeyEvent e) { - if (KeyboardUtils.isEnterKey(e.getKeyCode())) { - navigateToSelectedFrame(frameList, true); - } - } - }); - frameList.addListSelectionListener(e -> navigateToSelectedFrame(frameList, false)); return frameList; From 8fd5f061727c24aeeafd2ddaf72c646d197f2e2d Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 16 Sep 2016 16:41:43 +0300 Subject: [PATCH 094/558] Disable hash, id, retain trackings --- action/tracking/HashBasedTrackingAction.java | 1 - .../tracking/TrackInstanceCreationAction.java | 2 +- view/ClassesFilteredView.java | 40 ++++++------------- 3 files changed, 13 insertions(+), 30 deletions(-) diff --git a/action/tracking/HashBasedTrackingAction.java b/action/tracking/HashBasedTrackingAction.java index 8a8c29c2444d..c9aa65fa102b 100644 --- a/action/tracking/HashBasedTrackingAction.java +++ b/action/tracking/HashBasedTrackingAction.java @@ -10,4 +10,3 @@ public class HashBasedTrackingAction extends InstancesTrackingActionBase { return TrackingType.HASH; } } - diff --git a/action/tracking/TrackInstanceCreationAction.java b/action/tracking/TrackInstanceCreationAction.java index e8cfc3f9c9d8..810884643cc9 100644 --- a/action/tracking/TrackInstanceCreationAction.java +++ b/action/tracking/TrackInstanceCreationAction.java @@ -3,7 +3,7 @@ package org.jetbrains.debugger.memory.action.tracking; import org.jetbrains.annotations.NotNull; import org.jetbrains.debugger.memory.tracking.TrackingType; -public class TrackInstanceCreationAction extends InstancesTrackingActionBase{ +public class TrackInstanceCreationAction extends InstancesTrackingActionBase { @NotNull @Override protected TrackingType getType() { diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index e6f1b09fde6a..ac883146a947 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -17,7 +17,6 @@ import com.intellij.util.SmartList; import com.intellij.util.ui.components.BorderLayoutPanel; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; -import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import com.sun.jdi.VirtualMachine; import org.jetbrains.annotations.NotNull; @@ -27,12 +26,16 @@ import org.jetbrains.debugger.memory.component.MemoryViewManager; import org.jetbrains.debugger.memory.component.MemoryViewManagerState; import org.jetbrains.debugger.memory.event.InstancesTrackerListener; import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; -import org.jetbrains.debugger.memory.tracking.*; +import org.jetbrains.debugger.memory.tracking.ClassPreparedListener; +import org.jetbrains.debugger.memory.tracking.ConstructorInstancesTracker; +import org.jetbrains.debugger.memory.tracking.TrackerForNewInstances; +import org.jetbrains.debugger.memory.tracking.TrackingType; import org.jetbrains.debugger.memory.utils.AndroidUtil; import org.jetbrains.debugger.memory.utils.KeyboardUtils; import org.jetbrains.debugger.memory.utils.LowestPriorityCommand; import org.jetbrains.debugger.memory.utils.SingleAlarmWithMutableDelay; +import javax.swing.FocusManager; import javax.swing.*; import javax.swing.event.DocumentEvent; import java.awt.*; @@ -61,7 +64,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private final SearchTextField myFilterTextField = new FilterTextField(); private final ClassesTable myTable; private final InstancesTracker myInstancesTracker; - private final Map myTrackedClasses = new ConcurrentHashMap<>(); private final Map myConstructorTrackedClasses = new ConcurrentHashMap<>(); private volatile SuspendContextImpl myLastSuspendContext; @@ -84,7 +86,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable .schedule(new LowestPriorityCommand(getSuspendContext()) { @Override public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { - trackClass(ref, type, suspendContext); + trackClass(ref, type); } }); } @@ -92,7 +94,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void classRemoved(@NotNull String name) { - myTrackedClasses.keySet().removeIf(referenceType -> name.equals(referenceType.name())); myTable.getRowSorter().allRowsChanged(); } }; @@ -105,7 +106,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override protected void action() throws Exception { - for(Map.Entry entry : myInstancesTracker.getTrackedClasses().entrySet()) { + for (Map.Entry entry : myInstancesTracker.getTrackedClasses().entrySet()) { TrackingType type = entry.getValue(); String className = entry.getKey(); List classes = myDebugProcess.getVirtualMachineProxy().classesByName(className); @@ -113,12 +114,12 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable new ClassPreparedListener(className, myDebugSession) { @Override public void onClassPrepared(@NotNull ReferenceType referenceType) { - trackClass(referenceType, type, getSuspendContext()); + trackClass(referenceType, type); } }; } else { - for(ReferenceType ref : classes) { - trackClass(ref, type, getSuspendContext()); + for (ReferenceType ref : classes) { + trackClass(ref, type); } } } @@ -245,10 +246,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Nullable TrackerForNewInstances getStrategy(@NotNull ReferenceType ref) { - if (myTrackedClasses.containsKey(ref)) { - return myTrackedClasses.get(ref); - } - return myConstructorTrackedClasses.getOrDefault(ref, null); } @@ -265,8 +262,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } private void trackClass(@NotNull ReferenceType ref, - @NotNull TrackingType type, - @Nullable SuspendContextImpl suspendContext) { + @NotNull TrackingType type) { LOG.assertTrue(DebuggerManager.getInstance(myProject).isDebuggerManagerThread()); if (type == TrackingType.CREATION) { ConstructorInstancesTracker old = myConstructorTrackedClasses.getOrDefault(ref, null); @@ -277,9 +273,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable ConstructorInstancesTracker tracker = new ConstructorInstancesTracker(ref, myDebugSession); Disposer.register(ClassesFilteredView.this, tracker); myConstructorTrackedClasses.put(ref, tracker); - } else { - List instances = ref.instances(0); - myTrackedClasses.put(ref, InstanceTrackingStrategy.create(ref, suspendContext, type, instances)); } } @@ -306,11 +299,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable boolean isTrackingActive(@NotNull ReferenceType ref) { TrackerForNewInstances strategy = myConstructorTrackedClasses.getOrDefault(ref, null); - if (strategy != null) { - return strategy.isReady(); - } - - strategy = myTrackedClasses.getOrDefault(ref, null); return strategy != null && strategy.isReady(); } @@ -329,10 +317,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { final List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); - for (Map.Entry entry : myTrackedClasses.entrySet()) { - entry.getValue().update(suspendContext, entry.getKey().instances(0)); - } - if (classes.isEmpty()) { return; } @@ -359,7 +343,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable LOG.info(String.format("Instances query time = %d ms. Count = %d", delay, batch.size())); } - if(isContextValid()) { + if (isContextValid()) { final long[] counts = chunks.size() == 1 ? chunks.get(0) : IntStream.range(0, chunks.size()).boxed() .flatMapToLong(integer -> Arrays.stream(chunks.get(integer))) .toArray(); From a74350f5428d0c2ca3d5a7388ea06b863764bcbb Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 16 Sep 2016 18:33:04 +0300 Subject: [PATCH 095/558] Add custom renderer for 'Diff' column --- view/ClassesTable.java | 101 +++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 54 deletions(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 7cf693f2c32d..00c40ed6bf8b 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -39,6 +39,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { public static final DataKey DEBUG_SESSION_KEY = DataKey.create("ClassesTable.DebugSession"); public static final DataKey NEW_INSTANCES_PROVIDER_KEY = DataKey.create("ClassesTable.NewInstances"); + private static final int CLASSES_COLUMN_PREFERRED_WIDTH = 250; private static final int COUNT_COLUMN_MIN_WIDTH = 80; private static final int COUNT_COLUMN_MAX_WIDTH = 100; @@ -90,8 +91,9 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { setShowGrid(false); setIntercellSpacing(new JBDimension(0, 0)); - setDefaultRenderer(ReferenceType.class, new MyClassTableCellRenderer()); - setDefaultRenderer(Long.class, new MyNumberTableCellRenderer()); + setDefaultRenderer(ReferenceType.class, new MyClassColumnRenderer()); + setDefaultRenderer(Long.class, new MyCountColumnRenderer()); + setDefaultRenderer(DiffValue.class, new MyDiffColumnRenderer()); TableRowSorter sorter = new TableRowSorter<>(myModel); sorter.setRowFilter(new RowFilter() { @@ -129,8 +131,8 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { @Nullable ReferenceType getClassByName(@NotNull String name) { - for(ReferenceType ref : myElems) { - if(name.equals(ref.name())) { + for (ReferenceType ref : myElems) { + if (name.equals(ref.name())) { return ref; } } @@ -147,7 +149,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { myFilteringPattern = pattern; myMatcher = NameUtil.buildMatcher("*" + pattern).build(); getRowSorter().allRowsChanged(); - if(getSelectedClass() == null && getRowCount() > 0) { + if (getSelectedClass() == null && getRowCount() > 0) { getSelectionModel().setSelectionInterval(0, 0); } } @@ -255,10 +257,10 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { return myCounts.getOrDefault(myElems.get(ix), myUnknownValue).myCurrentCount; } }, - new AbstractTableColumnDescriptor("Diff", Long.class) { + new AbstractTableColumnDescriptor("Diff", DiffValue.class) { @Override public Object getValue(int ix) { - return myCounts.getOrDefault(myElems.get(ix), myUnknownValue).diff(); + return myCounts.getOrDefault(myElems.get(ix), myUnknownValue); } } }); @@ -319,7 +321,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { } } - private static class DiffValue { + private static class DiffValue implements Comparable { private long myOldCount; private long myCurrentCount; @@ -347,51 +349,16 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { return myCurrentCount - myOldCount; } + @Override + public int compareTo(@NotNull DiffValue o) { + return Long.compare(diff(), o.diff()); + } } - private class MyNumberTableCellRenderer extends ColoredTableCellRenderer { - + private abstract class MyTableCellRenderer extends ColoredTableCellRenderer { @Override - protected void customizeCellRenderer(JTable table, @Nullable Object value, - boolean isSelected, boolean hasFocus, int row, int column) { - if (value == null) { - return; - } - - TrackingType trackingType = getTrackingType(row); - if (!isSelected && trackingType != null) { - JBColor color = myParent.isTrackingActive(myElems.get(convertRowIndexToModel(row))) - ? trackingType.color() - : TrackingType.inactiveColor(); - setBackground(UIUtil.toAlpha(color, 20)); - } - - long val = (long) value; - setTextAlign(SwingConstants.RIGHT); - if (column == DiffViewTableModel.COUNT_COLUMN_INDEX) { - renderCount(val); - } else { - renderDiff(val); - } - } - - private void renderDiff(long diff) { - append(String.format("%s%d", diff > 0 ? "+" : "", diff)); - } - - private void renderCount(long count) { - append(String.valueOf(count)); - - } - - } - - private class MyClassTableCellRenderer extends ColoredTableCellRenderer { - - @Override - protected void customizeCellRenderer(JTable table, @Nullable Object value, - boolean isSelected, boolean hasFocus, int row, int column) { - String presentation = value == null ? "null" : ((ReferenceType) value).name(); + protected void customizeCellRenderer(JTable table, @Nullable Object value, boolean isSelected, + boolean hasFocus, int row, int column) { TrackingType trackingType = getTrackingType(row); if (trackingType != null && !isSelected) { JBColor color = myParent.isTrackingActive(myElems.get(convertRowIndexToModel(row))) @@ -400,8 +367,21 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { setBackground(UIUtil.toAlpha(color, 20)); } + if (value != null) { + addText(table, value, isSelected, hasFocus, row, column); + } + } + + protected abstract void addText(JTable table, @NotNull Object value, boolean isSelected, + boolean hasFocus, int row, int column); + } + + private class MyClassColumnRenderer extends MyTableCellRenderer { + @Override + protected void addText(JTable table, @NotNull Object value, boolean isSelected, boolean hasFocus, int row, int column) { + String presentation = ((ReferenceType) value).name(); append(" "); - if (value != null && isSelected) { + if (isSelected) { FList textRanges = myMatcher.matchingFragments(presentation); if (textRanges != null) { SimpleTextAttributes attributes = new SimpleTextAttributes(getBackground(), getForeground(), null, @@ -412,10 +392,23 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { } else { append(String.format("%s", presentation), SimpleTextAttributes.REGULAR_ATTRIBUTES); } + } + } - if (trackingType != null) { - append(String.format(" (%s)", trackingType.description())); - } + private class MyCountColumnRenderer extends MyTableCellRenderer { + @Override + protected void addText(JTable table, @NotNull Object value, boolean isSelected, boolean hasFocus, int row, int column) { + setTextAlign(SwingConstants.RIGHT); + append(value.toString()); + } + } + + private class MyDiffColumnRenderer extends MyTableCellRenderer { + @Override + protected void addText(JTable table, @NotNull Object value, boolean isSelected, boolean hasFocus, int row, int column) { + setTextAlign(SwingConstants.RIGHT); + long diff = ((DiffValue) value).diff(); + append(String.format("%s%d", diff > 0 ? "+" : "", diff)); } } } From e1b3f60d6b369785c901717212e0011ced24cc8a Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Sat, 17 Sep 2016 18:35:28 +0300 Subject: [PATCH 096/558] Open new instances on 'diff' column click --- view/ClassesTable.java | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 00c40ed6bf8b..6b6d58638915 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -31,7 +31,10 @@ import org.jetbrains.debugger.memory.utils.InstancesProvider; import javax.swing.*; import javax.swing.table.TableColumn; import javax.swing.table.TableRowSorter; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.util.*; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; public class ClassesTable extends JBTable implements DataProvider, Disposable { @@ -95,6 +98,20 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { setDefaultRenderer(Long.class, new MyCountColumnRenderer()); setDefaultRenderer(DiffValue.class, new MyDiffColumnRenderer()); + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (columnAtPoint(e.getPoint()) != DiffViewTableModel.DIFF_COLUMN_INDEX) { + return; + } + + ReferenceType ref = myElems.get(convertRowIndexToModel(rowAtPoint(e.getPoint()))); + TrackerForNewInstances strategy = myParent.getStrategy(ref); + if (strategy != null) { + new InstancesWindow(myDebugSession, limit -> strategy.getNewInstances(), ref.name()).show(); + } + } + }); TableRowSorter sorter = new TableRowSorter<>(myModel); sorter.setRowFilter(new RowFilter() { @Override @@ -378,7 +395,8 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private class MyClassColumnRenderer extends MyTableCellRenderer { @Override - protected void addText(JTable table, @NotNull Object value, boolean isSelected, boolean hasFocus, int row, int column) { + protected void addText(JTable table, @NotNull Object value, boolean isSelected, + boolean hasFocus, int row, int column) { String presentation = ((ReferenceType) value).name(); append(" "); if (isSelected) { @@ -397,18 +415,31 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private class MyCountColumnRenderer extends MyTableCellRenderer { @Override - protected void addText(JTable table, @NotNull Object value, boolean isSelected, boolean hasFocus, int row, int column) { + protected void addText(JTable table, @NotNull Object value, boolean isSelected, + boolean hasFocus, int row, int column) { setTextAlign(SwingConstants.RIGHT); append(value.toString()); } } private class MyDiffColumnRenderer extends MyTableCellRenderer { + private final SimpleTextAttributes myClickableCellAttributes = new SimpleTextAttributes(SimpleTextAttributes.STYLE_UNDERLINE, + JBColor.BLUE); + @Override protected void addText(JTable table, @NotNull Object value, boolean isSelected, boolean hasFocus, int row, int column) { - setTextAlign(SwingConstants.RIGHT); long diff = ((DiffValue) value).diff(); - append(String.format("%s%d", diff > 0 ? "+" : "", diff)); + setTextAlign(SwingConstants.RIGHT); + + ReferenceType ref = myElems.get(convertRowIndexToModel(row)); + TrackerForNewInstances strategy = myParent.getStrategy(ref); + SimpleTextAttributes attributes = SimpleTextAttributes.REGULAR_ATTRIBUTES; + if (strategy != null && strategy.isReady()) { + attributes = myClickableCellAttributes; + } + + String text = String.format("%s%d", diff > 0 ? "+" : "", diff); + append(text, attributes); } } } From 9660c9461aa3df56af24b1a3b777fc159034d021 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 21 Sep 2016 18:09:27 +0300 Subject: [PATCH 097/558] Show count of new/deleted instances in diff column for tracked instances --- tracking/ConstructorInstancesTracker.java | 8 +++++++- tracking/HashBasedTracking.java | 9 ++++++++- tracking/IdentityBasedTracking.java | 7 +++++++ tracking/RetainReferencesTracking.java | 6 +++++- tracking/TrackerForNewInstances.java | 3 +++ view/ClassesTable.java | 24 ++++++++++++++++------- 6 files changed, 47 insertions(+), 10 deletions(-) diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index 923bb64a926a..4eade041d9c4 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -38,11 +38,12 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp private final XDebugSession myDebugSession; private final CreationPositionTracker myPositionTracker; private final DebugProcessImpl myDebugProcess; + @Nullable private HashSet myNewObjects = null; + @NotNull private HashSet myTrackedObjects = new HashSet<>(); - public ConstructorInstancesTracker(@NotNull ReferenceType ref, @NotNull XDebugSession debugSession) { myReference = ref; @@ -80,6 +81,11 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp return myNewObjects == null ? Collections.EMPTY_LIST : new ArrayList<>(myNewObjects); } + @Override + public int getCount() { + return myNewObjects == null ? 0 : myNewObjects.size(); + } + @Override public boolean isReady() { return myNewObjects != null; diff --git a/tracking/HashBasedTracking.java b/tracking/HashBasedTracking.java index 80723075b04e..110137d11191 100644 --- a/tracking/HashBasedTracking.java +++ b/tracking/HashBasedTracking.java @@ -15,10 +15,11 @@ class HashBasedTracking extends InstanceTrackingStrategy { private static final Logger LOG = Logger.getInstance(HashBasedTracking.class); private final ReferenceType myClassType; + private MyHashCodeMethodWrapper myHashCodeWrapper; private Set myHashesSet; private MyState myState = MyState.WAIT_HASHES; - + private int myCount = 0; HashBasedTracking(@Nullable SuspendContextImpl suspendContext, @NotNull ReferenceType classType, @NotNull List initialInstances) { @@ -45,9 +46,15 @@ class HashBasedTracking extends InstanceTrackingStrategy { myHashesSet = toSetOfHashes(ref2hash); myState = myState.next(); + myCount = newInstances.size(); return newInstances; } + @Override + public int getCount() { + return myCount; + } + @Override public boolean isReady() { return myState == MyState.READY; diff --git a/tracking/IdentityBasedTracking.java b/tracking/IdentityBasedTracking.java index d3d84615a505..32e5170103d5 100644 --- a/tracking/IdentityBasedTracking.java +++ b/tracking/IdentityBasedTracking.java @@ -9,6 +9,7 @@ import java.util.stream.Collectors; class IdentityBasedTracking extends InstanceTrackingStrategy { private long myLastId = -1; + private int myCount = 0; IdentityBasedTracking(@NotNull List initialInstances) { myLastId = getMaxId(initialInstances); } @@ -21,10 +22,16 @@ class IdentityBasedTracking extends InstanceTrackingStrategy { .filter(reference -> reference.uniqueID() > myLastId) .collect(Collectors.toList()); myLastId = Math.max(myLastId, getMaxId(result)); + myCount = result.size(); return result; } private long getMaxId(@NotNull List references) { return references.stream().map(ObjectReference::uniqueID).max(Long::compare).orElse(-1L); } + + @Override + public int getCount() { + return myCount; + } } diff --git a/tracking/RetainReferencesTracking.java b/tracking/RetainReferencesTracking.java index f5a20d72ce63..c35070e95d7d 100644 --- a/tracking/RetainReferencesTracking.java +++ b/tracking/RetainReferencesTracking.java @@ -8,7 +8,6 @@ import java.util.List; class RetainReferencesTracking extends InstanceTrackingStrategy { private final IdentityBasedTracking myIdTracker; - @SuppressWarnings("unused") private List myHardRefs; RetainReferencesTracking(@NotNull List initialInstances) { myHardRefs = initialInstances; @@ -23,4 +22,9 @@ class RetainReferencesTracking extends InstanceTrackingStrategy { myHardRefs = references; return myIdTracker.getNewInstances(); } + + @Override + public int getCount() { + return myHardRefs.size(); + } } diff --git a/tracking/TrackerForNewInstances.java b/tracking/TrackerForNewInstances.java index d28b930a7b77..b6fc329f5d36 100644 --- a/tracking/TrackerForNewInstances.java +++ b/tracking/TrackerForNewInstances.java @@ -8,5 +8,8 @@ import java.util.List; public interface TrackerForNewInstances { @NotNull List getNewInstances(); + + int getCount(); + boolean isReady(); } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 6b6d58638915..57cc6177fed5 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -107,7 +107,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { ReferenceType ref = myElems.get(convertRowIndexToModel(rowAtPoint(e.getPoint()))); TrackerForNewInstances strategy = myParent.getStrategy(ref); - if (strategy != null) { + if (strategy != null && strategy.isReady()) { new InstancesWindow(myDebugSession, limit -> strategy.getNewInstances(), ref.name()).show(); } } @@ -121,7 +121,6 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { DiffValue diff = myCounts.getOrDefault(ref, myUnknownValue); return !(myOnlyWithDiff && diff.diff() == 0 || myOnlyWithInstances && !diff.hasInstance()) && myMatcher.matches(ref.name()); - } }); @@ -433,13 +432,24 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { ReferenceType ref = myElems.get(convertRowIndexToModel(row)); TrackerForNewInstances strategy = myParent.getStrategy(ref); - SimpleTextAttributes attributes = SimpleTextAttributes.REGULAR_ATTRIBUTES; if (strategy != null && strategy.isReady()) { - attributes = myClickableCellAttributes; + DiffValue diffValue = (DiffValue) value; + long current = diffValue.myCurrentCount; + long old = diffValue.myOldCount; + long trackedNew = strategy.getCount(); + long deleted = Math.max(0, old + trackedNew - current); + if (trackedNew != 0) { + append(String.format("+%d", trackedNew), myClickableCellAttributes); + if (deleted != 0) { + append(String.format(" / %d", -deleted), SimpleTextAttributes.REGULAR_ATTRIBUTES); + } + } else { + append("0", SimpleTextAttributes.REGULAR_ATTRIBUTES); + } + } else { + String text = String.format("%s%d", diff > 0 ? "+" : "", diff); + append(text, SimpleTextAttributes.REGULAR_ATTRIBUTES); } - - String text = String.format("%s%d", diff > 0 ? "+" : "", diff); - append(text, attributes); } } } From bf65fc000590102d130ab6c55f1b36fdc200e090 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 21 Sep 2016 18:42:11 +0300 Subject: [PATCH 098/558] Fix bug when progress information not updated if a new filtering task has been started --- view/FilteringProgressView.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/view/FilteringProgressView.java b/view/FilteringProgressView.java index 7f17f6b9a8d7..90c7b1b6091d 100644 --- a/view/FilteringProgressView.java +++ b/view/FilteringProgressView.java @@ -63,6 +63,8 @@ class FilteringProgressView extends BorderLayoutPanel { myIsInProcess = true; myProgressBar.setMinimum(0); myProgressBar.setMaximum(totalCount); + myProgressBar.setValue(0); + myProgressText.setText(getDescription()); setProgressBarVisible(true); } From afc20f1dfd425637fc65099a6a3d19d35b6eedae Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 21 Sep 2016 19:34:22 +0300 Subject: [PATCH 099/558] Remove redundant operations in dispose method of InstancesWindow --- view/InstancesWindow.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index b9f1059c13e5..455a06790262 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -228,10 +228,7 @@ public class InstancesWindow extends DialogWrapper { @Override public void dispose() { - ActionManager.getInstance().removeAnActionListener(myActionListener); - myDebugSession.removeSessionListener(myDebugSessionListener); cancelFilteringTask(); - Disposer.dispose(myInstancesTree); } From 68af4c67edaec14ec810fa405e673eda92770c0f Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 23 Sep 2016 15:35:06 +0300 Subject: [PATCH 100/558] Do not restart filtering for instances in instances tree after "mark object"/"add to watch"/"evaluate expr" actions --- view/InstancesTree.java | 88 +++++++++++++++++++++++++++++++++++++++ view/InstancesWindow.java | 67 +++++++---------------------- 2 files changed, 102 insertions(+), 53 deletions(-) create mode 100644 view/InstancesTree.java diff --git a/view/InstancesTree.java b/view/InstancesTree.java new file mode 100644 index 000000000000..f5e70d207e40 --- /dev/null +++ b/view/InstancesTree.java @@ -0,0 +1,88 @@ +package org.jetbrains.debugger.memory.view; + +import com.intellij.openapi.project.Project; +import com.intellij.ui.SimpleTextAttributes; +import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; +import com.intellij.xdebugger.frame.*; +import com.intellij.xdebugger.impl.actions.XDebuggerActions; +import com.intellij.xdebugger.impl.frame.XValueMarkers; +import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; +import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState; +import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.List; + +class InstancesTree extends XDebuggerTree { + private final XValueNodeImpl myRoot; + private final Runnable myOnRootExpandAction; + private List myChildren; + + InstancesTree(@NotNull Project project, + @NotNull XDebuggerEditorsProvider editorsProvider, + @Nullable XValueMarkers valueMarkers, + @NotNull Runnable onRootExpand) { + super(project, editorsProvider, null, XDebuggerActions.INSPECT_TREE_POPUP_GROUP, valueMarkers); + myOnRootExpandAction = onRootExpand; + myRoot = new XValueNodeImpl(this, null, "root", new MyRootValue()); + + myRoot.children(); + setRoot(myRoot, false); + myRoot.setLeaf(false); + setSelectionRow(0); + expandNodesOnLoad(node -> node == myRoot); + } + + void addChildren(@NotNull XValueChildrenList children, boolean last) { + if (myChildren == null) { + myChildren = new ArrayList<>(); + } + + myChildren.add(children); + myRoot.addChildren(children, last); + } + + void rebuildTree(@NotNull RebuildPolicy policy, @NotNull XDebuggerTreeState state) { + if (policy == RebuildPolicy.RELOAD_INSTANCES) { + myChildren = null; + } + + rebuildAndRestore(state); + } + + void rebuildTree(@NotNull RebuildPolicy policy) { + rebuildTree(policy, XDebuggerTreeState.saveState(this)); + } + + void setMessage(@Nullable Icon icon, @NotNull String text, @NotNull SimpleTextAttributes textAttributes) { + myRoot.clearChildren(); + myRoot.setMessage(text, icon, textAttributes, null); + } + + enum RebuildPolicy { + RELOAD_INSTANCES, ONLY_UPDATE_LABELS + } + + private class MyRootValue extends XValue { + @Override + public void computeChildren(@NotNull XCompositeNode node) { + if (myChildren == null) { + myOnRootExpandAction.run(); + } else { + for (XValueChildrenList children : myChildren) { + myRoot.addChildren(children, false); + } + + myRoot.addChildren(XValueChildrenList.EMPTY, true); + } + } + + @Override + public void computePresentation(@NotNull XValueNode node, @NotNull XValuePlace place) { + node.setPresentation(null, "", "", true); + } + } +} diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 455a06790262..6bc8ed60e9c8 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -35,18 +35,16 @@ import com.intellij.util.ui.update.UiNotifyConnector; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; import com.intellij.xdebugger.XExpression; -import com.intellij.xdebugger.frame.*; +import com.intellij.xdebugger.frame.XValue; +import com.intellij.xdebugger.frame.XValueChildrenList; import com.intellij.xdebugger.impl.XDebugSessionImpl; import com.intellij.xdebugger.impl.actions.XDebuggerActionBase; -import com.intellij.xdebugger.impl.evaluate.quick.XDebuggerTreeCreator; import com.intellij.xdebugger.impl.frame.XValueMarkers; import com.intellij.xdebugger.impl.ui.XDebuggerExpressionEditor; import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants; import com.intellij.xdebugger.impl.ui.tree.ValueMarkup; -import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState; import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase; -import com.intellij.xdebugger.impl.ui.tree.nodes.XDebuggerTreeNode; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.sun.jdi.BooleanValue; import com.sun.jdi.ObjectReference; @@ -135,12 +133,6 @@ public class InstancesWindow extends DialogWrapper { return comp; } - @Override - protected void dispose() { - super.dispose(); - myInstancesView.dispose(); - } - @NotNull @Override protected Action[] createActions() { @@ -151,7 +143,7 @@ public class InstancesWindow extends DialogWrapper { private static final int MAX_TREE_NODE_COUNT = 2000; private static final int FILTERING_CHUNK_SIZE = 30; - private final XDebuggerTree myInstancesTree; + private final InstancesTree myInstancesTree; private final XDebuggerExpressionEditor myFilterConditionEditor; private final XDebugSessionListener myDebugSessionListener = new MySessionListener(); @@ -171,6 +163,7 @@ public class InstancesWindow extends DialogWrapper { MyInstancesView() { super(new BorderLayout(0, JBUI.scale(BORDER_LAYOUT_DEFAULT_GAP))); + Disposer.register(InstancesWindow.this.myDisposable, MyInstancesView.this); XValueMarkers markers = getValueMarkers(); ActionManager.getInstance().addAnActionListener(myActionListener, InstancesWindow.this.myDisposable); myDebugSession.addSessionListener(myDebugSessionListener, InstancesWindow.this.myDisposable); @@ -198,13 +191,7 @@ public class InstancesWindow extends DialogWrapper { myProgress.addStopActionListener(this::cancelFilteringTask); - XDebuggerTreeCreator treeCreator = - new XDebuggerTreeCreator(myProject, editorsProvider, null, markers); - - myInstancesTree = (XDebuggerTree) treeCreator.createTree(getTreeRootDescriptor()); - myInstancesTree.setRootVisible(false); - myInstancesTree.getRoot().setLeaf(false); - myInstancesTree.setExpandableItemsEnabled(true); + myInstancesTree = new InstancesTree(myProject, editorsProvider, markers, this::updateInstances); myFilterButton.addActionListener(e -> { String expression = myFilterConditionEditor.getExpression().getExpression(); @@ -213,7 +200,7 @@ public class InstancesWindow extends DialogWrapper { } myFilterButton.setEnabled(false); - myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree)); + myInstancesTree.rebuildTree(InstancesTree.RebuildPolicy.RELOAD_INSTANCES); }); JBScrollPane treeScrollPane = new JBScrollPane(myInstancesTree); @@ -308,28 +295,6 @@ public class InstancesWindow extends DialogWrapper { : null; } - private void addChildrenToTree(XValueChildrenList children, boolean last) { - XDebuggerTreeNode root = myInstancesTree.getRoot(); - if (root != null) { - ((XValueNodeImpl) root).addChildren(children, last); - } - } - - @NotNull - private Pair getTreeRootDescriptor() { - return Pair.pair(new XValue() { - @Override - public void computeChildren(@NotNull XCompositeNode node) { - updateInstances(); - } - - @Override - public void computePresentation(@NotNull XValueNode node, @NotNull XValuePlace place) { - node.setPresentation(null, "", "", true); - } - }, "root"); - } - private class MySessionListener implements XDebugSessionListener { private volatile XDebuggerTreeState myTreeState = null; @@ -339,12 +304,8 @@ public class InstancesWindow extends DialogWrapper { myTreeState = XDebuggerTreeState.saveState(myInstancesTree); cancelFilteringTask(); - myInstancesTree.getRoot().clearChildren(); - XCompositeNode root = (XCompositeNode) myInstancesTree.getRoot(); - - root.setMessage("The application is running", XDebuggerUIConstants.INFORMATION_MESSAGE_ICON, - SimpleTextAttributes.REGULAR_ATTRIBUTES, null); - myProgress.setVisible(false); + myInstancesTree.setMessage(XDebuggerUIConstants.INFORMATION_MESSAGE_ICON, + "The application is running", SimpleTextAttributes.REGULAR_ATTRIBUTES); }); } @@ -352,7 +313,7 @@ public class InstancesWindow extends DialogWrapper { public void sessionPaused() { SwingUtilities.invokeLater(() -> { myProgress.setVisible(true); - myInstancesTree.rebuildAndRestore(myTreeState); + myInstancesTree.rebuildTree(InstancesTree.RebuildPolicy.RELOAD_INSTANCES, myTreeState); }); } } @@ -380,8 +341,8 @@ public class InstancesWindow extends DialogWrapper { new ValueMarkup(expression.replace("@", ""), new JBColor(0, 0), null)); } - SwingUtilities.invokeLater(() -> - myInstancesTree.rebuildAndRestore(XDebuggerTreeState.saveState(myInstancesTree))); + SwingUtilities.invokeLater(() -> myInstancesTree + .rebuildTree(InstancesTree.RebuildPolicy.ONLY_UPDATE_LABELS)); } } } @@ -433,9 +394,9 @@ public class InstancesWindow extends DialogWrapper { if (!myErrorsGroup.isEmpty()) { XValueChildrenList lst = new XValueChildrenList(); lst.addBottomGroup(myErrorsGroup); - addChildrenToTree(lst, true); + myInstancesTree.addChildren(lst, true); } else { - addChildrenToTree(XValueChildrenList.EMPTY, true); + myInstancesTree.addChildren(XValueChildrenList.EMPTY, true); } myFilterButton.setEnabled(true); @@ -494,7 +455,7 @@ public class InstancesWindow extends DialogWrapper { if (children.size() > 0) { SwingUtilities.invokeLater(() -> { if (MyFilteringWorker.this == myFilteringTask) { - addChildrenToTree(children, false); + myInstancesTree.addChildren(children, false); } }); } From 3dfd874ee41e132d55319a2a709759309f4b2604 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 23 Sep 2016 15:54:58 +0300 Subject: [PATCH 101/558] Update only presentation for new instances in tree after step of debugger (instead of instances list) --- view/ClassesTable.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 57cc6177fed5..e5331c639f18 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -107,8 +107,9 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { ReferenceType ref = myElems.get(convertRowIndexToModel(rowAtPoint(e.getPoint()))); TrackerForNewInstances strategy = myParent.getStrategy(ref); - if (strategy != null && strategy.isReady()) { - new InstancesWindow(myDebugSession, limit -> strategy.getNewInstances(), ref.name()).show(); + if (strategy != null && strategy.isReady() && strategy.getCount() > 0) { + List newInstances = strategy.getNewInstances(); + new InstancesWindow(myDebugSession, limit -> newInstances, ref.name()).show(); } } }); From cdc7d9ab1b7fb942c7585d1606bf138e4d263d2d Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 23 Sep 2016 17:02:22 +0300 Subject: [PATCH 102/558] Track instances creation when tool window is closed --- view/ClassesFilteredView.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index ac883146a947..a3fe1a720f65 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -212,7 +212,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable public void sessionPaused() { SwingUtilities.invokeLater(() -> myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_SUSPENDED)); if (myNeedReloadClasses) { - myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::commitTracked); + commitAllTrackers(); updateClassesAndCounts(); } } @@ -255,6 +255,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable if (myNeedReloadClasses) { SuspendContextImpl suspendContext = getSuspendContext(); if (suspendContext != null && !suspendContext.equals(myLastSuspendContext)) { + commitAllTrackers(); updateClassesAndCounts(); } } @@ -282,6 +283,10 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } } + private void commitAllTrackers() { + myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::commitTracked); + } + private SuspendContextImpl getSuspendContext() { return DebuggerManagerImpl.getInstanceEx(myProject).getContext().getSuspendContext(); } From 9a7366a04b74d364f3f9a026011bc82f07b6ff0f Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 23 Sep 2016 18:36:14 +0300 Subject: [PATCH 103/558] Fix bug when instances tree was not empty after session has been resumed --- view/InstancesTree.java | 1 + 1 file changed, 1 insertion(+) diff --git a/view/InstancesTree.java b/view/InstancesTree.java index f5e70d207e40..d2b612ef672b 100644 --- a/view/InstancesTree.java +++ b/view/InstancesTree.java @@ -58,6 +58,7 @@ class InstancesTree extends XDebuggerTree { } void setMessage(@Nullable Icon icon, @NotNull String text, @NotNull SimpleTextAttributes textAttributes) { + myChildren = null; myRoot.clearChildren(); myRoot.setMessage(text, icon, textAttributes, null); } From d6c4f24783b6f61811dcb3331ed18f9a97adff7a Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 23 Sep 2016 19:02:15 +0300 Subject: [PATCH 104/558] Add limit for amount of instances for tracking by constructor calls --- tracking/ConstructorInstancesTracker.java | 38 ++++++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index 4eade041d9c4..57f46ade8153 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -24,6 +24,7 @@ import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import com.sun.jdi.event.LocatableEvent; import com.sun.jdi.request.BreakpointRequest; +import com.sun.jdi.request.EventRequest; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.CreationPositionTracker; @@ -34,6 +35,7 @@ import java.util.*; import java.util.stream.Collectors; public class ConstructorInstancesTracker implements TrackerForNewInstances, Disposable { + private static final int TRACKED_INSTANCES_LIMIT = 2000; private final ReferenceType myReference; private final XDebugSession myDebugSession; private final CreationPositionTracker myPositionTracker; @@ -42,6 +44,8 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp @Nullable private HashSet myNewObjects = null; + private final MyConstructorBreakpoints myBreakpoint; + @NotNull private HashSet myTrackedObjects = new HashSet<>(); public ConstructorInstancesTracker(@NotNull ReferenceType ref, @@ -59,14 +63,19 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp new JavaLineBreakpointProperties(), new LineBreakpointState<>()); - MyConstructorBreakpoints breakpoint = new MyConstructorBreakpoints(project, bpn); - breakpoint.createRequestForPreparedClass(myDebugProcess, myReference); + myBreakpoint = new MyConstructorBreakpoints(project, bpn); + myBreakpoint.createRequestForPreparedClass(myDebugProcess, myReference); - Disposer.register(this, breakpoint); + Disposer.register(this, myBreakpoint); } public void obsolete() { + if(myNewObjects != null) { + myNewObjects.forEach(ObjectReference::enableCollection); + } + myNewObjects = null; + myBreakpoint.enable(); myPositionTracker.releaseBySession(myDebugSession); } @@ -98,6 +107,8 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp private final class MyConstructorBreakpoints extends LineBreakpoint implements Disposable { + private final List myRequests = new ArrayList<>(); + MyConstructorBreakpoints(Project project, XBreakpoint xBreakpoint) { super(project, xBreakpoint); } @@ -107,8 +118,9 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp classType.methods().stream().filter(Method::isConstructor).forEach(cons -> { Location loc = cons.location(); BreakpointRequest breakpointRequest = debugProcess.getRequestsManager().createBreakpointRequest(this, loc); - breakpointRequest.enable(); + myRequests.add(breakpointRequest); }); + enable(); } @Override @@ -120,6 +132,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp myDebugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { @Override protected void action() throws Exception { + disable(); myDebugProcess.getRequestsManager().deleteRequest(MyConstructorBreakpoints.this); } }); @@ -133,6 +146,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp if (suspendContext != null) { ObjectReference thisRef = getThisObject(suspendContext, event); if (myReference.equals(thisRef.referenceType())) { + thisRef.disableCollection(); myTrackedObjects.add(thisRef); ThreadReferenceProxyImpl threadReferenceProxy = suspendContext.getThread(); List stack = threadReferenceProxy == null ? null : threadReferenceProxy.frames(); @@ -150,12 +164,26 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp }).filter(Objects::nonNull).collect(Collectors.toList()); myPositionTracker.addStack(myDebugSession, thisRef, stackFrameDescriptors); } + } } } catch (EvaluateException e) { - e.printStackTrace(); + return false; } + + if(myTrackedObjects.size() >= TRACKED_INSTANCES_LIMIT) { + disable(); + } + return false; } + + void enable() { + myRequests.forEach(EventRequest::enable); + } + + void disable() { + myRequests.forEach(EventRequest::disable); + } } } From c5a966c3b1886f9e2473fd680f7a48eee1f482ae Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 23 Sep 2016 19:44:22 +0300 Subject: [PATCH 105/558] Rewrite diff column renderer --- tracking/ConstructorInstancesTracker.java | 3 +-- view/ClassesTable.java | 20 +++++++++----------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index 57f46ade8153..f5e8478e2657 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -40,12 +40,11 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp private final XDebugSession myDebugSession; private final CreationPositionTracker myPositionTracker; private final DebugProcessImpl myDebugProcess; + private final MyConstructorBreakpoints myBreakpoint; @Nullable private HashSet myNewObjects = null; - private final MyConstructorBreakpoints myBreakpoint; - @NotNull private HashSet myTrackedObjects = new HashSet<>(); public ConstructorInstancesTracker(@NotNull ReferenceType ref, diff --git a/view/ClassesTable.java b/view/ClassesTable.java index e5331c639f18..b1d0239ae7f8 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -433,22 +433,20 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { ReferenceType ref = myElems.get(convertRowIndexToModel(row)); TrackerForNewInstances strategy = myParent.getStrategy(ref); + + String text = String.format("%s%d", diff > 0 ? "+" : "", diff); + if (strategy != null && strategy.isReady()) { - DiffValue diffValue = (DiffValue) value; - long current = diffValue.myCurrentCount; - long old = diffValue.myOldCount; long trackedNew = strategy.getCount(); - long deleted = Math.max(0, old + trackedNew - current); - if (trackedNew != 0) { - append(String.format("+%d", trackedNew), myClickableCellAttributes); - if (deleted != 0) { - append(String.format(" / %d", -deleted), SimpleTextAttributes.REGULAR_ATTRIBUTES); - } + if (trackedNew == diff) { + append(text, diff == 0 ? SimpleTextAttributes.REGULAR_ATTRIBUTES : myClickableCellAttributes); } else { - append("0", SimpleTextAttributes.REGULAR_ATTRIBUTES); + append(text, SimpleTextAttributes.REGULAR_ATTRIBUTES); + if (trackedNew != 0) { + append(String.format(" (%d)", trackedNew), myClickableCellAttributes); + } } } else { - String text = String.format("%s%d", diff > 0 ? "+" : "", diff); append(text, SimpleTextAttributes.REGULAR_ATTRIBUTES); } } From ae4c4f0227a5858fa4a97986684ce869e94bbaef Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 23 Sep 2016 20:25:58 +0300 Subject: [PATCH 106/558] Navigate to line with operator "new" instead of constructor location --- view/StackFramePopup.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/view/StackFramePopup.java b/view/StackFramePopup.java index 64a70330e53d..484e34d25b7a 100644 --- a/view/StackFramePopup.java +++ b/view/StackFramePopup.java @@ -43,6 +43,7 @@ public class StackFramePopup { .setItemChoosenCallback(() -> navigateToSelectedFrame(list, true)) .createPopup(); + list.setSelectedIndex(1); popup.showInFocusCenter(); } @@ -87,7 +88,7 @@ public class StackFramePopup { frame.line() - 1); OpenFileDescriptor descriptor = info.getDescriptor(); if (descriptor != null) { - FileEditorManager.getInstance(myProject).openTextEditor(descriptor, focusEditor); + FileEditorManager.getInstance(myProject).openEditor(descriptor, focusEditor); } }); } From e8f60afae3fde0d5687212e7b1b39992cfe12485 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 30 Sep 2016 15:48:10 +0300 Subject: [PATCH 107/558] Open java file for library classes (if it available) when action "go to allocation Source" is used --- view/StackFramePopup.java | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/view/StackFramePopup.java b/view/StackFramePopup.java index 484e34d25b7a..08c68924676f 100644 --- a/view/StackFramePopup.java +++ b/view/StackFramePopup.java @@ -8,8 +8,11 @@ import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.PsiUtilCore; import com.intellij.ui.ColoredListCellRenderer; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBList; @@ -21,6 +24,8 @@ import javax.swing.*; import java.util.List; public class StackFramePopup { + private static final char ANONYMOUS_CLASS_DELIMITER = '$'; + private final Project myProject; private final List myStackFrame; private final GlobalSearchScope myScope; @@ -80,14 +85,24 @@ public class StackFramePopup { private void navigateToSelectedFrame(@NotNull JBList frameList, boolean focusEditor) { StackFrameDescriptor frame = (StackFrameDescriptor) frameList.getSelectedValue(); if (frame != null) { - PsiClass psiClass = DebuggerUtils.findClass(frame.path(), myProject, myScope); + String path = frame.path(); + int anonymousClassDelimiterIndex = path.indexOf(ANONYMOUS_CLASS_DELIMITER); + int pathLength = anonymousClassDelimiterIndex > 0 ? anonymousClassDelimiterIndex : path.length(); + path = path.substring(0, pathLength); + PsiClass psiClass = DebuggerUtils.findClass(path, myProject, myScope); if (psiClass != null) { ApplicationManager.getApplication().runReadAction(() -> { + PsiElement navigationElement = psiClass.getNavigationElement(); + VirtualFile file = PsiUtilCore.getVirtualFile(navigationElement); + if (file == null) { + file = psiClass.getContainingFile().getVirtualFile(); + } + OpenFileHyperlinkInfo info = - new OpenFileHyperlinkInfo(myProject, psiClass.getContainingFile().getVirtualFile(), - frame.line() - 1); + new OpenFileHyperlinkInfo(myProject, file, frame.line() - 1); OpenFileDescriptor descriptor = info.getDescriptor(); if (descriptor != null) { + descriptor.setUseCurrentWindow(true); FileEditorManager.getInstance(myProject).openEditor(descriptor, focusEditor); } }); From 962e1ec513c7cf649c9d3cd2fe47886f913f6759 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 30 Sep 2016 18:36:25 +0300 Subject: [PATCH 108/558] Add ability to select strategy for tracking of instances in background --- action/EnableBackgroundTrackingAction.java | 23 ++++++ component/InstancesTracker.java | 15 ++++ event/InstancesTrackerListener.java | 5 +- toolwindow/MemoryViewToolWindowFactory.java | 19 +++-- tracking/BackgroundTracker.java | 5 ++ tracking/ConstructorInstancesTracker.java | 80 ++++++++++++++++++--- tracking/TrackerForNewInstances.java | 3 +- view/ClassesFilteredView.java | 62 ++++++++++------ 8 files changed, 172 insertions(+), 40 deletions(-) create mode 100644 action/EnableBackgroundTrackingAction.java create mode 100644 tracking/BackgroundTracker.java diff --git a/action/EnableBackgroundTrackingAction.java b/action/EnableBackgroundTrackingAction.java new file mode 100644 index 000000000000..f475aa11e339 --- /dev/null +++ b/action/EnableBackgroundTrackingAction.java @@ -0,0 +1,23 @@ +package org.jetbrains.debugger.memory.action; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.project.Project; +import org.jetbrains.debugger.memory.component.InstancesTracker; + +public class EnableBackgroundTrackingAction extends ToggleAction{ + + @Override + public boolean isSelected(AnActionEvent e) { + Project project = e.getProject(); + return project != null && InstancesTracker.getInstance(project).isBackgroundTrackingEnabled(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + Project project = e.getProject(); + if (project != null) { + InstancesTracker.getInstance(project).setBackgroundTackingEnabled(state); + } + } +} diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java index 28c416bec251..390a3a3c62ee 100644 --- a/component/InstancesTracker.java +++ b/component/InstancesTracker.java @@ -34,6 +34,10 @@ public class InstancesTracker extends AbstractProjectComponent return myState.classes.containsKey(className); } + public boolean isBackgroundTrackingEnabled() { + return myState.isBackgroundTrackingEnabled; + } + @Nullable public TrackingType getTrackingType(@NotNull String className) { return myState.classes.getOrDefault(className, null); @@ -69,6 +73,14 @@ public class InstancesTracker extends AbstractProjectComponent myDispatcher.addListener(listener, parentDisposable); } + public void setBackgroundTackingEnabled(boolean state) { + boolean oldState = myState.isBackgroundTrackingEnabled; + if(state != oldState) { + myState.isBackgroundTrackingEnabled = state; + myDispatcher.getMulticaster().backgroundTrackingValueChanged(state); + } + } + @Nullable @Override public MyState getState() { @@ -81,6 +93,8 @@ public class InstancesTracker extends AbstractProjectComponent } static class MyState { + boolean isBackgroundTrackingEnabled = false; + @NotNull @AbstractCollection(surroundWithTag = false, elementTypes = {Map.Entry.class}) final Map classes = new ConcurrentHashMap<>(); @@ -89,6 +103,7 @@ public class InstancesTracker extends AbstractProjectComponent } MyState(@NotNull MyState state) { + isBackgroundTrackingEnabled = state.isBackgroundTrackingEnabled; for (Map.Entry classState : state.classes.entrySet()) { classes.put(classState.getKey(), classState.getValue()); } diff --git a/event/InstancesTrackerListener.java b/event/InstancesTrackerListener.java index 0e8924e17657..e01372032a1c 100644 --- a/event/InstancesTrackerListener.java +++ b/event/InstancesTrackerListener.java @@ -5,10 +5,13 @@ import org.jetbrains.debugger.memory.tracking.TrackingType; import java.util.EventListener; -public interface InstancesTrackerListener extends EventListener{ +public interface InstancesTrackerListener extends EventListener { default void classChanged(@NotNull String name, @NotNull TrackingType type) { } default void classRemoved(@NotNull String name) { } + + default void backgroundTrackingValueChanged(boolean newState) { + } } diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java index 2b8680786822..497a6926cfee 100644 --- a/toolwindow/MemoryViewToolWindowFactory.java +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -39,6 +39,9 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware private final HashMap myMemoryViews = new HashMap<>(); + @Nullable + private ClassesFilteredView myCurrentView = null; + { myEmptyContent = new JBLabel("Run debugging to see loaded classes", SwingConstants.CENTER); } @@ -65,14 +68,15 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware public void stateChanged() { if (!myMemoryViews.isEmpty() && !toolWindow.isDisposed()) { myMemoryViews.values().forEach(classesFilteredView -> - classesFilteredView.setNeedReloadClasses(toolWindow.isVisible())); + classesFilteredView.setActive(toolWindow.isVisible())); } } }, project); ActionGroup group = new DefaultActionGroup( ActionManager.getInstance().getAction("MemoryView.ShowOnlyWithInstances"), - ActionManager.getInstance().getAction("MemoryView.ShowOnlyWithDiff") + ActionManager.getInstance().getAction("MemoryView.ShowOnlyWithDiff"), + ActionManager.getInstance().getAction("MemoryView.EnableTrackingWithClosedWindow") ); ((ToolWindowEx) toolWindow).setAdditionalGearActions(group); toolWindow.getComponent().setLayout(new BorderLayout()); @@ -88,16 +92,14 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware ToolWindow toolWindow = getToolWindow(session.getProject()); if (!myMemoryViews.containsKey(session)) { ClassesFilteredView newView = new ClassesFilteredView(session); - newView.setNeedReloadClasses(toolWindow != null && toolWindow.isVisible()); + newView.setActive(toolWindow != null && toolWindow.isVisible()); myMemoryViews.put(session, newView); } } private void removeSession(@NotNull XDebugSession session) { ClassesFilteredView removed = myMemoryViews.remove(session); - if(removed != null) { - Disposer.dispose(removed); - } + Disposer.dispose(removed); } private void updateCurrentMemoryView(@NotNull Project project, @NotNull ToolWindow toolWindow) { @@ -106,6 +108,11 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware if (session != null && myMemoryViews.containsKey(session)) { ClassesFilteredView view = myMemoryViews.get(session); replaceToolWindowContent(toolWindow, view); + if (myCurrentView != null) { + myCurrentView.setActive(true); + } + + myCurrentView = view; return; } } diff --git a/tracking/BackgroundTracker.java b/tracking/BackgroundTracker.java new file mode 100644 index 000000000000..51ce3955b786 --- /dev/null +++ b/tracking/BackgroundTracker.java @@ -0,0 +1,5 @@ +package org.jetbrains.debugger.memory.tracking; + +interface BackgroundTracker { + void setBackgroundMode(boolean isBackgroundMode); +} diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index f5e8478e2657..c8924f518569 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -12,7 +12,6 @@ import com.intellij.debugger.ui.breakpoints.JavaLineBreakpointType; import com.intellij.debugger.ui.breakpoints.LineBreakpoint; import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Disposer; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.breakpoints.XBreakpoint; import com.intellij.xdebugger.impl.XDebuggerManagerImpl; @@ -28,13 +27,14 @@ import com.sun.jdi.request.EventRequest; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.CreationPositionTracker; +import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; import org.jetbrains.java.debugger.breakpoints.properties.JavaLineBreakpointProperties; import java.util.*; import java.util.stream.Collectors; -public class ConstructorInstancesTracker implements TrackerForNewInstances, Disposable { +public class ConstructorInstancesTracker implements TrackerForNewInstances, Disposable, BackgroundTracker { private static final int TRACKED_INSTANCES_LIMIT = 2000; private final ReferenceType myReference; private final XDebugSession myDebugSession; @@ -47,12 +47,17 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp @NotNull private HashSet myTrackedObjects = new HashSet<>(); + + private boolean myIsBackgroundMode; + private boolean myIsBackgroundTrackingEnabled; + public ConstructorInstancesTracker(@NotNull ReferenceType ref, @NotNull XDebugSession debugSession) { myReference = ref; myDebugSession = debugSession; myPositionTracker = CreationPositionTracker.getInstance(debugSession.getProject()); Project project = debugSession.getProject(); + myIsBackgroundTrackingEnabled = InstancesTracker.getInstance(myDebugSession.getProject()).isBackgroundTrackingEnabled(); myDebugProcess = (DebugProcessImpl) DebuggerManager.getInstance(project) .getDebugProcess(debugSession.getDebugProcess().getProcessHandler()); JavaLineBreakpointType breakPointType = new JavaLineBreakpointType(); @@ -64,17 +69,17 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp myBreakpoint = new MyConstructorBreakpoints(project, bpn); myBreakpoint.createRequestForPreparedClass(myDebugProcess, myReference); - - Disposer.register(this, myBreakpoint); } public void obsolete() { - if(myNewObjects != null) { + if (myNewObjects != null) { myNewObjects.forEach(ObjectReference::enableCollection); } myNewObjects = null; - myBreakpoint.enable(); + if (!myIsBackgroundMode || myIsBackgroundTrackingEnabled) { + myBreakpoint.enable(); + } myPositionTracker.releaseBySession(myDebugSession); } @@ -101,11 +106,54 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp @Override public void dispose() { + myBreakpoint.dispose(); + } + + @Override + public void backgroundTrackingValueChanged(boolean newValue) { + if (myIsBackgroundTrackingEnabled != newValue) { + myIsBackgroundTrackingEnabled = newValue; + myDebugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { + @Override + protected void action() throws Exception { + if (newValue) { + myBreakpoint.enable(); + } else { + myBreakpoint.disable(); + } + } + }); + } + } + + @Override + public void setBackgroundMode(boolean isBackgroundMode) { + if (myIsBackgroundMode == isBackgroundMode) { + return; + } + + myIsBackgroundMode = isBackgroundMode; + if (isBackgroundMode) { + doEnableBackgroundMode(); + } else { + doDisableBackgroundMode(); + } + } + + private void doDisableBackgroundMode() { + myBreakpoint.enable(); + } + + private void doEnableBackgroundMode() { + if (!myIsBackgroundTrackingEnabled) { + myBreakpoint.disable(); + } } private final class MyConstructorBreakpoints extends LineBreakpoint implements Disposable { + private boolean myIsEnabled = false; private final List myRequests = new ArrayList<>(); MyConstructorBreakpoints(Project project, XBreakpoint xBreakpoint) { @@ -114,12 +162,15 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp @Override protected void createRequestForPreparedClass(DebugProcessImpl debugProcess, ReferenceType classType) { - classType.methods().stream().filter(Method::isConstructor).forEach(cons -> { + classType.methods().stream().filter(Method::isConstructor).forEach(cons -> { Location loc = cons.location(); BreakpointRequest breakpointRequest = debugProcess.getRequestsManager().createBreakpointRequest(this, loc); myRequests.add(breakpointRequest); }); - enable(); + + if (!myIsBackgroundMode || myIsBackgroundTrackingEnabled) { + enable(); + } } @Override @@ -170,7 +221,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp return false; } - if(myTrackedObjects.size() >= TRACKED_INSTANCES_LIMIT) { + if (myTrackedObjects.size() >= TRACKED_INSTANCES_LIMIT) { disable(); } @@ -178,11 +229,18 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp } void enable() { - myRequests.forEach(EventRequest::enable); + if (!myIsEnabled) { + myRequests.forEach(EventRequest::enable); + myIsEnabled = true; + } } void disable() { - myRequests.forEach(EventRequest::disable); + if (myIsEnabled) { + myRequests.forEach(EventRequest::disable); + myIsEnabled = false; + } } } } + diff --git a/tracking/TrackerForNewInstances.java b/tracking/TrackerForNewInstances.java index b6fc329f5d36..75a54b0b3985 100644 --- a/tracking/TrackerForNewInstances.java +++ b/tracking/TrackerForNewInstances.java @@ -2,10 +2,11 @@ package org.jetbrains.debugger.memory.tracking; import com.sun.jdi.ObjectReference; import org.jetbrains.annotations.NotNull; +import org.jetbrains.debugger.memory.event.InstancesTrackerListener; import java.util.List; -public interface TrackerForNewInstances { +public interface TrackerForNewInstances extends InstancesTrackerListener { @NotNull List getNewInstances(); diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index a3fe1a720f65..2be691d6b076 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -65,9 +65,11 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private final ClassesTable myTable; private final InstancesTracker myInstancesTracker; private final Map myConstructorTrackedClasses = new ConcurrentHashMap<>(); + private final XDebugSessionListener myDebugSessionListener; private volatile SuspendContextImpl myLastSuspendContext; - private volatile boolean myNeedReloadClasses = false; + + private boolean myIsActive; public ClassesFilteredView(@NotNull XDebugSession debugSession) { super(); @@ -192,7 +194,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable MemoryViewManager.getInstance().addMemoryViewManagerListener(memoryViewManagerListener, this); - myDebugSession.addSessionListener(new XDebugSessionListener() { + myDebugSessionListener = new XDebugSessionListener() { @Override public void sessionResumed() { myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::obsolete); @@ -211,12 +213,10 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void sessionPaused() { SwingUtilities.invokeLater(() -> myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_SUSPENDED)); - if (myNeedReloadClasses) { - commitAllTrackers(); - updateClassesAndCounts(); - } + commitAllTrackers(); + updateClassesAndCounts(); } - }, this); + }; mySingleAlarm = new SingleAlarmWithMutableDelay(() -> { myLastSuspendContext = getSuspendContext(); @@ -249,19 +249,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable return myConstructorTrackedClasses.getOrDefault(ref, null); } - public void setNeedReloadClasses(boolean value) { - if (myNeedReloadClasses != value) { - myNeedReloadClasses = value; - if (myNeedReloadClasses) { - SuspendContextImpl suspendContext = getSuspendContext(); - if (suspendContext != null && !suspendContext.equals(myLastSuspendContext)) { - commitAllTrackers(); - updateClassesAndCounts(); - } - } - } - } - private void trackClass(@NotNull ReferenceType ref, @NotNull TrackingType type) { LOG.assertTrue(DebuggerManager.getInstance(myProject).isDebuggerManagerThread()); @@ -272,6 +259,8 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } ConstructorInstancesTracker tracker = new ConstructorInstancesTracker(ref, myDebugSession); + tracker.setBackgroundMode(!myIsActive); + Disposer.register(ClassesFilteredView.this, tracker); myConstructorTrackedClasses.put(ref, tracker); } @@ -305,13 +294,44 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable boolean isTrackingActive(@NotNull ReferenceType ref) { TrackerForNewInstances strategy = myConstructorTrackedClasses.getOrDefault(ref, null); return strategy != null && strategy.isReady(); - } @Override public void dispose() { } + public void setActive(boolean active) { + if (myIsActive == active) { + return; + } + + myIsActive = active; + myDebugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { + @Override + protected void action() throws Exception { + if (active) { + doActivate(); + } else { + doPause(); + } + } + }); + } + + private void doActivate() { + myDebugSession.addSessionListener(myDebugSessionListener, ClassesFilteredView.this); + if (getSuspendContext() != null) { + myConstructorTrackedClasses.values().forEach(x -> x.setBackgroundMode(false)); + commitAllTrackers(); + updateClassesAndCounts(); + } + } + + private void doPause() { + myDebugSession.removeSessionListener(myDebugSessionListener); + myConstructorTrackedClasses.values().forEach(x -> x.setBackgroundMode(true)); + } + private final class MyUpdateClassesCommand extends LowestPriorityCommand { MyUpdateClassesCommand(@Nullable SuspendContextImpl suspendContext) { From d25f44cc3c2327f083e9bb136b40a68d01ac16d9 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 30 Sep 2016 20:04:41 +0300 Subject: [PATCH 109/558] Save stacks for new objects after debugger step --- component/CreationPositionTracker.java | 58 ++++++++++++++++++++++---- view/ClassesTable.java | 17 +++++--- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/component/CreationPositionTracker.java b/component/CreationPositionTracker.java index 6045d9089b41..6bf4a4d27f56 100644 --- a/component/CreationPositionTracker.java +++ b/component/CreationPositionTracker.java @@ -5,18 +5,23 @@ import com.intellij.openapi.project.Project; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; -import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; public class CreationPositionTracker extends AbstractProjectComponent { - private final Map>> mySession2Reference2Stack - = new ConcurrentHashMap<>(); + private final ConcurrentHashMap>> + mySession2Reference2Stack = new ConcurrentHashMap<>(); + + private final ConcurrentHashMap>> + myPinnedSession2Reference2Stack = new ConcurrentHashMap<>(); public CreationPositionTracker(Project project) { super(project); @@ -28,9 +33,8 @@ public class CreationPositionTracker extends AbstractProjectComponent { @Nullable public List getStack(@NotNull XDebugSession session, @NotNull ObjectReference ref) { - Map> ref2Stack = mySession2Reference2Stack.getOrDefault(session, null); - List stack = ref2Stack != null ? ref2Stack.getOrDefault(ref, null) : null; - return stack != null ? Collections.unmodifiableList(stack) : null; + List stack = extract(mySession2Reference2Stack, session, ref); + return stack != null ? stack : extract(myPinnedSession2Reference2Stack, session, ref); } public void addStack(@NotNull XDebugSession session, @NotNull ObjectReference ref, @@ -49,10 +53,46 @@ public class CreationPositionTracker extends AbstractProjectComponent { } public void releaseBySession(@NotNull XDebugSession session) { - if(!mySession2Reference2Stack.containsKey(session)) { - return; + if(mySession2Reference2Stack.containsKey(session)) { + mySession2Reference2Stack.put(session, new ConcurrentHashMap<>()); + } + } + + public void unpinStacks(@NotNull XDebugSession session, @NotNull ReferenceType ref) { + Map> ref2Stack = myPinnedSession2Reference2Stack.getOrDefault(session, null); + if (ref2Stack != null) { + Iterator iterator = ref2Stack.keySet().iterator(); + while (iterator.hasNext()) { + ObjectReference reference = iterator.next(); + if (ref.equals(reference.referenceType())) { + iterator.remove(); + } + } + + if (ref2Stack.isEmpty()) { + myPinnedSession2Reference2Stack.remove(session); + } + } + } + + public void pinStacks(@NotNull XDebugSession session, @NotNull ReferenceType ref) { + Map> ref2Stack = mySession2Reference2Stack.getOrDefault(session, null); + if (ref2Stack != null) { + Map> ref2StacksByReferenceType = ref2Stack.entrySet().stream() + .filter(entry -> ref.equals(entry.getKey().referenceType())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + myPinnedSession2Reference2Stack.put(session, ref2StacksByReferenceType); + } + } + + @Nullable + private static T extract(@NotNull Map> map, + @NotNull XDebugSession session, @NotNull ObjectReference ref) { + Map ref2something = map.getOrDefault(session, null); + if(ref2something != null) { + return ref2something.getOrDefault(ref, null); } - mySession2Reference2Stack.put(session, new ConcurrentHashMap<>()); + return null; } } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index b1d0239ae7f8..94bdd7ec401c 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -3,6 +3,7 @@ package org.jetbrains.debugger.memory.view; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.DataKey; import com.intellij.openapi.actionSystem.DataProvider; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.TextRange; import com.intellij.psi.codeStyle.MinusculeMatcher; import com.intellij.psi.codeStyle.NameUtil; @@ -21,6 +22,7 @@ import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.component.CreationPositionTracker; import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.tracking.TrackerForNewInstances; import org.jetbrains.debugger.memory.tracking.TrackingType; @@ -109,7 +111,11 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { TrackerForNewInstances strategy = myParent.getStrategy(ref); if (strategy != null && strategy.isReady() && strategy.getCount() > 0) { List newInstances = strategy.getNewInstances(); - new InstancesWindow(myDebugSession, limit -> newInstances, ref.name()).show(); + CreationPositionTracker.getInstance(myDebugSession.getProject()).pinStacks(myDebugSession, ref); + InstancesWindow instancesWindow = new InstancesWindow(myDebugSession, limit -> newInstances, ref.name()); + Disposer.register(instancesWindow.getDisposable(), + () -> CreationPositionTracker.getInstance(myDebugSession.getProject()).unpinStacks(myDebugSession, ref)); + instancesWindow.show(); } } }); @@ -423,11 +429,12 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { } private class MyDiffColumnRenderer extends MyTableCellRenderer { - private final SimpleTextAttributes myClickableCellAttributes = new SimpleTextAttributes(SimpleTextAttributes.STYLE_UNDERLINE, - JBColor.BLUE); + private final SimpleTextAttributes myClickableCellAttributes = + new SimpleTextAttributes(SimpleTextAttributes.STYLE_UNDERLINE, JBColor.BLUE); @Override - protected void addText(JTable table, @NotNull Object value, boolean isSelected, boolean hasFocus, int row, int column) { + protected void addText(JTable table, @NotNull Object value, boolean isSelected, + boolean hasFocus, int row, int column) { long diff = ((DiffValue) value).diff(); setTextAlign(SwingConstants.RIGHT); @@ -439,7 +446,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { if (strategy != null && strategy.isReady()) { long trackedNew = strategy.getCount(); if (trackedNew == diff) { - append(text, diff == 0 ? SimpleTextAttributes.REGULAR_ATTRIBUTES : myClickableCellAttributes); + append(text, diff == 0 ? SimpleTextAttributes.REGULAR_ATTRIBUTES : myClickableCellAttributes); } else { append(text, SimpleTextAttributes.REGULAR_ATTRIBUTES); if (trackedNew != 0) { From cbc274b27f384485489e11234494509e249d10f4 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Fri, 30 Sep 2016 20:35:01 +0300 Subject: [PATCH 110/558] Save stacks for new objects after debugger step. Associate "Jump to allocation Source" action with appropriate debug session --- ...java => JumpToAllocationSourceAction.java} | 6 +++--- component/CreationPositionTracker.java | 2 +- view/InstancesTree.java | 20 ++++++++++++++++++- view/InstancesWindow.java | 2 +- 4 files changed, 24 insertions(+), 6 deletions(-) rename action/tracking/{GetAllocationSourceAction.java => JumpToAllocationSourceAction.java} (90%) diff --git a/action/tracking/GetAllocationSourceAction.java b/action/tracking/JumpToAllocationSourceAction.java similarity index 90% rename from action/tracking/GetAllocationSourceAction.java rename to action/tracking/JumpToAllocationSourceAction.java index e396d989ae6e..dba977e27710 100644 --- a/action/tracking/GetAllocationSourceAction.java +++ b/action/tracking/JumpToAllocationSourceAction.java @@ -13,11 +13,12 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.action.DebuggerTreeAction; import org.jetbrains.debugger.memory.component.CreationPositionTracker; import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; +import org.jetbrains.debugger.memory.view.InstancesTree; import org.jetbrains.debugger.memory.view.StackFramePopup; import java.util.List; -public class GetAllocationSourceAction extends DebuggerTreeAction { +public class JumpToAllocationSourceAction extends DebuggerTreeAction { @Override public void update(AnActionEvent e) { e.getPresentation().setVisible(getStack(e) != null); @@ -46,8 +47,7 @@ public class GetAllocationSourceAction extends DebuggerTreeAction { return null; } - // TODO: Wrong way: session may be not current - XDebugSession session = XDebuggerManager.getInstance(project).getCurrentSession(); + XDebugSession session = e.getData(InstancesTree.DEBUG_SESSION_DATA_KEY); return session == null ? null : CreationPositionTracker.getInstance(project).getStack(session, ref); } } diff --git a/component/CreationPositionTracker.java b/component/CreationPositionTracker.java index 6bf4a4d27f56..7b2937adc1fb 100644 --- a/component/CreationPositionTracker.java +++ b/component/CreationPositionTracker.java @@ -38,7 +38,7 @@ public class CreationPositionTracker extends AbstractProjectComponent { } public void addStack(@NotNull XDebugSession session, @NotNull ObjectReference ref, - @NotNull List stack) { + @NotNull List< StackFrameDescriptor> stack) { if (!mySession2Reference2Stack.containsKey(session)) { mySession2Reference2Stack.put(session, new ConcurrentHashMap<>()); session.addSessionListener(new XDebugSessionListener() { diff --git a/view/InstancesTree.java b/view/InstancesTree.java index d2b612ef672b..4a59e3e88b4f 100644 --- a/view/InstancesTree.java +++ b/view/InstancesTree.java @@ -1,7 +1,10 @@ package org.jetbrains.debugger.memory.view; +import com.intellij.openapi.actionSystem.DataKey; +import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.project.Project; import com.intellij.ui.SimpleTextAttributes; +import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; import com.intellij.xdebugger.frame.*; import com.intellij.xdebugger.impl.actions.XDebuggerActions; @@ -9,6 +12,7 @@ import com.intellij.xdebugger.impl.frame.XValueMarkers; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -16,18 +20,22 @@ import javax.swing.*; import java.util.ArrayList; import java.util.List; -class InstancesTree extends XDebuggerTree { +public class InstancesTree extends XDebuggerTree implements DataProvider { + public static final DataKey DEBUG_SESSION_DATA_KEY = DataKey.create("InstancesTree.DebugSession"); private final XValueNodeImpl myRoot; private final Runnable myOnRootExpandAction; + private final XDebugSession myDebugSession; private List myChildren; InstancesTree(@NotNull Project project, + @NotNull XDebugSession debugSession, @NotNull XDebuggerEditorsProvider editorsProvider, @Nullable XValueMarkers valueMarkers, @NotNull Runnable onRootExpand) { super(project, editorsProvider, null, XDebuggerActions.INSPECT_TREE_POPUP_GROUP, valueMarkers); myOnRootExpandAction = onRootExpand; myRoot = new XValueNodeImpl(this, null, "root", new MyRootValue()); + myDebugSession = debugSession; myRoot.children(); setRoot(myRoot, false); @@ -63,6 +71,16 @@ class InstancesTree extends XDebuggerTree { myRoot.setMessage(text, icon, textAttributes, null); } + @Nullable + @Override + public Object getData(@NonNls String dataId) { + if(DEBUG_SESSION_DATA_KEY.is(dataId)) { + return myDebugSession; + } + + return super.getData(dataId); + } + enum RebuildPolicy { RELOAD_INSTANCES, ONLY_UPDATE_LABELS } diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 6bc8ed60e9c8..6365c49db968 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -191,7 +191,7 @@ public class InstancesWindow extends DialogWrapper { myProgress.addStopActionListener(this::cancelFilteringTask); - myInstancesTree = new InstancesTree(myProject, editorsProvider, markers, this::updateInstances); + myInstancesTree = new InstancesTree(myProject, myDebugSession, editorsProvider, markers, this::updateInstances); myFilterButton.addActionListener(e -> { String expression = myFilterConditionEditor.getExpression().getExpression(); From b2f146d9bdccf8b551a6b4550c451f0df02c0db3 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 7 Oct 2016 19:08:29 +0300 Subject: [PATCH 111/558] Open tracked instances only by right mouse click --- view/ClassesTable.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 94bdd7ec401c..64ef97be29a6 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -66,7 +66,6 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private volatile List myElems = Collections.unmodifiableList(new ArrayList<>()); - // TODO: parent must be unknown in this context ClassesTable(@NotNull XDebugSession session, boolean onlyWithDiff, boolean onlyWithInstances, @NotNull ClassesFilteredView parent) { setModel(myModel); @@ -103,7 +102,8 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { - if (columnAtPoint(e.getPoint()) != DiffViewTableModel.DIFF_COLUMN_INDEX) { + if (columnAtPoint(e.getPoint()) != DiffViewTableModel.DIFF_COLUMN_INDEX || + e.getButton() != MouseEvent.BUTTON1) { return; } From 9f4524adcff51d3144a9872c459b83e93156695e Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 7 Oct 2016 21:47:23 +0300 Subject: [PATCH 112/558] Fix bug when classes table was unsorted after opened --- view/ClassesTable.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 64ef97be29a6..0e097cf2078f 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -214,6 +214,8 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { changeSelection(ix, DiffViewTableModel.CLASSNAME_COLUMN_INDEX, false, false); } + + getRowSorter().allRowsChanged(); } void hideContent() { From dbdb249ebe23d642ce17164df5b5b943c397a3e2 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Sun, 9 Oct 2016 20:42:51 +0300 Subject: [PATCH 113/558] Disallow tracking for arrays --- .../tracking/TrackInstanceCreationAction.java | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/action/tracking/TrackInstanceCreationAction.java b/action/tracking/TrackInstanceCreationAction.java index 810884643cc9..4268cd30e6cb 100644 --- a/action/tracking/TrackInstanceCreationAction.java +++ b/action/tracking/TrackInstanceCreationAction.java @@ -1,12 +1,41 @@ package org.jetbrains.debugger.memory.action.tracking; -import org.jetbrains.annotations.NotNull; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.Project; +import com.sun.jdi.ArrayType; +import com.sun.jdi.ReferenceType; +import org.jetbrains.debugger.memory.action.ClassesActionBase; +import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.tracking.TrackingType; -public class TrackInstanceCreationAction extends InstancesTrackingActionBase { - @NotNull +public class TrackInstanceCreationAction extends ClassesActionBase { @Override - protected TrackingType getType() { - return TrackingType.CREATION; + protected boolean isEnabled(AnActionEvent e) { + Project project = e.getProject(); + if (!super.isEnabled(e) || project == null) { + return false; + } + + InstancesTracker tracker = InstancesTracker.getInstance(project); + ReferenceType selectedClass = getSelectedClass(e); + if (selectedClass != null) { + TrackingType currentType = tracker.getTrackingType(selectedClass.name()); + if (TrackingType.CREATION == currentType) { + return false; + } + } + + return selectedClass != null && !(selectedClass instanceof ArrayType); + } + + @Override + protected void perform(AnActionEvent e) { + Project project = e.getProject(); + ReferenceType selectedClass = getSelectedClass(e); + + if (selectedClass != null && project != null) { + InstancesTracker tracker = InstancesTracker.getInstance(project); + tracker.add(selectedClass, TrackingType.CREATION); + } } } From e671bfa03c87f46cbf7517d0bead37b241963608 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Sun, 9 Oct 2016 21:54:53 +0300 Subject: [PATCH 114/558] Dont track new objects before the first pause of session --- tracking/ConstructorInstancesTracker.java | 54 ++++++++++++++--------- tracking/TrackerForNewInstances.java | 3 +- view/ClassesFilteredView.java | 35 ++++++++++++--- 3 files changed, 64 insertions(+), 28 deletions(-) diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index c8924f518569..9dfff777e59f 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -28,6 +28,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.CreationPositionTracker; import org.jetbrains.debugger.memory.component.InstancesTracker; +import org.jetbrains.debugger.memory.event.InstancesTrackerListener; import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; import org.jetbrains.java.debugger.breakpoints.properties.JavaLineBreakpointProperties; @@ -48,8 +49,8 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp @NotNull private HashSet myTrackedObjects = new HashSet<>(); - private boolean myIsBackgroundMode; - private boolean myIsBackgroundTrackingEnabled; + private volatile boolean myIsBackgroundMode; + private volatile boolean myIsBackgroundTrackingEnabled; public ConstructorInstancesTracker(@NotNull ReferenceType ref, @NotNull XDebugSession debugSession) { @@ -57,7 +58,28 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp myDebugSession = debugSession; myPositionTracker = CreationPositionTracker.getInstance(debugSession.getProject()); Project project = debugSession.getProject(); - myIsBackgroundTrackingEnabled = InstancesTracker.getInstance(myDebugSession.getProject()).isBackgroundTrackingEnabled(); + myIsBackgroundTrackingEnabled = InstancesTracker.getInstance(myDebugSession.getProject()) + .isBackgroundTrackingEnabled(); + + InstancesTracker.getInstance(myDebugSession.getProject()).addTrackerListener(new InstancesTrackerListener() { + @Override + public void backgroundTrackingValueChanged(boolean newState) { + if (myIsBackgroundTrackingEnabled != newState) { + myIsBackgroundTrackingEnabled = newState; + myDebugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { + @Override + protected void action() throws Exception { + if (newState) { + myBreakpoint.enable(); + } else { + myBreakpoint.disable(); + } + } + }); + } + } + }, this); + myDebugProcess = (DebugProcessImpl) DebuggerManager.getInstance(project) .getDebugProcess(debugSession.getDebugProcess().getProcessHandler()); JavaLineBreakpointType breakPointType = new JavaLineBreakpointType(); @@ -99,6 +121,14 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp return myNewObjects == null ? 0 : myNewObjects.size(); } + public void enable() { + myBreakpoint.enable(); + } + + public void disable() { + myBreakpoint.disable(); + } + @Override public boolean isReady() { return myNewObjects != null; @@ -109,23 +139,6 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp myBreakpoint.dispose(); } - @Override - public void backgroundTrackingValueChanged(boolean newValue) { - if (myIsBackgroundTrackingEnabled != newValue) { - myIsBackgroundTrackingEnabled = newValue; - myDebugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { - @Override - protected void action() throws Exception { - if (newValue) { - myBreakpoint.enable(); - } else { - myBreakpoint.disable(); - } - } - }); - } - } - @Override public void setBackgroundMode(boolean isBackgroundMode) { if (myIsBackgroundMode == isBackgroundMode) { @@ -243,4 +256,3 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp } } } - diff --git a/tracking/TrackerForNewInstances.java b/tracking/TrackerForNewInstances.java index 75a54b0b3985..b6fc329f5d36 100644 --- a/tracking/TrackerForNewInstances.java +++ b/tracking/TrackerForNewInstances.java @@ -2,11 +2,10 @@ package org.jetbrains.debugger.memory.tracking; import com.sun.jdi.ObjectReference; import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.event.InstancesTrackerListener; import java.util.List; -public interface TrackerForNewInstances extends InstancesTrackerListener { +public interface TrackerForNewInstances { @NotNull List getNewInstances(); diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 2be691d6b076..a1278365cda6 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -69,6 +69,16 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private volatile SuspendContextImpl myLastSuspendContext; + /** + * Indicates that the debug session had been stopped at least once. + * + * State: false -> true + */ + private volatile boolean myIsTrackersActivated = false; + + /** + * Indicates that view is visible in tool window + */ private boolean myIsActive; public ClassesFilteredView(@NotNull XDebugSession debugSession) { @@ -88,7 +98,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable .schedule(new LowestPriorityCommand(getSuspendContext()) { @Override public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { - trackClass(ref, type); + trackClass(ref, type, myIsTrackersActivated); } }); } @@ -96,7 +106,12 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void classRemoved(@NotNull String name) { - myTable.getRowSorter().allRowsChanged(); + ReferenceType ref = myTable.getClassByName(name); + if (ref != null && myConstructorTrackedClasses.containsKey(ref)) { + ConstructorInstancesTracker removed = myConstructorTrackedClasses.remove(ref); + removed.dispose(); + myTable.getRowSorter().allRowsChanged(); + } } }; @@ -116,12 +131,12 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable new ClassPreparedListener(className, myDebugSession) { @Override public void onClassPrepared(@NotNull ReferenceType referenceType) { - trackClass(referenceType, type); + trackClass(referenceType, type, myIsTrackersActivated); } }; } else { for (ReferenceType ref : classes) { - trackClass(ref, type); + trackClass(ref, type, myIsTrackersActivated); } } } @@ -215,6 +230,10 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable SwingUtilities.invokeLater(() -> myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_SUSPENDED)); commitAllTrackers(); updateClassesAndCounts(); + if(!myIsTrackersActivated) { + myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::enable); + myIsTrackersActivated = true; + } } }; @@ -250,7 +269,8 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } private void trackClass(@NotNull ReferenceType ref, - @NotNull TrackingType type) { + @NotNull TrackingType type, + boolean isTrackerEnabled) { LOG.assertTrue(DebuggerManager.getInstance(myProject).isDebuggerManagerThread()); if (type == TrackingType.CREATION) { ConstructorInstancesTracker old = myConstructorTrackedClasses.getOrDefault(ref, null); @@ -260,6 +280,11 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable ConstructorInstancesTracker tracker = new ConstructorInstancesTracker(ref, myDebugSession); tracker.setBackgroundMode(!myIsActive); + if (isTrackerEnabled) { + tracker.enable(); + } else { + tracker.disable(); + } Disposer.register(ClassesFilteredView.this, tracker); myConstructorTrackedClasses.put(ref, tracker); From 714bf6ccdaf487cc514fab21bc4b22740075fe74 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 14 Oct 2016 16:28:33 +0300 Subject: [PATCH 115/558] Add counts of instances to popup dialog in classes table for "Show instances" actions --- action/ShowInstancesAction.java | 17 +++++++ .../ShowInstancesFromClassesViewAction.java | 22 ++++++++- action/ShowNewInstancesAction.java | 22 ++++++++- view/ClassesTable.java | 48 +++++++++++++++---- 4 files changed, 97 insertions(+), 12 deletions(-) create mode 100644 action/ShowInstancesAction.java diff --git a/action/ShowInstancesAction.java b/action/ShowInstancesAction.java new file mode 100644 index 000000000000..400d194a1a01 --- /dev/null +++ b/action/ShowInstancesAction.java @@ -0,0 +1,17 @@ +package org.jetbrains.debugger.memory.action; + +import com.intellij.openapi.actionSystem.AnActionEvent; + +abstract class ShowInstancesAction extends ClassesActionBase { + @Override + public void update(AnActionEvent e) { + boolean enabled = isEnabled(e); + if(enabled) { + e.getPresentation().setText(String.format("%s (%d)", getLabel(), getInstancesCount(e))); + } + } + + protected abstract String getLabel(); + + protected abstract int getInstancesCount(AnActionEvent e); +} diff --git a/action/ShowInstancesFromClassesViewAction.java b/action/ShowInstancesFromClassesViewAction.java index a03c5ec1f288..040432aeb97a 100644 --- a/action/ShowInstancesFromClassesViewAction.java +++ b/action/ShowInstancesFromClassesViewAction.java @@ -3,12 +3,14 @@ package org.jetbrains.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; +import org.jetbrains.debugger.memory.view.ClassesTable; import org.jetbrains.debugger.memory.view.InstancesWindow; -public class ShowInstancesFromClassesViewAction extends ClassesActionBase { +public class ShowInstancesFromClassesViewAction extends ShowInstancesAction { + private static final String POPUP_ELEMENT_LABEL = "Show Instances"; @Override protected boolean isEnabled(AnActionEvent e) { - return super.isEnabled(e) && getSelectedClass(e) != null ; + return super.isEnabled(e) && getSelectedClass(e) != null; } @Override @@ -19,4 +21,20 @@ public class ShowInstancesFromClassesViewAction extends ClassesActionBase { new InstancesWindow(debugSession, selectedClass::instances, selectedClass.name()).show(); } } + + @Override + protected String getLabel() { + return POPUP_ELEMENT_LABEL; + } + + @Override + protected int getInstancesCount(AnActionEvent e) { + ClassesTable.ReferenceCountProvider countProvider = e.getData(ClassesTable.REF_COUNT_PROVIDER_KEY); + ReferenceType selectedClass = getSelectedClass(e); + if (countProvider == null || selectedClass == null) { + return -1; + } + + return countProvider.getTotalCount(selectedClass); + } } diff --git a/action/ShowNewInstancesAction.java b/action/ShowNewInstancesAction.java index 08ea5b76b6e3..cfa3922f4089 100644 --- a/action/ShowNewInstancesAction.java +++ b/action/ShowNewInstancesAction.java @@ -7,7 +7,9 @@ import org.jetbrains.debugger.memory.utils.InstancesProvider; import org.jetbrains.debugger.memory.view.ClassesTable; import org.jetbrains.debugger.memory.view.InstancesWindow; -public class ShowNewInstancesAction extends ClassesActionBase { +public class ShowNewInstancesAction extends ShowInstancesAction { + private static final String POPUP_ELEMENT_LABEL = "Show New Instances"; + @Override protected boolean isEnabled(AnActionEvent e) { XDebugSession session = getDebugSession(e); @@ -16,12 +18,28 @@ public class ShowNewInstancesAction extends ClassesActionBase { return super.isEnabled(e) && session != null && selectedClass != null && provider != null; } + @Override + protected String getLabel() { + return POPUP_ELEMENT_LABEL; + } + + @Override + protected int getInstancesCount(AnActionEvent e) { + ClassesTable.ReferenceCountProvider countProvider = e.getData(ClassesTable.REF_COUNT_PROVIDER_KEY); + ReferenceType selectedClass = getSelectedClass(e); + if (countProvider == null || selectedClass == null) { + return -1; + } + + return countProvider.getNewInstancesCount(selectedClass); + } + @Override protected void perform(AnActionEvent e) { XDebugSession session = getDebugSession(e); ReferenceType selectedClass = getSelectedClass(e); InstancesProvider provider = e.getData(ClassesTable.NEW_INSTANCES_PROVIDER_KEY); - if(selectedClass != null && provider != null && session != null) { + if (selectedClass != null && provider != null && session != null) { new InstancesWindow(session, provider, selectedClass.name()).show(); } } diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 0e097cf2078f..7e9efca938cc 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -36,7 +36,6 @@ import javax.swing.table.TableRowSorter; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.*; -import java.util.List; import java.util.concurrent.ConcurrentHashMap; public class ClassesTable extends JBTable implements DataProvider, Disposable { @@ -44,6 +43,8 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { public static final DataKey DEBUG_SESSION_KEY = DataKey.create("ClassesTable.DebugSession"); public static final DataKey NEW_INSTANCES_PROVIDER_KEY = DataKey.create("ClassesTable.NewInstances"); + public static final DataKey REF_COUNT_PROVIDER_KEY = + DataKey.create("ClassesTable.ReferenceCountProvider"); private static final int CLASSES_COLUMN_PREFERRED_WIDTH = 250; private static final int COUNT_COLUMN_MIN_WIDTH = 80; @@ -57,6 +58,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private final Map myCounts = new ConcurrentHashMap<>(); private final InstancesTracker myInstancesTracker; private final ClassesFilteredView myParent; + private final ReferenceCountProvider myCountProvider; private boolean myOnlyWithDiff; @@ -139,6 +141,32 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { sorter.setSortKeys(myDefaultSortingKeys); setRowSorter(sorter); setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + myCountProvider = new ReferenceCountProvider() { + @Override + public int getTotalCount(@NotNull ReferenceType ref) { + return (int) myCounts.get(ref).myCurrentCount; + } + + @Override + public int getDiffCount(@NotNull ReferenceType ref) { + return (int) myCounts.get(ref).diff(); + } + + @Override + public int getNewInstancesCount(@NotNull ReferenceType ref) { + TrackerForNewInstances strategy = myParent.getStrategy(ref); + return strategy == null || !strategy.isReady() ? -1 : strategy.getCount(); + } + }; + } + + public interface ReferenceCountProvider { + int getTotalCount(@NotNull ReferenceType ref); + + int getDiffCount(@NotNull ReferenceType ref); + + int getNewInstancesCount(@NotNull ReferenceType ref); } @Nullable @@ -226,6 +254,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { myModel.show(); } + @Nullable @Override public Object getData(@NonNls String dataId) { @@ -246,6 +275,10 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { } } + if (REF_COUNT_PROVIDER_KEY.is(dataId)) { + return myCountProvider; + } + return null; } @@ -437,22 +470,21 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { @Override protected void addText(JTable table, @NotNull Object value, boolean isSelected, boolean hasFocus, int row, int column) { - long diff = ((DiffValue) value).diff(); setTextAlign(SwingConstants.RIGHT); ReferenceType ref = myElems.get(convertRowIndexToModel(row)); - TrackerForNewInstances strategy = myParent.getStrategy(ref); + long diff = myCountProvider.getDiffCount(ref); String text = String.format("%s%d", diff > 0 ? "+" : "", diff); - if (strategy != null && strategy.isReady()) { - long trackedNew = strategy.getCount(); - if (trackedNew == diff) { + int newInstancesCount = myCountProvider.getNewInstancesCount(ref); + if (newInstancesCount >= 0) { + if (newInstancesCount == diff) { append(text, diff == 0 ? SimpleTextAttributes.REGULAR_ATTRIBUTES : myClickableCellAttributes); } else { append(text, SimpleTextAttributes.REGULAR_ATTRIBUTES); - if (trackedNew != 0) { - append(String.format(" (%d)", trackedNew), myClickableCellAttributes); + if (newInstancesCount != 0) { + append(String.format(" (%d)", newInstancesCount), myClickableCellAttributes); } } } else { From 182ad9c8485972b1d24ee46588e9929fc07ce27f Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 14 Oct 2016 18:18:26 +0300 Subject: [PATCH 116/558] Do not update class view if there are no steps while the tool window was closed --- view/ClassesFilteredView.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index a1278365cda6..7c47ddd3a721 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -230,7 +230,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable SwingUtilities.invokeLater(() -> myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_SUSPENDED)); commitAllTrackers(); updateClassesAndCounts(); - if(!myIsTrackersActivated) { + if (!myIsTrackersActivated) { myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::enable); myIsTrackersActivated = true; } @@ -345,8 +345,8 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private void doActivate() { myDebugSession.addSessionListener(myDebugSessionListener, ClassesFilteredView.this); - if (getSuspendContext() != null) { - myConstructorTrackedClasses.values().forEach(x -> x.setBackgroundMode(false)); + myConstructorTrackedClasses.values().forEach(x -> x.setBackgroundMode(false)); + if (!myLastSuspendContext.equals(getSuspendContext())) { commitAllTrackers(); updateClassesAndCounts(); } From ca81c0270bee1fdcc5dc590dca4b00b134e0827c Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 14 Oct 2016 19:07:30 +0300 Subject: [PATCH 117/558] Fix NPE when tool window has been closed before the starting of debug session --- view/ClassesFilteredView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 7c47ddd3a721..69074e5d1c6f 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -346,7 +346,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private void doActivate() { myDebugSession.addSessionListener(myDebugSessionListener, ClassesFilteredView.this); myConstructorTrackedClasses.values().forEach(x -> x.setBackgroundMode(false)); - if (!myLastSuspendContext.equals(getSuspendContext())) { + if (myLastSuspendContext == null || !myLastSuspendContext.equals(getSuspendContext())) { commitAllTrackers(); updateClassesAndCounts(); } From 492442445ac1b3fecf1b6807337d73e4bd42cd4b Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Thu, 20 Oct 2016 19:45:28 +0300 Subject: [PATCH 118/558] Show stackframe in instances window (only if it exists) for selected instance --- view/InstancesTree.java | 33 ++++++- view/InstancesWindow.java | 17 +++- view/InstancesWithStackFrameView.java | 68 +++++++++++++ view/StackFrameList.java | 136 ++++++++++++++++++++++++++ view/StackFramePopup.java | 83 ++-------------- 5 files changed, 255 insertions(+), 82 deletions(-) create mode 100644 view/InstancesWithStackFrameView.java create mode 100644 view/StackFrameList.java diff --git a/view/InstancesTree.java b/view/InstancesTree.java index 4a59e3e88b4f..6352ffe02c9a 100644 --- a/view/InstancesTree.java +++ b/view/InstancesTree.java @@ -1,7 +1,9 @@ package org.jetbrains.debugger.memory.view; +import com.intellij.debugger.ui.impl.watch.NodeDescriptorProvider; +import com.intellij.debugger.ui.tree.NodeDescriptor; +import com.intellij.debugger.ui.tree.ValueDescriptor; import com.intellij.openapi.actionSystem.DataKey; -import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.project.Project; import com.intellij.ui.SimpleTextAttributes; import com.intellij.xdebugger.XDebugSession; @@ -12,15 +14,18 @@ import com.intellij.xdebugger.impl.frame.XValueMarkers; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.Value; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import javax.swing.tree.TreePath; import java.util.ArrayList; import java.util.List; -public class InstancesTree extends XDebuggerTree implements DataProvider { +public class InstancesTree extends XDebuggerTree { public static final DataKey DEBUG_SESSION_DATA_KEY = DataKey.create("InstancesTree.DebugSession"); private final XValueNodeImpl myRoot; private final Runnable myOnRootExpandAction; @@ -71,10 +76,32 @@ public class InstancesTree extends XDebuggerTree implements DataProvider { myRoot.setMessage(text, icon, textAttributes, null); } + @Nullable + ObjectReference getSelectedReference() { + TreePath selectionPath = getSelectionPath(); + Object selectedItem = selectionPath != null ? selectionPath.getLastPathComponent() : null; + if (selectedItem instanceof XValueNodeImpl) { + XValueNodeImpl xValueNode = (XValueNodeImpl) selectedItem; + XValue valueContainer = xValueNode.getValueContainer(); + + if (valueContainer instanceof NodeDescriptorProvider) { + NodeDescriptor descriptor = ((NodeDescriptorProvider) valueContainer).getDescriptor(); + + if (descriptor instanceof ValueDescriptor) { + Value value = ((ValueDescriptor) descriptor).getValue(); + + if (value instanceof ObjectReference) return (ObjectReference) value; + } + } + } + + return null; + } + @Nullable @Override public Object getData(@NonNls String dataId) { - if(DEBUG_SESSION_DATA_KEY.is(dataId)) { + if (DEBUG_SESSION_DATA_KEY.is(dataId)) { return myDebugSession; } diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 6365c49db968..7ee7d765021d 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -23,11 +23,11 @@ import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.openapi.wm.IdeFocusManager; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.JBColor; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBPanel; -import com.intellij.ui.components.JBScrollPane; import com.intellij.util.ui.JBDimension; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; @@ -57,6 +57,7 @@ import org.jetbrains.java.debugger.JavaDebuggerEditorsProvider; import javax.swing.*; import javax.swing.tree.TreeNode; import java.awt.*; +import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -203,9 +204,18 @@ public class InstancesWindow extends DialogWrapper { myInstancesTree.rebuildTree(InstancesTree.RebuildPolicy.RELOAD_INSTANCES); }); - JBScrollPane treeScrollPane = new JBScrollPane(myInstancesTree); + + StackFrameList list = new StackFrameList(myProject, + Collections.emptyList(), + GlobalSearchScope.allScope(myProject)); + + list.addListSelectionListener(e -> list.navigateToSelectedValue(false)); + + InstancesWithStackFrameView instancesWithStackFrame = new InstancesWithStackFrameView(myDebugSession, + myInstancesTree, list); + add(filteringPane, BorderLayout.NORTH); - add(treeScrollPane, BorderLayout.CENTER); + add(instancesWithStackFrame.getComponent(), BorderLayout.CENTER); JComponent focusedComponent = myFilterConditionEditor.getEditorComponent(); UiNotifyConnector.doWhenFirstShown(focusedComponent, () -> @@ -268,6 +278,7 @@ public class InstancesWindow extends DialogWrapper { XExpression expression = myFilterConditionEditor.getExpression(); if (expression != null && !expression.getExpression().isEmpty()) { try { + myEvaluator.setReferenceExpression(TextWithImportsImpl.fromXExpression(expression)); myEvaluator.setReferenceExpression(TextWithImportsImpl. fromXExpression(expression)); evaluator = myEvaluator.getEvaluator(); diff --git a/view/InstancesWithStackFrameView.java b/view/InstancesWithStackFrameView.java new file mode 100644 index 000000000000..ec3609119b3b --- /dev/null +++ b/view/InstancesWithStackFrameView.java @@ -0,0 +1,68 @@ +package org.jetbrains.debugger.memory.view; + +import com.intellij.ui.JBSplitter; +import com.intellij.ui.components.JBScrollPane; +import com.intellij.xdebugger.XDebugSession; +import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.debugger.memory.component.CreationPositionTracker; +import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; + +import javax.swing.*; +import java.util.Collections; +import java.util.List; + +class InstancesWithStackFrameView { + private static final float DEFAULT_SPLITTER_PROPORTION = 0.7f; + private static final List EMPTY_FRAME = Collections.emptyList(); + + private float myHidedProportion; + + private final JBSplitter mySplitter = new JBSplitter(false, DEFAULT_SPLITTER_PROPORTION); + private boolean myIsHided = false; + + InstancesWithStackFrameView(@NotNull XDebugSession debugSession, @NotNull InstancesTree tree, + @NotNull StackFrameList list) { + mySplitter.setFirstComponent(new JBScrollPane(tree)); + mySplitter.setSecondComponent(new JBScrollPane(list)); + myHidedProportion = DEFAULT_SPLITTER_PROPORTION; + + hideStackFrame(); + + tree.addTreeSelectionListener(e -> { + ObjectReference ref = tree.getSelectedReference(); + if (ref != null) { + List stack = CreationPositionTracker.getInstance(debugSession.getProject()) + .getStack(debugSession, ref); + if (stack != null) { + list.setFrame(stack); + showStackFrame(); + } else { + list.setFrame(EMPTY_FRAME); + hideStackFrame(); + } + } + }); + } + + JComponent getComponent() { + return mySplitter; + } + + private void hideStackFrame() { + if (!myIsHided) { + myHidedProportion = mySplitter.getProportion(); + mySplitter.getSecondComponent().setVisible(false); + mySplitter.setProportion(1.f); + myIsHided = true; + } + } + + private void showStackFrame() { + if(myIsHided) { + mySplitter.getSecondComponent().setVisible(true); + mySplitter.setProportion(myHidedProportion); + myIsHided = false; + } + } +} diff --git a/view/StackFrameList.java b/view/StackFrameList.java new file mode 100644 index 000000000000..28f1914936ca --- /dev/null +++ b/view/StackFrameList.java @@ -0,0 +1,136 @@ +package org.jetbrains.debugger.memory.view; + +import com.intellij.debugger.engine.DebuggerUtils; +import com.intellij.execution.filters.OpenFileHyperlinkInfo; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.ScrollType; +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.OpenFileDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.PsiUtilCore; +import com.intellij.ui.ColoredListCellRenderer; +import com.intellij.ui.SimpleTextAttributes; +import com.intellij.ui.components.JBList; +import org.apache.commons.lang.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.List; + +class StackFrameList extends JBList { + private static final char ANONYMOUS_CLASS_DELIMITER = '$'; + private static final MyOpenFilesState myEditorState = new MyOpenFilesState(); + + private List myStackFrames; + private final Project myProject; + private final GlobalSearchScope myScope; + private final MyListModel myModel = new MyListModel(); + + StackFrameList(@NotNull Project project, + @NotNull List stack, + @NotNull GlobalSearchScope searchScope) { + super(); + + + myStackFrames = new ArrayList<>(stack); + myProject = project; + myScope = searchScope; + + setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + //noinspection unchecked + setModel(myModel); + + setCellRenderer(new ColoredListCellRenderer() { + @Override + protected void customizeCellRenderer(@NotNull JList list, + StackFrameDescriptor value, int index, boolean isSelected, boolean hasFocus) { + append(String.format("%s:%d, %s", value.methodName(), value.line(), value.className())); + String packageName = value.packageName(); + if (!StringUtils.isEmpty(packageName)) { + append(String.format(" (%s)", value.packageName()), SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES); + } + } + }); + } + + void setFrame(@NotNull List stack) { + myModel.update(stack); + } + + void navigateToSelectedValue(boolean focusOnEditor) { + StackFrameDescriptor selectedValue = (StackFrameDescriptor) getSelectedValue(); + if (selectedValue != null) { + navigateToFrame(selectedValue, focusOnEditor); + } + } + + private void navigateToFrame(@NotNull StackFrameDescriptor frame, boolean focusOnEditor) { + String path = frame.path(); + int anonymousClassDelimiterIndex = path.indexOf(ANONYMOUS_CLASS_DELIMITER); + int pathLength = anonymousClassDelimiterIndex > 0 ? anonymousClassDelimiterIndex : path.length(); + path = path.substring(0, pathLength); + PsiClass psiClass = DebuggerUtils.findClass(path, myProject, myScope); + if (psiClass != null) { + ApplicationManager.getApplication().runReadAction(() -> { + PsiElement navigationElement = psiClass.getNavigationElement(); + VirtualFile file = PsiUtilCore.getVirtualFile(navigationElement); + + if (file == null) { + file = psiClass.getContainingFile().getVirtualFile(); + } + + OpenFileHyperlinkInfo info = + new OpenFileHyperlinkInfo(myProject, file, frame.line() - 1); + OpenFileDescriptor descriptor = info.getDescriptor(); + if (descriptor != null) { + FileEditorManager manager = FileEditorManager.getInstance(myProject); + if (myEditorState.myIsNeedToCloseLastOpenedFile && myEditorState.myLastOpenedFile != null && + manager.isFileOpen(myEditorState.myLastOpenedFile)) { + manager.closeFile(myEditorState.myLastOpenedFile); + } + + descriptor.setUseCurrentWindow(false); + descriptor.setScrollType(ScrollType.CENTER); + + myEditorState.myIsNeedToCloseLastOpenedFile = !manager.isFileOpen(descriptor.getFile()); + List fileEditors = manager.openEditor(descriptor, focusOnEditor); + if (!fileEditors.isEmpty()) { + myEditorState.myLastOpenedFile = descriptor.getFile(); + } + } + }); + } + } + + private static class MyOpenFilesState { + VirtualFile myLastOpenedFile; + boolean myIsNeedToCloseLastOpenedFile; + } + + private class MyListModel extends AbstractListModel { + + public void update(@NotNull List newFrame) { + fireIntervalRemoved(this, 0, getSize()); + myStackFrames = newFrame; + fireIntervalAdded(this, 0, getSize()); + } + + @Override + public int getSize() { + return myStackFrames.size(); + } + + @Override + public StackFrameDescriptor getElementAt(int index) { + return myStackFrames.get(index); + } + } +} diff --git a/view/StackFramePopup.java b/view/StackFramePopup.java index 08c68924676f..0069d0500152 100644 --- a/view/StackFramePopup.java +++ b/view/StackFramePopup.java @@ -1,31 +1,15 @@ package org.jetbrains.debugger.memory.view; -import com.intellij.debugger.engine.DebuggerUtils; -import com.intellij.execution.filters.OpenFileHyperlinkInfo; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiElement; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.util.PsiUtilCore; -import com.intellij.ui.ColoredListCellRenderer; -import com.intellij.ui.SimpleTextAttributes; -import com.intellij.ui.components.JBList; -import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; -import javax.swing.*; import java.util.List; public class StackFramePopup { - private static final char ANONYMOUS_CLASS_DELIMITER = '$'; - private final Project myProject; private final List myStackFrame; private final GlobalSearchScope myScope; @@ -39,74 +23,21 @@ public class StackFramePopup { } public void show() { - JBList list = createStackList(); + StackFrameList list = new StackFrameList(myProject, myStackFrame, myScope); + list.addListSelectionListener(e -> { + if (!e.getValueIsAdjusting()) { + list.navigateToSelectedValue(false); + } + }); JBPopup popup = JBPopupFactory.getInstance().createListPopupBuilder(list) .setTitle("Select stack frame") .setAutoSelectIfEmpty(true) .setResizable(false) - .setItemChoosenCallback(() -> navigateToSelectedFrame(list, true)) + .setItemChoosenCallback(() -> list.navigateToSelectedValue(true)) .createPopup(); list.setSelectedIndex(1); popup.showInFocusCenter(); } - - private JBList createStackList() { - JBList frameList = new JBList(new AbstractListModel() { - @Override - public int getSize() { - return myStackFrame.size(); - } - - @Override - public Object getElementAt(int index) { - return myStackFrame.get(index); - } - }); - - frameList.setCellRenderer(new ColoredListCellRenderer() { - @Override - protected void customizeCellRenderer(@NotNull JList list, - StackFrameDescriptor value, int index, boolean isSelected, boolean hasFocus) { - append(String.format("%s:%d, %s", value.methodName(), value.line(), value.className())); - String packageName = value.packageName(); - if (!StringUtils.isEmpty(packageName)) { - append(String.format(" (%s)", value.packageName()), SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES); - } - } - }); - - frameList.addListSelectionListener(e -> navigateToSelectedFrame(frameList, false)); - - return frameList; - } - - private void navigateToSelectedFrame(@NotNull JBList frameList, boolean focusEditor) { - StackFrameDescriptor frame = (StackFrameDescriptor) frameList.getSelectedValue(); - if (frame != null) { - String path = frame.path(); - int anonymousClassDelimiterIndex = path.indexOf(ANONYMOUS_CLASS_DELIMITER); - int pathLength = anonymousClassDelimiterIndex > 0 ? anonymousClassDelimiterIndex : path.length(); - path = path.substring(0, pathLength); - PsiClass psiClass = DebuggerUtils.findClass(path, myProject, myScope); - if (psiClass != null) { - ApplicationManager.getApplication().runReadAction(() -> { - PsiElement navigationElement = psiClass.getNavigationElement(); - VirtualFile file = PsiUtilCore.getVirtualFile(navigationElement); - if (file == null) { - file = psiClass.getContainingFile().getVirtualFile(); - } - - OpenFileHyperlinkInfo info = - new OpenFileHyperlinkInfo(myProject, file, frame.line() - 1); - OpenFileDescriptor descriptor = info.getDescriptor(); - if (descriptor != null) { - descriptor.setUseCurrentWindow(true); - FileEditorManager.getInstance(myProject).openEditor(descriptor, focusEditor); - } - }); - } - } - } } From feac108339b597ec08a7ef56f196a70a31df2b54 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Thu, 20 Oct 2016 19:57:45 +0300 Subject: [PATCH 119/558] Increase the default width for instances window --- view/InstancesWindow.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 7ee7d765021d..1ff270b0b1c1 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -64,7 +64,7 @@ import java.util.concurrent.atomic.AtomicInteger; import static java.lang.Math.min; public class InstancesWindow extends DialogWrapper { - private static final int DEFAULT_WINDOW_WIDTH = 700; + private static final int DEFAULT_WINDOW_WIDTH = 870; private static final int DEFAULT_WINDOW_HEIGHT = 400; private static final int FILTERING_BUTTON_ADDITIONAL_WIDTH = 30; private static final int BORDER_LAYOUT_DEFAULT_GAP = 5; @@ -108,11 +108,6 @@ public class InstancesWindow extends DialogWrapper { setTitle(String.format("Instances of %s%s", myClassName, warning)); } - @NotNull - @Override - protected String getDimensionServiceKey() { - return "#org.jetbrains.debugger.memory.view.InstancesWindow"; - } @Nullable @Override From 76f7150850e08164faf426bb8439ee9eac97fb10 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Thu, 20 Oct 2016 20:43:44 +0300 Subject: [PATCH 120/558] Do not reopen file during navigation by stack frame --- view/StackFrameList.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/view/StackFrameList.java b/view/StackFrameList.java index 28f1914936ca..91919a698ac5 100644 --- a/view/StackFrameList.java +++ b/view/StackFrameList.java @@ -92,15 +92,18 @@ class StackFrameList extends JBList { OpenFileDescriptor descriptor = info.getDescriptor(); if (descriptor != null) { FileEditorManager manager = FileEditorManager.getInstance(myProject); - if (myEditorState.myIsNeedToCloseLastOpenedFile && myEditorState.myLastOpenedFile != null && - manager.isFileOpen(myEditorState.myLastOpenedFile)) { + VirtualFile lastFile = myEditorState.myLastOpenedFile; + if (myEditorState.myIsNeedToCloseLastOpenedFile && lastFile != null && + manager.isFileOpen(lastFile) && !lastFile.equals(descriptor.getFile())) { manager.closeFile(myEditorState.myLastOpenedFile); } - descriptor.setUseCurrentWindow(false); descriptor.setScrollType(ScrollType.CENTER); - myEditorState.myIsNeedToCloseLastOpenedFile = !manager.isFileOpen(descriptor.getFile()); + if (lastFile == null || !lastFile.equals(descriptor.getFile())) { + myEditorState.myIsNeedToCloseLastOpenedFile = !manager.isFileOpen(descriptor.getFile()); + } + List fileEditors = manager.openEditor(descriptor, focusOnEditor); if (!fileEditors.isEmpty()) { myEditorState.myLastOpenedFile = descriptor.getFile(); From 8f7980b693d5adeadc5b1c4477ac10182f80497b Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Thu, 20 Oct 2016 20:48:25 +0300 Subject: [PATCH 121/558] Restore persistent size of InstancesWindow --- view/InstancesWindow.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 1ff270b0b1c1..01bef3327de8 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -108,6 +108,11 @@ public class InstancesWindow extends DialogWrapper { setTitle(String.format("Instances of %s%s", myClassName, warning)); } + @NotNull + @Override + protected String getDimensionServiceKey() { + return "#org.jetbrains.debugger.memory.view.InstancesWindow"; + } @Nullable @Override From 11d36214a6d6dded22bcd46f5f95f6dc6ebbf1c6 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 21 Oct 2016 20:49:51 +0300 Subject: [PATCH 122/558] Fix class cast exception when columns of classes table were exchanged. Open only window with new instances (instead of three) on double click on diff column. Make 'count' and 'diff' columns resizeable --- view/ClassesFilteredView.java | 40 +++++++++++++++++++++++++++++--- view/ClassesTable.java | 43 ++++++----------------------------- 2 files changed, 44 insertions(+), 39 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 69074e5d1c6f..99e0e5ff1637 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -17,10 +17,12 @@ import com.intellij.util.SmartList; import com.intellij.util.ui.components.BorderLayoutPanel; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; +import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import com.sun.jdi.VirtualMachine; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.component.CreationPositionTracker; import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.component.MemoryViewManager; import org.jetbrains.debugger.memory.component.MemoryViewManagerState; @@ -41,12 +43,14 @@ import javax.swing.event.DocumentEvent; import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; +import java.util.function.Function; import java.util.stream.IntStream; public class ClassesFilteredView extends BorderLayoutPanel implements Disposable { @@ -71,7 +75,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable /** * Indicates that the debug session had been stopped at least once. - * + *

* State: false -> true */ private volatile boolean myIsTrackersActivated = false; @@ -194,14 +198,44 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } }); + Function isOpenNewInstancesClick = e -> { + int column = myTable.convertColumnIndexToModel(myTable.columnAtPoint(e.getPoint())); + ReferenceType ref = myTable.getSelectedClass(); + return column == ClassesTable.DiffViewTableModel.DIFF_COLUMN_INDEX && ref != null && getStrategy(ref) != null; + }; + new DoubleClickListener() { @Override protected boolean onDoubleClick(MouseEvent event) { - handleClassSelection(myTable.getSelectedClass()); - return true; + if (!isOpenNewInstancesClick.apply(event)) { + handleClassSelection(myTable.getSelectedClass()); + return true; + } + + return false; } }.installOn(myTable); + myTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() != 1 || !isOpenNewInstancesClick.apply(e) || e.getButton() != MouseEvent.BUTTON1) { + return; + } + + ReferenceType ref = myTable.getSelectedClass(); + TrackerForNewInstances strategy = ref == null ? null : getStrategy(ref); + if (strategy != null && strategy.isReady() && strategy.getCount() > 0) { + List newInstances = strategy.getNewInstances(); + CreationPositionTracker.getInstance(myDebugSession.getProject()).pinStacks(myDebugSession, ref); + InstancesWindow instancesWindow = new InstancesWindow(myDebugSession, limit -> newInstances, ref.name()); + Disposer.register(instancesWindow.getDisposable(), + () -> CreationPositionTracker.getInstance(myDebugSession.getProject()).unpinStacks(myDebugSession, ref)); + instancesWindow.show(); + } + } + }); + final MemoryViewManagerListener memoryViewManagerListener = state -> { myTable.setFilteringByDiffNonZero(state.isShowWithDiffOnly); myTable.setFilteringByInstanceExists(state.isShowWithInstancesOnly); diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 7e9efca938cc..ea18ffe6f4b3 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -3,7 +3,6 @@ package org.jetbrains.debugger.memory.view; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.DataKey; import com.intellij.openapi.actionSystem.DataProvider; -import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.TextRange; import com.intellij.psi.codeStyle.MinusculeMatcher; import com.intellij.psi.codeStyle.NameUtil; @@ -22,7 +21,6 @@ import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.component.CreationPositionTracker; import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.tracking.TrackerForNewInstances; import org.jetbrains.debugger.memory.tracking.TrackingType; @@ -33,8 +31,6 @@ import org.jetbrains.debugger.memory.utils.InstancesProvider; import javax.swing.*; import javax.swing.table.TableColumn; import javax.swing.table.TableRowSorter; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -48,9 +44,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private static final int CLASSES_COLUMN_PREFERRED_WIDTH = 250; private static final int COUNT_COLUMN_MIN_WIDTH = 80; - private static final int COUNT_COLUMN_MAX_WIDTH = 100; private static final int DIFF_COLUMN_MIN_WIDTH = 80; - private static final int DIFF_COLUMN_MAX_WIDTH = 100; private final DiffViewTableModel myModel = new DiffViewTableModel(); private final UnknownDiffValue myUnknownValue = new UnknownDiffValue(); @@ -82,17 +76,12 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { TableColumn countColumn = getColumnModel().getColumn(DiffViewTableModel.COUNT_COLUMN_INDEX); TableColumn diffColumn = getColumnModel().getColumn(DiffViewTableModel.DIFF_COLUMN_INDEX); - setAutoResizeMode(AUTO_RESIZE_ALL_COLUMNS); + setAutoResizeMode(AUTO_RESIZE_NEXT_COLUMN); classesColumn.setPreferredWidth(JBUI.scale(CLASSES_COLUMN_PREFERRED_WIDTH)); - classesColumn.setResizable(false); countColumn.setMinWidth(JBUI.scale(COUNT_COLUMN_MIN_WIDTH)); - countColumn.setMaxWidth(JBUI.scale(COUNT_COLUMN_MAX_WIDTH)); - countColumn.setResizable(false); diffColumn.setMinWidth(JBUI.scale(DIFF_COLUMN_MIN_WIDTH)); - diffColumn.setMaxWidth(JBUI.scale(DIFF_COLUMN_MAX_WIDTH)); - diffColumn.setResizable(false); setShowGrid(false); setIntercellSpacing(new JBDimension(0, 0)); @@ -101,26 +90,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { setDefaultRenderer(Long.class, new MyCountColumnRenderer()); setDefaultRenderer(DiffValue.class, new MyDiffColumnRenderer()); - addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (columnAtPoint(e.getPoint()) != DiffViewTableModel.DIFF_COLUMN_INDEX || - e.getButton() != MouseEvent.BUTTON1) { - return; - } - ReferenceType ref = myElems.get(convertRowIndexToModel(rowAtPoint(e.getPoint()))); - TrackerForNewInstances strategy = myParent.getStrategy(ref); - if (strategy != null && strategy.isReady() && strategy.getCount() > 0) { - List newInstances = strategy.getNewInstances(); - CreationPositionTracker.getInstance(myDebugSession.getProject()).pinStacks(myDebugSession, ref); - InstancesWindow instancesWindow = new InstancesWindow(myDebugSession, limit -> newInstances, ref.name()); - Disposer.register(instancesWindow.getDisposable(), - () -> CreationPositionTracker.getInstance(myDebugSession.getProject()).unpinStacks(myDebugSession, ref)); - instancesWindow.show(); - } - } - }); TableRowSorter sorter = new TableRowSorter<>(myModel); sorter.setRowFilter(new RowFilter() { @Override @@ -288,14 +258,14 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { @Nullable private TrackingType getTrackingType(int row) { - ReferenceType ref = (ReferenceType) getValueAt(row, DiffViewTableModel.CLASSNAME_COLUMN_INDEX); + ReferenceType ref = (ReferenceType) getValueAt(row, convertColumnIndexToView(DiffViewTableModel.CLASSNAME_COLUMN_INDEX)); return myInstancesTracker.getTrackingType(ref.name()); } - private class DiffViewTableModel extends AbstractTableModelWithColumns { - private final static int CLASSNAME_COLUMN_INDEX = 0; - private final static int COUNT_COLUMN_INDEX = 1; - private final static int DIFF_COLUMN_INDEX = 2; + class DiffViewTableModel extends AbstractTableModelWithColumns { + final static int CLASSNAME_COLUMN_INDEX = 0; + final static int COUNT_COLUMN_INDEX = 1; + final static int DIFF_COLUMN_INDEX = 2; // Workaround: save selection after content of classes table has been hided private ReferenceType mySelectedClassWhenHided = null; @@ -417,6 +387,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { @Override protected void customizeCellRenderer(JTable table, @Nullable Object value, boolean isSelected, boolean hasFocus, int row, int column) { + column = convertColumnIndexToModel(column); TrackingType trackingType = getTrackingType(row); if (trackingType != null && !isSelected) { JBColor color = myParent.isTrackingActive(myElems.get(convertRowIndexToModel(row))) From f763c49fa50869771fcc5f32c10f2d5a735294f2 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 21 Oct 2016 20:58:12 +0300 Subject: [PATCH 123/558] Add icons for marking of tracked classes --- view/ClassesTable.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index ea18ffe6f4b3..5406930fe29d 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -14,10 +14,10 @@ import com.intellij.ui.table.JBTable; import com.intellij.util.containers.FList; import com.intellij.util.ui.JBDimension; import com.intellij.util.ui.JBUI; -import com.intellij.util.ui.UIUtil; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; +import icons.MemoryViewIcons; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -389,11 +389,10 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { boolean hasFocus, int row, int column) { column = convertColumnIndexToModel(column); TrackingType trackingType = getTrackingType(row); - if (trackingType != null && !isSelected) { - JBColor color = myParent.isTrackingActive(myElems.get(convertRowIndexToModel(row))) - ? trackingType.color() - : TrackingType.inactiveColor(); - setBackground(UIUtil.toAlpha(color, 20)); + + if (trackingType != null && column == DiffViewTableModel.DIFF_COLUMN_INDEX) { + setIcon(MemoryViewIcons.CLASS_TRACKED); + setTransparentIconBackground(true); } if (value != null) { From 8b7c8cd7d07a7d5747939469a0cc6f4bdaa578bc Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Wed, 26 Oct 2016 09:39:19 +0300 Subject: [PATCH 124/558] Fix bug with using disposed project --- component/CreationPositionTracker.java | 3 ++- view/ClassesFilteredView.java | 19 +++++++------------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/component/CreationPositionTracker.java b/component/CreationPositionTracker.java index 7b2937adc1fb..142d0e1f164f 100644 --- a/component/CreationPositionTracker.java +++ b/component/CreationPositionTracker.java @@ -27,8 +27,9 @@ public class CreationPositionTracker extends AbstractProjectComponent { super(project); } + @Nullable public static CreationPositionTracker getInstance(@NotNull Project project) { - return project.getComponent(CreationPositionTracker.class); + return project.isDisposed() ? null : project.getComponent(CreationPositionTracker.class); } @Nullable diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 99e0e5ff1637..92887d2c5fb2 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -57,7 +57,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private static final Logger LOG = Logger.getInstance(ClassesFilteredView.class); private final static double DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT = 0.5; private final static int DEFAULT_BATCH_SIZE = Integer.MAX_VALUE; - private static final String EMPTY_TABLE_CONTENT_WHEN_RUNNED = "The application is running"; + private static final String EMPTY_TABLE_CONTENT_WHEN_RUNNING = "The application is running"; private static final String EMPTY_TABLE_CONTENT_WHEN_SUSPENDED = "Nothing to show"; private final Project myProject; @@ -153,7 +153,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myTable = new ClassesTable(myDebugSession, memoryViewManagerState.isShowWithDiffOnly, memoryViewManagerState.isShowWithInstancesOnly, this); - myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNED); + myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNING); Disposer.register(this, myTable); myTable.addKeyListener(new KeyAdapter() { @@ -225,12 +225,12 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable ReferenceType ref = myTable.getSelectedClass(); TrackerForNewInstances strategy = ref == null ? null : getStrategy(ref); - if (strategy != null && strategy.isReady() && strategy.getCount() > 0) { + final CreationPositionTracker tracker = CreationPositionTracker.getInstance(myDebugSession.getProject()); + if (strategy != null && strategy.isReady() && strategy.getCount() > 0 && tracker != null) { List newInstances = strategy.getNewInstances(); - CreationPositionTracker.getInstance(myDebugSession.getProject()).pinStacks(myDebugSession, ref); + tracker.pinStacks(myDebugSession, ref); InstancesWindow instancesWindow = new InstancesWindow(myDebugSession, limit -> newInstances, ref.name()); - Disposer.register(instancesWindow.getDisposable(), - () -> CreationPositionTracker.getInstance(myDebugSession.getProject()).unpinStacks(myDebugSession, ref)); + Disposer.register(instancesWindow.getDisposable(), () -> tracker.unpinStacks(myDebugSession, ref)); instancesWindow.show(); } } @@ -248,7 +248,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable public void sessionResumed() { myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::obsolete); SwingUtilities.invokeLater(() -> { - myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNED); + myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNING); myTable.hideContent(); }); mySingleAlarm.cancelAllRequests(); @@ -350,11 +350,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable return ActionManager.getInstance().createActionPopupMenu("ClassesView.PopupActionGroup", group); } - boolean isTrackingActive(@NotNull ReferenceType ref) { - TrackerForNewInstances strategy = myConstructorTrackedClasses.getOrDefault(ref, null); - return strategy != null && strategy.isReady(); - } - @Override public void dispose() { } From 7ac870c8f42133db94ecb17436a870ef7fc0be8c Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 26 Oct 2016 15:28:01 +0300 Subject: [PATCH 125/558] Close the stack frames when selection was lost --- view/InstancesWithStackFrameView.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/view/InstancesWithStackFrameView.java b/view/InstancesWithStackFrameView.java index ec3609119b3b..8c3c46fb48d8 100644 --- a/view/InstancesWithStackFrameView.java +++ b/view/InstancesWithStackFrameView.java @@ -29,19 +29,20 @@ class InstancesWithStackFrameView { hideStackFrame(); + CreationPositionTracker tracker = CreationPositionTracker.getInstance(debugSession.getProject()); tree.addTreeSelectionListener(e -> { ObjectReference ref = tree.getSelectedReference(); - if (ref != null) { - List stack = CreationPositionTracker.getInstance(debugSession.getProject()) - .getStack(debugSession, ref); + if (ref != null && tracker != null) { + List stack = tracker.getStack(debugSession, ref); if (stack != null) { list.setFrame(stack); showStackFrame(); - } else { - list.setFrame(EMPTY_FRAME); - hideStackFrame(); + return; } } + + list.setFrame(EMPTY_FRAME); + hideStackFrame(); }); } @@ -59,7 +60,7 @@ class InstancesWithStackFrameView { } private void showStackFrame() { - if(myIsHided) { + if (myIsHided) { mySplitter.getSecondComponent().setVisible(true); mySplitter.setProportion(myHidedProportion); myIsHided = false; From f72b48927f11d91c4eb95b33d295de56fe9fefdf Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 26 Oct 2016 15:51:15 +0300 Subject: [PATCH 126/558] Add ability to hide stack frames --- view/InstancesWithStackFrameView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/view/InstancesWithStackFrameView.java b/view/InstancesWithStackFrameView.java index 8c3c46fb48d8..f5758e70f5eb 100644 --- a/view/InstancesWithStackFrameView.java +++ b/view/InstancesWithStackFrameView.java @@ -25,6 +25,7 @@ class InstancesWithStackFrameView { @NotNull StackFrameList list) { mySplitter.setFirstComponent(new JBScrollPane(tree)); mySplitter.setSecondComponent(new JBScrollPane(list)); + mySplitter.setHonorComponentsMinimumSize(false); myHidedProportion = DEFAULT_SPLITTER_PROPORTION; hideStackFrame(); From e966aeb6f9403ce359477e3938abbcc0df774d26 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 26 Oct 2016 16:10:23 +0300 Subject: [PATCH 127/558] Move focus to the editor after a double click on an element of the stack trace --- view/InstancesWindow.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 01bef3327de8..a7ddc6385eaf 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -24,6 +24,7 @@ import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.ui.DoubleClickListener; import com.intellij.ui.JBColor; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBLabel; @@ -57,6 +58,7 @@ import org.jetbrains.java.debugger.JavaDebuggerEditorsProvider; import javax.swing.*; import javax.swing.tree.TreeNode; import java.awt.*; +import java.awt.event.MouseEvent; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -210,6 +212,13 @@ public class InstancesWindow extends DialogWrapper { GlobalSearchScope.allScope(myProject)); list.addListSelectionListener(e -> list.navigateToSelectedValue(false)); + new DoubleClickListener() { + @Override + protected boolean onDoubleClick(MouseEvent event) { + list.navigateToSelectedValue(true); + return true; + } + }.installOn(list); InstancesWithStackFrameView instancesWithStackFrame = new InstancesWithStackFrameView(myDebugSession, myInstancesTree, list); From 97bbd20508500033ba1f0eeab793222bd3527eb7 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 26 Oct 2016 19:44:45 +0300 Subject: [PATCH 128/558] Add ability to start tracking of new objects in instances window --- .../tracking/InstancesTrackingActionBase.java | 2 +- .../tracking/TrackInstanceCreationAction.java | 2 +- component/CreationPositionTracker.java | 3 ++ component/InstancesTracker.java | 5 +- view/ClassesFilteredView.java | 3 +- view/InstancesWindow.java | 2 +- view/InstancesWithStackFrameView.java | 51 ++++++++++++++++--- 7 files changed, 54 insertions(+), 14 deletions(-) diff --git a/action/tracking/InstancesTrackingActionBase.java b/action/tracking/InstancesTrackingActionBase.java index aacd946e7beb..64b514f01c80 100644 --- a/action/tracking/InstancesTrackingActionBase.java +++ b/action/tracking/InstancesTrackingActionBase.java @@ -21,7 +21,7 @@ abstract class InstancesTrackingActionBase extends ToggleAction { InstancesTracker tracker = InstancesTracker.getInstance(project); TrackingType currentType = tracker.getTrackingType(selectedClass.name()); if (state) { - tracker.add(selectedClass, type); + tracker.add(selectedClass.name(), type); } else if(type.equals(currentType)) { tracker.remove(selectedClass); } diff --git a/action/tracking/TrackInstanceCreationAction.java b/action/tracking/TrackInstanceCreationAction.java index 4268cd30e6cb..d1ad2c036e1a 100644 --- a/action/tracking/TrackInstanceCreationAction.java +++ b/action/tracking/TrackInstanceCreationAction.java @@ -35,7 +35,7 @@ public class TrackInstanceCreationAction extends ClassesActionBase { if (selectedClass != null && project != null) { InstancesTracker tracker = InstancesTracker.getInstance(project); - tracker.add(selectedClass, TrackingType.CREATION); + tracker.add(selectedClass.name(), TrackingType.CREATION); } } } diff --git a/component/CreationPositionTracker.java b/component/CreationPositionTracker.java index 142d0e1f164f..ee54aab35e3e 100644 --- a/component/CreationPositionTracker.java +++ b/component/CreationPositionTracker.java @@ -17,6 +17,9 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; public class CreationPositionTracker extends AbstractProjectComponent { + /** + * Stores all tracked instance for each debug session. + */ private final ConcurrentHashMap>> mySession2Reference2Stack = new ConcurrentHashMap<>(); diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java index 390a3a3c62ee..c9576836cbf8 100644 --- a/component/InstancesTracker.java +++ b/component/InstancesTracker.java @@ -48,13 +48,12 @@ public class InstancesTracker extends AbstractProjectComponent return new HashMap<>(myState.classes); } - public void add(@NotNull ReferenceType ref, @NotNull TrackingType type) { - String name = ref.name(); + public void add(@NotNull String name, @NotNull TrackingType type) { if (type.equals(myState.classes.getOrDefault(name, null))) { return; } - myState.classes.put(ref.name(), type); + myState.classes.put(name, type); myDispatcher.getMulticaster().classChanged(name, type); } diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 92887d2c5fb2..b14fd73017a9 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -76,7 +76,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable /** * Indicates that the debug session had been stopped at least once. *

- * State: false -> true + * State: false to true */ private volatile boolean myIsTrackersActivated = false; @@ -106,6 +106,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } }); } + myTable.repaint(); } @Override diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index a7ddc6385eaf..065f00847db9 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -221,7 +221,7 @@ public class InstancesWindow extends DialogWrapper { }.installOn(list); InstancesWithStackFrameView instancesWithStackFrame = new InstancesWithStackFrameView(myDebugSession, - myInstancesTree, list); + myInstancesTree, list, myClassName); add(filteringPane, BorderLayout.NORTH); add(instancesWithStackFrame.getComponent(), BorderLayout.CENTER); diff --git a/view/InstancesWithStackFrameView.java b/view/InstancesWithStackFrameView.java index f5758e70f5eb..e9af45371d75 100644 --- a/view/InstancesWithStackFrameView.java +++ b/view/InstancesWithStackFrameView.java @@ -2,18 +2,25 @@ package org.jetbrains.debugger.memory.view; import com.intellij.ui.JBSplitter; import com.intellij.ui.components.JBScrollPane; +import com.intellij.ui.components.labels.LinkLabel; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ObjectReference; import org.jetbrains.annotations.NotNull; import org.jetbrains.debugger.memory.component.CreationPositionTracker; +import org.jetbrains.debugger.memory.component.InstancesTracker; +import org.jetbrains.debugger.memory.event.InstancesTrackerListener; +import org.jetbrains.debugger.memory.tracking.TrackingType; import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; import javax.swing.*; import java.util.Collections; import java.util.List; +import java.util.Objects; class InstancesWithStackFrameView { private static final float DEFAULT_SPLITTER_PROPORTION = 0.7f; + private static final String EMPTY_TEXT_WHEN_ITEM_NOT_SELECTED = "Select instance to see stack frame"; + private static final String EMPTY_TEXT_WHEN_STACK_NOT_FOUND = "No stack frame for this instance"; private static final List EMPTY_FRAME = Collections.emptyList(); private float myHidedProportion; @@ -22,28 +29,56 @@ class InstancesWithStackFrameView { private boolean myIsHided = false; InstancesWithStackFrameView(@NotNull XDebugSession debugSession, @NotNull InstancesTree tree, - @NotNull StackFrameList list) { + @NotNull StackFrameList list, String className) { mySplitter.setFirstComponent(new JBScrollPane(tree)); - mySplitter.setSecondComponent(new JBScrollPane(list)); + + list.setEmptyText(EMPTY_TEXT_WHEN_ITEM_NOT_SELECTED); + LinkLabel linkLabel = new LinkLabel<>("Enable tracking for calls of constructors", null, + (aSource, aLinkData) -> + InstancesTracker.getInstance(debugSession.getProject()).add(className, TrackingType.CREATION)); + + linkLabel.setHorizontalAlignment(SwingConstants.CENTER); + linkLabel.setPaintUnderline(false); + + JComponent stackComponent = new JBScrollPane(list); + + CreationPositionTracker tracker = CreationPositionTracker.getInstance(debugSession.getProject()); + InstancesTracker instancesTracker = InstancesTracker.getInstance(debugSession.getProject()); + instancesTracker.addTrackerListener(new InstancesTrackerListener() { + @Override + public void classChanged(@NotNull String name, @NotNull TrackingType type) { + if (Objects.equals(className, name) && type == TrackingType.CREATION) { + mySplitter.setSecondComponent(stackComponent); + } + } + + @Override + public void classRemoved(@NotNull String name) { + if (Objects.equals(name, className)) { + mySplitter.setSecondComponent(linkLabel); + } + } + }, tree); + + mySplitter.setSecondComponent(instancesTracker.isTracked(className) ? stackComponent : linkLabel); + mySplitter.setHonorComponentsMinimumSize(false); myHidedProportion = DEFAULT_SPLITTER_PROPORTION; - hideStackFrame(); - - CreationPositionTracker tracker = CreationPositionTracker.getInstance(debugSession.getProject()); tree.addTreeSelectionListener(e -> { ObjectReference ref = tree.getSelectedReference(); if (ref != null && tracker != null) { List stack = tracker.getStack(debugSession, ref); if (stack != null) { list.setFrame(stack); - showStackFrame(); return; } + list.setEmptyText(EMPTY_TEXT_WHEN_STACK_NOT_FOUND); + } else { + list.setEmptyText(EMPTY_TEXT_WHEN_ITEM_NOT_SELECTED); } list.setFrame(EMPTY_FRAME); - hideStackFrame(); }); } @@ -51,6 +86,7 @@ class InstancesWithStackFrameView { return mySplitter; } + @SuppressWarnings("unused") private void hideStackFrame() { if (!myIsHided) { myHidedProportion = mySplitter.getProportion(); @@ -60,6 +96,7 @@ class InstancesWithStackFrameView { } } + @SuppressWarnings("unused") private void showStackFrame() { if (myIsHided) { mySplitter.getSecondComponent().setVisible(true); From 569592c3e0a7202c52a47bd08e078ca8cab63d3a Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 26 Oct 2016 19:57:17 +0300 Subject: [PATCH 129/558] Remove the unused classes --- action/tracking/HashBasedTrackingAction.java | 12 ----- .../tracking/IdentityBasedTrackingAction.java | 12 ----- .../tracking/InstancesTrackingActionBase.java | 47 ------------------- .../RetainReferenceBasedTrackingAction.java | 12 ----- action/tracking/TrackingActionGroup.java | 13 ----- 5 files changed, 96 deletions(-) delete mode 100644 action/tracking/HashBasedTrackingAction.java delete mode 100644 action/tracking/IdentityBasedTrackingAction.java delete mode 100644 action/tracking/InstancesTrackingActionBase.java delete mode 100644 action/tracking/RetainReferenceBasedTrackingAction.java delete mode 100644 action/tracking/TrackingActionGroup.java diff --git a/action/tracking/HashBasedTrackingAction.java b/action/tracking/HashBasedTrackingAction.java deleted file mode 100644 index c9aa65fa102b..000000000000 --- a/action/tracking/HashBasedTrackingAction.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.jetbrains.debugger.memory.action.tracking; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.tracking.TrackingType; - -public class HashBasedTrackingAction extends InstancesTrackingActionBase { - @NotNull - @Override - protected TrackingType getType() { - return TrackingType.HASH; - } -} diff --git a/action/tracking/IdentityBasedTrackingAction.java b/action/tracking/IdentityBasedTrackingAction.java deleted file mode 100644 index f43f65952cf1..000000000000 --- a/action/tracking/IdentityBasedTrackingAction.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.jetbrains.debugger.memory.action.tracking; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.tracking.TrackingType; - -public class IdentityBasedTrackingAction extends InstancesTrackingActionBase { - @NotNull - @Override - protected TrackingType getType() { - return TrackingType.IDENTITY; - } -} diff --git a/action/tracking/InstancesTrackingActionBase.java b/action/tracking/InstancesTrackingActionBase.java deleted file mode 100644 index 64b514f01c80..000000000000 --- a/action/tracking/InstancesTrackingActionBase.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.jetbrains.debugger.memory.action.tracking; - -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.ToggleAction; -import com.intellij.openapi.project.Project; -import com.sun.jdi.ReferenceType; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.component.InstancesTracker; -import org.jetbrains.debugger.memory.view.ClassesTable; - -import org.jetbrains.debugger.memory.tracking.TrackingType; - -abstract class InstancesTrackingActionBase extends ToggleAction { - @Override - public void setSelected(AnActionEvent e, boolean state) { - TrackingType type = getType(); - ReferenceType selectedClass = getSelectedClass(e); - Project project = e.getProject(); - if (selectedClass != null && project != null) { - InstancesTracker tracker = InstancesTracker.getInstance(project); - TrackingType currentType = tracker.getTrackingType(selectedClass.name()); - if (state) { - tracker.add(selectedClass.name(), type); - } else if(type.equals(currentType)) { - tracker.remove(selectedClass); - } - } - } - - @Override - public boolean isSelected(AnActionEvent e) { - TrackingType type = getType(); - ReferenceType selectedClass = getSelectedClass(e); - Project project = e.getProject(); - return selectedClass != null && project != null && - type.equals(InstancesTracker.getInstance(project).getTrackingType(selectedClass.name())); - } - - @NotNull - protected abstract TrackingType getType(); - - @Nullable - private ReferenceType getSelectedClass(AnActionEvent e) { - return e.getData(ClassesTable.SELECTED_CLASS_KEY); - } -} diff --git a/action/tracking/RetainReferenceBasedTrackingAction.java b/action/tracking/RetainReferenceBasedTrackingAction.java deleted file mode 100644 index bf86f08209f2..000000000000 --- a/action/tracking/RetainReferenceBasedTrackingAction.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.jetbrains.debugger.memory.action.tracking; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.tracking.TrackingType; - -public class RetainReferenceBasedTrackingAction extends InstancesTrackingActionBase { - @NotNull - @Override - protected TrackingType getType() { - return TrackingType.RETAIN; - } -} diff --git a/action/tracking/TrackingActionGroup.java b/action/tracking/TrackingActionGroup.java deleted file mode 100644 index fd586f3125dc..000000000000 --- a/action/tracking/TrackingActionGroup.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.jetbrains.debugger.memory.action.tracking; - -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.DefaultActionGroup; -import com.intellij.openapi.project.DumbAware; -import org.jetbrains.debugger.memory.view.ClassesTable; - -public class TrackingActionGroup extends DefaultActionGroup implements DumbAware{ - @Override - public void update(AnActionEvent e) { - e.getPresentation().setVisible(e.getData(ClassesTable.SELECTED_CLASS_KEY) != null); - } -} From 2793e72de98bd1efdec2935b50ff7906fbe810cf Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 26 Oct 2016 20:13:22 +0300 Subject: [PATCH 130/558] Use one toggle action instead of the two different actions enable/disable tracking --- action/tracking/DisableTrackingAction.java | 26 ---------- .../JumpToAllocationSourceAction.java | 3 +- .../tracking/TrackInstanceCreationAction.java | 41 --------------- .../tracking/TrackInstancesToggleAction.java | 52 +++++++++++++++++++ component/InstancesTracker.java | 4 +- 5 files changed, 55 insertions(+), 71 deletions(-) delete mode 100644 action/tracking/DisableTrackingAction.java delete mode 100644 action/tracking/TrackInstanceCreationAction.java create mode 100644 action/tracking/TrackInstancesToggleAction.java diff --git a/action/tracking/DisableTrackingAction.java b/action/tracking/DisableTrackingAction.java deleted file mode 100644 index a492115e8d5b..000000000000 --- a/action/tracking/DisableTrackingAction.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.jetbrains.debugger.memory.action.tracking; - -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; -import com.sun.jdi.ReferenceType; -import org.jetbrains.debugger.memory.action.ClassesActionBase; -import org.jetbrains.debugger.memory.component.InstancesTracker; - -public class DisableTrackingAction extends ClassesActionBase { - @Override - protected boolean isEnabled(AnActionEvent e) { - Project project = e.getProject(); - ReferenceType selectedClass = getSelectedClass(e); - return super.isEnabled(e) && selectedClass != null && - project != null && InstancesTracker.getInstance(project).isTracked(selectedClass.name()); - } - - @Override - protected void perform(AnActionEvent e) { - Project project = e.getProject(); - ReferenceType selectedClass = getSelectedClass(e); - if(project != null && selectedClass != null) { - InstancesTracker.getInstance(project).remove(selectedClass); - } - } -} diff --git a/action/tracking/JumpToAllocationSourceAction.java b/action/tracking/JumpToAllocationSourceAction.java index dba977e27710..218fdfa6084e 100644 --- a/action/tracking/JumpToAllocationSourceAction.java +++ b/action/tracking/JumpToAllocationSourceAction.java @@ -48,6 +48,7 @@ public class JumpToAllocationSourceAction extends DebuggerTreeAction { } XDebugSession session = e.getData(InstancesTree.DEBUG_SESSION_DATA_KEY); - return session == null ? null : CreationPositionTracker.getInstance(project).getStack(session, ref); + CreationPositionTracker tracker = CreationPositionTracker.getInstance(project); + return session == null || tracker == null ? null : tracker.getStack(session, ref); } } diff --git a/action/tracking/TrackInstanceCreationAction.java b/action/tracking/TrackInstanceCreationAction.java deleted file mode 100644 index d1ad2c036e1a..000000000000 --- a/action/tracking/TrackInstanceCreationAction.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.jetbrains.debugger.memory.action.tracking; - -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; -import com.sun.jdi.ArrayType; -import com.sun.jdi.ReferenceType; -import org.jetbrains.debugger.memory.action.ClassesActionBase; -import org.jetbrains.debugger.memory.component.InstancesTracker; -import org.jetbrains.debugger.memory.tracking.TrackingType; - -public class TrackInstanceCreationAction extends ClassesActionBase { - @Override - protected boolean isEnabled(AnActionEvent e) { - Project project = e.getProject(); - if (!super.isEnabled(e) || project == null) { - return false; - } - - InstancesTracker tracker = InstancesTracker.getInstance(project); - ReferenceType selectedClass = getSelectedClass(e); - if (selectedClass != null) { - TrackingType currentType = tracker.getTrackingType(selectedClass.name()); - if (TrackingType.CREATION == currentType) { - return false; - } - } - - return selectedClass != null && !(selectedClass instanceof ArrayType); - } - - @Override - protected void perform(AnActionEvent e) { - Project project = e.getProject(); - ReferenceType selectedClass = getSelectedClass(e); - - if (selectedClass != null && project != null) { - InstancesTracker tracker = InstancesTracker.getInstance(project); - tracker.add(selectedClass.name(), TrackingType.CREATION); - } - } -} diff --git a/action/tracking/TrackInstancesToggleAction.java b/action/tracking/TrackInstancesToggleAction.java new file mode 100644 index 000000000000..58f7a8f478b2 --- /dev/null +++ b/action/tracking/TrackInstancesToggleAction.java @@ -0,0 +1,52 @@ +package org.jetbrains.debugger.memory.action.tracking; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.xdebugger.XDebugSession; +import com.sun.jdi.ReferenceType; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.debugger.memory.component.InstancesTracker; +import org.jetbrains.debugger.memory.tracking.TrackingType; +import org.jetbrains.debugger.memory.view.ClassesTable; + +public class TrackInstancesToggleAction extends ToggleAction { + @Override + public boolean isSelected(AnActionEvent e) { + ReferenceType selectedClass = getSelectedClass(e); + XDebugSession debugSession = getDebugSession(e); + if (debugSession != null && selectedClass != null) { + InstancesTracker tracker = InstancesTracker.getInstance(debugSession.getProject()); + return tracker.isTracked(selectedClass.name()); + } + + return false; + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + ReferenceType selectedClass = getSelectedClass(e); + XDebugSession debugSession = getDebugSession(e); + if (selectedClass != null && debugSession != null) { + InstancesTracker tracker = InstancesTracker.getInstance(debugSession.getProject()); + boolean isAlreadyTracked = tracker.isTracked(selectedClass.name()); + + if (isAlreadyTracked && !state) { + tracker.remove(selectedClass.name()); + } + + if (!isAlreadyTracked && state) { + tracker.add(selectedClass.name(), TrackingType.CREATION); + } + } + } + + @Nullable + private ReferenceType getSelectedClass(AnActionEvent e) { + return e.getData(ClassesTable.SELECTED_CLASS_KEY); + } + + @Nullable + private XDebugSession getDebugSession(AnActionEvent e) { + return e.getData(ClassesTable.DEBUG_SESSION_KEY); + } +} diff --git a/component/InstancesTracker.java b/component/InstancesTracker.java index c9576836cbf8..1a47159e20ca 100644 --- a/component/InstancesTracker.java +++ b/component/InstancesTracker.java @@ -6,7 +6,6 @@ import com.intellij.openapi.project.Project; import com.intellij.util.EventDispatcher; import com.intellij.util.containers.HashMap; import com.intellij.util.xmlb.annotations.AbstractCollection; -import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.event.InstancesTrackerListener; @@ -57,8 +56,7 @@ public class InstancesTracker extends AbstractProjectComponent myDispatcher.getMulticaster().classChanged(name, type); } - public boolean remove(@NotNull ReferenceType ref) { - String name = ref.name(); + public boolean remove(@NotNull String name) { TrackingType removed = myState.classes.remove(name); if (removed != null) { myDispatcher.getMulticaster().classRemoved(name); From 136cd18763f4c893bc444ad63b6673ced45274c7 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 27 Oct 2016 21:19:19 +0300 Subject: [PATCH 131/558] Fix minor code issues and highlight the label in stack frame --- view/ClassesFilteredView.java | 2 +- view/InstancesWithStackFrameView.java | 22 ++++++++++++++-------- view/StackFrameList.java | 3 +-- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index b14fd73017a9..fbc636d55946 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -114,7 +114,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable ReferenceType ref = myTable.getClassByName(name); if (ref != null && myConstructorTrackedClasses.containsKey(ref)) { ConstructorInstancesTracker removed = myConstructorTrackedClasses.remove(ref); - removed.dispose(); + Disposer.dispose(removed); myTable.getRowSorter().allRowsChanged(); } } diff --git a/view/InstancesWithStackFrameView.java b/view/InstancesWithStackFrameView.java index e9af45371d75..a595f973c423 100644 --- a/view/InstancesWithStackFrameView.java +++ b/view/InstancesWithStackFrameView.java @@ -1,10 +1,13 @@ package org.jetbrains.debugger.memory.view; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.ui.JBSplitter; import com.intellij.ui.components.JBScrollPane; -import com.intellij.ui.components.labels.LinkLabel; +import com.intellij.ui.components.labels.ActionLink; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ObjectReference; +import icons.MemoryViewIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.debugger.memory.component.CreationPositionTracker; import org.jetbrains.debugger.memory.component.InstancesTracker; @@ -33,12 +36,15 @@ class InstancesWithStackFrameView { mySplitter.setFirstComponent(new JBScrollPane(tree)); list.setEmptyText(EMPTY_TEXT_WHEN_ITEM_NOT_SELECTED); - LinkLabel linkLabel = new LinkLabel<>("Enable tracking for calls of constructors", null, - (aSource, aLinkData) -> - InstancesTracker.getInstance(debugSession.getProject()).add(className, TrackingType.CREATION)); + ActionLink actionLink = new ActionLink("Enable tracking for calls of constructors", MemoryViewIcons.CLASS_TRACKED, new AnAction() { + @Override + public void actionPerformed(AnActionEvent e) { + InstancesTracker.getInstance(debugSession.getProject()).add(className, TrackingType.CREATION); + } + }); - linkLabel.setHorizontalAlignment(SwingConstants.CENTER); - linkLabel.setPaintUnderline(false); + actionLink.setHorizontalAlignment(SwingConstants.CENTER); + actionLink.setPaintUnderline(false); JComponent stackComponent = new JBScrollPane(list); @@ -55,12 +61,12 @@ class InstancesWithStackFrameView { @Override public void classRemoved(@NotNull String name) { if (Objects.equals(name, className)) { - mySplitter.setSecondComponent(linkLabel); + mySplitter.setSecondComponent(actionLink); } } }, tree); - mySplitter.setSecondComponent(instancesTracker.isTracked(className) ? stackComponent : linkLabel); + mySplitter.setSecondComponent(instancesTracker.isTracked(className) ? stackComponent : actionLink); mySplitter.setHonorComponentsMinimumSize(false); myHidedProportion = DEFAULT_SPLITTER_PROPORTION; diff --git a/view/StackFrameList.java b/view/StackFrameList.java index 91919a698ac5..02bcb0586f26 100644 --- a/view/StackFrameList.java +++ b/view/StackFrameList.java @@ -38,7 +38,6 @@ class StackFrameList extends JBList { @NotNull GlobalSearchScope searchScope) { super(); - myStackFrames = new ArrayList<>(stack); myProject = project; myScope = searchScope; @@ -120,7 +119,7 @@ class StackFrameList extends JBList { private class MyListModel extends AbstractListModel { - public void update(@NotNull List newFrame) { + void update(@NotNull List newFrame) { fireIntervalRemoved(this, 0, getSize()); myStackFrames = newFrame; fireIntervalAdded(this, 0, getSize()); From bc91eef180ff7f18f45ea9f042e116158afea758 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Thu, 27 Oct 2016 22:23:42 +0300 Subject: [PATCH 132/558] Switch cursor to hand-cursor on a diff column if new instances can be shown --- view/ClassesFilteredView.java | 36 ++++++++++++++++++++++++++++++----- view/ClassesTable.java | 1 - 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index fbc636d55946..531c2bc91973 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -41,10 +41,7 @@ import javax.swing.FocusManager; import javax.swing.*; import javax.swing.event.DocumentEvent; import java.awt.*; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; +import java.awt.event.*; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -53,6 +50,9 @@ import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.IntStream; +import static org.jetbrains.debugger.memory.view.ClassesTable.DiffViewTableModel.CLASSNAME_COLUMN_INDEX; +import static org.jetbrains.debugger.memory.view.ClassesTable.DiffViewTableModel.DIFF_COLUMN_INDEX; + public class ClassesFilteredView extends BorderLayoutPanel implements Disposable { private static final Logger LOG = Logger.getInstance(ClassesFilteredView.class); private final static double DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT = 0.5; @@ -157,6 +157,32 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNING); Disposer.register(this, myTable); + myTable.addMouseMotionListener(new MouseMotionListener() { + @Override + public void mouseDragged(MouseEvent e) { + } + + @Override + public void mouseMoved(MouseEvent e) { + final int col = myTable.columnAtPoint(e.getPoint()); + final int row = myTable.rowAtPoint(e.getPoint()); + if (col == -1 || row == -1 || col != DIFF_COLUMN_INDEX) { + myTable.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + return; + } + + final int modelRow = myTable.convertRowIndexToModel(row); + + final ReferenceType ref = (ReferenceType) myTable.getModel().getValueAt(modelRow, CLASSNAME_COLUMN_INDEX); + final ConstructorInstancesTracker tracker = myConstructorTrackedClasses.getOrDefault(ref, null); + if (tracker != null && tracker.isReady() && tracker.getCount() > 0) { + myTable.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } else { + myTable.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + } + }); + myTable.addKeyListener(new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { @@ -202,7 +228,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable Function isOpenNewInstancesClick = e -> { int column = myTable.convertColumnIndexToModel(myTable.columnAtPoint(e.getPoint())); ReferenceType ref = myTable.getSelectedClass(); - return column == ClassesTable.DiffViewTableModel.DIFF_COLUMN_INDEX && ref != null && getStrategy(ref) != null; + return column == DIFF_COLUMN_INDEX && ref != null && getStrategy(ref) != null; }; new DoubleClickListener() { diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 5406930fe29d..888c1674d80f 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -90,7 +90,6 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { setDefaultRenderer(Long.class, new MyCountColumnRenderer()); setDefaultRenderer(DiffValue.class, new MyDiffColumnRenderer()); - TableRowSorter sorter = new TableRowSorter<>(myModel); sorter.setRowFilter(new RowFilter() { @Override From 1965ea96ed7b05a749c31c8175da29f05886cc3e Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 28 Oct 2016 13:47:18 +0300 Subject: [PATCH 133/558] Disallow to track arrays --- .../tracking/TrackInstancesToggleAction.java | 12 +++++++ view/InstancesWithStackFrameView.java | 33 +++++++++++++------ 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/action/tracking/TrackInstancesToggleAction.java b/action/tracking/TrackInstancesToggleAction.java index 58f7a8f478b2..2a35327966df 100644 --- a/action/tracking/TrackInstancesToggleAction.java +++ b/action/tracking/TrackInstancesToggleAction.java @@ -3,13 +3,25 @@ package org.jetbrains.debugger.memory.action.tracking; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.ToggleAction; import com.intellij.xdebugger.XDebugSession; +import com.sun.jdi.ArrayType; import com.sun.jdi.ReferenceType; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.memory.component.InstancesTracker; import org.jetbrains.debugger.memory.tracking.TrackingType; import org.jetbrains.debugger.memory.view.ClassesTable; public class TrackInstancesToggleAction extends ToggleAction { + @Override + public void update(@NotNull AnActionEvent e) { + ReferenceType selectedClass = getSelectedClass(e); + if (selectedClass instanceof ArrayType) { + e.getPresentation().setEnabled(false); + } else { + super.update(e); + } + } + @Override public boolean isSelected(AnActionEvent e) { ReferenceType selectedClass = getSelectedClass(e); diff --git a/view/InstancesWithStackFrameView.java b/view/InstancesWithStackFrameView.java index a595f973c423..9047916a2ae7 100644 --- a/view/InstancesWithStackFrameView.java +++ b/view/InstancesWithStackFrameView.java @@ -3,6 +3,7 @@ package org.jetbrains.debugger.memory.view; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.ui.JBSplitter; +import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.components.labels.ActionLink; import com.intellij.xdebugger.XDebugSession; @@ -24,6 +25,7 @@ class InstancesWithStackFrameView { private static final float DEFAULT_SPLITTER_PROPORTION = 0.7f; private static final String EMPTY_TEXT_WHEN_ITEM_NOT_SELECTED = "Select instance to see stack frame"; private static final String EMPTY_TEXT_WHEN_STACK_NOT_FOUND = "No stack frame for this instance"; + private static final String TEXT_FOR_ARRAYS = "Arrays could not be tracked"; private static final List EMPTY_FRAME = Collections.emptyList(); private float myHidedProportion; @@ -36,15 +38,22 @@ class InstancesWithStackFrameView { mySplitter.setFirstComponent(new JBScrollPane(tree)); list.setEmptyText(EMPTY_TEXT_WHEN_ITEM_NOT_SELECTED); - ActionLink actionLink = new ActionLink("Enable tracking for calls of constructors", MemoryViewIcons.CLASS_TRACKED, new AnAction() { - @Override - public void actionPerformed(AnActionEvent e) { - InstancesTracker.getInstance(debugSession.getProject()).add(className, TrackingType.CREATION); - } - }); + JLabel stackTraceLabel; + if (isArrayType(className)) { + stackTraceLabel = new JBLabel(TEXT_FOR_ARRAYS, SwingConstants.CENTER); + } else { + ActionLink actionLink = new ActionLink("Enable tracking for calls of constructors", MemoryViewIcons.CLASS_TRACKED, new AnAction() { + @Override + public void actionPerformed(AnActionEvent e) { + InstancesTracker.getInstance(debugSession.getProject()).add(className, TrackingType.CREATION); + } + }); + + actionLink.setHorizontalAlignment(SwingConstants.CENTER); + actionLink.setPaintUnderline(false); + stackTraceLabel = actionLink; + } - actionLink.setHorizontalAlignment(SwingConstants.CENTER); - actionLink.setPaintUnderline(false); JComponent stackComponent = new JBScrollPane(list); @@ -61,12 +70,12 @@ class InstancesWithStackFrameView { @Override public void classRemoved(@NotNull String name) { if (Objects.equals(name, className)) { - mySplitter.setSecondComponent(actionLink); + mySplitter.setSecondComponent(stackTraceLabel); } } }, tree); - mySplitter.setSecondComponent(instancesTracker.isTracked(className) ? stackComponent : actionLink); + mySplitter.setSecondComponent(instancesTracker.isTracked(className) ? stackComponent : stackTraceLabel); mySplitter.setHonorComponentsMinimumSize(false); myHidedProportion = DEFAULT_SPLITTER_PROPORTION; @@ -92,6 +101,10 @@ class InstancesWithStackFrameView { return mySplitter; } + private static boolean isArrayType(@NotNull String className) { + return className.contains("[]"); + } + @SuppressWarnings("unused") private void hideStackFrame() { if (!myIsHided) { From b435bcc269e4ae43d4819294a350590facae65c5 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 28 Oct 2016 15:38:12 +0300 Subject: [PATCH 134/558] Avoid tracking before the class table not shown yet --- view/ClassesFilteredView.java | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 531c2bc91973..bb391ddccef5 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -289,23 +289,16 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void sessionPaused() { SwingUtilities.invokeLater(() -> myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_SUSPENDED)); - commitAllTrackers(); updateClassesAndCounts(); - if (!myIsTrackersActivated) { - myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::enable); - myIsTrackersActivated = true; - } } }; mySingleAlarm = new SingleAlarmWithMutableDelay(() -> { myLastSuspendContext = getSuspendContext(); if (myLastSuspendContext != null) { - SwingUtilities.invokeLater(() -> { - myTable.setBusy(true); - myDebugProcess.getManagerThread() - .schedule(new MyUpdateClassesCommand(myLastSuspendContext)); - }); + SwingUtilities.invokeLater(() -> myTable.setBusy(true)); + myDebugProcess.getManagerThread() + .schedule(new MyUpdateClassesCommand(myLastSuspendContext)); } }, this); @@ -403,7 +396,9 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myDebugSession.addSessionListener(myDebugSessionListener, ClassesFilteredView.this); myConstructorTrackedClasses.values().forEach(x -> x.setBackgroundMode(false)); if (myLastSuspendContext == null || !myLastSuspendContext.equals(getSuspendContext())) { - commitAllTrackers(); + if(myIsTrackersActivated) { + commitAllTrackers(); + } updateClassesAndCounts(); } } @@ -421,6 +416,12 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { + if (!myIsTrackersActivated) { + myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::enable); + myIsTrackersActivated = true; + } else { + commitAllTrackers(); + } final List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); if (classes.isEmpty()) { From 96ee7464dfd24d5edad57bb15ed1aace423924f2 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 28 Oct 2016 17:09:27 +0300 Subject: [PATCH 135/558] Do not switch focus to the editor after selecting of another stack frame --- view/StackFrameList.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/view/StackFrameList.java b/view/StackFrameList.java index 02bcb0586f26..ce9431591901 100644 --- a/view/StackFrameList.java +++ b/view/StackFrameList.java @@ -7,6 +7,7 @@ import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.OpenFileDescriptor; +import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiClass; @@ -90,21 +91,23 @@ class StackFrameList extends JBList { new OpenFileHyperlinkInfo(myProject, file, frame.line() - 1); OpenFileDescriptor descriptor = info.getDescriptor(); if (descriptor != null) { - FileEditorManager manager = FileEditorManager.getInstance(myProject); + FileEditorManagerImpl manager = (FileEditorManagerImpl)FileEditorManager.getInstance(myProject); VirtualFile lastFile = myEditorState.myLastOpenedFile; if (myEditorState.myIsNeedToCloseLastOpenedFile && lastFile != null && manager.isFileOpen(lastFile) && !lastFile.equals(descriptor.getFile())) { - manager.closeFile(myEditorState.myLastOpenedFile); + manager.closeFile(myEditorState.myLastOpenedFile, false, true); } descriptor.setScrollType(ScrollType.CENTER); + descriptor.setUseCurrentWindow(true); if (lastFile == null || !lastFile.equals(descriptor.getFile())) { myEditorState.myIsNeedToCloseLastOpenedFile = !manager.isFileOpen(descriptor.getFile()); } - List fileEditors = manager.openEditor(descriptor, focusOnEditor); - if (!fileEditors.isEmpty()) { + descriptor.navigateInEditor(myProject, focusOnEditor); + FileEditor[] editors = manager.getEditors(descriptor.getFile()); + if (editors.length != 0) { myEditorState.myLastOpenedFile = descriptor.getFile(); } } From 5b9d31e376be3d4c51ae2222054f575c16c2fe09 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 28 Oct 2016 18:21:48 +0300 Subject: [PATCH 136/558] Show all instances on double click on the diff column with empty tracker --- view/ClassesFilteredView.java | 131 +++++++++++++++++----------------- 1 file changed, 66 insertions(+), 65 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index bb391ddccef5..e343ad974b6d 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -47,7 +47,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; -import java.util.function.Function; import java.util.stream.IntStream; import static org.jetbrains.debugger.memory.view.ClassesTable.DiffViewTableModel.CLASSNAME_COLUMN_INDEX; @@ -157,31 +156,9 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNING); Disposer.register(this, myTable); - myTable.addMouseMotionListener(new MouseMotionListener() { - @Override - public void mouseDragged(MouseEvent e) { - } - - @Override - public void mouseMoved(MouseEvent e) { - final int col = myTable.columnAtPoint(e.getPoint()); - final int row = myTable.rowAtPoint(e.getPoint()); - if (col == -1 || row == -1 || col != DIFF_COLUMN_INDEX) { - myTable.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - return; - } - - final int modelRow = myTable.convertRowIndexToModel(row); - - final ReferenceType ref = (ReferenceType) myTable.getModel().getValueAt(modelRow, CLASSNAME_COLUMN_INDEX); - final ConstructorInstancesTracker tracker = myConstructorTrackedClasses.getOrDefault(ref, null); - if (tracker != null && tracker.isReady() && tracker.getCount() > 0) { - myTable.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - } else { - myTable.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - } - } - }); + myTable.addMouseMotionListener(new MyMouseMotionListener()); + myTable.addMouseListener(new MyOpenNewInstancesListener()); + new MyDoubleClickListener().installOn(myTable); myTable.addKeyListener(new KeyAdapter() { @Override @@ -225,44 +202,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } }); - Function isOpenNewInstancesClick = e -> { - int column = myTable.convertColumnIndexToModel(myTable.columnAtPoint(e.getPoint())); - ReferenceType ref = myTable.getSelectedClass(); - return column == DIFF_COLUMN_INDEX && ref != null && getStrategy(ref) != null; - }; - - new DoubleClickListener() { - @Override - protected boolean onDoubleClick(MouseEvent event) { - if (!isOpenNewInstancesClick.apply(event)) { - handleClassSelection(myTable.getSelectedClass()); - return true; - } - - return false; - } - }.installOn(myTable); - - myTable.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (e.getClickCount() != 1 || !isOpenNewInstancesClick.apply(e) || e.getButton() != MouseEvent.BUTTON1) { - return; - } - - ReferenceType ref = myTable.getSelectedClass(); - TrackerForNewInstances strategy = ref == null ? null : getStrategy(ref); - final CreationPositionTracker tracker = CreationPositionTracker.getInstance(myDebugSession.getProject()); - if (strategy != null && strategy.isReady() && strategy.getCount() > 0 && tracker != null) { - List newInstances = strategy.getNewInstances(); - tracker.pinStacks(myDebugSession, ref); - InstancesWindow instancesWindow = new InstancesWindow(myDebugSession, limit -> newInstances, ref.name()); - Disposer.register(instancesWindow.getDisposable(), () -> tracker.unpinStacks(myDebugSession, ref)); - instancesWindow.show(); - } - } - }); - final MemoryViewManagerListener memoryViewManagerListener = state -> { myTable.setFilteringByDiffNonZero(state.isShowWithDiffOnly); myTable.setFilteringByInstanceExists(state.isShowWithInstancesOnly); @@ -396,7 +335,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myDebugSession.addSessionListener(myDebugSessionListener, ClassesFilteredView.this); myConstructorTrackedClasses.values().forEach(x -> x.setBackgroundMode(false)); if (myLastSuspendContext == null || !myLastSuspendContext.equals(getSuspendContext())) { - if(myIsTrackersActivated) { + if (myIsTrackersActivated) { commitAllTrackers(); } updateClassesAndCounts(); @@ -479,4 +418,66 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable return false; } } + + private class MyOpenNewInstancesListener extends MouseAdapter { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() != 1 || e.getButton() != MouseEvent.BUTTON1 || !isShowNewInstancesEvent(e)) { + return; + } + + final CreationPositionTracker tracker = CreationPositionTracker.getInstance(myDebugSession.getProject()); + ReferenceType ref = myTable.getSelectedClass(); + TrackerForNewInstances strategy = ref == null ? null : getStrategy(ref); + if (strategy != null && tracker != null) { + List newInstances = strategy.getNewInstances(); + tracker.pinStacks(myDebugSession, ref); + InstancesWindow instancesWindow = new InstancesWindow(myDebugSession, limit -> newInstances, ref.name()); + Disposer.register(instancesWindow.getDisposable(), () -> tracker.unpinStacks(myDebugSession, ref)); + instancesWindow.show(); + } + } + } + + private class MyDoubleClickListener extends DoubleClickListener { + @Override + protected boolean onDoubleClick(MouseEvent event) { + if (!isShowNewInstancesEvent(event)) { + handleClassSelection(myTable.getSelectedClass()); + return true; + } + + return false; + } + } + + private class MyMouseMotionListener implements MouseMotionListener { + @Override + public void mouseDragged(MouseEvent e) { + } + + @Override + public void mouseMoved(MouseEvent e) { + if (isShowNewInstancesEvent(e)) { + myTable.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } else { + myTable.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + } + } + + private boolean isShowNewInstancesEvent(@NotNull MouseEvent e) { + final int col = myTable.columnAtPoint(e.getPoint()); + final int row = myTable.rowAtPoint(e.getPoint()); + if (col == -1 || row == -1 || myTable.convertColumnIndexToModel(col) != DIFF_COLUMN_INDEX) { + return false; + } + + final int modelRow = myTable.convertRowIndexToModel(row); + + final ReferenceType ref = (ReferenceType) myTable.getModel().getValueAt(modelRow, CLASSNAME_COLUMN_INDEX); + final ConstructorInstancesTracker tracker = myConstructorTrackedClasses.getOrDefault(ref, null); + + return tracker != null && tracker.isReady() && tracker.getCount() > 0; + } } From e7eff50dacf6edbf92e7103d888ce85a86aa99c7 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 28 Oct 2016 18:47:27 +0300 Subject: [PATCH 137/558] Remember size of the splitter in instances view --- view/InstancesWithStackFrameView.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/view/InstancesWithStackFrameView.java b/view/InstancesWithStackFrameView.java index 9047916a2ae7..9ec7a35214b0 100644 --- a/view/InstancesWithStackFrameView.java +++ b/view/InstancesWithStackFrameView.java @@ -42,7 +42,7 @@ class InstancesWithStackFrameView { if (isArrayType(className)) { stackTraceLabel = new JBLabel(TEXT_FOR_ARRAYS, SwingConstants.CENTER); } else { - ActionLink actionLink = new ActionLink("Enable tracking for calls of constructors", MemoryViewIcons.CLASS_TRACKED, new AnAction() { + ActionLink actionLink = new ActionLink("Enable tracking for new instances", MemoryViewIcons.CLASS_TRACKED, new AnAction() { @Override public void actionPerformed(AnActionEvent e) { InstancesTracker.getInstance(debugSession.getProject()).add(className, TrackingType.CREATION); @@ -54,6 +54,7 @@ class InstancesWithStackFrameView { stackTraceLabel = actionLink; } + mySplitter.setSplitterProportionKey("InstancesWithStackFrameView.SplitterKey"); JComponent stackComponent = new JBScrollPane(list); @@ -86,6 +87,9 @@ class InstancesWithStackFrameView { List stack = tracker.getStack(debugSession, ref); if (stack != null) { list.setFrame(stack); + if(mySplitter.getProportion() == 1.f) { + mySplitter.setProportion(DEFAULT_SPLITTER_PROPORTION); + } return; } list.setEmptyText(EMPTY_TEXT_WHEN_STACK_NOT_FOUND); From 95810d48eb2a97834d15b00b3cc6f88c5501b2e8 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Sun, 30 Oct 2016 20:55:51 +0300 Subject: [PATCH 138/558] Fix memory leak with ClassesTable was not disposed when application closing --- toolwindow/MemoryViewToolWindowFactory.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java index 497a6926cfee..c404a39b3fed 100644 --- a/toolwindow/MemoryViewToolWindowFactory.java +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -7,6 +7,7 @@ import com.intellij.execution.ui.RunContentWithExecutorListener; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; @@ -16,7 +17,6 @@ import com.intellij.openapi.wm.ex.ToolWindowEx; import com.intellij.openapi.wm.ex.ToolWindowManagerAdapter; import com.intellij.openapi.wm.impl.ToolWindowImpl; import com.intellij.ui.components.JBLabel; -import com.intellij.util.containers.HashMap; import com.intellij.util.messages.MessageBusConnection; import com.intellij.xdebugger.XDebugProcess; import com.intellij.xdebugger.XDebugSession; @@ -31,13 +31,16 @@ import org.jetbrains.debugger.memory.view.ClassesFilteredView; import javax.swing.*; import java.awt.*; import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware { + private static final Logger LOG = Logger.getInstance(ClassesFilteredView.class); public final static String TOOL_WINDOW_ID = "Memory View"; private final JComponent myEmptyContent; - private final HashMap myMemoryViews = new HashMap<>(); + private final Map myMemoryViews = new ConcurrentHashMap<>(); @Nullable private ClassesFilteredView myCurrentView = null; @@ -89,6 +92,7 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware } private void addNewSession(@NotNull XDebugSession session) { + LOG.assertTrue(SwingUtilities.isEventDispatchThread()); ToolWindow toolWindow = getToolWindow(session.getProject()); if (!myMemoryViews.containsKey(session)) { ClassesFilteredView newView = new ClassesFilteredView(session); @@ -121,6 +125,7 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware } private void replaceToolWindowContent(@NotNull ToolWindow toolWindow, JComponent comp) { + LOG.assertTrue(SwingUtilities.isEventDispatchThread()); JComponent toolWindowComp = toolWindow.getComponent(); toolWindowComp.removeAll(); toolWindowComp.add(comp); @@ -174,11 +179,9 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware @Override public void processStopped(@NotNull XDebugProcess xDebugProcess) { - SwingUtilities.invokeLater(() -> { - XDebugSession session = xDebugProcess.getSession(); - removeSession(session); - updateView(session); - }); + XDebugSession session = xDebugProcess.getSession(); + removeSession(session); + SwingUtilities.invokeLater(() -> updateView(session)); } private void updateView(@NotNull XDebugSession debugSession) { From 70b7ea2374a91b0c3bb7599d139dbfb2f46b0149 Mon Sep 17 00:00:00 2001 From: Vitaliy Bibaev Date: Tue, 1 Nov 2016 00:23:16 +0300 Subject: [PATCH 139/558] Fix broken assertion in EAP (run the classes updater task in thread pool) --- utils/SingleAlarmWithMutableDelay.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/utils/SingleAlarmWithMutableDelay.java b/utils/SingleAlarmWithMutableDelay.java index 75cb937c187c..321cf46828c8 100644 --- a/utils/SingleAlarmWithMutableDelay.java +++ b/utils/SingleAlarmWithMutableDelay.java @@ -2,15 +2,13 @@ package org.jetbrains.debugger.memory.utils; import com.intellij.openapi.Disposable; import com.intellij.util.Alarm; -import com.intellij.util.config.ToggleBooleanProperty; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; public class SingleAlarmWithMutableDelay extends Alarm { private final Runnable myTask; private volatile int myDelay; public SingleAlarmWithMutableDelay(@NotNull Runnable task, @NotNull Disposable parentDisposable) { - super(parentDisposable); + super(ThreadToUse.POOLED_THREAD, parentDisposable); myTask = task; } From 88e4e02f915138ee9f2bcb3321ceb815e0db30dc Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 2 Nov 2016 16:51:36 +0300 Subject: [PATCH 140/558] Fix bug, when instances tracked in background were lost --- view/ClassesFilteredView.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index e343ad974b6d..cccd3f89356a 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -335,9 +335,6 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myDebugSession.addSessionListener(myDebugSessionListener, ClassesFilteredView.this); myConstructorTrackedClasses.values().forEach(x -> x.setBackgroundMode(false)); if (myLastSuspendContext == null || !myLastSuspendContext.equals(getSuspendContext())) { - if (myIsTrackersActivated) { - commitAllTrackers(); - } updateClassesAndCounts(); } } From 1dc8a5894dba0bf226ee63faf6cef279fcb7936c Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 2 Nov 2016 17:53:36 +0300 Subject: [PATCH 141/558] Add ability to show only tracked classes --- action/ShowInstancesAction.java | 2 +- action/ShowTrackedAction.java | 19 +++++++++++++++++++ component/MemoryViewManager.java | 11 +++++++++++ component/MemoryViewManagerState.java | 2 ++ toolwindow/MemoryViewToolWindowFactory.java | 3 +++ view/ClassesFilteredView.java | 3 ++- view/ClassesTable.java | 19 +++++++++++++++---- 7 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 action/ShowTrackedAction.java diff --git a/action/ShowInstancesAction.java b/action/ShowInstancesAction.java index 400d194a1a01..865e805caaf0 100644 --- a/action/ShowInstancesAction.java +++ b/action/ShowInstancesAction.java @@ -6,7 +6,7 @@ abstract class ShowInstancesAction extends ClassesActionBase { @Override public void update(AnActionEvent e) { boolean enabled = isEnabled(e); - if(enabled) { + if (enabled) { e.getPresentation().setText(String.format("%s (%d)", getLabel(), getInstancesCount(e))); } } diff --git a/action/ShowTrackedAction.java b/action/ShowTrackedAction.java new file mode 100644 index 000000000000..9c8228530b45 --- /dev/null +++ b/action/ShowTrackedAction.java @@ -0,0 +1,19 @@ +package org.jetbrains.debugger.memory.action; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import org.jetbrains.debugger.memory.component.MemoryViewManager; + +public class ShowTrackedAction extends ToggleAction { + @Override + public boolean isSelected(AnActionEvent e) { + return MemoryViewManager.getInstance().isNeedShowTrackedOnly(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + if (e.getProject() != null) { + MemoryViewManager.getInstance().setShowTrackedOnly(state); + } + } +} diff --git a/component/MemoryViewManager.java b/component/MemoryViewManager.java index 1af153b035a6..58d42f890fd6 100644 --- a/component/MemoryViewManager.java +++ b/component/MemoryViewManager.java @@ -53,6 +53,13 @@ public class MemoryViewManager extends ApplicationComponent.Adapter } } + public void setShowTrackedOnly(boolean value) { + if (myState.isShowTrackedOnly != value) { + myState.isShowTrackedOnly = value; + fireStateChanged(); + } + } + public boolean isNeedShowDiffOnly() { return myState.isShowWithDiffOnly; } @@ -61,6 +68,10 @@ public class MemoryViewManager extends ApplicationComponent.Adapter return myState.isShowWithInstancesOnly; } + public boolean isNeedShowTrackedOnly() { + return myState.isShowTrackedOnly; + } + public void addMemoryViewManagerListener(MemoryViewManagerListener listener, @NotNull Disposable parentDisposable) { myDispatcher.addListener(listener, parentDisposable); } diff --git a/component/MemoryViewManagerState.java b/component/MemoryViewManagerState.java index 0694133b5842..07e6f7272c45 100644 --- a/component/MemoryViewManagerState.java +++ b/component/MemoryViewManagerState.java @@ -3,6 +3,7 @@ package org.jetbrains.debugger.memory.component; public final class MemoryViewManagerState { public boolean isShowWithInstancesOnly = true; public boolean isShowWithDiffOnly = false; + public boolean isShowTrackedOnly = false; MemoryViewManagerState() { } @@ -10,5 +11,6 @@ public final class MemoryViewManagerState { MemoryViewManagerState(MemoryViewManagerState other) { isShowWithInstancesOnly = other.isShowWithInstancesOnly; isShowWithDiffOnly = other.isShowWithDiffOnly; + isShowTrackedOnly = other.isShowTrackedOnly; } } diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java index c404a39b3fed..3882052af371 100644 --- a/toolwindow/MemoryViewToolWindowFactory.java +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -7,6 +7,7 @@ import com.intellij.execution.ui.RunContentWithExecutorListener; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.actionSystem.Separator; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; @@ -79,6 +80,8 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware ActionGroup group = new DefaultActionGroup( ActionManager.getInstance().getAction("MemoryView.ShowOnlyWithInstances"), ActionManager.getInstance().getAction("MemoryView.ShowOnlyWithDiff"), + ActionManager.getInstance().getAction("MemoryView.ShowOnlyTracked"), + Separator.getInstance(), ActionManager.getInstance().getAction("MemoryView.EnableTrackingWithClosedWindow") ); ((ToolWindowEx) toolWindow).setAdditionalGearActions(group); diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index cccd3f89356a..a21ed8578ae6 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -152,7 +152,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable MemoryViewManagerState memoryViewManagerState = MemoryViewManager.getInstance().getState(); myTable = new ClassesTable(myDebugSession, memoryViewManagerState.isShowWithDiffOnly, - memoryViewManagerState.isShowWithInstancesOnly, this); + memoryViewManagerState.isShowWithInstancesOnly, memoryViewManagerState.isShowTrackedOnly, this); myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNING); Disposer.register(this, myTable); @@ -205,6 +205,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable final MemoryViewManagerListener memoryViewManagerListener = state -> { myTable.setFilteringByDiffNonZero(state.isShowWithDiffOnly); myTable.setFilteringByInstanceExists(state.isShowWithInstancesOnly); + myTable.setFilteringByTrackingState(state.isShowTrackedOnly); }; MemoryViewManager.getInstance().addMemoryViewManagerListener(memoryViewManagerListener, this); diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 888c1674d80f..1cfa45e7faf5 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -55,7 +55,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private final ReferenceCountProvider myCountProvider; private boolean myOnlyWithDiff; - + private boolean myOnlyTracked; private boolean myOnlyWithInstances; private MinusculeMatcher myMatcher = NameUtil.buildMatcher("*").build(); private String myFilteringPattern = ""; @@ -63,12 +63,13 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private volatile List myElems = Collections.unmodifiableList(new ArrayList<>()); ClassesTable(@NotNull XDebugSession session, boolean onlyWithDiff, boolean onlyWithInstances, - @NotNull ClassesFilteredView parent) { + boolean onlyTracked, @NotNull ClassesFilteredView parent) { setModel(myModel); myDebugSession = session; myOnlyWithDiff = onlyWithDiff; myOnlyWithInstances = onlyWithInstances; + myOnlyTracked = onlyTracked; myInstancesTracker = InstancesTracker.getInstance(myDebugSession.getProject()); myParent = parent; @@ -97,8 +98,11 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { int ix = entry.getIdentifier(); ReferenceType ref = myElems.get(ix); DiffValue diff = myCounts.getOrDefault(ref, myUnknownValue); - return !(myOnlyWithDiff && diff.diff() == 0 || myOnlyWithInstances && !diff.hasInstance()) - && myMatcher.matches(ref.name()); + + boolean isFilteringOptionsRefused = myOnlyWithDiff && diff.diff() == 0 + || myOnlyWithInstances && !diff.hasInstance() + || myOnlyTracked && myParent.getStrategy(ref) == null; + return !(isFilteringOptionsRefused) && myMatcher.matches(ref.name()); } }); @@ -189,6 +193,13 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { } } + void setFilteringByTrackingState(boolean value) { + if (myOnlyTracked != value) { + myOnlyTracked = value; + getRowSorter().allRowsChanged(); + } + } + void setClassesAndUpdateCounts(@NotNull List classes, @NotNull long[] counts) { assert classes.size() == counts.length; ReferenceType selectedClass = myModel.getSelectedClassBeforeHided(); From bea2dd58d8b276faa07fbeceb5ab7b009553c35d Mon Sep 17 00:00:00 2001 From: Alexandr Evstigneev Date: Sat, 5 Nov 2016 19:22:00 +0300 Subject: [PATCH 142/558] fixed DirectoriesScope uniting bug --- .../src/com/intellij/psi/search/GlobalSearchScopesCore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/analysis-api/src/com/intellij/psi/search/GlobalSearchScopesCore.java b/platform/analysis-api/src/com/intellij/psi/search/GlobalSearchScopesCore.java index 84c509b91441..1704647850e0 100644 --- a/platform/analysis-api/src/com/intellij/psi/search/GlobalSearchScopesCore.java +++ b/platform/analysis-api/src/com/intellij/psi/search/GlobalSearchScopesCore.java @@ -372,7 +372,7 @@ public class GlobalSearchScopesCore { if (scope instanceof DirectoriesScope) { DirectoriesScope other = (DirectoriesScope)scope; List newDirectories = new ArrayList<>(myDirectories.length + other.myDirectories.length); - newDirectories.addAll(Arrays.asList(other.myDirectories)); + newDirectories.addAll(Arrays.asList(myDirectories)); BitSet newWithSubdirectories = (BitSet)myWithSubdirectories.clone(); VirtualFile[] directories = other.myDirectories; for (int i = 0; i < directories.length; i++) { From b4cc43ca0e32916102e83cd4835d79fd34d164fc Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 9 Nov 2016 14:30:59 +0300 Subject: [PATCH 143/558] Add limit to waiting time for downloading of instances --- view/ClassesFilteredView.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index a21ed8578ae6..4f4e68607349 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -55,6 +55,7 @@ import static org.jetbrains.debugger.memory.view.ClassesTable.DiffViewTableModel public class ClassesFilteredView extends BorderLayoutPanel implements Disposable { private static final Logger LOG = Logger.getInstance(ClassesFilteredView.class); private final static double DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT = 0.5; + private final static double MAX_DELAY_MILLIS = TimeUnit.SECONDS.toMillis(2); private final static int DEFAULT_BATCH_SIZE = Integer.MAX_VALUE; private static final String EMPTY_TABLE_CONTENT_WHEN_RUNNING = "The application is running"; private static final String EMPTY_TABLE_CONTENT_WHEN_SUSPENDED = "Nothing to show"; @@ -383,7 +384,8 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable chunks.add(counts); - mySingleAlarm.setDelay((int) (DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT * delay)); + int waitTime = (int) Math.min(DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT * delay, MAX_DELAY_MILLIS); + mySingleAlarm.setDelay(waitTime); LOG.info(String.format("Instances query time = %d ms. Count = %d", delay, batch.size())); } From 57309905eb930d783018252c7d7a2868ea34a933 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 9 Nov 2016 15:25:23 +0300 Subject: [PATCH 144/558] Do not open classes view when debug session is not for jvm application --- toolwindow/MemoryViewToolWindowFactory.java | 28 +++++++++++++++------ 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java index 3882052af371..532051d8808e 100644 --- a/toolwindow/MemoryViewToolWindowFactory.java +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -40,6 +40,7 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware public final static String TOOL_WINDOW_ID = "Memory View"; private final JComponent myEmptyContent; + private final JComponent myMemoryViewNotSupportedContent; private final Map myMemoryViews = new ConcurrentHashMap<>(); @@ -48,6 +49,8 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware { myEmptyContent = new JBLabel("Run debugging to see loaded classes", SwingConstants.CENTER); + myMemoryViewNotSupportedContent = new JBLabel("The Memory View not available for this session", + SwingConstants.CENTER); } @Override @@ -98,9 +101,13 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware LOG.assertTrue(SwingUtilities.isEventDispatchThread()); ToolWindow toolWindow = getToolWindow(session.getProject()); if (!myMemoryViews.containsKey(session)) { - ClassesFilteredView newView = new ClassesFilteredView(session); - newView.setActive(toolWindow != null && toolWindow.isVisible()); - myMemoryViews.put(session, newView); + try { + ClassesFilteredView newView = new ClassesFilteredView(session); + newView.setActive(toolWindow != null && toolWindow.isVisible()); + myMemoryViews.put(session, newView); + } catch (Throwable e) { + LOG.warn("Cannot create new instance of the memory view", e); + } } } @@ -112,11 +119,18 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware private void updateCurrentMemoryView(@NotNull Project project, @NotNull ToolWindow toolWindow) { if (!project.isDisposed()) { XDebugSession session = XDebuggerManager.getInstance(project).getCurrentSession(); - if (session != null && myMemoryViews.containsKey(session)) { - ClassesFilteredView view = myMemoryViews.get(session); - replaceToolWindowContent(toolWindow, view); + if (session != null) { + ClassesFilteredView view; + if (myMemoryViews.containsKey(session)) { + view = myMemoryViews.get(session); + replaceToolWindowContent(toolWindow, view); + } else { + view = null; + replaceToolWindowContent(toolWindow, myMemoryViewNotSupportedContent); + } + if (myCurrentView != null) { - myCurrentView.setActive(true); + myCurrentView.setActive(false); } myCurrentView = view; From 557268e1765478a52a1a19a10c3895d5c2a9072f Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 9 Nov 2016 16:39:57 +0300 Subject: [PATCH 145/558] Fix NPE when classes view not exists for stepped session --- toolwindow/MemoryViewToolWindowFactory.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java index 532051d8808e..760cf3c5c835 100644 --- a/toolwindow/MemoryViewToolWindowFactory.java +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -113,7 +113,9 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware private void removeSession(@NotNull XDebugSession session) { ClassesFilteredView removed = myMemoryViews.remove(session); - Disposer.dispose(removed); + if (removed != null) { + Disposer.dispose(removed); + } } private void updateCurrentMemoryView(@NotNull Project project, @NotNull ToolWindow toolWindow) { From f469ae2add41185715af38785d73aa02818480a7 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 9 Nov 2016 17:55:32 +0300 Subject: [PATCH 146/558] Fix a few minor potential problem places --- toolwindow/MemoryViewToolWindowFactory.java | 2 +- tracking/ConstructorInstancesTracker.java | 9 +++++---- view/ClassesFilteredView.java | 9 ++++++++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/toolwindow/MemoryViewToolWindowFactory.java index 760cf3c5c835..d844cf1bf9c3 100644 --- a/toolwindow/MemoryViewToolWindowFactory.java +++ b/toolwindow/MemoryViewToolWindowFactory.java @@ -106,7 +106,7 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware newView.setActive(toolWindow != null && toolWindow.isVisible()); myMemoryViews.put(session, newView); } catch (Throwable e) { - LOG.warn("Cannot create new instance of the memory view", e); + LOG.warn("Cannot create new instance of the memory view. " + e.getMessage()); } } } diff --git a/tracking/ConstructorInstancesTracker.java b/tracking/ConstructorInstancesTracker.java index 9dfff777e59f..44ebf43d10b8 100644 --- a/tracking/ConstructorInstancesTracker.java +++ b/tracking/ConstructorInstancesTracker.java @@ -168,6 +168,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp private boolean myIsEnabled = false; private final List myRequests = new ArrayList<>(); + private volatile boolean myIsDeleted = false; MyConstructorBreakpoints(Project project, XBreakpoint xBreakpoint) { super(project, xBreakpoint); @@ -197,6 +198,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp protected void action() throws Exception { disable(); myDebugProcess.getRequestsManager().deleteRequest(MyConstructorBreakpoints.this); + myIsDeleted = true; } }); } @@ -208,7 +210,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp SuspendContextImpl suspendContext = action.getSuspendContext(); if (suspendContext != null) { ObjectReference thisRef = getThisObject(suspendContext, event); - if (myReference.equals(thisRef.referenceType())) { + if (myReference.equals(thisRef.referenceType()) && myPositionTracker != null) { thisRef.disableCollection(); myTrackedObjects.add(thisRef); ThreadReferenceProxyImpl threadReferenceProxy = suspendContext.getThread(); @@ -227,7 +229,6 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp }).filter(Objects::nonNull).collect(Collectors.toList()); myPositionTracker.addStack(myDebugSession, thisRef, stackFrameDescriptors); } - } } } catch (EvaluateException e) { @@ -242,14 +243,14 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp } void enable() { - if (!myIsEnabled) { + if (!myIsEnabled && !myIsDeleted) { myRequests.forEach(EventRequest::enable); myIsEnabled = true; } } void disable() { - if (myIsEnabled) { + if (myIsEnabled && !myIsDeleted) { myRequests.forEach(EventRequest::disable); myIsEnabled = false; } diff --git a/view/ClassesFilteredView.java b/view/ClassesFilteredView.java index 4f4e68607349..92b5268239fb 100644 --- a/view/ClassesFilteredView.java +++ b/view/ClassesFilteredView.java @@ -71,6 +71,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private final Map myConstructorTrackedClasses = new ConcurrentHashMap<>(); private final XDebugSessionListener myDebugSessionListener; + @Nullable private volatile SuspendContextImpl myLastSuspendContext; /** @@ -92,6 +93,11 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myDebugSession = debugSession; myDebugProcess = (DebugProcessImpl) DebuggerManager.getInstance(myProject) .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); + + if (myDebugProcess == null) { + throw new NullPointerException("Failed to receive a java debug process"); + } + myInstancesTracker = InstancesTracker.getInstance(myProject); InstancesTrackerListener instancesTrackerListener = new InstancesTrackerListener() { @Override @@ -336,7 +342,8 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private void doActivate() { myDebugSession.addSessionListener(myDebugSessionListener, ClassesFilteredView.this); myConstructorTrackedClasses.values().forEach(x -> x.setBackgroundMode(false)); - if (myLastSuspendContext == null || !myLastSuspendContext.equals(getSuspendContext())) { + final SuspendContextImpl lastContext = myLastSuspendContext; + if (lastContext == null || !lastContext.equals(getSuspendContext())) { updateClassesAndCounts(); } } From 35a42981d207439c992a3c5c50ae1469c32d75ba Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 9 Nov 2016 20:35:08 +0300 Subject: [PATCH 147/558] Log filtering in instances window --- view/InstancesWindow.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/view/InstancesWindow.java b/view/InstancesWindow.java index 065f00847db9..891ebbf82da7 100644 --- a/view/InstancesWindow.java +++ b/view/InstancesWindow.java @@ -18,6 +18,7 @@ import com.intellij.debugger.ui.tree.render.CachedEvaluator; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.AnActionListener; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Disposer; @@ -61,11 +62,14 @@ import java.awt.*; import java.awt.event.MouseEvent; import java.util.Collections; import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import static java.lang.Math.min; public class InstancesWindow extends DialogWrapper { + private static final Logger LOG = Logger.getInstance(InstancesWindow.class); + private static final int DEFAULT_WINDOW_WIDTH = 870; private static final int DEFAULT_WINDOW_HEIGHT = 400; private static final int FILTERING_BUTTON_ADDITIONAL_WIDTH = 30; @@ -288,10 +292,9 @@ public class InstancesWindow extends DialogWrapper { if (expression != null && !expression.getExpression().isEmpty()) { try { myEvaluator.setReferenceExpression(TextWithImportsImpl.fromXExpression(expression)); - myEvaluator.setReferenceExpression(TextWithImportsImpl. - fromXExpression(expression)); evaluator = myEvaluator.getEvaluator(); } catch (EvaluateException ignored) { + LOG.info("cannot create evaluator by the expression = " + expression.getExpression()); } } @@ -392,10 +395,10 @@ public class InstancesWindow extends DialogWrapper { private class MyFilteringWorker extends SwingWorker { private final List myReferences; - private final EvaluationContextImpl myEvaluationContext; - private final ExpressionEvaluator myExpressionEvaluator; private final ErrorsValueGroup myErrorsGroup = new ErrorsValueGroup("Errors"); + private final ExpressionEvaluator myExpressionEvaluator; + private final EvaluationContextImpl myEvaluationContext; private volatile boolean myDebuggerTaskCompleted = false; @NotNull @@ -430,6 +433,7 @@ public class InstancesWindow extends DialogWrapper { AtomicInteger totalChildren = new AtomicInteger(0); AtomicInteger totalViewed = new AtomicInteger(0); + long start = System.nanoTime(); for (int i = 0, size = myReferences.size(); i < size && totalChildren.get() < MAX_TREE_NODE_COUNT; i += FILTERING_CHUNK_SIZE) { myDebuggerTaskCompleted = false; @@ -499,6 +503,9 @@ public class InstancesWindow extends DialogWrapper { } } + LOG.info(String.format("Filtering time = %d ms, count = %d, matched = %d", + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start), totalViewed.get(), totalChildren.get())); + myCompletionReason = totalViewed.get() == myReferences.size() ? FilteringCompletionReason.ALL_CHECKED : FilteringCompletionReason.LIMIT_REACHED; From 84e44402408145f9b8c00c2dd48206265f901c28 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 11 Nov 2016 12:07:27 +0300 Subject: [PATCH 148/558] Do not draw border for focused cell in classes table. Prevented shaking of the text in focused cell --- view/ClassesTable.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/view/ClassesTable.java b/view/ClassesTable.java index 1cfa45e7faf5..cce435132c13 100644 --- a/view/ClassesTable.java +++ b/view/ClassesTable.java @@ -29,6 +29,7 @@ import org.jetbrains.debugger.memory.utils.AbstractTableModelWithColumns; import org.jetbrains.debugger.memory.utils.InstancesProvider; import javax.swing.*; +import javax.swing.border.Border; import javax.swing.table.TableColumn; import javax.swing.table.TableRowSorter; import java.util.*; @@ -42,6 +43,8 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { public static final DataKey REF_COUNT_PROVIDER_KEY = DataKey.create("ClassesTable.ReferenceCountProvider"); + private static final Border EMPTY_BORDER = BorderFactory.createEmptyBorder(); + private static final int CLASSES_COLUMN_PREFERRED_WIDTH = 250; private static final int COUNT_COLUMN_MIN_WIDTH = 80; private static final int DIFF_COLUMN_MIN_WIDTH = 80; @@ -400,6 +403,10 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { column = convertColumnIndexToModel(column); TrackingType trackingType = getTrackingType(row); + if (hasFocus) { + setBorder(EMPTY_BORDER); + } + if (trackingType != null && column == DiffViewTableModel.DIFF_COLUMN_INDEX) { setIcon(MemoryViewIcons.CLASS_TRACKED); setTransparentIconBackground(true); From bde833106b692d7adc6e34489013fd0bea3d28c2 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 14 Dec 2016 16:25:45 +0100 Subject: [PATCH 149/558] RegexpFilter: don't treat "error:/some/path" as a Windows "r:/" path --- .../src/com/intellij/execution/filters/RegexpFilter.java | 2 +- .../com/intellij/execution/filters/RegexpFilterTest.java | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/platform/lang-api/src/com/intellij/execution/filters/RegexpFilter.java b/platform/lang-api/src/com/intellij/execution/filters/RegexpFilter.java index 8bbfbb62b845..0b5d7f77beee 100644 --- a/platform/lang-api/src/com/intellij/execution/filters/RegexpFilter.java +++ b/platform/lang-api/src/com/intellij/execution/filters/RegexpFilter.java @@ -36,7 +36,7 @@ public class RegexpFilter implements Filter, DumbAware { @NonNls public static final String LINE_MACROS = "$LINE$"; @NonNls public static final String COLUMN_MACROS = "$COLUMN$"; - @NonNls private static final String FILE_PATH_REGEXP = "(?(?:\\p{Alpha}\\:|/)[0-9 a-z_A-Z\\-\\\\./]+)"; + @NonNls private static final String FILE_PATH_REGEXP = "(^|[\\W])(?(?:\\p{Alpha}\\:|/)[0-9 a-z_A-Z\\-\\\\./]+)"; @NonNls private static final String LINE_REGEXP = "(?[0-9]+)"; @NonNls private static final String COLUMN_REGEXP = "(?[0-9]+)"; diff --git a/platform/platform-tests/testSrc/com/intellij/execution/filters/RegexpFilterTest.java b/platform/platform-tests/testSrc/com/intellij/execution/filters/RegexpFilterTest.java index 17657a20ac62..1186082af088 100644 --- a/platform/platform-tests/testSrc/com/intellij/execution/filters/RegexpFilterTest.java +++ b/platform/platform-tests/testSrc/com/intellij/execution/filters/RegexpFilterTest.java @@ -87,6 +87,13 @@ public class RegexpFilterTest { assertNull(applyFilter(line)); } + @Test + public void testUnixPathWithSemicolonPrefix() { + createFilter("$FILE_PATH$"); + HLInfo info = (HLInfo)applyFilter("error:/usr/local/bar").hyperlinkInfo; + info.checkInfo("/usr/local/bar", 0, 0); + } + private Filter.Result applyFilter(String line) { return myFilter.applyFilter(line, line.length()); } From 80b48723de79660126c17d3b1d731895efd2c5b0 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 14 Dec 2016 16:31:23 +0100 Subject: [PATCH 150/558] [platform] restores and documents update channel selection behavior A previous fix made the channel selection incompatible with 2016.2; this commit restores compatibility and makes the result less dependent on the order of channels. --- .../updateSettings/impl/UpdateStrategy.kt | 1 + .../ide/updates/UpdateStrategyTest.kt | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateStrategy.kt b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateStrategy.kt index 52b15f462ac3..516c9444f73e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateStrategy.kt +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateStrategy.kt @@ -37,6 +37,7 @@ class UpdateStrategy(private val currentBuild: BuildNumber, private val updates: val result = product.channels.asSequence() .filter { ch -> ch.status >= selectedChannel } // filters out inapplicable channels + .sortedBy { ch -> ch.status } // reorders channels (EAPs first) .flatMap { ch -> ch.builds.asSequence().map { build -> build to ch } } // maps into a sequence of pairs .filter { p -> isApplicable(p.first, ignoredBuilds) } // filters out inapplicable builds .maxWith(Comparator { p1, p2 -> compareBuilds(p1.first.number, p2.first.number) }) // a build with the max number, preferring the same baseline diff --git a/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateStrategyTest.kt b/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateStrategyTest.kt index bf3e0a1d0e0d..df338bbd3aa5 100644 --- a/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateStrategyTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateStrategyTest.kt @@ -194,6 +194,30 @@ class UpdateStrategyTest { assertBuild("162.48", result.newBuild) } + @Test fun `duplicate builds - first suitable channel wins`() { + val build = """""" + val eap15 = """$build""" + val eap = """$build""" + val beta15 = """$build""" + val beta = """$build""" + val release15 = """$build""" + val release = """$build""" + + // note: this is a test; in production, release builds should never be proposed via channels with EAP licensing + assertEquals("IDEA15_EAP", check("IU-163.1", ChannelStatus.EAP, (eap15 + eap + beta15 + beta + release15 + release)).updatedChannel?.id) + assertEquals("IDEA_EAP", check("IU-163.1", ChannelStatus.EAP, (eap + eap15 + beta + beta15 + release + release15)).updatedChannel?.id) + assertEquals("IDEA15_EAP", check("IU-163.1", ChannelStatus.EAP, (release15 + release + beta15 + beta + eap15 + eap)).updatedChannel?.id) + assertEquals("IDEA_EAP", check("IU-163.1", ChannelStatus.EAP, (release + release15 + beta + beta15 + eap + eap15)).updatedChannel?.id) + + assertEquals("IDEA15_Beta", check("IU-163.1", ChannelStatus.BETA, (release15 + release + beta15 + beta + eap15 + eap)).updatedChannel?.id) + assertEquals("IDEA_Beta", check("IU-163.1", ChannelStatus.BETA, (release + release15 + beta + beta15 + eap + eap15)).updatedChannel?.id) + + assertEquals("IDEA15_Release", check("IU-163.1", ChannelStatus.RELEASE, (eap15 + eap + beta15 + beta + release15 + release)).updatedChannel?.id) + assertEquals("IDEA_Release", check("IU-163.1", ChannelStatus.RELEASE, (eap + eap15 + beta + beta15 + release + release15)).updatedChannel?.id) + assertEquals("IDEA15_Release", check("IU-163.1", ChannelStatus.RELEASE, (release15 + release + beta15 + beta + eap15 + eap)).updatedChannel?.id) + assertEquals("IDEA_Release", check("IU-163.1", ChannelStatus.RELEASE, (release + release15 + beta + beta15 + eap + eap15)).updatedChannel?.id) + } + private fun check(currentBuild: String, selectedChannel: ChannelStatus, testData: String, From 70907f8956f53305e989f936d05fee72ede52fa5 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 14 Dec 2016 16:54:26 +0100 Subject: [PATCH 151/558] Cleanup (test name) --- .../testSrc/com/intellij/ide/updates/UpdateStrategyTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateStrategyTest.kt b/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateStrategyTest.kt index df338bbd3aa5..44fc1948d1dd 100644 --- a/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateStrategyTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateStrategyTest.kt @@ -194,7 +194,7 @@ class UpdateStrategyTest { assertBuild("162.48", result.newBuild) } - @Test fun `duplicate builds - first suitable channel wins`() { + @Test fun `for duplicate builds, first matching channel is preferred`() { val build = """""" val eap15 = """$build""" val eap = """$build""" From d29c4fa1a73e03b852353186d792540150336b9f Mon Sep 17 00:00:00 2001 From: Alexander Lobas Date: Wed, 14 Dec 2016 18:52:33 +0300 Subject: [PATCH 152/558] IDEA-157763 Settings repository for IDE: new UI (after review) --- .../ide/customize/CustomizeFeaturedPluginsStepPanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java index 1231e6b6c1ed..041acdf5858e 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java @@ -113,7 +113,7 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt JLabel warningLabel = null; if (isVIM || isCloud) { if (isCloud) { - warningLabel = createHTMLLabel("JBA account"); + warningLabel = createHTMLLabel("From your JetBrains account"); warningLabel.setIcon(AllIcons.General.BalloonInformation); } else { From 1dfcf10e4a145ce5e9f66c7775f80626c7945b3b Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Wed, 14 Dec 2016 19:38:47 +0300 Subject: [PATCH 153/558] Collect to move to the IDEA repo --- .../com/intellij/debugger/memory/action}/ClassesActionBase.java | 0 .../com/intellij/debugger/memory/action}/DebuggerTreeAction.java | 0 .../debugger/memory/action}/EnableBackgroundTrackingAction.java | 0 .../intellij/debugger/memory/action}/JumpToTypeSourceAction.java | 0 .../debugger/memory/action}/ShowClassesWithDiffAction.java | 0 .../debugger/memory/action}/ShowClassesWithInstanceAction.java | 0 .../com/intellij/debugger/memory/action}/ShowInstancesAction.java | 0 .../debugger/memory/action}/ShowInstancesByClassAction.java | 0 .../memory/action}/ShowInstancesFromClassesViewAction.java | 0 .../intellij/debugger/memory/action}/ShowNewInstancesAction.java | 0 .../com/intellij/debugger/memory/action}/ShowTrackedAction.java | 0 .../memory/action}/tracking/JumpToAllocationSourceAction.java | 0 .../memory/action}/tracking/TrackInstancesToggleAction.java | 0 .../debugger/memory/component}/CreationPositionTracker.java | 0 .../com/intellij/debugger/memory/component}/InstancesTracker.java | 0 .../intellij/debugger/memory/component}/MemoryViewManager.java | 0 .../debugger/memory/component}/MemoryViewManagerState.java | 0 .../intellij/debugger/memory/event}/InstancesTrackerListener.java | 0 .../debugger/memory/event}/MemoryViewManagerListener.java | 0 .../com/intellij/debugger/memory/sizeof}/RetainedSizeFetcher.java | 0 .../com/intellij/debugger/memory/sizeof}/SizeOfEvalStrategy.java | 0 .../debugger/memory/toolwindow}/MemoryViewToolWindowFactory.java | 0 .../com/intellij/debugger/memory/tracking}/BackgroundTracker.java | 0 .../intellij/debugger/memory/tracking}/ClassPreparedListener.java | 0 .../debugger/memory/tracking}/ConstructorInstancesTracker.java | 0 .../com/intellij/debugger/memory/tracking}/HashBasedTracking.java | 0 .../intellij/debugger/memory/tracking}/IdentityBasedTracking.java | 0 .../debugger/memory/tracking}/InstanceTrackingStrategy.java | 0 .../debugger/memory/tracking}/RetainReferencesTracking.java | 0 .../debugger/memory/tracking}/TrackerForNewInstances.java | 0 .../src/com/intellij/debugger/memory/tracking}/TrackingType.java | 0 .../debugger/memory/utils}/AbstractTableColumnDescriptor.java | 0 .../debugger/memory/utils}/AbstractTableModelWithColumns.java | 0 .../impl/src/com/intellij/debugger/memory/utils}/AndroidUtil.java | 0 .../src/com/intellij/debugger/memory/utils}/ErrorsValueGroup.java | 0 .../com/intellij/debugger/memory/utils}/InstanceJavaValue.java | 0 .../intellij/debugger/memory/utils}/InstanceValueDescriptor.java | 0 .../com/intellij/debugger/memory/utils}/InstancesProvider.java | 0 .../src/com/intellij/debugger/memory/utils}/KeyboardUtils.java | 0 .../intellij/debugger/memory/utils}/LowestPriorityCommand.java | 0 .../impl/src/com/intellij/debugger/memory/utils}/NamesUtils.java | 0 .../debugger/memory/utils}/SingleAlarmWithMutableDelay.java | 0 .../com/intellij/debugger/memory/utils}/StackFrameDescriptor.java | 0 .../com/intellij/debugger/memory/view}/ClassesFilteredView.java | 0 .../impl/src/com/intellij/debugger/memory/view}/ClassesTable.java | 0 .../debugger/memory/view}/ExpressionEditorWithHistory.java | 0 .../com/intellij/debugger/memory/view}/FilteringProgressView.java | 0 .../src/com/intellij/debugger/memory/view}/InstancesTree.java | 0 .../src/com/intellij/debugger/memory/view}/InstancesWindow.java | 0 .../debugger/memory/view}/InstancesWithStackFrameView.java | 0 .../src/com/intellij/debugger/memory/view}/StackFrameList.java | 0 .../src/com/intellij/debugger/memory/view}/StackFramePopup.java | 0 52 files changed, 0 insertions(+), 0 deletions(-) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/ClassesActionBase.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/DebuggerTreeAction.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/EnableBackgroundTrackingAction.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/JumpToTypeSourceAction.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/ShowClassesWithDiffAction.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/ShowClassesWithInstanceAction.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/ShowInstancesAction.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/ShowInstancesByClassAction.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/ShowInstancesFromClassesViewAction.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/ShowNewInstancesAction.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/ShowTrackedAction.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/tracking/JumpToAllocationSourceAction.java (100%) rename {action => java/debugger/impl/src/com/intellij/debugger/memory/action}/tracking/TrackInstancesToggleAction.java (100%) rename {component => java/debugger/impl/src/com/intellij/debugger/memory/component}/CreationPositionTracker.java (100%) rename {component => java/debugger/impl/src/com/intellij/debugger/memory/component}/InstancesTracker.java (100%) rename {component => java/debugger/impl/src/com/intellij/debugger/memory/component}/MemoryViewManager.java (100%) rename {component => java/debugger/impl/src/com/intellij/debugger/memory/component}/MemoryViewManagerState.java (100%) rename {event => java/debugger/impl/src/com/intellij/debugger/memory/event}/InstancesTrackerListener.java (100%) rename {event => java/debugger/impl/src/com/intellij/debugger/memory/event}/MemoryViewManagerListener.java (100%) rename {sizeof => java/debugger/impl/src/com/intellij/debugger/memory/sizeof}/RetainedSizeFetcher.java (100%) rename {sizeof => java/debugger/impl/src/com/intellij/debugger/memory/sizeof}/SizeOfEvalStrategy.java (100%) rename {toolwindow => java/debugger/impl/src/com/intellij/debugger/memory/toolwindow}/MemoryViewToolWindowFactory.java (100%) rename {tracking => java/debugger/impl/src/com/intellij/debugger/memory/tracking}/BackgroundTracker.java (100%) rename {tracking => java/debugger/impl/src/com/intellij/debugger/memory/tracking}/ClassPreparedListener.java (100%) rename {tracking => java/debugger/impl/src/com/intellij/debugger/memory/tracking}/ConstructorInstancesTracker.java (100%) rename {tracking => java/debugger/impl/src/com/intellij/debugger/memory/tracking}/HashBasedTracking.java (100%) rename {tracking => java/debugger/impl/src/com/intellij/debugger/memory/tracking}/IdentityBasedTracking.java (100%) rename {tracking => java/debugger/impl/src/com/intellij/debugger/memory/tracking}/InstanceTrackingStrategy.java (100%) rename {tracking => java/debugger/impl/src/com/intellij/debugger/memory/tracking}/RetainReferencesTracking.java (100%) rename {tracking => java/debugger/impl/src/com/intellij/debugger/memory/tracking}/TrackerForNewInstances.java (100%) rename {tracking => java/debugger/impl/src/com/intellij/debugger/memory/tracking}/TrackingType.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/AbstractTableColumnDescriptor.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/AbstractTableModelWithColumns.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/AndroidUtil.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/ErrorsValueGroup.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/InstanceJavaValue.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/InstanceValueDescriptor.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/InstancesProvider.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/KeyboardUtils.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/LowestPriorityCommand.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/NamesUtils.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/SingleAlarmWithMutableDelay.java (100%) rename {utils => java/debugger/impl/src/com/intellij/debugger/memory/utils}/StackFrameDescriptor.java (100%) rename {view => java/debugger/impl/src/com/intellij/debugger/memory/view}/ClassesFilteredView.java (100%) rename {view => java/debugger/impl/src/com/intellij/debugger/memory/view}/ClassesTable.java (100%) rename {view => java/debugger/impl/src/com/intellij/debugger/memory/view}/ExpressionEditorWithHistory.java (100%) rename {view => java/debugger/impl/src/com/intellij/debugger/memory/view}/FilteringProgressView.java (100%) rename {view => java/debugger/impl/src/com/intellij/debugger/memory/view}/InstancesTree.java (100%) rename {view => java/debugger/impl/src/com/intellij/debugger/memory/view}/InstancesWindow.java (100%) rename {view => java/debugger/impl/src/com/intellij/debugger/memory/view}/InstancesWithStackFrameView.java (100%) rename {view => java/debugger/impl/src/com/intellij/debugger/memory/view}/StackFrameList.java (100%) rename {view => java/debugger/impl/src/com/intellij/debugger/memory/view}/StackFramePopup.java (100%) diff --git a/action/ClassesActionBase.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ClassesActionBase.java similarity index 100% rename from action/ClassesActionBase.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/ClassesActionBase.java diff --git a/action/DebuggerTreeAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/DebuggerTreeAction.java similarity index 100% rename from action/DebuggerTreeAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/DebuggerTreeAction.java diff --git a/action/EnableBackgroundTrackingAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/EnableBackgroundTrackingAction.java similarity index 100% rename from action/EnableBackgroundTrackingAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/EnableBackgroundTrackingAction.java diff --git a/action/JumpToTypeSourceAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/JumpToTypeSourceAction.java similarity index 100% rename from action/JumpToTypeSourceAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/JumpToTypeSourceAction.java diff --git a/action/ShowClassesWithDiffAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithDiffAction.java similarity index 100% rename from action/ShowClassesWithDiffAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithDiffAction.java diff --git a/action/ShowClassesWithInstanceAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithInstanceAction.java similarity index 100% rename from action/ShowClassesWithInstanceAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithInstanceAction.java diff --git a/action/ShowInstancesAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesAction.java similarity index 100% rename from action/ShowInstancesAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesAction.java diff --git a/action/ShowInstancesByClassAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesByClassAction.java similarity index 100% rename from action/ShowInstancesByClassAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesByClassAction.java diff --git a/action/ShowInstancesFromClassesViewAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesFromClassesViewAction.java similarity index 100% rename from action/ShowInstancesFromClassesViewAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesFromClassesViewAction.java diff --git a/action/ShowNewInstancesAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowNewInstancesAction.java similarity index 100% rename from action/ShowNewInstancesAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/ShowNewInstancesAction.java diff --git a/action/ShowTrackedAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowTrackedAction.java similarity index 100% rename from action/ShowTrackedAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/ShowTrackedAction.java diff --git a/action/tracking/JumpToAllocationSourceAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/JumpToAllocationSourceAction.java similarity index 100% rename from action/tracking/JumpToAllocationSourceAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/JumpToAllocationSourceAction.java diff --git a/action/tracking/TrackInstancesToggleAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/TrackInstancesToggleAction.java similarity index 100% rename from action/tracking/TrackInstancesToggleAction.java rename to java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/TrackInstancesToggleAction.java diff --git a/component/CreationPositionTracker.java b/java/debugger/impl/src/com/intellij/debugger/memory/component/CreationPositionTracker.java similarity index 100% rename from component/CreationPositionTracker.java rename to java/debugger/impl/src/com/intellij/debugger/memory/component/CreationPositionTracker.java diff --git a/component/InstancesTracker.java b/java/debugger/impl/src/com/intellij/debugger/memory/component/InstancesTracker.java similarity index 100% rename from component/InstancesTracker.java rename to java/debugger/impl/src/com/intellij/debugger/memory/component/InstancesTracker.java diff --git a/component/MemoryViewManager.java b/java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManager.java similarity index 100% rename from component/MemoryViewManager.java rename to java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManager.java diff --git a/component/MemoryViewManagerState.java b/java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManagerState.java similarity index 100% rename from component/MemoryViewManagerState.java rename to java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManagerState.java diff --git a/event/InstancesTrackerListener.java b/java/debugger/impl/src/com/intellij/debugger/memory/event/InstancesTrackerListener.java similarity index 100% rename from event/InstancesTrackerListener.java rename to java/debugger/impl/src/com/intellij/debugger/memory/event/InstancesTrackerListener.java diff --git a/event/MemoryViewManagerListener.java b/java/debugger/impl/src/com/intellij/debugger/memory/event/MemoryViewManagerListener.java similarity index 100% rename from event/MemoryViewManagerListener.java rename to java/debugger/impl/src/com/intellij/debugger/memory/event/MemoryViewManagerListener.java diff --git a/sizeof/RetainedSizeFetcher.java b/java/debugger/impl/src/com/intellij/debugger/memory/sizeof/RetainedSizeFetcher.java similarity index 100% rename from sizeof/RetainedSizeFetcher.java rename to java/debugger/impl/src/com/intellij/debugger/memory/sizeof/RetainedSizeFetcher.java diff --git a/sizeof/SizeOfEvalStrategy.java b/java/debugger/impl/src/com/intellij/debugger/memory/sizeof/SizeOfEvalStrategy.java similarity index 100% rename from sizeof/SizeOfEvalStrategy.java rename to java/debugger/impl/src/com/intellij/debugger/memory/sizeof/SizeOfEvalStrategy.java diff --git a/toolwindow/MemoryViewToolWindowFactory.java b/java/debugger/impl/src/com/intellij/debugger/memory/toolwindow/MemoryViewToolWindowFactory.java similarity index 100% rename from toolwindow/MemoryViewToolWindowFactory.java rename to java/debugger/impl/src/com/intellij/debugger/memory/toolwindow/MemoryViewToolWindowFactory.java diff --git a/tracking/BackgroundTracker.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/BackgroundTracker.java similarity index 100% rename from tracking/BackgroundTracker.java rename to java/debugger/impl/src/com/intellij/debugger/memory/tracking/BackgroundTracker.java diff --git a/tracking/ClassPreparedListener.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ClassPreparedListener.java similarity index 100% rename from tracking/ClassPreparedListener.java rename to java/debugger/impl/src/com/intellij/debugger/memory/tracking/ClassPreparedListener.java diff --git a/tracking/ConstructorInstancesTracker.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ConstructorInstancesTracker.java similarity index 100% rename from tracking/ConstructorInstancesTracker.java rename to java/debugger/impl/src/com/intellij/debugger/memory/tracking/ConstructorInstancesTracker.java diff --git a/tracking/HashBasedTracking.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/HashBasedTracking.java similarity index 100% rename from tracking/HashBasedTracking.java rename to java/debugger/impl/src/com/intellij/debugger/memory/tracking/HashBasedTracking.java diff --git a/tracking/IdentityBasedTracking.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/IdentityBasedTracking.java similarity index 100% rename from tracking/IdentityBasedTracking.java rename to java/debugger/impl/src/com/intellij/debugger/memory/tracking/IdentityBasedTracking.java diff --git a/tracking/InstanceTrackingStrategy.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/InstanceTrackingStrategy.java similarity index 100% rename from tracking/InstanceTrackingStrategy.java rename to java/debugger/impl/src/com/intellij/debugger/memory/tracking/InstanceTrackingStrategy.java diff --git a/tracking/RetainReferencesTracking.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/RetainReferencesTracking.java similarity index 100% rename from tracking/RetainReferencesTracking.java rename to java/debugger/impl/src/com/intellij/debugger/memory/tracking/RetainReferencesTracking.java diff --git a/tracking/TrackerForNewInstances.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackerForNewInstances.java similarity index 100% rename from tracking/TrackerForNewInstances.java rename to java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackerForNewInstances.java diff --git a/tracking/TrackingType.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackingType.java similarity index 100% rename from tracking/TrackingType.java rename to java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackingType.java diff --git a/utils/AbstractTableColumnDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableColumnDescriptor.java similarity index 100% rename from utils/AbstractTableColumnDescriptor.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableColumnDescriptor.java diff --git a/utils/AbstractTableModelWithColumns.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableModelWithColumns.java similarity index 100% rename from utils/AbstractTableModelWithColumns.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableModelWithColumns.java diff --git a/utils/AndroidUtil.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/AndroidUtil.java similarity index 100% rename from utils/AndroidUtil.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/AndroidUtil.java diff --git a/utils/ErrorsValueGroup.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/ErrorsValueGroup.java similarity index 100% rename from utils/ErrorsValueGroup.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/ErrorsValueGroup.java diff --git a/utils/InstanceJavaValue.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceJavaValue.java similarity index 100% rename from utils/InstanceJavaValue.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceJavaValue.java diff --git a/utils/InstanceValueDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceValueDescriptor.java similarity index 100% rename from utils/InstanceValueDescriptor.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceValueDescriptor.java diff --git a/utils/InstancesProvider.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstancesProvider.java similarity index 100% rename from utils/InstancesProvider.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/InstancesProvider.java diff --git a/utils/KeyboardUtils.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/KeyboardUtils.java similarity index 100% rename from utils/KeyboardUtils.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/KeyboardUtils.java diff --git a/utils/LowestPriorityCommand.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/LowestPriorityCommand.java similarity index 100% rename from utils/LowestPriorityCommand.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/LowestPriorityCommand.java diff --git a/utils/NamesUtils.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/NamesUtils.java similarity index 100% rename from utils/NamesUtils.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/NamesUtils.java diff --git a/utils/SingleAlarmWithMutableDelay.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/SingleAlarmWithMutableDelay.java similarity index 100% rename from utils/SingleAlarmWithMutableDelay.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/SingleAlarmWithMutableDelay.java diff --git a/utils/StackFrameDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/StackFrameDescriptor.java similarity index 100% rename from utils/StackFrameDescriptor.java rename to java/debugger/impl/src/com/intellij/debugger/memory/utils/StackFrameDescriptor.java diff --git a/view/ClassesFilteredView.java b/java/debugger/impl/src/com/intellij/debugger/memory/view/ClassesFilteredView.java similarity index 100% rename from view/ClassesFilteredView.java rename to java/debugger/impl/src/com/intellij/debugger/memory/view/ClassesFilteredView.java diff --git a/view/ClassesTable.java b/java/debugger/impl/src/com/intellij/debugger/memory/view/ClassesTable.java similarity index 100% rename from view/ClassesTable.java rename to java/debugger/impl/src/com/intellij/debugger/memory/view/ClassesTable.java diff --git a/view/ExpressionEditorWithHistory.java b/java/debugger/impl/src/com/intellij/debugger/memory/view/ExpressionEditorWithHistory.java similarity index 100% rename from view/ExpressionEditorWithHistory.java rename to java/debugger/impl/src/com/intellij/debugger/memory/view/ExpressionEditorWithHistory.java diff --git a/view/FilteringProgressView.java b/java/debugger/impl/src/com/intellij/debugger/memory/view/FilteringProgressView.java similarity index 100% rename from view/FilteringProgressView.java rename to java/debugger/impl/src/com/intellij/debugger/memory/view/FilteringProgressView.java diff --git a/view/InstancesTree.java b/java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesTree.java similarity index 100% rename from view/InstancesTree.java rename to java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesTree.java diff --git a/view/InstancesWindow.java b/java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesWindow.java similarity index 100% rename from view/InstancesWindow.java rename to java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesWindow.java diff --git a/view/InstancesWithStackFrameView.java b/java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesWithStackFrameView.java similarity index 100% rename from view/InstancesWithStackFrameView.java rename to java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesWithStackFrameView.java diff --git a/view/StackFrameList.java b/java/debugger/impl/src/com/intellij/debugger/memory/view/StackFrameList.java similarity index 100% rename from view/StackFrameList.java rename to java/debugger/impl/src/com/intellij/debugger/memory/view/StackFrameList.java diff --git a/view/StackFramePopup.java b/java/debugger/impl/src/com/intellij/debugger/memory/view/StackFramePopup.java similarity index 100% rename from view/StackFramePopup.java rename to java/debugger/impl/src/com/intellij/debugger/memory/view/StackFramePopup.java From 632fee20aa411de2f52ee2ff8f8565a148d185c1 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Wed, 14 Dec 2016 19:30:34 +0300 Subject: [PATCH 154/558] [groovy] fix expection on super reference in trait without supertypes (EA-74829) --- .../expressions/GrReferenceExpressionImpl.java | 4 +++- .../lang/highlighting/Gr23HighlightingTest.groovy | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java index 423045a26066..792e44cc1692 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java @@ -262,7 +262,9 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl 0) { + return PsiIntersectionType.createIntersection(ArrayUtil.reverseArray(superTypes)); + } } return factory.createType((PsiClass)resolved); } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/Gr23HighlightingTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/Gr23HighlightingTest.groovy index 6ea50bee7e80..b52e95186116 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/Gr23HighlightingTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/Gr23HighlightingTest.groovy @@ -475,4 +475,14 @@ class A implements T {} new A().foo ''', GroovyUnusedDeclarationInspection } + + void 'test no exception on super reference in trait without supertypes'() { + testHighlighting '''\ +trait SimpleTrait { + void foo() { + super.foo() + } +} +''' + } } From 08f38405adc5d56f73fac65d14552d22250907c5 Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Wed, 14 Dec 2016 15:25:55 +0100 Subject: [PATCH 155/558] support navigation in stacktraces where class fqns are prefixed with module name (IDEA-165456) --- .../navigation/ExceptionFilterTest.java | 38 +++++++++++++++++++ .../execution/filters/ExceptionWorker.java | 4 +- 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 java/java-tests/testSrc/com/intellij/navigation/ExceptionFilterTest.java diff --git a/java/java-tests/testSrc/com/intellij/navigation/ExceptionFilterTest.java b/java/java-tests/testSrc/com/intellij/navigation/ExceptionFilterTest.java new file mode 100644 index 000000000000..4072ef36b250 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/navigation/ExceptionFilterTest.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2016 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.navigation; + +import com.intellij.execution.filters.ExceptionInfoCache; +import com.intellij.execution.filters.ExceptionWorker; +import com.intellij.psi.PsiClass; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase; + +public class ExceptionFilterTest extends JavaCodeInsightFixtureTestCase { + + public void testJava9ModulePrefixed() throws Throwable { + PsiClass psiClass = myFixture.addClass("package p; public class A {\n" + + " public void foo() {}\n" + + "}"); + ExceptionWorker worker = new ExceptionWorker(new ExceptionInfoCache(GlobalSearchScope.projectScope(getProject()))); + String line = "at mod.name/p.A.foo(A.java:2)"; + worker.execute(line, line.length()); + PsiClass aClass = worker.getPsiClass(); + assertNotNull(aClass); + assertEquals(psiClass, aClass); + } +} \ No newline at end of file diff --git a/java/openapi/src/com/intellij/execution/filters/ExceptionWorker.java b/java/openapi/src/com/intellij/execution/filters/ExceptionWorker.java index 78183569b714..c2c97a9e310d 100644 --- a/java/openapi/src/com/intellij/execution/filters/ExceptionWorker.java +++ b/java/openapi/src/com/intellij/execution/filters/ExceptionWorker.java @@ -196,9 +196,11 @@ public class ExceptionWorker { final int dotIdx = line.lastIndexOf('.', lParenIdx); if (dotIdx < 0 || dotIdx < startIdx) return null; + int moduleIdx = line.indexOf('/'); + int classNameIdx = moduleIdx > -1 && moduleIdx < lParenIdx ? moduleIdx + 1 : startIdx + 1 + (startIdx >= 0 ? AT.length() : 0); // class, method, link - return Trinity.create(new TextRange(startIdx + 1 + (startIdx >= 0 ? AT.length() : 0), handleSpaces(line, dotIdx, -1, true)), + return Trinity.create(new TextRange(classNameIdx, handleSpaces(line, dotIdx, -1, true)), new TextRange(handleSpaces(line, dotIdx + 1, 1, true), handleSpaces(line, lParenIdx + 1, -1, true)), new TextRange(lParenIdx, rParenIdx)); } From 9544b4c8099f58e95d8f5fe0f207b40280559333 Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Wed, 14 Dec 2016 17:44:26 +0100 Subject: [PATCH 156/558] highlight inaccessible array component type (IDEA-165434) --- .../src/com/intellij/psi/util/PsiUtil.java | 26 +++++++++++++------ ...calClassUsedinArrayTypeOutsidePackage.java | 7 +++++ .../LightAdvHighlightingFixtureTest.java | 7 +++++ 3 files changed, 32 insertions(+), 8 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advFixture/PackageLocalClassUsedinArrayTypeOutsidePackage.java diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java index d79299b27445..3120f2e46a23 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java @@ -116,6 +116,19 @@ public final class PsiUtil extends PsiUtilCore { public static JavaResolveResult getAccessObjectClass(@NotNull PsiExpression expression) { if (expression instanceof PsiSuperExpression) return JavaResolveResult.EMPTY; PsiType type = expression.getType(); + final JavaResolveResult accessObject = getAccessObjectClass(type, expression.getProject()); + if (accessObject != null) return accessObject; + + if (type == null && expression instanceof PsiReferenceExpression) { + JavaResolveResult resolveResult = ((PsiReferenceExpression)expression).advancedResolve(false); + if (resolveResult.getElement() instanceof PsiClass) { + return resolveResult; + } + } + return JavaResolveResult.EMPTY; + } + + private static JavaResolveResult getAccessObjectClass(PsiType type, Project project) { if (type instanceof PsiClassType) { return ((PsiClassType)type).resolveGenerics(); } @@ -132,22 +145,19 @@ public final class PsiUtil extends PsiUtilCore { final PsiClass resolved = ((PsiClassType)upperBound).resolve(); final PsiFile containingFile = resolved != null ? resolved.getContainingFile() : null; final String packageName = containingFile instanceof PsiClassOwner ? ((PsiClassOwner)containingFile).getPackageName() : null; - String classText = StringUtil.isEmptyOrSpaces(packageName) ? "" : "package " +packageName + ";\n "; + String classText = StringUtil.isEmptyOrSpaces(packageName) ? "" : "package " + packageName + ";\n "; classText += "class I {}"; final PsiJavaFile file = - (PsiJavaFile)PsiFileFactory.getInstance(expression.getProject()).createFileFromText("inference_dummy.java", JavaLanguage.INSTANCE, classText); + (PsiJavaFile)PsiFileFactory.getInstance(project).createFileFromText("inference_dummy.java", JavaLanguage.INSTANCE, classText); final PsiTypeParameter freshParameter = file.getClasses()[0].getTypeParameters()[0]; return new ClassCandidateInfo(freshParameter, PsiSubstitutor.EMPTY); } } - if (type == null && expression instanceof PsiReferenceExpression) { - JavaResolveResult resolveResult = ((PsiReferenceExpression)expression).advancedResolve(false); - if (resolveResult.getElement() instanceof PsiClass) { - return resolveResult; - } + if (type instanceof PsiArrayType) { + return getAccessObjectClass(((PsiArrayType)type).getComponentType(), project); } - return JavaResolveResult.EMPTY; + return null; } public static boolean isConstantExpression(@Nullable PsiExpression expression) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advFixture/PackageLocalClassUsedinArrayTypeOutsidePackage.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advFixture/PackageLocalClassUsedinArrayTypeOutsidePackage.java new file mode 100644 index 000000000000..2901de562e1a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advFixture/PackageLocalClassUsedinArrayTypeOutsidePackage.java @@ -0,0 +1,7 @@ +package b; +class C { + { + int u = a.B.getAs().length; + a.B.getAs().clone(); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingFixtureTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingFixtureTest.java index e08e7e9f8923..5b3ecddbe758 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingFixtureTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingFixtureTest.java @@ -92,6 +92,13 @@ public class LightAdvHighlightingFixtureTest extends LightCodeInsightFixtureTest myFixture.checkHighlighting(); } + public void testPackageLocalClassUsedinArrayTypeOutsidePackage() throws Exception { + myFixture.addClass("package a; class A {}"); + myFixture.addClass("package a; public class B {public static A[] getAs() {return null;}}"); + myFixture.configureByFile(getTestName(false) + ".java"); + myFixture.checkHighlighting(); + } + @Override protected String getBasePath() { return JavaTestUtil.getRelativeJavaTestDataPath() + "/codeInsight/daemonCodeAnalyzer/advFixture"; From aa73c8c21a0ba3e748f793ea552a48ae6827ee64 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Wed, 14 Dec 2016 20:58:26 +0300 Subject: [PATCH 157/558] [groovy] completely get gid of byShape flag --- .../assignment/GrListOrMapInfo.java | 4 ++-- .../expressions/path/GrIndexPropertyImpl.java | 9 +++++--- .../plugins/groovy/lang/psi/util/PsiUtil.java | 22 +++++++++---------- .../processors/GroovyResolverProcessor.java | 2 +- .../convertToJava/GenerationUtil.java | 2 +- 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GrListOrMapInfo.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GrListOrMapInfo.java index 2918126327b9..c9f9dde3b413 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GrListOrMapInfo.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GrListOrMapInfo.java @@ -59,11 +59,11 @@ public class GrListOrMapInfo implements ConstructorCallInfo { GrNamedArgument[] args = myListOrMap.getNamedArguments(); if (args.length == 0) return new PsiType[]{myListOrMap.getType()}; - return PsiUtil.getArgumentTypes(args, GrExpression.EMPTY_ARRAY, GrClosableBlock.EMPTY_ARRAY, true, null, false); + return PsiUtil.getArgumentTypes(args, GrExpression.EMPTY_ARRAY, GrClosableBlock.EMPTY_ARRAY, true, null); } else { GrExpression[] args = myListOrMap.getInitializers(); - return PsiUtil.getArgumentTypes(GrNamedArgument.EMPTY_ARRAY, args, GrClosableBlock.EMPTY_ARRAY, true, null, false); + return PsiUtil.getArgumentTypes(GrNamedArgument.EMPTY_ARRAY, args, GrClosableBlock.EMPTY_ARRAY, true, null); } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyImpl.java index 0442a86c5964..c94e89b916c2 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyImpl.java @@ -22,7 +22,10 @@ import com.intellij.psi.*; import com.intellij.psi.impl.source.resolve.ResolveCache; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.InheritanceUtil; -import com.intellij.util.*; +import com.intellij.util.ArrayUtil; +import com.intellij.util.Function; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.NullableFunction; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; @@ -117,7 +120,7 @@ public class GrIndexPropertyImpl extends GrExpressionImpl implements GrIndexProp //don't use short PsiUtil.getArgumentTypes(...) because it use incorrect 'isSetter' value PsiType[] args = PsiUtil - .getArgumentTypes(argList.getNamedArguments(), argList.getExpressionArguments(), GrClosableBlock.EMPTY_ARRAY, true, null, false); + .getArgumentTypes(argList.getNamedArguments(), argList.getExpressionArguments(), GrClosableBlock.EMPTY_ARRAY, true, null); final GroovyResolveResult candidate = PsiImplUtil.extractUniqueResult(candidates); final PsiElement element = candidate.getElement(); if (element instanceof PsiNamedElement) { @@ -158,7 +161,7 @@ public class GrIndexPropertyImpl extends GrExpressionImpl implements GrIndexProp GrArgumentList argList = getArgumentList(); //don't use short PsiUtil.getArgumentTypes(...) because it use incorrect 'isSetter' value - PsiType[] argTypes = PsiUtil.getArgumentTypes(argList.getNamedArguments(), argList.getExpressionArguments(), GrClosableBlock.EMPTY_ARRAY, true, upToArgument, false); + PsiType[] argTypes = PsiUtil.getArgumentTypes(argList.getNamedArguments(), argList.getExpressionArguments(), GrClosableBlock.EMPTY_ARRAY, true, upToArgument); if (argTypes == null) return GroovyResolveResult.EMPTY_ARRAY; final GlobalSearchScope resolveScope = getResolveScope(); diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java index 2b95cb243e1b..464a0452fa97 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java @@ -239,19 +239,18 @@ public class PsiUtil { @Nullable public static PsiType[] getArgumentTypes(@Nullable PsiElement place, boolean nullAsBottom) { - return getArgumentTypes(place, nullAsBottom, null, false); + return getArgumentTypes(place, nullAsBottom, null); } @Nullable public static PsiType[] getArgumentTypes(@Nullable PsiElement place, boolean nullAsBottom, - @Nullable GrExpression stopAt, - boolean byShape) { + @Nullable GrExpression stopAt) { PsiElement parent = place instanceof GrEnumConstant ? place : place != null ? place.getParent() : null; if (parent instanceof GrIndexProperty) { GrIndexProperty index = (GrIndexProperty)parent; - PsiType[] argTypes = getArgumentTypes(index.getNamedArguments(), index.getExpressionArguments(), index.getClosureArguments(), nullAsBottom, stopAt, byShape); + PsiType[] argTypes = getArgumentTypes(index.getNamedArguments(), index.getExpressionArguments(), index.getClosureArguments(), nullAsBottom, stopAt); if (isLValue(index) && argTypes != null) { PsiType rawInitializer = TypeInferenceHelper.getInitializerTypeFor(index); @@ -268,15 +267,17 @@ public class PsiUtil { GrExpression[] expressions = call.getExpressionArguments(); GrClosableBlock[] closures = call.getClosureArguments(); - return getArgumentTypes(namedArgs, expressions, closures, nullAsBottom, stopAt, byShape); + return getArgumentTypes(namedArgs, expressions, closures, nullAsBottom, stopAt); } else if (parent instanceof GrAnonymousClassDefinition) { final GrArgumentList argList = ((GrAnonymousClassDefinition)parent).getArgumentListGroovy(); if (argList == null) { - return getArgumentTypes(GrNamedArgument.EMPTY_ARRAY, GrExpression.EMPTY_ARRAY, GrClosableBlock.EMPTY_ARRAY, nullAsBottom, stopAt, byShape); + return getArgumentTypes(GrNamedArgument.EMPTY_ARRAY, GrExpression.EMPTY_ARRAY, GrClosableBlock.EMPTY_ARRAY, nullAsBottom, stopAt + ); } else { - return getArgumentTypes(argList.getNamedArguments(), argList.getExpressionArguments(), GrClosableBlock.EMPTY_ARRAY, nullAsBottom, stopAt, byShape); + return getArgumentTypes(argList.getNamedArguments(), argList.getExpressionArguments(), GrClosableBlock.EMPTY_ARRAY, nullAsBottom, stopAt + ); } } else if (parent instanceof GrBinaryExpression || parent instanceof GrAssignmentExpression) { @@ -297,7 +298,7 @@ public class PsiUtil { @Nullable public static PsiType[] getArgumentTypes(GrArgumentList argList) { - return getArgumentTypes(argList, false, null, false); + return getArgumentTypes(argList, false, null); } @Nullable @@ -305,13 +306,12 @@ public class PsiUtil { @NotNull GrExpression[] expressions, @NotNull GrClosableBlock[] closures, boolean nullAsBottom, - @Nullable GrExpression stopAt, - boolean byShape) { + @Nullable GrExpression stopAt) { List result = new ArrayList<>(); if (namedArgs.length > 0) { GrNamedArgument context = namedArgs[0]; - result.add(GrMapType.createFromNamedArgs(context, byShape ? GrNamedArgument.EMPTY_ARRAY : namedArgs)); + result.add(GrMapType.createFromNamedArgs(context, namedArgs)); } for (GrExpression expression : expressions) { diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/GroovyResolverProcessor.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/GroovyResolverProcessor.java index 099b0ccb5dd9..6900497a999b 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/GroovyResolverProcessor.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/GroovyResolverProcessor.java @@ -97,7 +97,7 @@ public abstract class GroovyResolverProcessor implements PsiScopeProcessor, Elem myThisType = PsiImplUtil.getQualifierType(ref); myTypeArguments = ref.getTypeArguments(); if (kinds.contains(GroovyResolveKind.METHOD) || myIsLValue) { - myArgumentTypesNonErased = PsiUtil.getArgumentTypes(ref, false, myUpToArgument, false); + myArgumentTypesNonErased = PsiUtil.getArgumentTypes(ref, false, myUpToArgument); myArgumentTypes = eraseTypes(myArgumentTypesNonErased); } else { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToJava/GenerationUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToJava/GenerationUtil.java index f66fdcc7e8e7..7517d9443127 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToJava/GenerationUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToJava/GenerationUtil.java @@ -157,7 +157,7 @@ public class GenerationUtil { type = caller.getType(); } if (type != null) { - final PsiType[] argumentTypes = PsiUtil.getArgumentTypes(namedArgs, exprs, closureArgs, false, null, false); + final PsiType[] argumentTypes = PsiUtil.getArgumentTypes(namedArgs, exprs, closureArgs, false, null); final GroovyResolveResult[] candidates = ResolveUtil.getMethodCandidates(type, methodName, psiContext, argumentTypes); call = PsiImplUtil.extractUniqueResult(candidates); } From d5068314b4f3e4ccfe980754c4aef2820b41dca2 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Mon, 5 Dec 2016 19:16:51 +0300 Subject: [PATCH 158/558] NPE: AddVariableInitializerFix.runAssignmentTemplate EA-92483 --- .../daemon/impl/quickfix/AddVariableInitializerFix.java | 3 ++- .../impl/quickfix/InitializeFinalFieldInConstructorFix.java | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddVariableInitializerFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddVariableInitializerFix.java index 29690467d913..9fdbd9975d26 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddVariableInitializerFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddVariableInitializerFix.java @@ -28,6 +28,7 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTypesUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.ObjectUtils; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -94,7 +95,7 @@ public class AddVariableInitializerFix implements IntentionAction { @Nullable Editor editor) { if (editor == null) return; LOG.assertTrue(!initializers.isEmpty()); - final PsiExpression initializer = ContainerUtil.getFirstItem(initializers); + final PsiExpression initializer = ObjectUtils.notNull(ContainerUtil.getFirstItem(initializers)); PsiElement context = initializers.size() == 1 ? initializer : PsiTreeUtil.findCommonParent(initializers); PsiDocumentManager.getInstance(initializer.getProject()).doPostponedOperationsAndUnblockDocument(editor.getDocument()); final TemplateBuilderImpl builder = (TemplateBuilderImpl)TemplateBuilderFactory.getInstance().createTemplateBuilder(context); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/InitializeFinalFieldInConstructorFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/InitializeFinalFieldInConstructorFix.java index 76d7d473d7f4..f99520fcac2f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/InitializeFinalFieldInConstructorFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/InitializeFinalFieldInConstructorFix.java @@ -100,7 +100,10 @@ public class InitializeFinalFieldInConstructorFix implements IntentionAction { final LookupElement[] suggestedInitializers = AddVariableInitializerFix.suggestInitializer(field); for (PsiMethod constructor : constructors) { - rExpressions.add(addFieldInitialization(constructor, suggestedInitializers, field, project)); + PsiExpression initialization = addFieldInitialization(constructor, suggestedInitializers, field, project); + if (initialization != null) { + rExpressions.add(initialization); + } } AddVariableInitializerFix.runAssignmentTemplate(rExpressions, suggestedInitializers, editor); } From c3a779de1b9225e3017a06b2bd49eb2a6c4cec1e Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Mon, 5 Dec 2016 19:20:08 +0300 Subject: [PATCH 159/558] CollectionAddAllCanBeReplacedWithConstructorInspection: replace log assertions with conditional if statements (EA-92609) --- ...ctionAddAllCanBeReplacedWithConstructorInspection.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInspection/CollectionAddAllCanBeReplacedWithConstructorInspection.java b/java/java-impl/src/com/intellij/codeInspection/CollectionAddAllCanBeReplacedWithConstructorInspection.java index b5bd14915bf5..941b3a5a5fc2 100644 --- a/java/java-impl/src/com/intellij/codeInspection/CollectionAddAllCanBeReplacedWithConstructorInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/CollectionAddAllCanBeReplacedWithConstructorInspection.java @@ -247,14 +247,12 @@ public class CollectionAddAllCanBeReplacedWithConstructorInspection extends Base @Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { final PsiMethodCallExpression methodCallExpression = myMethodCallExpression.getElement(); - - LOG.assertTrue(methodCallExpression != null); - + if (methodCallExpression == null) return; final PsiElement parameter = methodCallExpression.getArgumentList().getExpressions()[0].copy(); final PsiNewExpression element = myAssignmentExpression.getElement(); - LOG.assertTrue(element != null); + if (element == null) return; final PsiExpressionList constructorArguments = element.getArgumentList(); - LOG.assertTrue(constructorArguments != null); + if (constructorArguments == null) return; constructorArguments.add(parameter); methodCallExpression.delete(); } From ddfe441a38bfee9d29cbfa0030e1165a2964862b Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Wed, 14 Dec 2016 21:12:18 +0300 Subject: [PATCH 160/558] javac indices: enable by default --- platform/util/resources/misc/registry.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 6aa565b5dca6..8704d58ded7e 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -916,7 +916,7 @@ ide.projectView.globalOptions.description=Make Project View options such as auto ide.projectView.ProjectViewPaneTreeStructure.BuildChildrenInBackground=true ide.projectView.ProjectViewPaneTreeStructure.BuildChildrenInBackground.description=Temporary ability to control a tree building for the Project View pane -compiler.ref.index=false +compiler.ref.index=true compiler.ref.index.description=Enables find usages using references from compiler indices compiler.ref.index.restartRequired=true build.manager.async.fs.events=false From 846cc82c3f8a9bc59044f46fb22be54b562918a9 Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Wed, 14 Dec 2016 19:14:24 +0100 Subject: [PATCH 161/558] do not fail compilation because of problems in custom extensions; exceptions from custom extensions are now logged --- .../src/com/intellij/compiler/CompilerManagerImpl.java | 6 ++++++ .../src/org/jetbrains/jps/javac/JavacMain.java | 8 +++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java b/java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java index 709a52a91888..edc9bac68de5 100644 --- a/java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java +++ b/java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java @@ -24,6 +24,7 @@ import com.intellij.openapi.compiler.*; import com.intellij.openapi.compiler.Compiler; import com.intellij.openapi.compiler.util.InspectionValidator; import com.intellij.openapi.compiler.util.InspectionValidatorWrapper; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.StdFileTypes; @@ -64,6 +65,8 @@ import java.util.*; import java.util.concurrent.Semaphore; public class CompilerManagerImpl extends CompilerManager { + private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.CompilerManagerImpl"); + private final Project myProject; private final List myCompilers = new ArrayList<>(); @@ -507,6 +510,9 @@ public class CompilerManagerImpl extends CompilerManager { public void outputLineAvailable(String line) { // for debugging purposes uncomment this line //System.out.println(line); + if (line != null && line.startsWith(ExternalJavacManager.STDERR_LINE_PREFIX)) { + LOG.info(line.trim()); + } } public void registerImports(String className, Collection imports, Collection staticImports) { diff --git a/jps/jps-builders-6/src/org/jetbrains/jps/javac/JavacMain.java b/jps/jps-builders-6/src/org/jetbrains/jps/javac/JavacMain.java index 333d125018d5..0de00ccf2c19 100644 --- a/jps/jps-builders-6/src/org/jetbrains/jps/javac/JavacMain.java +++ b/jps/jps-builders-6/src/org/jetbrains/jps/javac/JavacMain.java @@ -167,7 +167,13 @@ public class JavacMain { out, wrapWithCallDispatcher(fileManager), diagnosticConsumer, _options, null, fileManager.getJavaFileObjectsFromFiles(sources) ); for (JavaCompilerToolExtension extension : JavaCompilerToolExtension.getExtensions()) { - extension.beforeCompileTaskExecution(compilingTool, task, _options, diagnosticConsumer); + try { + extension.beforeCompileTaskExecution(compilingTool, task, _options, diagnosticConsumer); + } + catch (Throwable e) { + fileManager.getContext().reportMessage(Diagnostic.Kind.MANDATORY_WARNING, extension.getId() + ": " + e.getMessage()); + e.printStackTrace(System.err); + } } //if (!IS_VM_6_VERSION) { //todo! From d0821c6651bb525675c93b7089a83fbaf60eca6a Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Wed, 14 Dec 2016 21:42:30 +0300 Subject: [PATCH 162/558] revert javac indices: enable by default --- platform/util/resources/misc/registry.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 8704d58ded7e..6aa565b5dca6 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -916,7 +916,7 @@ ide.projectView.globalOptions.description=Make Project View options such as auto ide.projectView.ProjectViewPaneTreeStructure.BuildChildrenInBackground=true ide.projectView.ProjectViewPaneTreeStructure.BuildChildrenInBackground.description=Temporary ability to control a tree building for the Project View pane -compiler.ref.index=true +compiler.ref.index=false compiler.ref.index.description=Enables find usages using references from compiler indices compiler.ref.index.restartRequired=true build.manager.async.fs.events=false From 23e75561bc85eba9b879b53ad670d5c372b26f2f Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Wed, 14 Dec 2016 22:27:15 +0300 Subject: [PATCH 163/558] javac indices: enable by default & disable in tests, again --- .../src/com/intellij/compiler/CompilerReferenceService.java | 4 +++- .../com/intellij/compiler/CompilerReferencesTestBase.java | 2 ++ platform/util/resources/misc/registry.properties | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/java/java-indexing-impl/src/com/intellij/compiler/CompilerReferenceService.java b/java/java-indexing-impl/src/com/intellij/compiler/CompilerReferenceService.java index 0459aae0d45e..ab49c954a003 100644 --- a/java/java-indexing-impl/src/com/intellij/compiler/CompilerReferenceService.java +++ b/java/java-indexing-impl/src/com/intellij/compiler/CompilerReferenceService.java @@ -15,6 +15,7 @@ */ package com.intellij.compiler; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.AbstractProjectComponent; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.Project; @@ -28,6 +29,7 @@ import org.jetbrains.annotations.Nullable; public abstract class CompilerReferenceService extends AbstractProjectComponent { public static final RegistryValue IS_ENABLED_KEY = Registry.get("compiler.ref.index"); + public static volatile boolean enabledInTests = false; protected CompilerReferenceService(Project project) { super(project); @@ -53,6 +55,6 @@ public abstract class CompilerReferenceService extends AbstractProjectComponent @NotNull FileType searchFileType); public static boolean isEnabled() { - return IS_ENABLED_KEY.asBoolean(); + return (!ApplicationManager.getApplication().isHeadlessEnvironment() || enabledInTests) && IS_ENABLED_KEY.asBoolean(); } } diff --git a/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesTestBase.java b/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesTestBase.java index 0fd0bde00891..f61f1fd0652e 100644 --- a/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesTestBase.java +++ b/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesTestBase.java @@ -24,12 +24,14 @@ public abstract class CompilerReferencesTestBase extends AbstractCompilerAwareTe public void setUp() throws Exception { myDefaultEnableState = CompilerReferenceService.IS_ENABLED_KEY.asBoolean(); CompilerReferenceService.IS_ENABLED_KEY.setValue(true); + CompilerReferenceService.enabledInTests = true; super.setUp(); } @Override public void tearDown() throws Exception { CompilerReferenceService.IS_ENABLED_KEY.setValue(myDefaultEnableState); + CompilerReferenceService.enabledInTests = false; super.tearDown(); } } diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 6aa565b5dca6..8704d58ded7e 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -916,7 +916,7 @@ ide.projectView.globalOptions.description=Make Project View options such as auto ide.projectView.ProjectViewPaneTreeStructure.BuildChildrenInBackground=true ide.projectView.ProjectViewPaneTreeStructure.BuildChildrenInBackground.description=Temporary ability to control a tree building for the Project View pane -compiler.ref.index=false +compiler.ref.index=true compiler.ref.index.description=Enables find usages using references from compiler indices compiler.ref.index.restartRequired=true build.manager.async.fs.events=false From 40e7a221065f29d34bfe838f81b8122aeeba604c Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Wed, 14 Dec 2016 22:31:03 +0300 Subject: [PATCH 164/558] CompilerReferencesFindUsagesTest add enable/disable of the service --- .../com/intellij/compiler/CompilerReferencesFindUsagesTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesFindUsagesTest.java b/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesFindUsagesTest.java index 46b1776dd016..3b78c4196f45 100644 --- a/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesFindUsagesTest.java +++ b/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesFindUsagesTest.java @@ -35,6 +35,7 @@ public class CompilerReferencesFindUsagesTest extends DaemonAnalyzerTestCase { public void setUp() throws Exception { myDefaultEnableState = CompilerReferenceService.IS_ENABLED_KEY.asBoolean(); CompilerReferenceService.IS_ENABLED_KEY.setValue(true); + CompilerReferenceService.enabledInTests = true; super.setUp(); myCompilerTester = new CompilerTester(myModule); } @@ -43,6 +44,7 @@ public class CompilerReferencesFindUsagesTest extends DaemonAnalyzerTestCase { public void tearDown() throws Exception { try { CompilerReferenceService.IS_ENABLED_KEY.setValue(myDefaultEnableState); + CompilerReferenceService.enabledInTests = false; myCompilerTester.tearDown(); } finally { From d59f4dd651671efe4c7fc80ea76c04e4123afeee Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Wed, 14 Dec 2016 22:04:14 +0300 Subject: [PATCH 165/558] [groovy] get rid of GrTopLevelDefinition, organize GrTopStatement hierarchy --- .../groovy/lang/psi/GroovyFileBase.java | 4 --- .../lang/psi/api/statements/GrField.java | 2 +- .../api/statements/GrTopLevelDefinition.java | 27 ------------------- .../statements/typedef/GrTypeDefinition.java | 5 ++-- .../typedef/members/GrMembersDeclaration.java | 3 +-- .../statements/typedef/members/GrMethod.java | 4 +-- .../lang/psi/api/toplevel/GrTopStatement.java | 3 ++- .../lang/psi/impl/GroovyFileBaseImpl.java | 7 ----- .../groovy/lang/psi/impl/GroovyFileImpl.java | 5 ++-- .../psi/impl/GroovyPsiElementFactoryImpl.java | 12 +++++---- .../editor/actions/GroovyStatementMover.java | 7 +++-- 11 files changed, 21 insertions(+), 58 deletions(-) delete mode 100644 plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/GrTopLevelDefinition.java diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/GroovyFileBase.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/GroovyFileBase.java index 8b532eddefd4..d4719f3aa187 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/GroovyFileBase.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/GroovyFileBase.java @@ -23,7 +23,6 @@ import com.intellij.psi.PsiImportHolder; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrTopLevelDefinition; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.GrTopStatement; @@ -53,9 +52,6 @@ public interface GroovyFileBase extends PsiFile, GrVariableDeclarationOwner, GrS @NotNull GrTypeDefinition[] getTypeDefinitions(); - @NotNull - GrTopLevelDefinition[] getTopLevelDefinitions(); - @NotNull GrMethod[] getMethods(); diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/GrField.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/GrField.java index 6e3ea04a0805..d6f7915c3e3b 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/GrField.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/GrField.java @@ -28,7 +28,7 @@ import java.util.Map; /** * @author ven */ -public interface GrField extends GrVariable, GrMember, PsiField, GrTopLevelDefinition, GrDocCommentOwner { +public interface GrField extends GrVariable, GrMember, PsiField, GrDocCommentOwner { GrField[] EMPTY_ARRAY = new GrField[0]; boolean isProperty(); diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/GrTopLevelDefinition.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/GrTopLevelDefinition.java deleted file mode 100644 index 651c13279b19..000000000000 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/GrTopLevelDefinition.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.plugins.groovy.lang.psi.api.statements; - -import org.jetbrains.plugins.groovy.lang.psi.GrNamedElement; -import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.GrTopStatement; - -/** - * author ven - */ -public interface GrTopLevelDefinition extends GrNamedElement, GrTopStatement { - GrTopLevelDefinition[] EMPTY_ARRAY = new GrTopLevelDefinition[0]; -} diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/typedef/GrTypeDefinition.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/typedef/GrTypeDefinition.java index bb1e53ae3393..116e500472b5 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/typedef/GrTypeDefinition.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/typedef/GrTypeDefinition.java @@ -25,15 +25,16 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocCommentOwner; +import org.jetbrains.plugins.groovy.lang.psi.GrNamedElement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrClassInitializer; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrTopLevelDefinition; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMembersDeclaration; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; +import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.GrTopStatement; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeParameterList; -public interface GrTypeDefinition extends PsiClass, GrTopLevelDefinition, GrDocCommentOwner, GrMember { +public interface GrTypeDefinition extends PsiClass, GrDocCommentOwner, GrMember, GrNamedElement, GrTopStatement { GrTypeDefinition[] EMPTY_ARRAY = new GrTypeDefinition[0]; ArrayFactory ARRAY_FACTORY = GrTypeDefinition[]::new; diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/typedef/members/GrMembersDeclaration.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/typedef/members/GrMembersDeclaration.java index 5c698e31ea58..bf102415e9ea 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/typedef/members/GrMembersDeclaration.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/typedef/members/GrMembersDeclaration.java @@ -19,12 +19,11 @@ import com.intellij.psi.PsiModifierListOwner; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList; -import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.GrTopStatement; /** * @author ven */ -public interface GrMembersDeclaration extends GroovyPsiElement, PsiModifierListOwner, GrTopStatement { +public interface GrMembersDeclaration extends GroovyPsiElement, PsiModifierListOwner { GrMembersDeclaration[] EMPTY_ARRAY = new GrMembersDeclaration[0]; GrMember[] getMembers(); diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/typedef/members/GrMethod.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/typedef/members/GrMethod.java index bb3dfa168cce..7d1dc44ec7f8 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/typedef/members/GrMethod.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/typedef/members/GrMethod.java @@ -27,10 +27,10 @@ import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocCommentOwner; import org.jetbrains.plugins.groovy.lang.psi.GrNamedElement; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrParametersOwner; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrTopLevelDefinition; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrCodeBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameterList; +import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.GrTopStatement; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeParameterListOwner; @@ -41,7 +41,7 @@ import java.util.Map; * @date: 26.03.2007 */ public interface GrMethod extends GrMembersDeclaration, GrNamedElement, PsiMethod, GrMember, - GrParametersOwner, GrTopLevelDefinition, GrTypeParameterListOwner, GrDocCommentOwner { + GrParametersOwner, GrTopStatement, GrTypeParameterListOwner, GrDocCommentOwner { GrMethod[] EMPTY_ARRAY = new GrMethod[0]; ArrayFactory ARRAY_FACTORY = count -> new GrMethod[count]; diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/toplevel/GrTopStatement.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/toplevel/GrTopStatement.java index b26f5d261909..de2bed74f45b 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/toplevel/GrTopStatement.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/toplevel/GrTopStatement.java @@ -19,11 +19,12 @@ package org.jetbrains.plugins.groovy.lang.psi.api.toplevel; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; /** + * Describes elemenets which may appear at top-level of file. + * * @author: Dmitry.Krasilschikov * @date: 21.03.2007 */ public interface GrTopStatement extends GroovyPsiElement { GrTopStatement[] EMPTY_ARRAY = new GrTopStatement[0]; - } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyFileBaseImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyFileBaseImpl.java index 7832aa48b280..a8782efe0a17 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyFileBaseImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyFileBaseImpl.java @@ -37,7 +37,6 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrTopLevelDefinition; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; @@ -82,12 +81,6 @@ public abstract class GroovyFileBaseImpl extends PsiFileBase implements GroovyFi return calcTreeElement().getChildrenAsPsiElements(TokenSets.TYPE_DEFINITIONS, GrTypeDefinition.ARRAY_FACTORY); } - @Override - @NotNull - public GrTopLevelDefinition[] getTopLevelDefinitions() { - return findChildrenByClass(GrTopLevelDefinition.class); - } - @Override @NotNull public GrMethod[] getMethods() { diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyFileImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyFileImpl.java index a0a58b19bad5..10447f32f876 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyFileImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyFileImpl.java @@ -40,7 +40,6 @@ import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrTopLevelDefinition; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; @@ -319,8 +318,8 @@ public class GroovyFileImpl extends GroovyFileBaseImpl implements GroovyFile, Ps } private static boolean shouldProcess(@Nullable PsiElement lastParent, @NotNull PsiElement run) { - return run instanceof GrAssignmentExpression // binding variables - || !(run instanceof GrTopLevelDefinition || run instanceof GrImportStatement || lastParent instanceof GrMember); + return run instanceof GrAssignmentExpression || // binding variables + run instanceof GrVariableDeclaration && !(lastParent instanceof GrMember); // local variables } @Override diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiElementFactoryImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiElementFactoryImpl.java index 06c4221ebb77..8c895a0bf623 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiElementFactoryImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiElementFactoryImpl.java @@ -486,7 +486,7 @@ public class GroovyPsiElementFactoryImpl extends GroovyPsiElementFactory { @Override public PsiElement createModifierFromText(@NotNull String name) { final GroovyFileBase file = createGroovyFileChecked(name + " foo() {}"); - final GrTopLevelDefinition[] definitions = file.getTopLevelDefinitions(); + final GrTopStatement[] definitions = file.getTopStatements(); if (definitions.length != 1) throw new IncorrectOperationException(name); return definitions[0].getFirstChild().getFirstChild(); } @@ -495,7 +495,7 @@ public class GroovyPsiElementFactoryImpl extends GroovyPsiElementFactory { @Override public GrCodeBlock createMethodBodyFromText(@NotNull String text) { final GroovyFileBase file = createGroovyFileChecked("def foo () {" + text + "}"); - final GrMethod method = (GrMethod) file.getTopLevelDefinitions()[0]; + final GrMethod method = (GrMethod) file.getTopStatements()[0]; return method.getBlock(); } @@ -535,15 +535,17 @@ public class GroovyPsiElementFactoryImpl extends GroovyPsiElementFactory { @Override public GrMethod createConstructorFromText(String constructorName, CharSequence constructorText, @Nullable PsiElement context) { GroovyFile file = createGroovyFileChecked("class " + constructorName + "{" + constructorText + "}", false, context); - GrTopLevelDefinition definition = file.getTopLevelDefinitions()[0]; + GrTypeDefinition definition = file.getTypeDefinitions()[0]; - if (!( definition != null && definition instanceof GrClassDefinition)) { + if (definition == null) { throw new IncorrectOperationException("constructorName: " + constructorName + ", text: " + constructorText); } - GrMethod[] methods = ((GrClassDefinition)definition).getCodeMethods(); + + GrMethod[] methods = definition.getCodeMethods(); if (methods.length != 1) { throw new IncorrectOperationException("constructorName: " + constructorName + ", text: " + constructorText); } + return methods[0]; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/editor/actions/GroovyStatementMover.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/editor/actions/GroovyStatementMover.java index f0907e230b97..c7c62f314631 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/editor/actions/GroovyStatementMover.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/editor/actions/GroovyStatementMover.java @@ -47,7 +47,6 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefini import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinitionBody; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMembersDeclaration; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; -import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.GrTopStatement; import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; import java.util.ArrayList; @@ -173,7 +172,7 @@ public class GroovyStatementMover extends StatementUpDownMover { } } - private boolean shouldDigInside(GrTopStatement statement) { + private boolean shouldDigInside(GroovyPsiElement statement) { if (stmtLevel && (statement instanceof GrMethod || statement instanceof GrTypeDefinition)) { return false; } @@ -183,9 +182,9 @@ public class GroovyStatementMover extends StatementUpDownMover { return true; } - private void addChildRanges(GrTopStatement[] statements) { + private void addChildRanges(GroovyPsiElement[] statements) { for (int i = 0; i < statements.length; i++) { - GrTopStatement statement = statements[i]; + GroovyPsiElement statement = statements[i]; if (nlsAfter(statement)) { final LineRange range = getLineRange(statement); if ((i == 0 || isStatement(statements[i-1])) && isStatement(statement)) { From b92199cd026959c922dba216de0fd416b7fe60b9 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Wed, 14 Dec 2016 22:09:21 +0300 Subject: [PATCH 166/558] [groovy] remove duplicate code --- .../psi/impl/statements/GrVariableDeclarationImpl.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrVariableDeclarationImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrVariableDeclarationImpl.java index 3a184aed9214..32db4978d30f 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrVariableDeclarationImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrVariableDeclarationImpl.java @@ -49,9 +49,6 @@ import org.jetbrains.plugins.groovy.lang.psi.stubs.GrVariableDeclarationStub; import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; -import java.util.ArrayList; -import java.util.List; - /** * @author: Dmitry.Krasilschikov */ @@ -180,11 +177,7 @@ public class GrVariableDeclarationImpl extends GrStubElementBase result = new ArrayList<>(); - for (PsiElement cur = getFirstChild(); cur != null; cur = cur.getNextSibling()) { - if (cur instanceof GrMember) result.add((GrMember)cur); - } - return result.toArray(new GrMember[result.size()]); + return findChildrenByClass(GrMember.class); } @Override From 1f5f5bb86cfb6a5a7fa19ac317765c0b581d9bec Mon Sep 17 00:00:00 2001 From: "Ilya.Kazakevich" Date: Thu, 15 Dec 2016 01:55:10 +0300 Subject: [PATCH 167/558] Tox test verbosity improved --- python/testSrc/com/jetbrains/env/PyToxTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/python/testSrc/com/jetbrains/env/PyToxTest.java b/python/testSrc/com/jetbrains/env/PyToxTest.java index f04227dba666..9481d02cd1c0 100644 --- a/python/testSrc/com/jetbrains/env/PyToxTest.java +++ b/python/testSrc/com/jetbrains/env/PyToxTest.java @@ -330,6 +330,7 @@ public final class PyToxTest extends PyEnvTestCase { // Check expected output final String message = String.format("Interpreter %s does not have expected string in output. \n ", interpreterName) + String.format("All: %s \n", all) + + String.format("Test tree: %s \n", getTestTree(interpreterSuite, 0)) + String.format("Error: %s \n", stderr); From f04e5ecef50579dc7ac776affb792e6fcfb61f30 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Thu, 15 Dec 2016 09:49:50 +0700 Subject: [PATCH 168/558] IDEA-165397 Replace stream API with loop: support lambdas as toArray argument --- .../streamToLoop/FunctionHelper.java | 52 +++++++++---------- .../streamToLoop/Operation.java | 4 +- .../streamToLoop/SourceOperation.java | 14 ++--- .../streamToLoop/TerminalOperation.java | 39 +++++++++----- .../streamToLoop/afterToArrayGenerator.java | 16 ++++++ .../streamToLoop/afterToArrayLambda.java | 20 +++++++ .../streamToLoop/beforeToArrayGenerator.java | 10 ++++ .../streamToLoop/beforeToArrayLambda.java | 13 +++++ 8 files changed, 121 insertions(+), 47 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterToArrayGenerator.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterToArrayLambda.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeToArrayGenerator.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeToArrayLambda.java diff --git a/java/java-impl/src/com/intellij/codeInspection/streamToLoop/FunctionHelper.java b/java/java-impl/src/com/intellij/codeInspection/streamToLoop/FunctionHelper.java index fccbd5dbfa7b..fea19fc671ed 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamToLoop/FunctionHelper.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamToLoop/FunctionHelper.java @@ -181,31 +181,31 @@ abstract class FunctionHelper { } /** - * Renames references to the variable oldName in given expression into newName + * Replaces all the references to the variable {@code name} in given expression with {@code replacement}. + * + *

+ * If the replacement is a new name to the variable, the caller must take care that this new name was not used before. + *

+ * * @param expression an expression to search-and-replace references inside - * @param oldName old name - * @param newName new name + * @param name a reference name to replace + * @param replacement a replacement expression (new name or literal) * @param context context - * @return resulting expression (might be the same as input expression) or null if expression already had references to newName, - * so rename may merge two variables + * @return resulting expression (might be the same as input expression) */ @NotNull - static PsiExpression renameVarReference(@NotNull PsiExpression expression, - String oldName, - String newName, - StreamToLoopReplacementContext context) { - if(oldName.equals(newName)) return expression; - PsiLambdaExpression lambda = (PsiLambdaExpression)context.createExpression("("+oldName+","+newName+")-> "+expression.getText()); - PsiParameter[] parameters = lambda.getParameterList().getParameters(); - PsiParameter oldVar = parameters[0]; - PsiParameter newVar = parameters[1]; + static PsiExpression replaceVarReference(@NotNull PsiExpression expression, + String name, + String replacement, + StreamToLoopReplacementContext context) { + if(name.equals(replacement)) return expression; + PsiLambdaExpression lambda = (PsiLambdaExpression)context.createExpression(name+"->"+expression.getText()); + PsiParameter var = lambda.getParameterList().getParameters()[0]; PsiElement body = lambda.getBody(); LOG.assertTrue(body != null); - if(ReferencesSearch.search(newVar, new LocalSearchScope(body)).findFirst() != null) { - throw new IllegalStateException("Reference with name "+newVar+" already exists in "+lambda.getText()); - } - for (PsiReference ref : ReferencesSearch.search(oldVar, new LocalSearchScope(body)).findAll()) { - ref.handleElementRename(newName); + PsiExpression replacementExpression = context.createExpression(replacement); + for (PsiReference ref : ReferencesSearch.search(var, new LocalSearchScope(body)).findAll()) { + ref.getElement().replace(replacementExpression); } return (PsiExpression)lambda.getBody(); } @@ -304,7 +304,7 @@ abstract class FunctionHelper { LOG.assertTrue(body instanceof PsiExpression); myExpression = (PsiExpression)body; EntryStream.zip(lambda.getParameterList().getParameters(), newNames) - .forKeyValue((param, newName) -> myExpression = renameVarReference(myExpression, param.getName(), newName, context)); + .forKeyValue((param, newName) -> myExpression = replaceVarReference(myExpression, param.getName(), newName, context)); } @Override @@ -336,7 +336,7 @@ abstract class FunctionHelper { if(oldName.equals(newName)) return; PsiExpression qualifier = myMethodRef.getQualifierExpression(); if(qualifier == null) return; - qualifier = renameVarReference(qualifier, oldName, newName, context); + qualifier = replaceVarReference(qualifier, oldName, newName, context); myMethodRef = fromText(context, qualifier.getText()+"::"+myMethodRef.getReferenceName()); } } @@ -382,7 +382,7 @@ abstract class FunctionHelper { @Override void rename(String oldName, String newName, StreamToLoopReplacementContext context) { - myExpression = renameVarReference(myExpression, oldName, newName, context); + myExpression = replaceVarReference(myExpression, oldName, newName, context); } @Override @@ -421,7 +421,7 @@ abstract class FunctionHelper { @Override void rename(String oldName, String newName, StreamToLoopReplacementContext context) { - myReference = renameVarReference(myReference, oldName, newName, context); + myReference = replaceVarReference(myReference, oldName, newName, context); } @Override @@ -473,7 +473,7 @@ abstract class FunctionHelper { void transform(StreamToLoopReplacementContext context, String... newNames) { LOG.assertTrue(newNames.length == myParameters.length); EntryStream.zip(myParameters, newNames).forKeyValue( - (oldName, newName) -> myBody = renameVarReference(myBody, oldName, newName, context)); + (oldName, newName) -> myBody = replaceVarReference(myBody, oldName, newName, context)); } void rename(String oldName, String newName, StreamToLoopReplacementContext context) { @@ -484,7 +484,7 @@ abstract class FunctionHelper { if (!paramName.equals(oldName) && !StreamEx.of(myParameters).has(paramName)) { try { - myBody = renameVarReference(myBody, newName, paramName, context); + myBody = replaceVarReference(myBody, newName, paramName, context); myParameters[(int)idx.getAsLong()] = paramName; break; } @@ -496,7 +496,7 @@ abstract class FunctionHelper { } } } - myBody = renameVarReference(myBody, oldName, newName, context); + myBody = replaceVarReference(myBody, oldName, newName, context); } @Override diff --git a/java/java-impl/src/com/intellij/codeInspection/streamToLoop/Operation.java b/java/java-impl/src/com/intellij/codeInspection/streamToLoop/Operation.java index 9834ab17537d..2c3178014267 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamToLoop/Operation.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamToLoop/Operation.java @@ -264,7 +264,7 @@ abstract class Operation { @Override void rename(String oldName, String newName, StreamToLoopReplacementContext context) { - myExpression = FunctionHelper.renameVarReference(myExpression, oldName, newName, context); + myExpression = FunctionHelper.replaceVarReference(myExpression, oldName, newName, context); } @Override @@ -288,7 +288,7 @@ abstract class Operation { @Override void rename(String oldName, String newName, StreamToLoopReplacementContext context) { - myLimit = FunctionHelper.renameVarReference(myLimit, oldName, newName, context); + myLimit = FunctionHelper.replaceVarReference(myLimit, oldName, newName, context); } @Override diff --git a/java/java-impl/src/com/intellij/codeInspection/streamToLoop/SourceOperation.java b/java/java-impl/src/com/intellij/codeInspection/streamToLoop/SourceOperation.java index 6a14a01128dc..8e9b93a4c982 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamToLoop/SourceOperation.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamToLoop/SourceOperation.java @@ -30,7 +30,7 @@ import java.util.Arrays; import java.util.function.Consumer; import static com.intellij.codeInspection.streamToLoop.FunctionHelper.processUsedNames; -import static com.intellij.codeInspection.streamToLoop.FunctionHelper.renameVarReference; +import static com.intellij.codeInspection.streamToLoop.FunctionHelper.replaceVarReference; /** * @author Tagir Valeev @@ -109,7 +109,7 @@ abstract class SourceOperation extends Operation { @Override void rename(String oldName, String newName, StreamToLoopReplacementContext context) { - myQualifier = renameVarReference(myQualifier, oldName, newName, context); + myQualifier = replaceVarReference(myQualifier, oldName, newName, context); } @Override @@ -146,7 +146,7 @@ abstract class SourceOperation extends Operation { @Override void rename(String oldName, String newName, StreamToLoopReplacementContext context) { - Arrays.asList(myArgList).replaceAll(arg -> renameVarReference(arg, oldName, newName, context)); + Arrays.asList(myArgList).replaceAll(arg -> replaceVarReference(arg, oldName, newName, context)); } @Override @@ -189,7 +189,7 @@ abstract class SourceOperation extends Operation { void rename(String oldName, String newName, StreamToLoopReplacementContext context) { myFn.rename(oldName, newName, context); if(myLimit != null) { - myLimit = renameVarReference(myLimit, oldName, newName, context); + myLimit = replaceVarReference(myLimit, oldName, newName, context); } } @@ -227,7 +227,7 @@ abstract class SourceOperation extends Operation { @Override void rename(String oldName, String newName, StreamToLoopReplacementContext context) { - myInitializer = renameVarReference(myInitializer, oldName, newName, context); + myInitializer = replaceVarReference(myInitializer, oldName, newName, context); myFn.rename(oldName, newName, context); } @@ -264,8 +264,8 @@ abstract class SourceOperation extends Operation { @Override void rename(String oldName, String newName, StreamToLoopReplacementContext context) { - myOrigin = renameVarReference(myOrigin, oldName, newName, context); - myBound = renameVarReference(myBound, oldName, newName, context); + myOrigin = replaceVarReference(myOrigin, oldName, newName, context); + myBound = replaceVarReference(myBound, oldName, newName, context); } @Override diff --git a/java/java-impl/src/com/intellij/codeInspection/streamToLoop/TerminalOperation.java b/java/java-impl/src/com/intellij/codeInspection/streamToLoop/TerminalOperation.java index d51864f0b85f..1b14a67c3d0f 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamToLoop/TerminalOperation.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamToLoop/TerminalOperation.java @@ -111,20 +111,12 @@ abstract class TerminalOperation extends Operation { if(args.length == 0) return new ToPrimitiveArrayTerminalOperation(componentType.getCanonicalText()); } else { - String arr = ""; + FunctionHelper fn = null; if(args.length == 1) { - if(!(args[0] instanceof PsiMethodReferenceExpression)) return null; - PsiMethodReferenceExpression arrCtor = (PsiMethodReferenceExpression)args[0]; - if(!arrCtor.isConstructor()) return null; - PsiTypeElement typeElement = arrCtor.getQualifierType(); - if(typeElement == null) return null; - PsiType type = typeElement.getType(); - if(!(type instanceof PsiArrayType)) return null; - arr = "new "+type.getCanonicalText().replaceFirst("\\[]", "[0]"); + fn = FunctionHelper.create(args[0], 1); + if(fn == null) return null; } - return new AccumulatedTerminalOperation("list", CommonClassNames.JAVA_UTIL_LIST + "<" + elementType.getCanonicalText() + ">", - "new "+ CommonClassNames.JAVA_UTIL_ARRAY_LIST+"<>()", "{acc}.add({item});", - "{acc}.toArray("+arr+")"); + return new ToArrayTerminalOperation(elementType, fn); } } if ((name.equals("max") || name.equals("min")) && args.length < 2) { @@ -425,6 +417,29 @@ abstract class TerminalOperation extends Operation { } } + static class ToArrayTerminalOperation extends TerminalOperation { + private final String myType; + private final FunctionHelper mySupplier; + + public ToArrayTerminalOperation(PsiType type, FunctionHelper supplier) { + myType = type.getCanonicalText(); + mySupplier = supplier; + } + + @Override + String generate(StreamVariable inVar, StreamToLoopReplacementContext context) { + String list = context.declareResult("list", CommonClassNames.JAVA_UTIL_LIST + "<" + myType + ">", + "new " + CommonClassNames.JAVA_UTIL_ARRAY_LIST + "<>()", false); + String toArrayArg = ""; + if(mySupplier != null) { + mySupplier.transform(context, "0"); + toArrayArg = mySupplier.getText(); + } + context.setFinisher(list + ".toArray(" + toArrayArg + ")"); + return list+".add("+inVar+");\n"; + } + } + static class FindTerminalOperation extends TerminalOperation { private String myType; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterToArrayGenerator.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterToArrayGenerator.java new file mode 100644 index 000000000000..59fb60571d62 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterToArrayGenerator.java @@ -0,0 +1,16 @@ +// "Replace Stream API chain with loop" "true" + +import java.util.*; +import java.util.function.*; + +public class Main { + private static A[] toArraySkippingNulls(List list, IntFunction generator) { + List result = new ArrayList<>(); + for (Object o : list) { + if (Objects.nonNull(o)) { + result.add(o); + } + } + return result.toArray(generator.apply(0)); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterToArrayLambda.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterToArrayLambda.java new file mode 100644 index 000000000000..3f18288104de --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterToArrayLambda.java @@ -0,0 +1,20 @@ +// "Replace Stream API chain with loop" "true" + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class Main { + private static Number[] test(int[] numbers) { + List list = new ArrayList<>(); + for (int number : numbers) { + Integer integer = number; + list.add(integer); + } + return list.toArray(new Integer[0]); + } + + public static void main(String[] args) { + System.out.println(Arrays.asList(test(new int[] {1,2,3}))); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeToArrayGenerator.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeToArrayGenerator.java new file mode 100644 index 000000000000..adfe6236d2de --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeToArrayGenerator.java @@ -0,0 +1,10 @@ +// "Replace Stream API chain with loop" "true" + +import java.util.*; +import java.util.function.*; + +public class Main { + private static A[] toArraySkippingNulls(List list, IntFunction generator) { + return list.stream().filter(Objects::nonNull).toArray(generator); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeToArrayLambda.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeToArrayLambda.java new file mode 100644 index 000000000000..a3fb4b3142ad --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeToArrayLambda.java @@ -0,0 +1,13 @@ +// "Replace Stream API chain with loop" "true" + +import java.util.Arrays; + +public class Main { + private static Number[] test(int[] numbers) { + return Arrays.stream(numbers).boxed().toArray(size -> new Integer[size]); + } + + public static void main(String[] args) { + System.out.println(Arrays.asList(test(new int[] {1,2,3}))); + } +} \ No newline at end of file From d8329c5638d25976f4227f5a9ee6709a58c65701 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Thu, 15 Dec 2016 11:14:07 +0700 Subject: [PATCH 169/558] Reduced copy-paste logic to check PsiElement being increment/decrement or used in write --- .../inline/InlineLocalHandler.java | 18 ++-------------- .../refactoring/util/RefactoringUtil.java | 21 ++++--------------- .../src/com/intellij/psi/util/PsiUtil.java | 14 ++----------- 3 files changed, 8 insertions(+), 45 deletions(-) diff --git a/java/java-impl/src/com/intellij/refactoring/inline/InlineLocalHandler.java b/java/java-impl/src/com/intellij/refactoring/inline/InlineLocalHandler.java index b045fda2888b..d48e13b43989 100644 --- a/java/java-impl/src/com/intellij/refactoring/inline/InlineLocalHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/inline/InlineLocalHandler.java @@ -36,7 +36,6 @@ import com.intellij.openapi.wm.WindowManager; import com.intellij.psi.*; import com.intellij.psi.controlFlow.DefUseUtil; import com.intellij.psi.search.searches.ReferencesSearch; -import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.PsiUtilCore; @@ -46,6 +45,7 @@ import com.intellij.refactoring.listeners.RefactoringEventData; import com.intellij.refactoring.listeners.RefactoringEventListener; import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.refactoring.util.InlineUtil; +import com.intellij.refactoring.util.RefactoringUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.Query; import com.intellij.util.containers.ContainerUtil; @@ -295,29 +295,15 @@ public class InlineLocalHandler extends JavaInlineActionHandler { if (((PsiArrayAccessExpression)parent).getIndexExpression() == element) continue; if (defToInline instanceof PsiExpression && !(defToInline instanceof PsiNewExpression)) continue; element = parent; - parent = parent.getParent(); } - if (parent instanceof PsiAssignmentExpression && element == ((PsiAssignmentExpression)parent).getLExpression() - || isUnaryWriteExpression(parent)) { - + if (RefactoringUtil.isAssignmentLHS(element)) { return element; } } return null; } - private static boolean isUnaryWriteExpression(PsiElement parent) { - IElementType tokenType = null; - if (parent instanceof PsiPrefixExpression) { - tokenType = ((PsiPrefixExpression)parent).getOperationTokenType(); - } - if (parent instanceof PsiPostfixExpression) { - tokenType = ((PsiPostfixExpression)parent).getOperationTokenType(); - } - return tokenType == JavaTokenType.PLUSPLUS || tokenType == JavaTokenType.MINUSMINUS; - } - private static boolean isSameDefinition(final PsiElement def, final PsiExpression defToInline) { if (def instanceof PsiLocalVariable) return defToInline.equals(((PsiLocalVariable)def).getInitializer()); final PsiElement parent = def.getParent(); diff --git a/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java b/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java index e8bc507d2375..339d4c6dfa34 100644 --- a/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java +++ b/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java @@ -44,7 +44,6 @@ import com.intellij.psi.javadoc.PsiDocTagValue; import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.SearchScope; import com.intellij.psi.search.searches.ReferencesSearch; -import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.*; import com.intellij.refactoring.PackageWrapper; import com.intellij.refactoring.introduceField.ElementToWorkOn; @@ -421,25 +420,13 @@ public class RefactoringUtil { return type; } - public static boolean isAssignmentLHS(PsiElement element) { - PsiElement parent = element.getParent(); - - return parent instanceof PsiAssignmentExpression && element.equals(((PsiAssignmentExpression)parent).getLExpression()) || - isPlusPlusOrMinusMinus(parent); + public static boolean isAssignmentLHS(@NotNull PsiElement element) { + return element instanceof PsiExpression && PsiUtil.isAccessedForWriting((PsiExpression)element); } + @Contract("null -> false") public static boolean isPlusPlusOrMinusMinus(PsiElement element) { - if (element instanceof PsiPrefixExpression) { - return ((PsiPrefixExpression)element).getOperationTokenType() == JavaTokenType.PLUSPLUS || - ((PsiPrefixExpression)element).getOperationTokenType() == JavaTokenType.MINUSMINUS; - } - else if (element instanceof PsiPostfixExpression) { - IElementType operandTokenType = ((PsiPostfixExpression)element).getOperationTokenType(); - return operandTokenType == JavaTokenType.PLUSPLUS || operandTokenType == JavaTokenType.MINUSMINUS; - } - else { - return false; - } + return PsiUtil.isIncrementDecrementOperation(element); } private static void removeFinalParameters(PsiMethod method) throws IncorrectOperationException { diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java index 3120f2e46a23..7ec4c766b739 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java @@ -83,18 +83,8 @@ public final class PsiUtil extends PsiUtilCore { public static boolean isAccessedForWriting(@NotNull PsiExpression expr) { if (isOnAssignmentLeftHand(expr)) return true; - PsiElement parent = PsiTreeUtil.skipParentsOfType(expr, PsiParenthesizedExpression.class); - if (parent instanceof PsiPrefixExpression) { - IElementType tokenType = ((PsiPrefixExpression) parent).getOperationTokenType(); - return tokenType == JavaTokenType.PLUSPLUS || tokenType == JavaTokenType.MINUSMINUS; - } - else if (parent instanceof PsiPostfixExpression) { - IElementType tokenType = ((PsiPostfixExpression) parent).getOperationTokenType(); - return tokenType == JavaTokenType.PLUSPLUS || tokenType == JavaTokenType.MINUSMINUS; - } - else { - return false; - } + PsiElement parent = skipParenthesizedExprUp(expr.getParent()); + return isIncrementDecrementOperation(parent); } public static boolean isAccessedForReading(@NotNull PsiExpression expr) { From 3741495801e379454132f6a6f80b26e1a00f13c8 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Thu, 15 Dec 2016 11:51:58 +0700 Subject: [PATCH 170/558] LambdaGenerationUtil: disable if expression is accessed for writing Fixes IDEA-165482 Optional.isPresent suggests to replace the code which assigns the variable defined outside --- .../codeInspection/util/LambdaGenerationUtil.java | 14 +++++++++----- .../beforeAssignmentIncomplete.java | 13 +++++++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/optionalIsPresent/beforeAssignmentIncomplete.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/util/LambdaGenerationUtil.java b/java/java-analysis-impl/src/com/intellij/codeInspection/util/LambdaGenerationUtil.java index 67506323e6b0..e66c4abd4246 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/util/LambdaGenerationUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/util/LambdaGenerationUtil.java @@ -19,6 +19,7 @@ import com.intellij.codeInsight.ExceptionUtil; import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -104,15 +105,18 @@ public class LambdaGenerationUtil { if(!myCanBeLambdaBody) return; super.visitReferenceExpression(expression); PsiElement element = expression.resolve(); - if (element instanceof PsiVariable && - !(element instanceof PsiField) && - !myVariableAllowedPredicate.test((PsiVariable)element) && - !PsiTreeUtil.isAncestor(myRoot, element, true) && - !HighlightControlFlowUtil.isEffectivelyFinal((PsiVariable)element, myRoot, null)) { + if (element instanceof PsiVariable && !isAllowedInLambda(expression, (PsiVariable)element)) { myCanBeLambdaBody = false; } } + private boolean isAllowedInLambda(PsiReferenceExpression expression, PsiVariable variable) { + return variable instanceof PsiField || + myVariableAllowedPredicate.test(variable) || + PsiTreeUtil.isAncestor(myRoot, variable, true) || + !PsiUtil.isAccessedForWriting(expression) && HighlightControlFlowUtil.isEffectivelyFinal(variable, myRoot, null); + } + @Override public void visitBreakStatement(PsiBreakStatement statement) { PsiStatement exitedStatement = statement.findExitedStatement(); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/optionalIsPresent/beforeAssignmentIncomplete.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/optionalIsPresent/beforeAssignmentIncomplete.java new file mode 100644 index 000000000000..a02b43d29a99 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/optionalIsPresent/beforeAssignmentIncomplete.java @@ -0,0 +1,13 @@ +// "Replace Optional.isPresent() condition with functional style expression" "false" + +import java.util.Optional; + +public class Main { + void assignVariable(Optional opt) { + String s; + if(opt.isPresent()) { + s = opt.get().toString(); + } + String s2 = s; + } +} From c770886a923349ba404427d5705da38047e55c57 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Thu, 15 Dec 2016 14:15:02 +0700 Subject: [PATCH 171/558] PsiUtil#isIncrementDecrementOperation: loosen argument contract to nullable; RefactoringUtil#isPlusPlusOrMinusMinus is replaced everywhere with PsiUtil#isIncrementDecrementOperation (IDEA-CR-16815) --- .../encapsulateFields/EncapsulateFieldsProcessor.java | 4 ++-- .../encapsulateFields/JavaEncapsulateFieldHelper.java | 7 ++----- .../refactoring/extractclass/ExtractClassProcessor.java | 4 ++-- .../src/com/intellij/refactoring/util/RefactoringUtil.java | 7 +------ java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java | 3 ++- 5 files changed, 9 insertions(+), 16 deletions(-) diff --git a/java/java-impl/src/com/intellij/refactoring/encapsulateFields/EncapsulateFieldsProcessor.java b/java/java-impl/src/com/intellij/refactoring/encapsulateFields/EncapsulateFieldsProcessor.java index a1d4f7323455..1804d5f2f9cd 100644 --- a/java/java-impl/src/com/intellij/refactoring/encapsulateFields/EncapsulateFieldsProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/encapsulateFields/EncapsulateFieldsProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -173,7 +173,7 @@ public class EncapsulateFieldsProcessor extends BaseRefactoringProcessor { PsiElement element = info.getElement(); if (element != null) { PsiElement parent = element.getParent(); - if (RefactoringUtil.isPlusPlusOrMinusMinus(parent) && !(parent.getParent() instanceof PsiExpressionStatement)) { + if (PsiUtil.isIncrementDecrementOperation(parent) && !(parent.getParent() instanceof PsiExpressionStatement)) { conflicts.putValue(parent, "Unable to proceed with postfix/prefix expression when it's result type is used"); } } diff --git a/java/java-impl/src/com/intellij/refactoring/encapsulateFields/JavaEncapsulateFieldHelper.java b/java/java-impl/src/com/intellij/refactoring/encapsulateFields/JavaEncapsulateFieldHelper.java index 25c9c0ea1278..06aa52cab4b2 100644 --- a/java/java-impl/src/com/intellij/refactoring/encapsulateFields/JavaEncapsulateFieldHelper.java +++ b/java/java-impl/src/com/intellij/refactoring/encapsulateFields/JavaEncapsulateFieldHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2016 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,10 @@ package com.intellij.refactoring.encapsulateFields; import com.intellij.codeInsight.generation.GenerateMembersUtil; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; -import com.intellij.psi.util.PropertyUtil; import com.intellij.psi.util.PsiUtil; -import com.intellij.refactoring.util.RefactoringUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.VisibilityUtil; import com.intellij.util.containers.ContainerUtil; @@ -180,7 +177,7 @@ public class JavaEncapsulateFieldHelper extends EncapsulateFieldHelper { } } } - else if (RefactoringUtil.isPlusPlusOrMinusMinus(parent)){ + else if (PsiUtil.isIncrementDecrementOperation(parent)){ IElementType sign; if (parent instanceof PsiPrefixExpression){ sign = ((PsiPrefixExpression)parent).getOperationTokenType(); diff --git a/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassProcessor.java b/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassProcessor.java index c3570eec6605..3221ef5a5a8b 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassProcessor.java @@ -25,7 +25,6 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtil; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; @@ -40,6 +39,7 @@ import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PropertyUtil; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; import com.intellij.refactoring.MoveDestination; import com.intellij.refactoring.RefactorJBundle; import com.intellij.refactoring.extractclass.usageInfo.*; @@ -630,7 +630,7 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor { if (element instanceof PsiReferenceExpression) { final PsiReferenceExpression exp = (PsiReferenceExpression)element; - if (RefactoringUtil.isPlusPlusOrMinusMinus(exp.getParent())) { + if (PsiUtil.isIncrementDecrementOperation(exp.getParent())) { usages.add(isStatic ? new ReplaceStaticVariableIncrementDecrement(exp, qualifiedName) : new ReplaceInstanceVariableIncrementDecrement(exp, delegateFieldName, setter, getter, field.getName())); diff --git a/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java b/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java index 339d4c6dfa34..34cc63ce8ceb 100644 --- a/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java +++ b/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java @@ -424,11 +424,6 @@ public class RefactoringUtil { return element instanceof PsiExpression && PsiUtil.isAccessedForWriting((PsiExpression)element); } - @Contract("null -> false") - public static boolean isPlusPlusOrMinusMinus(PsiElement element) { - return PsiUtil.isIncrementDecrementOperation(element); - } - private static void removeFinalParameters(PsiMethod method) throws IncorrectOperationException { PsiParameterList paramList = method.getParameterList(); PsiParameter[] params = paramList.getParameters(); @@ -633,7 +628,7 @@ public class RefactoringUtil { return EXPR_COPY_PROHIBITED; } - if (isPlusPlusOrMinusMinus(element)) { + if (PsiUtil.isIncrementDecrementOperation(element)) { return EXPR_COPY_PROHIBITED; } diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java index 7ec4c766b739..d16641d1459b 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java @@ -330,7 +330,8 @@ public final class PsiUtil extends PsiUtilCore { return codeBlock; } - public static boolean isIncrementDecrementOperation(@NotNull PsiElement element) { + @Contract("null -> false") + public static boolean isIncrementDecrementOperation(@Nullable PsiElement element) { if (element instanceof PsiPostfixExpression) { final IElementType sign = ((PsiPostfixExpression)element).getOperationTokenType(); if (sign == JavaTokenType.PLUSPLUS || sign == JavaTokenType.MINUSMINUS) From 036373f1582be1af10d509b86bcfacb131ef6ec0 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Thu, 15 Dec 2016 11:24:36 +0300 Subject: [PATCH 172/558] EDU-800 Broken adaptive course creation --- .../student/src/com/jetbrains/edu/learning/StudyUtils.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java index a94fb35e1974..10a14e60b356 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java @@ -263,7 +263,11 @@ public class StudyUtils { } public static void showNoSdkNotification(@NotNull final Task currentTask, @NotNull final Project project) { - final Language language = currentTask.getLesson().getCourse().getLanguageById(); + final Lesson lesson = currentTask.getLesson(); + if (lesson == null) return; + final Course course = lesson.getCourse(); + if (course == null) return; + final Language language = course.getLanguageById(); StudyExecutor.INSTANCE.forLanguage(language).showNoSdkNotification(project); } From 3b7f2879e288c57d09ed8536cc4b48a06cd623d0 Mon Sep 17 00:00:00 2001 From: nik Date: Thu, 15 Dec 2016 11:36:34 +0300 Subject: [PATCH 173/558] download_kotlin script corrected to properly download latest Kotlin builds --- build/scripts/download_kotlin.gant | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/scripts/download_kotlin.gant b/build/scripts/download_kotlin.gant index c601a822034d..6de3427626d7 100644 --- a/build/scripts/download_kotlin.gant +++ b/build/scripts/download_kotlin.gant @@ -321,6 +321,6 @@ class TeamCityBuildLocator { String getDownloadUrl(String buildNumber) { String encodedBuildNumber = URLEncoder.encode(buildNumber, "UTF-8") - return "$host/guestAuth/repository/download/$buildTypeId/$encodedBuildNumber/kotlin-plugin-1.0.5-release-IJ2016.3-2.zip" + return "$host/guestAuth/repository/download/$buildTypeId/$encodedBuildNumber/kotlin-plugin-1.0.5-release-IJ2016.3-3.zip" } } From 89e08570b41f3f7d155466de69a8582efb81ad48 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Thu, 15 Dec 2016 15:36:32 +0700 Subject: [PATCH 174/558] IDEA-165395 Replace stream API with loop: l.stream().toArray(String[]::new) --- ...SimplifyStreamApiCallChainsInspection.java | 98 ++++++++++++++++++- .../afterToArrayLambda.java | 10 ++ .../afterToArrayMethodRef.java | 9 ++ .../afterToArrayNoArg.java | 9 ++ .../beforeToArrayGenerator.java | 10 ++ .../beforeToArrayLambda.java | 9 ++ .../beforeToArrayMethodRef.java | 9 ++ .../beforeToArrayNoArg.java | 9 ++ .../SimplifyStreamApiCallChains.html | 1 + 9 files changed, 159 insertions(+), 5 deletions(-) create mode 100644 java/java-tests/testData/inspection/streamApiCallChains/afterToArrayLambda.java create mode 100644 java/java-tests/testData/inspection/streamApiCallChains/afterToArrayMethodRef.java create mode 100644 java/java-tests/testData/inspection/streamApiCallChains/afterToArrayNoArg.java create mode 100644 java/java-tests/testData/inspection/streamApiCallChains/beforeToArrayGenerator.java create mode 100644 java/java-tests/testData/inspection/streamApiCallChains/beforeToArrayLambda.java create mode 100644 java/java-tests/testData/inspection/streamApiCallChains/beforeToArrayMethodRef.java create mode 100644 java/java-tests/testData/inspection/streamApiCallChains/beforeToArrayNoArg.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java index d1bbf5327cb9..0e7c7276dfd4 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java @@ -24,6 +24,7 @@ import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.impl.PsiDiamondTypeUtil; import com.intellij.psi.util.*; import com.siyeh.ig.psiutils.BoolUtils; +import com.siyeh.ig.psiutils.CommentTracker; import com.siyeh.ig.psiutils.ExpressionUtils; import com.siyeh.ig.psiutils.StreamApiUtil; import org.jetbrains.annotations.Contract; @@ -59,6 +60,7 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns private static final String ANY_MATCH_METHOD = "anyMatch"; private static final String NONE_MATCH_METHOD = "noneMatch"; private static final String ALL_MATCH_METHOD = "allMatch"; + private static final String TO_ARRAY_METHOD = "toArray"; private static final String COUNTING_COLLECTOR = "counting"; private static final String TO_LIST_COLLECTOR = "toList"; @@ -100,7 +102,10 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns else if (isCallOf(method, CommonClassNames.JAVA_UTIL_OPTIONAL, IS_PRESENT_METHOD, 0)) { handleOptionalIsPresent(methodCall); } - else if (isStreamCall(method, ANY_MATCH_METHOD)) { + else if (isStreamCall(method, TO_ARRAY_METHOD, false)) { + handleToArray(methodCall); + } + else if (isStreamCall(method, ANY_MATCH_METHOD, true)) { if(isParentNegated(methodCall)) { boolean argNegated = isArgumentLambdaNegated(methodCall); registerMatchFix(methodCall, @@ -109,7 +114,7 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns argNegated ? ALL_MATCH_METHOD : NONE_MATCH_METHOD)); } } - else if (isStreamCall(method, NONE_MATCH_METHOD)) { + else if (isStreamCall(method, NONE_MATCH_METHOD, true)) { if(isParentNegated(methodCall)) { registerMatchFix(methodCall, new SimplifyMatchNegationFix("!"+psiClass.getName()+".noneMatch(...)", ANY_MATCH_METHOD)); } @@ -117,7 +122,7 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns registerMatchFix(methodCall, new SimplifyMatchNegationFix(psiClass.getName()+".noneMatch(x -> !(...))", ALL_MATCH_METHOD)); } } - else if (isStreamCall(method, ALL_MATCH_METHOD)) { + else if (isStreamCall(method, ALL_MATCH_METHOD, true)) { if(isArgumentLambdaNegated(methodCall)) { boolean parentNegated = isParentNegated(methodCall); registerMatchFix(methodCall, @@ -131,6 +136,22 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns } } + private void handleToArray(PsiMethodCallExpression methodCall) { + PsiReferenceExpression methodExpression = methodCall.getMethodExpression(); + PsiExpression qualifier = methodExpression.getQualifierExpression(); + if(qualifier instanceof PsiMethodCallExpression && isCollectionStream((PsiMethodCallExpression)qualifier)) { + PsiArrayType type = getArrayType(methodCall); + if(type != null) { + PsiElement nameElement = methodExpression.getReferenceNameElement(); + LOG.assertTrue(nameElement != null); + String replacement = type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT+"[]") ? "" : + "new "+type.getCanonicalText().replaceFirst("\\[]", "[0]"); + holder.registerProblem(nameElement, "Can be replaced with collection.toArray()", + new SimplifyCallChainFix(new ReplaceWithToArrayFix(replacement))); + } + } + } + void registerMatchFix(PsiMethodCallExpression methodCall, SimplifyMatchNegationFix fix) { PsiElement nameElement = methodCall.getMethodExpression().getReferenceNameElement(); if(nameElement != null) { @@ -341,6 +362,44 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns }; } + @Nullable + private static PsiArrayType getArrayType(PsiMethodCallExpression call) { + PsiType type = call.getType(); + if(!(type instanceof PsiArrayType)) return null; + PsiArrayType candidate = (PsiArrayType)type; + PsiExpression[] args = call.getArgumentList().getExpressions(); + if(args.length == 0) return candidate; + if(args.length != 1) return null; + PsiExpression supplier = args[0]; + if(supplier instanceof PsiMethodReferenceExpression) { + // like toArray(String[]::new) + PsiMethodReferenceExpression methodRef = (PsiMethodReferenceExpression)supplier; + PsiTypeElement qualifierType = methodRef.getQualifierType(); + if (methodRef.isConstructor() && qualifierType != null && candidate.isAssignableFrom(qualifierType.getType())) { + return candidate; + } + } else if(supplier instanceof PsiLambdaExpression) { + // like toArray(size -> new String[size]) + PsiLambdaExpression lambda = (PsiLambdaExpression)supplier; + PsiParameter[] parameters = lambda.getParameterList().getParameters(); + if(parameters.length != 1) return null; + PsiParameter sizeParameter = parameters[0]; + PsiExpression body = LambdaUtil.extractSingleExpressionFromBody(lambda.getBody()); + if(body instanceof PsiNewExpression) { + PsiNewExpression newExpression = (PsiNewExpression)body; + PsiExpression[] dimensions = newExpression.getArrayDimensions(); + PsiType newExpressionType = newExpression.getType(); + if (dimensions.length != 0 && + ExpressionUtils.isReferenceTo(dimensions[0], sizeParameter) && + newExpressionType != null && + candidate.isAssignableFrom(newExpressionType)) { + return candidate; + } + } + } + return null; + } + private static boolean isCollectionConstructor(PsiMethod ctor) { if(!ctor.getModifierList().hasExplicitModifier(PsiModifier.PUBLIC)) return false; PsiParameterList list = ctor.getParameterList(); @@ -452,8 +511,10 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns return false; } - static boolean isStreamCall(@Nullable PsiMethod method, @NotNull String methodName) { - if (method == null || !methodName.equals(method.getName()) || method.getParameterList().getParametersCount() != 1) { + @Contract("null, _, _ -> false") + static boolean isStreamCall(@Nullable PsiMethod method, @NotNull String methodName, boolean checkSingleParameter) { + if (method == null || !methodName.equals(method.getName()) || + (checkSingleParameter && method.getParameterList().getParametersCount() != 1)) { return false; } final PsiClass containingClass = method.getContainingClass(); @@ -890,4 +951,31 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns ((PsiMethodCallExpression)grandParent).getTypeArgumentList().delete(); } } + + private static class ReplaceWithToArrayFix implements CallChainFix { + private final String myReplacement; + + private ReplaceWithToArrayFix(String replacement) { + myReplacement = replacement; + } + + @Override + public String getName() { + return "Replace 'collection.stream().toArray()' with 'collection.toArray()'"; + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + PsiMethodCallExpression toArrayCall = PsiTreeUtil.getParentOfType(descriptor.getStartElement(), PsiMethodCallExpression.class); + if (toArrayCall == null) return; + PsiExpression qualifier = toArrayCall.getMethodExpression().getQualifierExpression(); + if(!(qualifier instanceof PsiMethodCallExpression)) return; + PsiMethodCallExpression streamCall = (PsiMethodCallExpression)qualifier; + PsiExpression collectionExpression = streamCall.getMethodExpression().getQualifierExpression(); + if(collectionExpression == null) return; + CommentTracker ct = new CommentTracker(); + PsiElement result = ct.replaceAndRestoreComments(toArrayCall, ct.text(collectionExpression) + ".toArray(" + myReplacement + ")"); + CodeStyleManager.getInstance(project).reformat(JavaCodeStyleManager.getInstance(project).shortenClassReferences(result)); + } + } } diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterToArrayLambda.java b/java/java-tests/testData/inspection/streamApiCallChains/afterToArrayLambda.java new file mode 100644 index 000000000000..0102e3b564e8 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterToArrayLambda.java @@ -0,0 +1,10 @@ +// "Replace 'collection.stream().toArray()' with 'collection.toArray()'" "true" + +import java.util.*; + +class Test { + public void testToArray(List data) { + /*generate array*/ + String[][] array = data.subList(0, /*max number*/ 10).toArray(new String[0][]); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterToArrayMethodRef.java b/java/java-tests/testData/inspection/streamApiCallChains/afterToArrayMethodRef.java new file mode 100644 index 000000000000..25edc83fb602 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterToArrayMethodRef.java @@ -0,0 +1,9 @@ +// "Replace 'collection.stream().toArray()' with 'collection.toArray()'" "true" + +import java.util.*; + +class Test { + public void testToArray(List data) { + Object[] array = data.toArray(new String[0]); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterToArrayNoArg.java b/java/java-tests/testData/inspection/streamApiCallChains/afterToArrayNoArg.java new file mode 100644 index 000000000000..912701802841 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterToArrayNoArg.java @@ -0,0 +1,9 @@ +// "Replace 'collection.stream().toArray()' with 'collection.toArray()'" "true" + +import java.util.*; + +class Test { + public void testToArray(List data) { + Object[] array = data.toArray(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeToArrayGenerator.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeToArrayGenerator.java new file mode 100644 index 000000000000..512e226d8aab --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeToArrayGenerator.java @@ -0,0 +1,10 @@ +// "Replace 'collection.stream().toArray()' with 'collection.toArray()'" "false" + +import java.util.*; +import java.util.function.*; + +class Test { + public void testToArray(List data, IntFunction generator) { + Object[] array = data.stream().toArray(generator); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeToArrayLambda.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeToArrayLambda.java new file mode 100644 index 000000000000..5e46ad818a69 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeToArrayLambda.java @@ -0,0 +1,9 @@ +// "Replace 'collection.stream().toArray()' with 'collection.toArray()'" "true" + +import java.util.*; + +class Test { + public void testToArray(List data) { + String[][] array = data.subList(0, /*max number*/ 10).stream().toArray((size) -> /*generate array*/ new String[(size)][]); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeToArrayMethodRef.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeToArrayMethodRef.java new file mode 100644 index 000000000000..5ab47ff4a578 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeToArrayMethodRef.java @@ -0,0 +1,9 @@ +// "Replace 'collection.stream().toArray()' with 'collection.toArray()'" "true" + +import java.util.*; + +class Test { + public void testToArray(List data) { + Object[] array = data.stream().toArray(String[]::new); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeToArrayNoArg.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeToArrayNoArg.java new file mode 100644 index 000000000000..83bee90d1e2d --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeToArrayNoArg.java @@ -0,0 +1,9 @@ +// "Replace 'collection.stream().toArray()' with 'collection.toArray()'" "true" + +import java.util.*; + +class Test { + public void testToArray(List data) { + Object[] array = data.stream().toArray(); + } +} \ No newline at end of file diff --git a/resources-en/src/inspectionDescriptions/SimplifyStreamApiCallChains.html b/resources-en/src/inspectionDescriptions/SimplifyStreamApiCallChains.html index b16ab16d4000..a7d64eb764ec 100644 --- a/resources-en/src/inspectionDescriptions/SimplifyStreamApiCallChains.html +++ b/resources-en/src/inspectionDescriptions/SimplifyStreamApiCallChains.html @@ -11,6 +11,7 @@ It allows to avoid creating redundant temporary objects when traversing a collec
  • collection.stream().collect(Collectors.toList()) → new ArrayList<>(collection)
  • collection.stream().collect(Collectors.toSet()) → new HashSet<>(collection)
  • collection.stream().collect(Collectors.toCollection(CollectionType::new)) → new CollectionType<>(collection)
  • +
  • collection.stream().toArray() → collection.toArray()
  • Arrays.asList().stream() → Arrays.stream() or Stream.of()
  • Collections.singleton().stream() → Stream.of()
  • Collections.singletonList().stream() → Stream.of()
  • From e73e12bcb1379b330b4d34e47acfcf657a16f7fd Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Thu, 15 Dec 2016 15:47:35 +0700 Subject: [PATCH 175/558] SimplifyStreamApiCallChainsInspection#handleToArray simplified via getQualifierMethodCall --- .../SimplifyStreamApiCallChainsInspection.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java index 0e7c7276dfd4..11430747f69d 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java @@ -137,12 +137,10 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns } private void handleToArray(PsiMethodCallExpression methodCall) { - PsiReferenceExpression methodExpression = methodCall.getMethodExpression(); - PsiExpression qualifier = methodExpression.getQualifierExpression(); - if(qualifier instanceof PsiMethodCallExpression && isCollectionStream((PsiMethodCallExpression)qualifier)) { + if(isCollectionStream(getQualifierMethodCall(methodCall))) { PsiArrayType type = getArrayType(methodCall); if(type != null) { - PsiElement nameElement = methodExpression.getReferenceNameElement(); + PsiElement nameElement = methodCall.getMethodExpression().getReferenceNameElement(); LOG.assertTrue(nameElement != null); String replacement = type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT+"[]") ? "" : "new "+type.getCanonicalText().replaceFirst("\\[]", "[0]"); From 02877b57966f60cc51ffc046ee26a31fb6719d51 Mon Sep 17 00:00:00 2001 From: Elizaveta Shashkova Date: Thu, 15 Dec 2016 12:08:32 +0300 Subject: [PATCH 176/558] Disable pyqt tests on windows until the conda package update --- .../com/jetbrains/env/python/PythonDebuggerTest.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/python/testSrc/com/jetbrains/env/python/PythonDebuggerTest.java b/python/testSrc/com/jetbrains/env/python/PythonDebuggerTest.java index 9131681e0a30..b50bb4d1f376 100644 --- a/python/testSrc/com/jetbrains/env/python/PythonDebuggerTest.java +++ b/python/testSrc/com/jetbrains/env/python/PythonDebuggerTest.java @@ -739,6 +739,10 @@ public class PythonDebuggerTest extends PyEnvTestCase { @Test @Staging public void testPyQtQThreadInheritor() throws Exception { + if (UsefulTestCase.IS_UNDER_TEAMCITY && SystemInfo.isWindows) { + return; //Don't run under Windows + } + runPythonTest(new PyDebuggerTask("/debug", "test_pyqt1.py") { @Override protected void init() { @@ -777,6 +781,10 @@ public class PythonDebuggerTest extends PyEnvTestCase { @Test @Staging public void testPyQtMoveToThread() throws Exception { + if (UsefulTestCase.IS_UNDER_TEAMCITY && SystemInfo.isWindows) { + return; //Don't run under Windows + } + runPythonTest(new PyDebuggerTask("/debug", "test_pyqt2.py") { @Override protected void init() { @@ -816,6 +824,10 @@ public class PythonDebuggerTest extends PyEnvTestCase { @Test @Staging public void testPyQtQRunnableInheritor() throws Exception { + if (UsefulTestCase.IS_UNDER_TEAMCITY && SystemInfo.isWindows) { + return; //Don't run under Windows + } + runPythonTest(new PyDebuggerTask("/debug", "test_pyqt3.py") { @Override protected void init() { From 061d469f2f69e7d2a2bf78e8a162da041e9dd0cc Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 14 Dec 2016 13:05:22 +0300 Subject: [PATCH 177/558] cleanup --- .../util/containers/CircularCharBuffer.java | 20 +++++++------------ .../containers/CircularCharBufferTest.java | 5 ++--- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/platform/util/src/com/intellij/util/containers/CircularCharBuffer.java b/platform/util/src/com/intellij/util/containers/CircularCharBuffer.java index 912c397f9fb8..ffc550c7f8d5 100644 --- a/platform/util/src/com/intellij/util/containers/CircularCharBuffer.java +++ b/platform/util/src/com/intellij/util/containers/CircularCharBuffer.java @@ -22,7 +22,6 @@ import org.jetbrains.annotations.NotNull; * A single threaded, resizable, circular char queue backed by an array. */ public class CircularCharBuffer { - private char[] myArray; private final int myMaxCapacity; private int mySize; @@ -30,11 +29,13 @@ public class CircularCharBuffer { private int myHead; public CircularCharBuffer(int initialCapacity) { - this(initialCapacity, -1); + this(initialCapacity, Integer.MAX_VALUE); } public CircularCharBuffer(int initialCapacity, int maxCapacity) { - assert maxCapacity < 0 || initialCapacity <= maxCapacity; + if (maxCapacity <= 0 || initialCapacity > maxCapacity) { + throw new IllegalArgumentException( "There must be 0 < initialCapacity <= maxCapacity, but got initialCapacity=" + initialCapacity + "; maxCapacity=" + maxCapacity); + } myArray = new char[initialCapacity]; myMaxCapacity = maxCapacity; mySize = 0; @@ -70,15 +71,11 @@ public class CircularCharBuffer { } mySize++; if (mySize > length) { - doPoll(); + poll(); } } public int poll() { - return doPoll(); - } - - public int doPoll() { if (mySize == 0) { return -1; } @@ -110,14 +107,11 @@ public class CircularCharBuffer { if (newSize <= length) { return true; } - if (myMaxCapacity > -1 && length == myMaxCapacity) { + if (length == myMaxCapacity) { return false; } normalize(); - int newLength = Math.max(length << 1, newSize); - if (myMaxCapacity > -1) { - newLength = Math.min(myMaxCapacity, newLength); - } + int newLength = Math.min(myMaxCapacity, Math.max(length << 1, newSize)); char[] newArray = new char[newLength]; System.arraycopy(myArray, myHead, newArray, 0, mySize); myArray = newArray; diff --git a/platform/util/testSrc/com/intellij/util/containers/CircularCharBufferTest.java b/platform/util/testSrc/com/intellij/util/containers/CircularCharBufferTest.java index b19255622378..9cc31b6a4966 100644 --- a/platform/util/testSrc/com/intellij/util/containers/CircularCharBufferTest.java +++ b/platform/util/testSrc/com/intellij/util/containers/CircularCharBufferTest.java @@ -19,7 +19,6 @@ import org.junit.Assert; import org.junit.Test; public class CircularCharBufferTest { - @Test public void testSingleAdd() throws Exception { CircularCharBuffer queue = new CircularCharBuffer(0); @@ -33,7 +32,7 @@ public class CircularCharBufferTest { @Test public void testResize1() throws Exception { CircularCharBuffer queue = new CircularCharBuffer(4); - char[] buf = new char[] {'1', '2', '3', '4', '5'}; + char[] buf = {'1', '2', '3', '4', '5'}; queue.add(buf); Assert.assertEquals(queue.size(), buf.length); for (char c : buf) { @@ -45,7 +44,7 @@ public class CircularCharBufferTest { @Test public void testResize2() throws Exception { CircularCharBuffer queue = new CircularCharBuffer(4, 16); - char[] buf = new char[] {'1', '2', '3', '4', '5', '6', '7', '8'}; + char[] buf = {'1', '2', '3', '4', '5', '6', '7', '8'}; queue.add(buf); Assert.assertEquals(queue.size(), buf.length); Assert.assertEquals(buf[0], queue.poll()); From 0e5676b5020330e31952f81d80a38c4c8267bb14 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 14 Dec 2016 13:52:49 +0300 Subject: [PATCH 178/558] console: speed optimisation --- .../execution/impl/ConsoleBuffer.java | 155 +++--------------- .../intellij/execution/impl/ConsoleUtil.java | 30 ++-- .../execution/impl/ConsoleViewImpl.java | 120 ++++++-------- .../execution/impl/ConsoleBufferTest.java | 102 ++++++------ .../execution/impl/ConsoleUtilTest.java | 2 +- 5 files changed, 141 insertions(+), 268 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleBuffer.java b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleBuffer.java index 4d1ce56257f0..4e69ed64761d 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleBuffer.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleBuffer.java @@ -23,6 +23,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.SystemProperties; +import com.intellij.util.containers.HashSet; import gnu.trove.TIntArrayList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -42,7 +43,6 @@ import java.util.*; * @since 4/5/11 5:26 PM */ public class ConsoleBuffer { - private static final int DEFAULT_CYCLIC_BUFFER_UNIT_SIZE = 256; private static final boolean DEBUG_PROCESSING = false; @@ -52,7 +52,7 @@ public class ConsoleBuffer { *

    * Feel free to check rationale for using this approach at {@link #myCyclicBufferSize} contract. */ - private final Deque myDeferredOutput = new ArrayDeque<>(); + final Deque myDeferredOutput = new ArrayDeque<>(); private final Set myContentTypesToNotStripOnCycling = new HashSet<>(); /** @@ -104,8 +104,7 @@ public class ConsoleBuffer { *

    * Target offsets are anchored to the {@link #myDeferredOutput deferred buffer}. */ - private final List myDeferredTokens = new ArrayList<>(); - private final Set myDeferredTypes = new HashSet<>(); + private final List myTokens = new ArrayList<>(); private boolean myKeepSlashR = true; @@ -147,10 +146,6 @@ public class ConsoleBuffer { myKeepSlashR = keep; } - boolean isEmpty() { - return myDeferredOutput.isEmpty() || myDeferredOutput.size() == 1 && myDeferredOutput.getFirst().length() <= 0; - } - int getLength() { return myDeferredOutputLength; } @@ -160,18 +155,8 @@ public class ConsoleBuffer { } @NotNull - List getDeferredTokens() { - return myDeferredTokens; - } - - @NotNull - Set getDeferredTokenTypes() { - return myDeferredTypes; - } - - @NotNull - Deque getDeferredOutput() { - return myDeferredOutput; + List getTokens() { + return myTokens; } @NotNull @@ -218,8 +203,7 @@ public class ConsoleBuffer { } } myDeferredOutputLength = 0; - myDeferredTypes.clear(); - myDeferredTokens.clear(); + myTokens.clear(); if (clearUserInputAsWell) { myDeferredUserInput = new StringBuffer(); } @@ -284,8 +268,6 @@ public class ConsoleBuffer { text = text.substring(text.length() - numberOfSymbolsToProceed); } - myDeferredTypes.add(contentType); - text = StringUtil.convertLineSeparators(text, myKeepSlashR); myDeferredOutputLength += text.length(); @@ -313,69 +295,10 @@ public class ConsoleBuffer { myDeferredUserInput.append(text); } - ConsoleUtil.addToken(text.length(), info, contentType, myDeferredTokens); + ConsoleUtil.addToken(text.length(), info, contentType, myTokens); return new Pair<>(text, trimmedSymbolsNumber); } - //private void checkState() { - // int bufferOffset = 0; - // Iterator iterator = myDeferredOutput.iterator(); - // StringBuilder currentBuffer = null; - // int prevTokenEnd = 0; - // for (TokenInfo token : myDeferredTokens) { - // if (prevTokenEnd != token.startOffset) { - // try { - // System.out.println("Problem detected!"); - // System.in.read(); - // } - // catch (IOException e) { - // e.printStackTrace(); - // } - // } - // prevTokenEnd = token.endOffset; - // char c = token.contentType == ConsoleViewContentType.ERROR_OUTPUT ? '2' : '1'; - // int length = token.getLength(); - // if (currentBuffer == null) { - // currentBuffer = iterator.next(); - // } - // - // while (length > 0) { - // if (bufferOffset == currentBuffer.length()) { - // if (!iterator.hasNext()) { - // try { - // System.out.println("Problem detected!"); - // System.in.read(); - // } - // catch (IOException e) { - // e.printStackTrace(); - // } - // } - // currentBuffer = iterator.next(); - // bufferOffset = 0; - // } - // else { - // int endOffset = Math.min(bufferOffset + length, currentBuffer.length()); - // if (token.contentType == ConsoleViewContentType.NORMAL_OUTPUT || token.contentType == ConsoleViewContentType.ERROR_OUTPUT) { - // for (int i = bufferOffset; i < endOffset; i++) { - // char c1 = currentBuffer.charAt(i); - // if (c1 != c && c1 != '\n') { - // try { - // System.out.println("Problem detected!"); - // System.in.read(); - // } - // catch (IOException e) { - // e.printStackTrace(); - // } - // } - // } - // } - // length -= endOffset - bufferOffset; - // bufferOffset = endOffset; - // } - // } - // } - //} - /** * IJ console works as follows - it receives managed process outputs from dedicated thread that serves that process and * pushes it to the {@link Document document} of editor used to represent process console. Important point here is that process @@ -395,7 +318,6 @@ public class ConsoleBuffer { } final int numberOfSymbolsToRemove = Math.min(myDeferredOutputLength, myDeferredOutputLength + numberOfNewSymbols - myCyclicBufferSize); - myDeferredTypes.clear(); if (DEBUG_PROCESSING) { log("Starting console trimming. Need to delete %d symbols (deferred output length: %d, number of new symbols: %d, " @@ -408,15 +330,14 @@ public class ConsoleBuffer { Context context = new Context(numberOfSymbolsToRemove); TIntArrayList indicesOfTokensToRemove = new TIntArrayList(); - for (int i = 0; i < myDeferredTokens.size(); i++) { - ConsoleViewImpl.TokenInfo tokenInfo = myDeferredTokens.get(i); - tokenInfo.startOffset -= context.removedSymbolsNumber; - tokenInfo.endOffset -= context.removedSymbolsNumber; + for (int i = 0; i < myTokens.size(); i++) { + ConsoleViewImpl.TokenInfo tokenInfo = myTokens.get(i); + tokenInfo.startOffset -= context.removedSymbols; + tokenInfo.endOffset -= context.removedSymbols; if (!context.canContinueProcessing()) { // Just update token offsets. - myDeferredTypes.add(tokenInfo.contentType); - if (context.removedSymbolsNumber == 0) { + if (context.removedSymbols == 0) { break; } continue; @@ -427,7 +348,6 @@ public class ConsoleBuffer { // Don't remove input text. if (myContentTypesToNotStripOnCycling.contains(tokenInfo.contentType)) { skip(context, tokenLength); - myDeferredTypes.add(tokenInfo.contentType); continue; } @@ -437,22 +357,19 @@ public class ConsoleBuffer { } else { tokenInfo.endOffset -= removedTokenSymbolsNumber; - myDeferredTypes.add(tokenInfo.contentType); } } for (int i = indicesOfTokensToRemove.size() - 1; i >= 0; i--) { - myDeferredTokens.remove(indicesOfTokensToRemove.get(i)); + myTokens.remove(indicesOfTokensToRemove.get(i)); } - if (!myDeferredTokens.isEmpty()) { - ConsoleViewImpl.TokenInfo tokenInfo = myDeferredTokens.get(0); + if (!myTokens.isEmpty()) { + ConsoleViewImpl.TokenInfo tokenInfo = myTokens.get(0); if (tokenInfo.startOffset > 0) { final HyperlinkInfo hyperlinkInfo = tokenInfo.getHyperlinkInfo(); - myDeferredTokens - .add(0, hyperlinkInfo != null ? new ConsoleViewImpl.HyperlinkTokenInfo(ConsoleViewContentType.USER_INPUT, 0, tokenInfo.startOffset, hyperlinkInfo) - : new ConsoleViewImpl.TokenInfo(ConsoleViewContentType.USER_INPUT, 0, tokenInfo.startOffset)); - myDeferredTypes.add(ConsoleViewContentType.USER_INPUT); + ConsoleViewImpl.TokenInfo info = new ConsoleViewImpl.TokenInfo(ConsoleViewContentType.USER_INPUT, 0, tokenInfo.startOffset, hyperlinkInfo); + myTokens.add(0, info); } } @@ -500,7 +417,7 @@ public class ConsoleBuffer { private int remove(@NotNull Context context, int tokenLength) { int removedSymbolsNumber = 0; - int remainingTotalNumberOfSymbolsToRemove = context.numberOfSymbolsToRemove - context.removedSymbolsNumber; + int remainingTotalNumberOfSymbolsToRemove = context.numberOfSymbolsToRemove - context.removedSymbols; int numberOfTokenSymbolsToRemove = Math.min(remainingTotalNumberOfSymbolsToRemove, tokenLength); while (numberOfTokenSymbolsToRemove > 0 && context.currentBuffer != null) { int diff = numberOfTokenSymbolsToRemove - (context.currentBuffer.length() - context.bufferOffset); @@ -508,12 +425,12 @@ public class ConsoleBuffer { int numberOfSymbolsRemovedFromCurrentBuffer = endDeleteBufferOffset - context.bufferOffset; if (DEBUG_PROCESSING) { log("About to delete %d symbols from the current buffer (offset is %d). Removed symbols number: %d. Current buffer: %d: '%s'", - numberOfSymbolsRemovedFromCurrentBuffer, context.bufferOffset, context.removedSymbolsNumber, context.currentBuffer.length(), + numberOfSymbolsRemovedFromCurrentBuffer, context.bufferOffset, context.removedSymbols, context.currentBuffer.length(), StringUtil.convertLineSeparators(context.currentBuffer.toString())); } numberOfTokenSymbolsToRemove -= numberOfSymbolsRemovedFromCurrentBuffer; removedSymbolsNumber += numberOfSymbolsRemovedFromCurrentBuffer; - context.removedSymbolsNumber += numberOfSymbolsRemovedFromCurrentBuffer; + context.removedSymbols += numberOfSymbolsRemovedFromCurrentBuffer; myDeferredOutputLength -= numberOfSymbolsRemovedFromCurrentBuffer; if (context.bufferOffset == 0 && (diff >= 0 || endDeleteBufferOffset == context.currentBuffer.length())) { @@ -539,7 +456,7 @@ public class ConsoleBuffer { StringBuilder currentBuffer; public final Iterator iterator; int bufferOffset; - int removedSymbolsNumber; + int removedSymbols; Context(int numberOfSymbolsToRemove) { this.numberOfSymbolsToRemove = numberOfSymbolsToRemove; @@ -548,7 +465,7 @@ public class ConsoleBuffer { } boolean canContinueProcessing() { - return removedSymbolsNumber < numberOfSymbolsToRemove && currentBuffer != null; + return removedSymbols < numberOfSymbolsToRemove && currentBuffer != null; } boolean nextBuffer() { @@ -566,7 +483,7 @@ public class ConsoleBuffer { return; } log("Tokens:"); - for (ConsoleViewImpl.TokenInfo token : myDeferredTokens) { + for (ConsoleViewImpl.TokenInfo token : myTokens) { log("\t" + token); } log("Data:"); @@ -577,34 +494,8 @@ public class ConsoleBuffer { } private static void log(Object o) { - //try { - // doLog(o); - //} - //catch (Exception e) { - // e.printStackTrace(); - //} } private static void log(String message, Object... formatData) { - //try { - // doLog(String.format(message, formatData)); - //} - //catch (Exception e) { - // e.printStackTrace(); - //} } - - //private static BufferedWriter myWriter; - //private static void doLog(Object o) throws Exception { - // if (!DEBUG_PROCESSING) { - // return; - // } - // File file = new File("/home/denis/log/console.log"); - // if (myWriter == null || !file.exists()) { - // myWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file))); - // } - // myWriter.write(o.toString()); - // myWriter.newLine(); - // myWriter.flush(); - //} } diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleUtil.java b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleUtil.java index 9c1d215fba9a..8e34fe8a99bb 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleUtil.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleUtil.java @@ -22,24 +22,17 @@ import org.jetbrains.annotations.Nullable; import java.util.List; -import static com.intellij.execution.impl.ConsoleViewImpl.HyperlinkTokenInfo; -import static com.intellij.execution.impl.ConsoleViewImpl.TokenInfo; - /** * Holds utility methods for console processing. * * @author Denis Zhdanov * @since 4/6/11 3:50 PM */ -public class ConsoleUtil { - - private ConsoleUtil() { - } - - public static void addToken(int length, @Nullable HyperlinkInfo info, @NotNull ConsoleViewContentType contentType, @NotNull List tokens) { +class ConsoleUtil { + static void addToken(int length, @Nullable HyperlinkInfo info, @NotNull ConsoleViewContentType contentType, @NotNull List outTokens) { int startOffset = 0; - if (!tokens.isEmpty()) { - final TokenInfo lastToken = tokens.get(tokens.size() - 1); + if (!outTokens.isEmpty()) { + final ConsoleViewImpl.TokenInfo lastToken = outTokens.get(outTokens.size() - 1); if (lastToken.contentType == contentType && info == lastToken.getHyperlinkInfo()) { lastToken.endOffset += length; // optimization return; @@ -49,8 +42,7 @@ public class ConsoleUtil { } } - tokens.add(info != null ? new HyperlinkTokenInfo(contentType, startOffset, startOffset + length, info) - : new TokenInfo(contentType, startOffset, startOffset + length)); + outTokens.add(new ConsoleViewImpl.TokenInfo(contentType, startOffset, startOffset + length, info)); } /** @@ -62,7 +54,7 @@ public class ConsoleUtil { * @param startOffset start offset of the removed text (inclusive) * @param endOffset end offset of the removed text (exclusive) */ - public static void updateTokensOnTextRemoval(@NotNull List tokens, int startOffset, int endOffset) { + static void updateTokensOnTextRemoval(@NotNull List tokens, int startOffset, int endOffset) { final int firstIndex = findTokenInfoIndexByOffset(tokens, startOffset); if (firstIndex >= tokens.size()) { return; @@ -72,7 +64,7 @@ public class ConsoleUtil { boolean updateOnly = false; int removeIndexStart = -1; int removeIndexEnd = -1; - final TokenInfo firstToken = tokens.get(firstIndex); + final ConsoleViewImpl.TokenInfo firstToken = tokens.get(firstIndex); if (startOffset == firstToken.startOffset) { // Removed range is located entirely at the first token. @@ -92,7 +84,7 @@ public class ConsoleUtil { } for (int i = firstIndex + 1; i < tokens.size(); i++) { - final TokenInfo tokenInfo = tokens.get(i); + final ConsoleViewImpl.TokenInfo tokenInfo = tokens.get(i); if (updateOnly) { tokenInfo.startOffset -= removedSymbolsNumber; tokenInfo.endOffset -= removedSymbolsNumber; @@ -111,7 +103,7 @@ public class ConsoleUtil { // Update current token offsets and adjust ranges of all subsequent tokens. tokenInfo.startOffset = startOffset; - tokenInfo.endOffset = startOffset + (tokenInfo.endOffset - endOffset); + tokenInfo.endOffset = startOffset + tokenInfo.endOffset - endOffset; updateOnly = true; } @@ -129,13 +121,13 @@ public class ConsoleUtil { * @param offset target offset * @return index of the target token within the given list; given list length if no such token is found */ - public static int findTokenInfoIndexByOffset(@NotNull List tokens, final int offset) { + static int findTokenInfoIndexByOffset(@NotNull List tokens, final int offset) { int low = 0; int high = tokens.size() - 1; while (low <= high) { final int mid = (low + high) / 2; - final TokenInfo midVal = tokens.get(mid); + final ConsoleViewImpl.TokenInfo midVal = tokens.get(mid); if (offset < midVal.startOffset) { high = mid - 1; } diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java index 022269643440..3a0a226e630b 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java @@ -126,7 +126,10 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo private final Collection myListeners = new CopyOnWriteArraySet<>(); private final List customActions = new ArrayList<>(); - private final ConsoleBuffer myBuffer = new ConsoleBuffer(); + + /** the text from {@link #print(String, ConsoleViewContentType)} goes there and stays there until {@link #flushDeferredText()} is called */ + private final ConsoleBuffer myDeferredBuffer = new ConsoleBuffer(); + private boolean myUpdateFoldingsEnabled = true; private EditorHyperlinkSupport myHyperlinks; private MyDiffContainer myJLayeredPane; @@ -179,11 +182,16 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo final ConsoleViewContentType contentType; int startOffset; int endOffset; + private final HyperlinkInfo myHyperlinkInfo; - TokenInfo(@NotNull ConsoleViewContentType contentType, int startOffset, int endOffset) { + TokenInfo(@NotNull ConsoleViewContentType contentType, + int startOffset, + int endOffset, + @Nullable HyperlinkInfo hyperlinkInfo) { this.contentType = contentType; this.startOffset = startOffset; this.endOffset = endOffset; + myHyperlinkInfo = hyperlinkInfo; } public int getLength() { @@ -195,23 +203,6 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo return contentType + "[" + startOffset + ";" + endOffset + "]"; } - @Nullable - public HyperlinkInfo getHyperlinkInfo() { - return null; - } - } - - static class HyperlinkTokenInfo extends TokenInfo { - @NotNull - private final HyperlinkInfo myHyperlinkInfo; - - HyperlinkTokenInfo(@NotNull ConsoleViewContentType contentType, final int startOffset, final int endOffset, @NotNull HyperlinkInfo hyperlinkInfo) { - super(contentType, startOffset, endOffset); - myHyperlinkInfo = hyperlinkInfo; - } - - @NotNull - @Override public HyperlinkInfo getHyperlinkInfo() { return myHyperlinkInfo; } @@ -361,7 +352,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo synchronized (LOCK) { // real document content will be cleared on next flush; myContentSize = 0; - myBuffer.clear(); + myDeferredBuffer.clear(); myFolding.clear(); } if (myFlushAlarm.isDisposed()) return; @@ -381,7 +372,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo flushDeferredText(); if (myEditor == null) return; int moveOffset = Math.min(offset, myEditor.getDocument().getTextLength()); - if (myBuffer.isUseCyclicBuffer() && moveOffset >= myEditor.getDocument().getTextLength()) { + if (myDeferredBuffer.isUseCyclicBuffer() && moveOffset >= myEditor.getDocument().getTextLength()) { moveOffset = 0; } myEditor.getCaretModel().moveToOffset(moveOffset); @@ -440,13 +431,13 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo } public void setEmulateCarriageReturn(boolean emulate) { - myBuffer.setKeepSlashR(emulate); + myDeferredBuffer.setKeepSlashR(emulate); } @Override public boolean hasDeferredOutput() { synchronized (LOCK) { - return myBuffer.getLength() > 0; + return myDeferredBuffer.getLength() > 0; } } @@ -568,7 +559,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo mySpareTimeAlarm.cancelAllRequests(); disposeEditor(); synchronized (LOCK) { - myBuffer.clear(); + myDeferredBuffer.clear(); } myEditor = null; myHyperlinks = null; @@ -615,7 +606,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo private void printHyperlink(@NotNull String text, @NotNull ConsoleViewContentType contentType, @Nullable HyperlinkInfo info) { synchronized (LOCK) { - Pair pair = myBuffer.print(text, contentType, info); + Pair pair = myDeferredBuffer.print(text, contentType, info); text = pair.first; myContentSize += text.length() - pair.second; @@ -623,16 +614,12 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo flushDeferredUserInput(); } if (myEditor != null) { - final boolean shouldFlushNow = myBuffer.isUseCyclicBuffer() && myBuffer.getLength() >= myBuffer.getCyclicBufferSize(); + final boolean shouldFlushNow = myDeferredBuffer.isUseCyclicBuffer() && myDeferredBuffer.getLength() >= myDeferredBuffer.getCyclicBufferSize(); addFlushRequest(new MyFlushRunnable(), shouldFlushNow ? 0 : DEFAULT_FLUSH_DELAY); } } } - private void addToken(int length, @Nullable HyperlinkInfo info, @NotNull ConsoleViewContentType contentType) { - ConsoleUtil.addToken(length, info, contentType, myTokens); - } - private static ModalityState getStateForUpdate() { return null;//myStateForUpdate != null ? myStateForUpdate.compute() : ModalityState.stateForComponent(this); } @@ -698,21 +685,21 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo final String addedText; - final Collection contentTypes; + final Collection contentTypes = new HashSet<>(); int deferredTokensSize; synchronized (LOCK) { if (myOutputPaused) return; - if (myBuffer.isEmpty()) return; + if (myDeferredBuffer.getLength() == 0) return; - addedText = myBuffer.getText(); + addedText = myDeferredBuffer.getText(); - contentTypes = Collections.unmodifiableCollection(new HashSet<>(myBuffer.getDeferredTokenTypes())); - List deferredTokens = myBuffer.getDeferredTokens(); + List deferredTokens = myDeferredBuffer.getTokens(); for (TokenInfo deferredToken : deferredTokens) { - addToken(deferredToken.getLength(), deferredToken.getHyperlinkInfo(), deferredToken.contentType); + ConsoleUtil.addToken(deferredToken.getLength(), deferredToken.getHyperlinkInfo(), deferredToken.contentType, myTokens); + contentTypes.add(deferredToken.contentType); } deferredTokensSize = deferredTokens.size(); - myBuffer.clear(false); + myDeferredBuffer.clear(false); cancelHeavyAlarm(); } final Document document = myEditor.getDocument(); @@ -793,7 +780,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo } private void flushDeferredUserInput() { - final String textToSend = myBuffer.cutFirstUserInputLine(); + final String textToSend = myDeferredBuffer.cutFirstUserInputLine(); if (textToSend == null) { return; } @@ -874,7 +861,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo } }, this); - int bufferSize = myBuffer.isUseCyclicBuffer() ? myBuffer.getCyclicBufferSize() : 0; + int bufferSize = myDeferredBuffer.isUseCyclicBuffer() ? myDeferredBuffer.getCyclicBufferSize() : 0; editor.getDocument().setCyclicBufferSize(bufferSize); editor.putUserData(CONSOLE_VIEW_IN_EDITOR_VIEW, this); @@ -906,7 +893,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo if (event.getOldFragment().length() == 0 && newFragmentLength > 0) { synchronized (LOCK) { myContentSize += newFragmentLength; - addToken(newFragmentLength, null, ConsoleViewContentType.NORMAL_OUTPUT); + ConsoleUtil.addToken(newFragmentLength, null, ConsoleViewContentType.NORMAL_OUTPUT, myTokens); } } else { @@ -1549,28 +1536,27 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo */ private void insertUserText(final String s, int offset) { ApplicationManager.getApplication().assertIsDispatchThread(); - final ConsoleViewImpl consoleView = this; - final ConsoleBuffer buffer = consoleView.myBuffer; - final Editor editor = consoleView.myEditor; + final ConsoleBuffer buffer = myDeferredBuffer; + final Editor editor = myEditor; final Document document = editor.getDocument(); final int startOffset; String textToUse = StringUtil.convertLineSeparators(s); - synchronized (consoleView.LOCK) { - if (consoleView.myTokens.isEmpty()) { - addToken(0, null, ConsoleViewContentType.SYSTEM_OUTPUT); + synchronized (LOCK) { + if (myTokens.isEmpty()) { + ConsoleUtil.addToken(0, null, ConsoleViewContentType.SYSTEM_OUTPUT, myTokens); } - final TokenInfo info = consoleView.myTokens.get(consoleView.myTokens.size() - 1); + final TokenInfo info = myTokens.get(myTokens.size() - 1); if (info.contentType != ConsoleViewContentType.USER_INPUT && !StringUtil.containsChar(textToUse, '\n')) { - consoleView.print(textToUse, ConsoleViewContentType.USER_INPUT); - consoleView.flushDeferredText(); + print(textToUse, ConsoleViewContentType.USER_INPUT); + flushDeferredText(); editor.getCaretModel().moveToOffset(document.getTextLength()); editor.getSelectionModel().removeSelection(); return; } if (info.contentType != ConsoleViewContentType.USER_INPUT) { insertUserText("temp", offset); - final TokenInfo newInfo = consoleView.myTokens.get(consoleView.myTokens.size() - 1); + final TokenInfo newInfo = myTokens.get(myTokens.size() - 1); replaceUserText(textToUse, newInfo.startOffset, newInfo.endOffset); return; } @@ -1582,7 +1568,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo int charCountToAdd = textToUse.length(); info.endOffset += charCountToAdd; - consoleView.myContentSize += charCountToAdd; + myContentSize += charCountToAdd; } try { @@ -1609,19 +1595,18 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo insertUserText(text, start); return; } - final ConsoleViewImpl consoleView = this; - final ConsoleBuffer buffer = consoleView.myBuffer; - final Editor editor = consoleView.myEditor; + final ConsoleBuffer buffer = myDeferredBuffer; + final Editor editor = myEditor; final Document document = editor.getDocument(); final int startOffset; final int endOffset; - synchronized (consoleView.LOCK) { - if (consoleView.myTokens.isEmpty()) return; - final TokenInfo info = consoleView.myTokens.get(consoleView.myTokens.size() - 1); + synchronized (LOCK) { + if (myTokens.isEmpty()) return; + final TokenInfo info = myTokens.get(myTokens.size() - 1); if (info.contentType != ConsoleViewContentType.USER_INPUT) { - consoleView.print(text, ConsoleViewContentType.USER_INPUT); - consoleView.flushDeferredText(); + print(text, ConsoleViewContentType.USER_INPUT); + flushDeferredText(); editor.getCaretModel().moveToOffset(document.getTextLength()); editor.getSelectionModel().removeSelection(); return; @@ -1646,9 +1631,9 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo int charCountToReplace = text.length() - endOffset + startOffset; info.endOffset += charCountToReplace; if (info.startOffset == info.endOffset) { - consoleView.myTokens.remove(consoleView.myTokens.size() - 1); + myTokens.remove(myTokens.size() - 1); } - consoleView.myContentSize += charCountToReplace; + myContentSize += charCountToReplace; } try { @@ -1670,18 +1655,17 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo * @param length length of deleted text */ private void deleteUserText(int offset, int length) { - ConsoleViewImpl consoleView = this; - ConsoleBuffer buffer = consoleView.myBuffer; - final Editor editor = consoleView.myEditor; + ConsoleBuffer buffer = myDeferredBuffer; + final Editor editor = myEditor; final Document document = editor.getDocument(); final int startOffset; final int endOffset; - synchronized (consoleView.LOCK) { - if (consoleView.myTokens.isEmpty()) return; - final TokenInfo info = consoleView.myTokens.get(consoleView.myTokens.size() - 1); + synchronized (LOCK) { + if (myTokens.isEmpty()) return; + final TokenInfo info = myTokens.get(myTokens.size() - 1); if (info.contentType != ConsoleViewContentType.USER_INPUT) return; - if (myBuffer.getUserInputLength() == 0) return; + if (myDeferredBuffer.getUserInputLength() == 0) return; final int deferredOffset = myContentSize - buffer.getLength() - buffer.getUserInputLength(); startOffset = getStartOffset(offset, info, deferredOffset); diff --git a/platform/platform-tests/testSrc/com/intellij/execution/impl/ConsoleBufferTest.java b/platform/platform-tests/testSrc/com/intellij/execution/impl/ConsoleBufferTest.java index feceabc2e62f..cfaff535bfcd 100644 --- a/platform/platform-tests/testSrc/com/intellij/execution/impl/ConsoleBufferTest.java +++ b/platform/platform-tests/testSrc/com/intellij/execution/impl/ConsoleBufferTest.java @@ -2,21 +2,14 @@ package com.intellij.execution.impl; import com.intellij.execution.ui.ConsoleViewContentType; import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.testFramework.PlatformTestUtil; +import com.intellij.testFramework.UsefulTestCase; import org.jetbrains.annotations.NotNull; -import org.junit.Before; -import org.junit.Test; import java.util.*; +import java.util.stream.Collectors; -import static com.intellij.execution.impl.ConsoleViewImpl.TokenInfo; -import static org.junit.Assert.assertEquals; - -/** - * @author Denis Zhdanov - * @since 04/06/2011 - */ -public class ConsoleBufferTest { - +public class ConsoleBufferTest extends UsefulTestCase { private static final ConsoleViewContentType IMPORTANT_OUTPUT = new ConsoleViewContentType("IMPORTANT_OUTPUT", (TextAttributes)null); private static final ConsoleViewContentType NORMAL_OUTPUT = new ConsoleViewContentType("NORMAL_OUTPUT", (TextAttributes)null); private static final ConsoleViewContentType BORING_OUTPUT = new ConsoleViewContentType("BORING_OUTPUT", (TextAttributes)null); @@ -24,58 +17,54 @@ public class ConsoleBufferTest { private ConsoleBuffer myBuffer; private int myBufferSize; private int myBufferUnitSize; - - @Before + + @Override public void setUp() throws Exception { - myBufferSize = 10; - myBufferUnitSize = 3; + super.setUp(); + myBufferSize = isPerformanceTest() ? 1024 : 10; + myBufferUnitSize = isPerformanceTest() ? 256 : 3; init(); } - @Test - public void nonExceedingBufferSize() { + public void testNonExceedingBufferSize() { myBuffer.print("a", NORMAL_OUTPUT, null); - checkState(s("a"), new TokenInfo(NORMAL_OUTPUT, 0, 1)); + checkState(s("a"), new ConsoleViewImpl.TokenInfo(NORMAL_OUTPUT, 0, 1, null)); myBuffer.print("b", BORING_OUTPUT, null); - checkState(s("ab"), new TokenInfo(NORMAL_OUTPUT, 0, 1), new TokenInfo(BORING_OUTPUT, 1, 2)); + checkState(s("ab"), new ConsoleViewImpl.TokenInfo(NORMAL_OUTPUT, 0, 1, null), new ConsoleViewImpl.TokenInfo(BORING_OUTPUT, 1, 2, null)); myBuffer.print("cd", BORING_OUTPUT, null); - checkState(s("abc", "d"), new TokenInfo(NORMAL_OUTPUT, 0, 1), new TokenInfo(BORING_OUTPUT, 1, 4)); + checkState(s("abc", "d"), new ConsoleViewImpl.TokenInfo(NORMAL_OUTPUT, 0, 1, null), new ConsoleViewImpl.TokenInfo(BORING_OUTPUT, 1, 4, null)); myBuffer.print("ef", BORING_OUTPUT, null); - checkState(s("abc", "def"), new TokenInfo(NORMAL_OUTPUT, 0, 1), new TokenInfo(BORING_OUTPUT, 1, 6)); + checkState(s("abc", "def"), new ConsoleViewImpl.TokenInfo(NORMAL_OUTPUT, 0, 1, null), new ConsoleViewImpl.TokenInfo(BORING_OUTPUT, 1, 6, null)); myBuffer.print("ghij", NORMAL_OUTPUT, null); checkState( s("abc", "def", "ghi", "j"), - new TokenInfo(NORMAL_OUTPUT, 0, 1), new TokenInfo(BORING_OUTPUT, 1, 6), new TokenInfo(NORMAL_OUTPUT, 6, 10) + new ConsoleViewImpl.TokenInfo(NORMAL_OUTPUT, 0, 1, null), new ConsoleViewImpl.TokenInfo(BORING_OUTPUT, 1, 6, null), new ConsoleViewImpl.TokenInfo(NORMAL_OUTPUT, 6, 10, null) ); } - @Test - public void cycling() { + public void testCycling() { myBuffer.print("abcdefghi", NORMAL_OUTPUT, null); myBuffer.print("jklm", BORING_OUTPUT, null); - checkState(s("def", "ghi", "jkl", "m"), new TokenInfo(NORMAL_OUTPUT, 0, 6), new TokenInfo(BORING_OUTPUT, 6, 10)); + checkState(s("def", "ghi", "jkl", "m"), new ConsoleViewImpl.TokenInfo(NORMAL_OUTPUT, 0, 6, null), new ConsoleViewImpl.TokenInfo(BORING_OUTPUT, 6, 10, null)); } - @Test - public void removedTokenOnCyclingIsNotShownAtDeferredTypes() { + public void testRemovedTokenOnCyclingIsNotShownAtDeferredTypes() { myBuffer.print("a", BORING_OUTPUT, null); myBuffer.print("bcdefghijklmn", NORMAL_OUTPUT, null); - checkState(s("efg", "hij", "klm", "n"), new TokenInfo(NORMAL_OUTPUT, 0, 10)); + checkState(s("efg", "hij", "klm", "n"), new ConsoleViewImpl.TokenInfo(NORMAL_OUTPUT, 0, 10, null)); } - @Test - public void tokenDataIsTrimmed() { + public void testTokenDataIsTrimmed() { myBuffer.print("1234567890", NORMAL_OUTPUT, null); myBuffer.print("123456789012", NORMAL_OUTPUT, null); - checkState(s("345", "678", "901", "2"), new TokenInfo(NORMAL_OUTPUT, 0, 10)); + checkState(s("345", "678", "901", "2"), new ConsoleViewImpl.TokenInfo(NORMAL_OUTPUT, 0, 10, null)); } - @Test - public void removingOfGreatNumberOfTokens() { + public void testRemovingOfGreatNumberOfTokens() { myBuffer.print("a", NORMAL_OUTPUT, null); myBuffer.print("b", BORING_OUTPUT, null); myBuffer.print("a", NORMAL_OUTPUT, null); @@ -83,23 +72,21 @@ public class ConsoleBufferTest { myBuffer.print("a", NORMAL_OUTPUT, null); myBuffer.print("1234567890", BORING_OUTPUT, null); - checkState(s("123", "456", "789", "0"), new TokenInfo(BORING_OUTPUT, 0, 10)); + checkState(s("123", "456", "789", "0"), new ConsoleViewImpl.TokenInfo(BORING_OUTPUT, 0, 10, null)); } - @Test - public void removeFromBufferEnd() { + public void testRemoveFromBufferEnd() { myBuffer.print("1234567", IMPORTANT_OUTPUT, null); myBuffer.print("890", NORMAL_OUTPUT, null); myBuffer.print("abc", BORING_OUTPUT, null); checkState( s("123", "456", "7ab", "c"), - new TokenInfo(IMPORTANT_OUTPUT, 0, 7), new TokenInfo(BORING_OUTPUT, 7, 10) + new ConsoleViewImpl.TokenInfo(IMPORTANT_OUTPUT, 0, 7, null), new ConsoleViewImpl.TokenInfo(BORING_OUTPUT, 7, 10, null) ); } - @Test - public void smallRemoveAtStart() { + public void testSmallRemoveAtStart() { myBuffer.print("abc", NORMAL_OUTPUT, null); myBuffer.print("def", BORING_OUTPUT, null); myBuffer.print("ghi", NORMAL_OUTPUT, null); @@ -108,8 +95,8 @@ public class ConsoleBufferTest { checkState( s("bc", "def", "ghi", "kl"), - new TokenInfo(NORMAL_OUTPUT, 0, 2), new TokenInfo(BORING_OUTPUT, 2, 5), new TokenInfo(NORMAL_OUTPUT, 5, 8), - new TokenInfo(BORING_OUTPUT, 8, 9), new TokenInfo(NORMAL_OUTPUT, 9, 10) + new ConsoleViewImpl.TokenInfo(NORMAL_OUTPUT, 0, 2, null), new ConsoleViewImpl.TokenInfo(BORING_OUTPUT, 2, 5, null), new ConsoleViewImpl.TokenInfo(NORMAL_OUTPUT, 5, 8, null), + new ConsoleViewImpl.TokenInfo(BORING_OUTPUT, 8, 9, null), new ConsoleViewImpl.TokenInfo(NORMAL_OUTPUT, 9, 10, null) ); } @@ -117,8 +104,8 @@ public class ConsoleBufferTest { return Arrays.asList(strings); } - private void checkState(@NotNull List expectedBuffers, @NotNull TokenInfo ... expectedTokens) { - Deque actualBuffers = myBuffer.getDeferredOutput(); + private void checkState(@NotNull List expectedBuffers, @NotNull ConsoleViewImpl.TokenInfo... expectedTokens) { + Collection actualBuffers = myBuffer.myDeferredOutput; assertEquals(expectedBuffers.size(), actualBuffers.size()); int i = 0; for (StringBuilder actual : actualBuffers) { @@ -126,23 +113,42 @@ public class ConsoleBufferTest { assertEquals(expected, actual.toString()); } - List actualTokens = myBuffer.getDeferredTokens(); + List actualTokens = myBuffer.getTokens(); assertEquals(expectedTokens.length, actualTokens.size()); i = 0; Set contentTypes = new HashSet<>(); - for (TokenInfo actual : actualTokens) { - TokenInfo expected = expectedTokens[i++]; + for (ConsoleViewImpl.TokenInfo actual : actualTokens) { + ConsoleViewImpl.TokenInfo expected = expectedTokens[i++]; assertEquals(expected.contentType, actual.contentType); assertEquals(expected.startOffset, actual.startOffset); assertEquals(expected.endOffset, actual.endOffset); contentTypes.add(expected.contentType); } - assertEquals(contentTypes, myBuffer.getDeferredTokenTypes()); + assertEquals(contentTypes, new HashSet<>(getAllDeferredTokenTypes(myBuffer.getTokens()))); } - + + // returns all ConsoleViewContentType mentioned in myTokens + @NotNull + Collection getAllDeferredTokenTypes(List myTokens) { + return myTokens.stream().map(info -> info.contentType).collect(Collectors.toSet()); + } + private void init() throws Exception { myBuffer = new ConsoleBuffer(true, myBufferSize, myBufferUnitSize); myBuffer.setContentTypesToNotStripOnCycling(Collections.singleton(IMPORTANT_OUTPUT)); } + + public void testPerformance() { + PlatformTestUtil.startPerformanceTest("slow console.print", 9 * 60 * 1000, ()->{ + myBuffer.clear(); + for (int i=0; i<20000000; i++) { + myBuffer.print("abc", NORMAL_OUTPUT, null); + myBuffer.print("ii\n", BORING_OUTPUT, null); + if (i > 1000000) { + int fi = 0; + } + } + }).cpuBound().assertTiming(); + } } diff --git a/platform/platform-tests/testSrc/com/intellij/execution/impl/ConsoleUtilTest.java b/platform/platform-tests/testSrc/com/intellij/execution/impl/ConsoleUtilTest.java index 01be17212c6e..46bb2e3f9128 100644 --- a/platform/platform-tests/testSrc/com/intellij/execution/impl/ConsoleUtilTest.java +++ b/platform/platform-tests/testSrc/com/intellij/execution/impl/ConsoleUtilTest.java @@ -98,6 +98,6 @@ public class ConsoleUtilTest { } private static ConsoleViewImpl.TokenInfo t(@NotNull ConsoleViewContentType contentType, int start, int end) { - return new ConsoleViewImpl.TokenInfo(contentType, start, end); + return new ConsoleViewImpl.TokenInfo(contentType, start, end, null); } } From 07d4f73ef5cc24cc344b1630b5a7c027a18b071a Mon Sep 17 00:00:00 2001 From: Valentina Kiryushkina Date: Thu, 15 Dec 2016 13:02:18 +0300 Subject: [PATCH 179/558] EDU-794 Disable stepik settings if no educational projects are opened --- .../edu/learning/stepic/StepicStudyOptions.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicStudyOptions.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicStudyOptions.java index 8f50b4b58914..607dcc6400d4 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicStudyOptions.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicStudyOptions.java @@ -39,6 +39,7 @@ public class StepicStudyOptions implements StudyOptionsProvider { private JPasswordField myPasswordField; private JPanel myPane; private JBCheckBox myEnableTestingFromSamples; + private final Project myProject; public StepicStudyOptions() { myLoginTextField.getDocument().addDocumentListener(new DocumentAdapter() { @@ -47,6 +48,12 @@ public class StepicStudyOptions implements StudyOptionsProvider { erasePassword(); } }); + myProject = StudyUtils.getStudyProject(); + if (myProject == null) { + myLoginTextField.setEnabled(false); + myPasswordField.setEnabled(false); + myEnableTestingFromSamples.setEnabled(false); + } } private void erasePassword() { @@ -102,9 +109,8 @@ public class StepicStudyOptions implements StudyOptionsProvider { @Override public void apply() throws ConfigurationException { - final Project project = StudyUtils.getStudyProject(); - if (project != null) { - StudyTaskManager taskManager = StudyTaskManager.getInstance(project); + if (myProject != null) { + StudyTaskManager taskManager = StudyTaskManager.getInstance(myProject); if (isTestingFromSamplesEnabled() != taskManager.isEnableTestingFromSamples()){ taskManager.setEnableTestingFromSamples(isTestingFromSamplesEnabled()); @@ -123,7 +129,7 @@ public class StepicStudyOptions implements StudyOptionsProvider { ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); stepicUser[0] = StudyUtils.execCancelable(() -> EduStepicAuthorizedClient.login(login, password)); }, "Logging In", true, - project); + myProject); if (stepicUser[0] != null && stepicUser[0].getAccessToken() != null) { taskManager.setUser(stepicUser[0]); From 50223c222e22b671812e4e4951154cb5b8f25e07 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Thu, 15 Dec 2016 17:11:57 +0700 Subject: [PATCH 180/558] Fix IDEA-165466 Stream API migration: automatically simplify emptyList().stream() to empty() SimplifyStreamApiCallChainsInspection refactoring: simplifyCollectionStreamCalls extracted; --- ...SimplifyStreamApiCallChainsInspection.java | 185 +++++++++--------- .../streamMigration/MigrateToStreamFix.java | 2 + .../streamApiMigration/afterFromAsList.java | 11 ++ .../afterFromEmptyList.java | 11 ++ .../streamApiMigration/beforeFromAsList.java | 13 ++ .../beforeFromEmptyList.java | 13 ++ 6 files changed, 143 insertions(+), 92 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFromAsList.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFromEmptyList.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFromAsList.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFromEmptyList.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java index 11430747f69d..f012f3eb8b5a 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java @@ -242,13 +242,6 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns } } - @Contract("null -> false") - private boolean isCollectionStream(PsiMethodCallExpression qualifierCall) { - if (qualifierCall == null) return false; - PsiMethod qualifier = qualifierCall.resolveMethod(); - return isCallOf(qualifier, CommonClassNames.JAVA_UTIL_COLLECTION, STREAM_METHOD, 0); - } - private void handleStreamForEach(PsiMethodCallExpression methodCall, PsiMethod method) { final String name; if (isCallOf(method, CommonClassNames.JAVA_UTIL_STREAM_STREAM, FOR_EACH_METHOD, 1)) { @@ -325,34 +318,7 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns } private void handleCollectionStream(PsiMethodCallExpression methodCall) { - final PsiMethodCallExpression qualifierCall = getQualifierMethodCall(methodCall); - if (qualifierCall == null) return; - final PsiMethod qualifier = qualifierCall.resolveMethod(); - ReplaceCollectionStreamFix fix = null; - if (isCallOf(qualifier, CommonClassNames.JAVA_UTIL_ARRAYS, AS_LIST_METHOD, 1)) { - if (hasSingleArrayArgument(qualifierCall)) { - fix = new ArraysAsListSingleArrayFix(); - } - else { - fix = new ReplaceWithStreamOfFix("Arrays.asList()"); - } - } - else if (isCallOf(qualifier, CommonClassNames.JAVA_UTIL_COLLECTIONS, SINGLETON_LIST_METHOD, 1)) { - if (!hasSingleArrayArgument(qualifierCall)) { - fix = new ReplaceSingletonWithStreamOfFix("Collections.singletonList()"); - } - } - else if (isCallOf(qualifier, CommonClassNames.JAVA_UTIL_COLLECTIONS, SINGLETON_METHOD, 1)) { - if (!hasSingleArrayArgument(qualifierCall)) { - fix = new ReplaceSingletonWithStreamOfFix("Collections.singleton()"); - } - } - else if (isCallOf(qualifier, CommonClassNames.JAVA_UTIL_COLLECTIONS, EMPTY_LIST_METHOD, 0)) { - fix = new ReplaceWithStreamEmptyFix(EMPTY_LIST_METHOD); - } - else if (isCallOf(qualifier, CommonClassNames.JAVA_UTIL_COLLECTIONS, EMPTY_SET_METHOD, 0)) { - fix = new ReplaceWithStreamEmptyFix(EMPTY_SET_METHOD); - } + ReplaceCollectionStreamFix fix = findCollectionStreamFix(methodCall); if (fix != null) { holder.registerProblem(methodCall, null, fix.getMessage(), new SimplifyCallChainFix(fix)); } @@ -360,6 +326,64 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns }; } + @Nullable + private static ReplaceCollectionStreamFix findCollectionStreamFix(PsiMethodCallExpression methodCall) { + PsiMethodCallExpression qualifierCall = getQualifierMethodCall(methodCall); + if (qualifierCall == null) return null; + PsiMethod qualifier = qualifierCall.resolveMethod(); + if (isCallOf(qualifier, CommonClassNames.JAVA_UTIL_ARRAYS, AS_LIST_METHOD, 1)) { + return hasSingleArrayArgument(qualifierCall) ? new ArraysAsListSingleArrayFix() : new ReplaceWithStreamOfFix("Arrays.asList()"); + } + else if (isCallOf(qualifier, CommonClassNames.JAVA_UTIL_COLLECTIONS, SINGLETON_LIST_METHOD, 1)) { + if (!hasSingleArrayArgument(qualifierCall)) { + return new ReplaceSingletonWithStreamOfFix("Collections.singletonList()"); + } + } + else if (isCallOf(qualifier, CommonClassNames.JAVA_UTIL_COLLECTIONS, SINGLETON_METHOD, 1)) { + if (!hasSingleArrayArgument(qualifierCall)) { + return new ReplaceSingletonWithStreamOfFix("Collections.singleton()"); + } + } + else if (isCallOf(qualifier, CommonClassNames.JAVA_UTIL_COLLECTIONS, EMPTY_LIST_METHOD, 0)) { + return new ReplaceWithStreamEmptyFix(EMPTY_LIST_METHOD); + } + else if (isCallOf(qualifier, CommonClassNames.JAVA_UTIL_COLLECTIONS, EMPTY_SET_METHOD, 0)) { + return new ReplaceWithStreamEmptyFix(EMPTY_SET_METHOD); + } + return null; + } + + @Contract("null -> false") + private static boolean isCollectionStream(PsiMethodCallExpression qualifierCall) { + if (qualifierCall == null) return false; + PsiMethod qualifier = qualifierCall.resolveMethod(); + return isCallOf(qualifier, CommonClassNames.JAVA_UTIL_COLLECTION, STREAM_METHOD, 0); + } + + public static PsiElement simplifyCollectionStreamCalls(PsiElement element) { + boolean replaced = true; + while(replaced) { + replaced = false; + PsiElement[] streamCalls = + PsiTreeUtil.collectElements(element, e -> e instanceof PsiMethodCallExpression && isCollectionStream((PsiMethodCallExpression)e)); + for (PsiElement streamCall : streamCalls) { + if (streamCall.isValid()) { + ReplaceCollectionStreamFix fix = findCollectionStreamFix((PsiMethodCallExpression)streamCall); + if (fix != null) { + PsiElement replacement = fix.simplify((PsiMethodCallExpression)streamCall); + if(replacement != null) { + replaced = true; + if(element == streamCall) { + element = replacement; + } + } + } + } + } + } + return element; + } + @Nullable private static PsiArrayType getArrayType(PsiMethodCallExpression call) { PsiType type = call.getType(); @@ -521,7 +545,7 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns interface CallChainFix { String getName(); - void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor); + void applyFix(@NotNull Project project, PsiElement element); } private static class SimplifyCallChainFix implements LocalQuickFix { @@ -547,31 +571,11 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns @Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - myFix.applyFix(project, descriptor); + myFix.applyFix(project, descriptor.getStartElement()); } } - private static abstract class CallChainFixBase implements CallChainFix { - @Override - public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - final PsiElement element = descriptor.getStartElement(); - if (element instanceof PsiMethodCallExpression) { - final PsiMethodCallExpression expression = (PsiMethodCallExpression)element; - final PsiExpression forEachMethodQualifier = expression.getMethodExpression().getQualifierExpression(); - if (forEachMethodQualifier instanceof PsiMethodCallExpression) { - final PsiMethodCallExpression previousExpression = (PsiMethodCallExpression)forEachMethodQualifier; - final PsiExpression qualifierExpression = previousExpression.getMethodExpression().getQualifierExpression(); - replaceMethodCall(expression, previousExpression, qualifierExpression); - } - } - } - - protected abstract void replaceMethodCall(@NotNull PsiMethodCallExpression methodCall, - @NotNull PsiMethodCallExpression qualifierCall, - @Nullable PsiExpression qualifierExpression); - } - - private static abstract class ReplaceCollectionStreamFix extends CallChainFixBase { + private static abstract class ReplaceCollectionStreamFix implements CallChainFix { private final String myClassName; private final String myMethodName; private final String myQualifierCall; @@ -601,13 +605,17 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns } @Override - protected void replaceMethodCall(@NotNull PsiMethodCallExpression methodCall, - @NotNull PsiMethodCallExpression qualifierCall, - @Nullable PsiExpression qualifierExpression) { - methodCall.getArgumentList().replace(qualifierCall.getArgumentList()); + public void applyFix(@NotNull Project project, PsiElement element) { + if (!(element instanceof PsiMethodCallExpression)) return; + simplify((PsiMethodCallExpression)element); + } - final Project project = methodCall.getProject(); - String typeParameter = getTypeParameter(qualifierCall); + @Nullable + private PsiElement simplify(PsiMethodCallExpression streamCall) { + PsiMethodCallExpression collectionCall = getQualifierMethodCall(streamCall); + if (collectionCall == null) return null; + streamCall.getArgumentList().replace(collectionCall.getArgumentList()); + String typeParameter = getTypeParameter(collectionCall); String replacement; if (typeParameter != null) { replacement = myClassName + ".<" + typeParameter + ">" + myMethodName; @@ -615,8 +623,9 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns else { replacement = myClassName + "." + myMethodName; } - final PsiExpression newMethodExpression = JavaPsiFacade.getElementFactory(project).createExpressionFromText(replacement, methodCall); - JavaCodeStyleManager.getInstance(project).shortenClassReferences(methodCall.getMethodExpression().replace(newMethodExpression)); + Project project = streamCall.getProject(); + PsiExpression newMethodExpression = JavaPsiFacade.getElementFactory(project).createExpressionFromText(replacement, streamCall); + return JavaCodeStyleManager.getInstance(project).shortenClassReferences(streamCall.getMethodExpression().replace(newMethodExpression)); } } @@ -661,7 +670,7 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns } } - static class ReplaceStreamMethodFix extends CallChainFixBase { + static class ReplaceStreamMethodFix implements CallChainFix { private final String myStreamMethod; private final String myCollectionMethod; private final boolean myChangeSemantics; @@ -689,19 +698,16 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns } @Override - protected void replaceMethodCall(@NotNull PsiMethodCallExpression methodCall, - @NotNull PsiMethodCallExpression qualifierCall, - @Nullable PsiExpression qualifierExpression) { - if (qualifierExpression != null) { - final PsiElement nameElement = methodCall.getMethodExpression().getReferenceNameElement(); - if (nameElement != null) { - qualifierCall.replace(qualifierExpression); - if (!myStreamMethod.equals(myCollectionMethod)) { - final Project project = methodCall.getProject(); - PsiIdentifier collectionMethod = JavaPsiFacade.getElementFactory(project).createIdentifier(myCollectionMethod); - nameElement.replace(collectionMethod); - } - } + public void applyFix(@NotNull Project project, PsiElement element) { + if (!(element instanceof PsiMethodCallExpression)) return; + PsiMethodCallExpression streamMethodCall = (PsiMethodCallExpression)element; + PsiMethodCallExpression collectionStreamCall = getQualifierMethodCall(streamMethodCall); + if (collectionStreamCall == null) return; + PsiExpression collectionExpression = collectionStreamCall.getMethodExpression().getQualifierExpression(); + if (collectionExpression == null) return; + collectionStreamCall.replace(collectionExpression); + if (!myStreamMethod.equals(myCollectionMethod)) { + streamMethodCall.getMethodExpression().handleElementRename(myCollectionMethod); } } } @@ -729,8 +735,7 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns } @Override - public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - PsiElement element = descriptor.getStartElement(); + public void applyFix(@NotNull Project project, PsiElement element) { if (element instanceof PsiMethodCallExpression) { PsiMethodCallExpression collectCall = (PsiMethodCallExpression)element; PsiExpression qualifierExpression = collectCall.getMethodExpression().getQualifierExpression(); @@ -796,8 +801,7 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns } @Override - public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - PsiElement element = descriptor.getStartElement(); + public void applyFix(@NotNull Project project, PsiElement element) { if (element instanceof PsiMethodCallExpression) { PsiMethodCallExpression isPresentCall = (PsiMethodCallExpression)element; PsiExpression isPresentQualifier = isPresentCall.getMethodExpression().getQualifierExpression(); @@ -840,8 +844,7 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns } @Override - public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - PsiElement element = descriptor.getStartElement(); + public void applyFix(@NotNull Project project, PsiElement element) { if(element instanceof PsiIdentifier) { String from = element.getText(); boolean removeParentNegation; @@ -895,8 +898,7 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns } @Override - public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - PsiElement element = descriptor.getStartElement(); + public void applyFix(@NotNull Project project, PsiElement element) { if(!(element instanceof PsiMethodCallExpression)) return; PsiMethodCallExpression collectCall = (PsiMethodCallExpression)element; PsiType type = collectCall.getType(); @@ -934,8 +936,7 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns } @Override - public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - PsiElement element = descriptor.getStartElement(); + public void applyFix(@NotNull Project project, PsiElement element) { if(!(element instanceof PsiIdentifier)) return; PsiElement parent = element.getParent(); if(!(parent instanceof PsiReferenceExpression)) return; @@ -963,8 +964,8 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns } @Override - public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - PsiMethodCallExpression toArrayCall = PsiTreeUtil.getParentOfType(descriptor.getStartElement(), PsiMethodCallExpression.class); + public void applyFix(@NotNull Project project, PsiElement element) { + PsiMethodCallExpression toArrayCall = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class); if (toArrayCall == null) return; PsiExpression qualifier = toArrayCall.getMethodExpression().getQualifierExpression(); if(!(qualifier instanceof PsiMethodCallExpression)) return; diff --git a/java/java-impl/src/com/intellij/codeInspection/streamMigration/MigrateToStreamFix.java b/java/java-impl/src/com/intellij/codeInspection/streamMigration/MigrateToStreamFix.java index 629e4a319b9d..b63c976f4d43 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamMigration/MigrateToStreamFix.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamMigration/MigrateToStreamFix.java @@ -18,6 +18,7 @@ package com.intellij.codeInspection.streamMigration; import com.intellij.codeInspection.LambdaCanBeMethodReferenceInspection; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.codeInspection.SimplifyStreamApiCallChainsInspection; import com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.*; import com.intellij.openapi.project.Project; import com.intellij.psi.*; @@ -109,6 +110,7 @@ abstract class MigrateToStreamFix implements LocalQuickFix { if (result == null) return; LambdaCanBeMethodReferenceInspection.replaceAllLambdasWithMethodReferences(result); PsiDiamondTypeUtil.removeRedundantTypeArguments(result); + result = SimplifyStreamApiCallChainsInspection.simplifyCollectionStreamCalls(result); CodeStyleManager.getInstance(project).reformat(JavaCodeStyleManager.getInstance(project).shortenClassReferences(result)); } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFromAsList.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFromAsList.java new file mode 100644 index 000000000000..8053495e25a1 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFromAsList.java @@ -0,0 +1,11 @@ +// "Replace with collect" "true" + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class Main { + private void collect() { + Set res = Stream.of("a", "b", "c").filter(s -> !s.isEmpty()).collect(Collectors.toSet()); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFromEmptyList.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFromEmptyList.java new file mode 100644 index 000000000000..774c9ed41217 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFromEmptyList.java @@ -0,0 +1,11 @@ +// "Replace with collect" "true" + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class Main { + private void collect() { + Set res = Stream.empty().filter(s -> !s.isEmpty()).collect(Collectors.toSet()); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFromAsList.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFromAsList.java new file mode 100644 index 000000000000..a6258eb8d94a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFromAsList.java @@ -0,0 +1,13 @@ +// "Replace with collect" "true" + +import java.util.*; + +public class Main { + private void collect() { + Set res = new HashSet<>(); + for (String s : Arrays.asList("a", "b", "c")) { + if (!s.isEmpty()) + res.add(s); + } + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFromEmptyList.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFromEmptyList.java new file mode 100644 index 000000000000..5c599f6cdb56 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFromEmptyList.java @@ -0,0 +1,13 @@ +// "Replace with collect" "true" + +import java.util.*; + +public class Main { + private void collect() { + Set res = new HashSet<>(); + for (String s : Collections.emptyList()) { + if (!s.isEmpty()) + res.add(s); + } + } +} From 160e199d9f45aefd0ec12142acb87b3b9554be91 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Thu, 8 Dec 2016 15:21:17 +0300 Subject: [PATCH 181/558] extract base class and create disposable tree --- .../com/intellij/ui/tree/AsyncTreeModel.java | 26 ++++++---- .../src/com/intellij/ui/tree/Command.java | 31 +++++++----- .../intellij/ui/tree/InvokableTreeModel.java | 49 +++++++++++++++++++ .../src/com/intellij/ui/tree/Invoker.java | 23 ++++----- 4 files changed, 96 insertions(+), 33 deletions(-) create mode 100644 platform/platform-impl/src/com/intellij/ui/tree/InvokableTreeModel.java diff --git a/platform/platform-impl/src/com/intellij/ui/tree/AsyncTreeModel.java b/platform/platform-impl/src/com/intellij/ui/tree/AsyncTreeModel.java index 8d5f5f82eaee..bc67f3c7a525 100644 --- a/platform/platform-impl/src/com/intellij/ui/tree/AsyncTreeModel.java +++ b/platform/platform-impl/src/com/intellij/ui/tree/AsyncTreeModel.java @@ -17,7 +17,7 @@ package com.intellij.ui.tree; import com.intellij.openapi.Disposable; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.util.ui.tree.AbstractTreeModel; +import com.intellij.openapi.util.Disposer; import com.intellij.util.ui.tree.TreeModelAdapter; import org.jetbrains.annotations.NotNull; @@ -32,9 +32,9 @@ import java.util.IdentityHashMap; /** * @author Sergey.Malenkov */ -public class AsyncTreeModel extends AbstractTreeModel implements Disposable { +public class AsyncTreeModel extends InvokableTreeModel { private static final Logger LOG = Logger.getInstance(AsyncTreeModel.class); - private final Command.Processor processor = new Command.Processor(createForegroundInvoker(), createBackgroundInvoker()); + private final Command.Processor processor; private final Tree tree = new Tree(); private final TreeModel model; private final TreeModelListener listener = new TreeModelAdapter() { @@ -74,6 +74,16 @@ public class AsyncTreeModel extends AbstractTreeModel implements Disposable { }; public AsyncTreeModel(TreeModel model) { + if (model instanceof Disposable) { + Disposer.register(this, (Disposable)model); + } + if (model instanceof InvokableTreeModel) { + InvokableTreeModel invokable = (InvokableTreeModel)model; + processor = new Command.Processor(super.invoker, invokable.invoker); + } + else { + processor = new Command.Processor(super.invoker, new Invoker.BackgroundQueue(this)); + } this.model = model; this.model.addTreeModelListener(listener); } @@ -121,13 +131,9 @@ public class AsyncTreeModel extends AbstractTreeModel implements Disposable { } @NotNull - protected Invoker createForegroundInvoker() { - return new Invoker.EDT(getClass().getName()); - } - - @NotNull - protected Invoker createBackgroundInvoker() { - return new Invoker.BackgroundQueue(getClass().getName()); + @Override + protected Invoker createInvoker() { + return new Invoker.EDT(this); } protected Object createLoadingNode() { diff --git a/platform/platform-impl/src/com/intellij/ui/tree/Command.java b/platform/platform-impl/src/com/intellij/ui/tree/Command.java index 2fde7945b836..7538bd57537e 100644 --- a/platform/platform-impl/src/com/intellij/ui/tree/Command.java +++ b/platform/platform-impl/src/com/intellij/ui/tree/Command.java @@ -15,9 +15,6 @@ */ package com.intellij.ui.tree; -import com.intellij.openapi.Disposable; -import com.intellij.openapi.util.Disposer; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import java.util.function.Consumer; @@ -27,27 +24,34 @@ import java.util.function.Supplier; * @author Sergey.Malenkov */ interface Command extends Supplier, Consumer { - final class Processor implements Disposable { + final class Processor { public final Invoker foreground; public final Invoker background; - public Processor(@NotNull @NonNls String name, boolean queueInBackground) { - this(new Invoker.EDT(name), queueInBackground ? new Invoker.BackgroundQueue(name) : new Invoker.Background(name)); - } - public Processor(@NotNull Invoker foreground, @NotNull Invoker background) { this.foreground = foreground; this.background = background; } + /** + * Lets the specified consumer to accept the given value on the foreground thread. + */ public void consume(Consumer consumer, T value) { if (consumer != null) foreground.invokeLaterIfNeeded(() -> consumer.accept(value)); } + /** + * Lets the specified command to produce a value on the background thread + * and to accept this value on the foreground thread. + */ public void process(Command command) { if (command != null) background.invokeLaterIfNeeded(() -> consume(command, command.get())); } + /** + * Lets the specified supplier to produce a value on the background thread + * and the specified consumer to accept this value on the foreground thread. + */ public void process(Supplier supplier, Consumer consumer) { if (supplier != null) { background.invokeLaterIfNeeded(() -> consume(consumer, supplier.get())); @@ -57,10 +61,13 @@ interface Command extends Supplier, Consumer { } } - @Override - public void dispose() { - Disposer.dispose(foreground); - Disposer.dispose(background); + /** + * Returns a workload of both task queues. + * + * @return amount of tasks, which are executing or waiting for execution + */ + public int getTaskCount() { + return foreground.getTaskCount() + background.getTaskCount(); } } } diff --git a/platform/platform-impl/src/com/intellij/ui/tree/InvokableTreeModel.java b/platform/platform-impl/src/com/intellij/ui/tree/InvokableTreeModel.java new file mode 100644 index 000000000000..ad9e76f2f3b2 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/tree/InvokableTreeModel.java @@ -0,0 +1,49 @@ +/* + * Copyright 2000-2016 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.ui.tree; + +import com.intellij.openapi.Disposable; +import com.intellij.util.ui.tree.AbstractTreeModel; +import org.jetbrains.annotations.NotNull; + +import javax.swing.tree.TreePath; + +/** + * @author Sergey.Malenkov + */ +public abstract class InvokableTreeModel extends AbstractTreeModel implements Disposable { + protected final Invoker invoker = createInvoker(); + + /** + * Creates a new invoker to process tree commands. + * By default, these commands are processed + * on a single background thread one after another. + * + * @return a new invoker for current tree model + */ + @NotNull + protected Invoker createInvoker() { + return new Invoker.BackgroundQueue(this); + } + + @Override + public void dispose() { + } + + @Override + public void valueForPathChanged(TreePath path, Object newValue) { + } +} diff --git a/platform/platform-impl/src/com/intellij/ui/tree/Invoker.java b/platform/platform-impl/src/com/intellij/ui/tree/Invoker.java index 865574de9ddb..b2045877b2ca 100644 --- a/platform/platform-impl/src/com/intellij/ui/tree/Invoker.java +++ b/platform/platform-impl/src/com/intellij/ui/tree/Invoker.java @@ -19,10 +19,10 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Expirable; import com.intellij.util.concurrency.QueueProcessor; import com.intellij.util.containers.TransferToEDTQueue; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.ide.PooledThreadExecutor; @@ -39,8 +39,9 @@ abstract class Invoker implements Disposable { private final String description; volatile boolean disposed; - private Invoker(String prefix, String name) { - description = "Invoker." + prefix + ":" + name + " " + UID.getAndIncrement(); + private Invoker(String prefix, Disposable parent) { + description = UID.getAndIncrement() + ".Invoker." + prefix + ":" + parent.getClass().getName(); + Disposer.register(parent, this); } @Override @@ -136,12 +137,12 @@ abstract class Invoker implements Disposable { public static class EDT extends Invoker { private final TransferToEDTQueue queue; - public EDT(@NotNull @NonNls String name) { - this(name, 200); + public EDT(@NotNull Disposable parent) { + this(parent, 200); } - public EDT(@NotNull @NonNls String name, int maxUnitOfWorkThresholdMs) { - super("EDT", name); + public EDT(@NotNull Disposable parent, int maxUnitOfWorkThresholdMs) { + super("EDT", parent); queue = TransferToEDTQueue.createRunnableMerger(toString(), maxUnitOfWorkThresholdMs); } @@ -169,8 +170,8 @@ abstract class Invoker implements Disposable { * but requires a good synchronization. */ public static class Background extends Invoker { - public Background(@NotNull @NonNls String name) { - super("Background", name); + public Background(@NotNull Disposable parent) { + super("Background", parent); } @Override @@ -198,8 +199,8 @@ abstract class Invoker implements Disposable { public static class BackgroundQueue extends Invoker { private final QueueProcessor queue; - public BackgroundQueue(@NotNull @NonNls String name) { - super("Background", name); + public BackgroundQueue(@NotNull Disposable parent) { + super("Background.Queue", parent); queue = QueueProcessor.createRunnableQueueProcessor(); } From 8af1a26499d92b145e8a88a1df0cda64d5f8967c Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Thu, 15 Dec 2016 13:35:26 +0300 Subject: [PATCH 182/558] IDEA-163336 FileTextField memory leak --- .../util/ui/update/LazyUiDisposable.java | 77 ++++++------------- 1 file changed, 24 insertions(+), 53 deletions(-) diff --git a/platform/platform-api/src/com/intellij/util/ui/update/LazyUiDisposable.java b/platform/platform-api/src/com/intellij/util/ui/update/LazyUiDisposable.java index b4cc5fe1e587..763362e05824 100644 --- a/platform/platform-api/src/com/intellij/util/ui/update/LazyUiDisposable.java +++ b/platform/platform-api/src/com/intellij/util/ui/update/LazyUiDisposable.java @@ -17,27 +17,25 @@ package com.intellij.util.ui.update; import com.intellij.ide.DataManager; import com.intellij.openapi.Disposable; -import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.actionSystem.DataKey; -import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; -import com.intellij.util.Consumer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.concurrency.AsyncPromise; -import org.jetbrains.concurrency.Promise; -import javax.swing.*; +import javax.swing.JComponent; +import java.util.concurrent.atomic.AtomicReference; + +import static com.intellij.openapi.actionSystem.CommonDataKeys.PROJECT; +import static com.intellij.openapi.actionSystem.PlatformDataKeys.UI_DISPOSABLE; public abstract class LazyUiDisposable implements Activatable { private Throwable myAllocation; - private boolean myWasEverShown; - + private final AtomicReference myUI; private final Disposable myParent; private final T myChild; @@ -46,6 +44,7 @@ public abstract class LazyUiDisposable implements Activata myAllocation = new Exception(); } + myUI = new AtomicReference<>(ui); myParent = parent; myChild = child; @@ -53,57 +52,29 @@ public abstract class LazyUiDisposable implements Activata } public final void showNotify() { - if (myWasEverShown) return; + JComponent ui = myUI.getAndSet(null); + if (ui == null) return; - try { - findParentDisposable() - .done(parent -> { - Project project = null; - if (ApplicationManager.getApplication() != null) { - project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext()); - } - initialize(parent, myChild, project); - Disposer.register(parent, myChild); - }); + Project project = null; + Disposable parent = myParent; + + if (ApplicationManager.getApplication() != null) { + DataContext context = DataManager.getInstance().getDataContext(ui); + project = PROJECT.getData(context); + if (parent == null) { + parent = UI_DISPOSABLE.getData(context); + } } - finally { - myWasEverShown = true; + if (parent == null) { + Logger.getInstance(LazyUiDisposable.class).warn("use application as a parent disposable"); + parent = Disposer.get("ui"); } + initialize(parent, myChild, project); + Disposer.register(parent, myChild); } public final void hideNotify() { } protected abstract void initialize(@NotNull Disposable parent, @NotNull T child, @Nullable Project project); - - @NotNull - private Promise findParentDisposable() { - return findDisposable(myParent, PlatformDataKeys.UI_DISPOSABLE); - } - - private static Promise findDisposable(Disposable defaultValue, final DataKey key) { - if (defaultValue == null) { - if (ApplicationManager.getApplication() != null) { - final AsyncPromise result = new AsyncPromise<>(); - DataManager.getInstance().getDataContextFromFocus() - .doWhenDone(new Consumer() { - @Override - public void consume(DataContext context) { - Disposable disposable = key.getData(context); - if (disposable == null) { - disposable = Disposer.get("ui"); - } - result.setResult(disposable); - } - }); - return result; - } - else { - return null; - } - } - else { - return Promise.resolve(defaultValue); - } - } } \ No newline at end of file From bd5bb360d694b3af672eb2bb91cab5ca46c8df9f Mon Sep 17 00:00:00 2001 From: "Vassiliy.Kudryashov" Date: Thu, 15 Dec 2016 14:14:12 +0300 Subject: [PATCH 183/558] Restart Run Profile: check if project is not disposed --- .../src/com/intellij/execution/impl/ExecutionManagerImpl.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java b/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java index 98afea62de05..b642ec816a9c 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java @@ -114,6 +114,9 @@ public class ExecutionManagerImpl extends ExecutionManager implements Disposable } private static void start(@NotNull ExecutionEnvironment environment) { + //start() can be called during restartRunProfile() after pretty long 'awaitTermination()' so we have to check if the project is still here + if (environment.getProject().isDisposed()) return; + RunnerAndConfigurationSettings settings = environment.getRunnerAndConfigurationSettings(); ProgramRunnerUtil.executeConfiguration(environment, settings != null && settings.isEditBeforeRun(), true); } From 8a0acd09fa844131632fe785966285094f36a493 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Thu, 15 Dec 2016 11:38:12 +0100 Subject: [PATCH 184/558] =?UTF-8?q?speed=20up=20save=20=E2=80=94=20track?= =?UTF-8?q?=20keymap=20scheme=20state?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/SchemeManagerImpl.kt | 10 +++--- .../colors/impl/AbstractColorsScheme.java | 17 +++++----- .../psi/codeStyle/CodeStyleScheme.java | 9 +++-- .../source/codeStyle/CodeStyleSchemeImpl.java | 7 ++++ .../codeStyle/CodeStyleSchemesImpl.java | 12 ------- .../colors/impl/EditorColorsManagerImpl.java | 9 +---- .../openapi/keymap/impl/DefaultKeymapImpl.kt | 3 ++ .../openapi/keymap/impl/KeymapImpl.kt | 10 ++++++ .../openapi/keymap/impl/KeymapManagerImpl.kt | 4 --- .../SerializableScheme.java | 34 +++++++++++++++++++ .../configurationStore/scheme-impl.kt | 8 ++--- .../com/intellij/openapi/options/scheme.kt | 6 +++- plugins/copyright/src/CopyrightManager.kt | 2 -- 13 files changed, 81 insertions(+), 50 deletions(-) create mode 100644 platform/projectModel-impl/src/com/intellij/configurationStore/SerializableScheme.java diff --git a/platform/configuration-store-impl/src/SchemeManagerImpl.kt b/platform/configuration-store-impl/src/SchemeManagerImpl.kt index b8029348d197..82d8c1d787a0 100644 --- a/platform/configuration-store-impl/src/SchemeManagerImpl.kt +++ b/platform/configuration-store-impl/src/SchemeManagerImpl.kt @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -499,9 +499,9 @@ class SchemeManagerImpl(val fileSpec: String, var hasSchemes = false val nameGenerator = UniqueNameGenerator() - val schemesToSave = SmartList() + val changedSchemes = SmartList() for (scheme in schemes) { - val state = processor.getState(scheme) + val state = (scheme as? SerializableScheme)?.schemeState ?: processor.getState(scheme) if (state == SchemeState.NON_PERSISTENT) { continue } @@ -510,7 +510,7 @@ class SchemeManagerImpl(val fileSpec: String, if (state != SchemeState.UNCHANGED) { @Suppress("UNCHECKED_CAST") - schemesToSave.add(scheme as MUTABLE_SCHEME) + changedSchemes.add(scheme as MUTABLE_SCHEME) } val fileName = scheme.fileName @@ -519,7 +519,7 @@ class SchemeManagerImpl(val fileSpec: String, } } - for (scheme in schemesToSave) { + for (scheme in changedSchemes) { try { saveScheme(scheme, nameGenerator) } diff --git a/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java b/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java index 660e470b7d48..eb97b8116e5f 100644 --- a/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java +++ b/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java @@ -28,11 +28,8 @@ import com.intellij.openapi.editor.markup.EffectType; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.options.FontSize; import com.intellij.openapi.options.SchemeManager; -import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.Couple; -import com.intellij.openapi.util.InvalidDataException; -import com.intellij.openapi.util.Ref; -import com.intellij.openapi.util.WriteExternalException; +import com.intellij.openapi.options.SchemeState; +import com.intellij.openapi.util.*; import com.intellij.util.JdomKt; import com.intellij.util.PlatformUtils; import com.intellij.util.containers.ContainerUtilRt; @@ -831,12 +828,14 @@ public abstract class AbstractColorsScheme implements EditorColorsScheme, Serial return myParentScheme instanceof AbstractColorsScheme ? ((AbstractColorsScheme)myParentScheme).getDirectlyDefinedAttributes(key) : null; } - public boolean isSaveNeeded() { - return myIsSaveNeeded; + @NotNull + @Override + public SchemeState getSchemeState() { + return myIsSaveNeeded ? SchemeState.POSSIBLY_CHANGED : SchemeState.UNCHANGED; } - public void setSaveNeeded(boolean isSaveNeeded) { - myIsSaveNeeded = isSaveNeeded; + public void setSaveNeeded(boolean value) { + myIsSaveNeeded = value; } public boolean isReadOnly() { return false; } diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleScheme.java b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleScheme.java index 5863e96644d6..c44ca033b6b9 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleScheme.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleScheme.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 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,9 +22,12 @@ public interface CodeStyleScheme extends Scheme { @Override @NotNull String getName(); + boolean isDefault(); - default void resetToDefaults() {} - + + default void resetToDefaults() { + } + @NotNull CodeStyleSettings getCodeStyleSettings(); } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemeImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemeImpl.java index ae0c2aa9951f..4b8bb1b17451 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemeImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemeImpl.java @@ -19,6 +19,7 @@ import com.intellij.configurationStore.SchemeDataHolder; import com.intellij.configurationStore.SerializableScheme; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.options.ExternalizableSchemeAdapter; +import com.intellij.openapi.options.SchemeState; import com.intellij.openapi.util.InvalidDataException; import com.intellij.psi.codeStyle.CodeStyleScheme; import com.intellij.psi.codeStyle.CodeStyleSettings; @@ -104,6 +105,12 @@ public class CodeStyleSchemeImpl extends ExternalizableSchemeAdapter implements myCodeStyleSettings = new CodeStyleSettings(); } + @Nullable + @Override + public SchemeState getSchemeState() { + return isInitialized() ? SchemeState.POSSIBLY_CHANGED : SchemeState.UNCHANGED; + } + @Override @NotNull public Element writeScheme() { diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemesImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemesImpl.java index 9fac75081dc1..d0a42646ebb7 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemesImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemesImpl.java @@ -19,7 +19,6 @@ import com.intellij.configurationStore.LazySchemeProcessor; import com.intellij.configurationStore.SchemeDataHolder; import com.intellij.openapi.options.SchemeManager; import com.intellij.openapi.options.SchemeManagerFactory; -import com.intellij.openapi.options.SchemeState; import com.intellij.psi.codeStyle.CodeStyleScheme; import com.intellij.psi.codeStyle.CodeStyleSchemes; import org.jetbrains.annotations.NonNls; @@ -45,17 +44,6 @@ public abstract class CodeStyleSchemesImpl extends CodeStyleSchemes { boolean isBundled) { return new CodeStyleSchemeImpl(attributeProvider.apply("name"), attributeProvider.apply("parent"), dataHolder); } - - @NotNull - @Override - public SchemeState getState(@NotNull CodeStyleScheme scheme) { - if (!(scheme instanceof CodeStyleSchemeImpl)) { - return SchemeState.NON_PERSISTENT; - } - else { - return ((CodeStyleSchemeImpl)scheme).isInitialized() ? SchemeState.POSSIBLY_CHANGED : SchemeState.UNCHANGED; - } - } }); mySchemeManager.loadSchemes(); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java index eb282b581cd5..7faba683c61c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java @@ -65,8 +65,6 @@ public class EditorColorsManagerImpl extends EditorColorsManager implements Pers private static final Logger LOG = Logger.getInstance(EditorColorsManagerImpl.class); private static final ExtensionPointName BUNDLED_EP_NAME = ExtensionPointName.create("com.intellij.bundledColorScheme"); - private static final String DEFAULT_NAME = "Default"; - private final ComponentTreeEventDispatcher myTreeDispatcher = ComponentTreeEventDispatcher.create(EditorColorsListener.class); private final DefaultColorSchemesManager myDefaultColorSchemeManager; @@ -100,12 +98,7 @@ public class EditorColorsManagerImpl extends EditorColorsManager implements Pers @NotNull @Override public SchemeState getState(@NotNull EditorColorsScheme scheme) { - if (scheme instanceof EditorColorsSchemeImpl && !(scheme instanceof ReadOnlyColorsScheme)) { - return ((EditorColorsSchemeImpl)scheme).isSaveNeeded() ? SchemeState.POSSIBLY_CHANGED : SchemeState.UNCHANGED; - } - else { - return SchemeState.NON_PERSISTENT; - } + return scheme instanceof ReadOnlyColorsScheme ? SchemeState.NON_PERSISTENT : SchemeState.POSSIBLY_CHANGED; } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/DefaultKeymapImpl.kt b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/DefaultKeymapImpl.kt index 83467b71fe3e..68402bcb1bf5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/DefaultKeymapImpl.kt +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/DefaultKeymapImpl.kt @@ -19,6 +19,7 @@ import com.intellij.configurationStore.SchemeDataHolder import com.intellij.openapi.actionSystem.IdeActions import com.intellij.openapi.actionSystem.MouseShortcut import com.intellij.openapi.keymap.KeymapManager +import com.intellij.openapi.options.SchemeState import com.intellij.openapi.util.SystemInfo import org.jdom.Element import java.awt.event.MouseEvent @@ -30,6 +31,8 @@ open class DefaultKeymapImpl(dataHolder: SchemeDataHolder) : KeymapI // ignore } + override fun getSchemeState() = SchemeState.NON_PERSISTENT + override fun getPresentableName(): String = DefaultKeymap.instance.getKeymapPresentableName(this) override fun readExternal(keymapElement: Element) { diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapImpl.kt b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapImpl.kt index e0af0ffce24a..07bd78eccbab 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapImpl.kt +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapImpl.kt @@ -25,6 +25,7 @@ import com.intellij.openapi.keymap.Keymap import com.intellij.openapi.keymap.KeymapUtil import com.intellij.openapi.keymap.ex.KeymapManagerEx import com.intellij.openapi.options.ExternalizableSchemeAdapter +import com.intellij.openapi.options.SchemeState import com.intellij.openapi.util.InvalidDataException import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.util.registry.Registry @@ -61,6 +62,7 @@ private val LOG = logger() fun KeymapImpl(name: String, dataHolder: SchemeDataHolder): KeymapImpl { val result = KeymapImpl(dataHolder) result.name = name + result.schemeState = SchemeState.UNCHANGED return result } @@ -68,6 +70,11 @@ open class KeymapImpl @JvmOverloads constructor(private var dataHolder: SchemeDa private var parent: KeymapImpl? = null open var canModify = true + @JvmField + internal var schemeState: SchemeState? = null + + override fun getSchemeState() = schemeState + private val actionIdToShortcuts = THashMap>() get() { val dataHolder = dataHolder @@ -218,6 +225,7 @@ open class KeymapImpl @JvmOverloads constructor(private var dataHolder: SchemeDa private fun cleanShortcutsCache() { _keystrokeToIds = null _mouseShortcutToListOfIds = null + schemeState = SchemeState.POSSIBLY_CHANGED } override fun removeAllActionShortcuts(actionId: String) { @@ -587,6 +595,8 @@ open class KeymapImpl @JvmOverloads constructor(private var dataHolder: SchemeDa keymapElement.setAttribute(PARENT_ATTRIBUTE, it.name) } writeOwnActionIds(keymapElement) + + schemeState = SchemeState.UNCHANGED return keymapElement } diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapManagerImpl.kt b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapManagerImpl.kt index 38ac74e31915..5a98a1f290ac 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapManagerImpl.kt +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapManagerImpl.kt @@ -28,7 +28,6 @@ import com.intellij.openapi.keymap.KeymapManagerListener import com.intellij.openapi.keymap.ex.KeymapManagerEx import com.intellij.openapi.options.SchemeManager import com.intellij.openapi.options.SchemeManagerFactory -import com.intellij.openapi.options.SchemeState import com.intellij.openapi.util.Condition import com.intellij.openapi.util.Conditions import com.intellij.openapi.util.Disposer @@ -55,9 +54,6 @@ class KeymapManagerImpl(defaultKeymap: DefaultKeymap, factory: SchemeManagerFact name: String, attributeProvider: Function, isBundled: Boolean) = KeymapImpl(name, dataHolder) - - override fun getState(scheme: Keymap) = if (scheme.canModify()) SchemeState.POSSIBLY_CHANGED else SchemeState.NON_PERSISTENT - override fun onCurrentSchemeSwitched(oldScheme: Keymap?, newScheme: Keymap?) { for (listener in listeners) { listener.activeKeymapChanged(newScheme) diff --git a/platform/projectModel-impl/src/com/intellij/configurationStore/SerializableScheme.java b/platform/projectModel-impl/src/com/intellij/configurationStore/SerializableScheme.java new file mode 100644 index 000000000000..5398d3a7a6ba --- /dev/null +++ b/platform/projectModel-impl/src/com/intellij/configurationStore/SerializableScheme.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2016 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.configurationStore; + +import com.intellij.openapi.options.SchemeState; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface SerializableScheme { + @NotNull + Element writeScheme(); + + /** + * Null if unsure. + */ + @Nullable + default SchemeState getSchemeState() { + return null; + } +} diff --git a/platform/projectModel-impl/src/com/intellij/configurationStore/scheme-impl.kt b/platform/projectModel-impl/src/com/intellij/configurationStore/scheme-impl.kt index 50044ee63459..0215c7731626 100644 --- a/platform/projectModel-impl/src/com/intellij/configurationStore/scheme-impl.kt +++ b/platform/projectModel-impl/src/com/intellij/configurationStore/scheme-impl.kt @@ -38,10 +38,6 @@ interface SchemeDataHolder { fun updateDigest(data: Element) } -interface SerializableScheme { - fun writeScheme(): Element -} - /** * A scheme processor can implement this interface to provide a file extension different from default .xml. * @see SchemeProcessor @@ -99,8 +95,7 @@ abstract class SchemeWrapper(name: String) : ExternalizableSchem val scheme: T get() = lazyScheme.value - val schemeState: SchemeState - get() = if (lazyScheme.isInitialized()) SchemeState.POSSIBLY_CHANGED else SchemeState.UNCHANGED + override fun getSchemeState() = if (lazyScheme.isInitialized()) SchemeState.POSSIBLY_CHANGED else SchemeState.UNCHANGED init { this.name = name @@ -112,6 +107,7 @@ abstract class LazySchemeWrapper(name: String, dataHolder: SchemeDat override final fun writeScheme(): Element { val dataHolder = dataHolder.get() + @Suppress("IfThenToElvis") return if (dataHolder == null) writer(scheme) else dataHolder.read() } } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/options/scheme.kt b/platform/projectModel-impl/src/com/intellij/openapi/options/scheme.kt index 775a7f5c00a3..327c09a4d277 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/options/scheme.kt +++ b/platform/projectModel-impl/src/com/intellij/openapi/options/scheme.kt @@ -77,5 +77,9 @@ abstract class SchemeProcessor { open fun onCurrentSchemeSwitched(oldScheme: SCHEME?, newScheme: SCHEME?) { } - open fun getState(scheme: SCHEME): SchemeState = SchemeState.POSSIBLY_CHANGED + /** + * If scheme implements [com.intellij.configurationStore.SerializableScheme], this method will be called only if [com.intellij.configurationStore.SerializableScheme.getSchemeState] returns `null` + */ + @Suppress("KDocUnresolvedReference") + open fun getState(scheme: SCHEME) = SchemeState.POSSIBLY_CHANGED } \ No newline at end of file diff --git a/plugins/copyright/src/CopyrightManager.kt b/plugins/copyright/src/CopyrightManager.kt index 16e6d23ed650..fa7c89c346a0 100644 --- a/plugins/copyright/src/CopyrightManager.kt +++ b/plugins/copyright/src/CopyrightManager.kt @@ -94,8 +94,6 @@ class CopyrightManager(private val project: Project, schemeManagerFactory: Schem return CopyrightLazySchemeWrapper(name, dataHolder, schemeWriter) } - override fun getState(scheme: SchemeWrapper) = scheme.schemeState - override fun isSchemeFile(name: CharSequence) = !StringUtil.equals(name, "profiles_settings.xml") }, isUseOldFileNameSanitize = true, streamProvider = schemeManagerIprProvider) From a239c4888b93f81ae7c1a508b38b1f275c622c04 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Thu, 15 Dec 2016 11:47:33 +0100 Subject: [PATCH 185/558] =?UTF-8?q?speed=20up=20save=20=E2=80=94=20track?= =?UTF-8?q?=20quicklist=20scheme=20state?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../openapi/actionSystem/ex/QuickList.java | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/ex/QuickList.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/ex/QuickList.java index 465b3240606d..3a70215d8338 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/ex/QuickList.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/ex/QuickList.java @@ -17,6 +17,7 @@ package com.intellij.openapi.actionSystem.ex; import com.intellij.configurationStore.SerializableScheme; import com.intellij.openapi.options.ExternalizableSchemeAdapter; +import com.intellij.openapi.options.SchemeState; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.ArrayUtil; @@ -38,6 +39,7 @@ public class QuickList extends ExternalizableSchemeAdapter implements Serializab private String myDescription; private String[] myActionIds = ArrayUtil.EMPTY_STRING_ARRAY; + private SchemeState schemeState; /** * With read external to be called immediately after in mind @@ -59,6 +61,7 @@ public class QuickList extends ExternalizableSchemeAdapter implements Serializab public void setDescription(@Nullable String value) { myDescription = StringUtil.nullize(value); + schemeState = SchemeState.POSSIBLY_CHANGED; } public String[] getActionIds() { @@ -67,6 +70,7 @@ public class QuickList extends ExternalizableSchemeAdapter implements Serializab public void setActionIds(@NotNull String[] value) { myActionIds = value; + schemeState = SchemeState.POSSIBLY_CHANGED; } public boolean equals(Object o) { @@ -96,17 +100,6 @@ public class QuickList extends ExternalizableSchemeAdapter implements Serializab return QUICK_LIST_PREFIX + getName(); } - private void writeExternal(@NotNull Element groupElement) { - groupElement.setAttribute(DISPLAY_NAME_TAG, getName()); - if (myDescription != null) { - groupElement.setAttribute(DESCRIPTION_TAG, myDescription); - } - - for (String actionId : getActionIds()) { - groupElement.addContent(new Element(ACTION_TAG).setAttribute(ID_TAG, actionId)); - } - } - public void readExternal(@NotNull Element element) { setName(element.getAttributeValue(DISPLAY_NAME_TAG)); myDescription = StringUtil.nullize(element.getAttributeValue(DESCRIPTION_TAG)); @@ -122,7 +115,22 @@ public class QuickList extends ExternalizableSchemeAdapter implements Serializab @Override public Element writeScheme() { Element element = new Element("list"); - writeExternal(element); + element.setAttribute(DISPLAY_NAME_TAG, getName()); + if (myDescription != null) { + element.setAttribute(DESCRIPTION_TAG, myDescription); + } + + for (String actionId : getActionIds()) { + element.addContent(new Element(ACTION_TAG).setAttribute(ID_TAG, actionId)); + } + + schemeState = SchemeState.UNCHANGED; return element; } + + @Nullable + @Override + public SchemeState getSchemeState() { + return schemeState; + } } \ No newline at end of file From c261f8c81a2734de791adf52cc2b34dd7647abeb Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Thu, 15 Dec 2016 12:48:17 +0100 Subject: [PATCH 186/558] fix compilation --- .../intellij/openapi/editor/colors/EditorColorSchemeTest.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/colors/EditorColorSchemeTest.kt b/platform/platform-tests/testSrc/com/intellij/openapi/editor/colors/EditorColorSchemeTest.kt index e26459cd7c36..723782bb029b 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/editor/colors/EditorColorSchemeTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/colors/EditorColorSchemeTest.kt @@ -19,11 +19,11 @@ import com.intellij.configurationStore.SchemeManagerFactoryBase import com.intellij.openapi.editor.colors.ex.DefaultColorSchemesManager import com.intellij.openapi.editor.colors.impl.AbstractColorsScheme import com.intellij.openapi.editor.colors.impl.EditorColorsManagerImpl -import com.intellij.testFramework.rules.InMemoryFsRule import com.intellij.testFramework.ProjectRule +import com.intellij.testFramework.assertions.Assertions.assertThat +import com.intellij.testFramework.rules.InMemoryFsRule import com.intellij.util.io.readText import com.intellij.util.io.write -import com.intellij.testFramework.assertions.Assertions.assertThat import org.junit.ClassRule import org.junit.Rule import org.junit.Test @@ -54,7 +54,7 @@ class EditorColorSchemeTest { val scheme = manager.getScheme("Foo") assertThat(scheme.name).isEqualTo("Foo") - (scheme as AbstractColorsScheme).isSaveNeeded = true + (scheme as AbstractColorsScheme).setSaveNeeded(true) schemeManagerFactory.save() From 2a73607a9e41bc9f58a9c341415d5c16907a68b2 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Thu, 15 Dec 2016 14:50:17 +0300 Subject: [PATCH 187/558] IDEA-163336 use project disposable instead of application one if it exists --- .../com/intellij/util/ui/update/LazyUiDisposable.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/platform/platform-api/src/com/intellij/util/ui/update/LazyUiDisposable.java b/platform/platform-api/src/com/intellij/util/ui/update/LazyUiDisposable.java index 763362e05824..4cf1188d6b08 100644 --- a/platform/platform-api/src/com/intellij/util/ui/update/LazyUiDisposable.java +++ b/platform/platform-api/src/com/intellij/util/ui/update/LazyUiDisposable.java @@ -66,8 +66,14 @@ public abstract class LazyUiDisposable implements Activata } } if (parent == null) { - Logger.getInstance(LazyUiDisposable.class).warn("use application as a parent disposable"); - parent = Disposer.get("ui"); + if (project == null) { + Logger.getInstance(LazyUiDisposable.class).warn("use application as a parent disposable"); + parent = Disposer.get("ui"); + } + else { + Logger.getInstance(LazyUiDisposable.class).warn("use project as a parent disposable"); + parent = project; + } } initialize(parent, myChild, project); Disposer.register(parent, myChild); From f2cb34bef1e8ac6439613cb4856fcfedca8334b9 Mon Sep 17 00:00:00 2001 From: Dennis Ushakov Date: Wed, 14 Dec 2016 16:50:46 +0300 Subject: [PATCH 188/558] remove annoying EC notification --- plugins/editorconfig/src/META-INF/plugin.xml | 1 - .../EditorConfigNotifierProvider.java | 60 ------------------- 2 files changed, 61 deletions(-) delete mode 100644 plugins/editorconfig/src/org/editorconfig/plugincomponents/EditorConfigNotifierProvider.java diff --git a/plugins/editorconfig/src/META-INF/plugin.xml b/plugins/editorconfig/src/META-INF/plugin.xml index b194bdbcdd8a..b18981f9f2a0 100644 --- a/plugins/editorconfig/src/META-INF/plugin.xml +++ b/plugins/editorconfig/src/META-INF/plugin.xml @@ -15,7 +15,6 @@ - diff --git a/plugins/editorconfig/src/org/editorconfig/plugincomponents/EditorConfigNotifierProvider.java b/plugins/editorconfig/src/org/editorconfig/plugincomponents/EditorConfigNotifierProvider.java deleted file mode 100644 index 1d455ea1de85..000000000000 --- a/plugins/editorconfig/src/org/editorconfig/plugincomponents/EditorConfigNotifierProvider.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.editorconfig.plugincomponents; - -import com.intellij.ide.util.PropertiesComponent; -import com.intellij.openapi.fileEditor.FileEditor; -import com.intellij.openapi.fileEditor.TextEditor; -import com.intellij.openapi.project.DumbAware; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.codeStyle.CodeStyleSettings; -import com.intellij.psi.codeStyle.CodeStyleSettingsManager; -import com.intellij.ui.EditorNotificationPanel; -import com.intellij.ui.EditorNotifications; -import com.intellij.ui.LightColors; -import icons.EditorconfigIcons; -import org.editorconfig.Utils; -import org.editorconfig.core.EditorConfig; -import org.editorconfig.settings.EditorConfigSettings; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -/** - * @author Dennis.Ushakov - */ -public class EditorConfigNotifierProvider extends EditorNotifications.Provider implements DumbAware { - private static final Key KEY = Key.create("editor.config.notification.panel"); - private static final String EDITOR_CONFIG_ACCEPTED = "editor.config.accepted"; - - @NotNull - @Override - public Key getKey() { - return KEY; - } - - @Override - public EditorNotificationPanel createNotificationPanel(@NotNull final VirtualFile file, @NotNull final FileEditor fileEditor) { - if (!(fileEditor instanceof TextEditor)) return null; - final Project project = ((TextEditor)fileEditor).getEditor().getProject(); - final CodeStyleSettings settings = CodeStyleSettingsManager.getInstance(project).getCurrentSettings(); - if (!Utils.isEnabled(settings) || PropertiesComponent.getInstance(project).getBoolean(EDITOR_CONFIG_ACCEPTED)) return null; - - final List pairs = SettingsProviderComponent.getInstance().getOutPairs(project, Utils.getFilePath(project, file)); - if (!pairs.isEmpty()) { - final EditorNotificationPanel panel = new EditorNotificationPanel(LightColors.GREEN) - .text("EditorConfig is overriding Code Style settings for this file") - .icon(EditorconfigIcons.Editorconfig); - panel.createActionLabel("OK", () -> { - PropertiesComponent.getInstance(project).setValue(EDITOR_CONFIG_ACCEPTED, true); - EditorNotifications.getInstance(project).updateAllNotifications(); - }); - panel.createActionLabel("Disable EditorConfig support", () -> { - settings.getCustomSettings(EditorConfigSettings.class).ENABLED = false; - EditorNotifications.getInstance(project).updateAllNotifications(); - }); - return panel; - } - return null; - } -} From c9efea14bb37d3827ad6dd4ae3623fe2c666383d Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Thu, 15 Dec 2016 15:09:24 +0300 Subject: [PATCH 189/558] vcs: remove dead code --- .../diff/impl/patch/apply/ApplyPatchHunk.java | 149 ------------------ .../impl/patch/apply/GenericPatchApplier.java | 4 +- .../impl/patch/apply/SequenceDescriptor.java | 45 ------ 3 files changed, 1 insertion(+), 197 deletions(-) delete mode 100644 platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyPatchHunk.java delete mode 100644 platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/SequenceDescriptor.java diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyPatchHunk.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyPatchHunk.java deleted file mode 100644 index 55d589e0c276..000000000000 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyPatchHunk.java +++ /dev/null @@ -1,149 +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 com.intellij.openapi.diff.impl.patch.apply; - -import com.intellij.openapi.diff.impl.patch.ApplyPatchException; -import com.intellij.openapi.diff.impl.patch.ApplyPatchStatus; -import com.intellij.openapi.diff.impl.patch.PatchHunk; -import com.intellij.openapi.diff.impl.patch.PatchLine; - -import java.util.ArrayList; -import java.util.List; - -public class ApplyPatchHunk { - private final PatchHunk myHunk; - - public ApplyPatchHunk(final PatchHunk hunk) { - myHunk = hunk; - } - - public ApplyPatchStatus apply(final List lines) throws ApplyPatchException { - List originalLines = new ArrayList<>(lines); - try { - return tryApply(lines, false); - } - catch(ApplyPatchException ex) { - lines.clear(); - lines.addAll(originalLines); - return tryApply(lines, true); - } - } - - private ApplyPatchStatus tryApply(final List lines, boolean acceptPartial) throws ApplyPatchException { - final List hunkLines = myHunk.getLines(); - ApplyPatchStatus result = null; - int curLine = findStartLine(hunkLines, lines); - for(PatchLine line: hunkLines) { - final String patchLineText = line.getText(); - switch (line.getType()) { - case CONTEXT: - checkContextMismatch(lines, curLine, patchLineText); - curLine++; - break; - - case ADD: - if (curLine < lines.size() && lines.get(curLine).equals(patchLineText) && acceptPartial) { - result = ApplyPatchStatus.and(result, ApplyPatchStatus.ALREADY_APPLIED); - } - else { - lines.add(curLine, patchLineText); - result = ApplyPatchStatus.and(result, ApplyPatchStatus.SUCCESS); - } - curLine++; - break; - - case REMOVE: - if (curLine >= lines.size() || !patchLineText.equals(lines.get(curLine))) { - if (acceptPartial) { - // we'll get a context mismatch exception later if it's actually a conflict and not an already applied line - result = ApplyPatchStatus.and(result, ApplyPatchStatus.ALREADY_APPLIED); - } - else { - checkContextMismatch(lines, curLine, patchLineText); - } - } - else { - lines.remove(curLine); - result = ApplyPatchStatus.and(result, ApplyPatchStatus.SUCCESS); - } - break; - } - } - if (result != null) { - return result; - } - return ApplyPatchStatus.SUCCESS; - } - - private static void checkContextMismatch(final List lines, final int curLine, final String patchLineText) throws ApplyPatchException { - if (curLine >= lines.size()) { - throw new ApplyPatchException("Unexpected end of document. Expected line:\n" + patchLineText); - } - if (!patchLineText.equals(lines.get(curLine))) { - throw new ApplyPatchException("Context mismatch. Expected line:\n" + patchLineText + "\nFound line:\n" + lines.get(curLine)); - } - } - - private int findStartLine(final List hunkLines, final List lines) throws ApplyPatchException { - int totalContextLines = countContextLines(hunkLines); - final int startLineBefore = myHunk.getStartLineBefore(); - if (getLinesProcessingContext(hunkLines, lines, startLineBefore) == totalContextLines) { - return startLineBefore; - } - int maxContextStartLine = -1; - int maxContextLines = 0; - for(int i=0;i< lines.size(); i++) { - int contextLines = getLinesProcessingContext(hunkLines, lines, i); - if (contextLines == totalContextLines) { - return i; - } - if (contextLines > maxContextLines) { - maxContextLines = contextLines; - maxContextStartLine = i; - } - } - if (maxContextLines < 2) { - throw new ApplyPatchException("couldn't find context"); - } - return maxContextStartLine; - } - - private int countContextLines(final List hunkLines) { - int count = 0; - for(PatchLine line: hunkLines) { - if (line.getType() == PatchLine.Type.CONTEXT || line.getType() == PatchLine.Type.REMOVE) { - count++; - } - } - return count; - } - - private int getLinesProcessingContext(final List hunkLines, final List lines, int startLine) { - int count = 0; - for(PatchLine line: hunkLines) { - PatchLine.Type type = line.getType(); - if (type == PatchLine.Type.REMOVE || type == PatchLine.Type.CONTEXT) { - // TODO: smarter algorithm (search outward from non-context lines) - if (startLine >= lines.size() || !line.getText().equals(lines.get(startLine))) { - return count; - } - count++; - startLine++; - } - } - return count; - } -} diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/GenericPatchApplier.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/GenericPatchApplier.java index e594fd9acdec..a6459f7cd97c 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/GenericPatchApplier.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/GenericPatchApplier.java @@ -659,7 +659,7 @@ public class GenericPatchApplier { } } - private class SequentialStepsChecker implements SequenceDescriptor { + private class SequentialStepsChecker { private int myDistance; // in the end, will be [excluding] end of changing interval private int myIdx; @@ -677,7 +677,6 @@ public class GenericPatchApplier { return myUsesAlreadyApplied; } - @Override public int getDistance() { return myDistance; } @@ -719,7 +718,6 @@ public class GenericPatchApplier { } } - @Override public int getSizeOfFragmentToBeReplaced() { return myForward ? (myIdx - myStartIdx) : (myStartIdx - myIdx); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/SequenceDescriptor.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/SequenceDescriptor.java deleted file mode 100644 index 91cec3ceb382..000000000000 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/SequenceDescriptor.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2000-2011 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.diff.impl.patch.apply; - -/** - * Created by IntelliJ IDEA. - * User: Irina.Chernushina - * Date: 9/28/11 - * Time: 2:50 PM - */ -public interface SequenceDescriptor { - SequenceDescriptor STUB = new SequenceDescriptor() { - @Override - public int getDistance() { - return 0; - } - - @Override - public int getSizeOfFragmentToBeReplaced() { - return 0; - } - - @Override - public boolean isUsesAlreadyApplied() { - return false; - } - }; - - int getDistance(); - int getSizeOfFragmentToBeReplaced(); - boolean isUsesAlreadyApplied(); -} From e599b58ef38ca667dccc99aca207349fc1d32f35 Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Thu, 15 Dec 2016 15:15:48 +0300 Subject: [PATCH 190/558] gradle: properties completion (IDEA-126618) --- plugins/gradle/src/META-INF/plugin.xml | 2 + .../PropertiesCompletionContributor.kt | 80 +++++++++++++++++++ .../resolve/GradleExtensionsContributor.kt | 10 ++- .../settings/GradleExtensionsSettings.java | 25 +++++- .../ProjectExtensionsDataBuilderImpl.groovy | 2 +- 5 files changed, 113 insertions(+), 6 deletions(-) create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/codeInsight/PropertiesCompletionContributor.kt diff --git a/plugins/gradle/src/META-INF/plugin.xml b/plugins/gradle/src/META-INF/plugin.xml index d7a5abdb5499..a764c549f1fd 100644 --- a/plugins/gradle/src/META-INF/plugin.xml +++ b/plugins/gradle/src/META-INF/plugin.xml @@ -157,6 +157,8 @@ id="myWeigher" order="first"/> + () { + + override fun addCompletions(params: CompletionParameters, + context: ProcessingContext, + result: CompletionResultSet) { + val position = params.position + + val prevSibling = position.prevSibling + if (prevSibling is ASTNode && prevSibling.elementType == GroovyTokenTypes.mDOT) return + + val extensionsData = GradleExtensionsContributor.getExtensionsFor(position) ?: return + for (gradleProp in extensionsData.findAllProperties()) { + val docRef = Ref.create() + val confVar = object : GrLightVariable(position.manager, gradleProp.name, gradleProp.typeFqn, position) { + override fun getNavigationElement(): PsiElement { + val navigationElement = super.getNavigationElement() + navigationElement.putUserData(NonCodeMembersHolder.DOCUMENTATION, docRef.get()) + return navigationElement + } + } + docRef.set(getDocumentation(gradleProp, confVar)) + val elementBuilder = LookupElementBuilder.create(confVar, gradleProp.name) + .withIcon(AllIcons.Nodes.Property) + .withPresentableText(gradleProp.name) + .withTailText(" (ext)", true) + .withTypeText(confVar.type.presentableText, GradleIcons.Gradle, false) + result.addElement(elementBuilder) + } + } + } + + init { + extend(CompletionType.BASIC, PATTERN, ConfigurationsCompletionProvider()) + } + + companion object { + private val PATTERN = psiElement().and(GRADLE_FILE_PATTERN).withParent(GrReferenceExpression::class.java) + } +} \ No newline at end of file diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleExtensionsContributor.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleExtensionsContributor.kt index 007f446ab345..9884a1d0616b 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleExtensionsContributor.kt +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleExtensionsContributor.kt @@ -262,7 +262,15 @@ class GradleExtensionsContributor : GradleMethodContextContributor { buffer.append(" " + gradleProp.name) val hasInitializer = !gradleProp.value.isNullOrBlank() if (hasInitializer) { - buffer.append(" = " + gradleProp.value) + buffer.append(" = "); + val longString = gradleProp.value!!.toString().length > 100 + if (longString) { + buffer.append("

    ") + } + buffer.append(gradleProp.value) + if (longString) { + buffer.append("
    ") + } } buffer.append("") if (hasInitializer) { diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/settings/GradleExtensionsSettings.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/settings/GradleExtensionsSettings.java index 849a561dd8bd..f18932687d09 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/settings/GradleExtensionsSettings.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/settings/GradleExtensionsSettings.java @@ -25,7 +25,9 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.psi.CommonClassNames; import com.intellij.util.SmartList; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.xmlb.XmlSerializerUtil; +import com.intellij.util.xmlb.annotations.AbstractCollection; import com.intellij.util.xmlb.annotations.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -39,10 +41,7 @@ import org.jetbrains.plugins.gradle.util.GradleConstants; import java.io.File; import java.io.IOException; import java.io.Serializable; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * @author Vladislav.Soroka @@ -228,6 +227,24 @@ public class GradleExtensionsSettings implements PersistentStateComponent findAllProperties() { + return findAllProperties(this, ContainerUtil.newHashMap()); + } + + @NotNull + private static Collection findAllProperties(@NotNull GradleExtensionsData extensionsData, + @NotNull Map result) { + for (GradleProp property : extensionsData.properties) { + if (result.containsKey(property.name)) continue; + result.put(property.name, property); + } + if (extensionsData.getParent() != null) { + findAllProperties(extensionsData.getParent(), result); + } + return result.values(); + } + @Nullable private static GradleProp findProperty(@NotNull GradleExtensionsData extensionsData, String propName) { for (GradleProp property : extensionsData.properties) { diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ProjectExtensionsDataBuilderImpl.groovy b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ProjectExtensionsDataBuilderImpl.groovy index 4eed78c5e543..6afb017fb4b5 100644 --- a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ProjectExtensionsDataBuilderImpl.groovy +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ProjectExtensionsDataBuilderImpl.groovy @@ -49,7 +49,7 @@ class ProjectExtensionsDataBuilderImpl implements ModelBuilderService { def conventions = project.extensions as DefaultConvention conventions.extraProperties.properties.each { name, value -> - if(name == 'extraModelBuilder') return + if(name == 'extraModelBuilder' || name.contains('.')) return String typeFqn = getType(value) result.gradleProperties.add(new DefaultGradleProperty( name, typeFqn, value.toString())) From f71c89f3e9d10675aa3e1128c427c91fa385c670 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Thu, 15 Dec 2016 15:32:13 +0300 Subject: [PATCH 191/558] javac ast indices: do not request for rebuild when only some part of project is under compilation --- .../BackwardReferenceIndexWriter.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexWriter.java b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexWriter.java index 12e841b424e7..395a67e62d2d 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexWriter.java +++ b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexWriter.java @@ -22,8 +22,10 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.backwardRefs.index.CompiledFileData; import org.jetbrains.jps.builders.java.JavaBuilderUtil; +import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType; import org.jetbrains.jps.builders.storage.BuildDataCorruptedException; import org.jetbrains.jps.incremental.CompileContext; +import org.jetbrains.jps.incremental.ModuleBuildTarget; import org.jetbrains.jps.incremental.java.JavaBuilder; import org.jetbrains.jps.incremental.storage.BuildDataManager; import org.jetbrains.jps.javac.ast.api.JavacRef; @@ -74,7 +76,12 @@ public class BackwardReferenceIndexWriter { CompilerBackwardReferenceIndex.removeIndexFiles(buildDir); } else if (CompilerBackwardReferenceIndex.versionDiffers(buildDir)) { - throw new BuildDataCorruptedException("backward reference index should be updated to actual version"); + if (areAllJavaModulesAffected(context)) { + throw new BuildDataCorruptedException("backward reference index should be updated to actual version"); + } else { + // do not request a rebuild if a project is affected incompletely and version is changed, just disable indices + CompilerBackwardReferenceIndex.removeIndexFiles(buildDir); + } } if (CompilerBackwardReferenceIndex.exist(buildDir) || isRebuild) { @@ -157,6 +164,17 @@ public class BackwardReferenceIndexWriter { private static int id(byte[] name, ByteArrayEnumerator byteArrayEnumerator) { return byteArrayEnumerator.enumerate(name); } + + private static boolean areAllJavaModulesAffected(CompileContext context) { + for (JavaModuleBuildTargetType type : JavaModuleBuildTargetType.ALL_TYPES) { + for (ModuleBuildTarget target : context.getProjectDescriptor().getBuildTargetIndex().getAllTargets(type)) { + if (!context.getScope().isWholeTargetAffected(target)) { + return false; + } + } + } + return true; + } } From b20d72cb7a580fc225eeed8132c4c3b1f82ec13b Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Tue, 13 Dec 2016 19:35:55 +0300 Subject: [PATCH 192/558] cleanup --- .../src/com/intellij/concurrency/DoOnce.java | 40 ------- .../intellij/concurrency/FinallyFuture.java | 111 ------------------ .../SameThreadExecutorWithTrampoline.java | 51 -------- 3 files changed, 202 deletions(-) delete mode 100644 platform/core-api/src/com/intellij/concurrency/DoOnce.java delete mode 100644 platform/core-api/src/com/intellij/concurrency/FinallyFuture.java delete mode 100644 platform/core-api/src/com/intellij/concurrency/SameThreadExecutorWithTrampoline.java diff --git a/platform/core-api/src/com/intellij/concurrency/DoOnce.java b/platform/core-api/src/com/intellij/concurrency/DoOnce.java deleted file mode 100644 index de82c159dde6..000000000000 --- a/platform/core-api/src/com/intellij/concurrency/DoOnce.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2000-2013 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.concurrency; - -import org.jetbrains.annotations.NotNull; - -import java.util.concurrent.atomic.AtomicReference; - -/** - * Executes given runnable exactly once. - *

    - * Author: dmitrylomov - */ -public class DoOnce { - private final AtomicReference myRunnable; - - public DoOnce(@NotNull Runnable runnable) { - myRunnable = new AtomicReference(runnable); - } - - public void execute() { - Runnable runnable = myRunnable.getAndSet(null); - if (runnable != null) { - runnable.run(); - } - } -} diff --git a/platform/core-api/src/com/intellij/concurrency/FinallyFuture.java b/platform/core-api/src/com/intellij/concurrency/FinallyFuture.java deleted file mode 100644 index 3533249d3a1a..000000000000 --- a/platform/core-api/src/com/intellij/concurrency/FinallyFuture.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2000-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.concurrency; - -import org.jetbrains.annotations.NotNull; - -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -/** - * Author: dmitrylomov - */ -public class FinallyFuture implements AsyncFuture { - private final DoOnce myFinallyBlock; - private final AsyncFuture myInner; - - public FinallyFuture(@NotNull AsyncFuture inner, @NotNull Runnable finallyBlock) { - myInner = inner; - myFinallyBlock = new DoOnce(finallyBlock); - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) { - boolean cancel = myInner.cancel(mayInterruptIfRunning); - if (cancel) { - myFinallyBlock.execute(); - } - return cancel; - } - - @Override - public boolean isCancelled() { - return myInner.isDone(); - } - - @Override - public boolean isDone() { - return myInner.isDone(); - } - - @Override - public V get() throws InterruptedException, ExecutionException { - try { - return myInner.get(); - } - finally { - myFinallyBlock.execute(); - } - } - - @Override - public void addConsumer(@NotNull Executor executor, @NotNull final ResultConsumer consumer) { - myInner.addConsumer(executor, new ResultConsumer() { - @Override - public void onSuccess(V value) { - try { - myFinallyBlock.execute(); - consumer.onSuccess(value); - } - catch (Throwable t) { - consumer.onFailure(t); - } - } - - @Override - public void onFailure(@NotNull Throwable t) { - try { - myFinallyBlock.execute(); - } - catch (Throwable t1) { - t = t1; - } - consumer.onFailure(t); - } - }); - } - - @Override - public V get(long timeout, @NotNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - boolean timeoutOccurred = false; - try { - try { - return myInner.get(timeout, unit); - } - catch (TimeoutException t) { - timeoutOccurred = true; - throw t; - } - } - finally { - if (!timeoutOccurred) { - myFinallyBlock.execute(); - } - } - } -} diff --git a/platform/core-api/src/com/intellij/concurrency/SameThreadExecutorWithTrampoline.java b/platform/core-api/src/com/intellij/concurrency/SameThreadExecutorWithTrampoline.java deleted file mode 100644 index e145592a7166..000000000000 --- a/platform/core-api/src/com/intellij/concurrency/SameThreadExecutorWithTrampoline.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2000-2013 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.concurrency; - -import com.intellij.util.containers.Queue; -import org.jetbrains.annotations.NotNull; - -import java.util.concurrent.Executor; - -/** - * Author: dmitrylomov - */ -public class SameThreadExecutorWithTrampoline implements Executor { - - private final ThreadLocal> myExecutionTrampoline = new ThreadLocal>(); - - public SameThreadExecutorWithTrampoline() {} - - - @Override - public void execute(@NotNull Runnable command) { - if (myExecutionTrampoline.get() != null) { - myExecutionTrampoline.get().addLast(command); - return; - } - try { - final Queue queue = new Queue(2); - myExecutionTrampoline.set(queue); - queue.addLast(command); - while(!queue.isEmpty()) { - final Runnable runnable = queue.pullFirst(); - runnable.run(); - } - } finally { - myExecutionTrampoline.set(null); - } - } -} From 71212e1b295b5e240401878e448995990f245992 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Thu, 15 Dec 2016 15:42:10 +0300 Subject: [PATCH 193/558] parallel modules loading --- .../module/impl/ModuleManagerImpl.java | 100 ++++++++++++------ .../module/impl/ModulePointerManagerImpl.java | 8 +- 2 files changed, 73 insertions(+), 35 deletions(-) diff --git a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java index ebba66987803..cb68b5625f7a 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java @@ -17,11 +17,13 @@ package com.intellij.openapi.module.impl; import com.intellij.ProjectTopics; +import com.intellij.concurrency.JobSchedulerImpl; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.components.ProjectComponent; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.*; +import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressIndicatorProvider; import com.intellij.openapi.project.Project; @@ -30,21 +32,18 @@ import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.ex.ProjectRootManagerEx; import com.intellij.openapi.roots.impl.ModifiableModelCommitter; -import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.*; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.StandardFileSystems; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.util.SmartList; +import com.intellij.util.concurrency.AppExecutorUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashMap; import com.intellij.util.containers.StringInterner; import com.intellij.util.containers.hash.HashSet; -import com.intellij.util.containers.hash.LinkedHashMap; import com.intellij.util.graph.*; import com.intellij.util.io.URLUtil; import com.intellij.util.messages.MessageBus; @@ -59,6 +58,7 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.*; +import java.util.concurrent.*; /** * @author max @@ -216,25 +216,57 @@ public abstract class ModuleManagerImpl extends ModuleManager implements Project myFailedModulePaths.addAll(myModulePathsToLoad); - final ProgressIndicator progressIndicator = myProject.isDefault() ? null : ProgressIndicatorProvider.getGlobalProgressIndicator(); - if (progressIndicator != null) { - progressIndicator.setText("Loading modules..."); - progressIndicator.setText2(""); - } + ProgressIndicator globalIndicator = ProgressIndicatorProvider.getGlobalProgressIndicator(); + ProgressIndicator progressIndicator = myProject.isDefault() || globalIndicator == null ? new EmptyProgressIndicator() : globalIndicator; + progressIndicator.setText("Loading modules..."); + progressIndicator.setText2(""); List modulesWithUnknownTypes = new ArrayList<>(); - List errors = new ArrayList<>(); + List errors = Collections.synchronizedList(new ArrayList<>()); ModuleGroupInterner groupInterner = new ModuleGroupInterner(); + + ExecutorService service = AppExecutorUtil.createBoundedApplicationPoolExecutor("modules loader", JobSchedulerImpl.CORES_COUNT); + List, ModulePath>> tasks = new ArrayList<>(); + Set paths = new java.util.HashSet<>(); for (ModulePath modulePath : myModulePathsToLoad) { - if (progressIndicator != null) { - progressIndicator.checkCanceled(); - progressIndicator.setFraction(progressIndicator.getFraction() + myProgressStep); + if (progressIndicator.isCanceled()) { + break; } try { - final Module module = moduleModel.loadModuleInternal(modulePath.getPath()); + String path = modulePath.getPath(); + if (!paths.add(path)) continue; + ThrowableComputable computable = moduleModel.loadModuleInternal(path); + Future future = service.submit(() -> { + progressIndicator.setFraction(progressIndicator.getFraction() + myProgressStep); + try { + return computable.compute(); + } + catch (IOException e) { + reportError(errors, modulePath, e); + } + catch (Exception e) { + LOG.error(e); + } + return null; + }); + tasks.add(Pair.create(future, modulePath)); + } + catch (IOException e) { + reportError(errors, modulePath, e); + } + } + + for (Pair, ModulePath> task : tasks) { + if (progressIndicator.isCanceled()) { + break; + } + try { + Module module = task.first.get(); + if (module == null) continue; if (isUnknownModuleType(module)) { modulesWithUnknownTypes.add(module); } + ModulePath modulePath = task.second; final String groupPathString = modulePath.getGroup(); if (groupPathString != null) { // model should be updated too @@ -242,20 +274,24 @@ public abstract class ModuleManagerImpl extends ModuleManager implements Project } myFailedModulePaths.remove(modulePath); } - catch (IOException e) { - errors.add(ModuleLoadingErrorDescription - .create(ProjectBundle.message("module.cannot.load.error", modulePath.getPath(), e.getMessage()), modulePath, this)); - } - catch (ModuleWithNameAlreadyExists moduleWithNameAlreadyExists) { - errors.add(ModuleLoadingErrorDescription.create(moduleWithNameAlreadyExists.getMessage(), modulePath, this)); + catch (Exception e) { + LOG.error(e); } } + service.shutdown(); + + progressIndicator.checkCanceled(); onModuleLoadErrors(errors); showUnknownModuleTypeNotification(modulesWithUnknownTypes); } + private void reportError(List errors, ModulePath modulePath, Exception e) { + errors.add(ModuleLoadingErrorDescription + .create(ProjectBundle.message("module.cannot.load.error", modulePath.getPath(), e.getMessage()), modulePath, this)); + } + public int getModulePathsCount() { return myModulePathsToLoad == null ? 0 : myModulePathsToLoad.size(); } private double myProgressStep; @@ -562,7 +598,7 @@ public abstract class ModuleManagerImpl extends ModuleManager implements Project protected abstract ModuleEx createAndLoadModule(@NotNull String filePath) throws IOException; class ModuleModelImpl implements ModifiableModuleModel { - final Map myModules = new LinkedHashMap<>(); + final Map myModules = Collections.synchronizedMap(new LinkedHashMap<>()); private volatile Module[] myModulesCache; private final List myModulesToDispose = new ArrayList<>(); @@ -696,7 +732,9 @@ public abstract class ModuleManagerImpl extends ModuleManager implements Project public Module loadModule(@NotNull String filePath) throws IOException, ModuleWithNameAlreadyExists { assertWritable(); try { - return loadModuleInternal(filePath); + Module module = getModuleByFilePath(filePath); + if (module != null) return module; + return loadModuleInternal(filePath).compute(); } catch (FileNotFoundException e) { throw e; @@ -707,7 +745,7 @@ public abstract class ModuleManagerImpl extends ModuleManager implements Project } @NotNull - private Module loadModuleInternal(@NotNull String filePath) throws ModuleWithNameAlreadyExists, IOException { + private ThrowableComputable loadModuleInternal(@NotNull String filePath) throws IOException { filePath = resolveShortWindowsName(filePath); final VirtualFile moduleFile = StandardFileSystems.local().findFileByPath(filePath); if (moduleFile == null || !moduleFile.exists()) { @@ -715,13 +753,13 @@ public abstract class ModuleManagerImpl extends ModuleManager implements Project } String path = moduleFile.getPath(); - ModuleEx module = getModuleByFilePath(path); - if (module == null) { - ApplicationManager.getApplication().invokeAndWait(() -> moduleFile.refresh(false, false)); - module = createAndLoadModule(path); - initModule(module, path, null); - } - return module; + ApplicationManager.getApplication().invokeAndWait(() -> moduleFile.refresh(false, false)); + return () -> ApplicationManager.getApplication().runReadAction((ThrowableComputable)() -> { + if (myProject.isDisposed()) return null; + ModuleEx result = createAndLoadModule(path); + initModule(result, path, null); + return result; + }); } private void initModule(@NotNull ModuleEx module, @NotNull String path, @Nullable Runnable beforeComponentCreation) { diff --git a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModulePointerManagerImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModulePointerManagerImpl.java index 2d7e49f26ab7..b2507ea0860c 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModulePointerManagerImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModulePointerManagerImpl.java @@ -61,7 +61,7 @@ public class ModulePointerManagerImpl extends ModulePointerManager { }); } - private void moduleAppears(Module module) { + private synchronized void moduleAppears(Module module) { ModulePointerImpl pointer = myUnresolved.remove(module.getName()); if (pointer != null && pointer.getModule() == null) { pointer.moduleAdded(module); @@ -79,7 +79,7 @@ public class ModulePointerManagerImpl extends ModulePointerManager { }); } - private void unregisterPointer(Module module) { + private synchronized void unregisterPointer(Module module) { final ModulePointerImpl pointer = myPointers.remove(module); if (pointer != null) { pointer.moduleRemoved(module); @@ -89,7 +89,7 @@ public class ModulePointerManagerImpl extends ModulePointerManager { @NotNull @Override - public ModulePointer create(@NotNull Module module) { + public synchronized ModulePointer create(@NotNull Module module) { ModulePointerImpl pointer = myPointers.get(module); if (pointer == null) { pointer = myUnresolved.get(module.getName()); @@ -106,7 +106,7 @@ public class ModulePointerManagerImpl extends ModulePointerManager { @NotNull @Override - public ModulePointer create(@NotNull String moduleName) { + public synchronized ModulePointer create(@NotNull String moduleName) { final Module module = ModuleManager.getInstance(myProject).findModuleByName(moduleName); if (module != null) { return create(module); From 1d12240250328b6b95ee721cd77af6da1a9e1713 Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Thu, 15 Dec 2016 16:00:39 +0300 Subject: [PATCH 194/558] gradle: tasks completion --- plugins/gradle/src/META-INF/plugin.xml | 2 +- ...> PropertiesTasksCompletionContributor.kt} | 35 ++++++++++++++----- 2 files changed, 28 insertions(+), 9 deletions(-) rename plugins/gradle/src/org/jetbrains/plugins/gradle/codeInsight/{PropertiesCompletionContributor.kt => PropertiesTasksCompletionContributor.kt} (67%) diff --git a/plugins/gradle/src/META-INF/plugin.xml b/plugins/gradle/src/META-INF/plugin.xml index a764c549f1fd..95f2400deef9 100644 --- a/plugins/gradle/src/META-INF/plugin.xml +++ b/plugins/gradle/src/META-INF/plugin.xml @@ -158,7 +158,7 @@ + implementationClass="org.jetbrains.plugins.gradle.codeInsight.PropertiesTasksCompletionContributor"/> () { +class PropertiesTasksCompletionContributor : AbstractGradleCompletionContributor() { + class PropertiesTasksCompletionProvider : CompletionProvider() { override fun addCompletions(params: CompletionParameters, context: ProcessingContext, @@ -52,26 +53,44 @@ class PropertiesCompletionContributor : AbstractGradleCompletionContributor() { val extensionsData = GradleExtensionsContributor.getExtensionsFor(position) ?: return for (gradleProp in extensionsData.findAllProperties()) { val docRef = Ref.create() - val confVar = object : GrLightVariable(position.manager, gradleProp.name, gradleProp.typeFqn, position) { + val propVar = object : GrLightVariable(position.manager, gradleProp.name, gradleProp.typeFqn, position) { override fun getNavigationElement(): PsiElement { val navigationElement = super.getNavigationElement() navigationElement.putUserData(NonCodeMembersHolder.DOCUMENTATION, docRef.get()) return navigationElement } } - docRef.set(getDocumentation(gradleProp, confVar)) - val elementBuilder = LookupElementBuilder.create(confVar, gradleProp.name) + docRef.set(getDocumentation(gradleProp, propVar)) + val elementBuilder = LookupElementBuilder.create(propVar, gradleProp.name) .withIcon(AllIcons.Nodes.Property) .withPresentableText(gradleProp.name) - .withTailText(" (ext)", true) - .withTypeText(confVar.type.presentableText, GradleIcons.Gradle, false) + .withTailText(" via ext", true) + .withTypeText(propVar.type.presentableText, GradleIcons.Gradle, false) + result.addElement(elementBuilder) + } + + for (gradleTask in extensionsData.tasks) { + val docRef = Ref.create() + val taskVar = object : GrLightVariable(position.manager, gradleTask.name, gradleTask.typeFqn, position) { + override fun getNavigationElement(): PsiElement { + val navigationElement = super.getNavigationElement() + navigationElement.putUserData(NonCodeMembersHolder.DOCUMENTATION, docRef.get()) + return navigationElement + } + } + docRef.set(getDocumentation(gradleTask, taskVar)) + val elementBuilder = LookupElementBuilder.create(taskVar, gradleTask.name) + .withIcon(ExternalSystemIcons.Task) + .withPresentableText(gradleTask.name) + .withTailText(" task", true) + .withTypeText(taskVar.type.presentableText) result.addElement(elementBuilder) } } } init { - extend(CompletionType.BASIC, PATTERN, ConfigurationsCompletionProvider()) + extend(CompletionType.BASIC, PATTERN, PropertiesTasksCompletionProvider()) } companion object { From 04a5bd8cd2941dfbd90f8c2f7e18932fc5896de7 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Thu, 15 Dec 2016 16:12:35 +0300 Subject: [PATCH 195/558] PY-21447 Jupyter Notebook fails to properly handle IPython.display Markdown load remote mathjax because it can't resolve it's dependencies inside jar --- python/ipnb/src/org/jetbrains/plugins/ipnb/IpnbJfxUtils.java | 5 ++--- .../src/org/jetbrains/plugins/ipnb/format/IpnbParser.java | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/IpnbJfxUtils.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/IpnbJfxUtils.java index 7cbd6f1d2248..c9cda9fee348 100644 --- a/python/ipnb/src/org/jetbrains/plugins/ipnb/IpnbJfxUtils.java +++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/IpnbJfxUtils.java @@ -60,9 +60,7 @@ public class IpnbJfxUtils { " }\n" + " });\n" + "" + ourBody; private static final String ourPrefix = ourStyle + ourBody; private static final String ourPostfix = ""; @@ -102,6 +100,7 @@ public class IpnbJfxUtils { final BorderPane pane = new BorderPane(webView); final String prefix; if (hasMath) { + prefix = String.format(ourMathJaxPrefix, width - 500, EditorColorsManager.getInstance().getGlobalScheme().getEditorFontSize()); } else { diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/format/IpnbParser.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/format/IpnbParser.java index 468a65a906a7..c2457bdafb6c 100644 --- a/python/ipnb/src/org/jetbrains/plugins/ipnb/format/IpnbParser.java +++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/format/IpnbParser.java @@ -361,7 +361,7 @@ public class IpnbParser { } raw.data = dataRaw; raw.execution_count = outputCell.getPromptNumber(); - raw.output_type = "execute_result"; + raw.output_type = outputCell.getPromptNumber() != null ? "execute_result" : "display_data"; } else { raw.latex = ((IpnbLatexOutputCell)outputCell).getLatex(); From 653944e2aa798ac27319630620443c46cc99be17 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Thu, 15 Dec 2016 16:17:23 +0300 Subject: [PATCH 196/558] javac ast indices: prevent npe while recompilation of single file --- .../jetbrains/jps/backwardRefs/BackwardReferenceRegistrar.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceRegistrar.java b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceRegistrar.java index b6fb8ee5177a..83d317582114 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceRegistrar.java +++ b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceRegistrar.java @@ -32,7 +32,7 @@ public class BackwardReferenceRegistrar implements JavacFileReferencesRegistrar @Override public boolean isEnabled() { - return BackwardReferenceIndexWriter.isEnabled(); + return BackwardReferenceIndexWriter.isEnabled() && BackwardReferenceIndexWriter.getInstance() != null; } @Override From 29bddd43fb84f4cf45d8b24bc7bea132e1d2e325 Mon Sep 17 00:00:00 2001 From: Dennis Ushakov Date: Thu, 15 Dec 2016 16:20:35 +0300 Subject: [PATCH 197/558] html tags completion in a2 inline template --- xml/xml-psi-impl/src/com/intellij/xml/util/XmlUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xml/xml-psi-impl/src/com/intellij/xml/util/XmlUtil.java b/xml/xml-psi-impl/src/com/intellij/xml/util/XmlUtil.java index 757f827bb212..c8373bd46c63 100644 --- a/xml/xml-psi-impl/src/com/intellij/xml/util/XmlUtil.java +++ b/xml/xml-psi-impl/src/com/intellij/xml/util/XmlUtil.java @@ -729,7 +729,7 @@ public class XmlUtil { if (file != null) { final Language language = file.getLanguage(); - if (language == HTMLLanguage.INSTANCE || language == XHTMLLanguage.INSTANCE) { + if (language.isKindOf(HTMLLanguage.INSTANCE) || language == XHTMLLanguage.INSTANCE) { return new String[][]{new String[]{"", XHTML_URI}}; } } From a8c9228ea217ce9c3428bc8fcedfe733fb1e5b0d Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Wed, 14 Dec 2016 16:46:57 +0100 Subject: [PATCH 198/558] Fix conda activation on Windows (PY-21923) --- .../python/run/PyVirtualEnvReader.kt | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/python/src/com/jetbrains/python/run/PyVirtualEnvReader.kt b/python/src/com/jetbrains/python/run/PyVirtualEnvReader.kt index 961577934eb0..7b412a228a45 100644 --- a/python/src/com/jetbrains/python/run/PyVirtualEnvReader.kt +++ b/python/src/com/jetbrains/python/run/PyVirtualEnvReader.kt @@ -67,10 +67,10 @@ class PyVirtualEnvReader(val virtualEnvSdkPath: String) : EnvironmentUtil.ShellE val envFile = FileUtil.createTempFile("pycharm-virualenv-envs.", ".tmp", false) try { FileUtil.copy(File(activate.first), activateFile); - FileUtil.appendToFile(activateFile, "\n\nset") + FileUtil.appendToFile(activateFile, "\n\nset >" + envFile.absoluteFile) - val command = if (activate.second != null) listOf(activateFile.path, activate.second!!, ">", envFile.absolutePath) - else listOf(activateFile.path, ">", envFile.absolutePath) + val command = if (activate.second != null) listOf(activateFile.path, activate.second!!) + else listOf(activateFile.path) return runProcessAndReadEnvs(command, envFile, LineSeparator.CRLF.separatorString) } @@ -99,14 +99,27 @@ class PyVirtualEnvReader(val virtualEnvSdkPath: String) : EnvironmentUtil.ShellE fun findActivateScript(path: String?, shellPath: String?): Pair? { val shellName = if (shellPath != null) File(shellPath).name else null - val activate = if (SystemInfo.isWindows) File(File(path).parentFile, "activate.bat") + val activate = if (SystemInfo.isWindows) findActivateOnWindows(path) else if (shellName == "fish" || shellName == "csh") File(File(path).parentFile, "activate." + shellName) else File(File(path).parentFile, "activate") - return if (activate.exists()) { + return if (activate != null && activate.exists()) { val sdk = PythonSdkType.findSdkByPath(path) - if (sdk != null && PythonSdkType.isCondaVirtualEnv(sdk)) Pair(activate.absolutePath, File(path).parentFile.parent) + if (sdk != null && PythonSdkType.isCondaVirtualEnv(sdk)) Pair(activate.absolutePath, condaEnvFolder(path)) else Pair(activate.absolutePath, null) } else null +} + +private fun condaEnvFolder(path: String?) = if (SystemInfo.isWindows) File(path).parent else File(path).parentFile.parent + +private fun findActivateOnWindows(path: String?): File? { + for (location in arrayListOf("activate.bat", "Scripts/activate.bat")) { + val file = File(File(path).parentFile, location) + if (file.exists()) { + return file + } + } + + return null } \ No newline at end of file From 7f70a817b8638040d3f9ff51c0c286a054194226 Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Thu, 15 Dec 2016 16:45:36 +0300 Subject: [PATCH 199/558] IDEA-165174 Go to column on the current line --- .../com/intellij/ide/util/GotoLineNumberDialog.java | 10 +++++----- .../src/messages/ActionsBundle.properties | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ide/util/GotoLineNumberDialog.java b/platform/platform-impl/src/com/intellij/ide/util/GotoLineNumberDialog.java index 2cfe3ec9266a..9dcea708cba8 100644 --- a/platform/platform-impl/src/com/intellij/ide/util/GotoLineNumberDialog.java +++ b/platform/platform-impl/src/com/intellij/ide/util/GotoLineNumberDialog.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,12 +37,12 @@ public class GotoLineNumberDialog extends DialogWrapper { private JTextField myField; private JTextField myOffsetField; private final Editor myEditor; - private final Pattern myPattern = PatternUtil.compileSafe("\\s*(\\d+)\\s*(?:[,:]?\\s*(\\d+)?)?\\s*", null); + private final Pattern myPattern = PatternUtil.compileSafe("\\s*(\\d+)?\\s*(?:[,:]?\\s*(\\d+)?)?\\s*", null); public GotoLineNumberDialog(Project project, Editor editor) { super(project, true); myEditor = editor; - setTitle("Go to Line"); + setTitle("Go to Line/Column"); init(); } @@ -63,7 +63,7 @@ public class GotoLineNumberDialog extends DialogWrapper { Matcher m = myPattern.matcher(getText()); if (!m.matches()) return null; - int l = StringUtil.parseInt(m.group(1), -1); + int l = StringUtil.parseInt(m.group(1), myEditor.getCaretModel().getLogicalPosition().line + 1); int c = StringUtil.parseInt(m.group(2), -1); return l > 0 ? new LogicalPosition(l - 1, Math.max(0, c - 1)) : null; } @@ -104,7 +104,7 @@ public class GotoLineNumberDialog extends DialogWrapper { gbConstraints.weightx = 0; gbConstraints.weighty = 1; gbConstraints.anchor = GridBagConstraints.EAST; - JLabel label = new JLabel("Line [:column]:"); + JLabel label = new JLabel("[Line] [:column]:"); panel.add(label, gbConstraints); gbConstraints.fill = GridBagConstraints.BOTH; diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index e950a921e1f5..94e69d8e377b 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -492,8 +492,8 @@ action.GotoSymbol.description=Quickly navigate to any symbol by name action.GotoCustomRegion.text=Custom Folding... action.GotoAction.text=_Find Action... action.GotoAction.description=Quickly navigate to action by name -action.GotoLine.text=_Line... -action.GotoLine.description=Navigate to a line in editor by its number +action.GotoLine.text=_Line/Column... +action.GotoLine.description=Navigate to a line (and/or column) in editor by its number action.GotoDeclaration.text=_Declaration action.GotoDeclaration.description=Navigate to the declaration of the symbol at caret action.GotoImplementation.text=I_mplementation(s) From 08163051f3dbdcaa15853a450db1ac09392ae7d0 Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Thu, 15 Dec 2016 12:29:59 +0100 Subject: [PATCH 200/558] show class/package conflict only if corresponding subdirectory contains classes so there could be usages with ambiguous imports --- .../codeInsight/daemon/impl/analysis/HighlightClassUtil.java | 2 +- .../packageClassClashNoClassInDir/pkg/sub.java | 3 +++ .../packageClassClashNoClassInDir/pkg/sub/empty.txt | 0 .../com/intellij/codeInsight/daemon/AdvHighlightingTest.java | 4 ++++ 4 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/packageClassClashNoClassInDir/pkg/sub.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/packageClassClashNoClassInDir/pkg/sub/empty.txt diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java index fc1bc4b71533..c57b9cd0ccff 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java @@ -279,7 +279,7 @@ public class HighlightClassUtil { if (directory instanceof PsiDirectory) { String simpleName = aClass.getName(); PsiDirectory subDirectory = ((PsiDirectory)directory).findSubdirectory(simpleName); - if (subDirectory != null && simpleName.equals(subDirectory.getName())) { + if (subDirectory != null && simpleName.equals(subDirectory.getName()) && PsiTreeUtil.findChildOfType(subDirectory, PsiJavaFile.class) != null) { String message = JavaErrorMessages.message("class.clashes.with.package", name); TextRange range = HighlightNamesUtil.getClassDeclarationTextRange(aClass); return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range).descriptionAndTooltip(message).create(); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/packageClassClashNoClassInDir/pkg/sub.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/packageClassClashNoClassInDir/pkg/sub.java new file mode 100644 index 000000000000..bcb9c34e7aaa --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/packageClassClashNoClassInDir/pkg/sub.java @@ -0,0 +1,3 @@ +package pkg; + +public class sub { } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/packageClassClashNoClassInDir/pkg/sub/empty.txt b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/packageClassClashNoClassInDir/pkg/sub/empty.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/AdvHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/AdvHighlightingTest.java index 9ba68642b658..a68d2053f18c 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/AdvHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/AdvHighlightingTest.java @@ -307,6 +307,10 @@ public class AdvHighlightingTest extends DaemonAnalyzerTestCase { doTest(BASE_PATH + "/packageClassClash2/pkg/Sub.java", BASE_PATH + "/packageClassClash2", false, false); } + public void testPackageAndClassConflictNoClassInSubdir() throws Exception { + doTest(BASE_PATH + "/packageClassClashNoClassInDir/pkg/sub.java", BASE_PATH + "/packageClassClashNoClassInDir", false, false); + } + // todo[r.sh] IDEA-91596 (probably PJCRE.resolve() should be changed to qualifier-first model) //public void testPackageAndClassConflict3() throws Exception { // doTest(BASE_PATH + "/packageClassClash3/test/Test.java", BASE_PATH + "/packageClassClash3", false, false); From 92855f2ab9d9a7675d489e100e0c0f0cf2de9b45 Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Thu, 15 Dec 2016 14:29:03 +0100 Subject: [PATCH 201/558] warn about ignored results of method calls in functional expressions (IDEA-165481) --- .../IgnoreResultOfCallInspectionBase.java | 55 +++++++++++++------ .../IgnoreResultOfCallInspectionTest.groovy | 18 ++++++ 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/IgnoreResultOfCallInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/IgnoreResultOfCallInspectionBase.java index 10776349550f..d2f3df67b52c 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/IgnoreResultOfCallInspectionBase.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/IgnoreResultOfCallInspectionBase.java @@ -23,6 +23,7 @@ import com.intellij.openapi.util.registry.Registry; import com.intellij.psi.*; import com.intellij.psi.util.PropertyUtil; import com.intellij.psi.util.PsiUtilCore; +import com.intellij.util.ObjectUtils; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; @@ -115,19 +116,32 @@ public class IgnoreResultOfCallInspectionBase extends BaseInspection { } private class IgnoreResultOfCallVisitor extends BaseInspectionVisitor { + @Override + public void visitMethodReferenceExpression(PsiMethodReferenceExpression expression) { + if (PsiType.VOID.equals(LambdaUtil.getFunctionalInterfaceReturnType(expression))) { + PsiElement resolve = expression.resolve(); + if (resolve instanceof PsiMethod) { + visitCalledExpression(expression, (PsiMethod)resolve, null); + } + } + } @Override - public void visitExpressionStatement(@NotNull PsiExpressionStatement statement) { - super.visitExpressionStatement(statement); - final PsiExpression expression = statement.getExpression(); - if (!(expression instanceof PsiMethodCallExpression)) { - return; - } - final PsiMethodCallExpression call = (PsiMethodCallExpression)expression; - final PsiMethod method = call.resolveMethod(); - if (method == null || method.isConstructor()) { - return; + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + PsiElement parent = expression.getParent(); + if (parent instanceof PsiExpressionStatement || + parent instanceof PsiLambdaExpression && PsiType.VOID.equals(LambdaUtil.getFunctionalInterfaceReturnType((PsiLambdaExpression)parent))) { + final PsiMethod method = expression.resolveMethod(); + if (method == null || method.isConstructor()) { + return; + } + visitCalledExpression(expression, method, parent); } + } + + private void visitCalledExpression(PsiExpression call, + PsiMethod method, + @Nullable PsiElement errorContainer) { final PsiType returnType = method.getReturnType(); if (PsiType.VOID.equals(returnType)) { return; @@ -136,24 +150,24 @@ public class IgnoreResultOfCallInspectionBase extends BaseInspection { if (aClass == null) { return; } - if (PsiUtilCore.hasErrorElementChild(statement)) { + if (errorContainer != null && PsiUtilCore.hasErrorElementChild(errorContainer)) { return; } if (PropertyUtil.isSimpleGetter(method)) { - registerMethodCallError(call, aClass); + registerMethodCallOrRefError(call, aClass); return; } if (m_reportAllNonLibraryCalls && !LibraryUtil.classIsInLibrary(aClass)) { - registerMethodCallError(call, aClass); + registerMethodCallOrRefError(call, aClass); return; } final PsiAnnotation anno = ControlFlowAnalyzer.findContractAnnotation(method); final boolean honorInferred = Registry.is("ide.ignore.call.result.inspection.honor.inferred.pure"); if (anno != null && - (honorInferred || !AnnotationUtil.isInferredAnnotation(anno)) && + (honorInferred || !AnnotationUtil.isInferredAnnotation(anno)) && Boolean.TRUE.equals(AnnotationUtil.getBooleanAttributeValue(anno, "pure"))) { - registerMethodCallError(call, aClass); + registerMethodCallOrRefError(call, aClass); return; } final PsiAnnotation annotation = findAnnotationInTree(method, null, Collections.singleton("javax.annotation.CheckReturnValue")); @@ -167,7 +181,16 @@ public class IgnoreResultOfCallInspectionBase extends BaseInspection { return; } - registerMethodCallError(call, aClass); + registerMethodCallOrRefError(call, aClass); + } + + private void registerMethodCallOrRefError(PsiExpression call, PsiClass aClass) { + if (call instanceof PsiMethodCallExpression) { + registerMethodCallError((PsiMethodCallExpression)call, aClass); + } + else if (call instanceof PsiMethodReferenceExpression){ + registerError(ObjectUtils.notNull(((PsiMethodReferenceExpression)call).getReferenceNameElement(), call), aClass); + } } @Nullable diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/IgnoreResultOfCallInspectionTest.groovy b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/IgnoreResultOfCallInspectionTest.groovy index 61a44d287f2d..fbebb3bb59cf 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/IgnoreResultOfCallInspectionTest.groovy +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/IgnoreResultOfCallInspectionTest.groovy @@ -180,6 +180,24 @@ class C { Util./*Result of 'Util.util()' is ignored*/util/**/(); } } +""" + } + + void testPureMethodInVoidFunctionalExpression() { + doTest """ +import org.jetbrains.annotations.Contract; + +class Util { + @Contract(pure=true) + static Object util() { return null; } +} + +class C { + { + Runnable r = () -> Util./*Result of 'Util.util()' is ignored*/util/**/(); + Runnable r1 = Util::/*Result of 'Util.util()' is ignored*/util/**/; + } +} """ } } \ No newline at end of file From 1812bf5e1d39a812e8fa4eb8bbf3279ae9a78e77 Mon Sep 17 00:00:00 2001 From: Valentina Kiryushkina Date: Thu, 15 Dec 2016 16:11:30 +0300 Subject: [PATCH 202/558] Fix EDU-801 Empty window instead of choice task: use proper class loader to get resource file --- .../jetbrains/edu/learning/editor/StudyChoiceVariantsPanel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/editor/StudyChoiceVariantsPanel.kt b/python/educational-core/student/src/com/jetbrains/edu/learning/editor/StudyChoiceVariantsPanel.kt index 57ffd9d0a6f7..7593fc413071 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/editor/StudyChoiceVariantsPanel.kt +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/editor/StudyChoiceVariantsPanel.kt @@ -40,7 +40,7 @@ class StudyChoiceVariantsPanel(task: Task) : JScrollPane() { checkBox.isWrapText = true checkBox.maxWidthProperty().bind(scene.widthProperty().subtract(LEFT_INSET).subtract(RIGHT_INSET)) checkBox.font = Font.font((EditorColorsManager.getInstance().globalScheme.editorFontSize + 2).toDouble()) - checkBox.stylesheets.add(String::class.java.getResource("/style/buttons.css").toExternalForm()) + checkBox.stylesheets.add(StudyChoiceVariantsPanel::class.java.getResource("/style/buttons.css").toExternalForm()) checkBox.selectedProperty().addListener { observableValue, wasSelected, isSelected -> if (isSelected) { task.selectedVariants.add(index) From 1a6994b6c25f37987d52926f56601036db7f5350 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Thu, 15 Dec 2016 17:36:00 +0300 Subject: [PATCH 203/558] fixed balloon shadow with the new hidpi --- platform/util/src/com/intellij/util/ui/UIUtil.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index 1206840e230b..561d6be2d149 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -1919,6 +1919,8 @@ public class UIUtil { if (isJDKManagedHiDPIScreen(g2d)) { return RetinaImage.create(g2d, width, height, type); } + //noinspection UndesirableClassUsage + return new BufferedImage(width, height, type); } return createImage(width, height, type); } From 505d415e4a5fdbf339a0c107b954df044f031ba7 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Thu, 15 Dec 2016 16:43:09 +0300 Subject: [PATCH 204/558] [groovy] extract GrOperatorExpression from GrBinaryFacade --- .../expressions/GrAssignmentExpression.java | 16 +-- .../expressions/GrBinaryExpression.java | 16 +-- .../expressions/GrOperatorExpression.java} | 25 ++-- .../statements/GrOperatorExpressionImpl.java | 99 +++++++++++++ .../GrAssignmentExpressionImpl.java | 130 ++---------------- .../expressions/GrBinaryExpressionImpl.java | 124 +---------------- .../GrAssignTypeCalculator.java | 7 +- .../GrBinaryExpressionTypeCalculator.java | 17 +-- .../GrBinaryExpressionTypeCalculators.java | 12 +- .../GrBinaryExpressionUtil.java | 27 ++-- .../GrBooleanExpressionTypeCalculator.java | 5 +- ...DecimalBinaryExpressionTypeCalculator.java | 3 +- .../GrDivExpressionTypeCalculator.java | 3 +- .../GrIntegerTypeCalculator.java | 5 +- .../GrMatcherTypeCalculator.java | 5 +- ...ultiplicativeExpressionTypeCalculator.java | 3 +- ...NumericBinaryExpressionTypeCalculator.java | 17 ++- .../GrRangeExpressionTypeCalculator.java | 9 +- 18 files changed, 182 insertions(+), 341 deletions(-) rename plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/{impl/statements/expressions/binaryCalculators/GrBinaryFacade.java => api/statements/expressions/GrOperatorExpression.java} (70%) create mode 100644 plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrOperatorExpressionImpl.java diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrAssignmentExpression.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrAssignmentExpression.java index aa7071a1928f..e42903ce0cd5 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrAssignmentExpression.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrAssignmentExpression.java @@ -16,31 +16,17 @@ package org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiPolyVariantReference; -import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; /** * @author ilyas */ -public interface GrAssignmentExpression extends GrExpression, PsiPolyVariantReference { +public interface GrAssignmentExpression extends GrOperatorExpression { @NotNull GrExpression getLValue(); @Nullable GrExpression getRValue(); - - @NotNull - IElementType getOperationTokenType(); - - @Override - @NotNull - GroovyResolveResult[] multiResolve(boolean incompleteCode); - - @NotNull - PsiElement getOperationToken(); } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrBinaryExpression.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrBinaryExpression.java index 9aaa0a8681c7..991b9a10bf5f 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrBinaryExpression.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrBinaryExpression.java @@ -16,17 +16,13 @@ package org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiPolyVariantReference; -import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; /** * @author ilyas */ -public interface GrBinaryExpression extends GrExpression, PsiPolyVariantReference { +public interface GrBinaryExpression extends GrOperatorExpression { /** * @return left operand of binary expression @@ -39,14 +35,4 @@ public interface GrBinaryExpression extends GrExpression, PsiPolyVariantReferenc */ @Nullable GrExpression getRightOperand(); - - @NotNull - IElementType getOperationTokenType(); - - @NotNull - PsiElement getOperationToken(); - - @NotNull - @Override - GroovyResolveResult[] multiResolve(final boolean incompleteCode); } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrBinaryFacade.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrOperatorExpression.java similarity index 70% rename from plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrBinaryFacade.java rename to plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrOperatorExpression.java index 2e43c1765a6e..0854410cd381 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrBinaryFacade.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrOperatorExpression.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 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,35 +13,30 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.binaryCalculators; +package org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiPolyVariantReference; +import com.intellij.psi.PsiType; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; -/** - * Created by Max Medvedev on 12/20/13 - */ -public interface GrBinaryFacade { - - @NotNull - GrExpression getLeftOperand(); +public interface GrOperatorExpression extends GrExpression, PsiPolyVariantReference { @Nullable - GrExpression getRightOperand(); + PsiType getLeftType(); - @NotNull - IElementType getOperationTokenType(); + @Nullable + PsiType getRightType(); @NotNull PsiElement getOperationToken(); @NotNull - GroovyResolveResult[] multiResolve(final boolean incompleteCode); + IElementType getOperationTokenType(); @NotNull - GrExpression getPsiElement(); + GroovyResolveResult[] multiResolve(final boolean incompleteCode); } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrOperatorExpressionImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrOperatorExpressionImpl.java new file mode 100644 index 000000000000..cbe73007899a --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrOperatorExpressionImpl.java @@ -0,0 +1,99 @@ +/* + * Copyright 2000-2016 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 com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiType; +import com.intellij.psi.tree.IElementType; +import com.intellij.util.ArrayUtil; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrOperatorExpression; +import org.jetbrains.plugins.groovy.lang.psi.dataFlow.types.TypeInferenceHelper; +import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; +import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrExpressionImpl; +import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.binaryCalculators.GrBinaryExpressionTypeCalculators; + +abstract public class GrOperatorExpressionImpl extends GrExpressionImpl implements GrOperatorExpression { + + protected GrOperatorExpressionImpl(@NotNull ASTNode node) { + super(node); + } + + @Nullable + @Override + public PsiType getType() { + return TypeInferenceHelper.getCurrentContext().getExpressionType(this, GrBinaryExpressionTypeCalculators::computeType); + } + + @Override + @NotNull + public IElementType getOperationTokenType() { + return getOperationToken().getNode().getElementType(); + } + + @Override + public PsiElement getElement() { + return this; + } + + @Override + public TextRange getRangeInElement() { + final PsiElement token = getOperationToken(); + final int offset = token.getStartOffsetInParent(); + return new TextRange(offset, offset + token.getTextLength()); + } + + @Override + public PsiElement resolve() { + return PsiImplUtil.extractUniqueElement(multiResolve(false)); + } + + @Override + public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException { + throw new IncorrectOperationException("assignment expression cannot be renamed"); + } + + @Override + public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException { + throw new IncorrectOperationException("assignment expression cannot be bound to anything"); + } + + @Override + public boolean isReferenceTo(PsiElement element) { + return getManager().areElementsEquivalent(resolve(), element); + } + + @NotNull + @Override + public Object[] getVariants() { + return ArrayUtil.EMPTY_OBJECT_ARRAY; + } + + @Override + public boolean isSoft() { + return false; + } + + @NotNull + @Override + public String getCanonicalText() { + return getText(); + } +} diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrAssignmentExpressionImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrAssignmentExpressionImpl.java index b4282cc7c5f0..15bb02431b5d 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrAssignmentExpressionImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrAssignmentExpressionImpl.java @@ -17,7 +17,6 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions; import com.intellij.lang.ASTNode; -import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; import com.intellij.psi.PsiType; @@ -26,10 +25,7 @@ import com.intellij.psi.impl.source.resolve.ResolveCache; import com.intellij.psi.scope.ElementClassHint; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.tree.IElementType; -import com.intellij.util.ArrayUtil; import com.intellij.util.ConcurrencyUtil; -import com.intellij.util.Function; -import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; @@ -43,10 +39,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrTupleE import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrIndexProperty; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.types.TypeInferenceHelper; import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyFileImpl; -import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; -import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.binaryCalculators.GrBinaryExpressionTypeCalculators; -import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.binaryCalculators.GrBinaryExpressionUtil; -import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.binaryCalculators.GrBinaryFacade; +import org.jetbrains.plugins.groovy.lang.psi.impl.statements.GrOperatorExpressionImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrBindingVariable; import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; @@ -55,11 +48,7 @@ import java.util.concurrent.ConcurrentMap; /** * @author ilyas */ -public class GrAssignmentExpressionImpl extends GrExpressionImpl implements GrAssignmentExpression { - - private GrBinaryFacade getFacade() { - return myFacade; - } +public class GrAssignmentExpressionImpl extends GrOperatorExpressionImpl implements GrAssignmentExpression { public GrAssignmentExpressionImpl(@NotNull ASTNode node) { super(node); @@ -85,23 +74,12 @@ public class GrAssignmentExpressionImpl extends GrExpressionImpl implements GrAs return null; } - @Override - @NotNull - public IElementType getOperationTokenType() { - return getOperationToken().getNode().getElementType(); - } - @Override @NotNull public PsiElement getOperationToken() { return findNotNullChildByType(TokenSets.ASSIGN_OP_SET); } - @Override - public PsiType getType() { - return TypeInferenceHelper.getCurrentContext().getExpressionType(this, TYPE_CALCULATOR); - } - @Override public void accept(GroovyElementVisitor visitor) { visitor.visitAssignmentExpression(this); @@ -113,55 +91,6 @@ public class GrAssignmentExpressionImpl extends GrExpressionImpl implements GrAs return TypeInferenceHelper.getCurrentContext().multiResolve(this, incompleteCode, RESOLVER); } - @Override - public PsiElement getElement() { - return this; - } - - @Override - public TextRange getRangeInElement() { - final PsiElement token = getOperationToken(); - final int offset = token.getStartOffsetInParent(); - return new TextRange(offset, offset + token.getTextLength()); - } - - @Override - public PsiElement resolve() { - return PsiImplUtil.extractUniqueElement(multiResolve(false)); - } - - @NotNull - @Override - public String getCanonicalText() { - return getText(); - } - - @Override - public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException { - throw new IncorrectOperationException("assignment expression cannot be renamed"); - } - - @Override - public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException { - throw new IncorrectOperationException("assignment expression cannot be bound to anything"); - } - - @Override - public boolean isReferenceTo(PsiElement element) { - return getManager().areElementsEquivalent(resolve(), element); - } - - @NotNull - @Override - public Object[] getVariants() { - return ArrayUtil.EMPTY_OBJECT_ARRAY; - } - - @Override - public boolean isSoft() { - return false; - } - @Override public PsiReference getReference() { final IElementType operationToken = getOperationTokenType(); @@ -219,44 +148,18 @@ public class GrAssignmentExpressionImpl extends GrExpressionImpl implements GrAs return processor.execute(variable, state); } - private final GrBinaryFacade myFacade = new GrBinaryFacade() { - @NotNull - @Override - public GrExpression getLeftOperand() { - return getLValue(); - } - - @Nullable - @Override - public GrExpression getRightOperand() { - return getRValue(); - } - - @NotNull - @Override - public IElementType getOperationTokenType() { - return GrAssignmentExpressionImpl.this.getOperationTokenType(); - } - - @NotNull - @Override - public PsiElement getOperationToken() { - return GrAssignmentExpressionImpl.this.getOperationToken(); - } - - @NotNull - @Override - public GroovyResolveResult[] multiResolve(boolean incompleteCode) { - return GrAssignmentExpressionImpl.this.multiResolve(false); - } - - @NotNull - @Override - public GrExpression getPsiElement() { - return GrAssignmentExpressionImpl.this; - } - }; + @Nullable + @Override + public PsiType getLeftType() { + return getLValue().getType(); + } + @Nullable + @Override + public PsiType getRightType() { + GrExpression rValue = getRValue(); + return rValue == null ? null : rValue.getType(); + } private static final ResolveCache.PolyVariantResolver RESOLVER = new ResolveCache.PolyVariantResolver() { @NotNull @@ -279,15 +182,10 @@ public class GrAssignmentExpressionImpl extends GrExpressionImpl implements GrAs } if (lType == null) return GroovyResolveResult.EMPTY_ARRAY; - PsiType rType = GrBinaryExpressionUtil.getRightType(assignmentExpression.getFacade()); + PsiType rType = assignmentExpression.getRightType(); final IElementType operatorToken = TokenSets.ASSIGNMENTS_TO_OPERATORS.get(opType); return TypesUtil.getOverloadedOperatorCandidates(lType, operatorToken, lValue, new PsiType[]{rType}); } }; - - private static final Function TYPE_CALCULATOR = expression -> { - final Function calculator = GrBinaryExpressionTypeCalculators.getTypeCalculator(expression.getFacade()); - return calculator.fun(expression.getFacade()); - }; } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrBinaryExpressionImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrBinaryExpressionImpl.java index c083b2e40f17..e946014672a6 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrBinaryExpressionImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrBinaryExpressionImpl.java @@ -17,16 +17,12 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions; import com.intellij.lang.ASTNode; -import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiRecursiveElementWalkingVisitor; import com.intellij.psi.PsiReference; import com.intellij.psi.PsiType; import com.intellij.psi.impl.source.resolve.ResolveCache; import com.intellij.psi.tree.IElementType; -import com.intellij.util.ArrayUtil; -import com.intellij.util.Function; -import com.intellij.util.IncorrectOperationException; import com.intellij.util.SmartList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -36,16 +32,14 @@ import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrBinaryExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.types.TypeInferenceHelper; -import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; -import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.binaryCalculators.GrBinaryExpressionTypeCalculators; -import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.binaryCalculators.GrBinaryFacade; +import org.jetbrains.plugins.groovy.lang.psi.impl.statements.GrOperatorExpressionImpl; import java.util.List; /** * @author ilyas */ -public abstract class GrBinaryExpressionImpl extends GrExpressionImpl implements GrBinaryExpression { +public abstract class GrBinaryExpressionImpl extends GrOperatorExpressionImpl implements GrBinaryExpression { private static final ResolveCache.PolyVariantResolver RESOLVER = new ResolveCache.PolyVariantResolver() { @@ -89,55 +83,15 @@ public abstract class GrBinaryExpressionImpl extends GrExpressionImpl implements } }; - private static final Function TYPE_CALCULATOR = - expression -> GrBinaryExpressionTypeCalculators.getTypeCalculator(expression.getFacade()).fun(expression.getFacade()); - - private final GrBinaryFacade myFacade = new GrBinaryFacade() { - @NotNull - @Override - public GrExpression getLeftOperand() { - return GrBinaryExpressionImpl.this.getLeftOperand(); - } - - @Nullable - @Override - public GrExpression getRightOperand() { - return GrBinaryExpressionImpl.this.getRightOperand(); - } - - @NotNull - @Override - public IElementType getOperationTokenType() { - return GrBinaryExpressionImpl.this.getOperationTokenType(); - } - - @NotNull - @Override - public PsiElement getOperationToken() { - return GrBinaryExpressionImpl.this.getOperationToken(); - } - - @NotNull - @Override - public GroovyResolveResult[] multiResolve(boolean incompleteCode) { - return GrBinaryExpressionImpl.this.multiResolve(incompleteCode); - } - - @NotNull - @Override - public GrExpression getPsiElement() { - return GrBinaryExpressionImpl.this; - } - }; - @Nullable - protected PsiType getRightType() { + public PsiType getRightType() { final GrExpression rightOperand = getRightOperand(); return rightOperand == null ? null : rightOperand.getType(); } @Nullable - protected PsiType getLeftType() { + @Override + public PsiType getLeftType() { return getLeftOperand().getType(); } @@ -158,15 +112,6 @@ public abstract class GrBinaryExpressionImpl extends GrExpressionImpl implements return last instanceof GrExpression ? (GrExpression)last : null; } - @Override - @NotNull - public IElementType getOperationTokenType() { - final PsiElement child = getOperationToken(); - final ASTNode node = child.getNode(); - assert node != null; - return node.getElementType(); - } - @Override @NotNull public PsiElement getOperationToken() { @@ -184,67 +129,8 @@ public abstract class GrBinaryExpressionImpl extends GrExpressionImpl implements return TypeInferenceHelper.getCurrentContext().multiResolve(this, incompleteCode, RESOLVER); } - @Override - public PsiType getType() { - return TypeInferenceHelper.getCurrentContext().getExpressionType(this, TYPE_CALCULATOR); - } - - @Override - public PsiElement getElement() { - return this; - } - - @Override - public TextRange getRangeInElement() { - final PsiElement token = getOperationToken(); - final int offset = token.getStartOffsetInParent(); - return new TextRange(offset, offset + token.getTextLength()); - } - - @Override - public PsiElement resolve() { - return PsiImplUtil.extractUniqueElement(multiResolve(false)); - } - - @NotNull - @Override - public String getCanonicalText() { - return getText(); - } - - @Override - public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException { - throw new IncorrectOperationException("binary expression cannot be renamed"); - } - - @Override - public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException { - throw new IncorrectOperationException("binary expression cannot be bound to anything"); - } - - @Override - public boolean isReferenceTo(PsiElement element) { - return getManager().areElementsEquivalent(resolve(), element); - } - - @NotNull - @Override - public Object[] getVariants() { - return ArrayUtil.EMPTY_OBJECT_ARRAY; - } - - @Override - public boolean isSoft() { - return false; - } - @Override public PsiReference getReference() { return this; } - - private GrBinaryFacade getFacade() { - return myFacade; - } - } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrAssignTypeCalculator.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrAssignTypeCalculator.java index e7afcbab4d6b..86b7a33e18e1 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrAssignTypeCalculator.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrAssignTypeCalculator.java @@ -17,15 +17,16 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.binary import com.intellij.psi.PsiType; import com.intellij.util.Function; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrOperatorExpression; /** * Created by Max Medvedev on 12/20/13 */ -public class GrAssignTypeCalculator implements Function { +public class GrAssignTypeCalculator implements Function { public static final GrAssignTypeCalculator INSTANCE = new GrAssignTypeCalculator(); @Override - public PsiType fun(GrBinaryFacade expression) { - return GrBinaryExpressionUtil.getRightType(expression); + public PsiType fun(GrOperatorExpression expression) { + return expression.getRightType(); } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrBinaryExpressionTypeCalculator.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrBinaryExpressionTypeCalculator.java index a6657a9ede69..77f9c34942a6 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrBinaryExpressionTypeCalculator.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrBinaryExpressionTypeCalculator.java @@ -20,30 +20,23 @@ import com.intellij.util.Function; import com.intellij.util.NullableFunction; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrOperatorExpression; import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; /** * Created by Max Medvedev on 12/20/13 */ -public class GrBinaryExpressionTypeCalculator implements NullableFunction { - public static final Function INSTANCE = new GrBinaryExpressionTypeCalculator(); +public class GrBinaryExpressionTypeCalculator implements NullableFunction { + public static final Function INSTANCE = new GrBinaryExpressionTypeCalculator(); @Override @Nullable - public PsiType fun(GrBinaryFacade e) { + public PsiType fun(GrOperatorExpression e) { final GroovyResolveResult resolveResult = PsiImplUtil.extractUniqueResult(e.multiResolve(false)); if (resolveResult.getElement() != null) { - return ResolveUtil.extractReturnTypeFromCandidate(resolveResult, e.getPsiElement(), new PsiType[]{getRightType(e)}); + return ResolveUtil.extractReturnTypeFromCandidate(resolveResult, e, new PsiType[]{e.getRightType()}); } return null; } - - @Nullable - protected static PsiType getRightType(GrBinaryFacade e) { - final GrExpression rightOperand = e.getRightOperand(); - return rightOperand == null ? null : rightOperand.getType(); - } - } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrBinaryExpressionTypeCalculators.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrBinaryExpressionTypeCalculators.java index 7313b78573f4..d3a24e7781c5 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrBinaryExpressionTypeCalculators.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrBinaryExpressionTypeCalculators.java @@ -20,8 +20,10 @@ import com.intellij.psi.tree.IElementType; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrOperatorExpression; import java.util.Map; @@ -29,7 +31,7 @@ import java.util.Map; * Created by Max Medvedev on 12/20/13 */ public class GrBinaryExpressionTypeCalculators { - private static final Map> MAP = ContainerUtil.newLinkedHashMap(); + private static final Map> MAP = ContainerUtil.newLinkedHashMap(); static { @@ -83,10 +85,10 @@ public class GrBinaryExpressionTypeCalculators { MAP.put(GroovyTokenTypes.mASSIGN, GrAssignTypeCalculator.INSTANCE); } - @NotNull - public static Function getTypeCalculator(GrBinaryFacade e) { - final Function function = MAP.get(e.getOperationTokenType()); + @Nullable + public static PsiType computeType(@NotNull GrOperatorExpression e) { + final Function function = MAP.get(e.getOperationTokenType()); assert function != null : e.getOperationTokenType(); - return function; + return function.fun(e); } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrBinaryExpressionUtil.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrBinaryExpressionUtil.java index 9c4ddc4418ae..efd18cfe70d6 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrBinaryExpressionUtil.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrBinaryExpressionUtil.java @@ -17,8 +17,7 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.binary import com.intellij.psi.CommonClassNames; import com.intellij.psi.PsiType; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrOperatorExpression; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames; @@ -26,37 +25,27 @@ import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames; * Created by Max Medvedev on 12/20/13 */ public class GrBinaryExpressionUtil { - @Nullable - public static PsiType getRightType(GrBinaryFacade e) { - final GrExpression rightOperand = e.getRightOperand(); - return rightOperand == null ? null : rightOperand.getType(); - } - @Nullable - public static PsiType getLeftType(GrBinaryFacade e) { - return e.getLeftOperand().getType(); - } - - public static PsiType getDefaultNumericResultType(PsiType ltype, PsiType rtype, GrBinaryFacade e) { + public static PsiType getDefaultNumericResultType(PsiType ltype, PsiType rtype, GrOperatorExpression e) { if (isBigDecimal(ltype, rtype)) return createBigDecimal(e); if (isFloatOrDouble(ltype, rtype)) return createDouble(e); if (isLong(ltype, rtype)) return createLong(e); return createInteger(e); } - public static PsiType createDouble(GrBinaryFacade e) { + public static PsiType createDouble(GrOperatorExpression e) { return getTypeByFQName(CommonClassNames.JAVA_LANG_DOUBLE, e); } - public static PsiType createLong(GrBinaryFacade e) { + public static PsiType createLong(GrOperatorExpression e) { return getTypeByFQName(CommonClassNames.JAVA_LANG_LONG, e); } - public static PsiType createInteger(GrBinaryFacade e) { + public static PsiType createInteger(GrOperatorExpression e) { return getTypeByFQName(CommonClassNames.JAVA_LANG_INTEGER, e); } - public static PsiType createBigDecimal(GrBinaryFacade e) { + public static PsiType createBigDecimal(GrOperatorExpression e) { return getTypeByFQName(GroovyCommonClassNames.JAVA_MATH_BIG_DECIMAL, e); } @@ -73,7 +62,7 @@ public class GrBinaryExpressionUtil { return ltype.equalsToText(CommonClassNames.JAVA_LANG_LONG) || rtype.equalsToText(CommonClassNames.JAVA_LANG_LONG); } - public static PsiType getTypeByFQName(String fqn, GrBinaryFacade e) { - return TypesUtil.createTypeByFQClassName(fqn, e.getPsiElement()); + public static PsiType getTypeByFQName(String fqn, GrOperatorExpression e) { + return TypesUtil.createTypeByFQClassName(fqn, e); } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrBooleanExpressionTypeCalculator.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrBooleanExpressionTypeCalculator.java index ec7317139462..c245f6a2fa19 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrBooleanExpressionTypeCalculator.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrBooleanExpressionTypeCalculator.java @@ -18,15 +18,16 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.binary import com.intellij.psi.CommonClassNames; import com.intellij.psi.PsiType; import com.intellij.util.Function; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrOperatorExpression; /** * Created by Max Medvedev on 12/20/13 */ -public class GrBooleanExpressionTypeCalculator implements Function{ +public class GrBooleanExpressionTypeCalculator implements Function{ public static final GrBooleanExpressionTypeCalculator INSTANCE = new GrBooleanExpressionTypeCalculator(); @Override - public PsiType fun(GrBinaryFacade expression) { + public PsiType fun(GrOperatorExpression expression) { return GrBinaryExpressionUtil.getTypeByFQName(CommonClassNames.JAVA_LANG_BOOLEAN, expression); } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrDecimalBinaryExpressionTypeCalculator.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrDecimalBinaryExpressionTypeCalculator.java index 3e8bd73a1072..4a9e9c938386 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrDecimalBinaryExpressionTypeCalculator.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrDecimalBinaryExpressionTypeCalculator.java @@ -18,6 +18,7 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.binary import com.intellij.psi.PsiType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrOperatorExpression; /** * Created by Max Medvedev on 12/20/13 @@ -27,7 +28,7 @@ public class GrDecimalBinaryExpressionTypeCalculator extends GrNumericBinaryExpr @Nullable @Override - protected PsiType inferNumericType(@NotNull PsiType ltype, @NotNull PsiType rtype, GrBinaryFacade e) { + protected PsiType inferNumericType(@NotNull PsiType ltype, @NotNull PsiType rtype, GrOperatorExpression e) { if (GrBinaryExpressionUtil.isBigDecimal(ltype, rtype)) return null; if (GrBinaryExpressionUtil.isFloatOrDouble(ltype, rtype)) return null; if (GrBinaryExpressionUtil.isLong(ltype, rtype)) return GrBinaryExpressionUtil.createLong(e); diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrDivExpressionTypeCalculator.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrDivExpressionTypeCalculator.java index 7f6a72524323..3c1d4a35c9ea 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrDivExpressionTypeCalculator.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrDivExpressionTypeCalculator.java @@ -18,6 +18,7 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.binary import com.intellij.psi.PsiType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrOperatorExpression; /** * Created by Max Medvedev on 12/20/13 @@ -27,7 +28,7 @@ public class GrDivExpressionTypeCalculator extends GrNumericBinaryExpressionType @Nullable @Override - protected PsiType inferNumericType(@NotNull PsiType ltype, @NotNull PsiType rtype, GrBinaryFacade e) { + protected PsiType inferNumericType(@NotNull PsiType ltype, @NotNull PsiType rtype, GrOperatorExpression e) { if (GrBinaryExpressionUtil.isFloatOrDouble(ltype, rtype)) { return GrBinaryExpressionUtil.createDouble(e); } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrIntegerTypeCalculator.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrIntegerTypeCalculator.java index 852fa32131df..f8fca88b9b71 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrIntegerTypeCalculator.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrIntegerTypeCalculator.java @@ -18,15 +18,16 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.binary import com.intellij.psi.CommonClassNames; import com.intellij.psi.PsiType; import com.intellij.util.Function; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrOperatorExpression; /** * Created by Max Medvedev on 12/20/13 */ -public class GrIntegerTypeCalculator implements Function { +public class GrIntegerTypeCalculator implements Function { public static final GrIntegerTypeCalculator INSTANCE = new GrIntegerTypeCalculator(); @Override - public PsiType fun(GrBinaryFacade expression) { + public PsiType fun(GrOperatorExpression expression) { return GrBinaryExpressionUtil.getTypeByFQName(CommonClassNames.JAVA_LANG_INTEGER, expression); } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrMatcherTypeCalculator.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrMatcherTypeCalculator.java index c4769b30c144..7a2fde3d54c9 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrMatcherTypeCalculator.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrMatcherTypeCalculator.java @@ -17,16 +17,17 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.binary import com.intellij.psi.PsiType; import com.intellij.util.Function; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrOperatorExpression; import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames; /** * Created by Max Medvedev on 12/20/13 */ -public class GrMatcherTypeCalculator implements Function { +public class GrMatcherTypeCalculator implements Function { public static final GrMatcherTypeCalculator INSTANCE = new GrMatcherTypeCalculator(); @Override - public PsiType fun(GrBinaryFacade expression) { + public PsiType fun(GrOperatorExpression expression) { return GrBinaryExpressionUtil.getTypeByFQName(GroovyCommonClassNames.JAVA_UTIL_REGEX_MATCHER, expression); } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrMultiplicativeExpressionTypeCalculator.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrMultiplicativeExpressionTypeCalculator.java index f466b38d39aa..c1d5e38ee8da 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrMultiplicativeExpressionTypeCalculator.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrMultiplicativeExpressionTypeCalculator.java @@ -18,6 +18,7 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.binary import com.intellij.psi.PsiType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrOperatorExpression; /** * Created by Max Medvedev on 12/20/13 @@ -27,7 +28,7 @@ public class GrMultiplicativeExpressionTypeCalculator extends GrNumericBinaryExp @Nullable @Override - protected PsiType inferNumericType(@NotNull PsiType ltype, @NotNull PsiType rtype, GrBinaryFacade e) { + protected PsiType inferNumericType(@NotNull PsiType ltype, @NotNull PsiType rtype, GrOperatorExpression e) { return GrBinaryExpressionUtil.getDefaultNumericResultType(ltype, rtype, e); } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrNumericBinaryExpressionTypeCalculator.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrNumericBinaryExpressionTypeCalculator.java index ac0ff7d6acdd..2872c53d3581 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrNumericBinaryExpressionTypeCalculator.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrNumericBinaryExpressionTypeCalculator.java @@ -20,6 +20,7 @@ import com.intellij.util.NullableFunction; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrOperatorExpression; import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; @@ -28,31 +29,29 @@ import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; /** * Created by Max Medvedev on 12/20/13 */ -public class GrNumericBinaryExpressionTypeCalculator implements NullableFunction { +public class GrNumericBinaryExpressionTypeCalculator implements NullableFunction { public static final GrNumericBinaryExpressionTypeCalculator INSTANCE = new GrNumericBinaryExpressionTypeCalculator(); @Nullable @Override - public PsiType fun(GrBinaryFacade e) { + public PsiType fun(GrOperatorExpression e) { final GroovyResolveResult resolveResult = PsiImplUtil.extractUniqueResult(e.multiResolve(false)); if (resolveResult.isApplicable() && !PsiUtil.isDGMMethod(resolveResult.getElement())) { - return ResolveUtil.extractReturnTypeFromCandidate(resolveResult, e.getPsiElement(), new PsiType[]{GrBinaryExpressionUtil.getRightType(e)}); + return ResolveUtil.extractReturnTypeFromCandidate(resolveResult, e, new PsiType[]{e.getRightType()}); } - PsiType lType = GrBinaryExpressionUtil.getLeftType(e); - PsiType rType = GrBinaryExpressionUtil.getRightType(e); + PsiType lType = e.getLeftType(); + PsiType rType = e.getRightType(); if (TypesUtil.isNumericType(lType) && TypesUtil.isNumericType(rType)) { - assert lType != null; - assert rType != null; return inferNumericType(lType, rType, e); } - return ResolveUtil.extractReturnTypeFromCandidate(resolveResult, e.getPsiElement(), new PsiType[]{rType}); + return ResolveUtil.extractReturnTypeFromCandidate(resolveResult, e, new PsiType[]{rType}); } @Nullable - protected PsiType inferNumericType(@NotNull PsiType ltype, @NotNull PsiType rtype, GrBinaryFacade e) { + protected PsiType inferNumericType(@NotNull PsiType ltype, @NotNull PsiType rtype, GrOperatorExpression e) { return GrBinaryExpressionUtil.getDefaultNumericResultType(ltype, rtype, e); } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrRangeExpressionTypeCalculator.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrRangeExpressionTypeCalculator.java index 9cd2cfd27574..62c992def0c6 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrRangeExpressionTypeCalculator.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/binaryCalculators/GrRangeExpressionTypeCalculator.java @@ -17,6 +17,7 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.binary import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiType; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrOperatorExpression; import org.jetbrains.plugins.groovy.lang.psi.impl.GrRangeType; /** @@ -26,13 +27,13 @@ public class GrRangeExpressionTypeCalculator extends GrBinaryExpressionTypeCalcu public static final GrBinaryExpressionTypeCalculator INSTANCE = new GrRangeExpressionTypeCalculator(); @Override - public PsiType fun(GrBinaryFacade e) { + public PsiType fun(GrOperatorExpression e) { final PsiType type = super.fun(e); if (type != null) return type; - final PsiType ltype = GrBinaryExpressionUtil.getLeftType(e); - final PsiType rtype = GrBinaryExpressionUtil.getRightType(e); + final PsiType ltype = e.getLeftType(); + final PsiType rtype = e.getRightType(); - return new GrRangeType(e.getPsiElement().getResolveScope(), JavaPsiFacade.getInstance(e.getPsiElement().getProject()), ltype, rtype); + return new GrRangeType(e.getResolveScope(), JavaPsiFacade.getInstance(e.getProject()), ltype, rtype); } } From e9456b1606923873365629e9d917ae559a3efeb0 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Thu, 15 Dec 2016 17:39:11 +0300 Subject: [PATCH 205/558] there is no need of intellilang in pycharm edu --- python/educational-python/build/plugin-list.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/python/educational-python/build/plugin-list.txt b/python/educational-python/build/plugin-list.txt index c66002a3a9b4..c635ee001828 100644 --- a/python/educational-python/build/plugin-list.txt +++ b/python/educational-python/build/plugin-list.txt @@ -2,8 +2,6 @@ svn4idea git4idea remote-servers-git github -IntelliLang -IntelliLang-python student student-python course-creator From 5606cca1e0a683eaf7b6f0243af5b7f71b98e894 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Thu, 15 Dec 2016 18:08:08 +0300 Subject: [PATCH 206/558] [groovy] fix resolve of operator assignment LHS (IDEA-165470) --- .../GrAssignmentExpressionImpl.java | 12 ++++- .../plugins/groovy/lang/psi/util/PsiUtil.java | 17 ++++-- .../lang/resolve/ResolveOperatorTest.groovy | 53 +++++++++++++++++++ 3 files changed, 76 insertions(+), 6 deletions(-) create mode 100644 plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveOperatorTest.groovy diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrAssignmentExpressionImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrAssignmentExpressionImpl.java index 15bb02431b5d..26b01bcf38eb 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrAssignmentExpressionImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrAssignmentExpressionImpl.java @@ -25,12 +25,16 @@ import com.intellij.psi.impl.source.resolve.ResolveCache; import com.intellij.psi.scope.ElementClassHint; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.CachedValueProvider.Result; +import com.intellij.psi.util.CachedValuesManager; +import com.intellij.psi.util.PsiModificationTracker; import com.intellij.util.ConcurrencyUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.lexer.TokenSets; import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; @@ -151,7 +155,11 @@ public class GrAssignmentExpressionImpl extends GrOperatorExpressionImpl impleme @Nullable @Override public PsiType getLeftType() { - return getLValue().getType(); + GrExpression expression = CachedValuesManager.getCachedValue(this, () -> Result.create( + GroovyPsiElementFactory.getInstance(getProject()).createExpressionFromText(getLValue().getText(), getContext()), + PsiModificationTracker.MODIFICATION_COUNT + )); + return expression.getType(); } @Nullable @@ -178,7 +186,7 @@ public class GrAssignmentExpressionImpl extends GrOperatorExpressionImpl impleme lType = ((GrIndexProperty)lValue).getGetterType(); } else { - lType = lValue.getType(); + lType = assignmentExpression.getLeftType(); } if (lType == null) return GroovyResolveResult.EMPTY_ARRAY; diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java index 464a0452fa97..54e331f47894 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java @@ -280,13 +280,22 @@ public class PsiUtil { ); } } - else if (parent instanceof GrBinaryExpression || parent instanceof GrAssignmentExpression) { - GrExpression right = parent instanceof GrBinaryExpression - ? ((GrBinaryExpression)parent).getRightOperand() - : ((GrAssignmentExpression)parent).getRValue(); + else if (parent instanceof GrBinaryExpression) { + GrExpression right = ((GrBinaryExpression)parent).getRightOperand(); PsiType type = right != null ? right.getType() : null; return new PsiType[]{notNullizeType(type, nullAsBottom, parent)}; } + else if (parent instanceof GrAssignmentExpression) { + GrAssignmentExpression assignment = (GrAssignmentExpression)parent; + PsiType type; + if (TokenSets.ASSIGNMENTS_TO_OPERATORS.containsKey(assignment.getOperationTokenType())) { + type = assignment.getType(); + } + else { + type = assignment.getRightType(); + } + return new PsiType[]{notNullizeType(type, nullAsBottom, parent)}; + } return null; } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveOperatorTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveOperatorTest.groovy new file mode 100644 index 000000000000..6fd550dda2e7 --- /dev/null +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveOperatorTest.groovy @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2016 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.resolve + +import com.intellij.psi.PsiMethod +import com.intellij.testFramework.LightProjectDescriptor +import groovy.transform.CompileStatic +import org.jetbrains.plugins.groovy.GroovyLightProjectDescriptor +import org.jetbrains.plugins.groovy.LightGroovyTestCase +import org.jetbrains.plugins.groovy.lang.psi.GroovyFile +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression + +@CompileStatic +class ResolveOperatorTest extends LightGroovyTestCase { + + final LightProjectDescriptor projectDescriptor = GroovyLightProjectDescriptor.GROOVY_LATEST + + void "test plus assignment"() { + fixture.with { + addClass '''\ +public class Foo { + private java.util.Set numbers; + public java.util.Set getNumbers() {return null;} + public void setNumbers(java.util.Set files) {} +} +''' + def file = configureByText('_.groovy', '''\ +def f(Foo foo, Integer i) { + foo.numbers += i +} +''') as GroovyFile + def assignment = file.methods.first().block.statements.first() as GrAssignmentExpression + def lValue = assignment.LValue as GrReferenceExpression + def resolved = lValue.resolve() + assert resolved instanceof PsiMethod + assert ((PsiMethod)resolved).name == "setNumbers" + } + } +} From 5e676d70f735defd0e32f10f8c232268b51d28c7 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Thu, 15 Dec 2016 18:12:09 +0300 Subject: [PATCH 207/558] javac ast indices: do not check for up-to-date in tests --- .../CompilerReferenceServiceImpl.java | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java index beeb8d10cee2..7d6fa3c950d8 100644 --- a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java +++ b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java @@ -141,25 +141,27 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple myDirtyModulesHolder.installVFSListener(); - ApplicationManager.getApplication().executeOnPooledThread(() -> { - CompileScope projectCompileScope = compilerManager.createProjectCompileScope(myProject); - boolean isUpToDate = compilerManager.isUpToDate(projectCompileScope); - executeOnBuildThread(() -> { - Module[] modules = ReadAction.compute(() -> { - if (myProject.isDisposed()) return null; - return projectCompileScope.getAffectedModules(); + if (!ApplicationManager.getApplication().isUnitTestMode()) { + ApplicationManager.getApplication().executeOnPooledThread(() -> { + CompileScope projectCompileScope = compilerManager.createProjectCompileScope(myProject); + boolean isUpToDate = compilerManager.isUpToDate(projectCompileScope); + executeOnBuildThread(() -> { + Module[] modules = ReadAction.compute(() -> { + if (myProject.isDisposed()) return null; + return projectCompileScope.getAffectedModules(); + }); + if (modules == null) return; + if (isUpToDate) { + myDirtyModulesHolder.compilerActivityFinished(modules, Module.EMPTY_ARRAY); + myCompilationCount.increment(); + openReaderIfNeed(); + } + else { + myDirtyModulesHolder.compilerActivityFinished(Module.EMPTY_ARRAY, modules); + } }); - if (modules == null) return; - if (isUpToDate) { - myDirtyModulesHolder.compilerActivityFinished(modules, Module.EMPTY_ARRAY); - myCompilationCount.increment(); - openReaderIfNeed(); - } - else { - myDirtyModulesHolder.compilerActivityFinished(Module.EMPTY_ARRAY, modules); - } }); - }); + } } } From 480363b049b4ceb355dff6d6b97d3a85cf7069f5 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Thu, 15 Dec 2016 18:20:11 +0300 Subject: [PATCH 208/558] rewrite restrictions for location of a lightweight dialog in one place --- .../ui/impl/GlassPaneDialogWrapperPeer.java | 41 ++++++------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java b/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java index dce9da43eafc..f7dd575ac055 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java @@ -43,6 +43,7 @@ import com.intellij.openapi.wm.impl.IdeGlassPaneEx; import com.intellij.ui.FocusTrackback; import com.intellij.ui.ScreenUtil; import com.intellij.ui.components.JBLayeredPane; +import com.intellij.util.ui.JBInsets; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NonNls; @@ -123,7 +124,7 @@ public class GlassPaneDialogWrapperPeer extends DialogWrapperPeer implements Foc myWindowManager = (WindowManagerEx) WindowManager.getInstance(); } - Window owner = parent instanceof Window ? (Window) parent : (Window) SwingUtilities.getAncestorOfClass(Window.class, parent); + Window owner = UIUtil.getWindow(parent); if (!(owner instanceof Dialog) && !(owner instanceof Frame)) { owner = JOptionPane.getRootFrame(); } @@ -306,19 +307,7 @@ public class GlassPaneDialogWrapperPeer extends DialogWrapperPeer implements Foc SwingUtilities.convertPointFromScreen(_p, pane); - final Insets insets = myDialog.getInsets(); - - // todo: fix coords to include shadow (border) paddings - // todo: reimplement dragging in every client to calculate window position properly - int _x = _p.x - insets.left; - int _y = _p.y - insets.top; - - final Container container = myDialog.getTransparentPane(); - - _x = _x > 0 ? (_x + myDialog.getWidth() < container.getWidth() ? _x : container.getWidth() - myDialog.getWidth()) : 0; - _y = _y > 0 ? (_y + myDialog.getHeight() < container.getHeight() ? _y : container.getHeight() - myDialog.getHeight()) : 0; - - myDialog.setLocation(_x, _y); + myDialog.setLocation(_p.x, _p.y); } @Override @@ -570,7 +559,15 @@ public class GlassPaneDialogWrapperPeer extends DialogWrapperPeer implements Foc } @Override - public void setBounds(final int x, final int y, final int width, final int height) { + public void setBounds(int x, int y, int width, int height) { + final Container p = myTransparentPane; + if (p != null) { + Rectangle bounds = new Rectangle(p.getWidth() - width, p.getHeight() - height); + JBInsets.removeFrom(bounds, getInsets()); + + x = bounds.width < 0 ? bounds.width / 2 : Math.min(bounds.x + bounds.width, Math.max(bounds.x, x)); + y = bounds.height < 0 ? bounds.height / 2 : Math.min(bounds.y + bounds.height, Math.max(bounds.y, y)); + } super.setBounds(x, y, width, height); if (myShadowSize == null || !myShadowSize.equals(getSize())) { @@ -579,20 +576,6 @@ public class GlassPaneDialogWrapperPeer extends DialogWrapperPeer implements Foc } } - @Override - public void setLocation(final int x, final int y) { - final Container p = myTransparentPane; - if (p != null) { - final Dimension s = p.getSize(); - - final int _x = (int) (x + getWidth() > s.getWidth() ? s.getWidth() - getWidth() : x); - final int _y = (int) (y + getHeight() > s.getHeight() ? s.getHeight() - getHeight() : y); - super.setLocation(_x, _y); - } else { - super.setLocation(x, y); - } - } - private void createShadow() { if (!RemoteDesktopDetector.isRemoteSession() && !JBUI.isHiDPI()) { shadow = ShadowBorderPainter.createShadow(this, getWidth(), getHeight()); From 8eebe1a76b55a5862c0f0d03348b42c83539dd6d Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Thu, 15 Dec 2016 18:21:14 +0300 Subject: [PATCH 209/558] use shared implementation for moving components instead of custom implementation --- .../openapi/progress/util/ProgressDialog.java | 39 +++++-------------- 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressDialog.java b/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressDialog.java index 1bfefde448d2..cb130518dc37 100644 --- a/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressDialog.java +++ b/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressDialog.java @@ -20,6 +20,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.DialogWrapperDialog; import com.intellij.openapi.ui.DialogWrapperPeer; import com.intellij.openapi.ui.impl.DialogWrapperPeerImpl; import com.intellij.openapi.ui.impl.FocusTrackbackProvider; @@ -30,7 +31,7 @@ import com.intellij.openapi.wm.ex.WindowManagerEx; import com.intellij.ui.FocusTrackback; import com.intellij.ui.PopupBorder; import com.intellij.ui.TitlePanel; -import com.intellij.ui.awt.RelativePoint; +import com.intellij.ui.WindowMoveListener; import com.intellij.ui.components.JBLabel; import com.intellij.util.Alarm; import com.intellij.util.ui.JBUI; @@ -103,7 +104,6 @@ class ProgressDialog implements Disposable { private JPanel myInnerPanel; DialogWrapper myPopup; private final Window myParentWindow; - private Point myLastClicked; public ProgressDialog(ProgressWindow progressWindow, boolean shouldShowBackground, Project project, String cancelText) { myProgressWindow = progressWindow; @@ -118,9 +118,7 @@ class ProgressDialog implements Disposable { public ProgressDialog(ProgressWindow progressWindow, boolean shouldShowBackground, Component parent, String cancelText) { myProgressWindow = progressWindow; - myParentWindow = parent instanceof Window - ? (Window)parent - : (Window)SwingUtilities.getAncestorOfClass(Window.class, parent); + myParentWindow = UIUtil.getWindow(parent); initDialog(shouldShowBackground, cancelText); } @@ -154,33 +152,14 @@ class ProgressDialog implements Disposable { createCenterPanel(); myTitlePanel.setActive(true); - myTitlePanel.addMouseListener(new MouseAdapter() { + WindowMoveListener moveListener = new WindowMoveListener(myTitlePanel) { @Override - public void mousePressed(@NotNull MouseEvent e) { - final Point titleOffset = RelativePoint.getNorthWestOf(myTitlePanel).getScreenPoint(); - myLastClicked = new RelativePoint(e).getScreenPoint(); - myLastClicked.x -= titleOffset.x; - myLastClicked.y -= titleOffset.y; + protected Component getView(Component component) { + return SwingUtilities.getAncestorOfClass(DialogWrapperDialog.class, component); } - }); - - myTitlePanel.addMouseMotionListener(new MouseMotionAdapter() { - @Override - public void mouseDragged(@NotNull MouseEvent e) { - if (myLastClicked == null) { - return; - } - // use absolute location, because the relative point was set when creating the event, and the dialog might have been moved since that - // (e.g. if processing of previous events was delayed by something) - final Point draggedTo = e.getLocationOnScreen(); - draggedTo.x -= myLastClicked.x; - draggedTo.y -= myLastClicked.y; - - if (myPopup != null) { - myPopup.setLocation(draggedTo); - } - } - }); + }; + myTitlePanel.addMouseListener(moveListener); + myTitlePanel.addMouseMotionListener(moveListener); } @Override From 31a31c8e42597141e77f64d3dac49f69a9b86135 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Thu, 15 Dec 2016 14:20:27 +0100 Subject: [PATCH 210/558] fix EditorColorSchemeTest test data (no way to avoid defaults) --- .../editor/colors/EditorColorSchemeTest.kt | 1 + .../testFramework/assertions/PathAssertEx.kt | 26 ++++++++++--------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/colors/EditorColorSchemeTest.kt b/platform/platform-tests/testSrc/com/intellij/openapi/editor/colors/EditorColorSchemeTest.kt index 723782bb029b..118143a98d6a 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/editor/colors/EditorColorSchemeTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/colors/EditorColorSchemeTest.kt @@ -61,6 +61,7 @@ class EditorColorSchemeTest { // JAVA_NUMBER is removed - see isParentOverwritingInheritance assertThat(removeSchemeMetaInfo(schemeFile.readText())).isEqualTo(""" + """.trimIndent()) diff --git a/platform/testFramework/extensions/src/com/intellij/testFramework/assertions/PathAssertEx.kt b/platform/testFramework/extensions/src/com/intellij/testFramework/assertions/PathAssertEx.kt index 6a38b684f069..1ba5a5235f5c 100644 --- a/platform/testFramework/extensions/src/com/intellij/testFramework/assertions/PathAssertEx.kt +++ b/platform/testFramework/extensions/src/com/intellij/testFramework/assertions/PathAssertEx.kt @@ -45,19 +45,21 @@ class PathAssertEx(actual: Path?) : PathAssert(actual) { fun hasChildren(vararg names: String) { paths.assertIsDirectory(info, actual) - Iterables(ComparatorBasedComparisonStrategy( - Comparator { o1, o2 -> - if (o1 is Path && o2 is Path) { - o1.compareTo(o2) - } - else if (o1 is String && o2 is String) { - o1.compareTo(o2) - } - else { - if ((o1 as Path).endsWith(o2 as String)) 0 else -1 - } + Iterables(ComparatorBasedComparisonStrategy(Comparator { o1, o2 -> + if (o1 is Path && o2 is Path) { + o1.compareTo(o2) } - )).assertContainsOnly(info, Files.newDirectoryStream(actual).use { it.toList() }, names) + else if (o1 is String && o2 is String) { + o1.compareTo(o2) + } + else if (o1 is String) { + if ((o2 as Path).endsWith(o1)) 0 else -1 + } + else { + if ((o1 as Path).endsWith(o2 as String)) 0 else -1 + } + })) + .assertContainsOnly(info, Files.newDirectoryStream(actual).use { it.toList() }, names) } } From 8dd7582c01ed2f53ca38d1308aef6fc570e1d617 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Thu, 15 Dec 2016 15:36:51 +0100 Subject: [PATCH 211/558] =?UTF-8?q?speed=20up=20save=20=E2=80=94=20track?= =?UTF-8?q?=20inspection=20scheme=20state?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ex/InspectionProfileTest.java | 9 +- .../codeInspection/ex/InspectionSchemeTest.kt | 6 +- .../codeInspection/InspectionProfile.java | 6 +- .../codeInspection/ModifiableModel.java | 9 -- .../ex/GlobalInspectionContextBase.java | 2 +- .../codeInspection/ex/InspectionProfile.kt | 26 +++++ .../ex/InspectionProfileImpl.java | 105 +++++++----------- .../ex/InspectionProfileModifiableModel.kt | 7 ++ .../ex/InspectionProfileWrapper.java | 5 - .../intellij/codeInspection/ex/ToolsImpl.java | 9 ++ .../BaseInspectionProfileManager.kt | 3 +- .../ProjectInspectionProfileManager.kt | 8 +- .../src/SchemeManagerImpl.kt | 2 +- .../DaemonCodeAnalyzerSettingsImpl.java | 3 +- .../daemon/impl/DaemonListeners.java | 21 ++-- .../actions/RunInspectionIntention.java | 7 +- .../ApplicationInspectionProfileManager.java | 29 ++--- .../header/InspectionToolsConfigurable.java | 2 +- .../com/intellij/testFramework/inspections.kt | 7 +- .../ig/fixes/SuppressForTestsScopeFix.java | 3 +- 20 files changed, 127 insertions(+), 142 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionProfileTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionProfileTest.java index be7dee7d7199..2a4c24924f3a 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionProfileTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionProfileTest.java @@ -84,7 +84,7 @@ public class InspectionProfileTest extends LightIdeaTestCase { } private static InspectionProfileImpl createProfile() { - return new InspectionProfileImpl(PROFILE, InspectionToolRegistrar.getInstance(), InspectionProfileImpl.getBaseProfile()); + return createProfile(InspectionProfileKt.getBASE_PROFILE()); } private static InspectionProfileImpl createProfile(@NotNull InspectionProfileImpl base) { @@ -100,8 +100,7 @@ public class InspectionProfileTest extends LightIdeaTestCase { try { //normally on open project profile wrappers are init for both managers updateProfile(profileManager, localProfile); - InspectionProfileImpl profile = new InspectionProfileImpl(PROFILE, InspectionToolRegistrar.getInstance(), projectProfileManager, - InspectionProfileImpl.getBaseProfile(), null); + InspectionProfileImpl profile = new InspectionProfileImpl(PROFILE, InspectionToolRegistrar.getInstance(), projectProfileManager, null); updateProfile(projectProfileManager, profile); projectProfileManager.setRootProfile(profile.getName()); @@ -550,7 +549,7 @@ public class InspectionProfileTest extends LightIdeaTestCase { } private static InspectionProfileImpl createProfile(@NotNull InspectionToolRegistrar registrar) { - InspectionProfileImpl base = new InspectionProfileImpl("Base", registrar); + InspectionProfileImpl base = new InspectionProfileImpl("Base", registrar, (InspectionProfileImpl)null); return new InspectionProfileImpl("Foo", registrar, base); } @@ -589,7 +588,7 @@ public class InspectionProfileTest extends LightIdeaTestCase { } public void testDoNotInstantiateOnSave() throws Exception { - InspectionProfileImpl profile = new InspectionProfileImpl("profile", InspectionToolRegistrar.getInstance(), InspectionProfileImpl.getBaseProfile()); + InspectionProfileImpl profile = new InspectionProfileImpl("profile", InspectionToolRegistrar.getInstance(), InspectionProfileKt.getBASE_PROFILE()); assertEquals(0, countInitializedTools(profile)); InspectionToolWrapper[] toolWrappers = profile.getInspectionTools(null); assertTrue(toolWrappers.length > 0); diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionSchemeTest.kt b/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionSchemeTest.kt index 752c2920558d..69a67666220e 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionSchemeTest.kt +++ b/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionSchemeTest.kt @@ -17,8 +17,9 @@ package com.intellij.codeInspection.ex import com.intellij.configurationStore.SchemeManagerFactoryBase import com.intellij.openapi.application.ApplicationManager -import com.intellij.testFramework.rules.InMemoryFsRule +import com.intellij.openapi.options.SchemeState import com.intellij.testFramework.ProjectRule +import com.intellij.testFramework.rules.InMemoryFsRule import com.intellij.testFramework.runInInitMode import com.intellij.util.io.readText import com.intellij.util.io.write @@ -53,12 +54,15 @@ class InspectionSchemeTest { assertThat(profileManager.profiles).hasSize(1) val scheme = profileManager.profiles.first() + assertThat(scheme.schemeState).isEqualTo(SchemeState.UNCHANGED) assertThat(scheme.name).isEqualTo("Bar") runInInitMode { scheme.initInspectionTools(null) } schemeManagerFactory.save() + assertThat(scheme.schemeState).isEqualTo(SchemeState.UNCHANGED) + assertThat(schemeFile.readText()).isEqualTo(schemeData) profileManager.profiles diff --git a/platform/analysis-api/src/com/intellij/codeInspection/InspectionProfile.java b/platform/analysis-api/src/com/intellij/codeInspection/InspectionProfile.java index d9968db36ede..2a05ae09933c 100644 --- a/platform/analysis-api/src/com/intellij/codeInspection/InspectionProfile.java +++ b/platform/analysis-api/src/com/intellij/codeInspection/InspectionProfile.java @@ -75,8 +75,6 @@ public interface InspectionProfile extends Comparable { @NotNull InspectionToolWrapper[] getInspectionTools(@Nullable PsiElement element); - void cleanup(@NotNull Project project); - boolean isToolEnabled(@Nullable HighlightDisplayKey key, PsiElement element); boolean isToolEnabled(@Nullable HighlightDisplayKey key); @@ -84,7 +82,7 @@ public interface InspectionProfile extends Comparable { boolean isExecutable(Project project); /** - * @see {@link ModifiableModel#setSingleTool(String)} + * @see {@link InspectionProfileImpl#setSingleTool(String)} * * @return tool short name when inspection profile corresponds to synthetic profile for single inspection run */ @@ -94,8 +92,6 @@ public interface InspectionProfile extends Comparable { @NotNull String getDisplayName(); - void scopesChanged(); - @NotNull List getAllEnabledInspectionTools(Project project); } diff --git a/platform/analysis-api/src/com/intellij/codeInspection/ModifiableModel.java b/platform/analysis-api/src/com/intellij/codeInspection/ModifiableModel.java index 54181b862648..f12623fad0e2 100644 --- a/platform/analysis-api/src/com/intellij/codeInspection/ModifiableModel.java +++ b/platform/analysis-api/src/com/intellij/codeInspection/ModifiableModel.java @@ -15,8 +15,6 @@ */ package com.intellij.codeInspection; -import com.intellij.codeHighlighting.HighlightDisplayLevel; -import com.intellij.codeInsight.daemon.HighlightDisplayKey; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.psi.search.scope.packageSet.NamedScope; @@ -26,13 +24,6 @@ import org.jetbrains.annotations.Nullable; public interface ModifiableModel extends InspectionProfile { void enableTool(@NotNull String inspectionTool, NamedScope namedScope, Project project); - void setErrorLevel(HighlightDisplayKey key, @NotNull HighlightDisplayLevel level, Project project); - - /** - * @see InspectionProfile#getSingleTool() - */ - void setSingleTool(@NotNull String toolShortName); - void disableTool(@NotNull String toolId, @NotNull PsiElement element); void disableTool(@NotNull String inspectionTool, @Nullable Project project); diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextBase.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextBase.java index 0475043d95e9..1b0837a49172 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextBase.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextBase.java @@ -329,7 +329,7 @@ public class GlobalInspectionContextBase extends UserDataHolderBase implements G protected List getUsedTools() { InspectionProfileImpl profile = getCurrentProfile(); List tools = profile.getAllEnabledInspectionTools(myProject); - Set dependentTools = new LinkedHashSet<>(); + Set> dependentTools = new LinkedHashSet<>(); for (Tools tool : tools) { profile.collectDependentInspections(tool.getTool(), dependentTools, getProject()); } diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfile.kt b/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfile.kt index daa91cf11a6e..969791048515 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfile.kt +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfile.kt @@ -19,14 +19,26 @@ import com.intellij.codeInspection.InspectionProfile import com.intellij.configurationStore.SerializableScheme import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.PathMacroManager +import com.intellij.openapi.options.SchemeState +import com.intellij.openapi.project.Project import com.intellij.profile.ProfileEx import com.intellij.profile.codeInspection.BaseInspectionProfileManager +import com.intellij.profile.codeInspection.InspectionProfileManager import com.intellij.profile.codeInspection.ProjectInspectionProfileManager import com.intellij.util.xmlb.annotations.Transient +const val DEFAULT_PROFILE_NAME = "Default" +val BASE_PROFILE by lazy { InspectionProfileImpl(DEFAULT_PROFILE_NAME) } + abstract class NewInspectionProfile(name: String, private var profileManager: BaseInspectionProfileManager) : ProfileEx(name), InspectionProfile, SerializableScheme { private var isProjectLevel: Boolean = false + @JvmField + @Transient + internal var schemeState: SchemeState? = null + + override fun getSchemeState() = schemeState + @Transient fun isProjectLevel() = isProjectLevel @@ -46,4 +58,18 @@ abstract class NewInspectionProfile(name: String, private var profileManager: Ba val profileManager = profileManager return PathMacroManager.getInstance((profileManager as? ProjectInspectionProfileManager)?.project ?: ApplicationManager.getApplication()) } + + override fun toString() = name + + override fun equals(other: Any?) = super.equals(other) && (other as NewInspectionProfile).profileManager === profileManager +} + +fun createSimple(name: String, project: Project, toolWrappers: List>): InspectionProfileImpl { + val profile = InspectionProfileImpl(name, object : InspectionToolRegistrar() { + override fun createTools() = toolWrappers + }, InspectionProfileManager.getInstance() as BaseInspectionProfileManager) + for (toolWrapper in toolWrappers) { + profile.enableTool(toolWrapper.shortName, project) + } + return profile } \ No newline at end of file diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java index 0595f94534aa..9140a1847240 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java @@ -23,9 +23,11 @@ import com.intellij.configurationStore.SchemeDataHolder; import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.options.SchemeState; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.*; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.profile.codeInspection.BaseInspectionProfileManager; import com.intellij.profile.codeInspection.InspectionProfileManager; import com.intellij.profile.codeInspection.ProjectInspectionProfileManager; @@ -60,7 +62,6 @@ public class InspectionProfileImpl extends NewInspectionProfile { @NonNls private static final String VALID_VERSION = "1.0"; @NonNls private static final String VERSION_TAG = "version"; @NonNls private static final String USED_LEVELS = "used_levels"; - public static final String DEFAULT_PROFILE_NAME = "Default"; @TestOnly public static boolean INIT_INSPECTIONS = false; protected final InspectionToolRegistrar myRegistrar; @@ -70,7 +71,7 @@ public class InspectionProfileImpl extends NewInspectionProfile { @Attribute("is_locked") protected boolean myLockedProfile; protected final InspectionProfileImpl myBaseProfile; - private volatile String myToolShortName = null; + private volatile String myToolShortName; private String[] myScopesOrder; private String myDescription; private volatile boolean myInitialized; @@ -82,15 +83,11 @@ public class InspectionProfileImpl extends NewInspectionProfile { public InspectionProfileImpl(@NotNull String profileName, @NotNull InspectionToolRegistrar registrar, @NotNull BaseInspectionProfileManager profileManager) { - this(profileName, registrar, profileManager, getBaseProfile(), null); + this(profileName, registrar, profileManager, InspectionProfileKt.getBASE_PROFILE(), null); } - public InspectionProfileImpl(@NotNull @NonNls String profileName) { - this(profileName, InspectionToolRegistrar.getInstance()); - } - - public InspectionProfileImpl(@NotNull String profileName, @NotNull InspectionToolRegistrar registrar) { - this(profileName, registrar, (BaseInspectionProfileManager)InspectionProfileManager.getInstance(), null, null); + public InspectionProfileImpl(@NotNull String profileName) { + this(profileName, InspectionToolRegistrar.getInstance(), (BaseInspectionProfileManager)InspectionProfileManager.getInstance(), null, null); } public InspectionProfileImpl(@NotNull String profileName, @@ -109,30 +106,16 @@ public class InspectionProfileImpl extends NewInspectionProfile { myRegistrar = registrar; myBaseProfile = baseProfile; myDataHolder = dataHolder; + if (dataHolder != null) { + schemeState = SchemeState.UNCHANGED; + } } public InspectionProfileImpl(@NotNull String profileName, @NotNull InspectionToolRegistrar registrar, @NotNull BaseInspectionProfileManager profileManager, @Nullable SchemeDataHolder dataHolder) { - this(profileName, registrar, profileManager, getBaseProfile(), dataHolder); - } - - @NotNull - public static InspectionProfileImpl createSimple(@NotNull String name, - @NotNull Project project, - @NotNull List toolWrappers) { - InspectionProfileImpl profile = new InspectionProfileImpl(name, new InspectionToolRegistrar() { - @NotNull - @Override - public List createTools() { - return toolWrappers; - } - }, (BaseInspectionProfileManager)InspectionProfileManager.getInstance()); - for (InspectionToolWrapper toolWrapper : toolWrappers) { - profile.enableTool(toolWrapper.getShortName(), project); - } - return profile; + this(profileName, registrar, profileManager, InspectionProfileKt.getBASE_PROFILE(), dataHolder); } private static boolean toolSettingsAreEqual(@NotNull String toolName, @NotNull InspectionProfileImpl profile1, @NotNull InspectionProfileImpl profile2) { @@ -152,19 +135,6 @@ public class InspectionProfileImpl extends NewInspectionProfile { return inspectionTool; } - @NotNull - public static InspectionProfileImpl getBaseProfile() { - return InspectionProfileImplHolder.DEFAULT_PROFILE; - } - - public void resetToEmpty(Project project) { - initInspectionTools(project); - final InspectionToolWrapper[] profileEntries = getInspectionTools(null); - for (InspectionToolWrapper toolWrapper : profileEntries) { - disableTool(toolWrapper.getShortName(), project); - } - } - @Override public HighlightDisplayLevel getErrorLevel(@NotNull HighlightDisplayKey inspectionToolKey, PsiElement element) { Project project = element == null ? null : element.getProject(); @@ -233,6 +203,11 @@ public class InspectionProfileImpl extends NewInspectionProfile { @Override @NotNull public Element writeScheme() { + return writeScheme(true); + } + + @NotNull + public Element writeScheme(boolean setSchemeStateToUnchanged) { if (myDataHolder != null) { return myDataHolder.read(); } @@ -244,6 +219,10 @@ public class InspectionProfileImpl extends NewInspectionProfile { if (isProjectLevel() && ProjectKt.isDirectoryBased(((ProjectInspectionProfileManager)getProfileManager()).getProject())) { return new Element("component").setAttribute("name", "InspectionProjectProfileManager").addContent(element); } + + if (setSchemeStateToUnchanged) { + schemeState = SchemeState.UNCHANGED; + } return element; } @@ -320,13 +299,12 @@ public class InspectionProfileImpl extends NewInspectionProfile { } public void collectDependentInspections(@NotNull InspectionToolWrapper toolWrapper, - @NotNull Set dependentEntries, + @NotNull Set> dependentEntries, Project project) { String mainToolId = toolWrapper.getMainToolId(); if (mainToolId != null) { InspectionToolWrapper dependentEntryWrapper = getInspectionTool(mainToolId, project); - if (dependentEntryWrapper == null) { LOG.error("Can't find main tool: '" + mainToolId+"' which was specified in "+toolWrapper); return; @@ -423,11 +401,15 @@ public class InspectionProfileImpl extends NewInspectionProfile { return getName(); } - @Override public void scopesChanged() { - for (ScopeToolState toolState : getAllTools(null)) { - toolState.scopesChanged(); + if (!myInitialized) { + return; } + + for (ToolsImpl tools : myTools.values()) { + tools.scopesChanged(); + } + getProfileManager().fireProfileChanged(this); } @@ -438,6 +420,7 @@ public class InspectionProfileImpl extends NewInspectionProfile { public void lockProfile(boolean isLocked) { myLockedProfile = isLocked; + schemeState = SchemeState.POSSIBLY_CHANGED; } @Override @@ -472,6 +455,7 @@ public class InspectionProfileImpl extends NewInspectionProfile { for (String toolId : toolIds) { getTools(toolId, project).setDefaultEnabled(false); } + schemeState = SchemeState.POSSIBLY_CHANGED; } @NotNull @@ -483,6 +467,7 @@ public class InspectionProfileImpl extends NewInspectionProfile { for (final String toolId : toolIds) { getTools(toolId, project).setDefaultEnabled(true); } + schemeState = SchemeState.POSSIBLY_CHANGED; } public boolean wasInitialized() { @@ -645,6 +630,7 @@ public class InspectionProfileImpl extends NewInspectionProfile { public void setScopesOrder(String[] scopesOrder) { myScopesOrder = scopesOrder; + schemeState = SchemeState.POSSIBLY_CHANGED; } private HighlightDisplayLevel getErrorLevel(@NotNull HighlightDisplayKey key, @Nullable Project project) { @@ -659,7 +645,6 @@ public class InspectionProfileImpl extends NewInspectionProfile { return new InspectionProfileModifiableModel(this); } - @Override public void cleanup(@NotNull Project project) { if (!myInitialized) { return; @@ -678,10 +663,12 @@ public class InspectionProfileImpl extends NewInspectionProfile { if (tools.getNonDefaultTools() == null) { tools.getDefaultState().setEnabled(true); } + schemeState = SchemeState.POSSIBLY_CHANGED; } public void enableTool(@NotNull String inspectionTool, NamedScope namedScope, Project project) { getTools(inspectionTool, project).enableTool(namedScope, project); + schemeState = SchemeState.POSSIBLY_CHANGED; } public void enableTools(@NotNull List inspectionTools, NamedScope namedScope, Project project) { @@ -694,6 +681,7 @@ public class InspectionProfileImpl extends NewInspectionProfile { for (String inspectionTool : inspectionTools) { getTools(inspectionTool, project).disableTool(namedScope, project); } + schemeState = SchemeState.POSSIBLY_CHANGED; } public void disableTool(@NotNull String inspectionTool, @Nullable Project project) { @@ -702,10 +690,12 @@ public class InspectionProfileImpl extends NewInspectionProfile { if (tools.getNonDefaultTools() == null) { tools.getDefaultState().setEnabled(false); } + schemeState = SchemeState.POSSIBLY_CHANGED; } public void setErrorLevel(@NotNull HighlightDisplayKey key, @NotNull HighlightDisplayLevel level, Project project) { getTools(key.toString(), project).setLevel(level); + schemeState = SchemeState.POSSIBLY_CHANGED; } @Override @@ -736,8 +726,9 @@ public class InspectionProfileImpl extends NewInspectionProfile { return myDescription; } - public void setDescription(String description) { - myDescription = description; + public void setDescription(@Nullable String description) { + myDescription = StringUtil.nullize(description); + schemeState = SchemeState.POSSIBLY_CHANGED; } public void convert(@NotNull Element element, @NotNull Project project) { @@ -830,6 +821,7 @@ public class InspectionProfileImpl extends NewInspectionProfile { public void removeScope(@NotNull String toolId, @NotNull String scopeName, Project project) { getTools(toolId, project).removeScope(scopeName); + schemeState = SchemeState.POSSIBLY_CHANGED; } public void removeScopes(@NotNull List toolIds, @NotNull String scopeName, Project project) { @@ -865,6 +857,7 @@ public class InspectionProfileImpl extends NewInspectionProfile { public void profileChanged() { myChangedToolNames = null; + schemeState = SchemeState.POSSIBLY_CHANGED; } @NotNull @@ -884,6 +877,7 @@ public class InspectionProfileImpl extends NewInspectionProfile { public void setErrorLevel(@NotNull HighlightDisplayKey key, @NotNull HighlightDisplayLevel level, String scopeName, Project project) { getTools(key.toString(), project).setLevel(level, scopeName, project); + schemeState = SchemeState.POSSIBLY_CHANGED; } public void setErrorLevel(@NotNull List keys, @NotNull HighlightDisplayLevel level, String scopeName, Project project) { @@ -902,19 +896,4 @@ public class InspectionProfileImpl extends NewInspectionProfile { enableTool(entry.getShortName(), project); } } - - @Override - @NotNull - public String toString() { - return getName(); - } - - @Override - public boolean equals(Object o) { - return super.equals(o) && ((InspectionProfileImpl)o).getProfileManager() == getProfileManager(); - } - - private static class InspectionProfileImplHolder { - private static final InspectionProfileImpl DEFAULT_PROFILE = new InspectionProfileImpl(DEFAULT_PROFILE_NAME); - } } diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileModifiableModel.kt b/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileModifiableModel.kt index 3b9cbb2f9dae..d048a06aa6df 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileModifiableModel.kt +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileModifiableModel.kt @@ -98,6 +98,13 @@ open class InspectionProfileModifiableModel(val source: InspectionProfileImpl) : modified = false } + fun resetToEmpty(project: Project) { + initInspectionTools(project) + for (toolWrapper in getInspectionTools(null)) { + disableTool(toolWrapper.shortName, project) + } + } + private fun InspectionProfileImpl.commit(model: InspectionProfileImpl) { name = model.name description = model.description diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileWrapper.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileWrapper.java index c4352c1c6be1..0212a8cf962b 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileWrapper.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileWrapper.java @@ -20,7 +20,6 @@ import com.intellij.codeInsight.daemon.HighlightDisplayKey; import com.intellij.codeInspection.InspectionProfileEntry; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.psi.PsiElement; import gnu.trove.THashSet; @@ -81,10 +80,6 @@ public class InspectionProfileWrapper { return myProfile.getInspectionTool(shortName, element); } - public void cleanup(@NotNull Project project) { - myProfile.cleanup(project); - } - @NotNull public InspectionProfileImpl getInspectionProfile() { return myProfile; diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/ToolsImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/ToolsImpl.java index e3c5b24204ef..48abc015ce7e 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/ToolsImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/ToolsImpl.java @@ -134,6 +134,15 @@ public class ToolsImpl implements Tools { } } + public void scopesChanged() { + if (myTools != null) { + for (ScopeToolState tool : myTools) { + tool.scopesChanged(); + } + } + myDefaultState.scopesChanged(); + } + public void writeExternal(@NotNull Element inspectionElement) { if (myTools != null) { for (ScopeToolState state : myTools) { diff --git a/platform/analysis-impl/src/com/intellij/profile/codeInspection/BaseInspectionProfileManager.kt b/platform/analysis-impl/src/com/intellij/profile/codeInspection/BaseInspectionProfileManager.kt index a17c5301a851..1a4126dc26ce 100644 --- a/platform/analysis-impl/src/com/intellij/profile/codeInspection/BaseInspectionProfileManager.kt +++ b/platform/analysis-impl/src/com/intellij/profile/codeInspection/BaseInspectionProfileManager.kt @@ -16,7 +16,6 @@ package com.intellij.profile.codeInspection import com.intellij.codeInsight.daemon.impl.SeverityRegistrar -import com.intellij.codeInspection.InspectionProfile import com.intellij.codeInspection.ex.InspectionProfileImpl import com.intellij.configurationStore.LazySchemeProcessor import com.intellij.openapi.diagnostic.Logger @@ -59,7 +58,7 @@ abstract class BaseInspectionProfileManager(messageBus: MessageBus) : Inspectio } } - open protected fun schemeRemoved(scheme: InspectionProfile) { + open protected fun schemeRemoved(scheme: InspectionProfileImpl) { } abstract fun fireProfileChanged(profile: InspectionProfileImpl) diff --git a/platform/analysis-impl/src/com/intellij/profile/codeInspection/ProjectInspectionProfileManager.kt b/platform/analysis-impl/src/com/intellij/profile/codeInspection/ProjectInspectionProfileManager.kt index 76a230b7d532..025cef72ada2 100644 --- a/platform/analysis-impl/src/com/intellij/profile/codeInspection/ProjectInspectionProfileManager.kt +++ b/platform/analysis-impl/src/com/intellij/profile/codeInspection/ProjectInspectionProfileManager.kt @@ -98,8 +98,7 @@ class ProjectInspectionProfileManager(val project: Project, name: String, attributeProvider: Function, isBundled: Boolean): InspectionProfileImpl { - val profile = InspectionProfileImpl(name, InspectionToolRegistrar.getInstance(), this@ProjectInspectionProfileManager, - InspectionProfileImpl.getBaseProfile(), dataHolder) + val profile = InspectionProfileImpl(name, InspectionToolRegistrar.getInstance(), this@ProjectInspectionProfileManager, dataHolder) profile.isProjectLevel = true return profile } @@ -165,7 +164,7 @@ class ProjectInspectionProfileManager(val project: Project, fun isCurrentProfileInitialized() = currentProfile.wasInitialized() - override fun schemeRemoved(scheme: InspectionProfile) { + override fun schemeRemoved(scheme: InspectionProfileImpl) { scheme.cleanup(project) } @@ -293,8 +292,7 @@ class ProjectInspectionProfileManager(val project: Project, if (currentScheme == null) { currentScheme = schemeManager.allSchemes.firstOrNull() if (currentScheme == null) { - currentScheme = InspectionProfileImpl(PROJECT_DEFAULT_PROFILE_NAME, InspectionToolRegistrar.getInstance(), this, - InspectionProfileImpl.getBaseProfile(), null) + currentScheme = InspectionProfileImpl(PROJECT_DEFAULT_PROFILE_NAME, InspectionToolRegistrar.getInstance(), this) currentScheme.copyFrom(applicationProfileManager.currentProfile) currentScheme.isProjectLevel = true currentScheme.name = PROJECT_DEFAULT_PROFILE_NAME diff --git a/platform/configuration-store-impl/src/SchemeManagerImpl.kt b/platform/configuration-store-impl/src/SchemeManagerImpl.kt index 82d8c1d787a0..c93dd0de68de 100644 --- a/platform/configuration-store-impl/src/SchemeManagerImpl.kt +++ b/platform/configuration-store-impl/src/SchemeManagerImpl.kt @@ -342,7 +342,7 @@ class SchemeManagerImpl(val fileSpec: String, // todo check is bundled/read-only schemes correctly handled val iterator = schemes.iterator() for (scheme in iterator) { - if (processor.getState(scheme) == SchemeState.NON_PERSISTENT) { + if ((scheme as? SerializableScheme)?.schemeState ?: processor.getState(scheme) == SchemeState.NON_PERSISTENT) { continue } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/DaemonCodeAnalyzerSettingsImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/DaemonCodeAnalyzerSettingsImpl.java index 0f42f4d8b3de..44eb143c535b 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/DaemonCodeAnalyzerSettingsImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/DaemonCodeAnalyzerSettingsImpl.java @@ -17,6 +17,7 @@ package com.intellij.codeInsight.daemon; import com.intellij.codeInspection.ex.ApplicationInspectionProfileManager; import com.intellij.codeInspection.ex.InspectionProfileImpl; +import com.intellij.codeInspection.ex.InspectionProfileKt; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; @@ -51,7 +52,7 @@ public class DaemonCodeAnalyzerSettingsImpl extends DaemonCodeAnalyzerSettings i public Element getState() { Element element = XmlSerializer.serialize(this, new SkipDefaultsSerializationFilter()); String profile = ApplicationInspectionProfileManager.getInstanceImpl().getRootProfileName(); - if (!InspectionProfileImpl.DEFAULT_PROFILE_NAME.equals(profile)) { + if (!InspectionProfileKt.DEFAULT_PROFILE_NAME.equals(profile)) { element.setAttribute("profile", profile); } return element; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java index b850e842f5a9..b4c9c7fb8ced 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java @@ -508,22 +508,19 @@ public class DaemonListeners implements Disposable { @Override public void profilesInitialized() { - inspectionProfilesInitialized(); + UIUtil.invokeLaterIfNeeded(() -> { + if (myProject.isDisposed()) return; + StatusBar statusBar = WindowManager.getInstance().getStatusBar(myProject); + myTogglePopupHintsPanel = new TogglePopupHintsPanel(myProject); + statusBar.addWidget(myTogglePopupHintsPanel, myProject); + updateStatusBar(); + + stopDaemonAndRestartAllFiles("Inspection profiles activated"); + }); } } private TogglePopupHintsPanel myTogglePopupHintsPanel; - private void inspectionProfilesInitialized() { - UIUtil.invokeLaterIfNeeded(() -> { - if (myProject.isDisposed()) return; - StatusBar statusBar = WindowManager.getInstance().getStatusBar(myProject); - myTogglePopupHintsPanel = new TogglePopupHintsPanel(myProject); - statusBar.addWidget(myTogglePopupHintsPanel, myProject); - updateStatusBar(); - - stopDaemonAndRestartAllFiles("Inspection profiles activated"); - }); - } public void updateStatusBar() { if (myTogglePopupHintsPanel != null) myTogglePopupHintsPanel.updateStatus(); diff --git a/platform/lang-impl/src/com/intellij/codeInspection/actions/RunInspectionIntention.java b/platform/lang-impl/src/com/intellij/codeInspection/actions/RunInspectionIntention.java index a5d3d175d5d8..a86981af0762 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/actions/RunInspectionIntention.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/actions/RunInspectionIntention.java @@ -136,12 +136,11 @@ public class RunInspectionIntention implements IntentionAction, HighPriorityActi @NotNull InspectionManagerEx managerEx, @Nullable PsiElement psiElement) { InspectionProfileImpl rootProfile = InspectionProfileManager.getInstance().getCurrentProfile(); - LinkedHashSet allWrappers = new LinkedHashSet<>(); + LinkedHashSet> allWrappers = new LinkedHashSet<>(); allWrappers.add(toolWrapper); rootProfile.collectDependentInspections(toolWrapper, allWrappers, managerEx.getProject()); - List toolWrappers = allWrappers.size() == 1 ? Collections.singletonList(allWrappers.iterator().next()) : new ArrayList<>(allWrappers); - InspectionProfileImpl model = InspectionProfileImpl.createSimple(toolWrapper.getDisplayName(), managerEx.getProject(), - toolWrappers); + List> toolWrappers = allWrappers.size() == 1 ? Collections.singletonList(allWrappers.iterator().next()) : new ArrayList<>(allWrappers); + InspectionProfileImpl model = InspectionProfileKt.createSimple(toolWrapper.getDisplayName(), managerEx.getProject(), toolWrappers); try { Element element = new Element("toCopy"); for (InspectionToolWrapper wrapper : toolWrappers) { diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/ApplicationInspectionProfileManager.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/ApplicationInspectionProfileManager.java index b2e179be8374..1a1cd66847d9 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/ApplicationInspectionProfileManager.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/ApplicationInspectionProfileManager.java @@ -21,7 +21,6 @@ import com.intellij.codeInsight.daemon.impl.DaemonListeners; import com.intellij.codeInsight.daemon.impl.HighlightInfoType; import com.intellij.codeInsight.daemon.impl.SeveritiesProvider; import com.intellij.codeInsight.daemon.impl.SeverityRegistrar; -import com.intellij.codeInsight.daemon.impl.analysis.HighlightingSettingsPerFile; import com.intellij.codeInspection.InspectionsBundle; import com.intellij.configurationStore.BundledSchemeEP; import com.intellij.configurationStore.SchemeDataHolder; @@ -41,9 +40,9 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.ui.Messages; import com.intellij.profile.codeInspection.*; +import com.intellij.ui.AppUIUtil; import com.intellij.util.ObjectUtils; import com.intellij.util.messages.MessageBus; -import com.intellij.util.ui.UIUtil; import org.jdom.Element; import org.jdom.JDOMException; import org.jetbrains.annotations.NotNull; @@ -118,11 +117,6 @@ public class ApplicationInspectionProfileManager extends BaseInspectionProfileMa return mySchemeManager; } - @NotNull - private InspectionProfileImpl createSampleProfile(@NotNull String name, InspectionProfileImpl baseProfile) { - return new InspectionProfileImpl(name, InspectionToolRegistrar.getInstance(), this, baseProfile, null); - } - // It should be public to be available from Upsource public static void registerProvidedSeverities() { for (SeveritiesProvider provider : Extensions.getExtensions(SeveritiesProvider.EP_NAME)) { @@ -159,7 +153,7 @@ public class ApplicationInspectionProfileManager extends BaseInspectionProfileMa mySchemeManager.loadSchemes(); if (mySchemeManager.isEmpty()) { - mySchemeManager.addScheme(createSampleProfile(InspectionProfileImpl.DEFAULT_PROFILE_NAME, InspectionProfileImpl.getBaseProfile())); + mySchemeManager.addScheme(new InspectionProfileImpl(InspectionProfileKt.DEFAULT_PROFILE_NAME, InspectionToolRegistrar.getInstance(), this)); } } @@ -239,16 +233,16 @@ public class ApplicationInspectionProfileManager extends BaseInspectionProfileMa } // use default as base, not random custom profile - InspectionProfileImpl result = mySchemeManager.findSchemeByName(InspectionProfileImpl.DEFAULT_PROFILE_NAME); + InspectionProfileImpl result = mySchemeManager.findSchemeByName(InspectionProfileKt.DEFAULT_PROFILE_NAME); if (result == null) { - return createSampleProfile(InspectionProfileImpl.DEFAULT_PROFILE_NAME, null); + return new InspectionProfileImpl(InspectionProfileKt.DEFAULT_PROFILE_NAME, InspectionToolRegistrar.getInstance(), this, null, null); } return result; } @NotNull public String getRootProfileName() { - return ObjectUtils.chooseNotNull(mySchemeManager.getCurrentSchemeName(), InspectionProfileImpl.DEFAULT_PROFILE_NAME); + return ObjectUtils.chooseNotNull(mySchemeManager.getCurrentSchemeName(), InspectionProfileKt.DEFAULT_PROFILE_NAME); } @Override @@ -261,17 +255,8 @@ public class ApplicationInspectionProfileManager extends BaseInspectionProfileMa } public static void onProfilesChanged() { - //cleanup caches blindly for all projects in case ide profile was modified - for (final Project project : ProjectManager.getInstance().getOpenProjects()) { - //noinspection EmptySynchronizedStatement - synchronized (HighlightingSettingsPerFile.getInstance(project)) { - } - - UIUtil.invokeLaterIfNeeded(() -> { - if (!project.isDisposed()) { - DaemonListeners.getInstance(project).updateStatusBar(); - } - }); + for (Project project : ProjectManager.getInstance().getOpenProjects()) { + AppUIUtil.invokeLaterIfProjectAlive(project, () -> DaemonListeners.getInstance(project).updateStatusBar()); } } } diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/InspectionToolsConfigurable.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/InspectionToolsConfigurable.java index 1098b26cd45f..ca3fd0737f29 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/InspectionToolsConfigurable.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/InspectionToolsConfigurable.java @@ -366,7 +366,7 @@ public abstract class InspectionToolsConfigurable extends BaseConfigurable LOG.assertTrue(panel != null); InspectionProfileImpl profile = getSelectedObject(); LOG.assertTrue(true); - Element element = profile.writeScheme(); + Element element = profile.writeScheme(false); File file = new File(FileUtil.toSystemDependentName(dir.getPath()), sanitizeFileName(profile.getName()) + ".xml"); if (file.isFile() && Messages.showOkCancelDialog(wholePanel, "File \'" + file + "\' already exist. Do you want to overwrite it?", "Warning", diff --git a/platform/testFramework/src/com/intellij/testFramework/inspections.kt b/platform/testFramework/src/com/intellij/testFramework/inspections.kt index 1cb636669136..1c9ce96c12d5 100644 --- a/platform/testFramework/src/com/intellij/testFramework/inspections.kt +++ b/platform/testFramework/src/com/intellij/testFramework/inspections.kt @@ -26,6 +26,7 @@ import com.intellij.openapi.util.Disposer import com.intellij.profile.codeInspection.ProjectInspectionProfileManager import com.intellij.testFramework.fixtures.impl.GlobalInspectionContextForTests import com.intellij.util.ReflectionUtil +import com.intellij.util.containers.mapSmart import gnu.trove.THashMap import org.jetbrains.annotations.TestOnly import java.util.* @@ -34,13 +35,13 @@ fun configureInspections(tools: Array, project: Project, parentDisposable: Disposable): InspectionProfileImpl { runInInitMode { - val profile = InspectionProfileImpl.createSimple(UUID.randomUUID().toString(), project, tools.map { InspectionToolRegistrar.wrapTool(it) }) + val profile = createSimple(UUID.randomUUID().toString(), project, tools.mapSmart { InspectionToolRegistrar.wrapTool(it) }) val profileManager = ProjectInspectionProfileManager.getInstance(project) // we don't restore old project profile because in tests it must be in any case null - app default profile Disposer.register(parentDisposable, Disposable { profileManager.deleteProfile(profile) profileManager.setCurrentProfile(null) - clearAllToolsIn(InspectionProfileImpl.getBaseProfile()) + clearAllToolsIn(BASE_PROFILE) }) profileManager.addProfile(profile) @@ -56,7 +57,7 @@ fun createGlobalContextForTool(scope: AnalysisScope, project: Project, toolWrappers: List> = emptyList()): GlobalInspectionContextForTests { runInInitMode { - val profile = InspectionProfileImpl.createSimple("test", project, toolWrappers) + val profile = createSimple("test", project, toolWrappers) val context = object : GlobalInspectionContextForTests(project, (InspectionManagerEx.getInstance(project) as InspectionManagerEx).contentManager) { override fun getUsedTools(): List { for (tool in toolWrappers) { diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/fixes/SuppressForTestsScopeFix.java b/plugins/InspectionGadgets/src/com/siyeh/ig/fixes/SuppressForTestsScopeFix.java index 557d3ecee7c1..92c66bfe3c6e 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/fixes/SuppressForTestsScopeFix.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/fixes/SuppressForTestsScopeFix.java @@ -84,8 +84,7 @@ public class SuppressForTestsScopeFix extends InspectionGadgetsFix { } private void addRemoveTestsScope(Project project, boolean add) { - final InspectionProjectProfileManager profileManager = InspectionProjectProfileManager.getInstance(project); - final InspectionProfileImpl profile = profileManager.getCurrentProfile(); + final InspectionProfileImpl profile = InspectionProjectProfileManager.getInstance(project).getCurrentProfile(); final String shortName = myInspection.getShortName(); final InspectionToolWrapper tool = profile.getInspectionTool(shortName, project); if (tool == null) { From 00f150b2d3d1759e5077ae6bbfebc9649fca6321 Mon Sep 17 00:00:00 2001 From: "Vassiliy.Kudryashov" Date: Thu, 15 Dec 2016 18:30:40 +0300 Subject: [PATCH 212/558] IDEA-65636 I can't copy text from tooltip, though I can select it --- .../src/com/intellij/ide/IdeTooltipManager.java | 6 +++--- .../intellij/openapi/wm/impl/IdeGlassPaneImpl.java | 14 ++++++++++++++ .../src/com/intellij/ui/BalloonImpl.java | 2 +- platform/util/src/com/intellij/util/ui/UIUtil.java | 5 +++-- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java b/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java index ebe80df58d0e..933d36307e58 100644 --- a/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java +++ b/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java @@ -446,14 +446,14 @@ public class IdeTooltipManager implements ApplicationComponent, AWTEventListener if (myCurrentTipUi != null) { RelativePoint target = me != null ? new RelativePoint(me) : null; - boolean isInside = target != null && myCurrentTipUi.isInside(target); - boolean isMovingForward = target != null && myCurrentTipUi.isMovingForward(target); - boolean canAutoHide = myCurrentTooltip.canAutohideOn(new TooltipEvent(me, isInside || isMovingForward, action, event)); + boolean isInsideOrMovingForward = target != null && (myCurrentTipUi.isInside(target) || myCurrentTipUi.isMovingForward(target)); + boolean canAutoHide = myCurrentTooltip.canAutohideOn(new TooltipEvent(me, isInsideOrMovingForward, action, event)); boolean implicitMouseMove = me != null && (me.getID() == MouseEvent.MOUSE_MOVED || me.getID() == MouseEvent.MOUSE_EXITED || me.getID() == MouseEvent.MOUSE_ENTERED); if (!canAutoHide + || (isInsideOrMovingForward && implicitMouseMove) || (myCurrentTooltip.isExplicitClose() && implicitMouseMove) || (tooltipToShow != null && !tooltipToShow.isHint() && Comparing.equal(myCurrentTooltip, tooltipToShow))) { if (myHideRunnable != null) { 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 0f29a8691dd4..6cd664fdfad6 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 @@ -24,13 +24,17 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.ui.Divider; import com.intellij.openapi.ui.Painter; 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.util.SystemInfo; import com.intellij.openapi.util.Weighted; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.wm.IdeGlassPane; import com.intellij.openapi.wm.IdeGlassPaneUtil; +import com.intellij.ui.BalloonImpl; import com.intellij.util.containers.FactoryMap; +import com.intellij.util.ui.EmptyClipboardOwner; import com.intellij.util.ui.MouseEventAdapter; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; @@ -39,6 +43,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.text.html.HTMLEditorKit; import java.awt.*; +import java.awt.datatransfer.StringSelection; import java.awt.event.AWTEventListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; @@ -291,6 +296,15 @@ public class IdeGlassPaneImpl extends JPanel implements IdeGlassPaneEx, IdeEvent if (UIUtil.getWindow(this) != UIUtil.getWindow(e.getComponent())) return false; final MouseEvent event = MouseEventAdapter.convert(e, eventRootPane); + if (event.isAltDown() && SwingUtilities.isLeftMouseButton(event) && event.getID() == MouseEvent.MOUSE_PRESSED) { + Component c = SwingUtilities.getDeepestComponentAt(e.getComponent(), e.getX(), e.getY()); + Balloon balloon = JBPopupFactory.getInstance().getParentBalloonFor(c); + if (balloon instanceof BalloonImpl) { + JComponent component = ((BalloonImpl)balloon).getComponent(); + component.getToolkit().getSystemClipboard().setContents( + new StringSelection(UIUtil.getDebugText(component)), EmptyClipboardOwner.INSTANCE); + } + } if (!IdeGlassPaneUtil.canBePreprocessed(e)) { return false; diff --git a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java index 537a6f89c08a..c5519e1a22f8 100644 --- a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java @@ -661,7 +661,7 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { return myContent; } - public Component getComponent() { + public JComponent getComponent() { return myComp; } diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index 561d6be2d149..9a897e2f9e39 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -3564,12 +3564,13 @@ public class UIUtil { private static void getAllTextsRecursivelyImpl(Component component, StringBuilder builder) { String candidate = ""; - int limit = builder.length() > 60 ? 20 : 40; if (component instanceof JLabel) candidate = ((JLabel)component).getText(); if (component instanceof JTextComponent) candidate = ((JTextComponent)component).getText(); if (component instanceof AbstractButton) candidate = ((AbstractButton)component).getText(); if (StringUtil.isNotEmpty(candidate)) { - builder.append(candidate.length() > limit ? (candidate.substring(0, limit - 3) + "...") : candidate).append('|'); + candidate = candidate.replaceAll(" 0) builder.append(' '); + builder.append(StringUtil.removeHtmlTags(candidate).trim()); } if (component instanceof Container) { Component[] components = ((Container)component).getComponents(); From 29881bdd512253c6d6e86d1926a834bff831d51b Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Thu, 15 Dec 2016 18:38:38 +0300 Subject: [PATCH 213/558] [vcs-log] minor: better formatting --- .../src/com/intellij/vcs/log/ui/frame/MainFrame.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 3f995eae9043..42462358b1c9 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 @@ -260,12 +260,12 @@ public class MainFrame extends JPanel implements DataProvider, Disposable { else if (VcsDataKeys.CHANGE_LISTS.is(dataId)) { List details = myLog.getSelectedDetails(); if (details.size() > VcsLogUtil.MAX_SELECTED_COMMITS) return null; - return ContainerUtil - .map2Array(details, CommittedChangeListForRevision.class, - details1 -> new CommittedChangeListForRevision(details1.getSubject(), details1.getFullMessage(), - VcsUserUtil.getShortPresentation(details1.getCommitter()), - new Date(details1.getCommitTime()), details1.getChanges(), - convertToRevisionNumber(details1.getId()))); + return ContainerUtil.map2Array(details, CommittedChangeListForRevision.class, + detail -> new CommittedChangeListForRevision(detail.getSubject(), detail.getFullMessage(), + VcsUserUtil.getShortPresentation(detail.getCommitter()), + new Date(detail.getCommitTime()), + detail.getChanges(), + convertToRevisionNumber(detail.getId()))); } else if (VcsDataKeys.VCS_REVISION_NUMBERS.is(dataId)) { List hashes = myLog.getSelectedCommits(); From 56d5a1dce7fc6090b42bffc8ca06419e2f21a29e Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Thu, 15 Dec 2016 18:39:12 +0300 Subject: [PATCH 214/558] [vcs-log] minor: diamonds --- .../impl/src/com/intellij/vcs/log/ui/frame/MainFrame.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 42462358b1c9..7a535666ad9a 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 @@ -343,14 +343,14 @@ public class MainFrame extends JPanel implements DataProvider, Disposable { @Override protected void onSelection(@NotNull int[] selection) { // just reset and wait for details to be loaded - myChangesBrowser.setChangesToDisplay(Collections.emptyList()); + myChangesBrowser.setChangesToDisplay(Collections.emptyList()); myChangesBrowser.getViewer().setEmptyText(""); } @Override protected void onEmptySelection() { myChangesBrowser.getViewer().setEmptyText("No commits selected"); - myChangesBrowser.setChangesToDisplay(Collections.emptyList()); + myChangesBrowser.setChangesToDisplay(Collections.emptyList()); } } @@ -358,7 +358,7 @@ public class MainFrame extends JPanel implements DataProvider, Disposable { @NotNull @Override protected List getOrderedComponents() { - return Arrays.asList(myGraphTable, myChangesBrowser.getPreferredFocusedComponent(), myTextFilter.getTextEditor()); + return Arrays.asList(myGraphTable, myChangesBrowser.getPreferredFocusedComponent(), myTextFilter.getTextEditor()); } } } From 9d2402d8e3d5e6a4ea2ce65b9b7497e290332570 Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Thu, 15 Dec 2016 16:49:56 +0100 Subject: [PATCH 215/558] Save and restore the old value of XDG_CONFIG_HOME (IDEA-165495) --- plugins/terminal/resources/fish/config.fish | 4 ++++ .../jetbrains/plugins/terminal/LocalTerminalDirectRunner.java | 1 + 2 files changed, 5 insertions(+) diff --git a/plugins/terminal/resources/fish/config.fish b/plugins/terminal/resources/fish/config.fish index ae0e041048fa..caf0197c48d4 100644 --- a/plugins/terminal/resources/fish/config.fish +++ b/plugins/terminal/resources/fish/config.fish @@ -1,3 +1,7 @@ +if test -n "$OLD_XDG_CONFIG_HOME" + set XDG_CONFIG_HOME "$OLD_XDG_CONFIG_HOME" +end + if test -f ~/.config/fish/config.fish . ~/.config/fish/config.fish end diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/LocalTerminalDirectRunner.java b/plugins/terminal/src/org/jetbrains/plugins/terminal/LocalTerminalDirectRunner.java index e662327d989b..8569af01950e 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/LocalTerminalDirectRunner.java +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/LocalTerminalDirectRunner.java @@ -216,6 +216,7 @@ public class LocalTerminalDirectRunner extends AbstractTerminalRunner Date: Thu, 15 Dec 2016 18:58:34 +0300 Subject: [PATCH 216/558] parallel modules loading: turned off by default --- .../openapi/module/impl/ModuleManagerImpl.java | 18 ++++++++++++++++-- .../util/resources/misc/registry.properties | 3 +++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java index cb68b5625f7a..e6164fe0224e 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java @@ -34,6 +34,7 @@ import com.intellij.openapi.roots.ex.ProjectRootManagerEx; import com.intellij.openapi.roots.impl.ModifiableModelCommitter; import com.intellij.openapi.util.*; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.StandardFileSystems; import com.intellij.openapi.vfs.VirtualFile; @@ -58,7 +59,8 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.*; -import java.util.concurrent.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; /** * @author max @@ -228,6 +230,7 @@ public abstract class ModuleManagerImpl extends ModuleManager implements Project ExecutorService service = AppExecutorUtil.createBoundedApplicationPoolExecutor("modules loader", JobSchedulerImpl.CORES_COUNT); List, ModulePath>> tasks = new ArrayList<>(); Set paths = new java.util.HashSet<>(); + boolean parallel = Registry.is("parallel.modules.loading"); for (ModulePath modulePath : myModulePathsToLoad) { if (progressIndicator.isCanceled()) { break; @@ -235,6 +238,10 @@ public abstract class ModuleManagerImpl extends ModuleManager implements Project try { String path = modulePath.getPath(); if (!paths.add(path)) continue; + if (!parallel) { + tasks.add(Pair.create(null, modulePath)); + continue; + } ThrowableComputable computable = moduleModel.loadModuleInternal(path); Future future = service.submit(() -> { progressIndicator.setFraction(progressIndicator.getFraction() + myProgressStep); @@ -261,7 +268,14 @@ public abstract class ModuleManagerImpl extends ModuleManager implements Project break; } try { - Module module = task.first.get(); + Module module; + if (parallel) { + module = task.first.get(); + } + else { + module = moduleModel.loadModuleInternal(task.second.getPath()).compute(); + progressIndicator.setFraction(progressIndicator.getFraction() + myProgressStep); + } if (module == null) continue; if (isUnknownModuleType(module)) { modulesWithUnknownTypes.add(module); diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 8704d58ded7e..9705c40542b2 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -916,6 +916,9 @@ ide.projectView.globalOptions.description=Make Project View options such as auto ide.projectView.ProjectViewPaneTreeStructure.BuildChildrenInBackground=true ide.projectView.ProjectViewPaneTreeStructure.BuildChildrenInBackground.description=Temporary ability to control a tree building for the Project View pane +parallel.modules.loading=false +parallel.modules.loading.description=Parallel modules loading + compiler.ref.index=true compiler.ref.index.description=Enables find usages using references from compiler indices compiler.ref.index.restartRequired=true From 6b667c6d175569c8d13c051b18b098b359179360 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Thu, 15 Dec 2016 17:17:11 +0100 Subject: [PATCH 217/558] disable DirectoryBasedStorageTest on CI --- .../testSrc/DirectoryBasedStorageTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/configuration-store-impl/testSrc/DirectoryBasedStorageTest.kt b/platform/configuration-store-impl/testSrc/DirectoryBasedStorageTest.kt index b1dcbe9ad516..ff23725474d9 100644 --- a/platform/configuration-store-impl/testSrc/DirectoryBasedStorageTest.kt +++ b/platform/configuration-store-impl/testSrc/DirectoryBasedStorageTest.kt @@ -18,10 +18,10 @@ package com.intellij.configurationStore import com.intellij.idea.Bombed import com.intellij.openapi.components.MainConfigurationStateSplitter import com.intellij.openapi.components.StateStorage -import com.intellij.testFramework.assertions.Assertions.assertThat import com.intellij.testFramework.ProjectRule import com.intellij.testFramework.RuleChain import com.intellij.testFramework.TemporaryDirectory +import com.intellij.testFramework.assertions.Assertions.assertThat import com.intellij.testFramework.runInEdtAndWait import com.intellij.util.loadElement import org.jdom.Element @@ -50,7 +50,7 @@ internal class TestStateSplitter : MainConfigurationStateSplitter() { override fun getSubStateFileName(element: Element) = element.getAttributeValue("name") } -@Bombed(user = "vladimir.krivosheev", year = 2016, month = Calendar.DECEMBER, day = 10) +@Bombed(user = "vladimir.krivosheev", year = 2018, month = Calendar.DECEMBER, day = 10) internal class DirectoryBasedStorageTest { companion object { @JvmField From 018e57d80478085b190c5a257ff7172b869cfa57 Mon Sep 17 00:00:00 2001 From: Alexander Kass Date: Thu, 15 Dec 2016 19:37:52 +0300 Subject: [PATCH 218/558] RegexpChecker: continue transaction IDEA-165426 --- .../regexp/intention/CheckRegExpForm.java | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/RegExpSupport/src/org/intellij/lang/regexp/intention/CheckRegExpForm.java b/RegExpSupport/src/org/intellij/lang/regexp/intention/CheckRegExpForm.java index 9c6b6502d02a..76d3bffe3d71 100644 --- a/RegExpSupport/src/org/intellij/lang/regexp/intention/CheckRegExpForm.java +++ b/RegExpSupport/src/org/intellij/lang/regexp/intention/CheckRegExpForm.java @@ -21,7 +21,8 @@ import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CustomShortcutSet; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.TransactionGuard; +import com.intellij.openapi.application.TransactionId; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.event.DocumentAdapter; import com.intellij.openapi.editor.event.DocumentEvent; @@ -112,19 +113,26 @@ public class CheckRegExpForm { }.registerCustomShortcutSet(CustomShortcutSet.fromString("shift TAB"), mySampleText); final Alarm updater = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, disposable); + Runnable scheduler = () -> { + TransactionId transactionId = TransactionGuard.getInstance().getContextTransaction(); + updater.cancelAllRequests(); + if (!updater.isDisposed()) { + updater.addRequest(() -> { + Boolean correct = isMatchingText(myRegexpFile, mySampleText.getText()); + TransactionGuard.getInstance().submitTransaction(myProject, transactionId, () -> setBalloonState(correct)); + }, 200); + } + }; DocumentAdapter documentListener = new DocumentAdapter() { @Override public void documentChanged(DocumentEvent e) { - updater.cancelAllRequests(); - if (!updater.isDisposed()) { - updater.addRequest(CheckRegExpForm.this::updateBalloon, 200); - } + scheduler.run(); } }; myRegExp.addDocumentListener(documentListener); mySampleText.addDocumentListener(documentListener); - updateBalloon(); + scheduler.run(); mySampleText.selectAll(); } @@ -138,6 +146,14 @@ public class CheckRegExpForm { myRootPanel.setBorder(JBUI.Borders.empty(UIUtil.DEFAULT_VGAP, UIUtil.DEFAULT_HGAP)); } + private void setBalloonState(Boolean correct) { + mySampleText.setBackground(correct != null && correct ? BACKGROUND_COLOR_MATCH : BACKGROUND_COLOR_NOMATCH); + myMessage.setText(correct == null ? "Pattern is too complex" : correct ? "Matches!" : "No match"); + myRootPanel.revalidate(); + Balloon balloon = JBPopupFactory.getInstance().getParentBalloonFor(myRootPanel); + if (balloon != null && !balloon.isDisposed()) balloon.revalidate(); + } + @NotNull public JComponent getPreferredFocusedComponent() { return mySampleText; @@ -148,40 +164,24 @@ public class CheckRegExpForm { return myRootPanel; } - private void updateBalloon() { - final Boolean correct = isMatchingText(myRegexpFile, mySampleText.getText()); - - ApplicationManager.getApplication().invokeLater(() -> { - mySampleText.setBackground(correct != null && correct ? BACKGROUND_COLOR_MATCH : BACKGROUND_COLOR_NOMATCH); - myMessage.setText(correct == null ? "Pattern is too complex" : correct ? "Matches!" : "No match"); - myRootPanel.revalidate(); - Balloon balloon = JBPopupFactory.getInstance().getParentBalloonFor(myRootPanel); - if (balloon != null && !balloon.isDisposed()) balloon.revalidate(); - }); - } - @TestOnly public static boolean isMatchingTextTest(@NotNull PsiFile regexpFile, @NotNull String sampleText) { Boolean result = isMatchingText(regexpFile, sampleText); return result != null && result; } - private static Boolean isMatchingText(@NotNull final PsiFile regexpFile, @NotNull String sampleText) { final String regExp = regexpFile.getText(); - Integer patternFlags = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public Integer compute() { - PsiLanguageInjectionHost host = InjectedLanguageUtil.findInjectionHost(regexpFile); - int flags = 0; - if (host != null) { - for (RegExpModifierProvider provider : RegExpModifierProvider.EP.allForLanguage(host.getLanguage())) { - flags = provider.getFlags(host, regexpFile); - if (flags > 0) break; - } + Integer patternFlags = ApplicationManager.getApplication().runReadAction((Computable)() -> { + PsiLanguageInjectionHost host = InjectedLanguageUtil.findInjectionHost(regexpFile); + int flags = 0; + if (host != null) { + for (RegExpModifierProvider provider : RegExpModifierProvider.EP.allForLanguage(host.getLanguage())) { + flags = provider.getFlags(host, regexpFile); + if (flags > 0) break; } - return flags; } + return flags; }); try { From 6105823fe7218f162cdef759356167f1d19c5c96 Mon Sep 17 00:00:00 2001 From: Pavel Fatin Date: Fri, 2 Dec 2016 09:57:11 +0100 Subject: [PATCH 219/558] true smooth scrolling: main switch --- platform/util/src/com/intellij/util/SystemProperties.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/platform/util/src/com/intellij/util/SystemProperties.java b/platform/util/src/com/intellij/util/SystemProperties.java index bcdc4591f798..3bfcc0da9565 100644 --- a/platform/util/src/com/intellij/util/SystemProperties.java +++ b/platform/util/src/com/intellij/util/SystemProperties.java @@ -103,4 +103,8 @@ public class SystemProperties { public static boolean has(String key) { return System.getProperty(key) != null; } + + public static boolean isTrueSmoothScrollingEnabled() { + return getBooleanProperty("idea.true.smooth.scrolling", false); + } } \ No newline at end of file From 68a630cfdfba9aa6f577512bdff561c9bc091ad9 Mon Sep 17 00:00:00 2001 From: Pavel Fatin Date: Fri, 2 Dec 2016 10:07:22 +0100 Subject: [PATCH 220/558] true smooth scrolling: hardware acceleration enabled --- .../intellij/openapi/ui/LoadingDecorator.java | 42 +++++++++++++++++-- .../openapi/editor/impl/EditorImpl.java | 18 +++++--- .../editor/impl/EditorMarkupModelImpl.java | 9 +++- 3 files changed, 58 insertions(+), 11 deletions(-) diff --git a/platform/platform-api/src/com/intellij/openapi/ui/LoadingDecorator.java b/platform/platform-api/src/com/intellij/openapi/ui/LoadingDecorator.java index 66b4846c2141..6587cb4c8a11 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/LoadingDecorator.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/LoadingDecorator.java @@ -20,6 +20,7 @@ import com.intellij.openapi.util.Disposer; import com.intellij.ui.components.JBLayeredPane; import com.intellij.ui.components.panels.NonOpaquePanel; import com.intellij.util.Alarm; +import com.intellij.util.SystemProperties; import com.intellij.util.ui.Animator; import com.intellij.util.ui.AsyncProcessIcon; import com.intellij.util.ui.UIUtil; @@ -65,7 +66,7 @@ public class LoadingDecorator { @Override protected void paintCycleEnd() { - myLoadingLayer.setVisible(false); + setLoadingLayerVisible(false); myLoadingLayer.setAlpha(-1); } }; @@ -73,11 +74,44 @@ public class LoadingDecorator { myPane.add(content, JLayeredPane.DEFAULT_LAYER, 0); - myPane.add(myLoadingLayer, JLayeredPane.DRAG_LAYER, 1); + if (!SystemProperties.isTrueSmoothScrollingEnabled()) { + myPane.add(myLoadingLayer, JLayeredPane.DRAG_LAYER, 1); + } Disposer.register(parent, myLoadingLayer.myProgress); } + private void setLoadingLayerVisible(boolean visible) { + if (SystemProperties.isTrueSmoothScrollingEnabled()) { + setLoadingLayerPresent(visible); + } + myLoadingLayer.setVisible(visible); + } + + public void setLoadingLayerVisible(boolean visible, boolean takeSnapshot) { + if (SystemProperties.isTrueSmoothScrollingEnabled()) { + setLoadingLayerPresent(visible); + } + myLoadingLayer.setVisible(visible, takeSnapshot); + } + + /* Placing the invisible layer on top of JViewport suppresses blit-accelerated scrolling + as JViewport.canUseWindowBlitter() doesn't take component's visibility into account. + + We need to add / remove the loading layer on demand to preserve the blit-based scrolling. + + Blit-acceleration copies as much of the rendered area as possible and then repaints only newly exposed region. + This helps to improve scrolling performance and to reduce CPU usage (especially if drawing is compute-intensive). */ + private void setLoadingLayerPresent(boolean present) { + if (present) { + if (myPane.getComponentCount() < 2) { + myPane.add(myLoadingLayer, JLayeredPane.DRAG_LAYER, 1); + } + } else { + myPane.remove(myLoadingLayer); + } + } + protected NonOpaquePanel customizeLoadingLayer(JPanel parent, JLabel text, AsyncProcessIcon icon) { parent.setLayout(new GridBagLayout()); @@ -114,7 +148,7 @@ public class LoadingDecorator { } protected void _startLoading(final boolean takeSnapshot) { - myLoadingLayer.setVisible(true, takeSnapshot); + setLoadingLayerVisible(true, takeSnapshot); } public void stopLoading() { @@ -123,7 +157,7 @@ public class LoadingDecorator { if (!isLoading()) return; - myLoadingLayer.setVisible(false, false); + setLoadingLayerVisible(false, false); } 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 2b2b7357be04..f05847963dc8 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 @@ -29,10 +29,7 @@ import com.intellij.ide.ui.customization.CustomActionsSchema; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.ActionManagerEx; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; -import com.intellij.openapi.application.TransactionGuard; -import com.intellij.openapi.application.TransactionGuardImpl; +import com.intellij.openapi.application.*; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.UndoConfirmationPolicy; import com.intellij.openapi.diagnostic.Logger; @@ -509,9 +506,13 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi myEditorComponent = new EditorComponentImpl(this); myScrollPane.putClientProperty(JBScrollPane.BRIGHTNESS_FROM_VIEW, true); myVerticalScrollBar = (MyScrollBar)myScrollPane.getVerticalScrollBar(); - myVerticalScrollBar.setOpaque(false); + // JBScrollPane.Layout relies on "opaque" property directly (instead of "editor.transparent.scrollbar") + myVerticalScrollBar.setOpaque(SystemProperties.isTrueSmoothScrollingEnabled()); myPanel = new JPanel(); + // JBScrollPane.Layout relies on "opaque" property directly (instead of "editor.transparent.scrollbar") + myScrollPane.getHorizontalScrollBar().setOpaque(SystemProperties.isTrueSmoothScrollingEnabled()); + UIUtil.putClientProperty( myPanel, UIUtil.NOT_IN_HIERARCHY_COMPONENTS, new Iterable() { @NotNull @@ -2706,7 +2707,12 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi public void setUI(ScrollBarUI ui) { if (myPersistentUI == null) myPersistentUI = ui; super.setUI(myPersistentUI); - setOpaque(false); + + /* Placing component(s) on top of JViewport suppresses blit-accelerated scrolling (for obvious reasons). + + Blit-acceleration copies as much of the rendered area as possible and then repaints only newly exposed region. + This helps to improve scrolling performance and to reduce CPU usage (especially if drawing is compute-intensive). */ + setOpaque(SystemProperties.isTrueSmoothScrollingEnabled()); } /** diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java index 14567dc6555c..c0b2269261e0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java @@ -54,6 +54,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.*; import com.intellij.ui.awt.RelativePoint; import com.intellij.util.Alarm; +import com.intellij.util.SystemProperties; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.ButtonlessScrollBarUI; import com.intellij.util.ui.GraphicsUtil; @@ -474,7 +475,13 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark } private boolean transparent() { - return Registry.is("editor.transparent.scrollbar", false) && EditorUtil.isRealFileEditor(myEditor); + /* Placing component(s) on top of JViewport suppresses blit-accelerated scrolling (for obvious reasons). + + Blit-acceleration copies as much of the rendered area as possible and then repaints only newly exposed region. + This helps to improve scrolling performance and to reduce CPU usage (especially if drawing is compute-intensive). */ + return !SystemProperties.isTrueSmoothScrollingEnabled() && + Registry.is("editor.transparent.scrollbar", false) && + EditorUtil.isRealFileEditor(myEditor); } private class MyErrorPanel extends ButtonlessScrollBarUI implements MouseMotionListener, MouseListener, MouseWheelListener, UISettingsListener { From 2a9b040b7757ca925fc679feceec8774b5ff1ef1 Mon Sep 17 00:00:00 2001 From: Pavel Fatin Date: Fri, 2 Dec 2016 10:27:37 +0100 Subject: [PATCH 221/558] true smooth scrolling: true double buffering enabled --- .../ide/BootstrapClassLoaderUtil.java | 1 + .../com/intellij/idea/IdeaApplication.java | 12 +++- .../openapi/editor/impl/EditorImpl.java | 8 +-- .../openapi/application/PathManager.java | 19 ++++++ .../com/intellij/util/MethodInvocator.java | 60 +++++++++++++++++++ .../com/intellij/util/ui/GraphicsUtil.java | 17 ++++++ .../xml/breadcrumbs/BreadcrumbsComponent.java | 4 +- 7 files changed, 114 insertions(+), 7 deletions(-) create mode 100644 platform/util/src/com/intellij/util/MethodInvocator.java diff --git a/platform/bootstrap/src/com/intellij/ide/BootstrapClassLoaderUtil.java b/platform/bootstrap/src/com/intellij/ide/BootstrapClassLoaderUtil.java index 72f186746272..0e84306e675f 100644 --- a/platform/bootstrap/src/com/intellij/ide/BootstrapClassLoaderUtil.java +++ b/platform/bootstrap/src/com/intellij/ide/BootstrapClassLoaderUtil.java @@ -50,6 +50,7 @@ public class BootstrapClassLoaderUtil extends ClassUtilCore { @NotNull public static ClassLoader initClassLoader(boolean updatePlugins) throws MalformedURLException { PathManager.loadProperties(); + PathManager.patchProperties(); Collection classpath = new LinkedHashSet(); addParentClasspath(classpath, false); diff --git a/platform/platform-impl/src/com/intellij/idea/IdeaApplication.java b/platform/platform-impl/src/com/intellij/idea/IdeaApplication.java index b014c03b8c17..65c58a83a1a7 100644 --- a/platform/platform-impl/src/com/intellij/idea/IdeaApplication.java +++ b/platform/platform-impl/src/com/intellij/idea/IdeaApplication.java @@ -44,6 +44,7 @@ import com.intellij.ui.CustomProtocolHandler; import com.intellij.ui.Splash; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.SystemProperties; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -151,7 +152,16 @@ public class IdeaApplication { if (headless) return; - if (Patches.SUN_BUG_ID_6209673) { + /* Using custom RepaintManager disables BufferStrategyPaintManager (and so, true double buffering) + because the only non-private constructor forces RepaintManager.BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_OFF. + + At the same time, http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6209673 seems to be now fixed. + + This matters only if swing.bufferPerWindow = true and we don't invoke JComponent.getGraphics() directly. + + True double buffering is needed to eliminate tearing on blit-accelerated scrolling and to restore + frame buffer content without the usual repainting, even when the EDT is blocked. */ + if (!SystemProperties.isTrueSmoothScrollingEnabled() && Patches.SUN_BUG_ID_6209673) { RepaintManager.setCurrentManager(new IdeRepaintManager()); } 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 f05847963dc8..d52fe9f8053e 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 @@ -79,10 +79,7 @@ import com.intellij.util.*; import com.intellij.util.concurrency.EdtExecutorService; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ContainerUtilRt; -import com.intellij.util.ui.ButtonlessScrollBarUI; -import com.intellij.util.ui.JBSwingUtilities; -import com.intellij.util.ui.JBUI; -import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.*; import com.intellij.util.ui.update.Activatable; import com.intellij.util.ui.update.UiNotifyConnector; import org.intellij.lang.annotations.JdkConstants; @@ -1084,7 +1081,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi DataContext context = getDataContext(); - Graphics graphics = myEditorComponent.getGraphics(); + Graphics graphics = SystemProperties.isTrueSmoothScrollingEnabled() ? + GraphicsUtil.safelyGetGraphics(myEditorComponent) : myEditorComponent.getGraphics(); if (graphics != null) { // editor component is not showing processKeyTypedImmediately(c, graphics, context); graphics.dispose(); diff --git a/platform/util/src/com/intellij/openapi/application/PathManager.java b/platform/util/src/com/intellij/openapi/application/PathManager.java index c40fb776b68b..4148d5ba8454 100644 --- a/platform/util/src/com/intellij/openapi/application/PathManager.java +++ b/platform/util/src/com/intellij/openapi/application/PathManager.java @@ -21,6 +21,7 @@ import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.SystemInfoRt; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.SystemProperties; import com.intellij.util.io.URLUtil; import com.sun.jna.TypeMapper; import com.sun.jna.platform.FileUtils; @@ -387,6 +388,24 @@ public class PathManager { } } + /** + * Provides a way to tweak system properties as early as possible. + */ + public static void patchProperties() { + /* Setting swing.bufferPerWindow = false disables true double buffering (by definition), + by forcing BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_OFF in RepaintManager's static initializer. + + At the same time, https://youtrack.jetbrains.com/issue/IDEA-35883 seems to be now fixed. + + This matters only if we use the default RepaintManager and don't invoke JComponent.getGraphics() directly. + + True double buffering is needed to eliminate tearing on blit-accelerated scrolling and to restore + frame buffer content without the usual repainting, even when the EDT is blocked. */ + if (SystemProperties.isTrueSmoothScrollingEnabled()) { + System.setProperty("swing.bufferPerWindow", "true"); + } + } + private static String getCustomPropertiesFile() { String configPath = getCustomOptionsDirectory(); return configPath != null ? configPath + File.separator + PROPERTIES_FILE_NAME : null; diff --git a/platform/util/src/com/intellij/util/MethodInvocator.java b/platform/util/src/com/intellij/util/MethodInvocator.java new file mode 100644 index 000000000000..3267b0677bd0 --- /dev/null +++ b/platform/util/src/com/intellij/util/MethodInvocator.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.util; + +import com.intellij.openapi.diagnostic.Logger; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class MethodInvocator { + private static final Logger LOG = Logger.getInstance("#com.intellij.util.MethodInvocator"); + + private Method myMethod; + + public MethodInvocator(Class aClass, String method, Class... parameterTypes) { + try { + myMethod = aClass.getDeclaredMethod(method, parameterTypes); + + if (!myMethod.isAccessible()) { + myMethod.setAccessible(true); + } + } + catch (NoSuchMethodException e) { + LOG.warn("Method not found: " + aClass.getName() + "." + method); + } + } + + public boolean isAvailable() { + return myMethod != null; + } + + public Object invoke(Object object, Object... arguments) { + if (!isAvailable()) { + throw new IllegalStateException("Method is not available"); + } + + try { + return myMethod.invoke(object, arguments); + } + catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } +} diff --git a/platform/util/src/com/intellij/util/ui/GraphicsUtil.java b/platform/util/src/com/intellij/util/ui/GraphicsUtil.java index 3d635f657f28..b7e9eeac2d1b 100644 --- a/platform/util/src/com/intellij/util/ui/GraphicsUtil.java +++ b/platform/util/src/com/intellij/util/ui/GraphicsUtil.java @@ -16,8 +16,10 @@ package com.intellij.util.ui; import com.intellij.openapi.ui.GraphicsConfig; +import com.intellij.util.MethodInvocator; import org.jetbrains.annotations.NotNull; +import javax.swing.*; import java.awt.*; import java.awt.image.BufferedImage; import java.util.Map; @@ -26,6 +28,8 @@ import java.util.Map; * @author Konstantin Bulenkov */ public class GraphicsUtil { + private static final MethodInvocator ourSafelyGetGraphicsMethod = new MethodInvocator(JComponent.class, "safelyGetGraphics", Component.class); + @SuppressWarnings("UndesirableClassUsage") private static final Graphics2D ourGraphics = new BufferedImage(1,1,BufferedImage.TYPE_INT_ARGB).createGraphics(); static { @@ -98,4 +102,17 @@ public class GraphicsUtil { g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); return config; } + + /* Invoking getGraphics() on any component disables true double buffering + withing JRootPane, even if no subsequent drawing is actually performed. + + This matters only if we use the default RepaintManager and swing.bufferPerWindow = true. + + True double buffering is needed to eliminate tearing on blit-accelerated scrolling and to restore + frame buffer content without the usual repainting, even when the EDT is blocked. */ + public static Graphics safelyGetGraphics(JComponent c) { + return ourSafelyGetGraphicsMethod.isAvailable() + ? (Graphics)ourSafelyGetGraphicsMethod.invoke(null, c) + : c.getGraphics(); + } } diff --git a/xml/openapi/src/com/intellij/xml/breadcrumbs/BreadcrumbsComponent.java b/xml/openapi/src/com/intellij/xml/breadcrumbs/BreadcrumbsComponent.java index 389f55c49162..b3d91aa0c8aa 100644 --- a/xml/openapi/src/com/intellij/xml/breadcrumbs/BreadcrumbsComponent.java +++ b/xml/openapi/src/com/intellij/xml/breadcrumbs/BreadcrumbsComponent.java @@ -24,7 +24,9 @@ import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.util.Weighted; import com.intellij.ui.paint.RectanglePainter; +import com.intellij.util.SystemProperties; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.ui.GraphicsUtil; import com.intellij.util.ui.JBInsets; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -341,7 +343,7 @@ public class BreadcrumbsComponent extends JComponent @Override public Dimension getPreferredSize() { - Graphics2D g2 = (Graphics2D)getGraphics(); + Graphics2D g2 = (Graphics2D) (SystemProperties.isTrueSmoothScrollingEnabled() ? GraphicsUtil.safelyGetGraphics(this) : getGraphics()); Dimension dim = new Dimension(Integer.MAX_VALUE, g2 != null ? DEFAULT_PAINTER.getSize("DUMMY", g2.getFontMetrics(), Integer.MAX_VALUE).height + 1 : 1); JBInsets.addTo(dim, getInsets()); return dim; From 9c6c053f9f19fc326d879b725bca8a114121491c Mon Sep 17 00:00:00 2001 From: Pavel Fatin Date: Fri, 2 Dec 2016 10:32:48 +0100 Subject: [PATCH 222/558] true smooth scrolling: hardware acceleration check --- .../intellij/ui/components/JBViewport.java | 78 +++++++++++++++++-- .../util/resources/misc/registry.properties | 3 + 2 files changed, 76 insertions(+), 5 deletions(-) diff --git a/platform/platform-api/src/com/intellij/ui/components/JBViewport.java b/platform/platform-api/src/com/intellij/ui/components/JBViewport.java index 709f67ec1800..b1b97088cb76 100644 --- a/platform/platform-api/src/com/intellij/ui/components/JBViewport.java +++ b/platform/platform-api/src/com/intellij/ui/components/JBViewport.java @@ -15,19 +15,21 @@ */ package com.intellij.ui.components; +import com.intellij.notification.Notification; +import com.intellij.notification.NotificationGroup; +import com.intellij.notification.NotificationType; import com.intellij.openapi.ui.TypingTarget; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.registry.Registry; +import com.intellij.openapi.util.registry.RegistryValue; import com.intellij.ui.components.JBScrollPane.Alignment; import com.intellij.ui.table.JBTable; -import com.intellij.util.ui.ComponentWithEmptyText; -import com.intellij.util.ui.JBInsets; -import com.intellij.util.ui.JBSwingUtilities; -import com.intellij.util.ui.StatusText; -import com.intellij.util.ui.UIUtil; +import com.intellij.util.MethodInvocator; +import com.intellij.util.ui.*; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import javax.swing.Timer; import javax.swing.border.AbstractBorder; import javax.swing.border.Border; import javax.swing.plaf.TreeUI; @@ -40,6 +42,14 @@ import java.awt.event.ContainerListener; import static com.intellij.util.ui.JBUI.emptyInsets; public class JBViewport extends JViewport implements ZoomableViewport { + private static final MethodInvocator ourCanUseWindowBlitterMethod = new MethodInvocator(JViewport.class, "canUseWindowBlitter"); + + private static final RegistryValue CAPABILITIES_DEBUG = Registry.get("idea.true.smooth.scrolling.debug"); + private static final NotificationGroup NOTIFICATION_GROUP = NotificationGroup.logOnlyGroup("scrolling-capabilities-debug"); + private static final int NOTIFICATION_TIMEOUT = 1500; + + private Notification myPreviousNotification; + private static final ViewportLayout ourLayoutManager = new ViewportLayout() { @Override @@ -89,6 +99,64 @@ public class JBViewport extends JViewport implements ZoomableViewport { }); } + @Override + public void setViewPosition(Point p) { + if (CAPABILITIES_DEBUG.asBoolean() && !isInsideLogToolWindow()) { + checkScrollingCapabilities(); + } + + super.setViewPosition(p); + } + + // A heuristic to detect whether this viewport belongs to the "Event Log" tool window (which we use for output) + private boolean isInsideLogToolWindow() { + Container parent1 = getParent(); + if (parent1 instanceof JScrollPane) { + Container parent2 = parent1.getParent(); + if (parent2 instanceof JPanel) { + Container parent3 = parent2.getParent(); + if (parent3 instanceof JPanel) { + return parent3.getClass().getName().startsWith("com.intellij.notification.EventLogToolWindowFactory"); + } + } + } + + return false; + } + + // Checks whether blit-accelerated scrolling is feasible, and if so, checks whether true double buffering is available. + private void checkScrollingCapabilities() { + if (myPreviousNotification == null || myPreviousNotification.isExpired()) { + if (!Boolean.TRUE.equals(isWindowBlitterAvailableFor(this))) { + myPreviousNotification = notify("Scrolling: cannot use window blitter"); + } + } + } + + /* Blit-acceleration copies as much of the rendered area as possible and then repaints only newly exposed region. + This helps to improve scrolling performance and to reduce CPU usage (especially if drawing is compute-intensive). + + Generally, this requires that viewport must not be obscured by its ancestors and must be showing. */ + @Nullable + private static Boolean isWindowBlitterAvailableFor(JViewport viewport) { + if (ourCanUseWindowBlitterMethod.isAvailable()) { + return (Boolean)ourCanUseWindowBlitterMethod.invoke(viewport); + } + + return null; + } + + private static Notification notify(String message) { + Notification notification = NOTIFICATION_GROUP.createNotification(message, NotificationType.INFORMATION); + notification.notify(null); + + Timer timer = new Timer(NOTIFICATION_TIMEOUT, event -> notification.expire()); + timer.setRepeats(false); + timer.start(); + + return notification; + } + @Override public Color getBackground() { Color color = super.getBackground(); diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 9705c40542b2..83fbcee0ba2c 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -684,6 +684,9 @@ editor.zero.latency.rendering.pipeline.flush.description=Flush graphics pipeline editor.zero.latency.rendering.debug=false editor.zero.latency.rendering.debug.description=Make a pause after immediate rendering +idea.true.smooth.scrolling.debug=false +idea.true.smooth.scrolling.debug.description=Check window blitter utilization + decompiler.use.line.mapping=true decompiler.use.line.mapping.description=Maps original to decompiled line numbers when stepping in debugger. decompiler.dump.original.lines=false From dc5c6a883f634ed5aa7e4a86fa451ce367b67c10 Mon Sep 17 00:00:00 2001 From: Pavel Fatin Date: Fri, 2 Dec 2016 10:39:57 +0100 Subject: [PATCH 223/558] true smooth scrolling: true double buffering check --- .../intellij/ui/components/JBViewport.java | 33 +++++++++++++++++++ .../util/resources/misc/registry.properties | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/platform/platform-api/src/com/intellij/ui/components/JBViewport.java b/platform/platform-api/src/com/intellij/ui/components/JBViewport.java index b1b97088cb76..78a025de5214 100644 --- a/platform/platform-api/src/com/intellij/ui/components/JBViewport.java +++ b/platform/platform-api/src/com/intellij/ui/components/JBViewport.java @@ -43,6 +43,8 @@ import static com.intellij.util.ui.JBUI.emptyInsets; public class JBViewport extends JViewport implements ZoomableViewport { private static final MethodInvocator ourCanUseWindowBlitterMethod = new MethodInvocator(JViewport.class, "canUseWindowBlitter"); + private static final MethodInvocator ourGetPaintManagerMethod = new MethodInvocator(RepaintManager.class, "getPaintManager"); + private static final MethodInvocator ourGetUseTrueDoubleBufferingMethod = new MethodInvocator(JRootPane.class, "getUseTrueDoubleBuffering"); private static final RegistryValue CAPABILITIES_DEBUG = Registry.get("idea.true.smooth.scrolling.debug"); private static final NotificationGroup NOTIFICATION_GROUP = NotificationGroup.logOnlyGroup("scrolling-capabilities-debug"); @@ -130,6 +132,11 @@ public class JBViewport extends JViewport implements ZoomableViewport { if (!Boolean.TRUE.equals(isWindowBlitterAvailableFor(this))) { myPreviousNotification = notify("Scrolling: cannot use window blitter"); } + else { + if (!Boolean.TRUE.equals(isTrueDoubleBufferingAvailableFor(this))) { + myPreviousNotification = notify("Scrolling: cannot use true double buffering"); + } + } } } @@ -146,6 +153,32 @@ public class JBViewport extends JViewport implements ZoomableViewport { return null; } + /* True double buffering is needed to eliminate tearing on blit-accelerated scrolling and to restore + frame buffer content without the usual repainting, even when the EDT is blocked. + + Generally, this requires default RepaintManager, swing.bufferPerWindow = true and + no prior direct invocations of JComponent.getGraphics() within JRootPane. */ + @Nullable + private static Boolean isTrueDoubleBufferingAvailableFor(JComponent component) { + if (ourGetPaintManagerMethod.isAvailable()) { + Object paintManager = ourGetPaintManagerMethod.invoke(RepaintManager.currentManager(component)); + + if (!"javax.swing.BufferStrategyPaintManager".equals(paintManager.getClass().getName())) { + return false; + } + + if (ourGetUseTrueDoubleBufferingMethod.isAvailable()) { + JRootPane rootPane = component.getRootPane(); + + if (rootPane != null) { + return (Boolean)ourGetUseTrueDoubleBufferingMethod.invoke(rootPane); + } + } + } + + return null; + } + private static Notification notify(String message) { Notification notification = NOTIFICATION_GROUP.createNotification(message, NotificationType.INFORMATION); notification.notify(null); diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 83fbcee0ba2c..b1dfa786c03f 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -685,7 +685,7 @@ editor.zero.latency.rendering.debug=false editor.zero.latency.rendering.debug.description=Make a pause after immediate rendering idea.true.smooth.scrolling.debug=false -idea.true.smooth.scrolling.debug.description=Check window blitter utilization +idea.true.smooth.scrolling.debug.description=Check window blitter and true double buffering utilization decompiler.use.line.mapping=true decompiler.use.line.mapping.description=Maps original to decompiled line numbers when stepping in debugger. From 72d99ed0bf829f4e8a91dad950259f84c6d445c7 Mon Sep 17 00:00:00 2001 From: Pavel Fatin Date: Wed, 14 Dec 2016 11:03:57 +0100 Subject: [PATCH 224/558] true smooth scrolling: component-level settings --- .../options/editor/EditorOptionsPanel.java | 6 ++- .../ui/components/ComponentSettings.java | 42 +++++++++++++++++++ .../intellij/ide/RemoteDesktopDetector.java | 2 + .../ex/EditorSettingsExternalizable.java | 2 + 4 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 platform/platform-api/src/com/intellij/ui/components/ComponentSettings.java diff --git a/platform/lang-impl/src/com/intellij/application/options/editor/EditorOptionsPanel.java b/platform/lang-impl/src/com/intellij/application/options/editor/EditorOptionsPanel.java index 6717077957d6..62658008c44f 100644 --- a/platform/lang-impl/src/com/intellij/application/options/editor/EditorOptionsPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/editor/EditorOptionsPanel.java @@ -51,6 +51,7 @@ import com.intellij.openapi.vcs.impl.LineStatusTrackerSettingListener; import com.intellij.ui.ListCellRendererWrapper; import com.intellij.ui.components.JBCheckBox; import com.intellij.ui.components.JBLabel; +import com.intellij.ui.components.ComponentSettings; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -122,7 +123,7 @@ public class EditorOptionsPanel implements SearchableConfigurable { myStripTrailingSpacesCombo.addItem(STRIP_CHANGED); myStripTrailingSpacesCombo.addItem(STRIP_ALL); myStripTrailingSpacesCombo.addItem(STRIP_NONE); - + myStripTrailingSpacesCombo.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -256,6 +257,7 @@ public class EditorOptionsPanel implements SearchableConfigurable { // Display editorSettings.setSmoothScrolling(myCbSmoothScrolling.isSelected()); + ComponentSettings.getInstance().setSmoothScrollingEnabled(myCbSmoothScrolling.isSelected()); // Brace Highlighting @@ -452,7 +454,7 @@ public class EditorOptionsPanel implements SearchableConfigurable { // Strip trailing spaces, ensure EOL on EOF on save isModified |= !getStripTrailingSpacesValue().equals(editorSettings.getStripTrailingSpaces()); isModified |= isModified(myCbKeepTrailingSpacesOnCaretLine, editorSettings.isKeepTrailingSpacesOnCaretLine()); - + isModified |= isModified(myCbEnsureBlankLineBeforeCheckBox, editorSettings.isEnsureNewLineAtEOF()); isModified |= isModified(myCbShowQuickDocOnMouseMove, editorSettings.isShowQuickDocOnMouseOverElement()); diff --git a/platform/platform-api/src/com/intellij/ui/components/ComponentSettings.java b/platform/platform-api/src/com/intellij/ui/components/ComponentSettings.java new file mode 100644 index 000000000000..3c69a21a3ea3 --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/components/ComponentSettings.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2016 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.ui.components; + +/** + * IDE-agnostic component settings. + */ +public class ComponentSettings { + private boolean mySmoothScrollingEnabled; + private boolean myRemoteDesktopConnected; + + private static final ComponentSettings ourInstance = new ComponentSettings(); + + public static ComponentSettings getInstance() { + return ourInstance; + } + + public boolean isSmoothScrollingEligible() { + return mySmoothScrollingEnabled && !myRemoteDesktopConnected; + } + + public void setSmoothScrollingEnabled(boolean enabled) { + mySmoothScrollingEnabled = enabled; + } + + public void setRemoteDesktopConnected(boolean connected) { + myRemoteDesktopConnected = connected; + } +} diff --git a/platform/platform-impl/src/com/intellij/ide/RemoteDesktopDetector.java b/platform/platform-impl/src/com/intellij/ide/RemoteDesktopDetector.java index 4796e2c52a1c..6fb6bd384bc4 100644 --- a/platform/platform-impl/src/com/intellij/ide/RemoteDesktopDetector.java +++ b/platform/platform-impl/src/com/intellij/ide/RemoteDesktopDetector.java @@ -23,6 +23,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.SystemInfo; +import com.intellij.ui.components.ComponentSettings; import com.sun.jna.platform.win32.User32; public class RemoteDesktopDetector { @@ -56,6 +57,7 @@ public class RemoteDesktopDetector { LOG.debug("Detected remote desktop: ", newValue); if (newValue != myRemoteDesktopConnected) { myRemoteDesktopConnected = newValue; + ComponentSettings.getInstance().setRemoteDesktopConnected(newValue); if (myRemoteDesktopConnected) { // We postpone notification to avoid recursive initialization of RemoteDesktopDetector // (in case it's initialized by request from com.intellij.notification.EventLog) diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java index 91e498429a3e..8c29dbee3cf9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java @@ -23,6 +23,7 @@ import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; import com.intellij.openapi.editor.impl.softwrap.SoftWrapAppliancePlaces; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.components.ComponentSettings; import org.intellij.lang.annotations.MagicConstant; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -148,6 +149,7 @@ public class EditorSettingsExternalizable implements PersistentStateComponent Date: Wed, 14 Dec 2016 17:44:58 +0100 Subject: [PATCH 225/558] true smooth scrolling: use high-precision mouse wheel events --- .../intellij/ui/components/JBScrollPane.java | 9 +- .../ui/components/SmoothScrollPane.java | 128 ++++++++++++++++++ 2 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 platform/platform-api/src/com/intellij/ui/components/SmoothScrollPane.java diff --git a/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java b/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java index 063cb0245dc8..b0ce6ee219fd 100644 --- a/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java +++ b/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java @@ -23,6 +23,7 @@ import com.intellij.openapi.wm.IdeGlassPane; import com.intellij.ui.IdeBorderFactory; import com.intellij.util.ArrayUtil; import com.intellij.util.ReflectionUtil; +import com.intellij.util.SystemProperties; import com.intellij.util.ui.ButtonlessScrollBarUI; import com.intellij.util.ui.JBInsets; import com.intellij.util.ui.RegionPainter; @@ -47,7 +48,7 @@ import java.lang.reflect.Field; import static com.intellij.util.ui.JBUI.emptyInsets; -public class JBScrollPane extends JScrollPane { +public class JBScrollPane extends SmoothScrollPane { /** * This key is used to specify which colors should use the scroll bars on the pane. * If a client property is set to {@code true} the bar's brightness @@ -730,8 +731,10 @@ public class JBScrollPane extends JScrollPane { * @return {@code true} if the specified event is valid, {@code false} otherwise */ public static boolean isScrollEvent(@NotNull MouseWheelEvent event) { - if (event.isConsumed()) return false; // event should not be consumed already - if (event.getWheelRotation() == 0) return false; // any rotation expected (forward or backward) + // event should not be consumed already + if (event.isConsumed()) return false; + // any rotation expected (forward or backward) + if (!SystemProperties.isTrueSmoothScrollingEnabled() && event.getWheelRotation() == 0) return false; return 0 == (SCROLL_MODIFIERS & event.getModifiers()); } diff --git a/platform/platform-api/src/com/intellij/ui/components/SmoothScrollPane.java b/platform/platform-api/src/com/intellij/ui/components/SmoothScrollPane.java new file mode 100644 index 000000000000..a7f885a65996 --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/components/SmoothScrollPane.java @@ -0,0 +1,128 @@ +/* + * Copyright 2000-2016 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.ui.components; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.util.SystemProperties; +import org.intellij.lang.annotations.MagicConstant; + +import javax.swing.*; +import javax.swing.plaf.ScrollPaneUI; +import javax.swing.plaf.basic.BasicScrollPaneUI; +import java.awt.*; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.lang.reflect.Field; + +import static java.lang.Math.*; + +/** + * Scroll pane that can handle high-precision mouse wheel events. + *

    + * Although Java 7 introduced {@link MouseWheelEvent#getPreciseWheelRotation()} method, + * {@link JScrollPane} doesn't use it so far. + * + * @see BasicScrollPaneUI.Handler#mouseWheelMoved(MouseWheelEvent) + * @see javax.swing.plaf.basic.BasicScrollBarUI#scrollByUnits + */ +public class SmoothScrollPane extends JScrollPane { + private static final Logger LOG = Logger.getInstance(SmoothScrollPane.class); + private static final double EPSILON = 1E-5D; + + public SmoothScrollPane() { + } + + public SmoothScrollPane(Component view) { + super(view); + } + + public SmoothScrollPane(int vsbPolicy, int hsbPolicy) { + super(vsbPolicy, hsbPolicy); + } + + public SmoothScrollPane(Component view, int vsbPolicy, int hsbPolicy) { + super(view, vsbPolicy, hsbPolicy); + } + + @Override + public void setUI(ScrollPaneUI ui) { + super.setUI(ui); + + if (ui instanceof BasicScrollPaneUI) { + try { + Field field = BasicScrollPaneUI.class.getDeclaredField("mouseScrollListener"); + field.setAccessible(true); + + Object value = field.get(ui); + + if (value instanceof MouseWheelListener) { + MouseWheelListener oldListener = (MouseWheelListener)value; + MouseWheelListener newListener = e -> handleMouseWheelEvent(e, oldListener); + field.set(ui, newListener); + removeMouseWheelListener(oldListener); + addMouseWheelListener(newListener); + } + } + catch (Exception exception) { + LOG.warn(exception); + } + } + } + + private void handleMouseWheelEvent(MouseWheelEvent e, MouseWheelListener delegate) { + if (SystemProperties.isTrueSmoothScrollingEnabled() && ComponentSettings.getInstance().isSmoothScrollingEligible() && + isWheelScrollingEnabled() && e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) { + + mouseWheelMoved(e); + e.consume(); + } + else { + delegate.mouseWheelMoved(e); + } + } + + private void mouseWheelMoved(MouseWheelEvent e) { + JScrollBar scrollbar = e.isShiftDown() ? getHorizontalScrollBar() : getVerticalScrollBar(); + + @MagicConstant(intValues = {SwingConstants.HORIZONTAL, SwingConstants.VERTICAL}) + int orientation = scrollbar.getOrientation(); + + JViewport viewport = getViewport(); + + if (viewport != null && (viewport.getView() instanceof Scrollable)) { + Scrollable view = (Scrollable)(viewport.getView()); + + double rotation = e.getPreciseWheelRotation(); + + // Use (0, 0) view position to obtain constant unit increment (which might otherwise be variable on smaller-than-unit scrolling). + Rectangle r = new Rectangle(new Point(0, 0), viewport.getViewSize()); + int unitIncrement = view.getScrollableUnitIncrement(r, orientation, 1); + + double delta = rotation * e.getScrollAmount() * unitIncrement; + + boolean limitDelta = abs(rotation) < 1.0D + EPSILON; + int blockIncrement = view.getScrollableBlockIncrement(r, orientation, 1); + double adjustedDelta = limitDelta ? max(-(double)blockIncrement, min(delta, (double)blockIncrement)) : delta; + + int value = scrollbar.getValue(); + int newValue = max(scrollbar.getMinimum(), min((int)round(value + adjustedDelta), scrollbar.getMaximum())); + + if (newValue != value) { + scrollbar.setValue(newValue); + } + } + } +} From ccdd38ceaa8e1ee6550bbe9611142fdd49ca0c22 Mon Sep 17 00:00:00 2001 From: Pavel Fatin Date: Thu, 15 Dec 2016 14:41:25 +0100 Subject: [PATCH 226/558] true smooth scrolling: use interpolation --- .../intellij/ui/components/Interpolator.java | 211 ++++++++++++++++++ .../intellij/ui/components/JBScrollPane.java | 2 +- .../components/SmoothBoundedRangeModel.java | 53 +++++ .../ui/components/SmoothScrollPane.java | 91 +++++++- .../intellij/ui/components/TargetHolder.java | 28 +++ .../openapi/editor/impl/EditorImpl.java | 37 ++- .../editor/impl/ScrollingModelImpl.java | 9 +- .../openapi/editor/impl/SettingsImpl.java | 4 +- 8 files changed, 419 insertions(+), 16 deletions(-) create mode 100644 platform/platform-api/src/com/intellij/ui/components/Interpolator.java create mode 100644 platform/platform-api/src/com/intellij/ui/components/SmoothBoundedRangeModel.java create mode 100644 platform/platform-api/src/com/intellij/ui/components/TargetHolder.java diff --git a/platform/platform-api/src/com/intellij/ui/components/Interpolator.java b/platform/platform-api/src/com/intellij/ui/components/Interpolator.java new file mode 100644 index 000000000000..20acc28c7c46 --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/components/Interpolator.java @@ -0,0 +1,211 @@ +/* + * Copyright 2000-2016 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.ui.components; + +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * A simple interpolator that relies on Swing {@link Timer}. + *

    + * Currently, it performs linear interpolation but we may add support + * for custom curves and ease in / ease out behavior. + */ +public class Interpolator { + private static final int PERIOD = 7; // ms + private static final long TIMEOUT = 500_000000L; // ns + + private final Supplier myInput; + private final Consumer myOutput; + + private final Timer myTimer = new Timer(PERIOD, new TimerListener()); + private final Deque mySegments = new ArrayDeque<>(); + + private boolean myProcessEvents = true; + private long myPreviousEventMoment = 0; + + /** + * Creates a new instance of {@link Interpolator}. + * + * @param input a getter of the value + * @param output a setter of the value + */ + public Interpolator(@NotNull Supplier input, @NotNull Consumer output) { + myInput = input; + myOutput = output; + } + + /** + * Adds a new target value. + * + * @param value target value + * @param delay initial delay + */ + public void setTarget(int value, int delay) { + if (!myProcessEvents) { + return; + } + + if (Objects.equals(getTarget(), value)) { + return; + } + + long moment = System.nanoTime(); + long elapsed = (moment - myPreviousEventMoment); + + if (mySegments.isEmpty()) { + mySegments.add(new Line(new Position(moment, myInput.get()), + new Position(moment + delay * 1000000L, value))); + } + else { + Segment segment = mySegments.getLast(); + mySegments.add(new Line(new Position(segment.getEnd().getMoment(), segment.getEnd().getValue()), + new Position(segment.getEnd().getMoment() + elapsed, value))); + } + + myPreviousEventMoment = moment; + + if (!myTimer.isRunning()) { + myTimer.start(); + } + } + + /** + * Gets the ultimate target value (or the current value if there's no target). + * + * @return the ultimate target value or the current value if there's no target + */ + public int getTarget() { + return mySegments.isEmpty() ? myInput.get() : mySegments.getLast().getEnd().getValue(); + } + + private class TimerListener implements ActionListener { + @Override + public void actionPerformed(ActionEvent event) { + long moment = System.nanoTime(); + + Segment segment = null; + + while (!mySegments.isEmpty()) { + segment = mySegments.getFirst(); + + if (segment.getEnd().getMoment() < moment) { + mySegments.removeFirst(); + continue; + } + + Integer v = segment.getValueAt(moment); + + if (v != null && !Objects.equals(v, myInput.get())) { + setValue(v); + } + + break; + } + + if (mySegments.isEmpty() && segment != null) { + setValue(segment.getEnd().getValue()); + } + + if (moment > myPreviousEventMoment + TIMEOUT) { + myTimer.stop(); + } + } + + private void setValue(Integer v) { + myProcessEvents = false; + myOutput.accept(v); + myProcessEvents = true; + } + } + + private static abstract class Segment { + protected final Position myBegin; + protected final Position myEnd; + + private Segment(Position begin, Position end) { + myBegin = begin; + myEnd = end; + } + + Position getBegin() { + return myBegin; + } + + Position getEnd() { + return myEnd; + } + + long getDuration() { + return myEnd.getMoment() - myBegin.getMoment(); + } + + int getDelta() { + return myEnd.getValue() - myBegin.getValue(); + } + + Integer getValueAt(long moment) { + return contains(moment) ? get(moment) : null; + } + + boolean contains(long moment) { + return myBegin.getMoment() <= moment && moment <= myEnd.getMoment(); + } + + protected abstract Integer get(long moment); + } + + private static class Line extends Segment { + private Line(Position begin, Position end) { + super(begin, end); + } + + @Override + protected Integer get(long moment) { + int range = myEnd.getValue() - myBegin.getValue(); + long duration = myEnd.getMoment() - myBegin.getMoment(); + long elapsed = moment - myBegin.getMoment(); + double fraction = (double)elapsed / duration; + return (int)Math.round((double)myBegin.getValue() + fraction * range); + } + } + + private static class Position { + private final long myMoment; + private final int myValue; + + private Position(long moment, int value) { + myMoment = moment; + myValue = value; + } + + long getMoment() { + return myMoment; + } + + int getValue() { + return myValue; + } + } +} diff --git a/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java b/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java index b0ce6ee219fd..f392de1ac076 100644 --- a/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java +++ b/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java @@ -245,7 +245,7 @@ public class JBScrollPane extends SmoothScrollPane { return vsbUI instanceof ButtonlessScrollBarUI && !((ButtonlessScrollBarUI)vsbUI).alwaysShowTrack(); } - private class MyScrollBar extends ScrollBar implements IdeGlassPane.TopComponent { + private class MyScrollBar extends SmoothScrollBar implements IdeGlassPane.TopComponent { public MyScrollBar(int orientation) { super(orientation); } diff --git a/platform/platform-api/src/com/intellij/ui/components/SmoothBoundedRangeModel.java b/platform/platform-api/src/com/intellij/ui/components/SmoothBoundedRangeModel.java new file mode 100644 index 000000000000..f9985f1ea538 --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/components/SmoothBoundedRangeModel.java @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2016 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.ui.components; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +/** + * A {@link DefaultBoundedRangeModel} that doesn't notify its scrollbar UI listener on value changes + * while the scrollbar value is adjusted (required for interpolation of the thumb input). + */ +public class SmoothBoundedRangeModel extends DefaultBoundedRangeModel { + private static final String SCROLLBAR_UI_LISTENER_PATTERN = "ScrollBarUI"; + + private final JScrollBar myScrollBar; + + public SmoothBoundedRangeModel(JScrollBar scrollBar) { + myScrollBar = scrollBar; + } + + /** + * Implementation of {@link DefaultBoundedRangeModel#fireStateChanged()} with filtering. + */ + @Override + protected void fireStateChanged() { + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ChangeListener.class) { + if (changeEvent == null) { + changeEvent = new ChangeEvent(this); + } + ChangeListener listener = (ChangeListener)listeners[i + 1]; + if (!(myScrollBar.getValueIsAdjusting() && listener.getClass().getName().contains(SCROLLBAR_UI_LISTENER_PATTERN))) { + listener.stateChanged(changeEvent); + } + } + } + } +} diff --git a/platform/platform-api/src/com/intellij/ui/components/SmoothScrollPane.java b/platform/platform-api/src/com/intellij/ui/components/SmoothScrollPane.java index a7f885a65996..43bf50288e6b 100644 --- a/platform/platform-api/src/com/intellij/ui/components/SmoothScrollPane.java +++ b/platform/platform-api/src/com/intellij/ui/components/SmoothScrollPane.java @@ -30,18 +30,28 @@ import java.lang.reflect.Field; import static java.lang.Math.*; /** - * Scroll pane that can handle high-precision mouse wheel events. - *

    - * Although Java 7 introduced {@link MouseWheelEvent#getPreciseWheelRotation()} method, - * {@link JScrollPane} doesn't use it so far. - * - * @see BasicScrollPaneUI.Handler#mouseWheelMoved(MouseWheelEvent) - * @see javax.swing.plaf.basic.BasicScrollBarUI#scrollByUnits + * Scroll pane that supports high-precision mouse wheel events and input interpolation. */ public class SmoothScrollPane extends JScrollPane { private static final Logger LOG = Logger.getInstance(SmoothScrollPane.class); private static final double EPSILON = 1E-5D; + // We may enhance the value derivation (make it distance-dependent, etc), + // additionally, we may add these values to the Registry. + private static final int THUMB_DELAY = 50; // ms + private static final int WHEEL_MIN_DELAY = 60; // ms + private static final int WHEEL_MAX_DELAY = 140; // ms + private static final int DEFAULT_DELAY = 120; // ms + + private enum InputSource { + SCROLLBAR_THUMB, + MOUSE_WHEEL, + UNKNOWN + } + + private InputSource myInputSource = InputSource.UNKNOWN; + private double myWheelRotation; + public SmoothScrollPane() { } @@ -57,6 +67,24 @@ public class SmoothScrollPane extends JScrollPane { super(view, vsbPolicy, hsbPolicy); } + @Override + protected void processMouseWheelEvent(MouseWheelEvent e) { + myInputSource = InputSource.MOUSE_WHEEL; + myWheelRotation = e.getPreciseWheelRotation(); + super.processMouseWheelEvent(e); + myInputSource = InputSource.UNKNOWN; + } + + @Override + public JScrollBar createVerticalScrollBar() { + return new SmoothScrollBar(Adjustable.VERTICAL); + } + + @Override + public JScrollBar createHorizontalScrollBar() { + return new SmoothScrollBar(Adjustable.HORIZONTAL); + } + @Override public void setUI(ScrollPaneUI ui) { super.setUI(ui); @@ -94,6 +122,13 @@ public class SmoothScrollPane extends JScrollPane { } } + /** + * Although Java 7 introduced {@link MouseWheelEvent#getPreciseWheelRotation()} method, + * {@link JScrollPane} doesn't use it so far. + * + * @see BasicScrollPaneUI.Handler#mouseWheelMoved(MouseWheelEvent) + * @see javax.swing.plaf.basic.BasicScrollBarUI#scrollByUnits + */ private void mouseWheelMoved(MouseWheelEvent e) { JScrollBar scrollbar = e.isShiftDown() ? getHorizontalScrollBar() : getVerticalScrollBar(); @@ -117,7 +152,7 @@ public class SmoothScrollPane extends JScrollPane { int blockIncrement = view.getScrollableBlockIncrement(r, orientation, 1); double adjustedDelta = limitDelta ? max(-(double)blockIncrement, min(delta, (double)blockIncrement)) : delta; - int value = scrollbar.getValue(); + int value = scrollbar instanceof TargetHolder ? (((TargetHolder)scrollbar).getTarget()) : scrollbar.getValue(); int newValue = max(scrollbar.getMinimum(), min((int)round(value + adjustedDelta), scrollbar.getMaximum())); if (newValue != value) { @@ -125,4 +160,44 @@ public class SmoothScrollPane extends JScrollPane { } } } + + public int getInitialDelay(boolean valueIsAdjusting) { + InputSource source = valueIsAdjusting ? InputSource.SCROLLBAR_THUMB : myInputSource; + + switch (source) { + case SCROLLBAR_THUMB: + return THUMB_DELAY; + case MOUSE_WHEEL: + return max(WHEEL_MIN_DELAY, min((int)round(abs(myWheelRotation) * WHEEL_MAX_DELAY), WHEEL_MAX_DELAY)); + default: + return DEFAULT_DELAY; + } + } + + protected class SmoothScrollBar extends ScrollBar implements TargetHolder { + private final Interpolator myInterpolator = new Interpolator(super::getValue, super::setValue); + + protected SmoothScrollBar(int orientation) { + super(orientation); + setModel(new SmoothBoundedRangeModel(this)); + } + + @Override + public void setValue(int value) { + if (isShowing() && + SystemProperties.isTrueSmoothScrollingEnabled() && + ComponentSettings.getInstance().isSmoothScrollingEligible()) { + + myInterpolator.setTarget(value, getInitialDelay(getValueIsAdjusting())); + } + else { + super.setValue(value); + } + } + + @Override + public int getTarget() { + return myInterpolator.getTarget(); + } + } } diff --git a/platform/platform-api/src/com/intellij/ui/components/TargetHolder.java b/platform/platform-api/src/com/intellij/ui/components/TargetHolder.java new file mode 100644 index 000000000000..f62bd1acfa67 --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/components/TargetHolder.java @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2016 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.ui.components; + +/** + * An entity that has a target value. + */ +public interface TargetHolder { + /** + * Gets the target value. + * + * @return the target value. + */ + int getTarget(); +} 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 d52fe9f8053e..c6a00492f2d8 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 @@ -29,7 +29,10 @@ import com.intellij.ide.ui.customization.CustomActionsSchema; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.ActionManagerEx; -import com.intellij.openapi.application.*; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.TransactionGuard; +import com.intellij.openapi.application.TransactionGuardImpl; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.UndoConfirmationPolicy; import com.intellij.openapi.diagnostic.Logger; @@ -71,9 +74,7 @@ import com.intellij.openapi.wm.ex.ToolWindowManagerEx; import com.intellij.openapi.wm.impl.IdeGlassPaneImpl; import com.intellij.psi.PsiDocumentManager; import com.intellij.ui.*; -import com.intellij.ui.components.JBLayeredPane; -import com.intellij.ui.components.JBScrollBar; -import com.intellij.ui.components.JBScrollPane; +import com.intellij.ui.components.*; import com.intellij.ui.mac.MacGestureSupportForEditor; import com.intellij.util.*; import com.intellij.util.concurrency.EdtExecutorService; @@ -2682,13 +2683,15 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi private static final Field decrButtonField = ReflectionUtil.getDeclaredField(BasicScrollBarUI.class, "decrButton"); private static final Field incrButtonField = ReflectionUtil.getDeclaredField(BasicScrollBarUI.class, "incrButton"); - class MyScrollBar extends JBScrollBar implements IdeGlassPane.TopComponent { + class MyScrollBar extends JBScrollBar implements IdeGlassPane.TopComponent, TargetHolder { @NonNls private static final String APPLE_LAF_AQUA_SCROLL_BAR_UI_CLASS = "apple.laf.AquaScrollBarUI"; private ScrollBarUI myPersistentUI; + private final Interpolator myInterpolator = new Interpolator(super::getValue, super::setValue); private MyScrollBar(@JdkConstants.AdjustableOrientation int orientation) { super(orientation); setPersistentUI(createEditorScrollbarUI(EditorImpl.this)); + setModel(new SmoothBoundedRangeModel(this)); } void setPersistentUI(ScrollBarUI ui) { @@ -2713,6 +2716,30 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi setOpaque(SystemProperties.isTrueSmoothScrollingEnabled()); } + /** + * Because {@link MyScrollBar} doesn't extend {@link SmoothScrollPane.SmoothScrollBar} + * we need to add the interpolation support separately. + * + * @see SmoothScrollPane.SmoothScrollBar#setValue(int) + */ + @Override + public void setValue(int value) { + if (isShowing() && + SystemProperties.isTrueSmoothScrollingEnabled() && + ComponentSettings.getInstance().isSmoothScrollingEligible()) { + + myInterpolator.setTarget(value, ((MyScrollPane)myScrollPane).getInitialDelay(getValueIsAdjusting())); + } + else { + super.setValue(value); + } + } + + @Override + public int getTarget() { + return myInterpolator.getTarget(); + } + /** * This is helper method. It returns height of the top (decrease) scroll bar * button. Please note, that it's possible to return real height only if scroll bar diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/ScrollingModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/ScrollingModelImpl.java index e068cd0f95ff..d348029e3c5f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/ScrollingModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/ScrollingModelImpl.java @@ -39,6 +39,8 @@ import com.intellij.openapi.editor.ex.ScrollingModelEx; import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.fileEditor.impl.text.AsyncEditorLoader; import com.intellij.openapi.util.Disposer; +import com.intellij.ui.components.TargetHolder; +import com.intellij.util.SystemProperties; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.Animator; import org.jetbrains.annotations.NotNull; @@ -131,6 +133,10 @@ public class ScrollingModelImpl implements ScrollingModelEx { @Override public Rectangle getVisibleAreaOnScrollingFinished() { assertIsDispatchThread(); + if (SystemProperties.isTrueSmoothScrollingEnabled()) { + Rectangle viewRect = myEditor.getScrollPane().getViewport().getViewRect(); + return new Rectangle(getOffset(getHorizontalScrollBar()), getOffset(getVerticalScrollBar()), viewRect.width, viewRect.height); + } if (myCurrentAnimationRequest != null) { return myCurrentAnimationRequest.getTargetVisibleArea(); } @@ -290,7 +296,8 @@ public class ScrollingModelImpl implements ScrollingModelEx { } private static int getOffset(JScrollBar scrollBar) { - return scrollBar == null ? 0 : scrollBar.getValue(); + return scrollBar == null ? 0 : + scrollBar instanceof TargetHolder ? ((TargetHolder)scrollBar).getTarget() : scrollBar.getValue(); } private static int getExtent(JScrollBar scrollBar) { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SettingsImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SettingsImpl.java index 7cffb4ba72ee..1da4ce74f5ce 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SettingsImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SettingsImpl.java @@ -38,6 +38,7 @@ import com.intellij.openapi.util.registry.Registry; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; +import com.intellij.util.SystemProperties; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -492,7 +493,8 @@ public class SettingsImpl implements EditorSettings { @Override public boolean isAnimatedScrolling() { - return myIsAnimatedScrolling != null + return !SystemProperties.isTrueSmoothScrollingEnabled() && // uses its own interpolation + myIsAnimatedScrolling != null ? myIsAnimatedScrolling.booleanValue() : EditorSettingsExternalizable.getInstance().isSmoothScrolling(); } From 76dd1df83abb1ba3af5500c883c1477ac334f933 Mon Sep 17 00:00:00 2001 From: Pavel Fatin Date: Thu, 15 Dec 2016 15:33:13 +0100 Subject: [PATCH 227/558] true smooth scrolling: component settings extended --- .../com/intellij/ui}/ComponentSettings.java | 22 ++++++++++++++++--- .../src/com/intellij/ide/PowerSaveMode.java | 3 +++ .../options/editor/EditorOptionsPanel.java | 2 +- .../ui/components/SmoothScrollPane.java | 8 +++---- .../intellij/ide/RemoteDesktopDetector.java | 2 +- .../ex/EditorSettingsExternalizable.java | 2 +- .../openapi/editor/impl/EditorImpl.java | 5 +---- 7 files changed, 29 insertions(+), 15 deletions(-) rename platform/{platform-api/src/com/intellij/ui/components => core-api/src/com/intellij/ui}/ComponentSettings.java (63%) diff --git a/platform/platform-api/src/com/intellij/ui/components/ComponentSettings.java b/platform/core-api/src/com/intellij/ui/ComponentSettings.java similarity index 63% rename from platform/platform-api/src/com/intellij/ui/components/ComponentSettings.java rename to platform/core-api/src/com/intellij/ui/ComponentSettings.java index 3c69a21a3ea3..18416afeb82e 100644 --- a/platform/platform-api/src/com/intellij/ui/components/ComponentSettings.java +++ b/platform/core-api/src/com/intellij/ui/ComponentSettings.java @@ -13,7 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.ui.components; +package com.intellij.ui; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.util.SystemProperties; + +import java.awt.*; /** * IDE-agnostic component settings. @@ -21,6 +26,7 @@ package com.intellij.ui.components; public class ComponentSettings { private boolean mySmoothScrollingEnabled; private boolean myRemoteDesktopConnected; + private boolean myPowerSaveModeEnabled; private static final ComponentSettings ourInstance = new ComponentSettings(); @@ -28,8 +34,14 @@ public class ComponentSettings { return ourInstance; } - public boolean isSmoothScrollingEligible() { - return mySmoothScrollingEnabled && !myRemoteDesktopConnected; + public boolean isSmoothScrollingEligibleFor(Component component) { + return SystemProperties.isTrueSmoothScrollingEnabled() && + !ApplicationManager.getApplication().isUnitTestMode() && + mySmoothScrollingEnabled && + !myRemoteDesktopConnected && + !myPowerSaveModeEnabled && + component != null && + component.isShowing(); } public void setSmoothScrollingEnabled(boolean enabled) { @@ -39,4 +51,8 @@ public class ComponentSettings { public void setRemoteDesktopConnected(boolean connected) { myRemoteDesktopConnected = connected; } + + public void setPowerSaveModeEnabled(boolean enabled) { + myPowerSaveModeEnabled = enabled; + } } diff --git a/platform/core-impl/src/com/intellij/ide/PowerSaveMode.java b/platform/core-impl/src/com/intellij/ide/PowerSaveMode.java index bae48da54d2e..41da2195c640 100644 --- a/platform/core-impl/src/com/intellij/ide/PowerSaveMode.java +++ b/platform/core-impl/src/com/intellij/ide/PowerSaveMode.java @@ -17,6 +17,7 @@ package com.intellij.ide; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.components.ServiceManager; +import com.intellij.ui.ComponentSettings; import com.intellij.util.messages.MessageBus; import com.intellij.util.messages.Topic; @@ -30,6 +31,7 @@ public class PowerSaveMode { public PowerSaveMode(MessageBus bus) { myBus = bus; + ComponentSettings.getInstance().setPowerSaveModeEnabled(myEnabled); } public static boolean isEnabled() { @@ -42,6 +44,7 @@ public class PowerSaveMode { instance.myEnabled = value; instance.myBus.syncPublisher(TOPIC).powerSaveStateChanged(); PropertiesComponent.getInstance().setValue(POWER_SAVE_MODE, value); + ComponentSettings.getInstance().setPowerSaveModeEnabled(value); } } diff --git a/platform/lang-impl/src/com/intellij/application/options/editor/EditorOptionsPanel.java b/platform/lang-impl/src/com/intellij/application/options/editor/EditorOptionsPanel.java index 62658008c44f..f3b80c687c76 100644 --- a/platform/lang-impl/src/com/intellij/application/options/editor/EditorOptionsPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/editor/EditorOptionsPanel.java @@ -51,7 +51,7 @@ import com.intellij.openapi.vcs.impl.LineStatusTrackerSettingListener; import com.intellij.ui.ListCellRendererWrapper; import com.intellij.ui.components.JBCheckBox; import com.intellij.ui.components.JBLabel; -import com.intellij.ui.components.ComponentSettings; +import com.intellij.ui.ComponentSettings; import org.jetbrains.annotations.NotNull; import javax.swing.*; diff --git a/platform/platform-api/src/com/intellij/ui/components/SmoothScrollPane.java b/platform/platform-api/src/com/intellij/ui/components/SmoothScrollPane.java index 43bf50288e6b..eae839f9d6b6 100644 --- a/platform/platform-api/src/com/intellij/ui/components/SmoothScrollPane.java +++ b/platform/platform-api/src/com/intellij/ui/components/SmoothScrollPane.java @@ -16,6 +16,7 @@ package com.intellij.ui.components; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.ui.ComponentSettings; import com.intellij.util.SystemProperties; import org.intellij.lang.annotations.MagicConstant; @@ -111,7 +112,7 @@ public class SmoothScrollPane extends JScrollPane { } private void handleMouseWheelEvent(MouseWheelEvent e, MouseWheelListener delegate) { - if (SystemProperties.isTrueSmoothScrollingEnabled() && ComponentSettings.getInstance().isSmoothScrollingEligible() && + if (ComponentSettings.getInstance().isSmoothScrollingEligibleFor(this) && isWheelScrollingEnabled() && e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) { mouseWheelMoved(e); @@ -184,10 +185,7 @@ public class SmoothScrollPane extends JScrollPane { @Override public void setValue(int value) { - if (isShowing() && - SystemProperties.isTrueSmoothScrollingEnabled() && - ComponentSettings.getInstance().isSmoothScrollingEligible()) { - + if (ComponentSettings.getInstance().isSmoothScrollingEligibleFor(getViewport().getView())) { myInterpolator.setTarget(value, getInitialDelay(getValueIsAdjusting())); } else { diff --git a/platform/platform-impl/src/com/intellij/ide/RemoteDesktopDetector.java b/platform/platform-impl/src/com/intellij/ide/RemoteDesktopDetector.java index 6fb6bd384bc4..8c51ca0cedd5 100644 --- a/platform/platform-impl/src/com/intellij/ide/RemoteDesktopDetector.java +++ b/platform/platform-impl/src/com/intellij/ide/RemoteDesktopDetector.java @@ -23,7 +23,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.SystemInfo; -import com.intellij.ui.components.ComponentSettings; +import com.intellij.ui.ComponentSettings; import com.sun.jna.platform.win32.User32; public class RemoteDesktopDetector { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java index 8c29dbee3cf9..3146a500da60 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java @@ -23,7 +23,7 @@ import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; import com.intellij.openapi.editor.impl.softwrap.SoftWrapAppliancePlaces; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.ui.components.ComponentSettings; +import com.intellij.ui.ComponentSettings; import org.intellij.lang.annotations.MagicConstant; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; 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 c6a00492f2d8..be3587655295 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 @@ -2724,10 +2724,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi */ @Override public void setValue(int value) { - if (isShowing() && - SystemProperties.isTrueSmoothScrollingEnabled() && - ComponentSettings.getInstance().isSmoothScrollingEligible()) { - + if (ComponentSettings.getInstance().isSmoothScrollingEligibleFor(myEditorComponent)) { myInterpolator.setTarget(value, ((MyScrollPane)myScrollPane).getInitialDelay(getValueIsAdjusting())); } else { From 29a4d5e56b7677d2822e9dada6b21689dd1af197 Mon Sep 17 00:00:00 2001 From: Pavel Fatin Date: Thu, 15 Dec 2016 15:53:02 +0100 Subject: [PATCH 228/558] true smooth scrolling: handle non-Scrollable components --- .../ui/components/SmoothScrollPane.java | 59 +++++++++++-------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/platform/platform-api/src/com/intellij/ui/components/SmoothScrollPane.java b/platform/platform-api/src/com/intellij/ui/components/SmoothScrollPane.java index eae839f9d6b6..84d96a5629b4 100644 --- a/platform/platform-api/src/com/intellij/ui/components/SmoothScrollPane.java +++ b/platform/platform-api/src/com/intellij/ui/components/SmoothScrollPane.java @@ -17,8 +17,7 @@ package com.intellij.ui.components; import com.intellij.openapi.diagnostic.Logger; import com.intellij.ui.ComponentSettings; -import com.intellij.util.SystemProperties; -import org.intellij.lang.annotations.MagicConstant; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.plaf.ScrollPaneUI; @@ -132,36 +131,50 @@ public class SmoothScrollPane extends JScrollPane { */ private void mouseWheelMoved(MouseWheelEvent e) { JScrollBar scrollbar = e.isShiftDown() ? getHorizontalScrollBar() : getVerticalScrollBar(); - - @MagicConstant(intValues = {SwingConstants.HORIZONTAL, SwingConstants.VERTICAL}) - int orientation = scrollbar.getOrientation(); - JViewport viewport = getViewport(); - if (viewport != null && (viewport.getView() instanceof Scrollable)) { - Scrollable view = (Scrollable)(viewport.getView()); + double rotation = e.getPreciseWheelRotation(); + int direction = rotation < 0 ? -1 : 1; + int unitIncrement = getUnitIncrement(viewport, scrollbar, direction); + double delta = rotation * e.getScrollAmount() * unitIncrement; - double rotation = e.getPreciseWheelRotation(); + boolean limitDelta = abs(rotation) < 1.0D + EPSILON; + int blockIncrement = getBlockIncrement(viewport, scrollbar, direction); + double adjustedDelta = limitDelta ? max(-(double)blockIncrement, min(delta, (double)blockIncrement)) : delta; + int value = scrollbar instanceof TargetHolder ? (((TargetHolder)scrollbar).getTarget()) : scrollbar.getValue(); + int newValue = max(scrollbar.getMinimum(), min((int)round(value + adjustedDelta), scrollbar.getMaximum())); + + if (newValue != value) { + scrollbar.setValue(newValue); + } + } + + private static int getUnitIncrement(JViewport viewport, JScrollBar scrollbar, int direction) { + Scrollable scrollable = getScrollable(viewport); + + if (scrollable == null) { + return scrollbar.getUnitIncrement(direction); + } + else { // Use (0, 0) view position to obtain constant unit increment (which might otherwise be variable on smaller-than-unit scrolling). Rectangle r = new Rectangle(new Point(0, 0), viewport.getViewSize()); - int unitIncrement = view.getScrollableUnitIncrement(r, orientation, 1); - - double delta = rotation * e.getScrollAmount() * unitIncrement; - - boolean limitDelta = abs(rotation) < 1.0D + EPSILON; - int blockIncrement = view.getScrollableBlockIncrement(r, orientation, 1); - double adjustedDelta = limitDelta ? max(-(double)blockIncrement, min(delta, (double)blockIncrement)) : delta; - - int value = scrollbar instanceof TargetHolder ? (((TargetHolder)scrollbar).getTarget()) : scrollbar.getValue(); - int newValue = max(scrollbar.getMinimum(), min((int)round(value + adjustedDelta), scrollbar.getMaximum())); - - if (newValue != value) { - scrollbar.setValue(newValue); - } + return scrollable.getScrollableUnitIncrement(r, scrollbar.getOrientation(), 1); } } + private static int getBlockIncrement(JViewport viewport, JScrollBar scrollbar, int direction) { + Scrollable scrollable = getScrollable(viewport); + Rectangle r = new Rectangle(new Point(0, 0), viewport.getViewSize()); + return scrollable == null ? scrollbar.getBlockIncrement(direction) + : scrollable.getScrollableBlockIncrement(r, scrollbar.getOrientation(), 1); + } + + @Nullable + private static Scrollable getScrollable(JViewport viewport) { + return viewport != null && (viewport.getView() instanceof Scrollable) ? (Scrollable)(viewport.getView()) : null; + } + public int getInitialDelay(boolean valueIsAdjusting) { InputSource source = valueIsAdjusting ? InputSource.SCROLLBAR_THUMB : myInputSource; From 61f8872bcd9ec0266ed0c1c9a62b489f957e6206 Mon Sep 17 00:00:00 2001 From: Pavel Fatin Date: Thu, 15 Dec 2016 16:28:37 +0100 Subject: [PATCH 229/558] true smooth scrolling: detailed description --- bin/idea.properties | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/bin/idea.properties b/bin/idea.properties index 200f8a26e94c..c9b96aab0573 100644 --- a/bin/idea.properties +++ b/bin/idea.properties @@ -111,3 +111,27 @@ sun.java2d.uiScale.enabled=true # Uncommenting the option prevents this behavior outside of user home directory. #--------------------------------------------------------------------- #idea.chooser.lookup.for.project.dirs=false + +#----------------------------------------------------------------------- +# Experimental option that does a number of things to make truly smooth scrolling possible: +# +# * Enables hardware-accelerated scrolling. +# Blit-acceleration copies as much of the rendered area as possible and then repaints only newly exposed region. +# This helps to improve scrolling performance and to reduce CPU usage (especially if drawing is compute-intensive). +# +# * Enables "true double buffering". +# True double buffering is needed to eliminate tearing on blit-accelerated scrolling and to restore +# frame buffer content without the usual repainting, even when the EDT is blocked. +# +# * Adds "idea.true.smooth.scrolling.debug" option. +# Checks whether blit-accelerated scrolling is feasible, and if so, checks whether true double buffering is available. +# +# * Enables handling of high-precision mouse wheel events. +# Although Java 7 introduced MouseWheelEven.getPreciseWheelRotation() method, JScrollPane doesn't use it so far. +# Depends on the Editor / General / Smooth Scrolling setting, remote desktop detection and power save mode state. +# +# * Enables interpolation of scrolling input (scrollbar thumb, mouse wheel, etc). +# Smooths input which lacks both spatial and temporal resolution, performs the rendering asynchronously. +# Depends on the Editor / General / Smooth Scrolling setting, remote desktop detection and power save mode state. +#----------------------------------------------------------------------- +#idea.true.smooth.scrolling=true From 580a514b18de0d212c5f6344ac10523e6fb907c8 Mon Sep 17 00:00:00 2001 From: Pavel Fatin Date: Thu, 15 Dec 2016 18:16:36 +0100 Subject: [PATCH 230/558] true smooth scrolling: improve keyboard page navigation --- .../editor/actions/EditorActionUtil.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java index 7bc40ac6be51..3dc2e7ace656 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java @@ -47,6 +47,7 @@ import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.psi.tree.IElementType; import com.intellij.util.DocumentUtil; import com.intellij.util.EditorPopupHandler; +import com.intellij.util.SystemProperties; import com.intellij.util.text.CharArrayUtil; import org.jetbrains.annotations.NotNull; @@ -85,7 +86,7 @@ public class EditorActionUtil { return; } - Rectangle viewRectangle = editor.getScrollingModel().getVisibleArea(); + Rectangle viewRectangle = getVisibleArea(editor); int lineNumber = editor.getCaretModel().getVisualPosition().line; VisualPosition startPos = editor.xyToVisualPosition(new Point(0, viewRectangle.y)); int start = startPos.line + 1; @@ -103,7 +104,7 @@ public class EditorActionUtil { int columnShift, int lineShift, boolean withSelection) { - Rectangle visibleArea = editor.getScrollingModel().getVisibleArea(); + Rectangle visibleArea = getVisibleArea(editor); VisualPosition pos = editor.getCaretModel().getVisualPosition(); Point caretLocation = editor.visualPositionToXY(pos); int caretVShift = caretLocation.y - visibleArea.y; @@ -787,7 +788,7 @@ public class EditorActionUtil { public static void moveCaretPageUp(@NotNull Editor editor, boolean isWithSelection) { int lineHeight = editor.getLineHeight(); - Rectangle visibleArea = editor.getScrollingModel().getVisibleArea(); + Rectangle visibleArea = getVisibleArea(editor); int linesIncrement = visibleArea.height / lineHeight; editor.getScrollingModel().scrollVertically(visibleArea.y - visibleArea.y % lineHeight - linesIncrement * lineHeight); int lineShift = -linesIncrement; @@ -796,7 +797,7 @@ public class EditorActionUtil { public static void moveCaretPageDown(@NotNull Editor editor, boolean isWithSelection) { int lineHeight = editor.getLineHeight(); - Rectangle visibleArea = editor.getScrollingModel().getVisibleArea(); + Rectangle visibleArea = getVisibleArea(editor); int linesIncrement = visibleArea.height / lineHeight; int allowedBottom = ((EditorEx)editor).getContentSize().height - visibleArea.height; editor.getScrollingModel().scrollVertically( @@ -810,7 +811,7 @@ public class EditorActionUtil { int selectionStart = selectionModel.getLeadSelectionOffset(); CaretModel caretModel = editor.getCaretModel(); LogicalPosition blockSelectionStart = caretModel.getLogicalPosition(); - Rectangle visibleArea = editor.getScrollingModel().getVisibleArea(); + Rectangle visibleArea = getVisibleArea(editor); int lineNumber = visibleArea.y / lineHeight; if (visibleArea.y % lineHeight > 0) { lineNumber++; @@ -826,13 +827,19 @@ public class EditorActionUtil { int selectionStart = selectionModel.getLeadSelectionOffset(); CaretModel caretModel = editor.getCaretModel(); LogicalPosition blockSelectionStart = caretModel.getLogicalPosition(); - Rectangle visibleArea = editor.getScrollingModel().getVisibleArea(); + Rectangle visibleArea = getVisibleArea(editor); int lineNumber = Math.max(0, (visibleArea.y + visibleArea.height) / lineHeight - 1); VisualPosition pos = new VisualPosition(lineNumber, editor.getCaretModel().getVisualPosition().column); editor.getCaretModel().moveToVisualPosition(pos); setupSelection(editor, isWithSelection, selectionStart, blockSelectionStart); } + @NotNull + private static Rectangle getVisibleArea(@NotNull Editor editor) { + return SystemProperties.isTrueSmoothScrollingEnabled() ? editor.getScrollingModel().getVisibleAreaOnScrollingFinished() + : editor.getScrollingModel().getVisibleArea(); + } + public static EditorPopupHandler createEditorPopupHandler(@NotNull final String groupId) { return new EditorPopupHandler() { @Override From 06645aa401ada27a2b15031db0a5f0052e39ff99 Mon Sep 17 00:00:00 2001 From: Anton Tarasov Date: Thu, 15 Dec 2016 20:17:11 +0300 Subject: [PATCH 231/558] Document the ColorScheme's FONT_SCALE attribute --- .../colors/impl/AbstractColorsScheme.java | 30 ++++++++++++------- .../util/src/com/intellij/util/ui/UIUtil.java | 2 ++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java b/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java index eb97b8116e5f..ea6bdc8da192 100644 --- a/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java +++ b/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java @@ -35,6 +35,7 @@ import com.intellij.util.PlatformUtils; import com.intellij.util.containers.ContainerUtilRt; import com.intellij.util.containers.HashMap; import com.intellij.util.ui.JBUI; +import com.intellij.util.ui.UIUtil; import gnu.trove.THashMap; import org.jdom.Element; import org.jetbrains.annotations.NonNls; @@ -71,9 +72,9 @@ public abstract class AbstractColorsScheme implements EditorColorsScheme, Serial private String mySchemeName; private float myConsoleLineSpacing = -1; - + private boolean myIsSaveNeeded; - + private boolean myCanBeDeleted = true; // version influences XML format and triggers migration @@ -115,13 +116,13 @@ public abstract class AbstractColorsScheme implements EditorColorsScheme, Serial @NonNls private static final String META_INFO_ELEMENT = "metaInfo"; @NonNls private static final String PROPERTY_ELEMENT = "property"; @NonNls private static final String PROPERTY_NAME_ATTR = "name"; - + @NonNls private static final String META_INFO_CREATION_TIME = "created"; @NonNls private static final String META_INFO_MODIFIED_TIME = "modified"; @NonNls private static final String META_INFO_IDE = "ide"; @NonNls private static final String META_INFO_IDE_VERSION = "ideVersion"; @NonNls private static final String META_INFO_ORIGINAL = "originalScheme"; - + //endregion protected AbstractColorsScheme(EditorColorsScheme parentScheme) { @@ -201,7 +202,7 @@ public abstract class AbstractColorsScheme implements EditorColorsScheme, Serial myFontPreferences.register(getEditorFontName(), fontSize); initFonts(); } - + @Override public void setQuickDocFontSize(@NotNull FontSize fontSize) { if (myQuickDocFontSize != fontSize) { @@ -259,7 +260,7 @@ public abstract class AbstractColorsScheme implements EditorColorsScheme, Serial public FontSize getQuickDocFontSize() { return myQuickDocFontSize; } - + @Override public float getLineSpacing() { float spacing = myLineSpacing; @@ -269,7 +270,7 @@ public abstract class AbstractColorsScheme implements EditorColorsScheme, Serial protected void initFonts() { String editorFontName = getEditorFontName(); int editorFontSize = getEditorFontSize(); - + myFallbackFontName = FontPreferences.getFallbackName(editorFontName, editorFontSize, myParentScheme); if (myFallbackFontName != null) { editorFontName = myFallbackFontName; @@ -396,8 +397,8 @@ public abstract class AbstractColorsScheme implements EditorColorsScheme, Serial } return defaultScheme; } - - + + private void readMetaInfo(@NotNull Element metaInfoElement) { myMetaInfo.clear(); for (Element e: metaInfoElement.getChildren()) { @@ -448,7 +449,7 @@ public abstract class AbstractColorsScheme implements EditorColorsScheme, Serial put(TODO_DEFAULT_ATTRIBUTES.getExternalName(), of(fromHex("268BD2"), fromHex("54AAE3"))); } }; - + private void readColors(Element childNode) { for (Element colorElement : childNode.getChildren(OPTION_ELEMENT)) { Color valueColor = myValueReader.read(Color.class, colorElement); @@ -557,6 +558,15 @@ public abstract class AbstractColorsScheme implements EditorColorsScheme, Serial parentNode.setAttribute(NAME_ATTR, getName()); parentNode.setAttribute(VERSION_ATTR, Integer.toString(myVersion)); + /** + * FONT_SCALE is used to correctly identify the font size in both the JDK-managed HiDPI mode and + * the IDE-managed HiDPI mode: {@link UIUtil#isJDKManagedHiDPI()}. Also, it helps to distinguish + * the "hidpi-aware" scheme version from the previous one. Namely, the absence of the FONT_SCALE + * attribute in the scheme indicates the previous "hidpi-unaware" scheme and the restored font size + * is reset to default. It's assumed this (transition case) happens only once, after which the IDE + * will be able to restore the font size according to its scale and the IDE HiDPI mode. The default + * FONT_SCALE value should also be written by that reason. + */ JdomKt.addOptionTag(parentNode, FONT_SCALE, String.valueOf(JBUI.scale(1f))); // must precede font options if (myParentScheme != null && myParentScheme != EmptyColorScheme.INSTANCE) { diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index 9a897e2f9e39..76df98175015 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -361,6 +361,8 @@ public class UIUtil { /** * Returns whether the JDK-managed HiDPI mode is enabled. * (True for macOS JDK >= 7.10 versions) + * + * @see JBUI.ScaleType */ public static boolean isJDKManagedHiDPI() { if (jdkManagedHiDPI != null) { From 6485b3003c8e62578d1e777d068e1dc7bb722b8d Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 15 Dec 2016 18:36:40 +0100 Subject: [PATCH 232/558] Cleanup (logging category) --- .../src/com/intellij/codeInsight/AnnotationTargetUtil.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/java-psi-api/src/com/intellij/codeInsight/AnnotationTargetUtil.java b/java/java-psi-api/src/com/intellij/codeInsight/AnnotationTargetUtil.java index 324cb0e657a8..6f48206a0c79 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/AnnotationTargetUtil.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/AnnotationTargetUtil.java @@ -29,7 +29,7 @@ import java.util.Set; * @author peter */ public class AnnotationTargetUtil { - private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.AnnotationUtil"); + private static final Logger LOG = Logger.getInstance(AnnotationTargetUtil.class); public static final Set DEFAULT_TARGETS = ContainerUtil.immutableSet( TargetType.PACKAGE, TargetType.TYPE, TargetType.ANNOTATION_TYPE, TargetType.FIELD, TargetType.METHOD, TargetType.CONSTRUCTOR, @@ -94,11 +94,11 @@ public class AnnotationTargetUtil { if (scope instanceof PsiForeachStatement) { return LOCAL_VARIABLE_TARGETS; } - if (scope instanceof PsiParameterList && scope.getParent() instanceof PsiLambdaExpression && + if (scope instanceof PsiParameterList && scope.getParent() instanceof PsiLambdaExpression && ((PsiParameter)element).getTypeElement() == null) { return TargetType.EMPTY_ARRAY; } - + return PARAMETER_TARGETS; } if (element instanceof PsiLocalVariable) { From 61e02864a59d1861c0bf7f26fdac0acda2ae7b13 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 15 Dec 2016 18:37:45 +0100 Subject: [PATCH 233/558] [java] recognizes Java 9's 'MODULE' annotation target --- java/java-psi-api/src/com/intellij/psi/PsiAnnotation.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/java-psi-api/src/com/intellij/psi/PsiAnnotation.java b/java/java-psi-api/src/com/intellij/psi/PsiAnnotation.java index 7027c6788957..631b70802154 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiAnnotation.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiAnnotation.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -47,7 +47,7 @@ public interface PsiAnnotation extends PsiAnnotationMemberValue, PsiMetaOwner { */ enum TargetType { // see java.lang.annotation.ElementType - TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_USE, TYPE_PARAMETER, + TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_USE, TYPE_PARAMETER, MODULE, // auxiliary value, used when it's impossible to determine annotation's targets UNKNOWN; From cd0fb9a8c1eec563af5dc3dd91261f8e48fdb7d3 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Thu, 15 Dec 2016 20:41:19 +0300 Subject: [PATCH 234/558] revert 5606cca, we are not ready yet --- .../GrAssignmentExpressionImpl.java | 12 +---- .../plugins/groovy/lang/psi/util/PsiUtil.java | 17 ++---- .../lang/resolve/ResolveOperatorTest.groovy | 53 ------------------- 3 files changed, 6 insertions(+), 76 deletions(-) delete mode 100644 plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveOperatorTest.groovy diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrAssignmentExpressionImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrAssignmentExpressionImpl.java index 26b01bcf38eb..15bb02431b5d 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrAssignmentExpressionImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrAssignmentExpressionImpl.java @@ -25,16 +25,12 @@ import com.intellij.psi.impl.source.resolve.ResolveCache; import com.intellij.psi.scope.ElementClassHint; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.tree.IElementType; -import com.intellij.psi.util.CachedValueProvider.Result; -import com.intellij.psi.util.CachedValuesManager; -import com.intellij.psi.util.PsiModificationTracker; import com.intellij.util.ConcurrencyUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.lexer.TokenSets; import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; -import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; @@ -155,11 +151,7 @@ public class GrAssignmentExpressionImpl extends GrOperatorExpressionImpl impleme @Nullable @Override public PsiType getLeftType() { - GrExpression expression = CachedValuesManager.getCachedValue(this, () -> Result.create( - GroovyPsiElementFactory.getInstance(getProject()).createExpressionFromText(getLValue().getText(), getContext()), - PsiModificationTracker.MODIFICATION_COUNT - )); - return expression.getType(); + return getLValue().getType(); } @Nullable @@ -186,7 +178,7 @@ public class GrAssignmentExpressionImpl extends GrOperatorExpressionImpl impleme lType = ((GrIndexProperty)lValue).getGetterType(); } else { - lType = assignmentExpression.getLeftType(); + lType = lValue.getType(); } if (lType == null) return GroovyResolveResult.EMPTY_ARRAY; diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java index 54e331f47894..464a0452fa97 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java @@ -280,22 +280,13 @@ public class PsiUtil { ); } } - else if (parent instanceof GrBinaryExpression) { - GrExpression right = ((GrBinaryExpression)parent).getRightOperand(); + else if (parent instanceof GrBinaryExpression || parent instanceof GrAssignmentExpression) { + GrExpression right = parent instanceof GrBinaryExpression + ? ((GrBinaryExpression)parent).getRightOperand() + : ((GrAssignmentExpression)parent).getRValue(); PsiType type = right != null ? right.getType() : null; return new PsiType[]{notNullizeType(type, nullAsBottom, parent)}; } - else if (parent instanceof GrAssignmentExpression) { - GrAssignmentExpression assignment = (GrAssignmentExpression)parent; - PsiType type; - if (TokenSets.ASSIGNMENTS_TO_OPERATORS.containsKey(assignment.getOperationTokenType())) { - type = assignment.getType(); - } - else { - type = assignment.getRightType(); - } - return new PsiType[]{notNullizeType(type, nullAsBottom, parent)}; - } return null; } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveOperatorTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveOperatorTest.groovy deleted file mode 100644 index 6fd550dda2e7..000000000000 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveOperatorTest.groovy +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2000-2016 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.resolve - -import com.intellij.psi.PsiMethod -import com.intellij.testFramework.LightProjectDescriptor -import groovy.transform.CompileStatic -import org.jetbrains.plugins.groovy.GroovyLightProjectDescriptor -import org.jetbrains.plugins.groovy.LightGroovyTestCase -import org.jetbrains.plugins.groovy.lang.psi.GroovyFile -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression - -@CompileStatic -class ResolveOperatorTest extends LightGroovyTestCase { - - final LightProjectDescriptor projectDescriptor = GroovyLightProjectDescriptor.GROOVY_LATEST - - void "test plus assignment"() { - fixture.with { - addClass '''\ -public class Foo { - private java.util.Set numbers; - public java.util.Set getNumbers() {return null;} - public void setNumbers(java.util.Set files) {} -} -''' - def file = configureByText('_.groovy', '''\ -def f(Foo foo, Integer i) { - foo.numbers += i -} -''') as GroovyFile - def assignment = file.methods.first().block.statements.first() as GrAssignmentExpression - def lValue = assignment.LValue as GrReferenceExpression - def resolved = lValue.resolve() - assert resolved instanceof PsiMethod - assert ((PsiMethod)resolved).name == "setNumbers" - } - } -} From 47d46bf1b3e153368a059b47177a2e687bb53001 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Thu, 15 Dec 2016 20:04:26 +0300 Subject: [PATCH 235/558] do not use our new format in EAP due to stepik restrictions --- .../src/com/jetbrains/edu/learning/stepic/CourseInfo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CourseInfo.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CourseInfo.java index deff5ee2611f..9f0a840570cf 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CourseInfo.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CourseInfo.java @@ -27,7 +27,7 @@ public class CourseInfo { List myAuthors = new ArrayList<>(); @SerializedName("summary") private String myDescription; - @SerializedName("course_format") private String myType = "pycharm" + EduStepicConnector.CURRENT_VERSION + " Python"; //course type in format "pycharm " + @SerializedName("course_format") private String myType = "pycharm" + /*EduStepicConnector.CURRENT_VERSION +*/ " Python"; //course type in format "pycharm " @Nullable private String username; @SerializedName("update_date") private Date updateDate; From 835d8a6f54adb5c41b8d89252bafcb70028bd90e Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Thu, 15 Dec 2016 20:05:04 +0300 Subject: [PATCH 236/558] set last_subtask_index properly --- .../src/com/jetbrains/edu/learning/courseFormat/Task.java | 1 + .../com/jetbrains/edu/learning/stepic/EduStepicConnector.java | 1 + .../src/com/jetbrains/edu/learning/stepic/StepicWrappers.java | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Task.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Task.java index 680399baa5fd..b54508d83820 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Task.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Task.java @@ -45,6 +45,7 @@ public class Task implements StudyItem { @Expose @SerializedName("choice_parameters") private ChoiceParameters myChoiceParameters = new ChoiceParameters(); private int myActiveSubtaskIndex = 0; + @SerializedName("last_subtask_index") @Expose private int myLastSubtaskIndex = 0; public Task() {} diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java index ea2fcb9e18db..4627f4e91750 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java @@ -270,6 +270,7 @@ public class EduStepicConnector { task.setUpdateDate(step.update_date); task.setName(block.options != null ? block.options.title : (PYCHARM_PREFIX + CURRENT_VERSION)); task.setText(block.text); + task.setLastSubtaskIndex(block.options.lastSubtaskIndex); for (StepicWrappers.TestFileWrapper wrapper : block.options.test) { task.addTestsTexts(wrapper.name, wrapper.text); } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java index 37e8d157bed1..c888c5707af0 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java @@ -59,7 +59,7 @@ public class StepicWrappers { public static StepOptions fromTask(final Project project, @NotNull final Task task) { final StepOptions source = new StepOptions(); - task.setLastSubtaskIndex(source.lastSubtaskIndex); + source.lastSubtaskIndex = task.getLastSubtaskIndex(); setTests(task, source, project); source.files = new ArrayList<>(); source.title = task.getName(); From 57f88cfa563f61e2b095469322ba79425e6766fb Mon Sep 17 00:00:00 2001 From: "Ilya.Kazakevich" Date: Thu, 15 Dec 2016 22:14:16 +0300 Subject: [PATCH 237/558] Old python removed from tox test, logging improved * For some reason helper output is mixed in TC, but not locally nor on agent when launched from command line nor from Idea. --- python/testData/toxtest/toxNose/tox.ini | 2 +- python/testSrc/com/jetbrains/env/PyToxTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/python/testData/toxtest/toxNose/tox.ini b/python/testData/toxtest/toxNose/tox.ini index ade6e36e3dcc..ef36c466a028 100644 --- a/python/testData/toxtest/toxNose/tox.ini +++ b/python/testData/toxtest/toxNose/tox.ini @@ -1,6 +1,6 @@ [tox] skipsdist=True -envlist = py26, py27, py32, py34 +envlist = py27, py34 [testenv] deps=nose commands=nosetests -s \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/env/PyToxTest.java b/python/testSrc/com/jetbrains/env/PyToxTest.java index 9481d02cd1c0..89499f9be7c4 100644 --- a/python/testSrc/com/jetbrains/env/PyToxTest.java +++ b/python/testSrc/com/jetbrains/env/PyToxTest.java @@ -71,10 +71,8 @@ public final class PyToxTest extends PyEnvTestCase { runPythonTest(new MyPyProcessWithConsoleTestTask("/toxtest/toxNose/", 1, () -> new MyTestProcessRunner(), Arrays.asList( - Pair.create("py26", new InterpreterExpectations("", true)), Pair.create("py27", new InterpreterExpectations("", true)), // Does not support 3.4 - Pair.create("py32", new InterpreterExpectations("SyntaxError", false)), Pair.create("py34", new InterpreterExpectations("SyntaxError", false)) ), Integer.MAX_VALUE) @@ -284,6 +282,8 @@ public final class PyToxTest extends PyEnvTestCase { // Interpreter should either run tests or mentioned as NotFound for (final SMTestProxy interpreterSuite : runner.getTestProxy().getChildren()) { final String interpreterName = interpreterSuite.getName(); + assert interpreterName.startsWith("py") : String + .format("Bad interpreter name: %s. Tree is %s \n", interpreterName, getTestTree(interpreterSuite, 0)); checkedInterpreters.add(interpreterName); final InterpreterExpectations expectations = myInterpreters.get(interpreterName); From 83585880a74505fadac08d85f841b2fb5354cf23 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Thu, 15 Dec 2016 23:27:20 +0300 Subject: [PATCH 238/558] [vcs-log] minor: use Comparator.comparing --- .../src/com/intellij/vcs/log/ui/VcsLogColorManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogColorManagerImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogColorManagerImpl.java index 2ece71fa5440..671eb1565dca 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogColorManagerImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogColorManagerImpl.java @@ -28,7 +28,7 @@ public class VcsLogColorManagerImpl implements VcsLogColorManager { public VcsLogColorManagerImpl(@NotNull Collection roots) { myRoots = new ArrayList<>(roots); - Collections.sort(myRoots, (o1, o2) -> o1.getName().compareTo(o2.getName())); + Collections.sort(myRoots, Comparator.comparing(VirtualFile::getName)); myRoots2Colors = ContainerUtil.newHashMap(); int i = 0; for (VirtualFile root : myRoots) { From 51f04e37ef62bfdfb79707642e01b1c97126b9a0 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Thu, 15 Dec 2016 23:27:46 +0300 Subject: [PATCH 239/558] [vcs-log] minor: remove unused method --- .../com/intellij/vcs/log/ui/VcsLogColorManagerImpl.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogColorManagerImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogColorManagerImpl.java index 671eb1565dca..cba1943ee6d8 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogColorManagerImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogColorManagerImpl.java @@ -52,15 +52,6 @@ public class VcsLogColorManagerImpl implements VcsLogColorManager { return new JBColor(() -> ColorUtil.mix(baseRootColor, UIUtil.getTableBackground(), 0.75)); } - @NotNull - public static JBColor getIndicatorColor(@NotNull final Color baseRootColor) { - if (Registry.is("vcs.log.square.labels")) return getBackgroundColor(baseRootColor); - return new JBColor(() -> { - if (UIUtil.isUnderDarcula()) return baseRootColor; - return ColorUtil.darker(ColorUtil.softer(baseRootColor), 2); - }); - } - @Override public boolean isMultipleRoots() { return myRoots.size() > 1; From f81a0be408f74f160d646360efac2b68f5ae94a1 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Thu, 15 Dec 2016 23:57:50 +0300 Subject: [PATCH 240/558] [vcs-log] minor: lambdify, use method reference --- .../intellij/vcs/log/ui/tables/GraphTableModel.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) 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 70eaf4853573..f044e5ac1dd4 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 @@ -67,12 +67,7 @@ public class GraphTableModel extends AbstractTableModel { public int getRowOfCommit(@NotNull final Hash hash, @NotNull VirtualFile root) { final int commitIndex = myLogData.getCommitIndex(hash, root); - return ContainerUtil.indexOf(VcsLogUtil.getVisibleCommits(myDataPack.getVisibleGraph()), new Condition() { - @Override - public boolean value(Integer integer) { - return integer == commitIndex; - } - }); + return ContainerUtil.indexOf(VcsLogUtil.getVisibleCommits(myDataPack.getVisibleGraph()), (Condition)i -> i == commitIndex); } public int getRowOfCommitByPartOfHash(@NotNull String partialHash) { @@ -110,7 +105,8 @@ public class GraphTableModel extends AbstractTableModel { case ROOT_COLUMN: return getRoot(rowIndex); case COMMIT_COLUMN: - return new GraphCommitCell(data.getSubject(), getRefsAtRow(rowIndex), myDataPack.getVisibleGraph().getRowInfo(rowIndex).getPrintElements()); + return new GraphCommitCell(data.getSubject(), getRefsAtRow(rowIndex), + myDataPack.getVisibleGraph().getRowInfo(rowIndex).getPrintElements()); case AUTHOR_COLUMN: String authorString = VcsUserUtil.getShortPresentation(data.getAuthor()); return authorString + (VcsUserUtil.isSamePerson(data.getAuthor(), data.getCommitter()) ? "" : "*"); @@ -217,6 +213,6 @@ public class GraphTableModel extends AbstractTableModel { @NotNull public List convertToCommitIds(@NotNull List rows) { - return ContainerUtil.map(rows, (NotNullFunction)row -> getIdAtRow(row)); + return ContainerUtil.map(rows, (NotNullFunction)this::getIdAtRow); } } From 153e9f3940316d1e39ec4a261657d1616d2a1d3e Mon Sep 17 00:00:00 2001 From: Pavel Fatin Date: Thu, 15 Dec 2016 23:05:24 +0100 Subject: [PATCH 241/558] true smooth scrolling: proper default option in the component settings --- platform/core-api/src/com/intellij/ui/ComponentSettings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/core-api/src/com/intellij/ui/ComponentSettings.java b/platform/core-api/src/com/intellij/ui/ComponentSettings.java index 18416afeb82e..634b2a69efd5 100644 --- a/platform/core-api/src/com/intellij/ui/ComponentSettings.java +++ b/platform/core-api/src/com/intellij/ui/ComponentSettings.java @@ -24,7 +24,7 @@ import java.awt.*; * IDE-agnostic component settings. */ public class ComponentSettings { - private boolean mySmoothScrollingEnabled; + private boolean mySmoothScrollingEnabled = true; private boolean myRemoteDesktopConnected; private boolean myPowerSaveModeEnabled; From ac591b7db13db2fe33b81550a14d7939f1bfbad1 Mon Sep 17 00:00:00 2001 From: Pavel Fatin Date: Thu, 15 Dec 2016 23:39:43 +0100 Subject: [PATCH 242/558] true smooth scrolling: newly added getGraphics calls substituted for safelyGetGraphics --- .../src/com/intellij/ui/components/JBViewport.java | 4 +++- .../src/com/intellij/openapi/editor/impl/EditorImpl.java | 3 +-- .../src/com/intellij/openapi/wm/impl/WindowManagerImpl.java | 4 ++-- platform/util/src/com/intellij/util/ui/GraphicsUtil.java | 5 +++-- platform/util/src/com/intellij/util/ui/UIUtil.java | 2 +- .../com/intellij/xml/breadcrumbs/BreadcrumbsComponent.java | 2 +- 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/platform/platform-api/src/com/intellij/ui/components/JBViewport.java b/platform/platform-api/src/com/intellij/ui/components/JBViewport.java index 78a025de5214..5526a5b1e35a 100644 --- a/platform/platform-api/src/com/intellij/ui/components/JBViewport.java +++ b/platform/platform-api/src/com/intellij/ui/components/JBViewport.java @@ -157,7 +157,9 @@ public class JBViewport extends JViewport implements ZoomableViewport { frame buffer content without the usual repainting, even when the EDT is blocked. Generally, this requires default RepaintManager, swing.bufferPerWindow = true and - no prior direct invocations of JComponent.getGraphics() within JRootPane. */ + no prior direct invocations of JComponent.getGraphics() within JRootPane. + + Use a breakpoint in JRootPane.disableTrueDoubleBuffering() to detect direct getGraphics() calls. */ @Nullable private static Boolean isTrueDoubleBufferingAvailableFor(JComponent component) { if (ourGetPaintManagerMethod.isAvailable()) { 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 be3587655295..a6307b6c6ed8 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 @@ -1082,8 +1082,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi DataContext context = getDataContext(); - Graphics graphics = SystemProperties.isTrueSmoothScrollingEnabled() ? - GraphicsUtil.safelyGetGraphics(myEditorComponent) : myEditorComponent.getGraphics(); + Graphics graphics = GraphicsUtil.safelyGetGraphics(myEditorComponent); if (graphics != null) { // editor component is not showing processKeyTypedImmediately(c, graphics, context); graphics.dispose(); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java index 50fbad55ca30..f2dd265ac18f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java @@ -39,8 +39,8 @@ import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeFrame; import com.intellij.ui.ScreenUtil; import com.intellij.util.EventDispatcher; import com.intellij.util.messages.MessageBus; +import com.intellij.util.ui.GraphicsUtil; import com.intellij.util.ui.JBInsets; -import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import com.sun.jna.platform.WindowUtils; import org.jdom.Element; @@ -736,7 +736,7 @@ public final class WindowManagerImpl extends WindowManagerEx implements NamedCom int extendedState = updateFrameBounds(frame); - Rectangle rectangle = ScreenUtil.boundsToDeviceSpace((Graphics2D)frame.getGraphics(), myFrameBounds); + Rectangle rectangle = ScreenUtil.boundsToDeviceSpace((Graphics2D)GraphicsUtil.safelyGetGraphics(frame), myFrameBounds); final Element frameElement = new Element(FRAME_ELEMENT); frameElement.setAttribute(X_ATTR, Integer.toString(rectangle.x)); diff --git a/platform/util/src/com/intellij/util/ui/GraphicsUtil.java b/platform/util/src/com/intellij/util/ui/GraphicsUtil.java index b7e9eeac2d1b..b202ab8bcd67 100644 --- a/platform/util/src/com/intellij/util/ui/GraphicsUtil.java +++ b/platform/util/src/com/intellij/util/ui/GraphicsUtil.java @@ -17,6 +17,7 @@ package com.intellij.util.ui; import com.intellij.openapi.ui.GraphicsConfig; import com.intellij.util.MethodInvocator; +import com.intellij.util.SystemProperties; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -110,8 +111,8 @@ public class GraphicsUtil { True double buffering is needed to eliminate tearing on blit-accelerated scrolling and to restore frame buffer content without the usual repainting, even when the EDT is blocked. */ - public static Graphics safelyGetGraphics(JComponent c) { - return ourSafelyGetGraphicsMethod.isAvailable() + public static Graphics safelyGetGraphics(Component c) { + return SystemProperties.isTrueSmoothScrollingEnabled() && ourSafelyGetGraphicsMethod.isAvailable() ? (Graphics)ourSafelyGetGraphicsMethod.invoke(null, c) : c.getGraphics(); } diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index 76df98175015..24dac5c122e1 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -1940,7 +1940,7 @@ public class UIUtil { @NotNull public static BufferedImage createImage(Component comp, int width, int height, int type) { return comp != null ? - createImage(comp.getGraphics(), width, height, type) : + createImage(GraphicsUtil.safelyGetGraphics(comp), width, height, type) : createImage(width, height, type); } diff --git a/xml/openapi/src/com/intellij/xml/breadcrumbs/BreadcrumbsComponent.java b/xml/openapi/src/com/intellij/xml/breadcrumbs/BreadcrumbsComponent.java index b3d91aa0c8aa..7ea0bd9910bc 100644 --- a/xml/openapi/src/com/intellij/xml/breadcrumbs/BreadcrumbsComponent.java +++ b/xml/openapi/src/com/intellij/xml/breadcrumbs/BreadcrumbsComponent.java @@ -343,7 +343,7 @@ public class BreadcrumbsComponent extends JComponent @Override public Dimension getPreferredSize() { - Graphics2D g2 = (Graphics2D) (SystemProperties.isTrueSmoothScrollingEnabled() ? GraphicsUtil.safelyGetGraphics(this) : getGraphics()); + Graphics2D g2 = (Graphics2D) GraphicsUtil.safelyGetGraphics(this); Dimension dim = new Dimension(Integer.MAX_VALUE, g2 != null ? DEFAULT_PAINTER.getSize("DUMMY", g2.getFontMetrics(), Integer.MAX_VALUE).height + 1 : 1); JBInsets.addTo(dim, getInsets()); return dim; From fabfa27a91613c3f1edcd3658736ff871fe9f510 Mon Sep 17 00:00:00 2001 From: "Gregory.Shrago" Date: Fri, 16 Dec 2016 04:48:10 +0300 Subject: [PATCH 243/558] EA-92900 - assert: ComponentManagerImpl.getPicoContainer --- .../src/com/intellij/openapi/wm/impl/IdeBackgroundUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeBackgroundUtil.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeBackgroundUtil.java index f8f16e5db2ff..88b95bd26bbc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeBackgroundUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeBackgroundUtil.java @@ -174,7 +174,7 @@ public class IdeBackgroundUtil { @NotNull public static String getBackgroundSpec(@Nullable Project project, @NotNull String propertyName) { - String spec = project == null ? null : PropertiesComponent.getInstance(project).getValue(propertyName); + String spec = project == null || project.isDisposed() ? null : PropertiesComponent.getInstance(project).getValue(propertyName); if (spec == null) spec = PropertiesComponent.getInstance().getValue(propertyName); return StringUtil.notNullize(spec, System.getProperty(propertyName, "")); } From d208f260f247dadc6ec0b8648487c12b1afb508f Mon Sep 17 00:00:00 2001 From: "Gregory.Shrago" Date: Fri, 16 Dec 2016 05:15:29 +0300 Subject: [PATCH 244/558] mem leaks: cleanup rootPane (Cancel action leaked via javax.swing.KeyboardManager) --- .../openapi/ui/impl/GlassPaneDialogWrapperPeer.java | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java b/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java index f7dd575ac055..2307ae19586c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java @@ -584,18 +584,8 @@ public class GlassPaneDialogWrapperPeer extends DialogWrapperPeer implements Foc @Override public void dispose() { + setVisible(false); remove(getContentPane()); - repaint(); - - final Runnable disposer = () -> setVisible(false); - - if (EventQueue.isDispatchThread()) { - disposer.run(); - } else { - //noinspection SSBasedInspection - SwingUtilities.invokeLater(disposer); - } - myRootPane = null; } @@ -693,6 +683,7 @@ public class GlassPaneDialogWrapperPeer extends DialogWrapperPeer implements Foc @Override public void dispose() { + DialogWrapper.cleanupRootPane(this); myDialog = null; } From cda8154dbcab44b18a1d59507ced975937819781 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Fri, 16 Dec 2016 10:47:37 +0700 Subject: [PATCH 245/558] FunctionHelper#transform: newNames -> argumentValues; javadoc updated; cosmetics (IDEA-CR-16814) --- .../streamToLoop/FunctionHelper.java | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInspection/streamToLoop/FunctionHelper.java b/java/java-impl/src/com/intellij/codeInspection/streamToLoop/FunctionHelper.java index fea19fc671ed..aa30c6ac21d2 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamToLoop/FunctionHelper.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamToLoop/FunctionHelper.java @@ -55,7 +55,7 @@ abstract class FunctionHelper { return myResultType; } - String getText() { + final String getText() { return getExpression().getText(); } @@ -63,23 +63,26 @@ abstract class FunctionHelper { /** * Try to perform "light" transformation. Works only for single-argument SAM. The function helper decides by itself - * how to name the SAM argument and returns it. After this method invocation normal transform cannot be performed. + * how to name the SAM argument and returns the assigned name. After this method invocation normal transform cannot be performed. * * @return SAM argument name or null if function helper refused to perform a transformation. - * @param type + * @param type type of the input variable (after generic substitution if applicable) */ String tryLightTransform(PsiType type) { return null; } /** - * Perform an adaptation of current function helper to the replacement context with given parameter names. - * Must be called exactly once prior using getExpression() or getText() + * Adapts this function helper converting it to the valid expression and inlining SAM parameters with provided values. + * Must be called exactly once prior using getExpression() or getText(). * * @param context a context for which transformation should be performed - * @param newNames names of the SAM parameters (the length must be exactly the same as number of SAM parameters) + * @param argumentValues values (expressions) of the SAM parameters to be inlined into function body + * (the length must be exactly the same as the number of SAM parameters). + * Usually it's just the references to the existing variables, but other expressions + * (e.g. constant literals) could be used as well if necessary. */ - abstract void transform(StreamToLoopReplacementContext context, String... newNames); + abstract void transform(StreamToLoopReplacementContext context, String... argumentValues); /** * Rename the references of some variable if it's used inside this function @@ -173,8 +176,8 @@ abstract class FunctionHelper { } @Override - void transform(StreamToLoopReplacementContext context, String... newNames) { - LOG.assertTrue(newNames.length == 0); + void transform(StreamToLoopReplacementContext context, String... argumentValues) { + LOG.assertTrue(argumentValues.length == 0); myExpression = context.createExpression("new java.util.HashMap<>()"); } }; @@ -273,7 +276,7 @@ abstract class FunctionHelper { } @Override - void transform(StreamToLoopReplacementContext context, String... newNames) { + void transform(StreamToLoopReplacementContext context, String... argumentValues) { PsiMethodReferenceExpression methodRef = fromText(context, myMethodRef.getText()); PsiExpression qualifier = methodRef.getQualifierExpression(); if(qualifier != null) { @@ -303,7 +306,7 @@ abstract class FunctionHelper { PsiElement body = lambda.getBody(); LOG.assertTrue(body instanceof PsiExpression); myExpression = (PsiExpression)body; - EntryStream.zip(lambda.getParameterList().getParameters(), newNames) + EntryStream.zip(lambda.getParameterList().getParameters(), argumentValues) .forKeyValue((param, newName) -> myExpression = replaceVarReference(myExpression, param.getName(), newName, context)); } @@ -391,9 +394,9 @@ abstract class FunctionHelper { } @Override - void transform(StreamToLoopReplacementContext context, String... newNames) { + void transform(StreamToLoopReplacementContext context, String... argumentValues) { String varName = context.declare(myNameCandidate, myFnType, myExpression.getText()); - myFinalExpression = context.createExpression(varName + "." + myMethodName + "(" + String.join(",", newNames) + ")"); + myFinalExpression = context.createExpression(varName + "." + myMethodName + "(" + String.join(",", argumentValues) + ")"); } } @@ -415,8 +418,8 @@ abstract class FunctionHelper { } @Override - void transform(StreamToLoopReplacementContext context, String... newNames) { - myExpression = context.createExpression(myReference.getText() + "." + myName + "(" + String.join(",", newNames) + ")"); + void transform(StreamToLoopReplacementContext context, String... argumentValues) { + myExpression = context.createExpression(myReference.getText() + "." + myName + "(" + String.join(",", argumentValues) + ")"); } @Override @@ -444,9 +447,9 @@ abstract class FunctionHelper { } @Override - void transform(StreamToLoopReplacementContext context, String... newNames) { - LOG.assertTrue(newNames.length == 1); - myExpression = context.createExpression(newNames[0]); + void transform(StreamToLoopReplacementContext context, String... argumentValues) { + LOG.assertTrue(argumentValues.length == 1); + myExpression = context.createExpression(argumentValues[0]); } } @@ -470,9 +473,9 @@ abstract class FunctionHelper { return myBody; } - void transform(StreamToLoopReplacementContext context, String... newNames) { - LOG.assertTrue(newNames.length == myParameters.length); - EntryStream.zip(myParameters, newNames).forKeyValue( + void transform(StreamToLoopReplacementContext context, String... argumentValues) { + LOG.assertTrue(argumentValues.length == myParameters.length); + EntryStream.zip(myParameters, argumentValues).forKeyValue( (oldName, newName) -> myBody = replaceVarReference(myBody, oldName, newName, context)); } From 9c78db8af09c69c7aba7268c650449dd423422e2 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Fri, 16 Dec 2016 11:12:15 +0700 Subject: [PATCH 246/558] FunctionHelper: fix renaming in lambdas if autogenerated name conflicts with the existing one --- .../streamToLoop/FunctionHelper.java | 34 +++++++++++-------- .../streamToLoop/afterCollectExtends.java | 24 +++++++++++++ .../streamToLoop/beforeCollectExtends.java | 17 ++++++++++ 3 files changed, 60 insertions(+), 15 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterCollectExtends.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeCollectExtends.java diff --git a/java/java-impl/src/com/intellij/codeInspection/streamToLoop/FunctionHelper.java b/java/java-impl/src/com/intellij/codeInspection/streamToLoop/FunctionHelper.java index aa30c6ac21d2..23c0968d4e0f 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamToLoop/FunctionHelper.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamToLoop/FunctionHelper.java @@ -34,7 +34,10 @@ import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; import java.util.function.Consumer; /** @@ -183,6 +186,14 @@ abstract class FunctionHelper { }; } + static boolean hasVarReference(PsiExpression expression, String name, StreamToLoopReplacementContext context) { + PsiLambdaExpression lambda = (PsiLambdaExpression)context.createExpression(name+"->"+expression.getText()); + PsiParameter var = lambda.getParameterList().getParameters()[0]; + PsiElement body = lambda.getBody(); + LOG.assertTrue(body != null); + return ReferencesSearch.search(var, new LocalSearchScope(body)).findFirst() != null; + } + /** * Replaces all the references to the variable {@code name} in given expression with {@code replacement}. * @@ -480,22 +491,15 @@ abstract class FunctionHelper { } void rename(String oldName, String newName, StreamToLoopReplacementContext context) { - OptionalLong idx = StreamEx.of(myParameters).indexOf(newName); - if(idx.isPresent()) { + int idx = ArrayUtil.indexOf(myParameters, newName); + if(idx >= 0) { + // If new name collides with existing parameter, rename it for(int i = 1;; i++) { String paramName = newName+'$'+i; - if (!paramName.equals(oldName) && - !StreamEx.of(myParameters).has(paramName)) { - try { - myBody = replaceVarReference(myBody, newName, paramName, context); - myParameters[(int)idx.getAsLong()] = paramName; - break; - } - catch(IllegalStateException ise) { - // something is really wrong if we already have references to all newName$1, newName$2, ... newName$50 - // or probably IllegalStateException was thrown by something else: at least we don't stuck in endless loop - if(i > 50) throw ise; - } + if (!paramName.equals(oldName) && !StreamEx.of(myParameters).has(paramName) && !hasVarReference(myBody, paramName, context)) { + myBody = replaceVarReference(myBody, newName, paramName, context); + myParameters[idx] = paramName; + break; } } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterCollectExtends.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterCollectExtends.java new file mode 100644 index 000000000000..197358b2bc1e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/afterCollectExtends.java @@ -0,0 +1,24 @@ +// "Replace Stream API chain with loop" "true" + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class Main { + public List getList() { + return Collections.emptyList(); + } + + private void collect() { + List list = new ArrayList<>(); + for (CharSequence charSequence : getList()) { + if (Objects.nonNull(charSequence)) { + list.add(charSequence); + } + } + List res = list; + System.out.println(res); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeCollectExtends.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeCollectExtends.java new file mode 100644 index 000000000000..14485bc9cf0d --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamToLoop/beforeCollectExtends.java @@ -0,0 +1,17 @@ +// "Replace Stream API chain with loop" "true" + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class Main { + public List getList() { + return Collections.emptyList(); + } + + private void collect() { + List res = getList().stream().filter(Objects::nonNull).collect(Collectors.toList()); + System.out.println(res); + } +} From 60f7a0cfdeae0923ad5e04921a397a6745de43d7 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 16 Dec 2016 11:34:15 +0300 Subject: [PATCH 247/558] Remove redundant files in Memory view, rename packages, add icons --- .../memory/action/ClassesActionBase.java | 19 +- .../memory/action/DebuggerTreeAction.java | 17 +- .../EnableBackgroundTrackingAction.java | 19 +- .../memory/action/JumpToTypeSourceAction.java | 19 +- .../action/ShowClassesWithDiffAction.java | 19 +- .../action/ShowClassesWithInstanceAction.java | 19 +- .../memory/action/ShowInstancesAction.java | 17 +- .../action/ShowInstancesByClassAction.java | 19 +- .../ShowInstancesFromClassesViewAction.java | 21 +- .../memory/action/ShowNewInstancesAction.java | 23 +- .../memory/action/ShowTrackedAction.java | 19 +- .../JumpToAllocationSourceAction.java | 27 +- .../tracking/TrackInstancesToggleAction.java | 27 +- .../component/CreationPositionTracker.java | 19 +- .../memory/component/InstancesTracker.java | 26 +- .../memory/component/MemoryViewManager.java | 21 +- .../component/MemoryViewManagerState.java | 17 +- .../event/InstancesTrackerListener.java | 19 +- .../event/MemoryViewManagerListener.java | 19 +- .../memory/filtering/CheckingResult.java | 33 ++ .../memory/filtering/CheckingResultImpl.java | 50 +++ .../memory/filtering/ConditionChecker.java | 33 ++ .../filtering/ConditionCheckerImpl.java | 110 ++++++ .../memory/filtering/FilteringResult.java | 27 ++ .../memory/filtering/FilteringTask.java | 88 +++++ .../filtering/FilteringTaskCallback.java | 41 ++ .../memory/sizeof/RetainedSizeFetcher.java | 109 ------ .../memory/sizeof/SizeOfEvalStrategy.java | 21 -- .../MemoryViewToolWindowFactory.java | 81 ++-- .../memory/tracking/BackgroundTracker.java | 17 +- .../tracking/ClassPreparedListener.java | 17 +- .../tracking/ConstructorInstancesTracker.java | 57 ++- .../memory/tracking/HashBasedTracking.java | 169 --------- .../tracking/IdentityBasedTracking.java | 37 -- .../tracking/InstanceTrackingStrategy.java | 52 --- .../tracking/RetainReferencesTracking.java | 30 -- .../tracking/TrackerForNewInstances.java | 17 +- .../memory/tracking/TrackingType.java | 35 +- .../{view => ui}/ClassesFilteredView.java | 76 ++-- .../memory/{view => ui}/ClassesTable.java | 145 +++---- .../ExpressionEditorWithHistory.java | 28 +- .../{view => ui}/FilteringProgressView.java | 31 +- .../memory/{view => ui}/InstancesTree.java | 23 +- .../memory/{view => ui}/InstancesWindow.java | 353 ++++++++---------- .../InstancesWithStackFrameView.java | 52 ++- .../memory/{view => ui}/StackFrameList.java | 29 +- .../memory/{view => ui}/StackFramePopup.java | 19 +- .../utils/AbstractTableColumnDescriptor.java | 17 +- .../utils/AbstractTableModelWithColumns.java | 17 +- .../debugger/memory/utils/AndroidUtil.java | 21 +- .../memory/utils/ErrorsValueGroup.java | 21 +- .../memory/utils/InstanceJavaValue.java | 27 +- .../memory/utils/InstanceValueDescriptor.java | 17 +- .../memory/utils/InstancesProvider.java | 17 +- .../debugger/memory/utils/KeyboardUtils.java | 17 +- .../memory/utils/LowestPriorityCommand.java | 17 +- .../debugger/memory/utils/NamesUtils.java | 17 +- .../utils/SingleAlarmWithMutableDelay.java | 17 +- .../memory/utils/StackFrameDescriptor.java | 17 +- .../src/debugger/memoryView/classTracked.png | Bin 0 -> 378 bytes .../debugger/memoryView/classTracked@2x.png | Bin 0 -> 1050 bytes .../memoryView/classTracked@2x_dark.png | Bin 0 -> 1047 bytes .../debugger/memoryView/classTracked_dark.png | Bin 0 -> 351 bytes .../memoryView/toolWindowDisabled.png | Bin 0 -> 241 bytes .../memoryView/toolWindowDisabled@2x.png | Bin 0 -> 472 bytes .../memoryView/toolWindowDisabled@2x_dark.png | Bin 0 -> 469 bytes .../memoryView/toolWindowDisabled_dark.png | Bin 0 -> 245 bytes .../debugger/memoryView/toolWindowEnabled.png | Bin 0 -> 291 bytes .../memoryView/toolWindowEnabled@2x.png | Bin 0 -> 604 bytes .../memoryView/toolWindowEnabled@2x_dark.png | Bin 0 -> 589 bytes .../memoryView/toolWindowEnabled_dark.png | Bin 0 -> 304 bytes .../util/src/com/intellij/icons/AllIcons.java | 7 + resources/src/componentSets/Debugger.xml | 24 +- resources/src/idea/JavaActions.xml | 55 ++- 74 files changed, 1565 insertions(+), 909 deletions(-) create mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/filtering/CheckingResult.java create mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/filtering/CheckingResultImpl.java create mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/filtering/ConditionChecker.java create mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/filtering/ConditionCheckerImpl.java create mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringResult.java create mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringTask.java create mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringTaskCallback.java delete mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/sizeof/RetainedSizeFetcher.java delete mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/sizeof/SizeOfEvalStrategy.java delete mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/tracking/HashBasedTracking.java delete mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/tracking/IdentityBasedTracking.java delete mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/tracking/InstanceTrackingStrategy.java delete mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/tracking/RetainReferencesTracking.java rename java/debugger/impl/src/com/intellij/debugger/memory/{view => ui}/ClassesFilteredView.java (85%) rename java/debugger/impl/src/com/intellij/debugger/memory/{view => ui}/ClassesTable.java (75%) rename java/debugger/impl/src/com/intellij/debugger/memory/{view => ui}/ExpressionEditorWithHistory.java (76%) rename java/debugger/impl/src/com/intellij/debugger/memory/{view => ui}/FilteringProgressView.java (75%) rename java/debugger/impl/src/com/intellij/debugger/memory/{view => ui}/InstancesTree.java (82%) rename java/debugger/impl/src/com/intellij/debugger/memory/{view => ui}/InstancesWindow.java (59%) rename java/debugger/impl/src/com/intellij/debugger/memory/{view => ui}/InstancesWithStackFrameView.java (70%) rename java/debugger/impl/src/com/intellij/debugger/memory/{view => ui}/StackFrameList.java (83%) rename java/debugger/impl/src/com/intellij/debugger/memory/{view => ui}/StackFramePopup.java (64%) create mode 100644 platform/icons/src/debugger/memoryView/classTracked.png create mode 100644 platform/icons/src/debugger/memoryView/classTracked@2x.png create mode 100644 platform/icons/src/debugger/memoryView/classTracked@2x_dark.png create mode 100644 platform/icons/src/debugger/memoryView/classTracked_dark.png create mode 100644 platform/icons/src/debugger/memoryView/toolWindowDisabled.png create mode 100644 platform/icons/src/debugger/memoryView/toolWindowDisabled@2x.png create mode 100644 platform/icons/src/debugger/memoryView/toolWindowDisabled@2x_dark.png create mode 100644 platform/icons/src/debugger/memoryView/toolWindowDisabled_dark.png create mode 100644 platform/icons/src/debugger/memoryView/toolWindowEnabled.png create mode 100644 platform/icons/src/debugger/memoryView/toolWindowEnabled@2x.png create mode 100644 platform/icons/src/debugger/memoryView/toolWindowEnabled@2x_dark.png create mode 100644 platform/icons/src/debugger/memoryView/toolWindowEnabled_dark.png diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/ClassesActionBase.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ClassesActionBase.java index e534614afd83..f9d148da405b 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/ClassesActionBase.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/ClassesActionBase.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.action; +/* + * Copyright 2000-2016 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.debugger.memory.action; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; @@ -6,7 +21,7 @@ import com.intellij.openapi.project.Project; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.view.ClassesTable; +import com.intellij.debugger.memory.ui.ClassesTable; public abstract class ClassesActionBase extends AnAction { @Override diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/DebuggerTreeAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/DebuggerTreeAction.java index c46d72c5ff97..cca58a47732a 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/DebuggerTreeAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/DebuggerTreeAction.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.action; +/* + * Copyright 2000-2016 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.debugger.memory.action; import com.intellij.debugger.ui.impl.watch.NodeDescriptorProvider; import com.intellij.debugger.ui.tree.NodeDescriptor; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/EnableBackgroundTrackingAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/EnableBackgroundTrackingAction.java index f475aa11e339..39c0459d3b3f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/EnableBackgroundTrackingAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/EnableBackgroundTrackingAction.java @@ -1,9 +1,24 @@ -package org.jetbrains.debugger.memory.action; +/* + * Copyright 2000-2016 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.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.ToggleAction; import com.intellij.openapi.project.Project; -import org.jetbrains.debugger.memory.component.InstancesTracker; +import com.intellij.debugger.memory.component.InstancesTracker; public class EnableBackgroundTrackingAction extends ToggleAction{ diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/JumpToTypeSourceAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/JumpToTypeSourceAction.java index 6e72daadb9ff..ea4c30ecdcfb 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/JumpToTypeSourceAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/JumpToTypeSourceAction.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.action; +/* + * Copyright 2000-2016 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.debugger.memory.action; import com.intellij.codeInsight.navigation.NavigationUtil; import com.intellij.debugger.engine.DebuggerUtils; @@ -49,7 +64,7 @@ public class JumpToTypeSourceAction extends ClassesActionBase { } @Nullable - private ReferenceType getObjectType(@NotNull ReferenceType ref) { + private static ReferenceType getObjectType(@NotNull ReferenceType ref) { if (!(ref instanceof ArrayType)) { return ref; } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithDiffAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithDiffAction.java index f12c349e0593..1461398e528f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithDiffAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithDiffAction.java @@ -1,9 +1,24 @@ -package org.jetbrains.debugger.memory.action; +/* + * Copyright 2000-2016 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.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.ToggleAction; import com.intellij.openapi.project.Project; -import org.jetbrains.debugger.memory.component.MemoryViewManager; +import com.intellij.debugger.memory.component.MemoryViewManager; public class ShowClassesWithDiffAction extends ToggleAction { @Override diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithInstanceAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithInstanceAction.java index 17681f97dfb9..a67b28256979 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithInstanceAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowClassesWithInstanceAction.java @@ -1,9 +1,24 @@ -package org.jetbrains.debugger.memory.action; +/* + * Copyright 2000-2016 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.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.ToggleAction; import com.intellij.openapi.project.Project; -import org.jetbrains.debugger.memory.component.MemoryViewManager; +import com.intellij.debugger.memory.component.MemoryViewManager; public class ShowClassesWithInstanceAction extends ToggleAction { @Override diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesAction.java index 865e805caaf0..2c0317d7c9de 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesAction.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.action; +/* + * Copyright 2000-2016 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.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesByClassAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesByClassAction.java index f4ccf9271eea..be035750b9be 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesByClassAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesByClassAction.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.action; +/* + * Copyright 2000-2016 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.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.Project; @@ -9,7 +24,7 @@ import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.view.InstancesWindow; +import com.intellij.debugger.memory.ui.InstancesWindow; public class ShowInstancesByClassAction extends DebuggerTreeAction { @Override diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesFromClassesViewAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesFromClassesViewAction.java index 040432aeb97a..35ccc88f2c11 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesFromClassesViewAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesFromClassesViewAction.java @@ -1,10 +1,25 @@ -package org.jetbrains.debugger.memory.action; +/* + * Copyright 2000-2016 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.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; -import org.jetbrains.debugger.memory.view.ClassesTable; -import org.jetbrains.debugger.memory.view.InstancesWindow; +import com.intellij.debugger.memory.ui.ClassesTable; +import com.intellij.debugger.memory.ui.InstancesWindow; public class ShowInstancesFromClassesViewAction extends ShowInstancesAction { private static final String POPUP_ELEMENT_LABEL = "Show Instances"; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowNewInstancesAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowNewInstancesAction.java index cfa3922f4089..bc3dc86e68c3 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowNewInstancesAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowNewInstancesAction.java @@ -1,11 +1,26 @@ -package org.jetbrains.debugger.memory.action; +/* + * Copyright 2000-2016 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.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; -import org.jetbrains.debugger.memory.utils.InstancesProvider; -import org.jetbrains.debugger.memory.view.ClassesTable; -import org.jetbrains.debugger.memory.view.InstancesWindow; +import com.intellij.debugger.memory.utils.InstancesProvider; +import com.intellij.debugger.memory.ui.ClassesTable; +import com.intellij.debugger.memory.ui.InstancesWindow; public class ShowNewInstancesAction extends ShowInstancesAction { private static final String POPUP_ELEMENT_LABEL = "Show New Instances"; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowTrackedAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowTrackedAction.java index 9c8228530b45..f0088419173e 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowTrackedAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowTrackedAction.java @@ -1,8 +1,23 @@ -package org.jetbrains.debugger.memory.action; +/* + * Copyright 2000-2016 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.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.ToggleAction; -import org.jetbrains.debugger.memory.component.MemoryViewManager; +import com.intellij.debugger.memory.component.MemoryViewManager; public class ShowTrackedAction extends ToggleAction { @Override diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/JumpToAllocationSourceAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/JumpToAllocationSourceAction.java index 218fdfa6084e..79aaa23e7d8e 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/JumpToAllocationSourceAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/JumpToAllocationSourceAction.java @@ -1,6 +1,23 @@ -package org.jetbrains.debugger.memory.action.tracking; +/* + * Copyright 2000-2016 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.debugger.memory.action.tracking; import com.intellij.debugger.DebuggerManager; +import com.intellij.debugger.memory.action.DebuggerTreeAction; +import com.intellij.debugger.memory.ui.StackFramePopup; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.Project; import com.intellij.psi.search.GlobalSearchScope; @@ -10,11 +27,9 @@ import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.sun.jdi.ObjectReference; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.action.DebuggerTreeAction; -import org.jetbrains.debugger.memory.component.CreationPositionTracker; -import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; -import org.jetbrains.debugger.memory.view.InstancesTree; -import org.jetbrains.debugger.memory.view.StackFramePopup; +import com.intellij.debugger.memory.component.CreationPositionTracker; +import com.intellij.debugger.memory.utils.StackFrameDescriptor; +import com.intellij.debugger.memory.ui.InstancesTree; import java.util.List; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/TrackInstancesToggleAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/TrackInstancesToggleAction.java index 2a35327966df..c3131508ef49 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/TrackInstancesToggleAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/TrackInstancesToggleAction.java @@ -1,5 +1,23 @@ -package org.jetbrains.debugger.memory.action.tracking; +/* + * Copyright 2000-2016 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.debugger.memory.action.tracking; +import com.intellij.debugger.memory.component.InstancesTracker; +import com.intellij.debugger.memory.tracking.TrackingType; +import com.intellij.debugger.memory.ui.ClassesTable; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.ToggleAction; import com.intellij.xdebugger.XDebugSession; @@ -7,9 +25,6 @@ import com.sun.jdi.ArrayType; import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.component.InstancesTracker; -import org.jetbrains.debugger.memory.tracking.TrackingType; -import org.jetbrains.debugger.memory.view.ClassesTable; public class TrackInstancesToggleAction extends ToggleAction { @Override @@ -53,12 +68,12 @@ public class TrackInstancesToggleAction extends ToggleAction { } @Nullable - private ReferenceType getSelectedClass(AnActionEvent e) { + private static ReferenceType getSelectedClass(AnActionEvent e) { return e.getData(ClassesTable.SELECTED_CLASS_KEY); } @Nullable - private XDebugSession getDebugSession(AnActionEvent e) { + private static XDebugSession getDebugSession(AnActionEvent e) { return e.getData(ClassesTable.DEBUG_SESSION_KEY); } } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/component/CreationPositionTracker.java b/java/debugger/impl/src/com/intellij/debugger/memory/component/CreationPositionTracker.java index ee54aab35e3e..a30a57db419a 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/component/CreationPositionTracker.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/component/CreationPositionTracker.java @@ -1,5 +1,21 @@ -package org.jetbrains.debugger.memory.component; +/* + * Copyright 2000-2016 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.debugger.memory.component; +import com.intellij.debugger.memory.utils.StackFrameDescriptor; import com.intellij.openapi.components.AbstractProjectComponent; import com.intellij.openapi.project.Project; import com.intellij.xdebugger.XDebugSession; @@ -8,7 +24,6 @@ import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; import java.util.Iterator; import java.util.List; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/component/InstancesTracker.java b/java/debugger/impl/src/com/intellij/debugger/memory/component/InstancesTracker.java index 1a47159e20ca..c158dfc376f7 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/component/InstancesTracker.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/component/InstancesTracker.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.component; +/* + * Copyright 2000-2016 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.debugger.memory.component; import com.intellij.openapi.Disposable; import com.intellij.openapi.components.*; @@ -8,8 +23,8 @@ import com.intellij.util.containers.HashMap; import com.intellij.util.xmlb.annotations.AbstractCollection; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.event.InstancesTrackerListener; -import org.jetbrains.debugger.memory.tracking.TrackingType; +import com.intellij.debugger.memory.event.InstancesTrackerListener; +import com.intellij.debugger.memory.tracking.TrackingType; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -56,14 +71,11 @@ public class InstancesTracker extends AbstractProjectComponent myDispatcher.getMulticaster().classChanged(name, type); } - public boolean remove(@NotNull String name) { + public void remove(@NotNull String name) { TrackingType removed = myState.classes.remove(name); if (removed != null) { myDispatcher.getMulticaster().classRemoved(name); - return true; } - - return false; } public void addTrackerListener(@NotNull InstancesTrackerListener listener, @NotNull Disposable parentDisposable) { diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManager.java b/java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManager.java index 58d42f890fd6..8d937d65297e 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManager.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManager.java @@ -1,5 +1,22 @@ -package org.jetbrains.debugger.memory.component; +/* + * Copyright 2000-2016 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.debugger.memory.component; +import com.intellij.debugger.memory.event.MemoryViewManagerListener; +import com.intellij.debugger.memory.toolwindow.MemoryViewToolWindowFactory; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.*; @@ -9,8 +26,6 @@ import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.util.EventDispatcher; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; -import org.jetbrains.debugger.memory.toolwindow.MemoryViewToolWindowFactory; @State(name = "MemoryViewSettings", storages = @Storage("memory.view.xml")) public class MemoryViewManager extends ApplicationComponent.Adapter diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManagerState.java b/java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManagerState.java index 07e6f7272c45..678b84e3cb9b 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManagerState.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewManagerState.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.component; +/* + * Copyright 2000-2016 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.debugger.memory.component; public final class MemoryViewManagerState { public boolean isShowWithInstancesOnly = true; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/event/InstancesTrackerListener.java b/java/debugger/impl/src/com/intellij/debugger/memory/event/InstancesTrackerListener.java index e01372032a1c..1fa7e3f94ef8 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/event/InstancesTrackerListener.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/event/InstancesTrackerListener.java @@ -1,7 +1,22 @@ -package org.jetbrains.debugger.memory.event; +/* + * Copyright 2000-2016 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.debugger.memory.event; +import com.intellij.debugger.memory.tracking.TrackingType; import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.tracking.TrackingType; import java.util.EventListener; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/event/MemoryViewManagerListener.java b/java/debugger/impl/src/com/intellij/debugger/memory/event/MemoryViewManagerListener.java index 1c7e1cf8f031..c814114f8731 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/event/MemoryViewManagerListener.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/event/MemoryViewManagerListener.java @@ -1,6 +1,21 @@ -package org.jetbrains.debugger.memory.event; +/* + * Copyright 2000-2016 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.debugger.memory.event; -import org.jetbrains.debugger.memory.component.MemoryViewManagerState; +import com.intellij.debugger.memory.component.MemoryViewManagerState; import org.jetbrains.annotations.NotNull; import java.util.EventListener; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/filtering/CheckingResult.java b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/CheckingResult.java new file mode 100644 index 000000000000..167228298b51 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/CheckingResult.java @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2016 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.debugger.memory.filtering; + +import org.jetbrains.annotations.NotNull; + +/** + * @author Vitaliy.Bibaev + */ +public interface CheckingResult { + @NotNull + Result getResult(); + + @NotNull + String getFailureDescription(); + + enum Result { + MATCH, NO_MATCH, ERROR + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/filtering/CheckingResultImpl.java b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/CheckingResultImpl.java new file mode 100644 index 000000000000..9b640d611985 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/CheckingResultImpl.java @@ -0,0 +1,50 @@ +/* + * Copyright 2000-2016 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.debugger.memory.filtering; + +import org.jetbrains.annotations.NotNull; + +/** + * @author Vitaliy.Bibaev + */ +public class CheckingResultImpl implements CheckingResult { + public static final CheckingResult SUCCESS = new CheckingResultImpl(CheckingResultImpl.Result.MATCH, ""); + public static final CheckingResult FAIL = new CheckingResultImpl(CheckingResultImpl.Result.NO_MATCH, ""); + + private final Result myResult; + private final String myDescription; + + private CheckingResultImpl(@NotNull Result result, @NotNull String description) { + myResult = result; + myDescription = description; + } + + public static CheckingResult error(@NotNull String description) { + return new CheckingResultImpl(Result.ERROR, description); + } + + @Override + @NotNull + public Result getResult() { + return myResult; + } + + @Override + @NotNull + public String getFailureDescription() { + return myDescription; + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/filtering/ConditionChecker.java b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/ConditionChecker.java new file mode 100644 index 000000000000..9dcbc4fdba22 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/ConditionChecker.java @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2016 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.debugger.memory.filtering; + +import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; + +/** + * @author Vitaliy.Bibaev + */ +public interface ConditionChecker { + CheckingResult check(@NotNull ObjectReference ref); + + ConditionChecker ALL_MATCHED_CHECKER = new ConditionChecker() { + @Override + public CheckingResult check(@NotNull ObjectReference ref) { + return CheckingResultImpl.SUCCESS; + } + }; +} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/filtering/ConditionCheckerImpl.java b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/ConditionCheckerImpl.java new file mode 100644 index 000000000000..45752525aa29 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/ConditionCheckerImpl.java @@ -0,0 +1,110 @@ +/* + * Copyright 2000-2016 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.debugger.memory.filtering; + +import com.intellij.debugger.engine.DebugProcessImpl; +import com.intellij.debugger.engine.SuspendContextImpl; +import com.intellij.debugger.engine.evaluation.EvaluateException; +import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; +import com.intellij.debugger.engine.evaluation.TextWithImportsImpl; +import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator; +import com.intellij.debugger.engine.events.DebuggerContextCommandImpl; +import com.intellij.debugger.ui.tree.render.CachedEvaluator; +import com.intellij.openapi.project.Project; +import com.intellij.xdebugger.XExpression; +import com.sun.jdi.BooleanValue; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.Value; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.concurrent.atomic.AtomicReference; + +/** + * @author Vitaliy.Bibaev + */ +public class ConditionCheckerImpl implements ConditionChecker { + private final DebugProcessImpl myDebugProcess; + private final MyCachedEvaluator myEvaluator; + private final AtomicReference myResultReference = new AtomicReference<>(); + + public ConditionCheckerImpl(@NotNull DebugProcessImpl debugProcess, + @Nullable XExpression expression, + @NotNull String className) { + myDebugProcess = debugProcess; + myEvaluator = new MyCachedEvaluator(myDebugProcess.getProject(), className); + myEvaluator.setReferenceExpression(TextWithImportsImpl.fromXExpression(expression)); + } + + @Override + public CheckingResult check(@NotNull ObjectReference ref) { + myDebugProcess.getManagerThread().invokeAndWait(new MyCheckerCommand(ref)); + return myResultReference.get(); + } + + private class MyCheckerCommand extends DebuggerContextCommandImpl { + private final ObjectReference myReference; + + protected MyCheckerCommand(@NotNull ObjectReference ref) { + super(myDebugProcess.getDebuggerContext()); + myReference = ref; + } + + @Override + public Priority getPriority() { + return Priority.LOWEST; + } + + @Override + public void threadAction(@NotNull SuspendContextImpl suspendContext) { + ExpressionEvaluator evaluator; + try { + evaluator = myEvaluator.getEvaluator(); + Value result = + evaluator.evaluate(new EvaluationContextImpl(suspendContext, suspendContext.getFrameProxy(), myReference)); + if (result instanceof BooleanValue && ((BooleanValue)result).value()) { + myResultReference.set(CheckingResultImpl.SUCCESS); + } + else { + myResultReference.set(CheckingResultImpl.FAIL); + } + } + catch (EvaluateException e) { + myResultReference.set(CheckingResultImpl.error(e.getMessage())); + } + } + } + + private static class MyCachedEvaluator extends CachedEvaluator { + private final Project myProject; + private final String myClassName; + + public MyCachedEvaluator(@NotNull Project project, @NotNull String className) { + super(); + myProject = project; + myClassName = className; + } + + ExpressionEvaluator getEvaluator() throws EvaluateException { + return getEvaluator(myProject); + } + + @Override + protected String getClassName() { + return myClassName; + } + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringResult.java b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringResult.java new file mode 100644 index 000000000000..951aaeba6cd7 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringResult.java @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2016 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.debugger.memory.filtering; + +/** + * @author Vitaliy.Bibaev + */ +public enum FilteringResult { + ALL_CHECKED, + + INTERRUPTED, + + LIMIT_REACHED +} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringTask.java b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringTask.java new file mode 100644 index 000000000000..a84b3360cdf5 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringTask.java @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2016 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.debugger.memory.filtering; + +import com.intellij.debugger.engine.DebugProcessImpl; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.xdebugger.XExpression; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * @author Vitaliy.Bibaev + */ +public class FilteringTask implements Runnable { + private final List myReferences; + private final ConditionChecker myChecker; + private final FilteringTaskCallback myCallback; + + private volatile boolean myIsCancelled = false; + + public FilteringTask(@NotNull ReferenceType referenceType, @NotNull DebugProcessImpl debugProcess, + @NotNull XExpression expression, @NotNull List references, + @NotNull FilteringTaskCallback callback) { + myChecker = StringUtil.isEmptyOrSpaces(expression.getExpression()) + ? ConditionChecker.ALL_MATCHED_CHECKER + : new ConditionCheckerImpl(debugProcess, expression, referenceType.name()); + myReferences = references; + myCallback = callback; + } + + public void cancel() { + myIsCancelled = true; + } + + public boolean isCancelled() { + return myIsCancelled; + } + + @Override + public void run() { + myCallback.started(myReferences.size()); + int proceedCount; + for (proceedCount = 0; proceedCount < myReferences.size(); proceedCount++) { + ObjectReference ref = myReferences.get(proceedCount); + CheckingResult result = myChecker.check(ref); + FilteringTaskCallback.Action action = FilteringTaskCallback.Action.CONTINUE; + switch (result.getResult()) { + case MATCH: + action = myCallback.matched(ref); + break; + case NO_MATCH: + action = myCallback.notMatched(ref); + break; + case ERROR: + action = myCallback.error(ref, result.getFailureDescription()); + break; + } + + if (action == FilteringTaskCallback.Action.STOP) { + break; + } + } + + FilteringResult reason = myIsCancelled + ? FilteringResult.INTERRUPTED + : proceedCount == myReferences.size() + ? FilteringResult.ALL_CHECKED + : FilteringResult.LIMIT_REACHED; + + myCallback.completed(reason); + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringTaskCallback.java b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringTaskCallback.java new file mode 100644 index 000000000000..7bb26999406a --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/FilteringTaskCallback.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2016 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.debugger.memory.filtering; + +import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; + +/** + * @author Vitaliy.Bibaev + */ +public interface FilteringTaskCallback { + void started(int totalCount); + + @NotNull + Action matched(@NotNull ObjectReference ref); + + @NotNull + Action notMatched(@NotNull ObjectReference ref); + + @NotNull + Action error(@NotNull ObjectReference ref, @NotNull String description); + + void completed(@NotNull FilteringResult reason); + + enum Action { + STOP, CONTINUE + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/sizeof/RetainedSizeFetcher.java b/java/debugger/impl/src/com/intellij/debugger/memory/sizeof/RetainedSizeFetcher.java deleted file mode 100644 index 78a9f66079ba..000000000000 --- a/java/debugger/impl/src/com/intellij/debugger/memory/sizeof/RetainedSizeFetcher.java +++ /dev/null @@ -1,109 +0,0 @@ -package org.jetbrains.debugger.memory.sizeof; - -import com.sun.jdi.*; - -import java.util.Collection; -import java.util.concurrent.TimeUnit; - -public class RetainedSizeFetcher extends SizeOfEvalStrategy { - private final static long REFERENCE_SIZE = 4; - private final static long CHAR_SIZE = 2; - private final static long INT_SIZE = 4; - private final static long FLOAT_SIZE = 4; - private final static long DOUBLE_SIZE = 8; - private final static long BYTE_SIZE = 1; - private final static long BOOLEAN_SIZE = 1; // ? - private final static long LONG_SIZE = 8; - private final static long SHORT_SIZE = 2; - - private final boolean myCheckReferringObjects; - - public RetainedSizeFetcher(boolean checkReferringObjects) { - myCheckReferringObjects = checkReferringObjects; - } - - private static long getPrimitiveTypeSize(PrimitiveValue value) { - if (value instanceof IntegerValue) { - return INT_SIZE; - } - if (value instanceof LongValue) { - return LONG_SIZE; - } - if (value instanceof DoubleValue) { - return DOUBLE_SIZE; - } - if (value instanceof FloatValue) { - return FLOAT_SIZE; - } - if (value instanceof CharValue) { - return CHAR_SIZE; - } - if (value instanceof ByteValue) { - return BYTE_SIZE; - } - if (value instanceof BooleanValue) { - return BOOLEAN_SIZE; - } - - return SHORT_SIZE; - } - - private long getValueSize(Value value, Collection visited) { - if (value == null) { - return 0; - } - - if (value instanceof PrimitiveValue) { - return getPrimitiveTypeSize((PrimitiveValue) value); - } else { - ObjectReference ref = (ObjectReference) value; - if (visited.contains(ref)) { - return 0; - } - - if (myCheckReferringObjects) { - long startTime = System.nanoTime(); - int needReferringObjectCount = visited.size() + 1; - Collection referringObjects = ref.referringObjects(needReferringObjectCount); - long duration = System.nanoTime() - startTime; - System.out.println("referringObjects(" + needReferringObjectCount + ") duration = " + duration + "ns "+ - "(" + TimeUnit.NANOSECONDS.toMillis(duration) + " ms)."); - if (referringObjects.stream().anyMatch(objectReference -> !visited.contains(objectReference))) { - return 0; - } - } - - return sizeOfImpl(ref, visited, false); - } - } - - @Override - protected long sizeOfImpl(ObjectReference ref, Collection visited, boolean isRoot) { - visited.add(ref); - long result = 0; - - if (ref instanceof ArrayReference) { - ArrayReference arrRef = (ArrayReference) ref; - int length = arrRef.length(); - for (int i = 0; i < length; i++) { - result += getValueSize(arrRef.getValue(i), visited); - } - } - - for (Field field : ref.referenceType().allFields()) { - Value val = ref.getValue(field); - if (field.isStatic()) { - continue; - } - - result += REFERENCE_SIZE; - if (val == null) { - continue; - } - - result += getValueSize(val, visited); - } - - return result; - } -} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/sizeof/SizeOfEvalStrategy.java b/java/debugger/impl/src/com/intellij/debugger/memory/sizeof/SizeOfEvalStrategy.java deleted file mode 100644 index 962909f3fbaf..000000000000 --- a/java/debugger/impl/src/com/intellij/debugger/memory/sizeof/SizeOfEvalStrategy.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.jetbrains.debugger.memory.sizeof; - -import com.sun.jdi.ObjectReference; - -import java.util.Collection; -import java.util.HashSet; -import java.util.concurrent.TimeUnit; - -abstract class SizeOfEvalStrategy { - public long sizeOf(ObjectReference ref) { - long startTime = System.nanoTime(); - long result = sizeOfImpl(ref, new HashSet<>(), true); - long duration = System.nanoTime() - startTime; - System.out.printf("org.jetbrains.plugin.sizeof [%d] == %d. time = %d ns (%d ms).%n", - ref.uniqueID(), result, duration, TimeUnit.NANOSECONDS.toMillis(duration)); - - return result; - } - - protected abstract long sizeOfImpl(ObjectReference ref, Collection visited, boolean isRoot); -} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/toolwindow/MemoryViewToolWindowFactory.java b/java/debugger/impl/src/com/intellij/debugger/memory/toolwindow/MemoryViewToolWindowFactory.java index d844cf1bf9c3..d760f28bbe37 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/toolwindow/MemoryViewToolWindowFactory.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/toolwindow/MemoryViewToolWindowFactory.java @@ -1,13 +1,30 @@ -package org.jetbrains.debugger.memory.toolwindow; +/* + * Copyright 2000-2016 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.debugger.memory.toolwindow; +import com.intellij.debugger.memory.component.MemoryViewManager; +import com.intellij.debugger.memory.ui.ClassesFilteredView; import com.intellij.execution.Executor; import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.execution.ui.RunContentManager; import com.intellij.execution.ui.RunContentWithExecutorListener; +import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.ActionManager; -import com.intellij.openapi.actionSystem.DefaultActionGroup; -import com.intellij.openapi.actionSystem.Separator; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; @@ -23,11 +40,8 @@ import com.intellij.xdebugger.XDebugProcess; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebuggerManager; import com.intellij.xdebugger.XDebuggerManagerListener; -import icons.MemoryViewIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.component.MemoryViewManager; -import org.jetbrains.debugger.memory.view.ClassesFilteredView; import javax.swing.*; import java.awt.*; @@ -50,7 +64,7 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware { myEmptyContent = new JBLabel("Run debugging to see loaded classes", SwingConstants.CENTER); myMemoryViewNotSupportedContent = new JBLabel("The Memory View not available for this session", - SwingConstants.CENTER); + SwingConstants.CENTER); } @Override @@ -60,34 +74,29 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware connection.subscribe(RunContentManager.TOPIC, new RunContentWithExecutorListener() { @Override public void contentSelected(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor) { - SwingUtilities.invokeLater(() -> updateCurrentMemoryView(project, toolWindow)); + ApplicationManager.getApplication().invokeLater(() -> updateCurrentMemoryView(project, toolWindow)); } @Override public void contentRemoved(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor) { - SwingUtilities.invokeLater(() -> updateCurrentMemoryView(project, toolWindow)); + ApplicationManager.getApplication().invokeLater(() -> updateCurrentMemoryView(project, toolWindow)); } }); - ((ToolWindowImpl) toolWindow).getToolWindowManager() - .addToolWindowManagerListener(new ToolWindowManagerAdapter() { - @Override - public void stateChanged() { - if (!myMemoryViews.isEmpty() && !toolWindow.isDisposed()) { - myMemoryViews.values().forEach(classesFilteredView -> - classesFilteredView.setActive(toolWindow.isVisible())); - } + ((ToolWindowImpl)toolWindow).getToolWindowManager() + .addToolWindowManagerListener(new ToolWindowManagerAdapter() { + @Override + public void stateChanged() { + if (!myMemoryViews.isEmpty() && !toolWindow.isDisposed()) { + myMemoryViews.values().forEach(classesFilteredView -> + classesFilteredView.setActive(toolWindow.isVisible())); } - }, project); + } + }, project); - ActionGroup group = new DefaultActionGroup( - ActionManager.getInstance().getAction("MemoryView.ShowOnlyWithInstances"), - ActionManager.getInstance().getAction("MemoryView.ShowOnlyWithDiff"), - ActionManager.getInstance().getAction("MemoryView.ShowOnlyTracked"), - Separator.getInstance(), - ActionManager.getInstance().getAction("MemoryView.EnableTrackingWithClosedWindow") - ); - ((ToolWindowEx) toolWindow).setAdditionalGearActions(group); + final ActionGroup group = (ActionGroup)ActionManager.getInstance().getAction("MemoryView.SettingsPopupActionGroup"); + + ((ToolWindowEx)toolWindow).setAdditionalGearActions(group); toolWindow.getComponent().setLayout(new BorderLayout()); for (XDebugSession session : XDebuggerManager.getInstance(project).getDebugSessions()) { @@ -105,7 +114,8 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware ClassesFilteredView newView = new ClassesFilteredView(session); newView.setActive(toolWindow != null && toolWindow.isVisible()); myMemoryViews.put(session, newView); - } catch (Throwable e) { + } + catch (Throwable e) { LOG.warn("Cannot create new instance of the memory view. " + e.getMessage()); } } @@ -126,7 +136,8 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware if (myMemoryViews.containsKey(session)) { view = myMemoryViews.get(session); replaceToolWindowContent(toolWindow, view); - } else { + } + else { view = null; replaceToolWindowContent(toolWindow, myMemoryViewNotSupportedContent); } @@ -143,7 +154,7 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware replaceToolWindowContent(toolWindow, myEmptyContent); } - private void replaceToolWindowContent(@NotNull ToolWindow toolWindow, JComponent comp) { + private static void replaceToolWindowContent(@NotNull ToolWindow toolWindow, JComponent comp) { LOG.assertTrue(SwingUtilities.isEventDispatchThread()); JComponent toolWindowComp = toolWindow.getComponent(); toolWindowComp.removeAll(); @@ -152,7 +163,7 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware } @Nullable - private ToolWindow getToolWindow(@NotNull Project project) { + private static ToolWindow getToolWindow(@NotNull Project project) { return MemoryViewManager.getInstance().getToolWindow(project); } @@ -170,15 +181,15 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware public void processStopped(@NotNull XDebugProcess debugProcess) { Project project = debugProcess.getSession().getProject(); boolean enabled = Arrays.stream(XDebuggerManager.getInstance(project) - .getDebugSessions()).anyMatch(session -> !session.getDebugProcess().equals(debugProcess)); + .getDebugSessions()).anyMatch(session -> !session.getDebugProcess().equals(debugProcess)); updateIcon(project, enabled); } private void updateIcon(@NotNull Project project, boolean enabled) { ToolWindow toolWindow = MemoryViewManager.getInstance().getToolWindow(project); if (toolWindow != null) { - SwingUtilities.invokeLater(() -> - toolWindow.setIcon(enabled ? MemoryViewIcons.MAIN_ENABLED : MemoryViewIcons.MAIN_DISABLED)); + Icon icon = enabled ? AllIcons.Debugger.MemoryView.ToolWindowEnabled : AllIcons.Debugger.MemoryView.ToolWindowDisabled; + ApplicationManager.getApplication().invokeLater(() -> toolWindow.setIcon(icon)); } } }); @@ -189,7 +200,7 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware private final class MyDebuggerStatusChangedListener implements XDebuggerManagerListener { @Override public void processStarted(@NotNull XDebugProcess xDebugProcess) { - SwingUtilities.invokeLater(() -> { + ApplicationManager.getApplication().invokeLater(() -> { XDebugSession session = xDebugProcess.getSession(); addNewSession(session); updateView(session); @@ -200,7 +211,7 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware public void processStopped(@NotNull XDebugProcess xDebugProcess) { XDebugSession session = xDebugProcess.getSession(); removeSession(session); - SwingUtilities.invokeLater(() -> updateView(session)); + ApplicationManager.getApplication().invokeLater(() -> updateView(session)); } private void updateView(@NotNull XDebugSession debugSession) { diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/BackgroundTracker.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/BackgroundTracker.java index 51ce3955b786..18bf3869b265 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/BackgroundTracker.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/BackgroundTracker.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.tracking; +/* + * Copyright 2000-2016 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.debugger.memory.tracking; interface BackgroundTracker { void setBackgroundMode(boolean isBackgroundMode); diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ClassPreparedListener.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ClassPreparedListener.java index f102d4905e30..411e86e51d95 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ClassPreparedListener.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ClassPreparedListener.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.tracking; +/* + * Copyright 2000-2016 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.debugger.memory.tracking; import com.intellij.debugger.DebuggerManager; import com.intellij.debugger.engine.DebugProcess; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ConstructorInstancesTracker.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ConstructorInstancesTracker.java index 44ebf43d10b8..6e7de547eb4d 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ConstructorInstancesTracker.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ConstructorInstancesTracker.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.tracking; +/* + * Copyright 2000-2016 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.debugger.memory.tracking; import com.intellij.debugger.DebuggerManager; import com.intellij.debugger.engine.DebugProcessImpl; @@ -8,11 +23,15 @@ import com.intellij.debugger.engine.events.DebuggerCommandImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; import com.intellij.debugger.jdi.StackFrameProxyImpl; import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; +import com.intellij.debugger.memory.component.CreationPositionTracker; +import com.intellij.debugger.memory.utils.StackFrameDescriptor; import com.intellij.debugger.ui.breakpoints.JavaLineBreakpointType; import com.intellij.debugger.ui.breakpoints.LineBreakpoint; import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XDebuggerManager; import com.intellij.xdebugger.breakpoints.XBreakpoint; import com.intellij.xdebugger.impl.XDebuggerManagerImpl; import com.intellij.xdebugger.impl.breakpoints.LineBreakpointState; @@ -26,10 +45,8 @@ import com.sun.jdi.request.BreakpointRequest; import com.sun.jdi.request.EventRequest; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.component.CreationPositionTracker; -import org.jetbrains.debugger.memory.component.InstancesTracker; -import org.jetbrains.debugger.memory.event.InstancesTrackerListener; -import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; +import com.intellij.debugger.memory.component.InstancesTracker; +import com.intellij.debugger.memory.event.InstancesTrackerListener; import org.jetbrains.java.debugger.breakpoints.properties.JavaLineBreakpointProperties; import java.util.*; @@ -59,7 +76,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp myPositionTracker = CreationPositionTracker.getInstance(debugSession.getProject()); Project project = debugSession.getProject(); myIsBackgroundTrackingEnabled = InstancesTracker.getInstance(myDebugSession.getProject()) - .isBackgroundTrackingEnabled(); + .isBackgroundTrackingEnabled(); InstancesTracker.getInstance(myDebugSession.getProject()).addTrackerListener(new InstancesTrackerListener() { @Override @@ -71,7 +88,8 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp protected void action() throws Exception { if (newState) { myBreakpoint.enable(); - } else { + } + else { myBreakpoint.disable(); } } @@ -80,14 +98,14 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp } }, this); - myDebugProcess = (DebugProcessImpl) DebuggerManager.getInstance(project) - .getDebugProcess(debugSession.getDebugProcess().getProcessHandler()); + myDebugProcess = (DebugProcessImpl)DebuggerManager.getInstance(project) + .getDebugProcess(debugSession.getDebugProcess().getProcessHandler()); JavaLineBreakpointType breakPointType = new JavaLineBreakpointType(); XBreakpoint bpn = new XLineBreakpointImpl<>(breakPointType, - ((XDebuggerManagerImpl) XDebuggerManagerImpl.getInstance(project)).getBreakpointManager(), - new JavaLineBreakpointProperties(), - new LineBreakpointState<>()); + ((XDebuggerManagerImpl)XDebuggerManager.getInstance(project)).getBreakpointManager(), + new JavaLineBreakpointProperties(), + new LineBreakpointState<>()); myBreakpoint = new MyConstructorBreakpoints(project, bpn); myBreakpoint.createRequestForPreparedClass(myDebugProcess, myReference); @@ -136,7 +154,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp @Override public void dispose() { - myBreakpoint.dispose(); + Disposer.dispose(myBreakpoint); } @Override @@ -148,7 +166,8 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp myIsBackgroundMode = isBackgroundMode; if (isBackgroundMode) { doEnableBackgroundMode(); - } else { + } + else { doDisableBackgroundMode(); } } @@ -164,7 +183,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp } private final class MyConstructorBreakpoints extends LineBreakpoint - implements Disposable { + implements Disposable { private boolean myIsEnabled = false; private final List myRequests = new ArrayList<>(); @@ -205,7 +224,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp @Override public boolean processLocatableEvent(SuspendContextCommandImpl action, LocatableEvent event) - throws EventProcessingException { + throws EventProcessingException { try { SuspendContextImpl suspendContext = action.getSuspendContext(); if (suspendContext != null) { @@ -223,7 +242,8 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp String typeName = loc.declaringType().name(); String methodName = loc.method().name(); return new StackFrameDescriptor(typeName, methodName, loc.lineNumber()); - } catch (EvaluateException e) { + } + catch (EvaluateException e) { return null; } }).filter(Objects::nonNull).collect(Collectors.toList()); @@ -231,7 +251,8 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp } } } - } catch (EvaluateException e) { + } + catch (EvaluateException e) { return false; } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/HashBasedTracking.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/HashBasedTracking.java deleted file mode 100644 index 110137d11191..000000000000 --- a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/HashBasedTracking.java +++ /dev/null @@ -1,169 +0,0 @@ -package org.jetbrains.debugger.memory.tracking; - -import com.intellij.debugger.engine.SuspendContextImpl; -import com.intellij.debugger.engine.jdi.ThreadReferenceProxy; -import com.intellij.openapi.diagnostic.Logger; -import com.sun.jdi.*; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -class HashBasedTracking extends InstanceTrackingStrategy { - private static final Logger LOG = Logger.getInstance(HashBasedTracking.class); - - private final ReferenceType myClassType; - - private MyHashCodeMethodWrapper myHashCodeWrapper; - private Set myHashesSet; - private MyState myState = MyState.WAIT_HASHES; - private int myCount = 0; - HashBasedTracking(@Nullable SuspendContextImpl suspendContext, - @NotNull ReferenceType classType, - @NotNull List initialInstances) { - myClassType = classType; - if (suspendContext != null) { - myHashesSet = toSetOfHashes(evalHashCodes(suspendContext, initialInstances)); - myState = myState.next(); - } - } - - @NotNull - @Override - protected List updateImpl(@NotNull SuspendContextImpl suspendContext, - @NotNull List references) { - Map> ref2hash = evalHashCodes(suspendContext, references); - List newInstances = new ArrayList<>(); - - for (ObjectReference ref : references) { - Optional hash = ref2hash.get(ref); - if (hash.isPresent() && !myState.equals(MyState.WAIT_HASHES) && !myHashesSet.contains(hash.get())) { - newInstances.add(ref); - } - } - - myHashesSet = toSetOfHashes(ref2hash); - myState = myState.next(); - myCount = newInstances.size(); - return newInstances; - } - - @Override - public int getCount() { - return myCount; - } - - @Override - public boolean isReady() { - return myState == MyState.READY; - } - - @NotNull - private Map> evalHashCodes(@NotNull SuspendContextImpl suspendContext, - @NotNull List references) { - MyHashCodeMethodWrapper hashCodeCaller = getHashCodeMethod(myClassType); - Map> result = new HashMap<>(); - ThreadReferenceProxy threadProxy = suspendContext.getThread(); - ThreadReference thread = threadProxy != null ? threadProxy.getThreadReference() : null; - if (thread == null || hashCodeCaller == null) { - LOG.warn(thread == null ? "Thread is null" : "hashCode not found"); - return result; - } - - long start = System.nanoTime(); - for (ObjectReference ref : references) { - Optional hash = Optional.empty(); - try { - if (!ref.isCollected()) { - ref.disableCollection(); - if (!ref.isCollected()) { - hash = hashCodeCaller.eval(ref, suspendContext, thread); - } - ref.enableCollection(); - } - } catch (InvalidTypeException | ClassNotLoadedException | - IncompatibleThreadStateException | InvocationException e) { - LOG.warn("Hash code evaluation failed", e); - } - - result.put(ref, hash); - } - - long duration = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); - LOG.info(String.format("Hash code evaluation: %d ms. Count = %d", duration, references.size())); - - return result; - } - - private Set toSetOfHashes(@NotNull Map> ref2hashCode) { - return ref2hashCode.values().stream() - .filter(Optional::isPresent) - .map(Optional::get) - .collect(Collectors.toCollection(HashSet::new)); - } - - @Nullable - private MyHashCodeMethodWrapper getHashCodeMethod(@NotNull ReferenceType referenceType) { - if (myHashCodeWrapper != null) { - return myHashCodeWrapper; - } - - final List referenceTypes = referenceType.virtualMachine().classesByName("java.util.Objects"); - if (!referenceTypes.isEmpty()) { - ClassType objectsClass = (ClassType) referenceTypes.get(0); - Method hashCodeFromObjects = objectsClass.methodsByName("hashCode").stream() - .filter(method -> method.argumentTypeNames().size() == 1 - && "java.lang.Object".equals(method.argumentTypeNames().get(0))) - .findFirst() - .orElse(null); - - myHashCodeWrapper = (ref, suspendContext, thread) -> { - final Value value = objectsClass.invokeMethod(thread, hashCodeFromObjects, - Collections.singletonList(ref), ClassType.INVOKE_SINGLE_THREADED); - return Optional.of(((IntegerValue) value).value()); - }; - } else { - final Method hashCode = referenceType.methodsByName("hashCode").stream() - .filter(method -> method.argumentTypeNames().isEmpty()) - .findFirst() - .orElse(null); - - myHashCodeWrapper = hashCode == null ? null : (ref, suspendContext, thread) -> { - final Value value = ref.invokeMethod(thread, hashCode, Collections.emptyList(), ClassType.INVOKE_SINGLE_THREADED); - return Optional.of(((IntegerValue) value).value()); - }; - } - - return myHashCodeWrapper; - } - - // WAIT_HASHES -> WAIT_UPDATE -> READY - enum MyState { - WAIT_HASHES { - @Override - public MyState next() { - return WAIT_UPDATE; - } - }, WAIT_UPDATE { - @Override - public MyState next() { - return READY; - } - }, READY { - @Override - public MyState next() { - return this; - } - }; - - public abstract MyState next(); - } - - private interface MyHashCodeMethodWrapper { - Optional eval(@NotNull ObjectReference ref, @NotNull SuspendContextImpl suspendContext, - @NotNull ThreadReference thread) throws InvocationException, InvalidTypeException, - ClassNotLoadedException, IncompatibleThreadStateException; - } -} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/IdentityBasedTracking.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/IdentityBasedTracking.java deleted file mode 100644 index 32e5170103d5..000000000000 --- a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/IdentityBasedTracking.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.jetbrains.debugger.memory.tracking; - -import com.intellij.debugger.engine.SuspendContextImpl; -import com.sun.jdi.ObjectReference; -import org.jetbrains.annotations.NotNull; - -import java.util.List; -import java.util.stream.Collectors; - -class IdentityBasedTracking extends InstanceTrackingStrategy { - private long myLastId = -1; - private int myCount = 0; - IdentityBasedTracking(@NotNull List initialInstances) { - myLastId = getMaxId(initialInstances); - } - - @NotNull - @Override - protected List updateImpl(@NotNull SuspendContextImpl suspendContext, - @NotNull List references) { - List result = references.stream() - .filter(reference -> reference.uniqueID() > myLastId) - .collect(Collectors.toList()); - myLastId = Math.max(myLastId, getMaxId(result)); - myCount = result.size(); - return result; - } - - private long getMaxId(@NotNull List references) { - return references.stream().map(ObjectReference::uniqueID).max(Long::compare).orElse(-1L); - } - - @Override - public int getCount() { - return myCount; - } -} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/InstanceTrackingStrategy.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/InstanceTrackingStrategy.java deleted file mode 100644 index 9385105e4380..000000000000 --- a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/InstanceTrackingStrategy.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.jetbrains.debugger.memory.tracking; - -import com.intellij.debugger.engine.SuspendContextImpl; -import com.sun.jdi.ObjectReference; -import com.sun.jdi.ReferenceType; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public abstract class InstanceTrackingStrategy implements TrackerForNewInstances { - @NotNull - private List myNewInstances = new ArrayList<>(); - private boolean myIsReady = false; - - public static InstanceTrackingStrategy create(@NotNull ReferenceType referenceType, - @Nullable SuspendContextImpl suspendContext, - @NotNull TrackingType type, - @NotNull List init) { - switch (type) { - case IDENTITY: - return new IdentityBasedTracking(init); - case HASH: - return new HashBasedTracking(suspendContext, referenceType, init); - case RETAIN: - return new RetainReferencesTracking(init); - } - - throw new UnsupportedOperationException("Such TrackingType not found"); - } - - @NotNull - public final List getNewInstances() { - return Collections.unmodifiableList(myNewInstances); - } - - public final void update(@NotNull SuspendContextImpl suspendContext, @NotNull List references) { - myNewInstances = updateImpl(suspendContext, references); - myIsReady = true; - } - - @Override - public boolean isReady() { - return myIsReady; - } - - @NotNull - protected abstract List updateImpl(@NotNull SuspendContextImpl suspendContext, - @NotNull List references); -} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/RetainReferencesTracking.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/RetainReferencesTracking.java deleted file mode 100644 index c35070e95d7d..000000000000 --- a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/RetainReferencesTracking.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.jetbrains.debugger.memory.tracking; - -import com.intellij.debugger.engine.SuspendContextImpl; -import com.sun.jdi.ObjectReference; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -class RetainReferencesTracking extends InstanceTrackingStrategy { - private final IdentityBasedTracking myIdTracker; - private List myHardRefs; - RetainReferencesTracking(@NotNull List initialInstances) { - myHardRefs = initialInstances; - myIdTracker = new IdentityBasedTracking(initialInstances); - } - - @NotNull - @Override - protected List updateImpl(@NotNull SuspendContextImpl suspendContext, - @NotNull List references) { - myIdTracker.update(suspendContext, references); - myHardRefs = references; - return myIdTracker.getNewInstances(); - } - - @Override - public int getCount() { - return myHardRefs.size(); - } -} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackerForNewInstances.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackerForNewInstances.java index b6fc329f5d36..9d343c63dd80 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackerForNewInstances.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackerForNewInstances.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.tracking; +/* + * Copyright 2000-2016 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.debugger.memory.tracking; import com.sun.jdi.ObjectReference; import org.jetbrains.annotations.NotNull; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackingType.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackingType.java index a000daaade40..6c645db57043 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackingType.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/TrackingType.java @@ -1,32 +1,33 @@ -package org.jetbrains.debugger.memory.tracking; +/* + * Copyright 2000-2016 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.debugger.memory.tracking; -import com.intellij.ui.JBColor; import org.jetbrains.annotations.NotNull; public enum TrackingType { - - IDENTITY("Id Based Tracking",JBColor.yellow), - HASH("Hash Based Tracking", JBColor.yellow), - RETAIN("Retaining Reference Object Tracking", JBColor.yellow), - CREATION("Track Constructors", JBColor.orange); + CREATION("Track Constructors"); private final String myDescription; - private final JBColor myHighlighting; - TrackingType(@NotNull String description, @NotNull JBColor highlighting) { + TrackingType(@SuppressWarnings("SameParameterValue") @NotNull String description) { myDescription = description; - myHighlighting = highlighting; - } - - @NotNull - public JBColor color() { - return myHighlighting; } @NotNull public String description() { return myDescription; } - - public static JBColor inactiveColor() { return JBColor.GRAY; } } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/view/ClassesFilteredView.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesFilteredView.java similarity index 85% rename from java/debugger/impl/src/com/intellij/debugger/memory/view/ClassesFilteredView.java rename to java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesFilteredView.java index 92b5268239fb..f25d6b714405 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/view/ClassesFilteredView.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesFilteredView.java @@ -1,14 +1,34 @@ -package org.jetbrains.debugger.memory.view; +/* + * Copyright 2000-2016 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.debugger.memory.ui; import com.intellij.debugger.DebuggerManager; +import com.intellij.debugger.DebuggerManagerEx; import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.events.DebuggerCommandImpl; -import com.intellij.debugger.impl.DebuggerManagerImpl; +import com.intellij.debugger.memory.event.MemoryViewManagerListener; +import com.intellij.debugger.memory.utils.KeyboardUtils; +import com.intellij.debugger.memory.utils.LowestPriorityCommand; +import com.intellij.debugger.memory.utils.SingleAlarmWithMutableDelay; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.ActionPopupMenu; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; @@ -22,20 +42,16 @@ import com.sun.jdi.ReferenceType; import com.sun.jdi.VirtualMachine; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.component.CreationPositionTracker; -import org.jetbrains.debugger.memory.component.InstancesTracker; -import org.jetbrains.debugger.memory.component.MemoryViewManager; -import org.jetbrains.debugger.memory.component.MemoryViewManagerState; -import org.jetbrains.debugger.memory.event.InstancesTrackerListener; -import org.jetbrains.debugger.memory.event.MemoryViewManagerListener; -import org.jetbrains.debugger.memory.tracking.ClassPreparedListener; -import org.jetbrains.debugger.memory.tracking.ConstructorInstancesTracker; -import org.jetbrains.debugger.memory.tracking.TrackerForNewInstances; -import org.jetbrains.debugger.memory.tracking.TrackingType; -import org.jetbrains.debugger.memory.utils.AndroidUtil; -import org.jetbrains.debugger.memory.utils.KeyboardUtils; -import org.jetbrains.debugger.memory.utils.LowestPriorityCommand; -import org.jetbrains.debugger.memory.utils.SingleAlarmWithMutableDelay; +import com.intellij.debugger.memory.component.CreationPositionTracker; +import com.intellij.debugger.memory.component.InstancesTracker; +import com.intellij.debugger.memory.component.MemoryViewManager; +import com.intellij.debugger.memory.component.MemoryViewManagerState; +import com.intellij.debugger.memory.event.InstancesTrackerListener; +import com.intellij.debugger.memory.tracking.ClassPreparedListener; +import com.intellij.debugger.memory.tracking.ConstructorInstancesTracker; +import com.intellij.debugger.memory.tracking.TrackerForNewInstances; +import com.intellij.debugger.memory.tracking.TrackingType; +import com.intellij.debugger.memory.utils.AndroidUtil; import javax.swing.FocusManager; import javax.swing.*; @@ -49,8 +65,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; -import static org.jetbrains.debugger.memory.view.ClassesTable.DiffViewTableModel.CLASSNAME_COLUMN_INDEX; -import static org.jetbrains.debugger.memory.view.ClassesTable.DiffViewTableModel.DIFF_COLUMN_INDEX; +import static com.intellij.debugger.memory.ui.ClassesTable.DiffViewTableModel.CLASSNAME_COLUMN_INDEX; +import static com.intellij.debugger.memory.ui.ClassesTable.DiffViewTableModel.DIFF_COLUMN_INDEX; public class ClassesFilteredView extends BorderLayoutPanel implements Disposable { private static final Logger LOG = Logger.getInstance(ClassesFilteredView.class); @@ -221,7 +237,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void sessionResumed() { myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::obsolete); - SwingUtilities.invokeLater(() -> { + ApplicationManager.getApplication().invokeLater(() -> { myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNING); myTable.hideContent(); }); @@ -235,7 +251,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void sessionPaused() { - SwingUtilities.invokeLater(() -> myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_SUSPENDED)); + ApplicationManager.getApplication().invokeLater(() -> myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_SUSPENDED)); updateClassesAndCounts(); } }; @@ -243,7 +259,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable mySingleAlarm = new SingleAlarmWithMutableDelay(() -> { myLastSuspendContext = getSuspendContext(); if (myLastSuspendContext != null) { - SwingUtilities.invokeLater(() -> myTable.setBusy(true)); + ApplicationManager.getApplication().invokeLater(() -> myTable.setBusy(true)); myDebugProcess.getManagerThread() .schedule(new MyUpdateClassesCommand(myLastSuspendContext)); } @@ -287,7 +303,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable tracker.disable(); } - Disposer.register(ClassesFilteredView.this, tracker); + Disposer.register(this, tracker); myConstructorTrackedClasses.put(ref, tracker); } } @@ -303,7 +319,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } private SuspendContextImpl getSuspendContext() { - return DebuggerManagerImpl.getInstanceEx(myProject).getContext().getSuspendContext(); + return DebuggerManagerEx.getInstanceEx(myProject).getContext().getSuspendContext(); } private void updateClassesAndCounts() { @@ -312,9 +328,9 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } } - private ActionPopupMenu createContextMenu() { - ActionGroup group = (ActionGroup) ActionManager.getInstance().getAction("ClassesView.PopupActionGroup"); - return ActionManager.getInstance().createActionPopupMenu("ClassesView.PopupActionGroup", group); + private static ActionPopupMenu createContextMenu() { + ActionGroup group = (ActionGroup) ActionManager.getInstance().getAction("MemoryView.ClassesPopupActionGroup"); + return ActionManager.getInstance().createActionPopupMenu("MemoryView.ClassesPopupActionGroup", group); } @Override @@ -340,7 +356,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } private void doActivate() { - myDebugSession.addSessionListener(myDebugSessionListener, ClassesFilteredView.this); + myDebugSession.addSessionListener(myDebugSessionListener, this); myConstructorTrackedClasses.values().forEach(x -> x.setBackgroundMode(false)); final SuspendContextImpl lastContext = myLastSuspendContext; if (lastContext == null || !lastContext.equals(getSuspendContext())) { @@ -400,10 +416,10 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable final long[] counts = chunks.size() == 1 ? chunks.get(0) : IntStream.range(0, chunks.size()).boxed() .flatMapToLong(integer -> Arrays.stream(chunks.get(integer))) .toArray(); - SwingUtilities.invokeLater(() -> myTable.setClassesAndUpdateCounts(classes, counts)); + ApplicationManager.getApplication().invokeLater(() -> myTable.setClassesAndUpdateCounts(classes, counts)); } - SwingUtilities.invokeLater(() -> myTable.setBusy(false)); + ApplicationManager.getApplication().invokeLater(() -> myTable.setBusy(false)); } private boolean isContextValid() { @@ -411,7 +427,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } } - private class FilterTextField extends SearchTextField { + private static class FilterTextField extends SearchTextField { FilterTextField() { super(false); } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/view/ClassesTable.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesTable.java similarity index 75% rename from java/debugger/impl/src/com/intellij/debugger/memory/view/ClassesTable.java rename to java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesTable.java index cce435132c13..6c4bcb9cbb15 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/view/ClassesTable.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesTable.java @@ -1,5 +1,24 @@ -package org.jetbrains.debugger.memory.view; +/* + * Copyright 2000-2016 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.debugger.memory.ui; +import com.intellij.debugger.memory.component.InstancesTracker; +import com.intellij.debugger.memory.utils.AbstractTableColumnDescriptor; +import com.intellij.debugger.memory.utils.AbstractTableModelWithColumns; +import com.intellij.icons.AllIcons; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.DataKey; import com.intellij.openapi.actionSystem.DataProvider; @@ -17,16 +36,12 @@ import com.intellij.util.ui.JBUI; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; -import icons.MemoryViewIcons; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.component.InstancesTracker; -import org.jetbrains.debugger.memory.tracking.TrackerForNewInstances; -import org.jetbrains.debugger.memory.tracking.TrackingType; -import org.jetbrains.debugger.memory.utils.AbstractTableColumnDescriptor; -import org.jetbrains.debugger.memory.utils.AbstractTableModelWithColumns; -import org.jetbrains.debugger.memory.utils.InstancesProvider; +import com.intellij.debugger.memory.tracking.TrackerForNewInstances; +import com.intellij.debugger.memory.tracking.TrackingType; +import com.intellij.debugger.memory.utils.InstancesProvider; import javax.swing.*; import javax.swing.border.Border; @@ -39,9 +54,9 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { public static final DataKey SELECTED_CLASS_KEY = DataKey.create("ClassesTable.SelectedClass"); public static final DataKey DEBUG_SESSION_KEY = DataKey.create("ClassesTable.DebugSession"); public static final DataKey NEW_INSTANCES_PROVIDER_KEY = - DataKey.create("ClassesTable.NewInstances"); + DataKey.create("ClassesTable.NewInstances"); public static final DataKey REF_COUNT_PROVIDER_KEY = - DataKey.create("ClassesTable.ReferenceCountProvider"); + DataKey.create("ClassesTable.ReferenceCountProvider"); private static final Border EMPTY_BORDER = BorderFactory.createEmptyBorder(); @@ -63,7 +78,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private MinusculeMatcher myMatcher = NameUtil.buildMatcher("*").build(); private String myFilteringPattern = ""; - private volatile List myElems = Collections.unmodifiableList(new ArrayList<>()); + private volatile List myItems = Collections.unmodifiableList(new ArrayList<>()); ClassesTable(@NotNull XDebugSession session, boolean onlyWithDiff, boolean onlyWithInstances, boolean onlyTracked, @NotNull ClassesFilteredView parent) { @@ -99,20 +114,20 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { @Override public boolean include(Entry entry) { int ix = entry.getIdentifier(); - ReferenceType ref = myElems.get(ix); + ReferenceType ref = myItems.get(ix); DiffValue diff = myCounts.getOrDefault(ref, myUnknownValue); boolean isFilteringOptionsRefused = myOnlyWithDiff && diff.diff() == 0 - || myOnlyWithInstances && !diff.hasInstance() - || myOnlyTracked && myParent.getStrategy(ref) == null; + || myOnlyWithInstances && !diff.hasInstance() + || myOnlyTracked && myParent.getStrategy(ref) == null; return !(isFilteringOptionsRefused) && myMatcher.matches(ref.name()); } }); List myDefaultSortingKeys = Arrays.asList( - new RowSorter.SortKey(DiffViewTableModel.DIFF_COLUMN_INDEX, SortOrder.DESCENDING), - new RowSorter.SortKey(DiffViewTableModel.COUNT_COLUMN_INDEX, SortOrder.DESCENDING), - new RowSorter.SortKey(DiffViewTableModel.CLASSNAME_COLUMN_INDEX, SortOrder.ASCENDING) + new RowSorter.SortKey(DiffViewTableModel.DIFF_COLUMN_INDEX, SortOrder.DESCENDING), + new RowSorter.SortKey(DiffViewTableModel.COUNT_COLUMN_INDEX, SortOrder.DESCENDING), + new RowSorter.SortKey(DiffViewTableModel.CLASSNAME_COLUMN_INDEX, SortOrder.ASCENDING) ); sorter.setSortKeys(myDefaultSortingKeys); setRowSorter(sorter); @@ -121,12 +136,12 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { myCountProvider = new ReferenceCountProvider() { @Override public int getTotalCount(@NotNull ReferenceType ref) { - return (int) myCounts.get(ref).myCurrentCount; + return (int)myCounts.get(ref).myCurrentCount; } @Override public int getDiffCount(@NotNull ReferenceType ref) { - return (int) myCounts.get(ref).diff(); + return (int)myCounts.get(ref).diff(); } @Override @@ -150,7 +165,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { int selectedRow = getSelectedRow(); if (selectedRow != -1) { int ix = convertRowIndexToModel(selectedRow); - return myElems.get(ix); + return myItems.get(ix); } return null; @@ -158,7 +173,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { @Nullable ReferenceType getClassByName(@NotNull String name) { - for (ReferenceType ref : myElems) { + for (ReferenceType ref : myItems) { if (name.equals(ref.name())) { return ref; } @@ -207,14 +222,14 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { assert classes.size() == counts.length; ReferenceType selectedClass = myModel.getSelectedClassBeforeHided(); int newSelectedIndex = classes.indexOf(selectedClass); - boolean isInitialized = !myElems.isEmpty(); - myElems = Collections.unmodifiableList(new ArrayList<>(classes)); + boolean isInitialized = !myItems.isEmpty(); + myItems = Collections.unmodifiableList(new ArrayList<>(classes)); for (int i = 0, size = classes.size(); i < size; i++) { ReferenceType ref = classes.get(i); DiffValue oldValue = isInitialized && !myCounts.containsKey(ref) - ? new DiffValue(0, 0) - : myCounts.getOrDefault(ref, myUnknownValue); + ? new DiffValue(0, 0) + : myCounts.getOrDefault(ref, myUnknownValue); myCounts.put(ref, oldValue.update(counts[i])); } @@ -223,7 +238,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { if (newSelectedIndex != -1 && !myModel.isHided()) { int ix = convertRowIndexToView(newSelectedIndex); changeSelection(ix, - DiffViewTableModel.CLASSNAME_COLUMN_INDEX, false, false); + DiffViewTableModel.CLASSNAME_COLUMN_INDEX, false, false); } getRowSorter().allRowsChanged(); @@ -253,7 +268,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { TrackerForNewInstances strategy = myParent.getStrategy(selectedClass); if (strategy != null && strategy.isReady()) { List newInstances = strategy.getNewInstances(); - return (InstancesProvider) limit -> newInstances; + return (InstancesProvider)limit -> newInstances; } } } @@ -271,7 +286,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { @Nullable private TrackingType getTrackingType(int row) { - ReferenceType ref = (ReferenceType) getValueAt(row, convertColumnIndexToView(DiffViewTableModel.CLASSNAME_COLUMN_INDEX)); + ReferenceType ref = (ReferenceType)getValueAt(row, convertColumnIndexToView(DiffViewTableModel.CLASSNAME_COLUMN_INDEX)); return myInstancesTracker.getTrackingType(ref.name()); } @@ -286,24 +301,24 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { DiffViewTableModel() { super(new AbstractTableColumnDescriptor[]{ - new AbstractTableColumnDescriptor("Class", ReferenceType.class) { - @Override - public Object getValue(int ix) { - return myElems.get(ix); - } - }, - new AbstractTableColumnDescriptor("Count", Long.class) { - @Override - public Object getValue(int ix) { - return myCounts.getOrDefault(myElems.get(ix), myUnknownValue).myCurrentCount; - } - }, - new AbstractTableColumnDescriptor("Diff", DiffValue.class) { - @Override - public Object getValue(int ix) { - return myCounts.getOrDefault(myElems.get(ix), myUnknownValue); - } + new AbstractTableColumnDescriptor("Class", ReferenceType.class) { + @Override + public Object getValue(int ix) { + return myItems.get(ix); } + }, + new AbstractTableColumnDescriptor("Count", Long.class) { + @Override + public Object getValue(int ix) { + return myCounts.getOrDefault(myItems.get(ix), myUnknownValue).myCurrentCount; + } + }, + new AbstractTableColumnDescriptor("Diff", DiffValue.class) { + @Override + public Object getValue(int ix) { + return myCounts.getOrDefault(myItems.get(ix), myUnknownValue); + } + } }); } @@ -333,7 +348,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { @Override public int getRowCount() { - return myIsWithContent ? myElems.size() : 0; + return myIsWithContent ? myItems.size() : 0; } } @@ -408,34 +423,34 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { } if (trackingType != null && column == DiffViewTableModel.DIFF_COLUMN_INDEX) { - setIcon(MemoryViewIcons.CLASS_TRACKED); + setIcon(AllIcons.Debugger.MemoryView.ClassTracked); setTransparentIconBackground(true); } if (value != null) { - addText(table, value, isSelected, hasFocus, row, column); + addText(value, isSelected, row); } } - protected abstract void addText(JTable table, @NotNull Object value, boolean isSelected, - boolean hasFocus, int row, int column); + protected abstract void addText(@NotNull Object value, boolean isSelected, int row); } private class MyClassColumnRenderer extends MyTableCellRenderer { @Override - protected void addText(JTable table, @NotNull Object value, boolean isSelected, - boolean hasFocus, int row, int column) { - String presentation = ((ReferenceType) value).name(); + protected void addText(@NotNull Object value, boolean isSelected, + int row) { + String presentation = ((ReferenceType)value).name(); append(" "); if (isSelected) { FList textRanges = myMatcher.matchingFragments(presentation); if (textRanges != null) { SimpleTextAttributes attributes = new SimpleTextAttributes(getBackground(), getForeground(), null, - SimpleTextAttributes.STYLE_SEARCH_MATCH); + SimpleTextAttributes.STYLE_SEARCH_MATCH); SpeedSearchUtil.appendColoredFragments(this, presentation, textRanges, - SimpleTextAttributes.REGULAR_ATTRIBUTES, attributes); + SimpleTextAttributes.REGULAR_ATTRIBUTES, attributes); } - } else { + } + else { append(String.format("%s", presentation), SimpleTextAttributes.REGULAR_ATTRIBUTES); } } @@ -443,8 +458,8 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private class MyCountColumnRenderer extends MyTableCellRenderer { @Override - protected void addText(JTable table, @NotNull Object value, boolean isSelected, - boolean hasFocus, int row, int column) { + protected void addText(@NotNull Object value, boolean isSelected, + int row) { setTextAlign(SwingConstants.RIGHT); append(value.toString()); } @@ -452,14 +467,14 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private class MyDiffColumnRenderer extends MyTableCellRenderer { private final SimpleTextAttributes myClickableCellAttributes = - new SimpleTextAttributes(SimpleTextAttributes.STYLE_UNDERLINE, JBColor.BLUE); + new SimpleTextAttributes(SimpleTextAttributes.STYLE_UNDERLINE, JBColor.BLUE); @Override - protected void addText(JTable table, @NotNull Object value, boolean isSelected, - boolean hasFocus, int row, int column) { + protected void addText(@NotNull Object value, boolean isSelected, + int row) { setTextAlign(SwingConstants.RIGHT); - ReferenceType ref = myElems.get(convertRowIndexToModel(row)); + ReferenceType ref = myItems.get(convertRowIndexToModel(row)); long diff = myCountProvider.getDiffCount(ref); String text = String.format("%s%d", diff > 0 ? "+" : "", diff); @@ -468,13 +483,15 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { if (newInstancesCount >= 0) { if (newInstancesCount == diff) { append(text, diff == 0 ? SimpleTextAttributes.REGULAR_ATTRIBUTES : myClickableCellAttributes); - } else { + } + else { append(text, SimpleTextAttributes.REGULAR_ATTRIBUTES); if (newInstancesCount != 0) { append(String.format(" (%d)", newInstancesCount), myClickableCellAttributes); } } - } else { + } + else { append(text, SimpleTextAttributes.REGULAR_ATTRIBUTES); } } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/view/ExpressionEditorWithHistory.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/ExpressionEditorWithHistory.java similarity index 76% rename from java/debugger/impl/src/com/intellij/debugger/memory/view/ExpressionEditorWithHistory.java rename to java/debugger/impl/src/com/intellij/debugger/memory/ui/ExpressionEditorWithHistory.java index d11e0e100873..87dbc415d098 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/view/ExpressionEditorWithHistory.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/ExpressionEditorWithHistory.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.view; +/* + * Copyright 2000-2016 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.debugger.memory.ui; import com.intellij.codeInsight.lookup.LookupManager; import com.intellij.debugger.engine.DebuggerUtils; @@ -14,10 +29,10 @@ import com.intellij.openapi.ui.popup.util.BaseListPopupStep; import com.intellij.psi.PsiClass; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.ColoredListCellRenderer; +import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.popup.list.ListPopupImpl; import com.intellij.xdebugger.XExpression; import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; -import com.intellij.xdebugger.impl.XSourcePositionImpl; import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl; import com.intellij.xdebugger.impl.ui.XDebuggerExpressionEditor; import org.jetbrains.annotations.NotNull; @@ -34,7 +49,7 @@ class ExpressionEditorWithHistory extends XDebuggerExpressionEditor { final @NotNull XDebuggerEditorsProvider debuggerEditorsProvider, final @Nullable Disposable parentDisposable) { super(project, debuggerEditorsProvider, HISTORY_ID_PREFIX + className, null, - XExpressionImpl.EMPTY_EXPRESSION, false, true, true); + XExpressionImpl.EMPTY_EXPRESSION, false, true, true); new AnAction("InstancesWindow.ShowHistory") { @Override @@ -53,9 +68,8 @@ class ExpressionEditorWithHistory extends XDebuggerExpressionEditor { protected Void doInBackground() throws Exception { ApplicationManager.getApplication().runReadAction(() -> { final PsiClass psiClass = DebuggerUtils.findClass(className, - project, GlobalSearchScope.allScope(project)); - XSourcePositionImpl position = XSourcePositionImpl.createByElement(psiClass); - SwingUtilities.invokeLater(() -> setSourcePosition(position)); + project, GlobalSearchScope.allScope(project)); + ApplicationManager.getApplication().invokeLater(() -> setContext(psiClass)); }); return null; } @@ -79,7 +93,7 @@ class ExpressionEditorWithHistory extends XDebuggerExpressionEditor { @Override protected void customizeCellRenderer(@NotNull JList list, XExpression value, int index, boolean selected, boolean hasFocus) { - append(value.getExpression()); + append(value.getExpression(), SimpleTextAttributes.REGULAR_ATTRIBUTES); } }; } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/view/FilteringProgressView.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/FilteringProgressView.java similarity index 75% rename from java/debugger/impl/src/com/intellij/debugger/memory/view/FilteringProgressView.java rename to java/debugger/impl/src/com/intellij/debugger/memory/ui/FilteringProgressView.java index 90c7b1b6091d..2372b5ba01ff 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/view/FilteringProgressView.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/FilteringProgressView.java @@ -1,5 +1,21 @@ -package org.jetbrains.debugger.memory.view; +/* + * Copyright 2000-2016 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.debugger.memory.ui; +import com.intellij.debugger.memory.filtering.FilteringResult; import com.intellij.icons.AllIcons; import com.intellij.ui.JBProgressBar; import com.intellij.ui.components.JBLabel; @@ -7,7 +23,6 @@ import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.components.BorderLayoutPanel; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.view.InstancesWindow.FilteringCompletionReason; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; @@ -19,7 +34,7 @@ class FilteringProgressView extends BorderLayoutPanel { private final JBProgressBar myProgressBar = new JBProgressBar(); private final BorderLayoutPanel myProgressPanel = new BorderLayoutPanel(); private final JBLabel myStopButton = new JBLabel(UIUtil.isUnderDarcula() - ? AllIcons.Actions.Clean : AllIcons.Actions.CleanLight); + ? AllIcons.Actions.Clean : AllIcons.Actions.CleanLight); private final JBLabel myProgressText = new JBLabel(); private int myProceedCount = 0; @@ -29,7 +44,7 @@ class FilteringProgressView extends BorderLayoutPanel { private boolean myIsInProcess = false; @Nullable - private FilteringCompletionReason myCompletionReason = null; + private FilteringResult myCompletionReason = null; FilteringProgressView() { myStopButton.setOpaque(true); @@ -50,9 +65,9 @@ class FilteringProgressView extends BorderLayoutPanel { void updateProgress(int proceedCount, int matchedCount, int errorCount) { if (myIsInProcess) { - myProceedCount += proceedCount; - myMatchedCount += matchedCount; - myErrorCount += errorCount; + myProceedCount = proceedCount; + myMatchedCount = matchedCount; + myErrorCount = errorCount; myProgressBar.setValue(myProceedCount); myProgressText.setText(getDescription()); } @@ -68,7 +83,7 @@ class FilteringProgressView extends BorderLayoutPanel { setProgressBarVisible(true); } - void complete(@NotNull FilteringCompletionReason reason) { + void complete(@NotNull FilteringResult reason) { if (!myIsInProcess) { throw new IllegalStateException("First you need to start progress"); } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesTree.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesTree.java similarity index 82% rename from java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesTree.java rename to java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesTree.java index 6352ffe02c9a..8bd1c851482c 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesTree.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesTree.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.view; +/* + * Copyright 2000-2016 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.debugger.memory.ui; import com.intellij.debugger.ui.impl.watch.NodeDescriptorProvider; import com.intellij.debugger.ui.tree.NodeDescriptor; @@ -11,6 +26,7 @@ import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; import com.intellij.xdebugger.frame.*; import com.intellij.xdebugger.impl.actions.XDebuggerActions; import com.intellij.xdebugger.impl.frame.XValueMarkers; +import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; @@ -20,7 +36,6 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.swing.*; import javax.swing.tree.TreePath; import java.util.ArrayList; import java.util.List; @@ -70,10 +85,10 @@ public class InstancesTree extends XDebuggerTree { rebuildTree(policy, XDebuggerTreeState.saveState(this)); } - void setMessage(@Nullable Icon icon, @NotNull String text, @NotNull SimpleTextAttributes textAttributes) { + void setInfoMessage(@SuppressWarnings("SameParameterValue") @NotNull String text) { myChildren = null; myRoot.clearChildren(); - myRoot.setMessage(text, icon, textAttributes, null); + myRoot.setMessage(text, XDebuggerUIConstants.INFORMATION_MESSAGE_ICON, SimpleTextAttributes.REGULAR_ATTRIBUTES, null); } @Nullable diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesWindow.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWindow.java similarity index 59% rename from java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesWindow.java rename to java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWindow.java index 891ebbf82da7..32ec574675b1 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesWindow.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWindow.java @@ -1,33 +1,47 @@ -package org.jetbrains.debugger.memory.view; +/* + * Copyright 2000-2016 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.debugger.memory.ui; import com.intellij.debugger.DebuggerManager; import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.JavaValue; import com.intellij.debugger.engine.SuspendContextImpl; -import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.EvaluationContext; import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; -import com.intellij.debugger.engine.evaluation.TextWithImportsImpl; -import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator; import com.intellij.debugger.engine.events.DebuggerContextCommandImpl; +import com.intellij.debugger.memory.filtering.FilteringResult; +import com.intellij.debugger.memory.filtering.FilteringTask; +import com.intellij.debugger.memory.filtering.FilteringTaskCallback; +import com.intellij.debugger.memory.utils.*; import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl; import com.intellij.debugger.ui.impl.watch.MessageDescriptor; import com.intellij.debugger.ui.impl.watch.NodeManagerImpl; import com.intellij.debugger.ui.tree.NodeDescriptor; -import com.intellij.debugger.ui.tree.render.CachedEvaluator; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.AnActionListener; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.Pair; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.DoubleClickListener; import com.intellij.ui.JBColor; -import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBPanel; import com.intellij.util.ui.JBDimension; @@ -43,17 +57,13 @@ import com.intellij.xdebugger.impl.XDebugSessionImpl; import com.intellij.xdebugger.impl.actions.XDebuggerActionBase; import com.intellij.xdebugger.impl.frame.XValueMarkers; import com.intellij.xdebugger.impl.ui.XDebuggerExpressionEditor; -import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants; import com.intellij.xdebugger.impl.ui.tree.ValueMarkup; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState; import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; -import com.sun.jdi.BooleanValue; import com.sun.jdi.ObjectReference; -import com.sun.jdi.Value; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.memory.utils.*; import org.jetbrains.java.debugger.JavaDebuggerEditorsProvider; import javax.swing.*; @@ -63,9 +73,6 @@ import java.awt.event.MouseEvent; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -import static java.lang.Math.min; public class InstancesWindow extends DialogWrapper { private static final Logger LOG = Logger.getInstance(InstancesWindow.class); @@ -74,7 +81,7 @@ public class InstancesWindow extends DialogWrapper { private static final int DEFAULT_WINDOW_HEIGHT = 400; private static final int FILTERING_BUTTON_ADDITIONAL_WIDTH = 30; private static final int BORDER_LAYOUT_DEFAULT_GAP = 5; - private static final int DEFAULT_INSTANCES_LIMIT = 500000; // All + private static final int DEFAULT_INSTANCES_LIMIT = 500000; private final Project myProject; private final XDebugSession myDebugSession; @@ -96,7 +103,7 @@ public class InstancesWindow extends DialogWrapper { myDebugSession.addSessionListener(new XDebugSessionListener() { @Override public void sessionStopped() { - SwingUtilities.invokeLater(() -> close(OK_EXIT_CODE)); + ApplicationManager.getApplication().invokeLater(() -> close(OK_EXIT_CODE)); } }, myDisposable); setModal(false); @@ -105,10 +112,6 @@ public class InstancesWindow extends DialogWrapper { root.setDefaultButton(myInstancesView.myFilterButton); } - enum FilteringCompletionReason { - ALL_CHECKED, INTERRUPTED, LIMIT_REACHED - } - private void addWarningMessage(@Nullable String message) { String warning = message == null ? "" : String.format(". Warning: %s", message); setTitle(String.format("Instances of %s%s", myClassName, warning)); @@ -125,7 +128,7 @@ public class InstancesWindow extends DialogWrapper { protected JComponent createCenterPanel() { myInstancesView = new MyInstancesView(); myInstancesView.setPreferredSize( - new JBDimension(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT)); + new JBDimension(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT)); return myInstancesView; } @@ -154,7 +157,6 @@ public class InstancesWindow extends DialogWrapper { private final XDebuggerExpressionEditor myFilterConditionEditor; private final XDebugSessionListener myDebugSessionListener = new MySessionListener(); - private final MyCachedEvaluator myEvaluator = new MyCachedEvaluator(); private final MyNodeManager myNodeManager = new MyNodeManager(myProject); private final JButton myFilterButton = new JButton("Filter"); @@ -170,19 +172,19 @@ public class InstancesWindow extends DialogWrapper { MyInstancesView() { super(new BorderLayout(0, JBUI.scale(BORDER_LAYOUT_DEFAULT_GAP))); - Disposer.register(InstancesWindow.this.myDisposable, MyInstancesView.this); + Disposer.register(InstancesWindow.this.myDisposable, this); XValueMarkers markers = getValueMarkers(); ActionManager.getInstance().addAnActionListener(myActionListener, InstancesWindow.this.myDisposable); myDebugSession.addSessionListener(myDebugSessionListener, InstancesWindow.this.myDisposable); JavaDebuggerEditorsProvider editorsProvider = new JavaDebuggerEditorsProvider(); myFilterConditionEditor = new ExpressionEditorWithHistory(myProject, myClassName, - editorsProvider, InstancesWindow.this.myDisposable); + editorsProvider, InstancesWindow.this.myDisposable); myFilterButton.setBorder(BorderFactory.createEmptyBorder()); Dimension filteringButtonSize = myFilterConditionEditor.getEditorComponent().getPreferredSize(); filteringButtonSize.width = JBUI.scale(FILTERING_BUTTON_ADDITIONAL_WIDTH) + - myFilterButton.getPreferredSize().width; + myFilterButton.getPreferredSize().width; myFilterButton.setPreferredSize(filteringButtonSize); JBPanel filteringPane = new JBPanel(new BorderLayout(JBUI.scale(BORDER_LAYOUT_DEFAULT_GAP), 0)); @@ -212,8 +214,8 @@ public class InstancesWindow extends DialogWrapper { StackFrameList list = new StackFrameList(myProject, - Collections.emptyList(), - GlobalSearchScope.allScope(myProject)); + Collections.emptyList(), + GlobalSearchScope.allScope(myProject)); list.addListSelectionListener(e -> list.navigateToSelectedValue(false)); new DoubleClickListener() { @@ -225,15 +227,15 @@ public class InstancesWindow extends DialogWrapper { }.installOn(list); InstancesWithStackFrameView instancesWithStackFrame = new InstancesWithStackFrameView(myDebugSession, - myInstancesTree, list, myClassName); + myInstancesTree, list, myClassName); add(filteringPane, BorderLayout.NORTH); add(instancesWithStackFrame.getComponent(), BorderLayout.CENTER); JComponent focusedComponent = myFilterConditionEditor.getEditorComponent(); UiNotifyConnector.doWhenFirstShown(focusedComponent, () -> - IdeFocusManager.findInstanceByComponent(focusedComponent) - .requestFocus(focusedComponent, true)); + IdeFocusManager.findInstanceByComponent(focusedComponent) + .requestFocus(focusedComponent, true)); } @Override @@ -243,8 +245,8 @@ public class InstancesWindow extends DialogWrapper { } private void updateInstances() { - DebugProcessImpl debugProcess = (DebugProcessImpl) DebuggerManager.getInstance(myProject) - .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); + DebugProcessImpl debugProcess = (DebugProcessImpl)DebuggerManager.getInstance(myProject) + .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); cancelFilteringTask(); @@ -258,49 +260,28 @@ public class InstancesWindow extends DialogWrapper { public void threadAction(@NotNull SuspendContextImpl suspendContext) { myIsAndroidVM = AndroidUtil.isAndroidVM(debugProcess.getVirtualMachineProxy().getVirtualMachine()); int limit = myIsAndroidVM - ? AndroidUtil.ANDROID_INSTANCES_LIMIT - : DEFAULT_INSTANCES_LIMIT; + ? AndroidUtil.ANDROID_INSTANCES_LIMIT + : DEFAULT_INSTANCES_LIMIT; List instances = myInstancesProvider.getInstances(limit + 1); EvaluationContextImpl evaluationContext = debugProcess - .getDebuggerContext().createEvaluationContext(); + .getDebuggerContext().createEvaluationContext(); if (instances.size() > limit) { addWarningMessage(String.format("Not all instances will be loaded (only %d)", limit)); instances = instances.subList(0, limit); } - final int progressSize = instances.size(); if (evaluationContext != null) { synchronized (myFilteringTaskLock) { - myFilteringTask = new MyFilteringWorker(instances, evaluationContext, createEvaluator()); - SwingUtilities.invokeLater(() -> { - myProgress.start(progressSize); - myFilteringTask.execute(); - }); + myFilteringTask = new MyFilteringWorker(instances, myFilterConditionEditor.getExpression(), evaluationContext); + myFilteringTask.execute(); } } } }); } - @Nullable - private ExpressionEvaluator createEvaluator() { - ExpressionEvaluator evaluator = null; - - XExpression expression = myFilterConditionEditor.getExpression(); - if (expression != null && !expression.getExpression().isEmpty()) { - try { - myEvaluator.setReferenceExpression(TextWithImportsImpl.fromXExpression(expression)); - evaluator = myEvaluator.getEvaluator(); - } catch (EvaluateException ignored) { - LOG.info("cannot create evaluator by the expression = " + expression.getExpression()); - } - } - - return evaluator; - } - private void cancelFilteringTask() { if (myFilteringTask != null) { synchronized (myFilteringTaskLock) { @@ -314,8 +295,8 @@ public class InstancesWindow extends DialogWrapper { private XValueMarkers getValueMarkers() { return myDebugSession instanceof XDebugSessionImpl - ? ((XDebugSessionImpl) myDebugSession).getValueMarkers() - : null; + ? ((XDebugSessionImpl)myDebugSession).getValueMarkers() + : null; } private class MySessionListener implements XDebugSessionListener { @@ -323,18 +304,18 @@ public class InstancesWindow extends DialogWrapper { @Override public void sessionResumed() { - SwingUtilities.invokeLater(() -> { + ApplicationManager.getApplication().invokeLater(() -> { myTreeState = XDebuggerTreeState.saveState(myInstancesTree); cancelFilteringTask(); - myInstancesTree.setMessage(XDebuggerUIConstants.INFORMATION_MESSAGE_ICON, - "The application is running", SimpleTextAttributes.REGULAR_ATTRIBUTES); + myInstancesTree.setInfoMessage( + "The application is running"); }); } @Override public void sessionPaused() { - SwingUtilities.invokeLater(() -> { + ApplicationManager.getApplication().invokeLater(() -> { myProgress.setVisible(true); myInstancesTree.rebuildTree(InstancesTree.RebuildPolicy.RELOAD_INSTANCES, myTreeState); }); @@ -346,8 +327,8 @@ public class InstancesWindow extends DialogWrapper { public void beforeActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) { if (dataContext.getData(PlatformDataKeys.CONTEXT_COMPONENT) == myInstancesView.myInstancesTree && (isAddToWatchesAction(action) || isEvaluateExpressionAction(action))) { - XValueNodeImpl selectedNode = XDebuggerTreeActionBase.getSelectedNode(dataContext); - XValueMarkers markers = getValueMarkers(); + final XValueNodeImpl selectedNode = XDebuggerTreeActionBase.getSelectedNode(dataContext); + final XValueMarkers markers = getValueMarkers(); if (markers != null && selectedNode != null) { @@ -356,175 +337,143 @@ public class InstancesWindow extends DialogWrapper { currentNode = currentNode.getParent(); } - XValue valueContainer = ((XValueNodeImpl) currentNode).getValueContainer(); + final XValue valueContainer = ((XValueNodeImpl)currentNode).getValueContainer(); - String expression = valueContainer.getEvaluationExpression(); + final String expression = valueContainer.getEvaluationExpression(); if (expression != null) { markers.markValue(valueContainer, - new ValueMarkup(expression.replace("@", ""), new JBColor(0, 0), null)); + new ValueMarkup(expression.replace("@", ""), new JBColor(0, 0), null)); } - SwingUtilities.invokeLater(() -> myInstancesTree - .rebuildTree(InstancesTree.RebuildPolicy.ONLY_UPDATE_LABELS)); + ApplicationManager.getApplication().invokeLater(() -> myInstancesTree + .rebuildTree(InstancesTree.RebuildPolicy.ONLY_UPDATE_LABELS)); } } } private boolean isAddToWatchesAction(AnAction action) { - String className = action.getClass().getSimpleName(); + final String className = action.getClass().getSimpleName(); return action instanceof XDebuggerTreeActionBase && className.equals("XAddToWatchesAction"); } private boolean isEvaluateExpressionAction(AnAction action) { - String className = action.getClass().getSimpleName(); + final String className = action.getClass().getSimpleName(); return action instanceof XDebuggerActionBase && className.equals("EvaluateAction"); } } - private final class MyCachedEvaluator extends CachedEvaluator { - @Override - @NotNull - protected String getClassName() { - return myClassName; + private class MyFilteringCallback implements FilteringTaskCallback { + private final ErrorsValueGroup myErrorsGroup = new ErrorsValueGroup(); + private final EvaluationContextImpl myEvaluationContext; + + private long myFilteringStartedTime; + + private int myProceedCount = 0; + private int myMatchedCount = 0; + private int myErrorsCount = 0; + + public MyFilteringCallback(@NotNull EvaluationContextImpl evaluationContext) { + myEvaluationContext = evaluationContext; } - ExpressionEvaluator getEvaluator() throws EvaluateException { - return getEvaluator(myProject); + private XValueChildrenList myChildren = new XValueChildrenList(); + + @Override + public void started(int total) { + myFilteringStartedTime = System.nanoTime(); + ApplicationManager.getApplication().invokeLater(() -> myProgress.start(total)); + } + + @NotNull + @Override + public Action matched(@NotNull ObjectReference ref) { + final JavaValue val = new InstanceJavaValue(new InstanceValueDescriptor(myProject, ref), + myEvaluationContext, myNodeManager); + myMatchedCount++; + myProceedCount++; + myChildren.add(val); + if (myChildren.size() >= FILTERING_CHUNK_SIZE) { + final int proceed = myProceedCount; + final int matched = myMatchedCount; + final int errors = myErrorsCount; + XValueChildrenList children = myChildren; + ApplicationManager.getApplication().invokeLater(() -> { + myProgress.updateProgress(proceed, matched, errors); + myInstancesTree.addChildren(children, false); + }); + myChildren = new XValueChildrenList(); + } + + return myMatchedCount < MAX_TREE_NODE_COUNT ? Action.CONTINUE : Action.STOP; + } + + @NotNull + @Override + public Action notMatched(@NotNull ObjectReference ref) { + myProceedCount++; + return Action.CONTINUE; + } + + @NotNull + @Override + public Action error(@NotNull ObjectReference ref, @NotNull String description) { + final JavaValue val = new InstanceJavaValue(new InstanceValueDescriptor(myProject, ref), + myEvaluationContext, myNodeManager); + myErrorsGroup.addErrorValue(description, val); + myProceedCount++; + myErrorsCount++; + return Action.CONTINUE; + } + + @Override + public void completed(@NotNull FilteringResult reason) { + if (!myErrorsGroup.isEmpty()) { + myChildren.addBottomGroup(myErrorsGroup); + } + + final long duration = System.nanoTime() - myFilteringStartedTime; + LOG.info(String.format("Filtering completed in %d ms for %d instances", + TimeUnit.NANOSECONDS.toMillis(duration), + myProceedCount)); + + final int proceed = myProceedCount; + final int matched = myMatchedCount; + final int errors = myErrorsCount; + final XValueChildrenList childrenList = myChildren; + ApplicationManager.getApplication().invokeLater(() -> { + myProgress.updateProgress(proceed, matched, errors); + myInstancesTree.addChildren(childrenList, true); + myFilterButton.setEnabled(true); + myProgress.complete(reason); + }); } } private class MyFilteringWorker extends SwingWorker { - private final List myReferences; - private final ErrorsValueGroup myErrorsGroup = new ErrorsValueGroup("Errors"); - private final ExpressionEvaluator myExpressionEvaluator; - - private final EvaluationContextImpl myEvaluationContext; - private volatile boolean myDebuggerTaskCompleted = false; - - @NotNull - private volatile FilteringCompletionReason myCompletionReason = FilteringCompletionReason.INTERRUPTED; + private final FilteringTask myTask; MyFilteringWorker(@NotNull List refs, - @NotNull EvaluationContextImpl evaluationContext, - @Nullable ExpressionEvaluator evaluator) { - myReferences = refs; - myEvaluationContext = evaluationContext; - myExpressionEvaluator = evaluator; - } - - @Override - protected void done() { - if (!myErrorsGroup.isEmpty()) { - XValueChildrenList lst = new XValueChildrenList(); - lst.addBottomGroup(myErrorsGroup); - myInstancesTree.addChildren(lst, true); - } else { - myInstancesTree.addChildren(XValueChildrenList.EMPTY, true); + @NotNull XExpression expression, + @NotNull EvaluationContextImpl evaluationContext) { + if (refs.size() != 0) { + DebugProcessImpl debugProcess = + (DebugProcessImpl)DebuggerManager.getInstance(myProject).getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); + myTask = new FilteringTask(refs.get(0).referenceType(), debugProcess, expression, refs, + new MyFilteringCallback(evaluationContext)); + } + else { + myTask = null; } - - myFilterButton.setEnabled(true); - SwingUtilities.invokeLater(() -> myProgress.complete(myCompletionReason)); } @Override protected Void doInBackground() throws Exception { - DebugProcessImpl debugProcess = (DebugProcessImpl) DebuggerManager.getInstance(myProject) - .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); - - AtomicInteger totalChildren = new AtomicInteger(0); - AtomicInteger totalViewed = new AtomicInteger(0); - long start = System.nanoTime(); - for (int i = 0, size = myReferences.size(); i < size && totalChildren.get() < MAX_TREE_NODE_COUNT; - i += FILTERING_CHUNK_SIZE) { - myDebuggerTaskCompleted = false; - final int chunkBegin = i; - debugProcess.getManagerThread().schedule(new DebuggerContextCommandImpl(debugProcess.getDebuggerContext()) { - @Override - public Priority getPriority() { - return Priority.LOWEST; - } - - @Override - public void threadAction(@NotNull SuspendContextImpl suspendContext) { - // TODO: need to rewrite this - XValueChildrenList children = new XValueChildrenList(); - int endOfChunk = min(chunkBegin + FILTERING_CHUNK_SIZE, size); - int errorCount = 0; - for (int j = chunkBegin; j < endOfChunk && totalChildren.get() < MAX_TREE_NODE_COUNT; j++) { - ObjectReference ref = myReferences.get(j); - totalViewed.incrementAndGet(); - Pair comparison = null; - if (myExpressionEvaluator != null) { - comparison = isSatisfy(myExpressionEvaluator, ref); - if (comparison.first == MyFilteringResult.EVAL_ERROR) { - errorCount++; - } - - if (comparison.first == MyFilteringResult.NO_MATCH) { - continue; - } - } - - JavaValue val = new InstanceJavaValue(null, new InstanceValueDescriptor(myProject, ref), - myEvaluationContext, myNodeManager, true); - if (comparison == null || comparison.first == MyFilteringResult.MATCH) { - children.add(val); - } else { - myErrorsGroup.addErrorValue(comparison.second, val); - } - - totalChildren.incrementAndGet(); - } - - if (children.size() > 0) { - SwingUtilities.invokeLater(() -> { - if (MyFilteringWorker.this == myFilteringTask) { - myInstancesTree.addChildren(children, false); - } - }); - } - - final int childrenSize = children.size(); - final int finalErrorsCount = errorCount; - SwingUtilities.invokeLater(() -> - myProgress.updateProgress(endOfChunk - chunkBegin, childrenSize, finalErrorsCount)); - - synchronized (MyFilteringWorker.this) { - myDebuggerTaskCompleted = true; - MyFilteringWorker.this.notify(); - } - } - }); - - synchronized (MyFilteringWorker.this) { - while (!myDebuggerTaskCompleted) { - MyFilteringWorker.this.wait(); - } - } + if (myTask != null) { + myTask.run(); } - LOG.info(String.format("Filtering time = %d ms, count = %d, matched = %d", - TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start), totalViewed.get(), totalChildren.get())); - - myCompletionReason = totalViewed.get() == myReferences.size() - ? FilteringCompletionReason.ALL_CHECKED - : FilteringCompletionReason.LIMIT_REACHED; - return null; } - - private Pair isSatisfy(@NotNull ExpressionEvaluator evaluator, @NotNull Value value) { - try { - Value result = evaluator.evaluate(myEvaluationContext.createEvaluationContext(value)); - if (result instanceof BooleanValue && ((BooleanValue) result).value()) { - return Pair.create(MyFilteringResult.MATCH, ""); - } - } catch (EvaluateException e) { - return Pair.create(MyFilteringResult.EVAL_ERROR, e.getMessage()); - } - - return Pair.create(MyFilteringResult.NO_MATCH, ""); - } } } @@ -548,8 +497,4 @@ public class InstancesWindow extends DialogWrapper { return new DebuggerTreeNodeImpl(null, new MessageDescriptor(message)); } } - - private enum MyFilteringResult { - MATCH, NO_MATCH, EVAL_ERROR - } } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesWithStackFrameView.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWithStackFrameView.java similarity index 70% rename from java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesWithStackFrameView.java rename to java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWithStackFrameView.java index 9ec7a35214b0..5016a14ee7bc 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/view/InstancesWithStackFrameView.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWithStackFrameView.java @@ -1,5 +1,21 @@ -package org.jetbrains.debugger.memory.view; +/* + * Copyright 2000-2016 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.debugger.memory.ui; +import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.ui.JBSplitter; @@ -8,13 +24,12 @@ import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.components.labels.ActionLink; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ObjectReference; -import icons.MemoryViewIcons; import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.component.CreationPositionTracker; -import org.jetbrains.debugger.memory.component.InstancesTracker; -import org.jetbrains.debugger.memory.event.InstancesTrackerListener; -import org.jetbrains.debugger.memory.tracking.TrackingType; -import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; +import com.intellij.debugger.memory.component.CreationPositionTracker; +import com.intellij.debugger.memory.component.InstancesTracker; +import com.intellij.debugger.memory.event.InstancesTrackerListener; +import com.intellij.debugger.memory.tracking.TrackingType; +import com.intellij.debugger.memory.utils.StackFrameDescriptor; import javax.swing.*; import java.util.Collections; @@ -41,13 +56,17 @@ class InstancesWithStackFrameView { JLabel stackTraceLabel; if (isArrayType(className)) { stackTraceLabel = new JBLabel(TEXT_FOR_ARRAYS, SwingConstants.CENTER); - } else { - ActionLink actionLink = new ActionLink("Enable tracking for new instances", MemoryViewIcons.CLASS_TRACKED, new AnAction() { - @Override - public void actionPerformed(AnActionEvent e) { - InstancesTracker.getInstance(debugSession.getProject()).add(className, TrackingType.CREATION); - } - }); + } + else { + ActionLink actionLink = new ActionLink("Enable tracking for new instances", + AllIcons.Debugger.MemoryView.ClassTracked, + new AnAction() { + @Override + public void actionPerformed(AnActionEvent e) { + InstancesTracker.getInstance(debugSession.getProject()) + .add(className, TrackingType.CREATION); + } + }); actionLink.setHorizontalAlignment(SwingConstants.CENTER); actionLink.setPaintUnderline(false); @@ -87,13 +106,14 @@ class InstancesWithStackFrameView { List stack = tracker.getStack(debugSession, ref); if (stack != null) { list.setFrame(stack); - if(mySplitter.getProportion() == 1.f) { + if (mySplitter.getProportion() == 1.f) { mySplitter.setProportion(DEFAULT_SPLITTER_PROPORTION); } return; } list.setEmptyText(EMPTY_TEXT_WHEN_STACK_NOT_FOUND); - } else { + } + else { list.setEmptyText(EMPTY_TEXT_WHEN_ITEM_NOT_SELECTED); } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/view/StackFrameList.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/StackFrameList.java similarity index 83% rename from java/debugger/impl/src/com/intellij/debugger/memory/view/StackFrameList.java rename to java/debugger/impl/src/com/intellij/debugger/memory/ui/StackFrameList.java index ce9431591901..60898d823133 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/view/StackFrameList.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/StackFrameList.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.view; +/* + * Copyright 2000-2016 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.debugger.memory.ui; import com.intellij.debugger.engine.DebuggerUtils; import com.intellij.execution.filters.OpenFileHyperlinkInfo; @@ -17,15 +32,14 @@ import com.intellij.psi.util.PsiUtilCore; import com.intellij.ui.ColoredListCellRenderer; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBList; -import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; +import com.intellij.debugger.memory.utils.StackFrameDescriptor; import javax.swing.*; import java.util.ArrayList; import java.util.List; -class StackFrameList extends JBList { +class StackFrameList extends JBList { private static final char ANONYMOUS_CLASS_DELIMITER = '$'; private static final MyOpenFilesState myEditorState = new MyOpenFilesState(); @@ -45,7 +59,6 @@ class StackFrameList extends JBList { setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - //noinspection unchecked setModel(myModel); setCellRenderer(new ColoredListCellRenderer() { @@ -54,7 +67,7 @@ class StackFrameList extends JBList { StackFrameDescriptor value, int index, boolean isSelected, boolean hasFocus) { append(String.format("%s:%d, %s", value.methodName(), value.line(), value.className())); String packageName = value.packageName(); - if (!StringUtils.isEmpty(packageName)) { + if (packageName.trim().isEmpty() /*!StringUtils.isEmpty(packageName)*/) { append(String.format(" (%s)", value.packageName()), SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES); } } @@ -66,7 +79,7 @@ class StackFrameList extends JBList { } void navigateToSelectedValue(boolean focusOnEditor) { - StackFrameDescriptor selectedValue = (StackFrameDescriptor) getSelectedValue(); + StackFrameDescriptor selectedValue = getSelectedValue(); if (selectedValue != null) { navigateToFrame(selectedValue, focusOnEditor); } @@ -88,7 +101,7 @@ class StackFrameList extends JBList { } OpenFileHyperlinkInfo info = - new OpenFileHyperlinkInfo(myProject, file, frame.line() - 1); + new OpenFileHyperlinkInfo(myProject, file, frame.line() - 1); OpenFileDescriptor descriptor = info.getDescriptor(); if (descriptor != null) { FileEditorManagerImpl manager = (FileEditorManagerImpl)FileEditorManager.getInstance(myProject); diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/view/StackFramePopup.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/StackFramePopup.java similarity index 64% rename from java/debugger/impl/src/com/intellij/debugger/memory/view/StackFramePopup.java rename to java/debugger/impl/src/com/intellij/debugger/memory/ui/StackFramePopup.java index 0069d0500152..fccbc280f83f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/view/StackFramePopup.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/StackFramePopup.java @@ -1,11 +1,26 @@ -package org.jetbrains.debugger.memory.view; +/* + * Copyright 2000-2016 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.debugger.memory.ui; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.psi.search.GlobalSearchScope; import org.jetbrains.annotations.NotNull; -import org.jetbrains.debugger.memory.utils.StackFrameDescriptor; +import com.intellij.debugger.memory.utils.StackFrameDescriptor; import java.util.List; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableColumnDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableColumnDescriptor.java index 3abb30a98e89..c651a142e716 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableColumnDescriptor.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableColumnDescriptor.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 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.debugger.memory.utils; public abstract class AbstractTableColumnDescriptor implements AbstractTableModelWithColumns.TableColumnDescriptor { private final String myName; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableModelWithColumns.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableModelWithColumns.java index d0a67129078c..d14633c11473 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableModelWithColumns.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/AbstractTableModelWithColumns.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 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.debugger.memory.utils; import javax.swing.table.AbstractTableModel; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/AndroidUtil.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/AndroidUtil.java index 112297fd4a96..4b22c6f9df6c 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/AndroidUtil.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/AndroidUtil.java @@ -1,13 +1,30 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 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.debugger.memory.utils; import com.sun.jdi.VirtualMachine; import org.jetbrains.annotations.NotNull; +import java.util.Locale; + public class AndroidUtil { public static final int ANDROID_COUNT_BY_CLASSES_BATCH_SIZE = 500; public static final int ANDROID_INSTANCES_LIMIT = 30000; public static boolean isAndroidVM(@NotNull VirtualMachine vm) { - return vm.name().toLowerCase().contains("dalvik"); + return vm.name().toLowerCase(Locale.ENGLISH).contains("dalvik"); } } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/ErrorsValueGroup.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/ErrorsValueGroup.java index 922a70cf9af3..50ba2cf355ef 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/ErrorsValueGroup.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/ErrorsValueGroup.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 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.debugger.memory.utils; import com.intellij.icons.AllIcons; import com.intellij.util.containers.HashMap; @@ -17,8 +32,8 @@ import java.util.Map; public class ErrorsValueGroup extends XValueGroup { private final Map> myErrorMessage2ValueMap = new HashMap<>(); - public ErrorsValueGroup(@NotNull String name) { - super(name); + public ErrorsValueGroup() { + super("Errors"); } public void addErrorValue(@NotNull String message, @NotNull XNamedValue value) { diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceJavaValue.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceJavaValue.java index 0c8046e6c989..a7d2042bce74 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceJavaValue.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceJavaValue.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 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.debugger.memory.utils; import com.intellij.debugger.engine.JavaValue; import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; @@ -9,12 +24,10 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class InstanceJavaValue extends JavaValue { - public InstanceJavaValue(JavaValue javaValue, - @NotNull ValueDescriptorImpl valueDescriptor, - @NotNull EvaluationContextImpl evaluationContext, - NodeManagerImpl nodeManager, - boolean contextSet) { - super(javaValue, valueDescriptor, evaluationContext, nodeManager, contextSet); + public InstanceJavaValue(@NotNull ValueDescriptorImpl valueDescriptor, + @NotNull EvaluationContextImpl evaluationContext, + NodeManagerImpl nodeManager) { + super(null, valueDescriptor, evaluationContext, nodeManager, false); } @Nullable diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceValueDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceValueDescriptor.java index 101d30714643..76202a68253e 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceValueDescriptor.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstanceValueDescriptor.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 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.debugger.memory.utils; import com.intellij.debugger.DebuggerContext; import com.intellij.debugger.engine.ContextUtil; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstancesProvider.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstancesProvider.java index 1a717474b1b8..159c5884e5ff 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstancesProvider.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/InstancesProvider.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 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.debugger.memory.utils; import com.sun.jdi.ObjectReference; import org.jetbrains.annotations.NotNull; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/KeyboardUtils.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/KeyboardUtils.java index 57899d96122e..0ae37a822941 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/KeyboardUtils.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/KeyboardUtils.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 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.debugger.memory.utils; import java.awt.event.KeyEvent; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/LowestPriorityCommand.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/LowestPriorityCommand.java index 214cc5681f66..757b990a826d 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/LowestPriorityCommand.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/LowestPriorityCommand.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 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.debugger.memory.utils; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/NamesUtils.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/NamesUtils.java index d7fdd334bf7d..eec5e949ba72 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/NamesUtils.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/NamesUtils.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 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.debugger.memory.utils; import com.intellij.openapi.util.text.StringUtil; import com.sun.jdi.ArrayReference; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/SingleAlarmWithMutableDelay.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/SingleAlarmWithMutableDelay.java index 321cf46828c8..7537abbb6d38 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/SingleAlarmWithMutableDelay.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/SingleAlarmWithMutableDelay.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 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.debugger.memory.utils; import com.intellij.openapi.Disposable; import com.intellij.util.Alarm; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/StackFrameDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/StackFrameDescriptor.java index 7d1ca9d6ebef..e37f6ec763c3 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/StackFrameDescriptor.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/StackFrameDescriptor.java @@ -1,4 +1,19 @@ -package org.jetbrains.debugger.memory.utils; +/* + * Copyright 2000-2016 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.debugger.memory.utils; import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; diff --git a/platform/icons/src/debugger/memoryView/classTracked.png b/platform/icons/src/debugger/memoryView/classTracked.png new file mode 100644 index 0000000000000000000000000000000000000000..27cdbcb6c2d0463533c25301f862d200a30ee45a GIT binary patch literal 378 zcmV-=0fqjFP)OfMT{_To$K`rsChLI zuK>|N{%^;b?pNb?rF8)L;UF65vTHyMH$h&+Fu-k|&u5^*J3xFBh;IRH{0_uFf$T*< zJP$+z`M-f0da%ZV^K8$PiF-2s0~P)U(LnwQmpNV_11+%$1-mMp&T&6emJw`JLD^@X8#yxzN**e7(l6_Yw#a@n6Zfmo(mux$**C2{1D?6WXi8kX#(s$I>?pI zA%I#%ktwR^z%v1arhEyL4VDEeZn%!s>~VF~(&mU*i4c8MP`~p90xTz0Gz4!?RYem4 z@{d|KVxp(^_KMi{v(;{Jf(Z1cf1xQy*@KSQzH8@CzN{{(yV*A?|`MY*?2O zpmwJ7Vj=(^HvMR9vfQsay*&mJxWNe`(Ap5+fmJv{Ca=b%{}gs??yNlT+zYh@ZN+uy z{|li+GPznsDRMo108EfBik9B4{LT@F7)am-Cy05?1s`FB?wX?3cQMg$$?38y<=u`G z5P~mYmw2-DGmLq}hK$gShJ&=Gn#6SXfK~vG^ z8temYBmV@;Jy3eK{8zXN=iwxTq23g;wlF?nki5Mu>!Vm3;Qux?ZWi{~xaEkRCWQ*ULh9vYr z0^$&3{QOO=pYtjKEUiUj);3!j9>Q;M9WKHtbF}ab0w{-$c2++|I!Rbb`Sc?~gGn}i zV@^X7dLY4j@~&unY>M&nZ_z=0+R$Q_%~4S?1pROqZop-sIsYeY(!PwNc;*1g2eb{G zk@B)5Y`q|Jb;dL#bzcehHD2~H?cwP0F(LjE6B1;Utm;y{n#t-D;2{A4Kf$XQw_ra+WOEpq786sUpPQED6jrw zRK%E8;$-F^KMvPV(Kd>{-Rih`3!u<=be3F6_h+tGH#!d2Xfsb9v-qFUVt{P?4}u>> UDE4W#(EtDd07*qoM6N<$f+}M6=l}o! literal 0 HcmV?d00001 diff --git a/platform/icons/src/debugger/memoryView/classTracked@2x_dark.png b/platform/icons/src/debugger/memoryView/classTracked@2x_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..3775b78fcc386bf128d8925bf1de2411b0a67e1c GIT binary patch literal 1047 zcmV+y1nB#TP)sWdUw6v_3r4_#-)VJ5kjJ-N`hJ; zF3>U8-U13Zo0b zWdv}#=+Hb}wD-0ED4t+6abkCA&xN;6#$Vl@?{l8TWt?S;-k=z+Ik zr%(#NiQ+f>nrJ=lz4qPjXlf(E#p}Z1_EBk|qYN@6AptRn^4U-qEY=hQ!?nY0#?h2PbbG z59Zo`fOAlUBhU-`*n)Hr0VeFb=bPS&sY38KI>k>hy<8PWpbRo3AweJE`za;(9YUDw zG06yIq$*s3^Ps>m^tICiyXKKov%y3^F8r$GMACp8L=W zK&SaSJ;WPu9e#vwL4z!t6_4R%wKkoXmh@vp4EP`R6}V}4kI!s-k1(JLBT)A2r_T6_ ze2vVo4^buFAk(Z#e8E^IVGJ(A1^5bU&Qc0rY1oRJ_zLg=&j8&w`+e)ZX-4yA z{1v$79cBvd6t%&b_oV5kdR+2-|{(8!Q8Yb%RGL^0h-}X)31}Kxca6( zkGb)K&HuphEsV{$hv{9;x)lpxX&odlkCi+%;?e8Lb6q#@~4G*mbrL R9xea?002ovPDHLkV1gC)^ZNh* literal 0 HcmV?d00001 diff --git a/platform/icons/src/debugger/memoryView/classTracked_dark.png b/platform/icons/src/debugger/memoryView/classTracked_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..955ba09d608e4599a762e0727038940ed8173a49 GIT binary patch literal 351 zcmV-l0igbgP)#$Q1qP{T)^oehIbj_eLDCX5V_5cw)G#W?yA5KRahVH0DZ zLwQcq8ljgU(Zu1&+xOmk-sGHnt2vIN{RZt%0<7-cDn*#Wxnge#Rlot%VAl)TDqsih zVc+wG9|auUrd?Qw!>qj=n&2ZNR_&jSIdCf%9R_NEiSB002ovPDHLkV1kbno8JHc literal 0 HcmV?d00001 diff --git a/platform/icons/src/debugger/memoryView/toolWindowDisabled.png b/platform/icons/src/debugger/memoryView/toolWindowDisabled.png new file mode 100644 index 0000000000000000000000000000000000000000..9c6218fa9d163ba867fe082a762286f82ef4ca73 GIT binary patch literal 241 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4u6ByT*@`3|IZc)B=-RNP8_k?V0$Q+@IM$_jA3QCU^3G{BB_t&d;9~^=;zw-mt$b pyWB}7F{r~JhIb;17E?D9!_~P554YRs2?71U;OXk;vd$@?2>_hYVoU%4 literal 0 HcmV?d00001 diff --git a/platform/icons/src/debugger/memoryView/toolWindowDisabled@2x.png b/platform/icons/src/debugger/memoryView/toolWindowDisabled@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..68c7411bfa2d1ff95cd0e68d55a7b7d86e83b800 GIT binary patch literal 472 zcmV;}0Vn>6P)NklJbj?OfSP<{2O7o@rZ6>zh37_vrm0OwKN&G1R$a?>VF8TO;^^i6fLbd|NRQZc z&3MIQAJ8<_TC^55RlS8z%zU#oj3$NB7=^(glO!P;_LNV;0N8OClkCZih`|gduq3wrohTuGU#j@ zbC~`Sszrh6V>#s59hcSQrZ@1QbtA5%Mzqj$-w5ELauwz<$t1p~myf%aS3S!&X>P)4 zZl;gTeGDF>n_S9@Sdm;xzjemQ(aDpCt*=H1nwX?8u-U$dfpj%uaA~~5OdlJA;||u6 zF1C3LBzHuQ*kq4eLL2`sU}S%@br-NrAs0J!v0__gh42?Ir(oW8wzC^ z2pC~)ty5gV!;qc=}Co@ueF7- zMxDh#@J0-gj*gfm)alk=D%_7heH~sGw`=cyW{tfZp4Y#9JKe8e z@lM44dHGYrfK5C%4`_$SG_ZX-^U(5jdh)co9D56<1VLbO1G7 z2jZWF1^KsoYO^|^nipZU38_yc|0Io zQIhKkvJq+C!h<^cXxC_z%WdCok&fW>rh#t9%fVd2ZEs^4> p7^DGNBf2JRX>T>MMyNq>wE#<#a1#SuN6-KO002ovPDHLkV1hphcDn!o literal 0 HcmV?d00001 diff --git a/platform/icons/src/debugger/memoryView/toolWindowEnabled@2x.png b/platform/icons/src/debugger/memoryView/toolWindowEnabled@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0c617eeb83176833def52827eeeb624389d33080 GIT binary patch literal 604 zcmV-i0;BzjP)HD^46c6XX@YA?>$OQP#!+hU+&Jgi^KQc_G`7*$tk%+t|iFlrOhee3w zH3?sQ1wxTZ(S#Kk+$tV*}(N)!TfUk zCb5f!lAr~yrVYatsMcyV!*$&=NUnje;d(c3e*Ju~W@5prN~UccURsnmj`K|{_6yXD zuC>yrtUo5a&ZO<8_{`sQt?#GxHACE4Xi-w!2_?O3HoJ`Xq9}^VZ0D?Hw6e$VRtn~s zXk#~Q7@mRw_z9j3g(0S5Cuc2*@BVJT{yVc)*f6{kce5|0E~W!*{Ky&vq$AQeS(M3S zE?9&at>BHAAUaOOnX!h+6wwdWx;~4_Mo55>D8$9o|gmU}4JusJ7n zE;ej?o+IdBYr}fc+*^M{cWLcQUM3iToXruN}&1X~R<7CGD>)@o6#QZaOK7wq6r}(QBg$8!l)Rc#+YI$w9YgkzJ%^z z1wH{mQR_KnxH!&WxfmB8X-a24=A3_~cx>DD^kSt}+So(l;pap=u~_UI;}G$4BKTno z*ceuXV?}fRyC^(@C@d6FN`=6n5XUBP3Y3THkw7v7>{7E(qm(oqOua_6EVVAe5tt|l zC_sJCFw}u)JRT3Js#*fcCtyqXr1|SdOIcfo38NyJv~=*HphVa8CpOs|AtXtQ>Jq(4 zOgrE;F;n=R^Gq@KY@Muz*vedsf@~!yscM=QfO}pT$ bmh|Wg;=pAPy}iS900000NkvXXu0mjff07T9 literal 0 HcmV?d00001 diff --git a/platform/icons/src/debugger/memoryView/toolWindowEnabled_dark.png b/platform/icons/src/debugger/memoryView/toolWindowEnabled_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..10ba7a9b194da2e90b93763cc4ede23893d61800 GIT binary patch literal 304 zcmV-00nh%4P)Upjlt@9LFHkD(gNa5?V^ z5W7H~#{709LQmVX*#;PLkJ{}tO>=Y%a<E9-u=Z{;dB10000 + + + com.intellij.debugger.memory.component.MemoryViewManager + + + com.intellij.debugger.ui.DebuggerPanelsManager @@ -10,7 +16,7 @@ com.intellij.debugger.ui.HotSwapUI - com.intellij.debugger.ui.HotSwapUIImpl + com.intellij.debugger.ui.HotSwapUIImpl @@ -19,8 +25,24 @@ + + + com.intellij.debugger.memory.component.InstancesTracker + + + + com.intellij.debugger.memory.component.CreationPositionTracker + + + + + + + diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/DomCompletionContributor.java b/xml/dom-impl/src/com/intellij/util/xml/impl/DomCompletionContributor.java index 5d18e9358d26..4d7fcf04504d 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/DomCompletionContributor.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/DomCompletionContributor.java @@ -82,7 +82,7 @@ public class DomCompletionContributor extends CompletionContributor{ } String[] enumeratedValues = XmlAttributeValueGetter.getEnumeratedValues((XmlAttribute)parent); - if (enumeratedValues != null && enumeratedValues.length > 0) { + if (enumeratedValues.length > 0) { String value = descriptor == null ? null : descriptor.getDefaultValue(); if (value == null || enumeratedValues.length != 1 || !value.equals(enumeratedValues[0])) { return true; diff --git a/xml/impl/src/com/intellij/codeInsight/completion/DtdCompletionData.java b/xml/impl/src/com/intellij/codeInsight/completion/DtdCompletionData.java index 61aa19f8f442..3eac2b511978 100644 --- a/xml/impl/src/com/intellij/codeInsight/completion/DtdCompletionData.java +++ b/xml/impl/src/com/intellij/codeInsight/completion/DtdCompletionData.java @@ -84,7 +84,7 @@ public class DtdCompletionData extends CompletionData { final CompletionVariant variant = new CompletionVariant(entityFilter); variant.includeScopeClass(XmlToken.class, true); variant.addCompletion(new DtdEntityGetter()); - variant.setInsertHandler(new XmlCompletionData.EntityRefInsertHandler()); + variant.setInsertHandler(XmlCompletionContributor.ENTITY_INSERT_HANDLER); registerVariant(variant); } } diff --git a/xml/impl/src/com/intellij/psi/filters/getters/HtmlAttributeValueGetter.java b/xml/impl/src/com/intellij/codeInsight/completion/HtmlCompletionContributor.java similarity index 62% rename from xml/impl/src/com/intellij/psi/filters/getters/HtmlAttributeValueGetter.java rename to xml/impl/src/com/intellij/codeInsight/completion/HtmlCompletionContributor.java index 1b889490d09d..f696ae204a76 100644 --- a/xml/impl/src/com/intellij/psi/filters/getters/HtmlAttributeValueGetter.java +++ b/xml/impl/src/com/intellij/codeInsight/completion/HtmlCompletionContributor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 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,43 +13,59 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.psi.filters.getters; +package com.intellij.codeInsight.completion; +import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.patterns.XmlPatterns; +import com.intellij.psi.PsiElement; +import com.intellij.psi.html.HtmlTag; +import com.intellij.psi.impl.source.html.dtd.HtmlElementDescriptorImpl; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.XmlAttribute; +import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.psi.xml.XmlTag; +import com.intellij.util.ArrayUtil; +import com.intellij.util.ProcessingContext; import com.intellij.xml.util.HtmlUtil; import com.intellij.xml.util.XmlUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.nio.charset.Charset; -/** - * Created by IntelliJ IDEA. - * User: ik - * Date: 24.11.2003 - * Time: 14:17:59 - * To change this template use Options | File Templates. - */ -public class HtmlAttributeValueGetter extends XmlAttributeValueGetter { - private final boolean myCaseSensitive; +import static com.intellij.patterns.PlatformPatterns.psiElement; - public HtmlAttributeValueGetter(boolean _caseSensitive) { - myCaseSensitive = _caseSensitive; +public class HtmlCompletionContributor extends CompletionContributor { + public HtmlCompletionContributor() { + extend(CompletionType.BASIC, psiElement().inside(XmlPatterns.xmlAttributeValue()), new CompletionProvider() { + @Override + protected void addCompletions(@NotNull CompletionParameters parameters, + ProcessingContext context, + @NotNull CompletionResultSet result) { + final PsiElement position = parameters.getPosition(); + if (PsiTreeUtil.getParentOfType(position, HtmlTag.class, false) == null) { + return; + } + final XmlAttributeValue attributeValue = PsiTreeUtil.getParentOfType(position, XmlAttributeValue.class, false); + if (attributeValue != null && attributeValue.getParent() instanceof XmlAttribute) { + for (String element : addSpecificCompletions((XmlAttribute)attributeValue.getParent())) { + result.addElement(LookupElementBuilder.create(element)); + } + } + } + }); } - @Override - @Nullable + @NotNull @NonNls - protected String[] addSpecificCompletions(final XmlAttribute attribute) { + protected static String[] addSpecificCompletions(final XmlAttribute attribute) { @NonNls String name = attribute.getName(); final XmlTag tag = attribute.getParent(); - if (tag == null) return null; + if (tag == null) return ArrayUtil.EMPTY_STRING_ARRAY; @NonNls String tagName = tag.getName(); - if (!myCaseSensitive) { + if (tag.getDescriptor() instanceof HtmlElementDescriptorImpl) { name = name.toLowerCase(); tagName = tagName.toLowerCase(); } @@ -81,7 +97,7 @@ public class HtmlAttributeValueGetter extends XmlAttributeValueGetter { else if ("http-equiv".equals(name) && "meta".equals(tagName)) { return HtmlUtil.RFC2616_HEADERS; } - else if("content".equals(name) && "meta".equals(tagName) && getAttribute(tag, "name") == null) { + else if("content".equals(name) && "meta".equals(tagName) && tag.getAttribute("name") == null) { return HtmlUtil.CONTENT_TYPES; } else if ("accept".equals(name) && "input".equals(tagName)) { @@ -97,21 +113,6 @@ public class HtmlAttributeValueGetter extends XmlAttributeValueGetter { } } - return null; - } - - @Nullable - private XmlAttribute getAttribute(@NotNull XmlTag tag, @NotNull String attributeName) { - if (myCaseSensitive) { - return tag.getAttribute(attributeName); - } - - for (XmlAttribute xmlAttribute : tag.getAttributes()) { - if (attributeName.equalsIgnoreCase(xmlAttribute.getName())) { - return xmlAttribute; - } - } - - return null; + return ArrayUtil.EMPTY_STRING_ARRAY; } } diff --git a/xml/impl/src/com/intellij/codeInsight/completion/HtmlCompletionData.java b/xml/impl/src/com/intellij/codeInsight/completion/HtmlCompletionData.java index 7bb515438bfc..011b35ccbfce 100644 --- a/xml/impl/src/com/intellij/codeInsight/completion/HtmlCompletionData.java +++ b/xml/impl/src/com/intellij/codeInsight/completion/HtmlCompletionData.java @@ -15,25 +15,18 @@ */ package com.intellij.codeInsight.completion; -import com.intellij.lang.Language; import com.intellij.openapi.util.Comparing; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiReference; import com.intellij.psi.filters.AndFilter; import com.intellij.psi.filters.ElementFilter; import com.intellij.psi.filters.OrFilter; import com.intellij.psi.filters.TextContainFilter; -import com.intellij.psi.filters.getters.HtmlAttributeValueGetter; -import com.intellij.psi.filters.getters.XmlAttributeValueGetter; import com.intellij.psi.filters.position.XmlTokenTypeFilter; -import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReference; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlTag; -import com.intellij.psi.xml.XmlToken; import com.intellij.psi.xml.XmlTokenType; import com.intellij.xml.util.HtmlUtil; -import org.jetbrains.annotations.NonNls; /** * @author Maxim.Mossienko @@ -41,11 +34,6 @@ import org.jetbrains.annotations.NonNls; @SuppressWarnings({"RefusedBequest"}) public class HtmlCompletionData extends XmlCompletionData { private boolean myCaseInsensitive; - private static final @NonNls String JAVASCRIPT_LANGUAGE_ID = "JavaScript"; - - public HtmlCompletionData() { - this(true); - } protected HtmlCompletionData(boolean _caseInsensitive) { myCaseInsensitive = _caseInsensitive; @@ -83,11 +71,6 @@ public class HtmlCompletionData extends XmlCompletionData { myCaseInsensitive = caseInsensitive; } - @Override - protected XmlAttributeValueGetter getAttributeValueGetter() { - return new HtmlAttributeValueGetter(!isCaseInsensitive()); - } - @Override protected ElementFilter createTagCompletionFilter() { return new ElementFilter() { @@ -132,36 +115,6 @@ public class HtmlCompletionData extends XmlCompletionData { }; } - @Override - protected ElementFilter createAttributeValueCompletionFilter() { - return new ElementFilter() { - @Override - public boolean isAcceptable(Object element, PsiElement context) { - if (isStyleAttributeContext(context)) return false; - if (isScriptContext((PsiElement)element)) return false; - if (hasCaseSensitiveFileReferences(context)) return false; - return true; - } - - @Override - public boolean isClassAcceptable(Class hintClass) { - return true; - } - }; - } - - private static boolean hasCaseSensitiveFileReferences(PsiElement context) { - for (PsiReference reference : context.getReferences()) { - if (reference instanceof FileReference && ((FileReference)reference).getFileReferenceSet().isCaseSensitive()) return true; - } - return false; - } - - private static boolean isScriptContext(PsiElement element) { - final Language language = element.getLanguage(); - - return language.getID().equals(JAVASCRIPT_LANGUAGE_ID); - } private boolean isStyleAttributeContext(PsiElement position) { XmlAttribute parentOfType = PsiTreeUtil.getParentOfType(position, XmlAttribute.class, false); @@ -173,26 +126,4 @@ public class HtmlCompletionData extends XmlCompletionData { super.registerVariant(variant); if (isCaseInsensitive()) variant.setCaseInsensitive(true); } - - @Override - public String findPrefix(PsiElement insertedElement, int offset) { - String prefix = super.findPrefix(insertedElement, offset); - - boolean searchForEntities = - insertedElement instanceof XmlToken && - ( ((XmlToken)insertedElement).getTokenType() == XmlTokenType.XML_DATA_CHARACTERS || - ((XmlToken)insertedElement).getTokenType() == XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN - ); - - if (searchForEntities) { - if (prefix.startsWith("&")) { - prefix = prefix.substring(1); - } else if (prefix.contains("&")) { - prefix = prefix.substring(prefix.indexOf("&") + 1); - } - } - - return prefix; - } - } diff --git a/xml/impl/src/com/intellij/codeInsight/completion/XmlAttributeReferenceCompletionProvider.java b/xml/impl/src/com/intellij/codeInsight/completion/XmlAttributeReferenceCompletionProvider.java index 0e220b66bb04..47c39c6dbade 100644 --- a/xml/impl/src/com/intellij/codeInsight/completion/XmlAttributeReferenceCompletionProvider.java +++ b/xml/impl/src/com/intellij/codeInsight/completion/XmlAttributeReferenceCompletionProvider.java @@ -21,6 +21,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiReference; import com.intellij.psi.html.HtmlTag; +import com.intellij.psi.impl.source.html.dtd.HtmlAttributeDescriptorImpl; import com.intellij.psi.impl.source.xml.XmlAttributeImpl; import com.intellij.psi.impl.source.xml.XmlAttributeReference; import com.intellij.psi.meta.PsiPresentableMetaData; @@ -78,10 +79,6 @@ public class XmlAttributeReferenceCompletionProvider extends CompletionProvider< ? attribute.getNamespacePrefix() + ":" : null; - CompletionData - completionData = CompletionUtil.getCompletionDataByElement(attribute, attribute.getContainingFile().getOriginalFile()); - boolean caseSensitive = !(completionData instanceof HtmlCompletionData) || ((HtmlCompletionData)completionData).isCaseSensitive(); - for (XmlAttributeDescriptor descriptor : descriptors) { if (isValidVariant(attribute, descriptor, attributes, extension)) { String name = descriptor.getName(tag); @@ -121,7 +118,7 @@ public class XmlAttributeReferenceCompletionProvider extends CompletionProvider< element = element.withLookupString(name.substring(separator + 1)); } element = element - .withCaseSensitivity(caseSensitive) + .withCaseSensitivity(!(descriptor instanceof HtmlAttributeDescriptorImpl)) .withInsertHandler(insertHandler); result.addElement( descriptor.isRequired() ? PrioritizedLookupElement.withPriority(element.appendTailText("(required)", true), 100) : diff --git a/xml/impl/src/com/intellij/codeInsight/completion/XmlCompletionContributor.java b/xml/impl/src/com/intellij/codeInsight/completion/XmlCompletionContributor.java index 9073329743e8..7ab8d58c34f0 100644 --- a/xml/impl/src/com/intellij/codeInsight/completion/XmlCompletionContributor.java +++ b/xml/impl/src/com/intellij/codeInsight/completion/XmlCompletionContributor.java @@ -22,10 +22,14 @@ import com.intellij.codeInsight.lookup.LookupElementDecorator; import com.intellij.codeInsight.template.emmet.completion.EmmetAbbreviationCompletionProvider; import com.intellij.featureStatistics.FeatureUsageTracker; import com.intellij.lang.ASTNode; +import com.intellij.lang.html.HTMLLanguage; import com.intellij.lang.xml.XMLLanguage; import com.intellij.openapi.actionSystem.IdeActions; +import com.intellij.openapi.editor.CaretModel; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.text.StringUtil; @@ -33,11 +37,17 @@ import com.intellij.patterns.XmlPatterns; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiReference; +import com.intellij.psi.impl.source.html.dtd.HtmlElementDescriptorImpl; +import com.intellij.psi.search.PsiElementProcessor; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.tree.TokenSet; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.*; import com.intellij.util.ProcessingContext; -import com.intellij.xml.XmlBundle; -import com.intellij.xml.XmlExtension; +import com.intellij.xml.*; +import com.intellij.xml.util.HtmlUtil; +import com.intellij.xml.util.XmlEnumeratedValueReference; +import com.intellij.xml.util.XmlUtil; import gnu.trove.THashSet; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -46,13 +56,17 @@ import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Set; +import static com.intellij.codeInsight.completion.XmlCompletionData.findDescriptorFile; import static com.intellij.patterns.PlatformPatterns.psiElement; +import static com.intellij.psi.filters.getters.XmlAttributeValueGetter.getEnumeratedValues; +import static com.intellij.xml.util.XmlUtil.VALUE_ATTR_NAME; /** * @author Dmitry Avdeev */ public class XmlCompletionContributor extends CompletionContributor { public static final Key WORD_COMPLETION_COMPATIBLE = Key.create("WORD_COMPLETION_COMPATIBLE"); + public static final EntityRefInsertHandler ENTITY_INSERT_HANDLER = new EntityRefInsertHandler(); @NonNls public static final String TAG_NAME_COMPLETION_FEATURE = "tag.name.completion"; private static final InsertHandlerDecorator QUOTE_EATER = new InsertHandlerDecorator() { @@ -80,6 +94,50 @@ public class XmlCompletionContributor extends CompletionContributor { public XmlCompletionContributor() { extend(CompletionType.BASIC, psiElement().inside(XmlPatterns.xmlFile()), new EmmetAbbreviationCompletionProvider()); + extend(CompletionType.BASIC, psiElement().inside(XmlPatterns.xmlFile()), new CompletionProvider() { + @Override + protected void addCompletions(@NotNull CompletionParameters parameters, + ProcessingContext context, + @NotNull CompletionResultSet result) { + PsiElement position = parameters.getPosition(); + IElementType type = position.getNode().getElementType(); + if (type != XmlTokenType.XML_DATA_CHARACTERS && type != XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN) { + return; + } + if ((position.getPrevSibling() != null && position.getPrevSibling().textMatches("&")) || position.textContains('&')) { + PrefixMatcher matcher = result.getPrefixMatcher(); + String prefix = matcher.getPrefix(); + if (prefix.startsWith("&")) { + prefix = prefix.substring(1); + } else if (prefix.contains("&")) { + prefix = prefix.substring(prefix.indexOf("&") + 1); + } + + addEntityRefCompletions(position, result.withPrefixMatcher(prefix)); + } + } + }); + extend(CompletionType.BASIC, psiElement().inside(XmlPatterns.xmlAttributeValue()), new CompletionProvider() { + @Override + protected void addCompletions(@NotNull CompletionParameters parameters, + ProcessingContext context, + @NotNull CompletionResultSet result) { + XmlAttribute attr = PsiTreeUtil.getParentOfType(parameters.getPosition(), XmlAttribute.class); + if (attr != null && !hasEnumerationReference(parameters, result)) { + final XmlAttributeDescriptor descriptor = attr.getDescriptor(); + + if (descriptor != null) { + if (descriptor.isFixed() && descriptor.getDefaultValue() != null) { + result.addElement(LookupElementBuilder.create(descriptor.getDefaultValue())); + return; + } + for (String value : getEnumeratedValues(attr)) { + result.addElement(LookupElementBuilder.create(value)); + } + } + } + } + }); extend(CompletionType.BASIC, psiElement().inside(XmlPatterns.xmlAttributeValue()), new CompletionProvider() { @@ -115,6 +173,36 @@ public class XmlCompletionContributor extends CompletionContributor { } } }); + extend(CompletionType.BASIC, psiElement().withElementType(XmlTokenType.XML_DATA_CHARACTERS), + new CompletionProvider() { + @Override + protected void addCompletions(@NotNull CompletionParameters parameters, + ProcessingContext context, + @NotNull CompletionResultSet result) { + XmlTag tag = PsiTreeUtil.getParentOfType(parameters.getPosition(), XmlTag.class, false); + if (tag != null && !hasEnumerationReference(parameters, result)) { + final XmlTag simpleContent = XmlUtil.getSchemaSimpleContent(tag); + if (simpleContent != null) { + XmlUtil.processEnumerationValues(simpleContent, (element) -> { + String value = element.getAttributeValue(VALUE_ATTR_NAME); + assert value != null; + result.addElement(LookupElementBuilder.create(value)); + return true; + }); + } + } + } + }); + } + + private static boolean hasEnumerationReference(CompletionParameters parameters, CompletionResultSet result) { + Ref hasRef = Ref.create(false); + LegacyCompletionContributor.processReferences(parameters, result, (reference, resultSet) -> { + if (reference instanceof XmlEnumeratedValueReference) { + hasRef.set(true); + } + }); + return hasRef.get(); } public static boolean isXmlNameCompletion(final CompletionParameters parameters) { @@ -218,4 +306,110 @@ public class XmlCompletionContributor extends CompletionContributor { } } } + + private static void addEntityRefCompletions(PsiElement context, CompletionResultSet resultSet) { + XmlFile containingFile = null; + XmlFile descriptorFile = null; + final XmlTag tag = PsiTreeUtil.getParentOfType(context, XmlTag.class); + + if (tag != null) { + containingFile = (XmlFile)tag.getContainingFile(); + descriptorFile = findDescriptorFile(tag, containingFile); + } + + if (HtmlUtil.isHtml5Context(tag)) { + descriptorFile = XmlUtil.findXmlFile(containingFile, Html5SchemaProvider.getCharsDtdLocation()); + } else if (tag == null) { + final XmlDocument document = PsiTreeUtil.getParentOfType(context, XmlDocument.class); + + if (document != null) { + containingFile = (XmlFile)document.getContainingFile(); + + final FileType ft = containingFile.getFileType(); + if (HtmlUtil.isHtml5Document(document)) { + descriptorFile = XmlUtil.findXmlFile(containingFile, Html5SchemaProvider.getCharsDtdLocation()); + } else if(ft != StdFileTypes.XML) { + final String namespace = ft == StdFileTypes.XHTML || ft == StdFileTypes.JSPX ? XmlUtil.XHTML_URI : XmlUtil.HTML_URI; + final XmlNSDescriptor nsDescriptor = document.getDefaultNSDescriptor(namespace, true); + + if (nsDescriptor != null) { + descriptorFile = nsDescriptor.getDescriptorFile(); + } + } + } + } + + if (descriptorFile != null && containingFile != null) { + final boolean acceptSystemEntities = containingFile.getFileType() == StdFileTypes.XML; + final boolean caseInsensitive = (tag != null && tag.getDescriptor() instanceof HtmlElementDescriptorImpl) || + containingFile.getLanguage().isKindOf(HTMLLanguage.INSTANCE); + + final PsiElementProcessor processor = new PsiElementProcessor() { + @Override + public boolean execute(@NotNull final PsiElement element) { + if (element instanceof XmlEntityDecl) { + final XmlEntityDecl xmlEntityDecl = (XmlEntityDecl)element; + if (xmlEntityDecl.isInternalReference() || acceptSystemEntities) { + final LookupElementBuilder _item = buildEntityLookupItem(xmlEntityDecl); + if (_item != null) { + resultSet.addElement(_item.withCaseSensitivity(!caseInsensitive)); + resultSet.stopHere(); + } + } + } + return true; + } + }; + + XmlUtil.processXmlElements(descriptorFile, processor, true); + if (descriptorFile != containingFile && acceptSystemEntities) { + final XmlProlog element = containingFile.getDocument().getProlog(); + if (element != null) XmlUtil.processXmlElements(element, processor, true); + } + } + } + + @Nullable + private static LookupElementBuilder buildEntityLookupItem(@NotNull final XmlEntityDecl decl) { + final String name = decl.getName(); + if (name == null) { + return null; + } + + final LookupElementBuilder result = LookupElementBuilder.create(name).withInsertHandler(ENTITY_INSERT_HANDLER); + final XmlAttributeValue value = decl.getValueElement(); + final ASTNode node = value.getNode(); + if (node != null) { + final ASTNode[] nodes = node.getChildren(TokenSet.create(XmlTokenType.XML_CHAR_ENTITY_REF)); + if (nodes.length == 1) { + final String valueText = nodes[0].getText(); + final int i = valueText.indexOf('#'); + if (i > 0) { + String s = valueText.substring(i + 1); + s = StringUtil.trimEnd(s, ";"); + + try { + final int unicodeChar = Integer.valueOf(s).intValue(); + return result.withTypeText(String.valueOf((char)unicodeChar)); + } + catch (NumberFormatException e) { + return result; + } + } + } + } + + return result; + } + + private static class EntityRefInsertHandler extends BasicInsertHandler { + @Override + public void handleInsert(InsertionContext context, LookupElement item) { + super.handleInsert(context, item); + context.setAddCompletionChar(false); + final CaretModel caretModel = context.getEditor().getCaretModel(); + context.getEditor().getDocument().insertString(caretModel.getOffset(), ";"); + caretModel.moveToOffset(caretModel.getOffset() + 1); + } + } } diff --git a/xml/impl/src/com/intellij/codeInsight/completion/XmlCompletionData.java b/xml/impl/src/com/intellij/codeInsight/completion/XmlCompletionData.java index e41f3dd9fefa..961304d07066 100644 --- a/xml/impl/src/com/intellij/codeInsight/completion/XmlCompletionData.java +++ b/xml/impl/src/com/intellij/codeInsight/completion/XmlCompletionData.java @@ -15,35 +15,13 @@ */ package com.intellij.codeInsight.completion; -import com.intellij.codeInsight.TailType; -import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.codeInsight.lookup.LookupValueFactory; -import com.intellij.lang.ASTNode; -import com.intellij.openapi.editor.CaretModel; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.fileTypes.StdFileTypes; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.PsiElement; import com.intellij.psi.filters.*; -import com.intellij.psi.filters.getters.XmlAttributeValueGetter; import com.intellij.psi.filters.position.LeftNeighbour; import com.intellij.psi.filters.position.XmlTokenTypeFilter; -import com.intellij.psi.search.PsiElementProcessor; -import com.intellij.psi.tree.TokenSet; -import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.*; -import com.intellij.util.ArrayUtil; -import com.intellij.xml.Html5SchemaProvider; import com.intellij.xml.XmlElementDescriptor; import com.intellij.xml.XmlNSDescriptor; -import com.intellij.xml.util.HtmlUtil; -import com.intellij.xml.util.XmlUtil; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; /** * Created by IntelliJ IDEA. @@ -72,41 +50,7 @@ public class XmlCompletionData extends CompletionData { registerVariant(variant); } - { - XmlAttributeValueGetter getter = getAttributeValueGetter(); - if (getter != null) { - final CompletionVariant variant = new CompletionVariant(createAttributeValueCompletionFilter()); - variant.includeScopeClass(XmlAttributeValue.class); - variant.addCompletion(getter, TailType.NONE); - variant.addCompletionFilter(TrueFilter.INSTANCE, TailType.NONE); - registerVariant(variant); - } - } - final ElementFilter entityCompletionFilter = createXmlEntityCompletionFilter(); - - { - final CompletionVariant variant = new CompletionVariant( - new AndFilter(new XmlTokenTypeFilter(XmlTokenType.XML_DATA_CHARACTERS), new NotFilter(entityCompletionFilter), new ElementFilter() { - @Override - public boolean isAcceptable(Object element, PsiElement context) { - XmlTag tag = PsiTreeUtil.getParentOfType(context, XmlTag.class, false); - if (tag != null) { - return XmlUtil.getSchemaSimpleContent(tag) != null; - } - return false; - } - - @Override - public boolean isClassAcceptable(Class hintClass) { - return true; - } - })); - variant.includeScopeClass(XmlToken.class, true); - variant.addCompletion(new SimpleTagContentEnumerationValuesGetter(), TailType.NONE); - - registerVariant(variant); - } { final CompletionVariant variant = new CompletionVariant( @@ -114,14 +58,6 @@ public class XmlCompletionData extends CompletionData { variant.includeScopeClass(XmlToken.class, true); registerVariant(variant); } - - { - final CompletionVariant variant = new CompletionVariant(entityCompletionFilter); - variant.includeScopeClass(XmlToken.class, true); - variant.addCompletion(new EntityRefGetter()); - variant.setInsertHandler(new EntityRefInsertHandler()); - registerVariant(variant); - } } protected ElementFilter createXmlEntityCompletionFilter() { @@ -130,142 +66,14 @@ public class XmlCompletionData extends CompletionData { XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN))); } - protected XmlAttributeValueGetter getAttributeValueGetter() { - return new XmlAttributeValueGetter(); - } - protected ElementFilter createAttributeCompletionFilter() { return TrueFilter.INSTANCE; } - protected ElementFilter createAttributeValueCompletionFilter() { - return TrueFilter.INSTANCE; - } - protected ElementFilter createTagCompletionFilter() { return TrueFilter.INSTANCE; } - private static class SimpleTagContentEnumerationValuesGetter implements ContextGetter { - @Override - public Object[] get(final PsiElement context, CompletionContext completionContext) { - XmlTag tag = PsiTreeUtil.getParentOfType(context, XmlTag.class, false); - if (tag != null) { - final XmlTag simpleContent = XmlUtil.getSchemaSimpleContent(tag); - if (simpleContent != null) { - final HashSet variants = new HashSet<>(); - XmlUtil.collectEnumerationValues(simpleContent, variants); - return ArrayUtil.toObjectArray(variants); - } - } - - return ArrayUtil.EMPTY_OBJECT_ARRAY; - } - } - - protected static class EntityRefGetter implements ContextGetter { - - @Nullable - private static Object getLookupItem(@Nullable final XmlEntityDecl decl) { - if (decl == null) { - return null; - } - - final String name = decl.getName(); - if (name == null) { - return null; - } - - final XmlAttributeValue value = decl.getValueElement(); - final ASTNode node = value.getNode(); - if (node != null) { - final ASTNode[] nodes = node.getChildren(TokenSet.create(XmlTokenType.XML_CHAR_ENTITY_REF)); - if (nodes.length == 1) { - final String valueText = nodes[0].getText(); - final int i = valueText.indexOf('#'); - if (i > 0) { - String s = valueText.substring(i + 1); - s = StringUtil.trimEnd(s, ";"); - - try { - final int unicodeChar = Integer.valueOf(s).intValue(); - return LookupValueFactory.createLookupValueWithHint(name, null, String.valueOf((char)unicodeChar)); - } - catch (NumberFormatException e) { - return null; - } - } - } - } - - return null; - } - - @Override - public Object[] get(final PsiElement context, CompletionContext completionContext) { - XmlFile containingFile = null; - XmlFile descriptorFile = null; - final XmlTag tag = PsiTreeUtil.getParentOfType(context, XmlTag.class); - - if (tag != null) { - containingFile = (XmlFile)tag.getContainingFile(); - descriptorFile = findDescriptorFile(tag, containingFile); - } - - if (HtmlUtil.isHtml5Context(tag)) { - descriptorFile = XmlUtil.findXmlFile(containingFile, Html5SchemaProvider.getCharsDtdLocation()); - } else if (tag == null) { - final XmlDocument document = PsiTreeUtil.getParentOfType(context, XmlDocument.class); - - if (document != null) { - containingFile = (XmlFile)document.getContainingFile(); - - final FileType ft = containingFile.getFileType(); - if (HtmlUtil.isHtml5Document(document)) { - descriptorFile = XmlUtil.findXmlFile(containingFile, Html5SchemaProvider.getCharsDtdLocation()); - } else if(ft != StdFileTypes.XML) { - final String namespace = ft == StdFileTypes.XHTML || ft == StdFileTypes.JSPX ? XmlUtil.XHTML_URI : XmlUtil.HTML_URI; - final XmlNSDescriptor nsDescriptor = document.getDefaultNSDescriptor(namespace, true); - - if (nsDescriptor != null) { - descriptorFile = nsDescriptor.getDescriptorFile(); - } - } - } - } - - if (descriptorFile != null) { - final List results = new ArrayList<>(); - final boolean acceptSystemEntities = containingFile.getFileType() == StdFileTypes.XML; - - final PsiElementProcessor processor = new PsiElementProcessor() { - @Override - public boolean execute(@NotNull final PsiElement element) { - if (element instanceof XmlEntityDecl) { - final XmlEntityDecl xmlEntityDecl = (XmlEntityDecl)element; - if (xmlEntityDecl.isInternalReference() || acceptSystemEntities) { - final String name = xmlEntityDecl.getName(); - final Object _item = getLookupItem(xmlEntityDecl); - results.add(_item == null ? name : _item); - } - } - return true; - } - }; - - XmlUtil.processXmlElements(descriptorFile, processor, true); - if (descriptorFile != containingFile && containingFile.getFileType() == StdFileTypes.XML) { - final XmlProlog element = containingFile.getDocument().getProlog(); - if (element != null) XmlUtil.processXmlElements(element, processor, true); - } - - return ArrayUtil.toObjectArray(results); - } - - return ArrayUtil.EMPTY_OBJECT_ARRAY; - } - } - public static XmlFile findDescriptorFile(@NotNull XmlTag tag, @NotNull XmlFile containingFile) { final XmlElementDescriptor descriptor = tag.getDescriptor(); final XmlNSDescriptor nsDescriptor = descriptor != null ? descriptor.getNSDescriptor() : null; @@ -277,15 +85,4 @@ public class XmlCompletionData extends CompletionData { } return descriptorFile; } - - protected static class EntityRefInsertHandler extends BasicInsertHandler { - @Override - public void handleInsert(InsertionContext context, LookupElement item) { - super.handleInsert(context, item); - context.setAddCompletionChar(false); - final CaretModel caretModel = context.getEditor().getCaretModel(); - context.getEditor().getDocument().insertString(caretModel.getOffset(), ";"); - caretModel.moveToOffset(caretModel.getOffset() + 1); - } - } } diff --git a/xml/impl/src/com/intellij/psi/filters/getters/XmlAttributeValueGetter.java b/xml/impl/src/com/intellij/psi/filters/getters/XmlAttributeValueGetter.java index 3d667a7a4632..57524c7a9ef2 100644 --- a/xml/impl/src/com/intellij/psi/filters/getters/XmlAttributeValueGetter.java +++ b/xml/impl/src/com/intellij/psi/filters/getters/XmlAttributeValueGetter.java @@ -15,19 +15,13 @@ */ package com.intellij.psi.filters.getters; -import com.intellij.codeInsight.completion.CompletionContext; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.PsiElement; -import com.intellij.psi.filters.ContextGetter; -import com.intellij.psi.impl.source.xml.XmlTokenImpl; -import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.XmlAttribute; -import com.intellij.psi.xml.XmlTokenType; import com.intellij.util.ArrayUtil; import com.intellij.xml.XmlAttributeDescriptor; import com.intellij.xml.impl.BasicXmlAttributeDescriptor; import com.intellij.xml.impl.XmlEnumerationDescriptor; -import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.NotNull; /** * Created by IntelliJ IDEA. @@ -36,15 +30,8 @@ import org.jetbrains.annotations.Nullable; * Time: 14:17:59 * To change this template use Options | File Templates. */ -public class XmlAttributeValueGetter implements ContextGetter { - public XmlAttributeValueGetter() {} - - @Override - public Object[] get(PsiElement context, CompletionContext completionContext) { - return getApplicableAttributeVariants(context); - } - - @Nullable +public class XmlAttributeValueGetter { + @NotNull public static String[] getEnumeratedValues(XmlAttribute attribute) { final XmlAttributeDescriptor descriptor = attribute.getDescriptor(); if (descriptor == null) { @@ -61,43 +48,6 @@ public class XmlAttributeValueGetter implements ContextGetter { else { result = descriptor.getEnumeratedValues(); } - return result != null ? StringUtil.filterEmptyStrings(result) : null; + return result != null ? StringUtil.filterEmptyStrings(result) : ArrayUtil.EMPTY_STRING_ARRAY; } - - private Object[] getApplicableAttributeVariants(PsiElement _context) { - if (_context instanceof XmlTokenImpl && ((XmlTokenImpl)_context).getTokenType() == XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN) { - XmlAttribute attr = PsiTreeUtil.getParentOfType(_context, XmlAttribute.class); - if (attr != null) { - final XmlAttributeDescriptor descriptor = attr.getDescriptor(); - - if (descriptor != null) { - if (descriptor.isFixed()) { - final String defaultValue = descriptor.getDefaultValue(); - return defaultValue == null ? ArrayUtil.EMPTY_OBJECT_ARRAY : new Object[]{defaultValue}; - } - - String[] values = getEnumeratedValues(attr); - - final String[] strings = addSpecificCompletions(attr); - - if (values == null || values.length == 0) { - values = strings; - } - else if (strings != null) { - values = ArrayUtil.mergeArrays(values, strings); - } - - return values == null ? ArrayUtil.EMPTY_OBJECT_ARRAY : values; - } - } - } - - return ArrayUtil.EMPTY_OBJECT_ARRAY; - } - - @Nullable - protected String[] addSpecificCompletions(final XmlAttribute context) { - return null; - } - } diff --git a/xml/impl/src/com/intellij/psi/impl/source/xml/DefaultXmlTagNameProvider.java b/xml/impl/src/com/intellij/psi/impl/source/xml/DefaultXmlTagNameProvider.java index ba87ef0af744..ca2e69387bdc 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/xml/DefaultXmlTagNameProvider.java +++ b/xml/impl/src/com/intellij/psi/impl/source/xml/DefaultXmlTagNameProvider.java @@ -31,6 +31,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.psi.impl.source.html.dtd.HtmlElementDescriptorImpl; import com.intellij.psi.meta.PsiPresentableMetaData; import com.intellij.psi.search.EverythingGlobalScope; import com.intellij.psi.search.GlobalSearchScope; @@ -104,7 +105,7 @@ public class DefaultXmlTagNameProvider implements XmlTagNameProvider { if (xmlExtension.useXmlTagInsertHandler()) { lookupElement = lookupElement.withInsertHandler(XmlTagInsertHandler.INSTANCE); } - + lookupElement = lookupElement.withCaseSensitivity(!(descriptor instanceof HtmlElementDescriptorImpl)); elements.add(PrioritizedLookupElement.withPriority(lookupElement, separator > 0 ? 0 : 1)); } } diff --git a/xml/impl/src/com/intellij/xml/util/XmlEnumeratedValueReferenceProvider.java b/xml/impl/src/com/intellij/xml/util/XmlEnumeratedValueReferenceProvider.java index 3bada7489489..e84a00847e3b 100644 --- a/xml/impl/src/com/intellij/xml/util/XmlEnumeratedValueReferenceProvider.java +++ b/xml/impl/src/com/intellij/xml/util/XmlEnumeratedValueReferenceProvider.java @@ -25,7 +25,6 @@ import com.intellij.psi.xml.XmlElement; import com.intellij.psi.xml.XmlTag; import com.intellij.psi.xml.XmlText; import com.intellij.util.ArrayUtil; -import com.intellij.util.Function; import com.intellij.util.ProcessingContext; import com.intellij.util.containers.ContainerUtil; import com.intellij.xml.impl.XmlEnumerationDescriptor; diff --git a/xml/xml-psi-impl/src/com/intellij/psi/impl/source/html/HtmlTagImpl.java b/xml/xml-psi-impl/src/com/intellij/psi/impl/source/html/HtmlTagImpl.java index deb42e88a82f..762b3e2c330e 100644 --- a/xml/xml-psi-impl/src/com/intellij/psi/impl/source/html/HtmlTagImpl.java +++ b/xml/xml-psi-impl/src/com/intellij/psi/impl/source/html/HtmlTagImpl.java @@ -69,7 +69,7 @@ public class HtmlTagImpl extends XmlTagImpl implements HtmlTag { } @Override - protected boolean isCaseSensitive() { + public boolean isCaseSensitive() { return false; } diff --git a/xml/xml-psi-impl/src/com/intellij/psi/impl/source/xml/XmlTagImpl.java b/xml/xml-psi-impl/src/com/intellij/psi/impl/source/xml/XmlTagImpl.java index a2fb579834b2..70a6c5a89e15 100644 --- a/xml/xml-psi-impl/src/com/intellij/psi/impl/source/xml/XmlTagImpl.java +++ b/xml/xml-psi-impl/src/com/intellij/psi/impl/source/xml/XmlTagImpl.java @@ -749,7 +749,7 @@ public class XmlTagImpl extends XmlElementImpl implements XmlTag, HintedReferenc return null; } - protected boolean isCaseSensitive() { + public boolean isCaseSensitive() { return true; } From 62f53794b1c59869c833400c49e7e750da37499d Mon Sep 17 00:00:00 2001 From: "Liana.Bakradze" Date: Fri, 16 Dec 2016 15:40:33 +0300 Subject: [PATCH 266/558] EDU-806 Upload task descriptions and tests for all subtasks --- .../jetbrains/edu/learning/StudyUtils.java | 6 +- .../edu/learning/courseFormat/Task.java | 9 +++ .../StudyProjectGenerator.java | 45 +++++++---- .../stepic/EduAdaptiveStepicConnector.java | 2 +- .../learning/stepic/EduStepicConnector.java | 10 ++- .../edu/learning/stepic/StepicWrappers.java | 79 +++++++++++++++---- 6 files changed, 114 insertions(+), 37 deletions(-) diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java index 10a14e60b356..99d184d14744 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java @@ -310,7 +310,11 @@ public class StudyUtils { if (manager == null) { return false; } - return manager.getTestFileName().equals(name); + String testFileName = manager.getTestFileName(); + if (name.equals(testFileName)) { + return true; + } + return name.startsWith(FileUtil.getNameWithoutExtension(testFileName)) && name.contains(EduNames.SUBTASK_MARKER); } @Nullable diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Task.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Task.java index b54508d83820..9d34cdbcc034 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Task.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Task.java @@ -39,6 +39,7 @@ public class Task implements StudyItem { private String text; private Map testsText = new HashMap<>(); + private Map taskTexts = new HashMap<>(); @Transient private Lesson myLesson; @Expose @SerializedName("update_date") private Date myUpdateDate; @@ -95,10 +96,18 @@ public class Task implements StudyItem { return testsText; } + public Map getTaskTexts() { + return taskTexts; + } + public void addTestsTexts(String name, String text) { testsText.put(name, text); } + public void addTaskText(String name, String text) { + taskTexts.put(name, text); + } + public Map getTaskFiles() { return taskFiles; } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java index b21504b714d7..58c56e80fdf2 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java @@ -284,30 +284,43 @@ public class StudyProjectGenerator { } } catch (IOException e) { - LOG.error("ERROR copying file " + name); + LOG.error(e); } } - final Map testsText = task.getTestsText(); - for (Map.Entry entry : testsText.entrySet()) { - final File testsFile = new File(taskDirectory, entry.getKey()); - if (testsFile.exists()) { - FileUtil.delete(testsFile); - } - FileUtil.createIfDoesntExist(testsFile); - try { - FileUtil.writeToFile(testsFile, entry.getValue()); - } - catch (IOException e) { - LOG.error("ERROR copying tests file"); - } + createFiles(taskDirectory, task.getTestsText()); + if (task.getTaskTexts().isEmpty()) { + createTaskHtml(task, taskDirectory); + return; } - final File taskText = new File(taskDirectory, "task.html"); + task.setText(null); + createFiles(taskDirectory, task.getTaskTexts()); + + } + + private static void createTaskHtml(@NotNull Task task, @NotNull File taskDirectory) { + final File taskText = new File(taskDirectory, EduNames.TASK_HTML); FileUtil.createIfDoesntExist(taskText); try { FileUtil.writeToFile(taskText, task.getText()); } catch (IOException e) { - LOG.error("ERROR copying tests file"); + LOG.error(e); + } + } + + private static void createFiles(@NotNull File taskDirectory, Map files) { + for (Map.Entry entry : files.entrySet()) { + final File file = new File(taskDirectory, entry.getKey()); + if (file.exists()) { + FileUtil.delete(file); + } + FileUtil.createIfDoesntExist(file); + try { + FileUtil.writeToFile(file, entry.getValue()); + } + catch (IOException e) { + LOG.error(e); + } } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java index e8e200f4c199..ef9e9e156ab1 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java @@ -432,7 +432,7 @@ public class EduAdaptiveStepicConnector { } if (step.options.test != null) { - for (StepicWrappers.TestFileWrapper wrapper : step.options.test) { + for (StepicWrappers.FileWrapper wrapper : step.options.test) { task.addTestsTexts(wrapper.name, wrapper.text); } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java index 523b6359d32e..963864e11e72 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java @@ -280,11 +280,17 @@ public class EduStepicConnector { task.setStepId(stepicId); task.setUpdateDate(step.update_date); task.setName(block.options != null ? block.options.title : (PYCHARM_PREFIX + CURRENT_VERSION)); - task.setText(block.text); task.setLastSubtaskIndex(block.options.lastSubtaskIndex); - for (StepicWrappers.TestFileWrapper wrapper : block.options.test) { + for (StepicWrappers.FileWrapper wrapper : block.options.test) { task.addTestsTexts(wrapper.name, wrapper.text); } + if (block.options.text != null) { + for (StepicWrappers.FileWrapper wrapper : block.options.text) { + task.addTaskText(wrapper.name, wrapper.text); + } + } else { + task.setText(block.text); + } task.taskFiles = new HashMap<>(); // TODO: it looks like we don't need taskFiles as map anymore if (block.options.files != null) { diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java index c888c5707af0..159e87c1f033 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java @@ -7,7 +7,9 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.Course; @@ -21,6 +23,7 @@ import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.io.InputStream; import java.util.*; +import java.util.stream.Collectors; public class StepicWrappers { private static final Logger LOG = Logger.getInstance(StepOptions.class); @@ -44,10 +47,10 @@ public class StepicWrappers { } public static class StepOptions { - @Expose List test; + @Expose List test; @Expose String title; @Expose List files; - @Expose String text; + @Expose List text; @Expose List> samples; @Expose Integer executionMemoryLimit; @Expose Integer executionTimeLimit; @@ -61,6 +64,7 @@ public class StepicWrappers { final StepOptions source = new StepOptions(); source.lastSubtaskIndex = task.getLastSubtaskIndex(); setTests(task, source, project); + setTaskTexts(task, source, project); source.files = new ArrayList<>(); source.title = task.getName(); for (final Map.Entry entry : task.getTaskFiles().entrySet()) { @@ -104,22 +108,63 @@ public class StepicWrappers { return source; } - private static void setTests(@NotNull final Task task, @NotNull final StepOptions source, @NotNull final Project project) { + private static void setTaskTexts(@NotNull Task task, @NotNull StepOptions stepOptions, @NotNull Project project) { + stepOptions.text = new ArrayList<>(); + VirtualFile taskDir = task.getTaskDir(project); + if (taskDir == null) { + return; + } + List taskDescriptionFiles = Arrays.stream(taskDir.getChildren()) + .filter(virtualFile -> StudyUtils.isTaskDescriptionFile(virtualFile.getName())) + .collect(Collectors.toList()); + for (VirtualFile taskDescriptionFile : taskDescriptionFiles) { + addFileWrapper(taskDescriptionFile, stepOptions.text); + } + } + + private static void setTests(@NotNull Task task, @NotNull StepOptions source, @NotNull Project project) { final Map testsText = task.getTestsText(); + source.test = new ArrayList<>(); if (testsText.isEmpty()) { - ApplicationManager.getApplication().runReadAction(() -> { - source.test = Collections.singletonList(new TestFileWrapper(EduNames.TESTS_FILE, task.getTestsText(project))); - }); + List testFiles = getTestFiles(task, project); + for (VirtualFile testFile : testFiles) { + addFileWrapper(testFile, source.test); + } } else { - source.test = new ArrayList<>(); for (Map.Entry entry : testsText.entrySet()) { - source.test.add(new TestFileWrapper(entry.getKey(), entry.getValue())); + source.test.add(new FileWrapper(entry.getKey(), entry.getValue())); } } } } + private static void addFileWrapper(@NotNull VirtualFile file, List wrappers) { + try { + wrappers.add(new FileWrapper(file.getName(), VfsUtilCore.loadText(file))); + } + catch (IOException e) { + LOG.error(e); + } + } + + private static List getTestFiles(@NotNull Task task, @NotNull Project project) { + List testFiles = new ArrayList<>(); + VirtualFile taskDir = task.getTaskDir(project); + if (taskDir == null) { + return testFiles; + } + if (!task.hasSubtasks()) { + VirtualFile testFile = taskDir.findChild(EduNames.TESTS_FILE); + testFiles.add(testFile); + return testFiles; + } + testFiles.addAll(Arrays.stream(taskDir.getChildren()) + .filter(file -> StudyUtils.isTestsFile(project, file.getName())) + .collect(Collectors.toList())); + return testFiles; + } + static class CodeTemplatesWrapper { String python3; String python27; @@ -191,11 +236,11 @@ public class StepicWrappers { } } - static class TestFileWrapper { + static class FileWrapper { @Expose public final String name; @Expose public final String text; - public TestFileWrapper(String name, String text) { + public FileWrapper(String name, String text) { this.name = name; this.text = text; } @@ -269,12 +314,12 @@ public class StepicWrappers { public Attempt(int step) { this.step = step; } - + public boolean isActive() { return status.equals("active"); } } - + static class Dataset { boolean is_multiple_choice; List options; @@ -382,7 +427,7 @@ public class StepicWrappers { public SubmissionToPostWrapper(@NotNull String attemptId, @NotNull String language, @NotNull String code) { submission = new Submission(attemptId, new Submission.CodeReply(language, code)); } - + public SubmissionToPostWrapper(@NotNull String attemptId, boolean[] choices) { submission = new Submission(attemptId, new Submission.ChoiceReply(choices)); } @@ -396,11 +441,11 @@ public class StepicWrappers { this.reply = reply; } - + interface Reply { - + } - + static class CodeReply implements Reply { String language; String code; @@ -410,7 +455,7 @@ public class StepicWrappers { this.code = code; } } - + static class ChoiceReply implements Reply { boolean[] choices; From 063ec0416ba6157fa7cec90bed617c8f51d870cf Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Fri, 16 Dec 2016 15:44:23 +0300 Subject: [PATCH 267/558] javac indices: enable in all tests --- .../src/com/intellij/compiler/CompilerReferenceService.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/java/java-indexing-impl/src/com/intellij/compiler/CompilerReferenceService.java b/java/java-indexing-impl/src/com/intellij/compiler/CompilerReferenceService.java index ab49c954a003..0459aae0d45e 100644 --- a/java/java-indexing-impl/src/com/intellij/compiler/CompilerReferenceService.java +++ b/java/java-indexing-impl/src/com/intellij/compiler/CompilerReferenceService.java @@ -15,7 +15,6 @@ */ package com.intellij.compiler; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.AbstractProjectComponent; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.Project; @@ -29,7 +28,6 @@ import org.jetbrains.annotations.Nullable; public abstract class CompilerReferenceService extends AbstractProjectComponent { public static final RegistryValue IS_ENABLED_KEY = Registry.get("compiler.ref.index"); - public static volatile boolean enabledInTests = false; protected CompilerReferenceService(Project project) { super(project); @@ -55,6 +53,6 @@ public abstract class CompilerReferenceService extends AbstractProjectComponent @NotNull FileType searchFileType); public static boolean isEnabled() { - return (!ApplicationManager.getApplication().isHeadlessEnvironment() || enabledInTests) && IS_ENABLED_KEY.asBoolean(); + return IS_ENABLED_KEY.asBoolean(); } } From 254a68b3e586dc3672b0d9d3895d69723ef8479d Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Fri, 16 Dec 2016 15:56:02 +0300 Subject: [PATCH 268/558] fix missed shadow for lightweight dialog --- .../ui/impl/GlassPaneDialogWrapperPeer.java | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java b/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java index 2307ae19586c..fc22282153dc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java @@ -44,7 +44,6 @@ import com.intellij.ui.FocusTrackback; import com.intellij.ui.ScreenUtil; import com.intellij.ui.components.JBLayeredPane; import com.intellij.util.ui.JBInsets; -import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -383,9 +382,10 @@ public class GlassPaneDialogWrapperPeer extends DialogWrapperPeer implements Foc private JComponent myContentPane; private MyRootPane myRootPane; private BufferedImage shadow; + private int shadowWidth; + private int shadowHeight; private final JLayeredPane myTransparentPane; private JButton myDefaultButton; - private Dimension myShadowSize = null; private final Container myWrapperPane; private Component myPreviouslyFocusedComponent; private Dimension myCachedSize = null; @@ -468,10 +468,6 @@ public class GlassPaneDialogWrapperPeer extends DialogWrapperPeer implements Foc return result; } - public Container getTransparentPane() { - return myTransparentPane; - } - private TransparentLayeredPane getExistingTransparentPane() { for (int i = 0; i < myPane.getComponentCount(); i++) { Component c = myPane.getComponent(i); @@ -570,15 +566,13 @@ public class GlassPaneDialogWrapperPeer extends DialogWrapperPeer implements Foc } super.setBounds(x, y, width, height); - if (myShadowSize == null || !myShadowSize.equals(getSize())) { - createShadow(); - myShadowSize = getSize(); + if (RemoteDesktopDetector.isRemoteSession()) { + shadow = null; } - } - - private void createShadow() { - if (!RemoteDesktopDetector.isRemoteSession() && !JBUI.isHiDPI()) { - shadow = ShadowBorderPainter.createShadow(this, getWidth(), getHeight()); + else if (shadow == null || shadowWidth != width || shadowHeight != height) { + shadow = ShadowBorderPainter.createShadow(this, width, height); + shadowWidth = width; + shadowHeight = height; } } From a9a367dd02d211b8a5f01f76605e5ddb222e2973 Mon Sep 17 00:00:00 2001 From: "Liana.Bakradze" Date: Fri, 16 Dec 2016 15:59:38 +0300 Subject: [PATCH 269/558] remove pycharm additional lessons --- .../edu/learning/courseGeneration/StudyProjectGenerator.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java index 58c56e80fdf2..2b6f2ab6530c 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java @@ -222,9 +222,11 @@ public class StudyProjectGenerator { flushCourseJson(course, courseDirectory); int lessonIndex = 1; + List additionalLessons = new ArrayList<>(); for (Lesson lesson : course.getLessons()) { if (lesson.getName().equals(EduNames.PYCHARM_ADDITIONAL)) { flushAdditionalFiles(courseDirectory, lesson); + additionalLessons.add(lesson); } else { final File lessonDirectory = new File(courseDirectory, EduNames.LESSON + String.valueOf(lessonIndex)); @@ -232,6 +234,9 @@ public class StudyProjectGenerator { lessonIndex += 1; } } + for (Lesson lesson : additionalLessons) { + course.getLessons().remove(lesson); + } } private static void flushAdditionalFiles(File courseDirectory, Lesson lesson) { From 034b335b0c9b4d02ea2efcf09f99611a1226ec5e Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 16 Dec 2016 16:07:57 +0300 Subject: [PATCH 270/558] Reduce delay between progress updating in filtering --- .../src/com/intellij/debugger/memory/ui/InstancesWindow.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWindow.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWindow.java index 5e695f59ab13..d9b0c548c413 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWindow.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWindow.java @@ -154,7 +154,7 @@ public class InstancesWindow extends DialogWrapper { private static final int FILTERING_CHUNK_SIZE = 50; private static final int MAX_DURATION_TO_UPDATE_TREE_SECONDS = 3; - private static final int FILTERING_PROGRESS_UPDATING_MIN_DELAY_MILLIS = 50; + private static final int FILTERING_PROGRESS_UPDATING_MIN_DELAY_MILLIS = 17; // ~ 60 fps private final InstancesTree myInstancesTree; private final XDebuggerExpressionEditor myFilterConditionEditor; From 7b34e2cd184aa823b88051511c4acf1260f3a7f5 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Fri, 16 Dec 2016 16:18:38 +0300 Subject: [PATCH 271/558] javac ast indices: get rid of private java compiler API except Name#toUtf() --- .../jps/javac/ast/Javac8RefScanner.java | 7 +- .../BackwardReferenceIndexWriter.java | 7 +- .../ast/JavacReferenceCollectorListener.java | 209 +++++++++++------- .../jps/javac/ast/JavacTreeRefScanner.java | 76 +++---- .../jps/javac/ast/JavacTreeScannerSink.java | 12 +- .../jps/javac/ast/api/JavacFileData.java | 36 ++- .../jetbrains/jps/javac/ast/api/JavacRef.java | 110 +++++---- .../packageInfo2/initialIndex.txt | 7 + .../packageInfo2/myPackage/package-info.java | 2 + .../references/ReferenceIndexTest.kt | 4 + 10 files changed, 277 insertions(+), 193 deletions(-) create mode 100644 jps/jps-builders/testData/referencesIndex/packageInfo2/initialIndex.txt create mode 100644 jps/jps-builders/testData/referencesIndex/packageInfo2/myPackage/package-info.java diff --git a/jps/javac-ref-scanner-8/src/org/jetbrains/jps/javac/ast/Javac8RefScanner.java b/jps/javac-ref-scanner-8/src/org/jetbrains/jps/javac/ast/Javac8RefScanner.java index d774f7f1daaa..69c61be01785 100644 --- a/jps/javac-ref-scanner-8/src/org/jetbrains/jps/javac/ast/Javac8RefScanner.java +++ b/jps/javac-ref-scanner-8/src/org/jetbrains/jps/javac/ast/Javac8RefScanner.java @@ -7,7 +7,6 @@ import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.tree.JCTree; import org.jetbrains.jps.javac.ast.api.JavacDef; -import org.jetbrains.jps.javac.ast.api.JavacRef; /** * Used via reflection in {@link JavacTreeRefScanner#createASTScanner()} @@ -20,7 +19,7 @@ public class Javac8RefScanner extends JavacTreeRefScanner { final Type type = lambda.type; final Symbol.TypeSymbol symbol = type.asElement(); if (symbol != null) { - sink.sinkDeclaration(new JavacDef.JavacFunExprDef(JavacRef.JavacSymbolRefBase.fromSymbol(symbol))); + sink.sinkDeclaration(new JavacDef.JavacFunExprDef(sink.asJavacRef(symbol))); } return super.visitLambdaExpression(node, sink); } @@ -30,8 +29,8 @@ public class Javac8RefScanner extends JavacTreeRefScanner { JCTree.JCMemberReference memberRef = (JCTree.JCMemberReference)node; final Symbol sym = memberRef.sym; if (sym != null) { - sink.sinkReference(JavacRef.JavacSymbolRefBase.fromSymbol(sym)); - sink.sinkDeclaration(new JavacDef.JavacFunExprDef(JavacRef.JavacSymbolRefBase.fromSymbol(memberRef.type.asElement()))); + sink.sinkReference(sink.asJavacRef(sym)); + sink.sinkDeclaration(new JavacDef.JavacFunExprDef(sink.asJavacRef(memberRef.type.asElement()))); } return super.visitMemberReference(node, sink); } diff --git a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexWriter.java b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexWriter.java index 395a67e62d2d..1b0495bc9078 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexWriter.java +++ b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexWriter.java @@ -17,7 +17,6 @@ package org.jetbrains.jps.backwardRefs; import com.intellij.util.SystemProperties; import com.intellij.util.indexing.InvertedIndex; -import com.sun.tools.javac.code.Flags; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.backwardRefs.index.CompiledFileData; @@ -31,12 +30,11 @@ import org.jetbrains.jps.incremental.storage.BuildDataManager; import org.jetbrains.jps.javac.ast.api.JavacRef; import org.jetbrains.jps.model.java.compiler.JavaCompilers; +import javax.lang.model.element.Modifier; import java.io.File; import java.io.IOException; import java.util.Collection; -import static com.sun.tools.javac.code.Flags.PRIVATE; - public class BackwardReferenceIndexWriter { public static final String PROP_KEY = "jps.backward.ref.index.builder"; @@ -152,9 +150,8 @@ public class BackwardReferenceIndexWriter { return null; } - //see Symbol.isPrivate() method private static boolean isPrivate(JavacRef ref) { - return (ref.getFlags() & Flags.AccessFlags) == PRIVATE; + return ref.getModifiers().contains(Modifier.PRIVATE); } private static int id(JavacRef ref, ByteArrayEnumerator byteArrayEnumerator) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java index 0231eb25ab84..8fa0b011935c 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java @@ -15,24 +15,26 @@ */ package org.jetbrains.jps.javac.ast; +import com.intellij.openapi.util.NotNullLazyValue; import com.intellij.util.Consumer; import com.intellij.util.ReflectionUtil; -import com.sun.source.util.JavacTask; -import com.sun.source.util.TaskEvent; -import com.sun.source.util.TaskListener; -import com.sun.tools.javac.code.Symbol; -import com.sun.tools.javac.tree.JCTree; +import com.sun.source.tree.*; +import com.sun.source.util.*; import com.sun.tools.javac.util.ClientCodeException; -import com.sun.tools.javac.util.Name; import gnu.trove.THashMap; import gnu.trove.THashSet; import gnu.trove.TObjectHashingStrategy; +import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.javac.ast.api.JavacDef; import org.jetbrains.jps.javac.ast.api.JavacFileData; import org.jetbrains.jps.javac.ast.api.JavacRef; +import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; +import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; import javax.tools.*; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -42,8 +44,17 @@ final class JavacReferenceCollectorListener implements TaskListener { private final boolean myDivideImportRefs; private final Consumer myDataConsumer; private final JavacTreeRefScanner myAstScanner; + private final Elements myElementUtility; + private final Types myTypeUtility; + private final Trees myTreeUtility; - private Name myAsterisk; + private NotNullLazyValue myAsterisk = new NotNullLazyValue() { + @NotNull + @Override + protected Name compute() { + return myElementUtility.getName("*"); + } + }; private final Map myIncompletelyProcessedFiles = new THashMap(10); @@ -52,7 +63,11 @@ final class JavacReferenceCollectorListener implements TaskListener { Consumer dataConsumer) { JavacTask javacTask = (JavacTask)task; Method addTaskMethod = ReflectionUtil.getMethod(JavacTask.class, "addTaskListener", TaskListener.class); // jdk >= 8 - final JavacReferenceCollectorListener taskListener = new JavacReferenceCollectorListener(divideImportRefs, dataConsumer); + final JavacReferenceCollectorListener taskListener = new JavacReferenceCollectorListener(divideImportRefs, + dataConsumer, + javacTask.getElements(), + javacTask.getTypes(), + Trees.instance(javacTask)); if (addTaskMethod != null) { try { addTaskMethod.invoke(task, taskListener); @@ -69,9 +84,16 @@ final class JavacReferenceCollectorListener implements TaskListener { } } - private JavacReferenceCollectorListener(boolean divideImportRefs, Consumer dataConsumer) { + private JavacReferenceCollectorListener(boolean divideImportRefs, + Consumer dataConsumer, + Elements elementUtility, + Types typeUtility, + Trees treeUtility) { myDivideImportRefs = divideImportRefs; myDataConsumer = dataConsumer; + myElementUtility = elementUtility; + myTypeUtility = typeUtility; + myTreeUtility = treeUtility; myAstScanner = JavacTreeRefScanner.createASTScanner(); } @@ -85,59 +107,49 @@ final class JavacReferenceCollectorListener implements TaskListener { try { if (e.getKind() == TaskEvent.Kind.ANALYZE) { // javac creates an event on each processed top level declared class not file - final JCTree.JCCompilationUnit unit = (JCTree.JCCompilationUnit)e.getCompilationUnit(); + final CompilationUnitTree unit = e.getCompilationUnit(); final String fileName = e.getSourceFile().getName(); - final List declarations = unit.getTypeDecls(); - final int size = declarations.size(); - boolean isFileDataComplete = true; - boolean collectImportsData = true; - JCTree declarationToProcess = null; - final TypeElement analyzedElement = e.getTypeElement(); + Tree declarationToProcess = myTreeUtility.getTree(e.getTypeElement()); - IncompletelyProcessedFile incompletelyProcessedFile; - switch (size) { - case 0: - incompletelyProcessedFile = new IncompletelyProcessedFile(0, fileName); - break; - case 1: - incompletelyProcessedFile = new IncompletelyProcessedFile(0, fileName); - declarationToProcess = declarations.get(0); - break; - default: - incompletelyProcessedFile = myIncompletelyProcessedFiles.get(fileName); - if (incompletelyProcessedFile == null) { - myIncompletelyProcessedFiles.put(fileName, incompletelyProcessedFile = new IncompletelyProcessedFile(size, fileName)); - } else { - collectImportsData = false; - } + boolean collectImportsData; + boolean addedToCache = true; + IncompletelyProcessedFile incompletelyProcessedFile = myIncompletelyProcessedFiles.get(fileName); + if (incompletelyProcessedFile == null) { + final int declarationCount = unit.getTypeDecls().size(); + incompletelyProcessedFile = new IncompletelyProcessedFile(declarationCount, fileName, unit); + if (declarationCount == 1 && declarationToProcess != null) { + addedToCache = false; + } else { + myIncompletelyProcessedFiles.put(fileName, incompletelyProcessedFile); + } + collectImportsData = true; + } + else { + collectImportsData = false; + } - if (incompletelyProcessedFile.decrementRemainDeclarationsAndGet() == 0) { - myIncompletelyProcessedFiles.remove(fileName); - } else { - isFileDataComplete = false; - } - - for (JCTree declaration : declarations) { - if (declaration.type != null && declaration.type.tsym == analyzedElement) { - declarationToProcess = declaration; - break; - } - } - - if (declarationToProcess == null) throw new IllegalStateException("Can't find tree for " + analyzedElement.getQualifiedName()); + final boolean isFileDataComplete; + if (incompletelyProcessedFile.decrementRemainDeclarationsAndGet(declarationToProcess) == 0) { + if (addedToCache) { + myIncompletelyProcessedFiles.remove(fileName); + } + isFileDataComplete = true; + } + else { + isFileDataComplete = false; } if (collectImportsData) { - scanImports(unit, incompletelyProcessedFile.myFileData.getRefs()); + scanImports(unit, incompletelyProcessedFile.myFileData.getRefs(), incompletelyProcessedFile.getTreeHelper()); if (myDivideImportRefs) { - scanImports(unit, incompletelyProcessedFile.myFileData.getImportRefs()); + scanImports(unit, incompletelyProcessedFile.myFileData.getImportRefs(), incompletelyProcessedFile.getTreeHelper()); } } final IncompletelyProcessedFile finalIncompletelyProcessedFile = incompletelyProcessedFile; JavacTreeScannerSink sink = new JavacTreeScannerSink() { @Override - public void sinkReference(JavacRef.JavacSymbolRefBase ref) { + public void sinkReference(JavacRef.JavacElementRefBase ref) { finalIncompletelyProcessedFile.myFileData.getRefs().add(ref); } @@ -145,11 +157,27 @@ final class JavacReferenceCollectorListener implements TaskListener { public void sinkDeclaration(JavacDef def) { finalIncompletelyProcessedFile.myFileData.getDefs().add(def); } + + @Override + public JavacRef.JavacElementRefBase asJavacRef(Element element) { + return JavacRef.JavacElementRefBase.fromElement(element, myElementUtility); + } + + @Override + public Element getReferencedElement(Tree tree) { + return finalIncompletelyProcessedFile.getTreeHelper().getReferencedElement(tree); + } + + @Override + public Types getTypeUtility() { + return myTypeUtility; + } + }; myAstScanner.scan(declarationToProcess, sink); if (isFileDataComplete) { - for (JCTree.JCAnnotation annotation : unit.getPackageAnnotations()) { + for (AnnotationTree annotation : unit.getPackageAnnotations()) { myAstScanner.scan(annotation, sink); } @@ -162,62 +190,65 @@ final class JavacReferenceCollectorListener implements TaskListener { } } - private Name getAsteriskFromCurrentNameTable(Name tableRepresentative) { - if (myAsterisk == null) { - myAsterisk = tableRepresentative.table.fromChars(new char[]{'*'}, 0, 1); - } - return myAsterisk; - } - - private void scanImports(JCTree.JCCompilationUnit compilationUnit, Collection symbols) { - for (JCTree.JCImport anImport : compilationUnit.getImports()) { - final JCTree.JCFieldAccess id = (JCTree.JCFieldAccess)anImport.getQualifiedIdentifier(); - final Symbol sym = id.sym; - if (sym == null) { - final JCTree.JCExpression qExpr = id.getExpression(); - if (qExpr instanceof JCTree.JCFieldAccess) { - final JCTree.JCFieldAccess classImport = (JCTree.JCFieldAccess)qExpr; - final Symbol ownerSym = classImport.sym; + private void scanImports(CompilationUnitTree compilationUnit, + Collection elements, + JavacTreeHelper treeHelper) { + for (ImportTree anImport : compilationUnit.getImports()) { + final MemberSelectTree id = (MemberSelectTree)anImport.getQualifiedIdentifier(); + final Element element = treeHelper.getReferencedElement(id); + if (element == null) { + final ExpressionTree qExpr = id.getExpression(); + if (qExpr instanceof MemberSelectTree) { + final MemberSelectTree classImport = (MemberSelectTree)qExpr; + final Element ownerElement = treeHelper.getReferencedElement(classImport); final Name name = id.getIdentifier(); - if (name != getAsteriskFromCurrentNameTable(name)) { + if (name != myAsterisk.getValue()) { // member import - for (Symbol memberSymbol : ownerSym.members().getElements()) { - if (memberSymbol.getSimpleName() == name) { - symbols.add(JavacRef.JavacSymbolRefBase.fromSymbol(memberSymbol)); + for (Element memberElement : myElementUtility.getAllMembers((TypeElement)ownerElement)) { + if (memberElement.getSimpleName() == name) { + elements.add(JavacRef.JavacElementRefBase.fromElement(memberElement, myElementUtility)); } } } - collectClassImports(ownerSym, symbols); + collectClassImports(ownerElement, elements); } } else { // class import - collectClassImports(sym, symbols); + collectClassImports(element, elements); } } } - private static void collectClassImports(Symbol baseImport, Collection collector) { - for (Symbol symbol = baseImport; - symbol != null && symbol.getKind() != ElementKind.PACKAGE; - symbol = symbol.owner) { - collector.add(JavacRef.JavacSymbolRefBase.fromSymbol(symbol)); + private void collectClassImports(Element baseImport, Collection collector) { + for (Element element = baseImport; + element != null && element.getKind() != ElementKind.PACKAGE; + element = element.getEnclosingElement()) { + collector.add(JavacRef.JavacElementRefBase.fromElement(element, myElementUtility)); } } private class IncompletelyProcessedFile { private final JavacFileData myFileData; + private final JavacTreeHelper myTreeHelper; private int myRemainDeclarations; - private IncompletelyProcessedFile(int remainDeclarations, String filePath) { + private IncompletelyProcessedFile(int remainDeclarations, + String filePath, + CompilationUnitTree unitTree) { myRemainDeclarations = remainDeclarations; myFileData = new JavacFileData(filePath, createReferenceHolder(), myDivideImportRefs ? createReferenceHolder() : Collections.emptyList(), createDefinitionHolder()); + myTreeHelper = new JavacTreeHelper(unitTree, myTreeUtility); } - private int decrementRemainDeclarationsAndGet() { - return --myRemainDeclarations; + private int decrementRemainDeclarationsAndGet(Tree declarationToProcess) { + return declarationToProcess == null ? myRemainDeclarations : --myRemainDeclarations; + } + + private JavacTreeHelper getTreeHelper() { + return myTreeHelper; } } @@ -225,12 +256,12 @@ final class JavacReferenceCollectorListener implements TaskListener { return new THashSet(new TObjectHashingStrategy() { @Override public int computeHashCode(JavacRef ref) { - return ((JavacRef.JavacSymbolRefBase) ref).getOriginalElement().hashCode(); + return ((JavacRef.JavacElementRefBase) ref).getOriginalElement().hashCode(); } @Override public boolean equals(JavacRef r1, JavacRef r2) { - return ((JavacRef.JavacSymbolRefBase) r1).getOriginalElement() == ((JavacRef.JavacSymbolRefBase) r2).getOriginalElement(); + return ((JavacRef.JavacElementRefBase) r1).getOriginalElement() == ((JavacRef.JavacElementRefBase) r2).getOriginalElement(); } }); } @@ -238,4 +269,18 @@ final class JavacReferenceCollectorListener implements TaskListener { private static List createDefinitionHolder() { return new ArrayList(); } + + private static class JavacTreeHelper { + private final TreePath myUnitPath; + private final Trees myTreeUtil; + + private JavacTreeHelper(CompilationUnitTree unit, Trees treeUtil) { + myUnitPath = new TreePath(unit); + myTreeUtil = treeUtil; + } + + private Element getReferencedElement(Tree tree) { + return myTreeUtil.getElement(new TreePath(myUnitPath, tree)); + } + } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacTreeRefScanner.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacTreeRefScanner.java index 19e3ba0d28fa..c9e9eeff1af3 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacTreeRefScanner.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacTreeRefScanner.java @@ -17,17 +17,28 @@ package org.jetbrains.jps.javac.ast; import com.sun.source.tree.*; import com.sun.source.util.TreeScanner; -import com.sun.tools.javac.code.Symbol; -import com.sun.tools.javac.code.Type; -import com.sun.tools.javac.tree.JCTree; -import com.sun.tools.javac.util.List; import org.jetbrains.jps.javac.ast.api.JavacDef; import org.jetbrains.jps.javac.ast.api.JavacRef; +import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; +import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; class JavacTreeRefScanner extends TreeScanner { + private static final Set ALLOWED_ELEMENTS = EnumSet.of(ElementKind.ENUM, + ElementKind.CLASS, + ElementKind.ANNOTATION_TYPE, + ElementKind.INTERFACE, + ElementKind.ENUM_CONSTANT, + ElementKind.FIELD, + ElementKind.CONSTRUCTOR, + ElementKind.METHOD); + @Override public Tree visitCompilationUnit(CompilationUnitTree node, JavacTreeScannerSink sink) { scan(node.getPackageAnnotations(), sink); @@ -37,49 +48,39 @@ class JavacTreeRefScanner extends TreeScanner { @Override public Tree visitIdentifier(IdentifierTree node, JavacTreeScannerSink sink) { - final JCTree.JCIdent javacIdentifier = (JCTree.JCIdent)node; - final Type type = javacIdentifier.type; - if (type == null) { + final Element element = sink.getReferencedElement(node); + if (element == null) { return null; } - if (type.getKind() == TypeKind.PACKAGE) { - return null; + if (ALLOWED_ELEMENTS.contains(element.getKind())) { + sink.sinkReference(sink.asJavacRef(element)); } - final Symbol sym = javacIdentifier.sym; - if (sym == null || - sym.getKind() == ElementKind.PARAMETER || - sym.getKind() == ElementKind.LOCAL_VARIABLE || - sym.getKind() == ElementKind.EXCEPTION_PARAMETER || - sym.getKind() == ElementKind.TYPE_PARAMETER) { - return null; - } - sink.sinkReference(JavacRef.JavacSymbolRefBase.fromSymbol(sym)); return null; } @Override public Tree visitVariable(VariableTree node, JavacTreeScannerSink sink) { - final Symbol.VarSymbol sym = ((JCTree.JCVariableDecl)node).sym; - if (sym != null && sym.getKind() == ElementKind.FIELD) { - sink.sinkReference(JavacRef.JavacSymbolRefBase.fromSymbol(sym)); + final Element element = sink.getReferencedElement(node); + if (element != null && element.getKind() == ElementKind.FIELD) { + sink.sinkReference(sink.asJavacRef(element)); } return super.visitVariable(node, sink); } @Override public Tree visitMemberSelect(MemberSelectTree node, JavacTreeScannerSink sink) { - final Symbol sym = ((JCTree.JCFieldAccess)node).sym; - if (sym != null && sym.getKind() != ElementKind.PACKAGE) { - sink.sinkReference(JavacRef.JavacSymbolRefBase.fromSymbol(sym)); + final Element element = sink.getReferencedElement(node); + if (element != null && element.getKind() != ElementKind.PACKAGE) { + sink.sinkReference(sink.asJavacRef(element)); } return super.visitMemberSelect(node, sink); } @Override public Tree visitMethod(MethodTree node, JavacTreeScannerSink sink) { - final Symbol.MethodSymbol sym = ((JCTree.JCMethodDecl)node).sym; - if (sym != null) { - sink.sinkReference(JavacRef.JavacSymbolRefBase.fromSymbol(sym)); + final Element element = sink.getReferencedElement(node); + if (element != null) { + sink.sinkReference(sink.asJavacRef(element)); } return super.visitMethod(node, sink); } @@ -87,25 +88,26 @@ class JavacTreeRefScanner extends TreeScanner { @Override public Tree visitClass(ClassTree node, JavacTreeScannerSink sink) { - JCTree.JCClassDecl classDecl = (JCTree.JCClassDecl)node; - Symbol.ClassSymbol sym = classDecl.sym; - if (sym == null) return null; + TypeElement element = (TypeElement)sink.getReferencedElement(node); + if (element == null) return null; - final Type superclass = sym.getSuperclass(); - final List interfaces = sym.getInterfaces(); + final TypeMirror superclass = element.getSuperclass(); + final List interfaces = element.getInterfaces(); final JavacRef[] supers; - if (superclass != Type.noType) { + if (superclass != sink.getTypeUtility().getNoType(TypeKind.NONE)) { supers = new JavacRef[interfaces.size() + 1]; - supers[interfaces.size()] = JavacRef.JavacSymbolRefBase.fromSymbol(superclass.asElement()); + supers[interfaces.size()] = sink.asJavacRef(sink.getTypeUtility().asElement(superclass)); + } else { supers = interfaces.isEmpty() ? JavacRef.EMPTY_ARRAY : new JavacRef[interfaces.size()]; } int i = 0; - for (Type anInterface : interfaces) { - supers[i++] = JavacRef.JavacSymbolRefBase.fromSymbol(anInterface.asElement()); + for (TypeMirror anInterface : interfaces) { + supers[i++] = sink.asJavacRef(sink.getTypeUtility().asElement(anInterface)); + } - final JavacRef.JavacSymbolRefBase aClass = JavacRef.JavacSymbolRefBase.fromSymbol(sym); + final JavacRef.JavacElementRefBase aClass = sink.asJavacRef(element); sink.sinkReference(aClass); sink.sinkDeclaration(new JavacDef.JavacClassDef(aClass, supers)); diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacTreeScannerSink.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacTreeScannerSink.java index 04569efe57e7..f5224b6f8dd4 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacTreeScannerSink.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacTreeScannerSink.java @@ -15,12 +15,22 @@ */ package org.jetbrains.jps.javac.ast; +import com.sun.source.tree.Tree; import org.jetbrains.jps.javac.ast.api.JavacDef; import org.jetbrains.jps.javac.ast.api.JavacRef; +import javax.lang.model.element.Element; +import javax.lang.model.util.Types; + interface JavacTreeScannerSink { - void sinkReference(JavacRef.JavacSymbolRefBase ref); + void sinkReference(JavacRef.JavacElementRefBase ref); void sinkDeclaration(JavacDef def); + + JavacRef.JavacElementRefBase asJavacRef(Element element); + + Element getReferencedElement(Tree tree); + + Types getTypeUtility(); } diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacFileData.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacFileData.java index 364cf25b0b85..a5c0dc572d4b 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacFileData.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacFileData.java @@ -19,12 +19,14 @@ import com.intellij.openapi.util.ThrowableComputable; import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; import com.intellij.util.ThrowableConsumer; import com.intellij.util.io.*; -import com.intellij.util.io.DataOutputStream; import org.jetbrains.annotations.NotNull; -import java.io.*; -import java.util.Collection; -import java.util.List; +import javax.lang.model.element.Modifier; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutput; +import java.io.IOException; +import java.util.*; public class JavacFileData { private final String myFilePath; @@ -212,7 +214,7 @@ public class JavacFileData { } else { throw new IllegalStateException("unknown type: " + ref.getClass()); } - out.writeLong(ref.getFlags()); + writeModifiers(out, ref.getModifiers()); writeBytes(out, ref.getName()); } @@ -221,11 +223,11 @@ public class JavacFileData { final byte marker = in.readByte(); switch (marker) { case CLASS_MARKER: - return new JavacRef.JavacClassImpl(in.readBoolean(), in.readLong(), readBytes(in)); + return new JavacRef.JavacClassImpl(in.readBoolean(), readModifiers(in), readBytes(in)); case METHOD_MARKER: - return new JavacRef.JavacMethodImpl(readBytes(in), in.readByte(), in.readLong(), readBytes(in)); + return new JavacRef.JavacMethodImpl(readBytes(in), in.readByte(), readModifiers(in), readBytes(in)); case FIELD_MARKER: - return new JavacRef.JavacFieldImpl(readBytes(in), in.readLong(), readBytes(in)); + return new JavacRef.JavacFieldImpl(readBytes(in), readModifiers(in), readBytes(in)); default: throw new IllegalStateException("unknown marker " + marker); } @@ -242,6 +244,24 @@ public class JavacFileData { in.readFully(buf); return buf; } + + private void writeModifiers(final DataOutput output, Set modifiers) throws IOException { + DataInputOutputUtil.writeSeq(output, modifiers, new ThrowableConsumer() { + @Override + public void consume(Modifier modifier) throws IOException { + IOUtil.writeUTF(output, modifier.name()); + } + }); + } + + private Set readModifiers(final DataInput input) throws IOException { + return EnumSet.copyOf(DataInputOutputUtil.readSeq(input, new ThrowableComputable() { + @Override + public Modifier compute() throws IOException { + return null; + } + })); + } }; } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacRef.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacRef.java index a7c532cfb3dc..cb1228c5105e 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacRef.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacRef.java @@ -15,17 +15,20 @@ */ package org.jetbrains.jps.javac.ast.api; -import com.sun.tools.javac.code.Symbol; -import com.sun.tools.javac.util.Convert; +import com.sun.tools.javac.util.Name; import org.jetbrains.annotations.NotNull; +import javax.lang.model.element.*; +import javax.lang.model.util.Elements; +import java.util.Set; + public interface JavacRef { JavacRef[] EMPTY_ARRAY = new JavacRef[0]; @NotNull byte[] getName(); - long getFlags(); + Set getModifiers(); @NotNull byte[] getOwnerName(); @@ -43,11 +46,11 @@ public interface JavacRef { abstract class JavacRefBase implements JavacRef { private final byte[] myName; - private final long myFlags; + private final Set myModifiers; - protected JavacRefBase(byte[] name, long flags) { + protected JavacRefBase(byte[] name, Set modifiers) { myName = name; - myFlags = flags; + myModifiers = modifiers; } @NotNull @@ -57,16 +60,16 @@ public interface JavacRef { } @Override - public final long getFlags() { - return myFlags; + public final Set getModifiers() { + return myModifiers; } } class JavacClassImpl extends JavacRefBase implements JavacClass { private boolean myAnonymous; - public JavacClassImpl(boolean anonymous, long flags, byte[] name) { - super(name, flags); + public JavacClassImpl(boolean anonymous, Set modifiers, byte[] name) { + super(name, modifiers); myAnonymous = anonymous; } @@ -79,19 +82,14 @@ public interface JavacRef { public boolean isAnonymous() { return myAnonymous; } - - @Override - public String toString() { - return Convert.utf2string(getName()); - } } class JavacMethodImpl extends JavacRefBase implements JavacMethod { private final byte[] myOwnerName; private final byte myParamCount; - public JavacMethodImpl(byte[] ownerName, byte paramCount, long flags, byte[] name) { - super(name, flags); + public JavacMethodImpl(byte[] ownerName, byte paramCount, Set modifiers, byte[] name) { + super(name, modifiers); myOwnerName = ownerName; myParamCount = paramCount; } @@ -105,18 +103,13 @@ public interface JavacRef { public byte[] getOwnerName() { return myOwnerName; } - - @Override - public String toString() { - return Convert.utf2string(getOwnerName()) + "." + Convert.utf2string(getName()) + "(" + myParamCount + ")"; - } } class JavacFieldImpl extends JavacRefBase implements JavacField { private final byte[] myOwnerName; - public JavacFieldImpl(byte[] ownerName, long flags, byte[] name) { - super(name, flags); + public JavacFieldImpl(byte[] ownerName, Set modifiers, byte[] name) { + super(name, modifiers); myOwnerName = ownerName; } @@ -125,79 +118,84 @@ public interface JavacRef { public byte[] getOwnerName() { return myOwnerName; } - - @Override - public String toString() { - return Convert.utf2string(getOwnerName()) + "." + Convert.utf2string(getName()); - } } - abstract class JavacSymbolRefBase implements JavacRef { - protected final @NotNull Symbol myOriginalElement; + abstract class JavacElementRefBase implements JavacRef { + protected final @NotNull Element myOriginalElement; + protected final Elements myElementUtility; - protected JavacSymbolRefBase(@NotNull Symbol element) {myOriginalElement = element;} + protected JavacElementRefBase(@NotNull Element element, Elements elementUtility) { + myOriginalElement = element; + myElementUtility = elementUtility; + } @NotNull - public Symbol getOriginalElement() { + public Element getOriginalElement() { return myOriginalElement; } @NotNull @Override public byte[] getName() { - return myOriginalElement.flatName().toUtf(); + return ((Name) myOriginalElement.getSimpleName()).toUtf(); } @Override - public long getFlags() { - return myOriginalElement.flags(); + public Set getModifiers() { + return myOriginalElement.getModifiers(); } @NotNull @Override public byte[] getOwnerName() { - return myOriginalElement.owner.flatName().toUtf(); + return ((Name) myElementUtility.getBinaryName(((TypeElement) myOriginalElement.getEnclosingElement()))).toUtf(); } - public static JavacRef.JavacSymbolRefBase fromSymbol(Symbol symbol) { - if (symbol instanceof Symbol.ClassSymbol) { - return new JavacRef.JavacSymbolClassImpl(symbol); + public static JavacElementRefBase fromElement(Element element, Elements elementUtility) { + if (element instanceof TypeElement) { + return new JavacElementClassImpl(element, elementUtility); } - else if (symbol instanceof Symbol.VarSymbol) { - return new JavacRef.JavacSymbolFieldImpl(symbol); + else if (element instanceof VariableElement) { + return new JavacElementFieldImpl(element, elementUtility); } - else if (symbol instanceof Symbol.MethodSymbol) { - return new JavacRef.JavacSymbolMethodImpl(symbol); + else if (element instanceof ExecutableElement) { + return new JavacElementMethodImpl(element, elementUtility); } - throw new AssertionError("unexpected symbol: " + symbol + " class: " + symbol.getClass()); + throw new AssertionError("unexpected element: " + element + " class: " + element.getClass()); } } - class JavacSymbolClassImpl extends JavacSymbolRefBase implements JavacClass { - public JavacSymbolClassImpl(@NotNull Symbol element) { - super(element); + class JavacElementClassImpl extends JavacElementRefBase implements JavacClass { + public JavacElementClassImpl(@NotNull Element element, Elements elementUtility) { + super(element, elementUtility); + } + + @NotNull + @Override + public byte[] getName() { + return ((Name) myElementUtility.getBinaryName(((TypeElement) myOriginalElement))).toUtf(); } @Override public boolean isAnonymous() { - return myOriginalElement.name.isEmpty(); + return ((Name) myOriginalElement.getSimpleName()).isEmpty(); } } - class JavacSymbolMethodImpl extends JavacSymbolRefBase implements JavacMethod { - public JavacSymbolMethodImpl(@NotNull Symbol element) { - super(element); + class JavacElementMethodImpl extends JavacElementRefBase implements JavacMethod { + public JavacElementMethodImpl(@NotNull Element element, Elements elementUtility) { + super(element, elementUtility); } @Override public byte getParamCount() { - return (byte)((Symbol.MethodSymbol)myOriginalElement).type.getParameterTypes().size(); + return (byte)((ExecutableElement)myOriginalElement).getParameters().size(); } } - class JavacSymbolFieldImpl extends JavacSymbolRefBase implements JavacField { - public JavacSymbolFieldImpl(@NotNull Symbol element) { - super(element); + class JavacElementFieldImpl extends JavacElementRefBase implements JavacField { + public JavacElementFieldImpl(@NotNull Element element, Elements elementUtility) { + super(element, elementUtility); } } } diff --git a/jps/jps-builders/testData/referencesIndex/packageInfo2/initialIndex.txt b/jps/jps-builders/testData/referencesIndex/packageInfo2/initialIndex.txt new file mode 100644 index 000000000000..924d8b108579 --- /dev/null +++ b/jps/jps-builders/testData/referencesIndex/packageInfo2/initialIndex.txt @@ -0,0 +1,7 @@ +Backward Hierarchy: + + +Backward References: +java.lang.Deprecated in package-info + +Class Definitions: \ No newline at end of file diff --git a/jps/jps-builders/testData/referencesIndex/packageInfo2/myPackage/package-info.java b/jps/jps-builders/testData/referencesIndex/packageInfo2/myPackage/package-info.java new file mode 100644 index 000000000000..3f5236e3ebc3 --- /dev/null +++ b/jps/jps-builders/testData/referencesIndex/packageInfo2/myPackage/package-info.java @@ -0,0 +1,2 @@ +@Deprecated +package myPackage; \ No newline at end of file diff --git a/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTest.kt b/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTest.kt index 13d667071be9..1ad0c467c67c 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTest.kt +++ b/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTest.kt @@ -78,6 +78,10 @@ class ReferenceIndexTest : ReferenceIndexTestBase() { assertIndexOnRebuild("myPackage/package-info.java") } + fun testPackageInfo2() { + assertIndexOnRebuild("myPackage/package-info.java") + } + fun testArrayRefs() { assertIndexOnRebuild("Array.java", "Foo.java", "Bar.java") } From dd8438e990930d67178312640418043e5c72ebcb Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Fri, 16 Dec 2016 16:20:01 +0300 Subject: [PATCH 272/558] javac ast indices: fix javac data serialization --- .../src/org/jetbrains/jps/javac/ast/api/JavacFileData.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacFileData.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacFileData.java index a5c0dc572d4b..6fb81e630d3a 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacFileData.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacFileData.java @@ -258,7 +258,7 @@ public class JavacFileData { return EnumSet.copyOf(DataInputOutputUtil.readSeq(input, new ThrowableComputable() { @Override public Modifier compute() throws IOException { - return null; + return Modifier.valueOf(IOUtil.readUTF(input)); } })); } From 853bbd5e93a6d3c58d6e10be6ea8cfbec490cf75 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Fri, 16 Dec 2016 16:20:40 +0300 Subject: [PATCH 273/558] fix compilation --- .../com/intellij/compiler/CompilerReferencesFindUsagesTest.java | 2 -- .../com/intellij/compiler/CompilerReferencesTestBase.java | 2 -- 2 files changed, 4 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesFindUsagesTest.java b/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesFindUsagesTest.java index 3b78c4196f45..46b1776dd016 100644 --- a/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesFindUsagesTest.java +++ b/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesFindUsagesTest.java @@ -35,7 +35,6 @@ public class CompilerReferencesFindUsagesTest extends DaemonAnalyzerTestCase { public void setUp() throws Exception { myDefaultEnableState = CompilerReferenceService.IS_ENABLED_KEY.asBoolean(); CompilerReferenceService.IS_ENABLED_KEY.setValue(true); - CompilerReferenceService.enabledInTests = true; super.setUp(); myCompilerTester = new CompilerTester(myModule); } @@ -44,7 +43,6 @@ public class CompilerReferencesFindUsagesTest extends DaemonAnalyzerTestCase { public void tearDown() throws Exception { try { CompilerReferenceService.IS_ENABLED_KEY.setValue(myDefaultEnableState); - CompilerReferenceService.enabledInTests = false; myCompilerTester.tearDown(); } finally { diff --git a/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesTestBase.java b/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesTestBase.java index f61f1fd0652e..0fd0bde00891 100644 --- a/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesTestBase.java +++ b/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesTestBase.java @@ -24,14 +24,12 @@ public abstract class CompilerReferencesTestBase extends AbstractCompilerAwareTe public void setUp() throws Exception { myDefaultEnableState = CompilerReferenceService.IS_ENABLED_KEY.asBoolean(); CompilerReferenceService.IS_ENABLED_KEY.setValue(true); - CompilerReferenceService.enabledInTests = true; super.setUp(); } @Override public void tearDown() throws Exception { CompilerReferenceService.IS_ENABLED_KEY.setValue(myDefaultEnableState); - CompilerReferenceService.enabledInTests = false; super.tearDown(); } } From d978c37af66551f2c15d413eb5881a4cd9bdf1e2 Mon Sep 17 00:00:00 2001 From: Sergey Simonchik Date: Fri, 16 Dec 2016 16:16:54 +0300 Subject: [PATCH 274/558] runnerw: remove eclipse project settings --- native/runner/runnerw/.cproject | 930 ------------------ native/runner/runnerw/.project | 71 -- .../org.eclipse.cdt.managedbuilder.core.prefs | 14 - .../org.eclipse.core.resources.prefs | 3 - 4 files changed, 1018 deletions(-) delete mode 100644 native/runner/runnerw/.cproject delete mode 100644 native/runner/runnerw/.project delete mode 100644 native/runner/runnerw/.settings/org.eclipse.cdt.managedbuilder.core.prefs delete mode 100644 native/runner/runnerw/.settings/org.eclipse.core.resources.prefs diff --git a/native/runner/runnerw/.cproject b/native/runner/runnerw/.cproject deleted file mode 100644 index 88d813ccca94..000000000000 --- a/native/runner/runnerw/.cproject +++ /dev/null @@ -1,930 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/native/runner/runnerw/.project b/native/runner/runnerw/.project deleted file mode 100644 index cc8212c4e102..000000000000 --- a/native/runner/runnerw/.project +++ /dev/null @@ -1,71 +0,0 @@ - - - runnerw - - - - - - org.eclipse.cdt.managedbuilder.core.genmakebuilder - clean,full,incremental, - - - ?name? - - - - org.eclipse.cdt.make.core.append_environment - true - - - org.eclipse.cdt.make.core.buildArguments - - - - org.eclipse.cdt.make.core.buildCommand - make - - - org.eclipse.cdt.make.core.buildLocation - ${workspace_loc:/ctrl/Debug} - - - org.eclipse.cdt.make.core.contents - org.eclipse.cdt.make.core.activeConfigSettings - - - org.eclipse.cdt.make.core.enableAutoBuild - false - - - org.eclipse.cdt.make.core.enableCleanBuild - true - - - org.eclipse.cdt.make.core.enableFullBuild - true - - - org.eclipse.cdt.make.core.stopOnError - true - - - org.eclipse.cdt.make.core.useDefaultBuildCmd - true - - - - - org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder - full,incremental, - - - - - - org.eclipse.cdt.core.cnature - org.eclipse.cdt.core.ccnature - org.eclipse.cdt.managedbuilder.core.managedBuildNature - org.eclipse.cdt.managedbuilder.core.ScannerConfigNature - - diff --git a/native/runner/runnerw/.settings/org.eclipse.cdt.managedbuilder.core.prefs b/native/runner/runnerw/.settings/org.eclipse.cdt.managedbuilder.core.prefs deleted file mode 100644 index 0f163f721d5d..000000000000 --- a/native/runner/runnerw/.settings/org.eclipse.cdt.managedbuilder.core.prefs +++ /dev/null @@ -1,14 +0,0 @@ -#Fri Jul 16 15:25:34 MSD 2010 -eclipse.preferences.version=1 -environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.997820937/CPATH/delimiter=; -environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.997820937/CPATH/operation=remove -environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.997820937/CPLUS_INCLUDE_PATH/delimiter=; -environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.997820937/CPLUS_INCLUDE_PATH/operation=remove -environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.997820937/C_INCLUDE_PATH/delimiter=; -environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.997820937/C_INCLUDE_PATH/operation=remove -environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.997820937/append=true -environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.997820937/appendContributed=true -environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.997820937/LIBRARY_PATH/delimiter=; -environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.997820937/LIBRARY_PATH/operation=remove -environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.997820937/append=true -environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.997820937/appendContributed=true diff --git a/native/runner/runnerw/.settings/org.eclipse.core.resources.prefs b/native/runner/runnerw/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index a3fdc8b23f2d..000000000000 --- a/native/runner/runnerw/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,3 +0,0 @@ -#Sat Jul 17 17:27:10 MSD 2010 -eclipse.preferences.version=1 -encoding/runnerw.cpp=Cp1251 From be229a094c0c7134246234aabb5c7b535bf517f1 Mon Sep 17 00:00:00 2001 From: Anton Tarasov Date: Fri, 16 Dec 2016 16:16:18 +0300 Subject: [PATCH 275/558] IDEA-165448 [hidpi-jdk] windows: sized text "floats" under kbd cursor on 200% scale --- .../openapi/editor/impl/FontInfo.java | 21 +++++++++++++++++++ .../editor/impl/view/SimpleTextFragment.java | 4 ++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/FontInfo.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/FontInfo.java index a51886db9f80..ce64c02f88e9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/FontInfo.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/FontInfo.java @@ -19,13 +19,17 @@ import com.intellij.Patches; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.ex.util.EditorUIUtil; import com.intellij.openapi.editor.impl.view.FontLayoutService; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.registry.Registry; +import com.intellij.util.ReflectionUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.UIUtil; import gnu.trove.TIntHashSet; import org.intellij.lang.annotations.JdkConstants; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import sun.font.FontStrike; +import sun.font.FontUtilities; import java.awt.*; import java.awt.font.FontRenderContext; @@ -34,6 +38,7 @@ import java.awt.font.TextAttribute; import java.awt.image.BufferedImage; import java.io.File; import java.io.FilenameFilter; +import java.lang.reflect.Method; import java.util.*; import java.util.List; @@ -46,6 +51,8 @@ public class FontInfo { private static final boolean USE_ALTERNATIVE_CAN_DISPLAY_PROCEDURE = Registry.is("ide.mac.fix.font.fallback"); private static final FontRenderContext DUMMY_CONTEXT = new FontRenderContext(null, false, false); + private static Ref getCodePointAdvanceRef; + private final TIntHashSet mySymbolsToBreakDrawingIteration = new TIntHashSet(); private final Font myFont; @@ -231,6 +238,20 @@ public class FontInfo { return FontLayoutService.getInstance().charWidth(metrics, codePoint); } + public float charWidth2D(int codePoint) { + if (getCodePointAdvanceRef == null) { + getCodePointAdvanceRef = new Ref<>(ReflectionUtil.getDeclaredMethod(FontStrike.class, "getCodePointAdvance", int.class)); + } + if (getCodePointAdvanceRef.get() != null) { + try { + FontStrike fs = FontUtilities.getFont2D(myFont).getStrike(myFont, myFontMetrics.getFontRenderContext()); + return (float)getCodePointAdvanceRef.get().invoke(fs, codePoint); + } + catch (Exception ignore) {} + } + return charWidth(codePoint); + } + public FontMetrics fontMetrics() { if (myFontMetrics == null) { // We need to use antialising-aware font metrics because we've alrady encountered a situation when non-antialiased symbol diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/SimpleTextFragment.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/SimpleTextFragment.java index 4454c1167d71..cd47c50aa035 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/SimpleTextFragment.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/SimpleTextFragment.java @@ -36,7 +36,7 @@ class SimpleTextFragment extends TextFragment { myFont = fontInfo.getFont(); float x = 0; for (int i = 0; i < myText.length; i++) { - x += fontInfo.charWidth(myText[i]); + x += fontInfo.charWidth2D(myText[i]); myCharPositions[i] = x; } } @@ -54,7 +54,7 @@ class SimpleTextFragment extends TextFragment { @Override public void draw(Graphics2D g, float x, float y, int startColumn, int endColumn) { g.setFont(myFont); - g.drawChars(myText, startColumn, endColumn - startColumn, (int)x, (int)y); + g.drawString(new String(myText, startColumn, endColumn - startColumn), x, y); } @Override From 427c7266bb5875a48804b40846a141b646999d28 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Fri, 16 Dec 2016 16:33:32 +0300 Subject: [PATCH 276/558] do not create sdk if project generation failed --- .../python/newProject/steps/PythonGenerateProjectCallback.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/python-community-configure/src/com/jetbrains/python/newProject/steps/PythonGenerateProjectCallback.java b/python/python-community-configure/src/com/jetbrains/python/newProject/steps/PythonGenerateProjectCallback.java index c615e7a1e30a..4a18e043b9e0 100644 --- a/python/python-community-configure/src/com/jetbrains/python/newProject/steps/PythonGenerateProjectCallback.java +++ b/python/python-community-configure/src/com/jetbrains/python/newProject/steps/PythonGenerateProjectCallback.java @@ -72,7 +72,7 @@ public class PythonGenerateProjectCallback implements NullableConsumer Date: Fri, 16 Dec 2016 16:35:15 +0300 Subject: [PATCH 277/558] do not login twice --- .../edu/learning/checker/StudyCheckTask.java | 4 +++- .../learning/stepic/EduStepicAuthorizedClient.java | 10 ++++++---- .../edu/learning/stepic/EduStepicConnector.java | 13 +++++++++---- .../jetbrains/edu/learning/stepic/LoginDialog.java | 2 +- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java index 84b3f6168ce7..bb51a047753a 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java @@ -104,7 +104,9 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun runAfterTaskCheckedActions(); final Course course = StudyTaskManager.getInstance(myProject).getCourse(); if (course != null && EduNames.STUDY.equals(course.getCourseMode())) { - EduStepicConnector.postSolution(myTask, testsOutput.isSuccess(), myProject); + if (StudyTaskManager.getInstance(myProject).getUser().getAccessToken() != null) { + EduStepicConnector.postSolution(myTask, testsOutput.isSuccess(), myProject); + } } } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java index d4bfa7d10cd1..c197c91e7f32 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java @@ -10,6 +10,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.net.HttpConfigurable; import com.intellij.util.net.ssl.CertificateManager; import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyUtils; import org.apache.http.*; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; @@ -78,10 +79,7 @@ public class EduStepicAuthorizedClient { } ourClient = initializeClient(stepicUser); if (ourClient == null) { - final StepicUser user = login(stepicUser); - if (user != null) { - ourClient = initializeClient(stepicUser); - } + ourClient = EduStepicClient.getHttpClient(); } return ourClient; } @@ -106,6 +104,10 @@ public class EduStepicAuthorizedClient { else { final StepicUser authorizedUser = login(stepicUser); if (authorizedUser != null) { + final Project project = StudyUtils.getStudyProject(); + if (project != null) { + StudyTaskManager.getInstance(project).setUser(authorizedUser); + } return initializeClient(authorizedUser); } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java index 963864e11e72..c457392ab0aa 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java @@ -2,6 +2,7 @@ package com.jetbrains.edu.learning.stepic; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; @@ -313,6 +314,7 @@ public class EduStepicConnector { try { final String response = postAttempt(project, task.getStepId()); + if (response.isEmpty()) return; final StepicWrappers.AttemptWrapper.Attempt attempt = new Gson().fromJson(response, StepicWrappers.AttemptContainer.class).attempts.get(0); final Map taskFiles = task.getTaskFiles(); @@ -326,10 +328,12 @@ public class EduStepicConnector { final String fileName = fileEntry.name; final VirtualFile virtualFile = taskDir.findFileByRelativePath(fileName); if (virtualFile != null) { - final Document document = FileDocumentManager.getInstance().getDocument(virtualFile); - if (document != null) { - files.add(new StepicWrappers.SolutionFile(fileName, document.getCharsSequence().toString())); - } + ApplicationManager.getApplication().runReadAction(() -> { + final Document document = FileDocumentManager.getInstance().getDocument(virtualFile); + if (document != null) { + files.add(new StepicWrappers.SolutionFile(fileName, document.getCharsSequence().toString())); + } + }); } } @@ -342,6 +346,7 @@ public class EduStepicConnector { public static String postAttempt(@NotNull Project project, int id) throws IOException { final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + if (StudyTaskManager.getInstance(project).getUser().getAccessToken() == null) return ""; final HttpPost attemptRequest = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.ATTEMPTS); String attemptRequestBody = new Gson().toJson(new StepicWrappers.AttemptWrapper(id)); attemptRequest.setEntity(new StringEntity(attemptRequestBody, ContentType.APPLICATION_JSON)); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/LoginDialog.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/LoginDialog.java index 15a3c65e07bb..270f73031dcb 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/LoginDialog.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/LoginDialog.java @@ -43,7 +43,7 @@ public class LoginDialog extends DialogWrapper { protected void doOKAction() { if (!validateLoginAndPasswordFields()) return; myStepicUser = EduStepicAuthorizedClient.login(myLoginPanel.getLogin(), myLoginPanel.getPassword()); - if (myStepicUser != null) { + if (myStepicUser != null && myStepicUser.getId() > 0 && myStepicUser.getAccessToken() != null) { doJustOkAction(); } else { From 775aa24e847b258685a9e581161318c4a694eac0 Mon Sep 17 00:00:00 2001 From: Valentina Kiryushkina Date: Thu, 15 Dec 2016 16:11:30 +0300 Subject: [PATCH 278/558] Fix EDU-801 Empty window instead of choice task: extract methods --- .../editor/StudyChoiceVariantsPanel.kt | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/editor/StudyChoiceVariantsPanel.kt b/python/educational-core/student/src/com/jetbrains/edu/learning/editor/StudyChoiceVariantsPanel.kt index 7593fc413071..6ea73cb6438c 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/editor/StudyChoiceVariantsPanel.kt +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/editor/StudyChoiceVariantsPanel.kt @@ -3,10 +3,12 @@ package com.jetbrains.edu.learning.editor import com.intellij.openapi.editor.colors.EditorColorsManager import com.jetbrains.edu.learning.courseFormat.Task import javafx.application.Platform +import javafx.beans.value.ObservableValue import javafx.embed.swing.JFXPanel import javafx.geometry.Insets import javafx.scene.Group import javafx.scene.Scene +import javafx.scene.control.ButtonBase import javafx.scene.control.CheckBox import javafx.scene.control.RadioButton import javafx.scene.control.ToggleGroup @@ -37,18 +39,8 @@ class StudyChoiceVariantsPanel(task: Task) : JScrollPane() { val isSelected = task.selectedVariants.contains(index) val checkBox = CheckBox(variant) checkBox.isSelected = isSelected - checkBox.isWrapText = true - checkBox.maxWidthProperty().bind(scene.widthProperty().subtract(LEFT_INSET).subtract(RIGHT_INSET)) - checkBox.font = Font.font((EditorColorsManager.getInstance().globalScheme.editorFontSize + 2).toDouble()) - checkBox.stylesheets.add(StudyChoiceVariantsPanel::class.java.getResource("/style/buttons.css").toExternalForm()) - checkBox.selectedProperty().addListener { observableValue, wasSelected, isSelected -> - if (isSelected) { - task.selectedVariants.add(index) - } - else { - task.selectedVariants.remove(index) - } - } + checkBox.selectedProperty().addListener(createSelectionListener(task, index)) + setUpButtonStyle(checkBox, scene) vBox.children.add(checkBox) } } @@ -57,20 +49,10 @@ class StudyChoiceVariantsPanel(task: Task) : JScrollPane() { for ((index, variant) in task.choiceVariants.withIndex()) { val isSelected = task.selectedVariants.contains(index) val radioButton = RadioButton(variant) - radioButton.isSelected = isSelected - radioButton.isWrapText = true - radioButton.maxWidthProperty().bind(scene.widthProperty().subtract(LEFT_INSET).subtract(RIGHT_INSET)) - radioButton.font = Font.font((EditorColorsManager.getInstance().globalScheme.editorFontSize + 2).toDouble()) - radioButton.stylesheets.add(String::class.java.getResource("/style/buttons.css").toExternalForm()) radioButton.toggleGroup = toggleGroup - radioButton.selectedProperty().addListener { observableValue, wasSelected, isSelected -> - if (isSelected) { - task.selectedVariants.add(index) - } - else { - task.selectedVariants.remove(index) - } - } + radioButton.isSelected = isSelected + radioButton.selectedProperty().addListener(createSelectionListener(task, index)) + setUpButtonStyle(radioButton, scene) vBox.children.add(radioButton) } } @@ -80,4 +62,22 @@ class StudyChoiceVariantsPanel(task: Task) : JScrollPane() { setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER) setViewportView(jfxPanel) } + + private fun createSelectionListener(task: Task, index: Int): (ObservableValue, Boolean, Boolean) -> Unit { + return { observableValue, wasSelected, isSelected -> + if (isSelected) { + task.selectedVariants.add(index) + } + else { + task.selectedVariants.remove(index) + } + } + } + + private fun setUpButtonStyle(button: ButtonBase, scene: Scene) { + button.isWrapText = true + button.maxWidthProperty().bind(scene.widthProperty().subtract(LEFT_INSET).subtract(RIGHT_INSET)) + button.font = Font.font((EditorColorsManager.getInstance().globalScheme.editorFontSize + 2).toDouble()) + button.stylesheets.add(javaClass.getResource("/style/buttons.css").toExternalForm()) + } } From 9b5a6fd6c8456f5369d6e86a5142ea3a4221a414 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Fri, 16 Dec 2016 16:55:09 +0300 Subject: [PATCH 279/558] IDEA-165402 Toolwindow initialization takes noticeable time during project loading without visual... --- .../intellij/ide/startup/impl/StartupManagerImpl.java | 8 +++++++- .../openapi/fileEditor/impl/EditorsSplitters.java | 9 --------- .../openapi/project/impl/ProjectManagerImpl.java | 4 +++- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ide/startup/impl/StartupManagerImpl.java b/platform/platform-impl/src/com/intellij/ide/startup/impl/StartupManagerImpl.java index df8951bfde07..27ee4b06c80f 100644 --- a/platform/platform-impl/src/com/intellij/ide/startup/impl/StartupManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ide/startup/impl/StartupManagerImpl.java @@ -141,10 +141,16 @@ public class StartupManagerImpl extends StartupManagerEx { } public void runPostStartupActivitiesFromExtensions() { - for (final StartupActivity extension : Extensions.getExtensions(StartupActivity.POST_STARTUP_ACTIVITY)) { + StartupActivity[] extensions = Extensions.getExtensions(StartupActivity.POST_STARTUP_ACTIVITY); + for (final StartupActivity extension : extensions) { final Runnable runnable = () -> { if (!myProject.isDisposed()) { + long start = System.currentTimeMillis(); extension.runActivity(myProject); + long duration = System.currentTimeMillis() - start; + if (duration > 200) { + LOG.info(extension.getClass().getSimpleName() + " run in " + duration); + } } }; if (extension instanceof DumbAware) { diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java index 7f1de68d398f..2f46836bf55d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java @@ -15,7 +15,6 @@ */ package com.intellij.openapi.fileEditor.impl; -import com.intellij.ide.IdeBundle; import com.intellij.ide.ui.UISettings; import com.intellij.ide.ui.UISettingsListener; import com.intellij.openapi.Disposable; @@ -235,7 +234,6 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener public void openFiles() { if (mySplittersElement != null) { - initializeProgress(); final JPanel comp = myUIBuilder.process(mySplittersElement, getTopPanel()); UIUtil.invokeAndWaitIfNeeded((Runnable)() -> { if (comp != null) { @@ -255,13 +253,6 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener } } - private static void initializeProgress() { - ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); - if (indicator != null) { - indicator.setText(IdeBundle.message("loading.editors")); - } - } - public int getEditorsCount() { return mySplittersElement == null ? 0 : countFiles(mySplittersElement); } diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java index 56f0bcf019bc..466b67db6242 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java @@ -352,7 +352,9 @@ public class ProjectManagerImpl extends ProjectManagerEx implements Disposable { } }, ModalityState.NON_MODAL); }; - if (myProgressManager.getProgressIndicator() != null) { + ProgressIndicator indicator = myProgressManager.getProgressIndicator(); + if (indicator != null) { + indicator.setText("Preparing workspace..."); process.run(); return true; } From 86a712d088ebc63711e7b8bb53dcb2ada61db16a Mon Sep 17 00:00:00 2001 From: Dennis Ushakov Date: Fri, 16 Dec 2016 17:23:53 +0300 Subject: [PATCH 280/558] remove for rhtml --- platform/platform-resources/src/META-INF/XmlPlugin.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/platform/platform-resources/src/META-INF/XmlPlugin.xml b/platform/platform-resources/src/META-INF/XmlPlugin.xml index d71e4a1e1440..b3207818bc74 100644 --- a/platform/platform-resources/src/META-INF/XmlPlugin.xml +++ b/platform/platform-resources/src/META-INF/XmlPlugin.xml @@ -312,9 +312,6 @@ - - - From ecec35b0b419cffd3b1d862e36ffc1673e11667f Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Fri, 16 Dec 2016 17:26:00 +0300 Subject: [PATCH 281/558] javac ast indices: get rid of private API for functional expression visitor --- .../jps/javac/ast/Javac8RefScanner.java | 27 ++++++++++--------- .../ast/JavacReferenceCollectorListener.java | 11 +++++++- .../jps/javac/ast/JavacTreeScannerSink.java | 3 +++ 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/jps/javac-ref-scanner-8/src/org/jetbrains/jps/javac/ast/Javac8RefScanner.java b/jps/javac-ref-scanner-8/src/org/jetbrains/jps/javac/ast/Javac8RefScanner.java index 69c61be01785..cc732d4e00f1 100644 --- a/jps/javac-ref-scanner-8/src/org/jetbrains/jps/javac/ast/Javac8RefScanner.java +++ b/jps/javac-ref-scanner-8/src/org/jetbrains/jps/javac/ast/Javac8RefScanner.java @@ -3,11 +3,11 @@ package org.jetbrains.jps.javac.ast; import com.sun.source.tree.LambdaExpressionTree; import com.sun.source.tree.MemberReferenceTree; import com.sun.source.tree.Tree; -import com.sun.tools.javac.code.Symbol; -import com.sun.tools.javac.code.Type; -import com.sun.tools.javac.tree.JCTree; import org.jetbrains.jps.javac.ast.api.JavacDef; +import javax.lang.model.element.Element; +import javax.lang.model.type.TypeMirror; + /** * Used via reflection in {@link JavacTreeRefScanner#createASTScanner()} */ @@ -15,22 +15,23 @@ import org.jetbrains.jps.javac.ast.api.JavacDef; public class Javac8RefScanner extends JavacTreeRefScanner { @Override public Tree visitLambdaExpression(LambdaExpressionTree node, JavacTreeScannerSink sink) { - JCTree.JCLambda lambda = (JCTree.JCLambda)node; - final Type type = lambda.type; - final Symbol.TypeSymbol symbol = type.asElement(); - if (symbol != null) { - sink.sinkDeclaration(new JavacDef.JavacFunExprDef(sink.asJavacRef(symbol))); + final TypeMirror type = sink.getType(node); + final Element element = sink.getTypeUtility().asElement(type); + if (element != null) { + sink.sinkDeclaration(new JavacDef.JavacFunExprDef(sink.asJavacRef(element))); } return super.visitLambdaExpression(node, sink); } @Override public Tree visitMemberReference(MemberReferenceTree node, JavacTreeScannerSink sink) { - JCTree.JCMemberReference memberRef = (JCTree.JCMemberReference)node; - final Symbol sym = memberRef.sym; - if (sym != null) { - sink.sinkReference(sink.asJavacRef(sym)); - sink.sinkDeclaration(new JavacDef.JavacFunExprDef(sink.asJavacRef(memberRef.type.asElement()))); + final Element element = sink.getReferencedElement(node); + if (element != null) { + sink.sinkReference(sink.asJavacRef(element)); + } + final TypeMirror type = sink.getType(node); + if (type != null) { + sink.sinkDeclaration(new JavacDef.JavacFunExprDef(sink.asJavacRef(sink.getTypeUtility().asElement(type)))); } return super.visitMemberReference(node, sink); } diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java index 8fa0b011935c..eae35d8fea7d 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java @@ -33,6 +33,7 @@ import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.*; @@ -168,11 +169,15 @@ final class JavacReferenceCollectorListener implements TaskListener { return finalIncompletelyProcessedFile.getTreeHelper().getReferencedElement(tree); } + @Override + public TypeMirror getType(Tree tree) { + return finalIncompletelyProcessedFile.getTreeHelper().getType(tree); + } + @Override public Types getTypeUtility() { return myTypeUtility; } - }; myAstScanner.scan(declarationToProcess, sink); @@ -282,5 +287,9 @@ final class JavacReferenceCollectorListener implements TaskListener { private Element getReferencedElement(Tree tree) { return myTreeUtil.getElement(new TreePath(myUnitPath, tree)); } + + public TypeMirror getType(Tree tree) { + return myTreeUtil.getTypeMirror(new TreePath(myUnitPath, tree)); + } } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacTreeScannerSink.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacTreeScannerSink.java index f5224b6f8dd4..2e700b6e5622 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacTreeScannerSink.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacTreeScannerSink.java @@ -20,6 +20,7 @@ import org.jetbrains.jps.javac.ast.api.JavacDef; import org.jetbrains.jps.javac.ast.api.JavacRef; import javax.lang.model.element.Element; +import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Types; interface JavacTreeScannerSink { @@ -32,5 +33,7 @@ interface JavacTreeScannerSink { Element getReferencedElement(Tree tree); + TypeMirror getType(Tree tree); + Types getTypeUtility(); } From 830be4a9310762ae2d2ac6e8672c670f0ae9a644 Mon Sep 17 00:00:00 2001 From: nik Date: Fri, 16 Dec 2016 17:26:03 +0300 Subject: [PATCH 282/558] project configuration: added registry option to automatically add tools.jar when a new JDK is created When IDEA-99632 is implemented we can include option 'include tools.jar' to JDK requirements, so this workaround will become obsolete. --- .../src/org/jetbrains/jps/model/java/impl/JavaSdkUtil.java | 7 +++++++ platform/util/resources/misc/registry.properties | 2 ++ 2 files changed, 9 insertions(+) diff --git a/jps/model-impl/src/org/jetbrains/jps/model/java/impl/JavaSdkUtil.java b/jps/model-impl/src/org/jetbrains/jps/model/java/impl/JavaSdkUtil.java index bf9fef523b31..ce7f46fc6b87 100644 --- a/jps/model-impl/src/org/jetbrains/jps/model/java/impl/JavaSdkUtil.java +++ b/jps/model-impl/src/org/jetbrains/jps/model/java/impl/JavaSdkUtil.java @@ -19,6 +19,7 @@ import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileFilters; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.FileUtilRt; +import com.intellij.openapi.util.registry.Registry; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -70,6 +71,12 @@ public class JavaSdkUtil { FileFilter jarFileFilter = FileFilters.filesWithExtension("jar"); Set pathFilter = ContainerUtil.newTroveSet(FileUtil.PATH_HASHING_STRATEGY); List rootFiles = ContainerUtil.newArrayList(); + if (Registry.is("project.structure.add.tools.jar.to.new.jdk")) { + File toolsJar = new File(home, "lib/tools.jar"); + if (toolsJar.isFile()) { + rootFiles.add(toolsJar); + } + } for (File jarDir : jarDirs) { if (jarDir != null && jarDir.isDirectory()) { File[] jarFiles = notNull(jarDir.listFiles(jarFileFilter), ArrayUtil.EMPTY_FILE_ARRAY); diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index b1dfa786c03f..e4f143bfb539 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -810,6 +810,8 @@ welcome.screen.project.grouping.enabled.description=Allows to group recent proje custom.folding.max.lookup.depth=50 custom.folding.max.lookup.depth.description=Max lookup depth for custom folding comments in a PSI tree. +project.structure.add.tools.jar.to.new.jdk=false +project.structure.add.tools.jar.to.new.jdk.description=Automatically add tools.jar to 'Classpath' when new JDK is created tfs.set.connection.timeout=false From 20818e37affd8e56529458fe8e1d3a60211c14e4 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Fri, 16 Dec 2016 17:45:14 +0300 Subject: [PATCH 283/558] javac ast indices: reduce "--add-exports" options in BuildManager --- .../impl/src/com/intellij/compiler/server/BuildManager.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java index ae8f82ed70a2..9c53b30da881 100644 --- a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java +++ b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java @@ -1168,12 +1168,6 @@ public class BuildManager implements Disposable { //TODO[Dmitry Batkovich] should be replaced with the proper solution if (sdkVersion != null && sdkVersion.isAtLeast(JavaSdkVersion.JDK_1_9)) { - cmdLine.addParameters("--add-exports", "jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED"); - cmdLine.addParameters("--add-exports", "jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED"); - cmdLine.addParameters("--add-exports", "jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED"); - cmdLine.addParameters("--add-exports", "jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED"); - cmdLine.addParameters("--add-exports", "jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED"); - cmdLine.addParameters("--add-exports", "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED"); cmdLine.addParameters("--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED"); } From 98de38d399d86fc6f09519533ce40c0a67eb7379 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Fri, 16 Dec 2016 17:57:40 +0300 Subject: [PATCH 284/558] store stepic user per application --- .../actions/CCGetCourseFromStepic.java | 6 +- .../coursecreator/actions/CCPushCourse.java | 2 +- .../coursecreator/actions/CCPushLesson.java | 2 +- .../learning/StudyCourseResourceManager.java | 26 -------- .../edu/learning/StudyProjectComponent.java | 2 +- .../edu/learning/StudyTaskManager.java | 11 ---- .../jetbrains/edu/learning/StudyUtils.java | 5 +- .../edu/learning/checker/StudyCheckTask.java | 3 +- .../StudyProjectGenerator.java | 34 +++++----- .../learning/stepic/CCStepicConnector.java | 38 ++++++------ .../stepic/EduAdaptiveStepicConnector.java | 62 +++++++++---------- .../stepic/EduStepicAuthorizedClient.java | 18 ++---- .../learning/stepic/EduStepicConnector.java | 47 +++++++------- .../learning/stepic/StepicStudyOptions.java | 14 ++--- .../learning/stepic/StepicUpdateSettings.java | 11 ++++ .../edu/learning/ui/StudyNewProjectPanel.java | 2 +- .../PyStudyDirectoryProjectGenerator.java | 4 +- 17 files changed, 126 insertions(+), 161 deletions(-) delete mode 100644 python/educational-core/student/src/com/jetbrains/edu/learning/StudyCourseResourceManager.java diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCGetCourseFromStepic.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCGetCourseFromStepic.java index f8b62a2e58c4..1e8c547709ac 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCGetCourseFromStepic.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCGetCourseFromStepic.java @@ -61,14 +61,14 @@ public class CCGetCourseFromStepic extends DumbAwareAction { private static void createCourse(Project project, String courseId) { final VirtualFile baseDir = project.getBaseDir(); - final CourseInfo info = CCStepicConnector.getCourseInfo(project, courseId); + final CourseInfo info = CCStepicConnector.getCourseInfo(courseId); if (info == null) return; final Course course = EduStepicConnector.getCourse(project, info); if (course != null) { - flushCourse(project, course); + flushCourse(course); - final File courseDirectory = StudyUtils.getCourseDirectory(project, course); + final File courseDirectory = StudyUtils.getCourseDirectory(course); ApplicationManager.getApplication().invokeAndWait(() -> ApplicationManager.getApplication().runWriteAction(() -> { final VirtualFile[] children = baseDir.getChildren(); for (VirtualFile child : children) { diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java index 87a4e51f3af0..9e5ecfed340a 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java @@ -66,7 +66,7 @@ public class CCPushCourse extends DumbAwareAction { final int lessonId = CCStepicConnector.postLesson(project, lesson, indicator); final List sections = info.getSections(); final Integer sectionId = sections.get(sections.size() - 1); - CCStepicConnector.postUnit(project, lessonId, lesson.getIndex(), sectionId); + CCStepicConnector.postUnit(lessonId, lesson.getIndex(), sectionId); } } } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java index 2122709f977e..da28246d5f07 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java @@ -84,7 +84,7 @@ public class CCPushLesson extends DumbAwareAction { final int lessonId = CCStepicConnector.postLesson(project, lesson, indicator); final List sections = info.getSections(); final Integer sectionId = sections.get(sections.size()-1); - CCStepicConnector.postUnit(project, lessonId, lesson.getIndex(), sectionId); + CCStepicConnector.postUnit(lessonId, lesson.getIndex(), sectionId); } }}); } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyCourseResourceManager.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyCourseResourceManager.java deleted file mode 100644 index 546d2406bad2..000000000000 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyCourseResourceManager.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.jetbrains.edu.learning; - -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.jetbrains.edu.learning.courseFormat.Task; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class StudyCourseResourceManager { - - /** - * Gets text of resource file such as test input file or task text in needed format - * - * @param fileName name of resource file which should exist in task directory - * @param wrapHTML if it's necessary to wrap text with html tags - * @return text of resource file wrapped with html tags if necessary - */ - @Nullable - public String getResourceText(@NotNull final Project project, @NotNull final Task task, @NotNull final String fileName, boolean wrapHTML) { - VirtualFile taskDir = task.getTaskDir(project); - if (taskDir != null) { - return StudyUtils.getFileText(taskDir.getCanonicalPath(), fileName, wrapHTML, "UTF-8"); - } - return null; - } -} diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java index 3764d5cf34d3..f546c900c152 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java @@ -148,7 +148,7 @@ public class StudyProjectComponent implements ProjectComponent { final Course course = EduStepicConnector.getCourse(myProject, info); if (course == null) return; - flushCourse(myProject, course); + flushCourse(course); course.initCourse(false); StudyLanguageManager manager = StudyUtils.getLanguageManager(course); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java index eed7a8e9d6ff..79fd21fd15f0 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java @@ -13,7 +13,6 @@ import com.intellij.util.xmlb.XmlSerializer; import com.intellij.util.xmlb.annotations.Transient; import com.jetbrains.edu.learning.courseFormat.*; import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; -import com.jetbrains.edu.learning.stepic.StepicUser; import com.jetbrains.edu.learning.ui.StudyToolWindow; import org.jdom.Element; import org.jdom.output.XMLOutputter; @@ -35,7 +34,6 @@ import java.util.Map; public class StudyTaskManager implements PersistentStateComponent, DumbAware { private static final Logger LOG = Logger.getInstance(StudyTaskManager.class); public static final int CURRENT_VERSION = 4; - private StepicUser myUser = new StepicUser(); private Course myCourse; public int VERSION = 4; @@ -199,15 +197,6 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb myTurnEditingMode = turnEditingMode; } - @NotNull - public StepicUser getUser() { - return myUser; - } - - public void setUser(@NotNull final StepicUser user) { - myUser = user; - } - public boolean isEnableTestingFromSamples() { return myEnableTestingFromSamples; } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java index 99d184d14744..6ac29a52f8de 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java @@ -61,6 +61,7 @@ import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.*; import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; import com.jetbrains.edu.learning.editor.StudyEditor; +import com.jetbrains.edu.learning.stepic.StepicUpdateSettings; import com.jetbrains.edu.learning.ui.StudyToolWindow; import com.jetbrains.edu.learning.ui.StudyToolWindowFactory; import com.petebevin.markdown.MarkdownProcessor; @@ -627,12 +628,12 @@ public class StudyUtils { } @NotNull - public static File getCourseDirectory(@NotNull Project project, Course course) { + public static File getCourseDirectory(Course course) { final File courseDirectory; if (course.isAdaptive()) { courseDirectory = new File(StudyProjectGenerator.OUR_COURSES_DIR, StudyProjectGenerator.ADAPTIVE_COURSE_PREFIX + course.getName() - + "_" + StudyTaskManager.getInstance(project).getUser().getEmail()); + + "_" + StepicUpdateSettings.getInstance().getUser().getEmail()); } else { courseDirectory = new File(StudyProjectGenerator.OUR_COURSES_DIR, course.getName()); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java index bb51a047753a..b2ee1e17b4b3 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java @@ -23,6 +23,7 @@ import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.*; import com.jetbrains.edu.learning.stepic.EduAdaptiveStepicConnector; import com.jetbrains.edu.learning.stepic.EduStepicConnector; +import com.jetbrains.edu.learning.stepic.StepicUpdateSettings; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -104,7 +105,7 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun runAfterTaskCheckedActions(); final Course course = StudyTaskManager.getInstance(myProject).getCourse(); if (course != null && EduNames.STUDY.equals(course.getCourseMode())) { - if (StudyTaskManager.getInstance(myProject).getUser().getAccessToken() != null) { + if (StepicUpdateSettings.getInstance().getUser().getAccessToken() != null) { EduStepicConnector.postSolution(myTask, testsOutput.isSuccess(), myProject); } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java index 2b6f2ab6530c..5029459a06c2 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java @@ -14,7 +14,6 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.startup.StartupManager; 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.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; @@ -37,6 +36,7 @@ import com.jetbrains.edu.learning.editor.StudyEditor; import com.jetbrains.edu.learning.statistics.EduUsagesCollector; import com.jetbrains.edu.learning.stepic.CourseInfo; import com.jetbrains.edu.learning.stepic.EduStepicConnector; +import com.jetbrains.edu.learning.stepic.StepicUpdateSettings; import com.jetbrains.edu.learning.stepic.StepicUser; import org.apache.commons.codec.binary.Base64; import org.jetbrains.annotations.NotNull; @@ -57,7 +57,6 @@ public class StudyProjectGenerator { private static final String COURSE_DESCRIPTION = "description"; private static final String CACHE_NAME = "courseNames.txt"; private final List myListeners = ContainerUtil.newArrayList(); - @Nullable public StepicUser myUser; private List myCourses = new ArrayList<>(); private List myEnrolledCoursesIds = new ArrayList<>(); protected CourseInfo mySelectedCourseInfo; @@ -67,7 +66,8 @@ public class StudyProjectGenerator { } public boolean isLoggedIn() { - return myUser != null && !StringUtil.isEmptyOrSpaces(myUser.getPassword()) && !StringUtil.isEmptyOrSpaces(myUser.getEmail()); + final StepicUser user = StepicUpdateSettings.getInstance().getUser(); + return user.getAccessToken() != null; } public void setEnrolledCoursesIds(@NotNull final List coursesIds) { @@ -88,16 +88,13 @@ public class StudyProjectGenerator { } public void generateProject(@NotNull final Project project, @NotNull final VirtualFile baseDir) { - if (myUser != null) { - StudyTaskManager.getInstance(project).setUser(myUser); - } final Course course = getCourse(project); if (course == null) { LOG.warn("Course is null"); Messages.showWarningDialog("Some problems occurred while creating the course", "Error in Course Creation"); return; } - final File courseDirectory = StudyUtils.getCourseDirectory(project, course); + final File courseDirectory = StudyUtils.getCourseDirectory(course); StudyTaskManager.getInstance(project).setCourse(course); ApplicationManager.getApplication().runWriteAction(() -> { StudyGenerator.createCourse(course, baseDir, courseDirectory, project); @@ -120,12 +117,15 @@ public class StudyProjectGenerator { } return getCourseFromStepic(project); } - else if (myUser != null) { - final File adaptiveCourseFile = new File(new File(OUR_COURSES_DIR, ADAPTIVE_COURSE_PREFIX + - mySelectedCourseInfo.getName() + "_" + - myUser.getEmail()), EduNames.COURSE_META_FILE); - if (adaptiveCourseFile.exists()) { - return readCourseFromCache(adaptiveCourseFile, true); + else { + final StepicUser user = StepicUpdateSettings.getInstance().getUser(); + if (user.getAccessToken() != null) { + final File adaptiveCourseFile = new File(new File(OUR_COURSES_DIR, ADAPTIVE_COURSE_PREFIX + + mySelectedCourseInfo.getName() + "_" + + user.getEmail()), EduNames.COURSE_META_FILE); + if (adaptiveCourseFile.exists()) { + return readCourseFromCache(adaptiveCourseFile, true); + } } } return getCourseFromStepic(project); @@ -137,7 +137,7 @@ public class StudyProjectGenerator { return execCancelable(() -> { final Course course = EduStepicConnector.getCourse(project, mySelectedCourseInfo); if (course != null) { - flushCourse(project, course); + flushCourse(course); course.initCourse(false); } return course; @@ -216,8 +216,8 @@ public class StudyProjectGenerator { } } - public static void flushCourse(@NotNull final Project project, @NotNull final Course course) { - final File courseDirectory = StudyUtils.getCourseDirectory(project, course); + public static void flushCourse(@NotNull final Course course) { + final File courseDirectory = StudyUtils.getCourseDirectory(course); FileUtil.createDirectory(courseDirectory); flushCourseJson(course, courseDirectory); @@ -423,7 +423,7 @@ public class StudyProjectGenerator { myCourses = getCoursesFromCache(); } if (force || myCourses.isEmpty()) { - myCourses = execCancelable(() -> EduStepicConnector.getCourses(myUser)); + myCourses = execCancelable(() -> EduStepicConnector.getCourses(StepicUpdateSettings.getInstance().getUser())); flushCache(myCourses); } if (myCourses.isEmpty() || (myCourses.size() == 1 && myCourses.contains(CourseInfo.INVALID_COURSE))) { diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CCStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CCStepicConnector.java index bd150917850c..246a48fb1850 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CCStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CCStepicConnector.java @@ -43,11 +43,11 @@ public class CCStepicConnector { private CCStepicConnector() { } - public static CourseInfo getCourseInfo(Project project, String courseId) { + public static CourseInfo getCourseInfo(String courseId) { final String url = EduStepicNames.COURSES + "/" + courseId; try { final StepicWrappers.CoursesContainer coursesContainer = - EduStepicAuthorizedClient.getFromStepic(url, StepicWrappers.CoursesContainer.class, project); + EduStepicAuthorizedClient.getFromStepic(url, StepicWrappers.CoursesContainer.class); return coursesContainer.courses.get(0); } catch (IOException e) { @@ -69,7 +69,7 @@ public class CCStepicConnector { indicator.setText("Uploading course to " + EduStepicNames.STEPIC_URL); final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/courses"); - final StepicUser currentUser = EduStepicAuthorizedClient.getCurrentUser(EduStepicAuthorizedClient.getHttpClient(project)); + final StepicUser currentUser = EduStepicAuthorizedClient.getCurrentUser(EduStepicAuthorizedClient.getHttpClient()); if (currentUser != null) { final List courseAuthors = course.getAuthors(); for (int i = 0; i < courseAuthors.size(); i++) { @@ -86,7 +86,7 @@ public class CCStepicConnector { request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; @@ -98,12 +98,12 @@ public class CCStepicConnector { } final CourseInfo postedCourse = new Gson().fromJson(responseString, StepicWrappers.CoursesContainer.class).courses.get(0); course.setId(postedCourse.id); - final int sectionId = postModule(project, postedCourse.id, 1, String.valueOf(postedCourse.getName())); + final int sectionId = postModule(postedCourse.id, 1, String.valueOf(postedCourse.getName())); int position = 1; for (Lesson lesson : course.getLessons()) { indicator.checkCanceled(); final int lessonId = postLesson(project, lesson, indicator); - postUnit(project, lessonId, position, sectionId); + postUnit(lessonId, position, sectionId); position += 1; } ApplicationManager.getApplication().runReadAction(() -> postAdditionalFiles(project, postedCourse.id, indicator)); @@ -125,7 +125,7 @@ public class CCStepicConnector { }); if (!files.isEmpty()) { - final int sectionId = postModule(project, id, 2, EduNames.PYCHARM_ADDITIONAL); + final int sectionId = postModule(id, 2, EduNames.PYCHARM_ADDITIONAL); final Lesson lesson = new Lesson(); lesson.setName(EduNames.PYCHARM_ADDITIONAL); final Task task = new Task(); @@ -151,11 +151,11 @@ public class CCStepicConnector { lesson.addTask(task); lesson.setIndex(1); final int lessonId = postLesson(project, lesson, indicator); - postUnit(project, lessonId, 1, sectionId); + postUnit(lessonId, 1, sectionId); } } - public static void postUnit(@NotNull final Project project, int lessonId, int position, int sectionId) { + public static void postUnit(int lessonId, int position, int sectionId) { final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.UNITS); final StepicWrappers.UnitWrapper unitWrapper = new StepicWrappers.UnitWrapper(); unitWrapper.unit = new StepicWrappers.Unit(); @@ -167,7 +167,7 @@ public class CCStepicConnector { request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; @@ -182,7 +182,7 @@ public class CCStepicConnector { } } - private static int postModule(@NotNull final Project project, int courseId, int position, @NotNull final String title) { + private static int postModule(int courseId, int position, @NotNull final String title) { final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/sections"); final StepicWrappers.Section section = new StepicWrappers.Section(); section.course = courseId; @@ -194,7 +194,7 @@ public class CCStepicConnector { request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; @@ -226,7 +226,7 @@ public class CCStepicConnector { request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; @@ -250,7 +250,7 @@ public class CCStepicConnector { request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; @@ -262,7 +262,7 @@ public class CCStepicConnector { } final Lesson postedLesson = new Gson().fromJson(responseString, Course.class).getLessons().get(0); for (Integer step : postedLesson.steps) { - deleteTask(step, project); + deleteTask(step); } for (Task task : lesson.getTaskList()) { @@ -284,7 +284,7 @@ public class CCStepicConnector { request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; @@ -308,11 +308,11 @@ public class CCStepicConnector { return -1; } - public static void deleteTask(@NotNull final Integer task, Project project) { + public static void deleteTask(@NotNull final Integer task) { final HttpDelete request = new HttpDelete(EduStepicNames.STEPIC_API_URL + EduStepicNames.STEP_SOURCES + task); ApplicationManager.getApplication().invokeLater(() -> { try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; @@ -337,7 +337,7 @@ public class CCStepicConnector { request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); final CloseableHttpResponse response = client.execute(request); final StatusLine line = response.getStatusLine(); final HttpEntity responseEntity = response.getEntity(); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java index ef9e9e156ab1..28ead09933d2 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java @@ -67,7 +67,7 @@ public class EduAdaptiveStepicConnector { @Nullable public static Task getNextRecommendation(@NotNull Project project, @NotNull Course course) { try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); final URI uri = new URIBuilder(EduStepicNames.STEPIC_API_URL + EduStepicNames.RECOMMENDATIONS_URL) .addParameter(EduNames.COURSE, String.valueOf(course.getId())) .build(); @@ -88,8 +88,7 @@ public class EduAdaptiveStepicConnector { final StepicWrappers.Recommendation recommendation = recomWrapper.recommendations[0]; final String lessonId = recommendation.lesson; final StepicWrappers.LessonContainer lessonContainer = EduStepicAuthorizedClient.getFromStepic(EduStepicNames.LESSONS + lessonId, - StepicWrappers.LessonContainer.class, - project); + StepicWrappers.LessonContainer.class); if (lessonContainer.lessons.size() == 1) { final Lesson realLesson = lessonContainer.lessons.get(0); course.getLessons().get(0).setId(Integer.parseInt(lessonId)); @@ -99,8 +98,8 @@ public class EduAdaptiveStepicConnector { if (taskFromStep != null) return taskFromStep; } - final StepicUser user = StudyTaskManager.getInstance(project).getUser(); - postRecommendationReaction(project, lessonId, + final StepicUser user = StepicUpdateSettings.getInstance().getUser(); + postRecommendationReaction(lessonId, String.valueOf(user.getId()), -1); return getNextRecommendation(project, course); } @@ -137,30 +136,29 @@ public class EduAdaptiveStepicConnector { @Nullable private static Task getTask(@NotNull Project project, @NotNull String name, int stepId) throws IOException { - final StepicWrappers.StepSource step = getStep(project, stepId); + final StepicWrappers.StepSource step = getStep(stepId); final String stepType = step.block.name; if (stepType.equals(CODE_TASK_TYPE)) { return getCodeTaskFromStep(project, step.block, name, stepId); } else if (stepType.equals(CHOICE_TYPE_TEXT)) { - return getChoiceTaskFromStep(project, name, step.block, stepId); + return getChoiceTaskFromStep(name, step.block, stepId); } else if (stepType.startsWith(EduStepicConnector.PYCHARM_PREFIX)) { - return EduStepicConnector.createTask(project, stepId); + return EduStepicConnector.createTask(stepId); } return null; } - private static Task getChoiceTaskFromStep(@NotNull Project project, - @NotNull String lessonName, + private static Task getChoiceTaskFromStep(@NotNull String lessonName, @NotNull StepicWrappers.Step block, int stepId) { final Task task = new Task(lessonName); task.setStepId(stepId); task.setText(block.text); - final StepicWrappers.AdaptiveAttemptWrapper.Attempt attempt = getAttemptForStep(project, stepId); + final StepicWrappers.AdaptiveAttemptWrapper.Attempt attempt = getAttemptForStep(stepId); if (attempt != null) { final StepicWrappers.AdaptiveAttemptWrapper.Dataset dataset = attempt.dataset; if (dataset != null) { @@ -184,16 +182,16 @@ public class EduAdaptiveStepicConnector { } @Nullable - private static StepicWrappers.AdaptiveAttemptWrapper.Attempt getAttemptForStep(@NotNull Project project, int id) { - final StepicUser user = StudyTaskManager.getInstance(project).getUser(); + private static StepicWrappers.AdaptiveAttemptWrapper.Attempt getAttemptForStep(int id) { + final StepicUser user = StepicUpdateSettings.getInstance().getUser(); try { final List attempts = getAttempts(user, id); if (attempts != null && attempts.size() > 0) { final StepicWrappers.AdaptiveAttemptWrapper.Attempt attempt = attempts.get(0); - return attempt.isActive() ? attempt : createNewAttempt(project, id); + return attempt.isActive() ? attempt : createNewAttempt(id); } else { - return createNewAttempt(project, id); + return createNewAttempt(id); } } catch (URISyntaxException | IOException e) { @@ -202,8 +200,8 @@ public class EduAdaptiveStepicConnector { return null; } - private static StepicWrappers.AdaptiveAttemptWrapper.Attempt createNewAttempt(@NotNull Project project, int id) throws IOException { - final String response = EduStepicConnector.postAttempt(project, id); + private static StepicWrappers.AdaptiveAttemptWrapper.Attempt createNewAttempt(int id) throws IOException { + final String response = EduStepicConnector.postAttempt(id); final StepicWrappers.AdaptiveAttemptContainer attempt = new Gson().fromJson(response, StepicWrappers.AdaptiveAttemptContainer.class); return attempt.attempts.get(0); } @@ -237,15 +235,14 @@ public class EduAdaptiveStepicConnector { request.setConfig(requestConfig); } - public static boolean postRecommendationReaction(@NotNull Project project, - @NotNull String lessonId, + public static boolean postRecommendationReaction(@NotNull String lessonId, @NotNull String user, int reaction) { final HttpPost post = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.RECOMMENDATION_REACTIONS_URL); final String json = new Gson() .toJson(new StepicWrappers.RecommendationReactionWrapper(new StepicWrappers.RecommendationReaction(reaction, user, lessonId))); post.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON)); - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); setTimeout(post); try { final CloseableHttpResponse execute = client.execute(post); @@ -274,10 +271,10 @@ public class EduAdaptiveStepicConnector { final Course course = StudyTaskManager.getInstance(project).getCourse(); if (course != null && editor != null && editor.getTaskFile() != null) { indicator.checkCanceled(); - final StepicUser user = StudyTaskManager.getInstance(project).getUser(); + final StepicUser user = StepicUpdateSettings.getInstance().getUser(); final boolean recommendationReaction = - postRecommendationReaction(project, String.valueOf(editor.getTaskFile().getTask().getLesson().getId()), + postRecommendationReaction(String.valueOf(editor.getTaskFile().getTask().getLesson().getId()), String.valueOf(user.getId()), reaction); if (recommendationReaction) { indicator.checkCanceled(); @@ -473,7 +470,7 @@ public class EduAdaptiveStepicConnector { public static Pair checkChoiceTask(@NotNull Project project, @NotNull Task task) { if (task.getSelectedVariants().isEmpty()) return Pair.create(false, "No variants selected"); - final StepicWrappers.AdaptiveAttemptWrapper.Attempt attempt = getAttemptForStep(project, task.getStepId()); + final StepicWrappers.AdaptiveAttemptWrapper.Attempt attempt = getAttemptForStep(task.getStepId()); if (attempt != null) { final int attemptId = attempt.id; @@ -483,10 +480,10 @@ public class EduAdaptiveStepicConnector { if (!isActiveAttempt) return Pair.create(false, "Your solution is out of date. Please try again"); final StepicWrappers.SubmissionToPostWrapper wrapper = new StepicWrappers.SubmissionToPostWrapper(String.valueOf(attemptId), createChoiceTaskAnswerArray(task)); - final Pair pair = doAdaptiveCheck(project, wrapper, attemptId); + final Pair pair = doAdaptiveCheck(wrapper, attemptId); if (!pair.getFirst()) { try { - createNewAttempt(project, task.getStepId()); + createNewAttempt(task.getStepId()); final Task updatedTask = getTask(project, task.getName(), task.getStepId()); if (updatedTask != null) { final List variants = updatedTask.getChoiceVariants(); @@ -517,7 +514,7 @@ public class EduAdaptiveStepicConnector { public static Pair checkCodeTask(@NotNull Project project, @NotNull Task task) { int attemptId = -1; try { - attemptId = getAttemptId(project, task); + attemptId = getAttemptId(task); } catch (IOException e) { LOG.warn(e.getMessage()); @@ -529,7 +526,7 @@ public class EduAdaptiveStepicConnector { final String answer = PYCHARM_COMMENT + editor.getDocument().getText(); final StepicWrappers.SubmissionToPostWrapper submissionToPost = new StepicWrappers.SubmissionToPostWrapper(String.valueOf(attemptId), language, answer); - return doAdaptiveCheck(project, submissionToPost, attemptId); + return doAdaptiveCheck(submissionToPost, attemptId); } } else { @@ -538,11 +535,10 @@ public class EduAdaptiveStepicConnector { return Pair.create(false, ""); } - private static Pair doAdaptiveCheck(@NotNull Project project, - @NotNull StepicWrappers.SubmissionToPostWrapper submission, + private static Pair doAdaptiveCheck(@NotNull StepicWrappers.SubmissionToPostWrapper submission, int attemptId) { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); - final StepicUser user = StudyTaskManager.getInstance(project).getUser(); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); + final StepicUser user = StepicUpdateSettings.getInstance().getUser(); StepicWrappers.ResultSubmissionWrapper wrapper = postResultsForCheck(client, submission); if (wrapper != null) { wrapper = getCheckResults(client, wrapper, attemptId, user.getId()); @@ -640,13 +636,13 @@ public class EduAdaptiveStepicConnector { return null; } - private static int getAttemptId(@NotNull final Project project, @NotNull Task task) throws IOException { + private static int getAttemptId(@NotNull Task task) throws IOException { final StepicWrappers.AdaptiveAttemptWrapper attemptWrapper = new StepicWrappers.AdaptiveAttemptWrapper(task.getStepId()); final HttpPost post = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.ATTEMPTS); post.setEntity(new StringEntity(new Gson().toJson(attemptWrapper))); - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); setTimeout(post); final CloseableHttpResponse httpResponse = client.execute(post); final int statusCode = httpResponse.getStatusLine().getStatusCode(); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java index c197c91e7f32..de79476be1c0 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java @@ -5,12 +5,9 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; 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.util.net.HttpConfigurable; import com.intellij.util.net.ssl.CertificateManager; -import com.jetbrains.edu.learning.StudyTaskManager; -import com.jetbrains.edu.learning.StudyUtils; import org.apache.http.*; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; @@ -48,24 +45,24 @@ public class EduStepicAuthorizedClient { } @NotNull - public static CloseableHttpClient getHttpClient(@NotNull final Project project) { + public static CloseableHttpClient getHttpClient() { if (ourClient != null) { return ourClient; } - final StepicUser stepicUser = StudyTaskManager.getInstance(project).getUser(); + final StepicUser stepicUser = StepicUpdateSettings.getInstance().getUser(); ourClient = initializeClient(stepicUser); if (ourClient == null) { final StepicUser user = login(stepicUser); if (user != null) { - StudyTaskManager.getInstance(project).setUser(user); + StepicUpdateSettings.getInstance().setUser(user); ourClient = initializeClient(stepicUser); } } return ourClient; } - public static T getFromStepic(String link, final Class container, @NotNull final Project project) throws IOException { - return EduStepicClient.getFromStepic(link, container, getHttpClient(project)); + public static T getFromStepic(String link, final Class container) throws IOException { + return EduStepicClient.getFromStepic(link, container, getHttpClient()); } /* @@ -104,10 +101,7 @@ public class EduStepicAuthorizedClient { else { final StepicUser authorizedUser = login(stepicUser); if (authorizedUser != null) { - final Project project = StudyUtils.getStudyProject(); - if (project != null) { - StudyTaskManager.getInstance(project).setUser(authorizedUser); - } + StepicUpdateSettings.getInstance().setUser(authorizedUser); return initializeClient(authorizedUser); } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java index c457392ab0aa..09c252976b9c 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java @@ -9,7 +9,6 @@ import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; import com.jetbrains.edu.learning.courseFormat.Task; @@ -203,7 +202,7 @@ public class EduStepicConnector { course.setLanguage(language); try { for (Integer section : info.sections) { - course.addLessons(getLessons(project, section)); + course.addLessons(getLessons(section)); } return course; } @@ -229,23 +228,23 @@ public class EduStepicConnector { return null; } - public static List getLessons(@NotNull Project project, int sectionId) throws IOException { + public static List getLessons(int sectionId) throws IOException { final StepicWrappers.SectionContainer sectionContainer = getFromStepic(EduStepicNames.SECTIONS + String.valueOf(sectionId), - StepicWrappers.SectionContainer.class, project); + StepicWrappers.SectionContainer.class); List unitIds = sectionContainer.sections.get(0).units; final List lessons = new ArrayList<>(); for (Integer unitId : unitIds) { StepicWrappers.UnitContainer - unit = getFromStepic(EduStepicNames.UNITS + "/" + String.valueOf(unitId), StepicWrappers.UnitContainer.class, project); + unit = getFromStepic(EduStepicNames.UNITS + "/" + String.valueOf(unitId), StepicWrappers.UnitContainer.class); int lessonID = unit.units.get(0).lesson; StepicWrappers.LessonContainer lessonContainer = getFromStepic(EduStepicNames.LESSONS + String.valueOf(lessonID), - StepicWrappers.LessonContainer.class, project); + StepicWrappers.LessonContainer.class); Lesson lesson = lessonContainer.lessons.get(0); lesson.taskList = new ArrayList<>(); for (int stepId : lesson.steps) { - final Task task = createTask(project, stepId); + final Task task = createTask(stepId); if (task != null) { lesson.addTask(task); } @@ -258,20 +257,18 @@ public class EduStepicConnector { return lessons; } - private static T getFromStepic(String link, final Class container, @Nullable final Project project) throws IOException{ - if (project != null) { - final StepicUser user = StudyTaskManager.getInstance(project).getUser(); - final boolean isAuthorized = user.getAccessToken() != null; - if (isAuthorized) { - return EduStepicAuthorizedClient.getFromStepic(link, container, user); - } + private static T getFromStepic(String link, final Class container) throws IOException{ + final StepicUser user = StepicUpdateSettings.getInstance().getUser(); + final boolean isAuthorized = user.getAccessToken() != null; + if (isAuthorized) { + return EduStepicAuthorizedClient.getFromStepic(link, container, user); } return EduStepicClient.getFromStepic(link, container); } @Nullable - public static Task createTask(@NotNull Project project, int stepicId) throws IOException { - final StepicWrappers.StepSource step = getStep(project, stepicId); + public static Task createTask(int stepicId) throws IOException { + final StepicWrappers.StepSource step = getStep(stepicId); final StepicWrappers.Step block = step.block; if (!block.name.startsWith(PYCHARM_PREFIX)) { LOG.error("Got a block with non-pycharm prefix: " + block.name + " for step: " + stepicId); @@ -302,9 +299,9 @@ public class EduStepicConnector { return task; } - public static StepicWrappers.StepSource getStep(@NotNull Project project, int step) throws IOException { + public static StepicWrappers.StepSource getStep(int step) throws IOException { return getFromStepic(EduStepicNames.STEPS + String.valueOf(step), - StepicWrappers.StepContainer.class, project).steps.get(0); + StepicWrappers.StepContainer.class).steps.get(0); } public static void postSolution(@NotNull final Task task, boolean passed, @NotNull final Project project) { @@ -313,7 +310,7 @@ public class EduStepicConnector { } try { - final String response = postAttempt(project, task.getStepId()); + final String response = postAttempt(task.getStepId()); if (response.isEmpty()) return; final StepicWrappers.AttemptWrapper.Attempt attempt = new Gson().fromJson(response, StepicWrappers.AttemptContainer.class).attempts.get(0); @@ -337,16 +334,16 @@ public class EduStepicConnector { } } - postSubmission(passed, attempt, project, files); + postSubmission(passed, attempt, files); } catch (IOException e) { LOG.error(e.getMessage()); } } - public static String postAttempt(@NotNull Project project, int id) throws IOException { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); - if (StudyTaskManager.getInstance(project).getUser().getAccessToken() == null) return ""; + public static String postAttempt(int id) throws IOException { + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); + if (StepicUpdateSettings.getInstance().getUser().getAccessToken() == null) return ""; final HttpPost attemptRequest = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.ATTEMPTS); String attemptRequestBody = new Gson().toJson(new StepicWrappers.AttemptWrapper(id)); attemptRequest.setEntity(new StringEntity(attemptRequestBody, ContentType.APPLICATION_JSON)); @@ -363,12 +360,12 @@ public class EduStepicConnector { } private static void postSubmission(boolean passed, StepicWrappers.AttemptWrapper.Attempt attempt, - Project project, ArrayList files) throws IOException { + ArrayList files) throws IOException { final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.SUBMISSIONS); String requestBody = new Gson().toJson(new StepicWrappers.SubmissionWrapper(attempt.id, passed ? "1" : "0", files)); request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicStudyOptions.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicStudyOptions.java index 607dcc6400d4..bc6fcc98d668 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicStudyOptions.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicStudyOptions.java @@ -92,7 +92,7 @@ public class StepicStudyOptions implements StudyOptionsProvider { Project project = StudyUtils.getStudyProject(); if (project != null) { StudyTaskManager taskManager = StudyTaskManager.getInstance(project); - final StepicUser user = taskManager.getUser(); + final StepicUser user = StepicUpdateSettings.getInstance().getUser(); setLogin(user.getEmail()); setPassword(user.getPassword()); myEnableTestingFromSamples.setSelected(taskManager.isEnableTestingFromSamples()); @@ -116,7 +116,7 @@ public class StepicStudyOptions implements StudyOptionsProvider { taskManager.setEnableTestingFromSamples(isTestingFromSamplesEnabled()); } - final StepicUser user = taskManager.getUser(); + final StepicUser user = StepicUpdateSettings.getInstance().getUser(); final boolean isCredentialsModified = !getLogin().equals(user.getEmail()) || !getPassword().equals(user.getPassword()); if (isCredentialsModified) { final String login = getLogin(); @@ -132,14 +132,14 @@ public class StepicStudyOptions implements StudyOptionsProvider { myProject); if (stepicUser[0] != null && stepicUser[0].getAccessToken() != null) { - taskManager.setUser(stepicUser[0]); + StepicUpdateSettings.getInstance().setUser(stepicUser[0]); } else { throw new ConfigurationException("Unable to login"); } } else { - removeCredentials(taskManager); + removeCredentials(); } } } @@ -148,8 +148,8 @@ public class StepicStudyOptions implements StudyOptionsProvider { } } - private static void removeCredentials(StudyTaskManager taskManager) { - taskManager.setUser(new StepicUser()); + private static void removeCredentials() { + StepicUpdateSettings.getInstance().setUser(new StepicUser()); } @Nullable @@ -163,7 +163,7 @@ public class StepicStudyOptions implements StudyOptionsProvider { if (project == null) return false; final StudyTaskManager taskManager = StudyTaskManager.getInstance(project); - final StepicUser user = taskManager.getUser(); + final StepicUser user = StepicUpdateSettings.getInstance().getUser(); return !getLogin().equals(user.getEmail()) || !getPassword().equals(user.getPassword()) diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicUpdateSettings.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicUpdateSettings.java index 241418d0f672..daa80c2ae446 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicUpdateSettings.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicUpdateSettings.java @@ -5,10 +5,12 @@ import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; import com.intellij.util.xmlb.XmlSerializerUtil; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @State(name = "StepicUpdateSettings", storages = @Storage("other.xml")) public class StepicUpdateSettings implements PersistentStateComponent { + private StepicUser myUser = new StepicUser(); public long LAST_TIME_CHECKED = 0; public StepicUpdateSettings() { @@ -37,4 +39,13 @@ public class StepicUpdateSettings implements PersistentStateComponent courses = myGenerator.getCourses(true); if (courses != null && myRefreshCourseList) { diff --git a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java index 988fef5e51cd..02f5ec3169b6 100644 --- a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java +++ b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java @@ -27,6 +27,7 @@ import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; import com.jetbrains.edu.learning.stepic.CourseInfo; import com.jetbrains.edu.learning.stepic.EduStepicConnector; +import com.jetbrains.edu.learning.stepic.StepicUpdateSettings; import com.jetbrains.edu.learning.ui.StudyNewProjectPanel; import com.jetbrains.python.configuration.PyConfigurableInterpreterList; import com.jetbrains.python.newProject.PyNewProjectSettings; @@ -188,7 +189,8 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator { ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); - return StudyUtils.execCancelable(() -> EduStepicConnector.enrollToCourse(course.getId(), myGenerator.myUser)); + return StudyUtils.execCancelable(() -> EduStepicConnector.enrollToCourse(course.getId(), + StepicUpdateSettings.getInstance().getUser())); }, "Creating Course", true, ProjectManager.getInstance().getDefaultProject()); } From c08f73e288fc55924e3d8e3f4571a6d0e711fc19 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Fri, 16 Dec 2016 19:07:27 +0300 Subject: [PATCH 285/558] javac ast indices: get rid of Name#toUtf() usages --- .../CompilerHierarchySearchType.java | 10 +- .../backwardRefs/CompilerReferenceReader.java | 4 +- .../backwardRefs/JavaLightUsageAdapter.java | 19 ++- .../backwardRefs/LanguageLightRefAdapter.java | 6 +- .../compiler/server/BuildManager.java | 5 - .../BackwardReferenceIndexWriter.java | 18 +-- .../jps/backwardRefs/ByteArrayEnumerator.java | 108 ------------------ .../CompilerBackwardReferenceIndex.java | 6 +- .../jps/backwardRefs/NameEnumerator.java | 51 +++++++++ .../backwardRefs/index/CompilerIndices.java | 2 +- .../ast/JavacReferenceCollectorListener.java | 9 +- .../jps/javac/ast/api/JavacFileData.java | 24 +--- .../jetbrains/jps/javac/ast/api/JavacRef.java | 68 ++++++----- .../jps/javac/ast/api/NameTableCache.java | 43 +++++++ .../references/ReferenceIndexTestBase.kt | 13 +-- 15 files changed, 175 insertions(+), 211 deletions(-) delete mode 100644 jps/jps-builders/src/org/jetbrains/jps/backwardRefs/ByteArrayEnumerator.java create mode 100644 jps/jps-builders/src/org/jetbrains/jps/backwardRefs/NameEnumerator.java create mode 100644 jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/NameTableCache.java diff --git a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerHierarchySearchType.java b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerHierarchySearchType.java index 775757bff5ed..cc74b983c227 100644 --- a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerHierarchySearchType.java +++ b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerHierarchySearchType.java @@ -18,7 +18,7 @@ package com.intellij.compiler.backwardRefs; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiNamedElement; import com.intellij.psi.impl.source.PsiFileWithStubSupport; -import org.jetbrains.jps.backwardRefs.ByteArrayEnumerator; +import org.jetbrains.jps.backwardRefs.NameEnumerator; import org.jetbrains.jps.backwardRefs.LightRef; import java.util.Collection; @@ -39,8 +39,8 @@ enum CompilerHierarchySearchType { } @Override - Object[] convertToIds(Collection lightRef, ByteArrayEnumerator byteArrayEnumerator) { - return lightRef.stream().map(r -> byteArrayEnumerator.getName(((LightRef.LightClassHierarchyElementDef)r).getName())).toArray(String[]::new); + Object[] convertToIds(Collection lightRef, NameEnumerator nameEnumerator) { + return lightRef.stream().map(r -> nameEnumerator.getName(((LightRef.LightClassHierarchyElementDef)r).getName())).toArray(String[]::new); } }, FUNCTIONAL_EXPRESSION { @@ -58,7 +58,7 @@ enum CompilerHierarchySearchType { } @Override - Object[] convertToIds(Collection lightRef, ByteArrayEnumerator byteArrayEnumerator) { + Object[] convertToIds(Collection lightRef, NameEnumerator nameEnumerator) { return lightRef.stream().map(r -> ((LightRef.LightFunExprDef) r).getId()).toArray(Integer[]::new); } }; @@ -70,5 +70,5 @@ enum CompilerHierarchySearchType { abstract Class getRequiredClass(LanguageLightRefAdapter adapter); - abstract Object[] convertToIds(Collection lightRef, ByteArrayEnumerator byteArrayEnumerator); + abstract Object[] convertToIds(Collection lightRef, NameEnumerator nameEnumerator); } diff --git a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceReader.java b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceReader.java index 523d1d2fda67..567afec8a78a 100644 --- a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceReader.java +++ b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceReader.java @@ -30,7 +30,7 @@ import gnu.trove.THashSet; import gnu.trove.TIntHashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.jps.backwardRefs.ByteArrayEnumerator; +import org.jetbrains.jps.backwardRefs.NameEnumerator; import org.jetbrains.jps.backwardRefs.CompilerBackwardReferenceIndex; import org.jetbrains.jps.backwardRefs.LightRef; import org.jetbrains.jps.backwardRefs.index.CompilerIndices; @@ -96,7 +96,7 @@ class CompilerReferenceReader { } @NotNull - ByteArrayEnumerator getNameEnumerator() { + NameEnumerator getNameEnumerator() { return myIndex.getByteSeqEum(); } diff --git a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/JavaLightUsageAdapter.java b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/JavaLightUsageAdapter.java index cbb4031165d7..b8a0a45e5a9f 100644 --- a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/JavaLightUsageAdapter.java +++ b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/JavaLightUsageAdapter.java @@ -28,10 +28,9 @@ import com.intellij.psi.util.ClassUtil; import com.intellij.util.ObjectUtils; import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; -import com.sun.tools.javac.util.Convert; import gnu.trove.TIntHashSet; import org.jetbrains.annotations.NotNull; -import org.jetbrains.jps.backwardRefs.ByteArrayEnumerator; +import org.jetbrains.jps.backwardRefs.NameEnumerator; import org.jetbrains.jps.backwardRefs.LightRef; import java.util.ArrayList; @@ -46,7 +45,7 @@ public class JavaLightUsageAdapter implements LanguageLightRefAdapter { } @Override - public LightRef asLightUsage(@NotNull PsiElement element, @NotNull ByteArrayEnumerator names) { + public LightRef asLightUsage(@NotNull PsiElement element, @NotNull NameEnumerator names) { if (mayBeVisibleOutsideOwnerFile(element)) { if (element instanceof PsiField) { final PsiField field = (PsiField)element; @@ -55,7 +54,7 @@ public class JavaLightUsageAdapter implements LanguageLightRefAdapter { final String jvmOwnerName = ClassUtil.getJVMClassName(aClass); final String name = field.getName(); if (name == null || jvmOwnerName == null) return null; - return new LightRef.JavaLightFieldRef(id(jvmOwnerName, names), id(name, names)); + return new LightRef.JavaLightFieldRef(names.enumerate(jvmOwnerName), names.enumerate(name)); } else if (element instanceof PsiMethod) { final PsiClass aClass = ((PsiMethod)element).getContainingClass(); @@ -65,12 +64,12 @@ public class JavaLightUsageAdapter implements LanguageLightRefAdapter { final PsiMethod method = (PsiMethod)element; final String name = method.isConstructor() ? "" : method.getName(); final int parametersCount = method.getParameterList().getParametersCount(); - return new LightRef.JavaLightMethodRef(id(jvmOwnerName, names), id(name, names), parametersCount); + return new LightRef.JavaLightMethodRef(names.enumerate(jvmOwnerName), names.enumerate(name), parametersCount); } else if (element instanceof PsiClass) { final String jvmClassName = ClassUtil.getJVMClassName((PsiClass)element); if (jvmClassName != null) { - return new LightRef.JavaLightClassRef(id(jvmClassName, names)); + return new LightRef.JavaLightClassRef(names.enumerate(jvmClassName)); } } } @@ -81,7 +80,7 @@ public class JavaLightUsageAdapter implements LanguageLightRefAdapter { @Override public List getHierarchyRestrictedToLibraryScope(@NotNull LightRef baseRef, @NotNull PsiElement basePsi, - @NotNull ByteArrayEnumerator names, @NotNull GlobalSearchScope libraryScope) { + @NotNull NameEnumerator names, @NotNull GlobalSearchScope libraryScope) { final PsiClass baseClass = ObjectUtils.notNull(basePsi instanceof PsiClass ? (PsiClass)basePsi : ReadAction.compute(() -> (PsiMember)basePsi).getContainingClass()); final List overridden = new ArrayList<>(); @@ -89,7 +88,7 @@ public class JavaLightUsageAdapter implements LanguageLightRefAdapter { if (c.hasModifierProperty(PsiModifier.PRIVATE)) return true; String qName = ReadAction.compute(() -> c.getQualifiedName()); if (qName == null) return true; - overridden.add(baseRef.override(id(qName, names))); + overridden.add(baseRef.override(names.enumerate(qName))); return true; }; @@ -139,8 +138,4 @@ public class JavaLightUsageAdapter implements LanguageLightRefAdapter { if (((PsiModifierListOwner)element).hasModifierProperty(PsiModifier.PRIVATE)) return false; return true; } - - private static int id(String name, ByteArrayEnumerator names) { - return names.enumerate(Convert.string2utf(name)); - } } diff --git a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/LanguageLightRefAdapter.java b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/LanguageLightRefAdapter.java index fb4ff7f739f4..8fb3dbe46ecc 100644 --- a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/LanguageLightRefAdapter.java +++ b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/LanguageLightRefAdapter.java @@ -22,7 +22,7 @@ import com.intellij.psi.impl.source.PsiFileWithStubSupport; import com.intellij.psi.search.GlobalSearchScope; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.jps.backwardRefs.ByteArrayEnumerator; +import org.jetbrains.jps.backwardRefs.NameEnumerator; import org.jetbrains.jps.backwardRefs.LightRef; import java.util.List; @@ -43,7 +43,7 @@ public interface LanguageLightRefAdapter { * @return */ @Nullable - LightRef asLightUsage(@NotNull PsiElement element, @NotNull ByteArrayEnumerator names); + LightRef asLightUsage(@NotNull PsiElement element, @NotNull NameEnumerator names); /** * @return "hierarchy" of given element inside the libraries scope. @@ -51,7 +51,7 @@ public interface LanguageLightRefAdapter { @NotNull List getHierarchyRestrictedToLibraryScope(@NotNull LightRef baseRef, @NotNull PsiElement basePsi, - @NotNull ByteArrayEnumerator names, + @NotNull NameEnumerator names, @NotNull GlobalSearchScope libraryScope); /** diff --git a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java index 9c53b30da881..a5b5b7a9dc1e 100644 --- a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java +++ b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java @@ -1166,11 +1166,6 @@ public class BuildManager implements Disposable { cmdLine.addParameters(args); } - //TODO[Dmitry Batkovich] should be replaced with the proper solution - if (sdkVersion != null && sdkVersion.isAtLeast(JavaSdkVersion.JDK_1_9)) { - cmdLine.addParameters("--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED"); - } - @SuppressWarnings("UnnecessaryFullyQualifiedName") final Class launcherClass = org.jetbrains.jps.cmdline.Launcher.class; diff --git a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexWriter.java b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexWriter.java index 1b0495bc9078..a175cb12fb16 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexWriter.java +++ b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/BackwardReferenceIndexWriter.java @@ -125,23 +125,23 @@ public class BackwardReferenceIndexWriter { @Nullable LightRef enumerateNames(JavacRef ref) { - ByteArrayEnumerator byteArrayEnumerator = myIndex.getByteSeqEum(); + NameEnumerator nameEnumerator = myIndex.getByteSeqEum(); if (ref instanceof JavacRef.JavacClass) { if (!isPrivate(ref) && !((JavacRef.JavacClass)ref).isAnonymous()) { - return new LightRef.JavaLightClassRef(id(ref, byteArrayEnumerator)); + return new LightRef.JavaLightClassRef(id(ref, nameEnumerator)); } } else { - byte[] ownerName = ref.getOwnerName(); + String ownerName = ref.getOwnerName(); if (isPrivate(ref)) { return null; } if (ref instanceof JavacRef.JavacField) { - return new LightRef.JavaLightFieldRef(id(ownerName, byteArrayEnumerator), id(ref, byteArrayEnumerator)); + return new LightRef.JavaLightFieldRef(id(ownerName, nameEnumerator), id(ref, nameEnumerator)); } else if (ref instanceof JavacRef.JavacMethod) { int paramCount = ((JavacRef.JavacMethod) ref).getParamCount(); - return new LightRef.JavaLightMethodRef(id(ownerName, byteArrayEnumerator), id(ref, byteArrayEnumerator), paramCount); + return new LightRef.JavaLightMethodRef(id(ownerName, nameEnumerator), id(ref, nameEnumerator), paramCount); } else { throw new AssertionError("unexpected symbol: " + ref + " class: " + ref.getClass()); @@ -154,12 +154,12 @@ public class BackwardReferenceIndexWriter { return ref.getModifiers().contains(Modifier.PRIVATE); } - private static int id(JavacRef ref, ByteArrayEnumerator byteArrayEnumerator) { - return id(ref.getName(), byteArrayEnumerator); + private static int id(JavacRef ref, NameEnumerator nameEnumerator) { + return id(ref.getName(), nameEnumerator); } - private static int id(byte[] name, ByteArrayEnumerator byteArrayEnumerator) { - return byteArrayEnumerator.enumerate(name); + private static int id(String name, NameEnumerator nameEnumerator) { + return nameEnumerator.enumerate(name); } private static boolean areAllJavaModulesAffected(CompileContext context) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/ByteArrayEnumerator.java b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/ByteArrayEnumerator.java deleted file mode 100644 index 4854c3fcec41..000000000000 --- a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/ByteArrayEnumerator.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2000-2016 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.jps.backwardRefs; - -import com.intellij.util.ObjectUtils; -import com.intellij.util.io.*; -import com.sun.tools.javac.util.Convert; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.jps.builders.storage.BuildDataCorruptedException; - -import java.io.*; -import java.util.Arrays; - -public class ByteArrayEnumerator extends PersistentEnumeratorDelegate { - @NotNull - private final CachingEnumerator myCache; - - public ByteArrayEnumerator(@NotNull final File file) throws IOException { - super(file, ByteSequenceDataExternalizer.INSTANCE, 1024 * 4, null); - myCache = new CachingEnumerator(new DataEnumerator() { - @Override - public int enumerate(@Nullable byte[] value) throws IOException { - return ByteArrayEnumerator.super.enumerate(value); - } - - @Nullable - @Override - public byte[] valueOf(int idx) throws IOException { - return ByteArrayEnumerator.super.valueOf(idx); - } - }, ByteSequenceDataExternalizer.INSTANCE); - } - - @Override - public synchronized int enumerate(@Nullable byte[] value) { - try { - return myCache.enumerate(value); - } - catch (IOException e) { - throw new BuildDataCorruptedException(e); - } - } - - @Nullable - @Override - public synchronized byte[] valueOf(int idx) throws IOException { - return myCache.valueOf(idx); - } - - @Override - public synchronized void close() throws IOException { - super.close(); - myCache.close(); - } - - @NotNull - public String getName(int idx) { - try { - return Convert.utf2string(ObjectUtils.notNull(valueOf(idx))); - } - catch (IOException e) { - throw new RuntimeException(e); - } - } - - private static class ByteSequenceDataExternalizer implements KeyDescriptor, DifferentSerializableBytesImplyNonEqualityPolicy { - private static final ByteSequenceDataExternalizer INSTANCE = new ByteSequenceDataExternalizer(); - - @Override - public void save(@NotNull DataOutput out, byte[] value) throws IOException { - out.writeInt(value.length); - out.write(value); - } - - @Override - public byte[] read(@NotNull DataInput in) throws IOException { - final int len = in.readInt(); - final byte[] buf = new byte[len]; - in.readFully(buf); - return buf; - } - - @Override - public int getHashCode(byte[] value) { - return Arrays.hashCode(value); - } - - @Override - public boolean isEqual(byte[] val1, byte[] val2) { - return Arrays.equals(val1, val2); - } - } -} - diff --git a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/CompilerBackwardReferenceIndex.java b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/CompilerBackwardReferenceIndex.java index 6625afccacc8..d9fe9f0d9b12 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/CompilerBackwardReferenceIndex.java +++ b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/CompilerBackwardReferenceIndex.java @@ -45,7 +45,7 @@ public class CompilerBackwardReferenceIndex { private static final String VERSION_FILE = "version"; private final Map, InvertedIndex> myIndices; - private final ByteArrayEnumerator myNameEnumerator; + private final NameEnumerator myNameEnumerator; private final PersistentStringEnumerator myFilePathEnumerator; private final File myIndicesDir; private final LowMemoryWatcher myLowMemoryWatcher = LowMemoryWatcher.register(new Runnable() { @@ -87,7 +87,7 @@ public class CompilerBackwardReferenceIndex { myIndices.put(indexExtension.getName(), new CompilerMapReduceIndex(indexExtension, myIndicesDir)); } - myNameEnumerator = new ByteArrayEnumerator(new File(myIndicesDir, NAME_ENUM_TAB)); + myNameEnumerator = new NameEnumerator(new File(myIndicesDir, NAME_ENUM_TAB)); } catch (IOException e) { removeIndexFiles(myIndicesDir); @@ -105,7 +105,7 @@ public class CompilerBackwardReferenceIndex { } @NotNull - public ByteArrayEnumerator getByteSeqEum() { + public NameEnumerator getByteSeqEum() { return myNameEnumerator; } diff --git a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/NameEnumerator.java b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/NameEnumerator.java new file mode 100644 index 000000000000..8dae8ae53a23 --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/NameEnumerator.java @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2016 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.jps.backwardRefs; + +import com.intellij.util.ObjectUtils; +import com.intellij.util.io.PersistentStringEnumerator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.builders.storage.BuildDataCorruptedException; + +import java.io.File; +import java.io.IOException; + +public class NameEnumerator extends PersistentStringEnumerator { + public NameEnumerator(@NotNull File file) throws IOException { + super(file); + } + + @Override + public int enumerate(String value) { + try { + return super.enumerate(value); + } + catch (IOException e) { + throw new BuildDataCorruptedException(e); + } + } + + @NotNull + public String getName(int idx) { + try { + return ObjectUtils.notNull(valueOf(idx)); + } + catch (IOException e) { + throw new RuntimeException(e); + } + } +} + diff --git a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/index/CompilerIndices.java b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/index/CompilerIndices.java index beaa94ca293f..3f4eeda44cfe 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/index/CompilerIndices.java +++ b/jps/jps-builders/src/org/jetbrains/jps/backwardRefs/index/CompilerIndices.java @@ -38,7 +38,7 @@ import java.util.Map; public class CompilerIndices { //TODO manage version separately - public final static int VERSION = 0; + public final static int VERSION = 1; public final static ID BACK_USAGES = ID.create("back.refs"); public final static ID> BACK_HIERARCHY = ID.create("back.hierarchy"); diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java index eae35d8fea7d..9d8fdacf7b14 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/JavacReferenceCollectorListener.java @@ -28,6 +28,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.javac.ast.api.JavacDef; import org.jetbrains.jps.javac.ast.api.JavacFileData; import org.jetbrains.jps.javac.ast.api.JavacRef; +import org.jetbrains.jps.javac.ast.api.NameTableCache; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; @@ -48,6 +49,7 @@ final class JavacReferenceCollectorListener implements TaskListener { private final Elements myElementUtility; private final Types myTypeUtility; private final Trees myTreeUtility; + private final NameTableCache myNameTableCache; private NotNullLazyValue myAsterisk = new NotNullLazyValue() { @NotNull @@ -96,6 +98,7 @@ final class JavacReferenceCollectorListener implements TaskListener { myTypeUtility = typeUtility; myTreeUtility = treeUtility; myAstScanner = JavacTreeRefScanner.createASTScanner(); + myNameTableCache = new NameTableCache(elementUtility); } @Override @@ -161,7 +164,7 @@ final class JavacReferenceCollectorListener implements TaskListener { @Override public JavacRef.JavacElementRefBase asJavacRef(Element element) { - return JavacRef.JavacElementRefBase.fromElement(element, myElementUtility); + return JavacRef.JavacElementRefBase.fromElement(element, myNameTableCache); } @Override @@ -211,7 +214,7 @@ final class JavacReferenceCollectorListener implements TaskListener { // member import for (Element memberElement : myElementUtility.getAllMembers((TypeElement)ownerElement)) { if (memberElement.getSimpleName() == name) { - elements.add(JavacRef.JavacElementRefBase.fromElement(memberElement, myElementUtility)); + elements.add(JavacRef.JavacElementRefBase.fromElement(memberElement, myNameTableCache)); } } } @@ -228,7 +231,7 @@ final class JavacReferenceCollectorListener implements TaskListener { for (Element element = baseImport; element != null && element.getKind() != ElementKind.PACKAGE; element = element.getEnclosingElement()) { - collector.add(JavacRef.JavacElementRefBase.fromElement(element, myElementUtility)); + collector.add(JavacRef.JavacElementRefBase.fromElement(element, myNameTableCache)); } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacFileData.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacFileData.java index 6fb81e630d3a..4528e5c43c66 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacFileData.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacFileData.java @@ -205,17 +205,17 @@ public class JavacFileData { } else if (ref instanceof JavacRef.JavacField) { out.writeByte(FIELD_MARKER); - writeBytes(out, ref.getOwnerName()); + IOUtil.writeUTF(out, ref.getOwnerName()); } else if (ref instanceof JavacRef.JavacMethod) { out.writeByte(METHOD_MARKER); - writeBytes(out, ref.getOwnerName()); + IOUtil.writeUTF(out, ref.getOwnerName()); out.write(((JavacRef.JavacMethod)ref).getParamCount()); } else { throw new IllegalStateException("unknown type: " + ref.getClass()); } writeModifiers(out, ref.getModifiers()); - writeBytes(out, ref.getName()); + IOUtil.writeUTF(out, ref.getName()); } @Override @@ -223,28 +223,16 @@ public class JavacFileData { final byte marker = in.readByte(); switch (marker) { case CLASS_MARKER: - return new JavacRef.JavacClassImpl(in.readBoolean(), readModifiers(in), readBytes(in)); + return new JavacRef.JavacClassImpl(in.readBoolean(), readModifiers(in), IOUtil.readUTF(in)); case METHOD_MARKER: - return new JavacRef.JavacMethodImpl(readBytes(in), in.readByte(), readModifiers(in), readBytes(in)); + return new JavacRef.JavacMethodImpl(IOUtil.readUTF(in), in.readByte(), readModifiers(in), IOUtil.readUTF(in)); case FIELD_MARKER: - return new JavacRef.JavacFieldImpl(readBytes(in), readModifiers(in), readBytes(in)); + return new JavacRef.JavacFieldImpl(IOUtil.readUTF(in), readModifiers(in), IOUtil.readUTF(in)); default: throw new IllegalStateException("unknown marker " + marker); } } - private void writeBytes(DataOutput out, byte[] bytes) throws IOException { - out.writeInt(bytes.length); - out.write(bytes); - } - - private byte[] readBytes(DataInput in) throws IOException { - final int len = in.readInt(); - final byte[] buf = new byte[len]; - in.readFully(buf); - return buf; - } - private void writeModifiers(final DataOutput output, Set modifiers) throws IOException { DataInputOutputUtil.writeSeq(output, modifiers, new ThrowableConsumer() { @Override diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacRef.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacRef.java index cb1228c5105e..6458c8c2b1e9 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacRef.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/JavacRef.java @@ -15,23 +15,21 @@ */ package org.jetbrains.jps.javac.ast.api; -import com.sun.tools.javac.util.Name; import org.jetbrains.annotations.NotNull; import javax.lang.model.element.*; -import javax.lang.model.util.Elements; import java.util.Set; public interface JavacRef { JavacRef[] EMPTY_ARRAY = new JavacRef[0]; @NotNull - byte[] getName(); + String getName(); Set getModifiers(); @NotNull - byte[] getOwnerName(); + String getOwnerName(); interface JavacClass extends JavacRef { boolean isAnonymous(); @@ -45,17 +43,17 @@ public interface JavacRef { } abstract class JavacRefBase implements JavacRef { - private final byte[] myName; + private final String myName; private final Set myModifiers; - protected JavacRefBase(byte[] name, Set modifiers) { + protected JavacRefBase(String name, Set modifiers) { myName = name; myModifiers = modifiers; } @NotNull @Override - public final byte[] getName() { + public final String getName() { return myName; } @@ -68,14 +66,14 @@ public interface JavacRef { class JavacClassImpl extends JavacRefBase implements JavacClass { private boolean myAnonymous; - public JavacClassImpl(boolean anonymous, Set modifiers, byte[] name) { + public JavacClassImpl(boolean anonymous, Set modifiers, String name) { super(name, modifiers); myAnonymous = anonymous; } @NotNull @Override - public byte[] getOwnerName() { + public String getOwnerName() { throw new UnsupportedOperationException(); } @@ -85,10 +83,10 @@ public interface JavacRef { } class JavacMethodImpl extends JavacRefBase implements JavacMethod { - private final byte[] myOwnerName; + private final String myOwnerName; private final byte myParamCount; - public JavacMethodImpl(byte[] ownerName, byte paramCount, Set modifiers, byte[] name) { + public JavacMethodImpl(String ownerName, byte paramCount, Set modifiers, String name) { super(name, modifiers); myOwnerName = ownerName; myParamCount = paramCount; @@ -100,33 +98,33 @@ public interface JavacRef { @NotNull @Override - public byte[] getOwnerName() { + public String getOwnerName() { return myOwnerName; } } class JavacFieldImpl extends JavacRefBase implements JavacField { - private final byte[] myOwnerName; + private final String myOwnerName; - public JavacFieldImpl(byte[] ownerName, Set modifiers, byte[] name) { + public JavacFieldImpl(String ownerName, Set modifiers, String name) { super(name, modifiers); myOwnerName = ownerName; } @NotNull @Override - public byte[] getOwnerName() { + public String getOwnerName() { return myOwnerName; } } abstract class JavacElementRefBase implements JavacRef { protected final @NotNull Element myOriginalElement; - protected final Elements myElementUtility; + protected final NameTableCache myNameTableCache; - protected JavacElementRefBase(@NotNull Element element, Elements elementUtility) { + protected JavacElementRefBase(@NotNull Element element, NameTableCache nameTableCache) { myOriginalElement = element; - myElementUtility = elementUtility; + myNameTableCache = nameTableCache; } @NotNull @@ -136,8 +134,8 @@ public interface JavacRef { @NotNull @Override - public byte[] getName() { - return ((Name) myOriginalElement.getSimpleName()).toUtf(); + public String getName() { + return myNameTableCache.get(myOriginalElement.getSimpleName()); } @Override @@ -147,44 +145,44 @@ public interface JavacRef { @NotNull @Override - public byte[] getOwnerName() { - return ((Name) myElementUtility.getBinaryName(((TypeElement) myOriginalElement.getEnclosingElement()))).toUtf(); + public String getOwnerName() { + return myNameTableCache.get(myNameTableCache.getBinaryName(myOriginalElement.getEnclosingElement())); } - public static JavacElementRefBase fromElement(Element element, Elements elementUtility) { + public static JavacElementRefBase fromElement(Element element, NameTableCache nameTableCache) { if (element instanceof TypeElement) { - return new JavacElementClassImpl(element, elementUtility); + return new JavacElementClassImpl(element, nameTableCache); } else if (element instanceof VariableElement) { - return new JavacElementFieldImpl(element, elementUtility); + return new JavacElementFieldImpl(element, nameTableCache); } else if (element instanceof ExecutableElement) { - return new JavacElementMethodImpl(element, elementUtility); + return new JavacElementMethodImpl(element, nameTableCache); } throw new AssertionError("unexpected element: " + element + " class: " + element.getClass()); } } class JavacElementClassImpl extends JavacElementRefBase implements JavacClass { - public JavacElementClassImpl(@NotNull Element element, Elements elementUtility) { - super(element, elementUtility); + public JavacElementClassImpl(@NotNull Element element, NameTableCache nameTableCache) { + super(element, nameTableCache); } @NotNull @Override - public byte[] getName() { - return ((Name) myElementUtility.getBinaryName(((TypeElement) myOriginalElement))).toUtf(); + public String getName() { + return myNameTableCache.get(myNameTableCache.getBinaryName(myOriginalElement)); } @Override public boolean isAnonymous() { - return ((Name) myOriginalElement.getSimpleName()).isEmpty(); + return myNameTableCache.get(myOriginalElement.getSimpleName()).isEmpty(); } } class JavacElementMethodImpl extends JavacElementRefBase implements JavacMethod { - public JavacElementMethodImpl(@NotNull Element element, Elements elementUtility) { - super(element, elementUtility); + public JavacElementMethodImpl(@NotNull Element element, NameTableCache nameTableCache) { + super(element, nameTableCache); } @Override @@ -194,8 +192,8 @@ public interface JavacRef { } class JavacElementFieldImpl extends JavacElementRefBase implements JavacField { - public JavacElementFieldImpl(@NotNull Element element, Elements elementUtility) { - super(element, elementUtility); + public JavacElementFieldImpl(@NotNull Element element, NameTableCache nameTableCache) { + super(element, nameTableCache); } } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/NameTableCache.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/NameTableCache.java new file mode 100644 index 000000000000..0ffcf53c2c95 --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ast/api/NameTableCache.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2016 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.jps.javac.ast.api; + +import com.intellij.util.containers.SLRUCache; +import org.jetbrains.annotations.NotNull; + +import javax.lang.model.element.Element; +import javax.lang.model.element.Name; +import javax.lang.model.element.TypeElement; +import javax.lang.model.util.Elements; + +public class NameTableCache extends SLRUCache { + private final Elements myElements; + + public NameTableCache(Elements elements) { + super(1000, 1000); + myElements = elements; + } + + @NotNull + @Override + public String createValue(Name name) { + return name.toString(); + } + + public Name getBinaryName(Element element) { + return myElements.getBinaryName((TypeElement)element); + } +} diff --git a/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTestBase.kt b/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTestBase.kt index 131d2c7776a3..5dc1bb86ad66 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTestBase.kt +++ b/jps/jps-builders/testSrc/org/jetbrains/references/ReferenceIndexTestBase.kt @@ -22,9 +22,8 @@ import com.intellij.util.indexing.ID import com.intellij.util.indexing.impl.MapIndexStorage import com.intellij.util.indexing.impl.MapReduceIndex import com.intellij.util.io.PersistentStringEnumerator -import com.sun.tools.javac.util.Convert import org.jetbrains.jps.backwardRefs.BackwardReferenceIndexWriter -import org.jetbrains.jps.backwardRefs.ByteArrayEnumerator +import org.jetbrains.jps.backwardRefs.NameEnumerator import org.jetbrains.jps.backwardRefs.CompilerBackwardReferenceIndex import org.jetbrains.jps.backwardRefs.LightRef import org.jetbrains.jps.backwardRefs.index.CompiledFileData @@ -175,13 +174,13 @@ abstract class ReferenceIndexTestBase : JpsBuildTestCase() { private fun getTestDataPath() = testDataRootPath + "/" + getTestName(true) + "/" - private fun Int.asName(byteArrayEnumerator: ByteArrayEnumerator): String = Convert.utf2string(byteArrayEnumerator.valueOf(this)) + private fun Int.asName(nameEnumerator: NameEnumerator): String = nameEnumerator.getName(this) - private fun LightRef.asText(byteArrayEnumerator: ByteArrayEnumerator): String = + private fun LightRef.asText(nameEnumerator: NameEnumerator): String = when (this) { - is LightRef.JavaLightMethodRef -> "${this.owner.name.asName(byteArrayEnumerator)}.${this.name.asName(byteArrayEnumerator)}(${this.parameterCount})" - is LightRef.JavaLightFieldRef -> "${this.owner.name.asName(byteArrayEnumerator)}.${this.name.asName(byteArrayEnumerator)}" - is LightRef.JavaLightClassRef -> this.name.asName(byteArrayEnumerator) + is LightRef.JavaLightMethodRef -> "${this.owner.name.asName(nameEnumerator)}.${this.name.asName(nameEnumerator)}(${this.parameterCount})" + is LightRef.JavaLightFieldRef -> "${this.owner.name.asName(nameEnumerator)}.${this.name.asName(nameEnumerator)}" + is LightRef.JavaLightClassRef -> this.name.asName(nameEnumerator) is LightRef.JavaLightFunExprDef -> "fun_expr(id=${this.id})" else -> throw UnsupportedOperationException() } From 07a54d49828d6ce2d6a03b7a1baca2e5491d9542 Mon Sep 17 00:00:00 2001 From: Sergey Ignatov Date: Fri, 16 Dec 2016 19:06:46 +0300 Subject: [PATCH 286/558] optimize imports --- .../plugins/groovy/intentions/GrIntentionTestCase.groovy | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/intentions/GrIntentionTestCase.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/intentions/GrIntentionTestCase.groovy index 21062efb9fc3..731aa2f77870 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/intentions/GrIntentionTestCase.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/intentions/GrIntentionTestCase.groovy @@ -16,22 +16,18 @@ package org.jetbrains.plugins.groovy.intentions - import com.intellij.codeInsight.intention.IntentionAction import com.intellij.codeInspection.LocalInspectionTool import com.intellij.openapi.util.text.StringUtil import com.intellij.psi.impl.source.PostprocessReformattingAspect import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase -import com.intellij.util.Function import org.jetbrains.annotations.NotNull import org.jetbrains.annotations.Nullable -import org.jetbrains.plugins.groovy.intentions.base.Intention /** * @author Maxim.Medvedev */ abstract class GrIntentionTestCase extends LightCodeInsightFixtureTestCase { - protected final String myHint private final Class[] myInspections From 802463b8cab489130993996d7c2196c04d864e26 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Fri, 16 Dec 2016 19:30:39 +0300 Subject: [PATCH 287/558] enroll to all courses created from stepic --- .../edu/learning/PyStudyDirectoryProjectGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java index 02f5ec3169b6..40b306cb8d01 100644 --- a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java +++ b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java @@ -186,7 +186,7 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator enrolledCoursesIds = myGenerator.getEnrolledCoursesIds(); final CourseInfo course = (CourseInfo)mySettingsPanel.getCoursesComboBox().getSelectedItem(); if (course == null) return true; - if (course.isAdaptive() && !enrolledCoursesIds.contains(course.getId())) { + if (course.getId() > 0 && !enrolledCoursesIds.contains(course.getId())) { ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> { ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); return StudyUtils.execCancelable(() -> EduStepicConnector.enrollToCourse(course.getId(), From 63d59ec6540e44aa1f63d726047d835500b2ed00 Mon Sep 17 00:00:00 2001 From: Alexander Lobas Date: Fri, 16 Dec 2016 19:29:22 +0300 Subject: [PATCH 288/558] IDEA-157763 Settings repository for IDE: new UI (after review) --- .../ide/cloudConfig/CloudConfigProvider.java | 6 +-- .../application/ConfigImportSettings.java | 4 -- .../openapi/application/ImportOldConfigs.form | 41 ++++++++----------- .../application/ImportOldConfigsPanel.java | 6 +-- .../wm/impl/status/IdeStatusBarImpl.java | 6 +++ .../src/messages/ApplicationBundle.properties | 9 ++-- 6 files changed, 29 insertions(+), 43 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ide/cloudConfig/CloudConfigProvider.java b/platform/platform-impl/src/com/intellij/ide/cloudConfig/CloudConfigProvider.java index f15b2d21cd0b..68ab8d587b4e 100644 --- a/platform/platform-impl/src/com/intellij/ide/cloudConfig/CloudConfigProvider.java +++ b/platform/platform-impl/src/com/intellij/ide/cloudConfig/CloudConfigProvider.java @@ -16,8 +16,6 @@ package com.intellij.ide.cloudConfig; import com.intellij.ide.customize.AbstractCustomizeWizardStep; -import com.intellij.openapi.application.ConfigImportSettings; -import com.intellij.openapi.application.ImportOldConfigsPanel; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,9 +38,7 @@ public abstract class CloudConfigProvider { myProvider = provider; } - public abstract void initConfigsPanel(@NotNull ImportOldConfigsPanel dialog, - @NotNull JPanel parentPanel, - @NotNull ConfigImportSettings settings); + public abstract void initConfigsPanel(@NotNull ButtonGroup group, @NotNull JRadioButton customButton); public abstract void importFinished(@NotNull File newConfigDir); diff --git a/platform/platform-impl/src/com/intellij/openapi/application/ConfigImportSettings.java b/platform/platform-impl/src/com/intellij/openapi/application/ConfigImportSettings.java index 005c006d1717..f0679ab459a1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/ConfigImportSettings.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/ConfigImportSettings.java @@ -68,10 +68,6 @@ public class ConfigImportSettings { return ApplicationBundle.message("error.please.select.previous.installation.home", productWithVendor); } - protected String getHomeLabel(String productName) { - return ApplicationBundle.message("editbox.installation.home", productName); - } - protected String getAutoImportLabel(File guessedOldConfig) { String path = FileUtil.getLocationRelativeToUserHome(guessedOldConfig.getAbsolutePath()); return ApplicationBundle.message("radio.import.auto", path); diff --git a/platform/platform-impl/src/com/intellij/openapi/application/ImportOldConfigs.form b/platform/platform-impl/src/com/intellij/openapi/application/ImportOldConfigs.form index 18257e1ee507..c5228575629c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/ImportOldConfigs.form +++ b/platform/platform-impl/src/com/intellij/openapi/application/ImportOldConfigs.form @@ -1,6 +1,6 @@
    - + @@ -11,7 +11,7 @@ - + @@ -38,10 +38,10 @@ - + - + @@ -65,7 +65,7 @@ - + @@ -73,17 +73,9 @@ - - - - - - - - - + @@ -110,12 +102,21 @@ - + + + + + + + + + + @@ -123,16 +124,6 @@ - - - - - - - - - - diff --git a/platform/platform-impl/src/com/intellij/openapi/application/ImportOldConfigsPanel.java b/platform/platform-impl/src/com/intellij/openapi/application/ImportOldConfigsPanel.java index 94fc8f51c524..3268c429f3e9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/ImportOldConfigsPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/ImportOldConfigsPanel.java @@ -44,9 +44,8 @@ public class ImportOldConfigsPanel extends JDialog { private File myLastSelection = null; private JButton myOkButton; private JLabel mySuggestLabel; - private JLabel myHomeLabel; private JRadioButton myRbImportAuto; - private JPanel myCustomPanel; + private JRadioButton myCustomButton; private final File myGuessedOldConfig; private final ConfigImportSettings mySettings; @@ -77,7 +76,6 @@ public class ImportOldConfigsPanel extends JDialog { else { myRbImportAuto.setVisible(false); } - myHomeLabel.setText(mySettings.getHomeLabel(productName)); myRbImport.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { @@ -124,7 +122,7 @@ public class ImportOldConfigsPanel extends JDialog { CloudConfigProvider configProvider = CloudConfigProvider.getProvider(); if (configProvider != null) { - configProvider.initConfigsPanel(this, myCustomPanel, mySettings); + configProvider.initConfigsPanel(group, myCustomButton); } getContentPane().setLayout(new BorderLayout()); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java index c59481010d32..6cc30d2d4357 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java @@ -693,6 +693,12 @@ public class IdeStatusBarImpl extends JComponent implements Accessible, StatusBa return bean == null ? null : bean.widget; } + @Nullable + public JComponent getWidgetComponent(@NotNull String id) { + WidgetBean bean = myWidgetMap.get(id); + return bean == null ? null : bean.component; + } + private interface StatusBarWrapper { void beforeUpdate(); } diff --git a/platform/platform-resources-en/src/messages/ApplicationBundle.properties b/platform/platform-resources-en/src/messages/ApplicationBundle.properties index dceef907270b..b3aee7b69b06 100644 --- a/platform/platform-resources-en/src/messages/ApplicationBundle.properties +++ b/platform/platform-resources-en/src/messages/ApplicationBundle.properties @@ -8,12 +8,11 @@ error.please.select.previous.installation.home=Please select previous {0} config error.selected.current.installation.home=You have selected current {0} installation home.\nPlease select previous {1} installation home error.does.not.appear.to.be.installation.home={0} does not appear to be {1} config folder or installation home error.no.read.permissions=You do not have read permissions to {0} -radio.do.not.import=I do not have a previous version of {0} or I do not want to import my settings -radio.import.auto=I want to import my settings from a previous version ({0}) -radio.import=I want to import my settings from a custom location -label.you.can.import=You can import your settings from a previous version of {0}. +radio.do.not.import=Do not import settings +radio.import.auto=Previous version ({0}) +radio.import=Custom location. Config folder or installation home of the previous version: +label.you.can.import=Import {0} settings from: button.ok=OK -editbox.installation.home=Specify config folder or installation home of the previous version of {0}: title.code.completion=Code Completion editbox.lookup.height=Lookup height (items): editbox.autopopup.javadoc.in.ms=Autopopup documentation in (ms): From 1ef364b71e82b4e16e33da0b4748577172896a57 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Fri, 16 Dec 2016 19:35:43 +0300 Subject: [PATCH 289/558] do not try to enroll to course if not logged in --- .../com/jetbrains/edu/learning/stepic/EduStepicConnector.java | 1 + 1 file changed, 1 insertion(+) diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java index 09c252976b9c..3af448d0da98 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java @@ -44,6 +44,7 @@ public class EduStepicConnector { } public static boolean enrollToCourse(final int courseId, final StepicUser stepicUser) { + if (stepicUser.getAccessToken() == null) return false; HttpPost post = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.ENROLLMENTS); try { final StepicWrappers.EnrollmentWrapper enrollment = new StepicWrappers.EnrollmentWrapper(String.valueOf(courseId)); From e8a961d6a6fcedd16f0f08859641b7596517091f Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Fri, 16 Dec 2016 19:36:02 +0300 Subject: [PATCH 290/558] disable testing from samples by default --- .../src/com/jetbrains/edu/learning/StudyTaskManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java index 79fd21fd15f0..574981ead8dd 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java @@ -43,7 +43,7 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb public boolean myShouldUseJavaFx = StudyUtils.hasJavaFx(); private StudyToolWindow.StudyToolWindowMode myToolWindowMode = StudyToolWindow.StudyToolWindowMode.TEXT; private boolean myTurnEditingMode = false; - private boolean myEnableTestingFromSamples = true; + private boolean myEnableTestingFromSamples = false; @Transient private final Project myProject; From 3e86b9ba7d1438096667ccc26fb57776d9f01875 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Fri, 16 Dec 2016 20:11:11 +0300 Subject: [PATCH 291/558] Remove unused method with no external usages --- .../vcs-api/src/com/intellij/openapi/vcs/AbstractVcs.java | 5 ----- plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java | 5 ----- 2 files changed, 10 deletions(-) diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/AbstractVcs.java b/platform/vcs-api/src/com/intellij/openapi/vcs/AbstractVcs.java index c674ae10e064..2dccde71dd14 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/AbstractVcs.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/AbstractVcs.java @@ -498,11 +498,6 @@ public abstract class AbstractVcs extends S return VcsType.centralized; } - // todo ? - public boolean checkImmediateParentsBeforeCommit() { - return false; - } - @Nullable protected VcsOutgoingChangesProvider getOutgoingProviderImpl() { return null; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java index 3adfbdd707bd..7216b5d072dc 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java @@ -261,11 +261,6 @@ public class SvnVcs extends AbstractVcs { } } - @Override - public boolean checkImmediateParentsBeforeCommit() { - return true; - } - private void upgradeIfNeeded(final MessageBus bus) { final MessageBusConnection connection = bus.connect(); connection.subscribe(ChangeListManagerImpl.LISTS_LOADED, new LocalChangeListsLoadedListener() { From 349772dadd3e978510da53d150b30bcd7f2b2b70 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Mon, 7 Nov 2016 18:24:04 +0300 Subject: [PATCH 292/558] [vcs-log] add setting for filtering by regular expression - add setting for regex filtering; - disable regex filtering by default; - add action into Quick Settings. IDEA-146772 --- .../intellij/vcs/log/VcsLogTextFilter.java | 7 +++ .../src/com/intellij/vcs/log/VcsLogUi.java | 4 ++ .../vcs-log/impl/src/META-INF/vcs-log.xml | 4 ++ .../vcs/log/data/VcsLogUiProperties.java | 4 ++ .../vcs/log/data/VcsLogUiPropertiesImpl.java | 11 +++++ .../log/data/index/VcsLogPersistentIndex.java | 16 ++++--- .../com/intellij/vcs/log/ui/VcsLogUiImpl.java | 11 +++++ .../ui/actions/EnableFilterByRegexAction.java | 48 +++++++++++++++++++ .../log/ui/filter/VcsLogClassicFilterUi.java | 13 +++-- .../log/ui/filter/VcsLogTextFilterImpl.java | 23 ++++++++- .../src/git4idea/log/GitBekParentFixer.java | 5 ++ 11 files changed, 133 insertions(+), 13 deletions(-) create mode 100644 platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/EnableFilterByRegexAction.java diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogTextFilter.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogTextFilter.java index e6270b973869..a611989a4fc6 100644 --- a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogTextFilter.java +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogTextFilter.java @@ -27,4 +27,11 @@ public interface VcsLogTextFilter extends VcsLogDetailsFilter { */ @NotNull String getText(); + + /** + * Check whether this pattern represents a regular expression. + * + * @return true if this pattern represents a regular expression, false otherwise. + */ + boolean isRegex(); } diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogUi.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogUi.java index ed8f1ece332f..bb0024da26b4 100644 --- a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogUi.java +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogUi.java @@ -62,4 +62,8 @@ public interface VcsLogUi { boolean isShowDetails(); void setShowDetails(boolean showDetails); + + void setFilterByRegexEnabled(boolean enable); + + boolean isFilterByRegexEnabled(); } diff --git a/platform/vcs-log/impl/src/META-INF/vcs-log.xml b/platform/vcs-log/impl/src/META-INF/vcs-log.xml index 84503696cf03..083be62383ab 100644 --- a/platform/vcs-log/impl/src/META-INF/vcs-log.xml +++ b/platform/vcs-log/impl/src/META-INF/vcs-log.xml @@ -61,9 +61,12 @@ + + @@ -103,6 +106,7 @@ + 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 95c2b3e191cb..c0bd8e961827 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 @@ -64,4 +64,8 @@ public interface VcsLogUiProperties { boolean isShowTagNames(); void setShowTagNames(boolean showTags); + + boolean isFilterByRegexEnabled(); + + void setFilterByRegexEnabled(boolean enabled); } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiPropertiesImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiPropertiesImpl.java index b5749b638a36..a170a13afb65 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiPropertiesImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiPropertiesImpl.java @@ -39,6 +39,7 @@ public abstract class VcsLogUiPropertiesImpl implements PersistentStateComponent public Map> FILTERS = ContainerUtil.newTreeMap(); public boolean COMPACT_REFERENCES_VIEW = true; public boolean SHOW_TAG_NAMES = false; + public boolean TEXT_FILTER_BY_REGEX = false; } @NotNull @@ -175,6 +176,16 @@ public abstract class VcsLogUiPropertiesImpl implements PersistentStateComponent getState().SHOW_TAG_NAMES = showTags; } + @Override + public boolean isFilterByRegexEnabled() { + return getState().TEXT_FILTER_BY_REGEX; + } + + @Override + public void setFilterByRegexEnabled(boolean enabled) { + getState().TEXT_FILTER_BY_REGEX = enabled; + } + public static class UserGroup { public List users = new ArrayList<>(); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java index 87ce6b3e4e63..edfa5212a131 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java @@ -38,7 +38,6 @@ import com.intellij.vcs.log.*; import com.intellij.vcs.log.data.*; import com.intellij.vcs.log.impl.FatalErrorHandler; import com.intellij.vcs.log.impl.VcsLogUserFilterImpl; -import com.intellij.vcs.log.impl.VcsLogUtil; import com.intellij.vcs.log.util.PersistentSet; import com.intellij.vcs.log.util.PersistentSetImpl; import com.intellij.vcs.log.util.StopWatch; @@ -55,6 +54,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; import java.util.stream.IntStream; import static com.intellij.vcs.log.data.index.VcsLogFullDetailsIndex.INDEX; @@ -261,12 +261,16 @@ public class VcsLogPersistentIndex implements VcsLogIndex, Disposable { } @NotNull - public TIntHashSet filterMessages(@NotNull String text) { + public TIntHashSet filterMessages(@NotNull String text, boolean isRegex) { if (myIndexStorage != null) { try { - if (VcsLogUtil.isRegexp(text)) { - Pattern pattern = Pattern.compile(text); - return filter(myIndexStorage.messages, message -> pattern.matcher(message).find()); + if (isRegex) { + try { + Pattern pattern = Pattern.compile(text, Pattern.CASE_INSENSITIVE); + return filter(myIndexStorage.messages, message -> pattern.matcher(message).find()); + } + catch (PatternSyntaxException ignored) { + } } else { TIntHashSet commitsForSearch = myIndexStorage.trigrams.getCommitsForSubstring(text); @@ -324,7 +328,7 @@ public class VcsLogPersistentIndex implements VcsLogIndex, Disposable { TIntHashSet filteredByMessage = null; if (textFilter != null) { - filteredByMessage = filterMessages(textFilter.getText()); + filteredByMessage = filterMessages(textFilter.getText(), textFilter.isRegex()); } TIntHashSet filteredByUser = null; diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUiImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUiImpl.java index 7812686d9145..48cefc9508a2 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUiImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUiImpl.java @@ -214,6 +214,17 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable { myMainFrame.getGraphTable().setShowTagNames(show); } + @Override + public void setFilterByRegexEnabled(boolean enable) { + myUiProperties.setFilterByRegexEnabled(enable); + applyFiltersAndUpdateUi(myMainFrame.getFilterUi().getFilters()); + } + + @Override + public boolean isFilterByRegexEnabled() { + return myUiProperties.isFilterByRegexEnabled(); + } + @NotNull public Future jumpToCommit(@NotNull Hash commitHash, @NotNull VirtualFile root) { SettableFuture future = SettableFuture.create(); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/EnableFilterByRegexAction.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/EnableFilterByRegexAction.java new file mode 100644 index 000000000000..306bc0e6efc3 --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/EnableFilterByRegexAction.java @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.vcs.log.ui.actions; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.project.DumbAware; +import com.intellij.vcs.log.VcsLogDataKeys; +import com.intellij.vcs.log.VcsLogUi; +import org.jetbrains.annotations.NotNull; + +public class EnableFilterByRegexAction extends ToggleAction implements DumbAware { + + @Override + public boolean isSelected(AnActionEvent e) { + VcsLogUi ui = e.getData(VcsLogDataKeys.VCS_LOG_UI); + if (ui == null) return false; + return ui.isFilterByRegexEnabled(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + VcsLogUi ui = e.getData(VcsLogDataKeys.VCS_LOG_UI); + if (ui != null) { + ui.setFilterByRegexEnabled(state); + } + } + + @Override + public void update(@NotNull AnActionEvent e) { + e.getPresentation().setEnabledAndVisible(e.getData(VcsLogDataKeys.VCS_LOG_UI) != null); + + super.update(e); + } +} 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 5d2a94c9efa7..14f42547ee1c 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 @@ -130,7 +130,8 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi { @Override public VcsLogFilterCollection getFilters() { ApplicationManager.getApplication().assertIsDispatchThread(); - Pair filtersFromText = getFiltersFromTextArea(myTextFilterModel.getFilter()); + Pair filtersFromText = + getFiltersFromTextArea(myTextFilterModel.getFilter(), myUiProperties.isFilterByRegexEnabled()); return new VcsLogFilterCollectionBuilder().with(myBranchFilterModel.getFilter()) .with(myUserFilterModel.getFilter()) .with(filtersFromText.second) @@ -145,7 +146,8 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi { } @NotNull - private static Pair getFiltersFromTextArea(@Nullable VcsLogTextFilter filter) { + private static Pair getFiltersFromTextArea(@Nullable VcsLogTextFilter filter, + boolean isRegexAllowed) { if (filter == null) { return Pair.empty(); } @@ -170,7 +172,7 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi { hashFilter = new VcsLogHashFilterImpl(hashes); } else { - textFilter = new VcsLogTextFilterImpl(text); + textFilter = new VcsLogTextFilterImpl(text, isRegexAllowed); hashFilter = null; } return Pair.create(textFilter, hashFilter); @@ -282,7 +284,8 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi { @NotNull @Override protected VcsLogTextFilter createFilter(@NotNull List values) { - return new VcsLogTextFilterImpl(ObjectUtils.assertNotNull(ContainerUtil.getFirstItem(values))); + return new VcsLogTextFilterImpl(ObjectUtils.assertNotNull(ContainerUtil.getFirstItem(values)), + myUiProperties.isFilterByRegexEnabled()); } @NotNull @@ -459,7 +462,7 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi { } protected void applyFilter() { - myTextFilterModel.setFilter(new VcsLogTextFilterImpl(getText())); + myTextFilterModel.setFilter(new VcsLogTextFilterImpl(getText(), myTextFilterModel.myUiProperties.isFilterByRegexEnabled())); addCurrentTextToHistory(); } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogTextFilterImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogTextFilterImpl.java index 010eb8835b6a..715c7e026a30 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogTextFilterImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogTextFilterImpl.java @@ -18,18 +18,32 @@ package com.intellij.vcs.log.ui.filter; import com.intellij.vcs.log.VcsCommitMetadata; import com.intellij.vcs.log.VcsLogDetailsFilter; import com.intellij.vcs.log.VcsLogTextFilter; +import com.intellij.vcs.log.impl.VcsLogUtil; import org.jetbrains.annotations.NotNull; +import java.util.regex.Pattern; + public class VcsLogTextFilterImpl implements VcsLogDetailsFilter, VcsLogTextFilter { @NotNull private final String myText; + private final boolean myIsRegexAllowed; - public VcsLogTextFilterImpl(@NotNull String text) { + public VcsLogTextFilterImpl(@NotNull String text, boolean isRegexAllowed) { myText = text; + myIsRegexAllowed = isRegexAllowed; + } + + // used in upsource + @SuppressWarnings("unused") + public VcsLogTextFilterImpl(@NotNull String text) { + this(text, false); } @Override public boolean matches(@NotNull VcsCommitMetadata details) { + if (isRegex()) { + return Pattern.compile(myText, Pattern.CASE_INSENSITIVE).matcher(details.getFullMessage()).find(); + } return details.getFullMessage().toLowerCase().contains(myText.toLowerCase()); } @@ -39,8 +53,13 @@ public class VcsLogTextFilterImpl implements VcsLogDetailsFilter, VcsLogTextFilt return myText; } + @Override + public boolean isRegex() { + return myIsRegexAllowed && VcsLogUtil.isRegexp(myText); + } + @Override public String toString() { - return "containing " + myText; + return (isRegex() ? "matching " : "containing ") + myText; } } diff --git a/plugins/git4idea/src/git4idea/log/GitBekParentFixer.java b/plugins/git4idea/src/git4idea/log/GitBekParentFixer.java index 665fc66143e4..e93341125ca2 100644 --- a/plugins/git4idea/src/git4idea/log/GitBekParentFixer.java +++ b/plugins/git4idea/src/git4idea/log/GitBekParentFixer.java @@ -89,6 +89,11 @@ class GitBekParentFixer { private static VcsLogFilterCollection createVcsLogFilterCollection() { final VcsLogTextFilter textFilter = new VcsLogTextFilter() { + @Override + public boolean isRegex() { + return false; + } + @NotNull @Override public String getText() { From 25966509934984d55c96851a2ca188d81e97d4d4 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Tue, 15 Nov 2016 19:42:38 +0300 Subject: [PATCH 293/558] [vcs-log] move text filter settings to a separate gear menu near the text filter itself --- .../vcs-log/impl/src/META-INF/vcs-log.xml | 10 ++- .../vcs/log/ui/VcsLogActionPlaces.java | 2 + .../log/ui/actions/VcsLogGearActionGroup.java | 63 +++++++++++++++++++ .../actions/VcsLogQuickSettingsActions.java | 39 +----------- .../VcsLogTextFilterSettingsActions.java | 25 ++++++++ .../intellij/vcs/log/ui/frame/MainFrame.java | 2 + 6 files changed, 103 insertions(+), 38 deletions(-) create mode 100644 platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/VcsLogGearActionGroup.java create mode 100644 platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/VcsLogTextFilterSettingsActions.java diff --git a/platform/vcs-log/impl/src/META-INF/vcs-log.xml b/platform/vcs-log/impl/src/META-INF/vcs-log.xml index 083be62383ab..a0064b1ec992 100644 --- a/platform/vcs-log/impl/src/META-INF/vcs-log.xml +++ b/platform/vcs-log/impl/src/META-INF/vcs-log.xml @@ -37,6 +37,8 @@ text="Go To Hash/Branch/Tag" description="Specify hash or name of a branch or a tag to navigate to the commit it points"/> + @@ -62,11 +64,10 @@ text="Show Commit Tooltip" description="Show tooltip for currently selected commit in the Log" use-shortcut-of="QuickJavaDoc"/> + text="Regex"/> - @@ -76,6 +77,9 @@ + + + @@ -106,6 +110,8 @@ + + diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogActionPlaces.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogActionPlaces.java index fcb970e0fde5..106b917ca709 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogActionPlaces.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogActionPlaces.java @@ -19,9 +19,11 @@ public class VcsLogActionPlaces { public static final String POPUP_ACTION_GROUP = "Vcs.Log.ContextMenu"; public static final String TOOLBAR_ACTION_GROUP = "Vcs.Log.Toolbar.Internal"; public static final String SETTINGS_ACTION_GROUP = "Vcs.Log.Settings"; + public static final String TEXT_FILTER_SETTINGS_ACTION_GROUP = "Vcs.Log.TextFilterSettings"; public static final String VCS_LOG_TABLE_PLACE = "Vcs.Log.ContextMenu"; public static final String VCS_LOG_INTELLI_SORT_ACTION = "Vcs.Log.IntelliSortChooser"; public static final String VCS_LOG_QUICK_SETTINGS_ACTION = "Vcs.Log.QuickSettings"; + public static final String VCS_LOG_TEXT_FILTER_SETTINGS_ACTION = "Vcs.Log.QuickTextFilterSettings"; public static final String VCS_LOG_FOCUS_TEXT_FILTER = "Vcs.Log.FocusTextFilter"; public static final String VCS_LOG_SHOW_DETAILS_ACTION = "Vcs.Log.ShowDetailsAction"; } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/VcsLogGearActionGroup.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/VcsLogGearActionGroup.java new file mode 100644 index 000000000000..57302ec94c1f --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/VcsLogGearActionGroup.java @@ -0,0 +1,63 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.vcs.log.ui.actions; + +import com.intellij.openapi.actionSystem.ActionButtonComponent; +import com.intellij.openapi.actionSystem.ActionManager; +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.JBPopupFactory; +import com.intellij.openapi.ui.popup.ListPopup; +import com.intellij.openapi.wm.impl.content.ToolWindowContentUi; +import com.intellij.vcs.log.VcsLogDataKeys; +import com.intellij.vcs.log.VcsLogUi; +import org.jetbrains.annotations.NotNull; + +import java.awt.*; + +public class VcsLogGearActionGroup extends DumbAwareAction { + @NotNull + private final String myActionGroup; + + public VcsLogGearActionGroup(@NotNull String group) { + myActionGroup = group; + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + DefaultActionGroup group = new DefaultActionGroup(ActionManager.getInstance().getAction(myActionGroup)); + + ListPopup popup = JBPopupFactory.getInstance() + .createActionGroupPopup(null, group, e.getDataContext(), JBPopupFactory.ActionSelectionAid.MNEMONICS, true, + ToolWindowContentUi.POPUP_PLACE); + Component component = e.getInputEvent().getComponent(); + if (component instanceof ActionButtonComponent) { + popup.showUnderneathOf(component); + } + else { + popup.showInCenterOf(component); + } + } + + @Override + public void update(@NotNull AnActionEvent e) { + Project project = e.getProject(); + VcsLogUi logUi = e.getData(VcsLogDataKeys.VCS_LOG_UI); + e.getPresentation().setEnabledAndVisible(project != null && logUi != null); + } +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/VcsLogQuickSettingsActions.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/VcsLogQuickSettingsActions.java index f785f06e11c6..4fb220beb85a 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/VcsLogQuickSettingsActions.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/VcsLogQuickSettingsActions.java @@ -15,44 +15,11 @@ */ package com.intellij.vcs.log.ui.actions; -import com.intellij.openapi.actionSystem.ActionButtonComponent; -import com.intellij.openapi.actionSystem.ActionManager; -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.JBPopupFactory; -import com.intellij.openapi.ui.popup.ListPopup; -import com.intellij.openapi.wm.impl.content.ToolWindowContentUi; -import com.intellij.vcs.log.VcsLogDataKeys; -import com.intellij.vcs.log.VcsLogUi; import com.intellij.vcs.log.ui.VcsLogActionPlaces; -import org.jetbrains.annotations.NotNull; -import java.awt.*; +public class VcsLogQuickSettingsActions extends VcsLogGearActionGroup { -public class VcsLogQuickSettingsActions extends DumbAwareAction { - - @Override - public void actionPerformed(@NotNull AnActionEvent e) { - DefaultActionGroup group = new DefaultActionGroup(ActionManager.getInstance().getAction(VcsLogActionPlaces.SETTINGS_ACTION_GROUP)); - - ListPopup popup = JBPopupFactory.getInstance() - .createActionGroupPopup(null, group, e.getDataContext(), JBPopupFactory.ActionSelectionAid.MNEMONICS, true, - ToolWindowContentUi.POPUP_PLACE); - Component component = e.getInputEvent().getComponent(); - if (component instanceof ActionButtonComponent) { - popup.showUnderneathOf(component); - } - else { - popup.showInCenterOf(component); - } - } - - @Override - public void update(@NotNull AnActionEvent e) { - Project project = e.getProject(); - VcsLogUi logUi = e.getData(VcsLogDataKeys.VCS_LOG_UI); - e.getPresentation().setEnabledAndVisible(project != null && logUi != null); + public VcsLogQuickSettingsActions() { + super(VcsLogActionPlaces.SETTINGS_ACTION_GROUP); } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/VcsLogTextFilterSettingsActions.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/VcsLogTextFilterSettingsActions.java new file mode 100644 index 000000000000..0768c1619235 --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/VcsLogTextFilterSettingsActions.java @@ -0,0 +1,25 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.vcs.log.ui.actions; + +import com.intellij.vcs.log.ui.VcsLogActionPlaces; + +public class VcsLogTextFilterSettingsActions extends VcsLogGearActionGroup { + + public VcsLogTextFilterSettingsActions() { + super(VcsLogActionPlaces.TEXT_FILTER_SETTINGS_ACTION_GROUP); + } +} 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 7a535666ad9a..8fe93ac3c2d6 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 @@ -199,6 +199,8 @@ public class MainFrame extends JPanel implements DataProvider, Disposable { toolbarGroup.add(ActionManager.getInstance().getAction(VcsLogActionPlaces.TOOLBAR_ACTION_GROUP)); DefaultActionGroup mainGroup = new DefaultActionGroup(); + mainGroup.add(ActionManager.getInstance().getAction(VcsLogActionPlaces.VCS_LOG_TEXT_FILTER_SETTINGS_ACTION)); + mainGroup.add(new Separator()); mainGroup.add(myFilterUi.createActionGroup()); mainGroup.addSeparator(); if (BekUtil.isBekEnabled()) { From 9b448aa8be7a770933392aa3edff005bda78f0f0 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Tue, 15 Nov 2016 20:04:32 +0300 Subject: [PATCH 294/558] [vcs-log] create a TextFilterSettings class for storing text filter settings --- .../vcs/log/data/VcsLogUiProperties.java | 1 - .../vcs/log/data/VcsLogUiPropertiesImpl.java | 28 +++++++++++++++++-- 2 files changed, 25 insertions(+), 4 deletions(-) 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 c0bd8e961827..29f43f073d2e 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 @@ -18,7 +18,6 @@ package com.intellij.vcs.log.data; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Collection; import java.util.List; public interface VcsLogUiProperties { diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiPropertiesImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiPropertiesImpl.java index a170a13afb65..e4899a2b890c 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiPropertiesImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiPropertiesImpl.java @@ -39,7 +39,7 @@ public abstract class VcsLogUiPropertiesImpl implements PersistentStateComponent public Map> FILTERS = ContainerUtil.newTreeMap(); public boolean COMPACT_REFERENCES_VIEW = true; public boolean SHOW_TAG_NAMES = false; - public boolean TEXT_FILTER_BY_REGEX = false; + public TextFilterSettings TEXT_FILTER_SETTINGS = new TextFilterSettings(); } @NotNull @@ -178,12 +178,18 @@ public abstract class VcsLogUiPropertiesImpl implements PersistentStateComponent @Override public boolean isFilterByRegexEnabled() { - return getState().TEXT_FILTER_BY_REGEX; + return getTextFilterSettings().REGEX; + } + + @NotNull + private TextFilterSettings getTextFilterSettings() { + if (getState().TEXT_FILTER_SETTINGS == null) getState().TEXT_FILTER_SETTINGS = new TextFilterSettings(); + return getState().TEXT_FILTER_SETTINGS; } @Override public void setFilterByRegexEnabled(boolean enabled) { - getState().TEXT_FILTER_BY_REGEX = enabled; + getState().TEXT_FILTER_SETTINGS = getTextFilterSettings().withRegex(enabled); } public static class UserGroup { @@ -203,4 +209,20 @@ public abstract class VcsLogUiPropertiesImpl implements PersistentStateComponent return users.hashCode(); } } + + public static class TextFilterSettings { + public boolean REGEX = false; + + public TextFilterSettings(boolean isFilterByRegexEnabled) { + this.REGEX = isFilterByRegexEnabled; + } + + public TextFilterSettings() { + this(false); + } + + public TextFilterSettings withRegex(boolean isRegex) { + return new TextFilterSettings(isRegex); + } + } } From 56a16b6378834e12843823569a6659caee993f6b Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Tue, 15 Nov 2016 23:03:05 +0300 Subject: [PATCH 295/558] [vcs-log] add "Match Case" option to text filter --- .../intellij/vcs/log/VcsLogTextFilter.java | 7 +++ .../src/com/intellij/vcs/log/VcsLogUi.java | 13 ++++- .../vcs-log/impl/src/META-INF/vcs-log.xml | 4 ++ .../vcs/log/data/VcsLogUiProperties.java | 6 +-- .../vcs/log/data/VcsLogUiPropertiesImpl.java | 49 +++++++++++-------- .../log/data/index/VcsLogPersistentIndex.java | 17 +++++-- .../com/intellij/vcs/log/ui/VcsLogUiImpl.java | 39 +++++++++++---- .../ui/actions/EnableFilterByRegexAction.java | 4 +- .../log/ui/actions/EnableMatchCaseAction.java | 48 ++++++++++++++++++ .../log/ui/filter/VcsLogClassicFilterUi.java | 16 ++++-- .../log/ui/filter/VcsLogTextFilterImpl.java | 24 +++++++-- .../src/git4idea/log/GitBekParentFixer.java | 5 ++ 12 files changed, 182 insertions(+), 50 deletions(-) create mode 100644 platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/EnableMatchCaseAction.java diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogTextFilter.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogTextFilter.java index a611989a4fc6..d23a588fa2f2 100644 --- a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogTextFilter.java +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogTextFilter.java @@ -34,4 +34,11 @@ public interface VcsLogTextFilter extends VcsLogDetailsFilter { * @return true if this pattern represents a regular expression, false otherwise. */ boolean isRegex(); + + /** + * Check whether the filter should be case sensitive. + * + * @return true if case sensitive, false otherwise. + */ + boolean matchesCase(); } diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogUi.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogUi.java index bb0024da26b4..f2e55d2f78e0 100644 --- a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogUi.java +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogUi.java @@ -63,7 +63,16 @@ public interface VcsLogUi { void setShowDetails(boolean showDetails); - void setFilterByRegexEnabled(boolean enable); + @NotNull + TextFilterSettings getTextFilterSettings(); - boolean isFilterByRegexEnabled(); + interface TextFilterSettings { + boolean isFilterByRegexEnabled(); + + void setFilterByRegexEnabled(boolean enabled); + + boolean isMatchCaseEnabled(); + + void setMatchCaseEnabled(boolean enabled); + } } diff --git a/platform/vcs-log/impl/src/META-INF/vcs-log.xml b/platform/vcs-log/impl/src/META-INF/vcs-log.xml index a0064b1ec992..eeb288d36925 100644 --- a/platform/vcs-log/impl/src/META-INF/vcs-log.xml +++ b/platform/vcs-log/impl/src/META-INF/vcs-log.xml @@ -65,6 +65,8 @@ use-shortcut-of="QuickJavaDoc"/> + @@ -79,6 +81,7 @@ + @@ -113,6 +116,7 @@ + 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 29f43f073d2e..d4b1d2f39c73 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 @@ -15,6 +15,7 @@ */ package com.intellij.vcs.log.data; +import com.intellij.vcs.log.VcsLogUi; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -64,7 +65,6 @@ public interface VcsLogUiProperties { void setShowTagNames(boolean showTags); - boolean isFilterByRegexEnabled(); - - void setFilterByRegexEnabled(boolean enabled); + @NotNull + VcsLogUi.TextFilterSettings getTextFilterSettings(); } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiPropertiesImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiPropertiesImpl.java index e4899a2b890c..3a8a1ff9b25c 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiPropertiesImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiPropertiesImpl.java @@ -17,6 +17,7 @@ package com.intellij.vcs.log.data; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.util.containers.ContainerUtil; +import com.intellij.vcs.log.VcsLogUi; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -39,7 +40,7 @@ public abstract class VcsLogUiPropertiesImpl implements PersistentStateComponent public Map> FILTERS = ContainerUtil.newTreeMap(); public boolean COMPACT_REFERENCES_VIEW = true; public boolean SHOW_TAG_NAMES = false; - public TextFilterSettings TEXT_FILTER_SETTINGS = new TextFilterSettings(); + public TextFilterSettingsImpl TEXT_FILTER_SETTINGS = new TextFilterSettingsImpl(); } @NotNull @@ -176,22 +177,12 @@ public abstract class VcsLogUiPropertiesImpl implements PersistentStateComponent getState().SHOW_TAG_NAMES = showTags; } - @Override - public boolean isFilterByRegexEnabled() { - return getTextFilterSettings().REGEX; - } - @NotNull - private TextFilterSettings getTextFilterSettings() { - if (getState().TEXT_FILTER_SETTINGS == null) getState().TEXT_FILTER_SETTINGS = new TextFilterSettings(); + public TextFilterSettingsImpl getTextFilterSettings() { + if (getState().TEXT_FILTER_SETTINGS == null) getState().TEXT_FILTER_SETTINGS = new TextFilterSettingsImpl(); return getState().TEXT_FILTER_SETTINGS; } - @Override - public void setFilterByRegexEnabled(boolean enabled) { - getState().TEXT_FILTER_SETTINGS = getTextFilterSettings().withRegex(enabled); - } - public static class UserGroup { public List users = new ArrayList<>(); @@ -210,19 +201,37 @@ public abstract class VcsLogUiPropertiesImpl implements PersistentStateComponent } } - public static class TextFilterSettings { + public static class TextFilterSettingsImpl implements VcsLogUi.TextFilterSettings { public boolean REGEX = false; + public boolean MATCH_CASE = false; - public TextFilterSettings(boolean isFilterByRegexEnabled) { - this.REGEX = isFilterByRegexEnabled; + public TextFilterSettingsImpl(boolean isFilterByRegexEnabled, boolean isMatchCaseEnabled) { + REGEX = isFilterByRegexEnabled; + MATCH_CASE = isMatchCaseEnabled; } - public TextFilterSettings() { - this(false); + public TextFilterSettingsImpl() { + this(false, false); } - public TextFilterSettings withRegex(boolean isRegex) { - return new TextFilterSettings(isRegex); + @Override + public boolean isFilterByRegexEnabled() { + return REGEX; + } + + @Override + public void setFilterByRegexEnabled(boolean enabled) { + REGEX = enabled; + } + + @Override + public boolean isMatchCaseEnabled() { + return MATCH_CASE; + } + + @Override + public void setMatchCaseEnabled(boolean enabled) { + MATCH_CASE = enabled; } } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java index edfa5212a131..81bc6ef9852c 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java @@ -261,12 +261,18 @@ public class VcsLogPersistentIndex implements VcsLogIndex, Disposable { } @NotNull - public TIntHashSet filterMessages(@NotNull String text, boolean isRegex) { + public TIntHashSet filterMessages(@NotNull String text, boolean isRegex, boolean isMatchCase) { if (myIndexStorage != null) { try { if (isRegex) { try { - Pattern pattern = Pattern.compile(text, Pattern.CASE_INSENSITIVE); + Pattern pattern; + if (isMatchCase) { + pattern = Pattern.compile(text); + } + else { + pattern = Pattern.compile(text, Pattern.CASE_INSENSITIVE); + } return filter(myIndexStorage.messages, message -> pattern.matcher(message).find()); } catch (PatternSyntaxException ignored) { @@ -280,7 +286,7 @@ public class VcsLogPersistentIndex implements VcsLogIndex, Disposable { try { String value = myIndexStorage.messages.get(commit); if (value != null) { - if (StringUtil.containsIgnoreCase(value, text)) { + if (isMatchCase ? StringUtil.contains(value, text) : StringUtil.containsIgnoreCase(value, text)) { result.add(commit); } } @@ -299,7 +305,8 @@ public class VcsLogPersistentIndex implements VcsLogIndex, Disposable { myFatalErrorsConsumer.consume(this, e); } - return filter(myIndexStorage.messages, message -> StringUtil.containsIgnoreCase(message, text)); + return filter(myIndexStorage.messages, + message -> isMatchCase ? StringUtil.contains(message, text) : StringUtil.containsIgnoreCase(message, text)); } return EmptyIntHashSet.INSTANCE; @@ -328,7 +335,7 @@ public class VcsLogPersistentIndex implements VcsLogIndex, Disposable { TIntHashSet filteredByMessage = null; if (textFilter != null) { - filteredByMessage = filterMessages(textFilter.getText(), textFilter.isRegex()); + filteredByMessage = filterMessages(textFilter.getText(), textFilter.isRegex(), textFilter.matchesCase()); } TIntHashSet filteredByUser = null; diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUiImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUiImpl.java index 48cefc9508a2..051e6195edd0 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUiImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUiImpl.java @@ -45,7 +45,8 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable { @NotNull private final VcsLogFilterer myFilterer; @NotNull private final Collection myLogListeners = ContainerUtil.newArrayList(); - private final VisiblePackChangeListener myVisiblePackChangeListener; + @NotNull private final VisiblePackChangeListener myVisiblePackChangeListener; + @NotNull private final MyTextFilterSettings myTextFilterSettings; @NotNull private VisiblePack myVisiblePack; @@ -63,6 +64,7 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable { myLog = new VcsLogImpl(logData, this); myVisiblePack = VisiblePack.EMPTY; myMainFrame = new MainFrame(logData, this, project, uiProperties, myLog, myVisiblePack); + myTextFilterSettings = new MyTextFilterSettings(); for (VcsLogHighlighterFactory factory : Extensions.getExtensions(LOG_HIGHLIGHTER_FACTORY_EP, myProject)) { getTable().addHighlighter(factory.createHighlighter(logData, this)); @@ -214,15 +216,10 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable { myMainFrame.getGraphTable().setShowTagNames(show); } + @NotNull @Override - public void setFilterByRegexEnabled(boolean enable) { - myUiProperties.setFilterByRegexEnabled(enable); - applyFiltersAndUpdateUi(myMainFrame.getFilterUi().getFilters()); - } - - @Override - public boolean isFilterByRegexEnabled() { - return myUiProperties.isFilterByRegexEnabled(); + public TextFilterSettings getTextFilterSettings() { + return myTextFilterSettings; } @NotNull @@ -367,4 +364,28 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable { getTable().removeAllHighlighters(); myVisiblePack = VisiblePack.EMPTY; } + + private class MyTextFilterSettings implements TextFilterSettings { + @Override + public boolean isFilterByRegexEnabled() { + return myUiProperties.getTextFilterSettings().isFilterByRegexEnabled(); + } + + @Override + public void setFilterByRegexEnabled(boolean enabled) { + myUiProperties.getTextFilterSettings().setFilterByRegexEnabled(enabled); + applyFiltersAndUpdateUi(myMainFrame.getFilterUi().getFilters()); + } + + @Override + public boolean isMatchCaseEnabled() { + return myUiProperties.getTextFilterSettings().isMatchCaseEnabled(); + } + + @Override + public void setMatchCaseEnabled(boolean enabled) { + myUiProperties.getTextFilterSettings().setMatchCaseEnabled(enabled); + applyFiltersAndUpdateUi(myMainFrame.getFilterUi().getFilters()); + } + } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/EnableFilterByRegexAction.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/EnableFilterByRegexAction.java index 306bc0e6efc3..0651073f797a 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/EnableFilterByRegexAction.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/EnableFilterByRegexAction.java @@ -28,14 +28,14 @@ public class EnableFilterByRegexAction extends ToggleAction implements DumbAware public boolean isSelected(AnActionEvent e) { VcsLogUi ui = e.getData(VcsLogDataKeys.VCS_LOG_UI); if (ui == null) return false; - return ui.isFilterByRegexEnabled(); + return ui.getTextFilterSettings().isFilterByRegexEnabled(); } @Override public void setSelected(AnActionEvent e, boolean state) { VcsLogUi ui = e.getData(VcsLogDataKeys.VCS_LOG_UI); if (ui != null) { - ui.setFilterByRegexEnabled(state); + ui.getTextFilterSettings().setFilterByRegexEnabled(state); } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/EnableMatchCaseAction.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/EnableMatchCaseAction.java new file mode 100644 index 000000000000..8d3a1d8e43bc --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/EnableMatchCaseAction.java @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.vcs.log.ui.actions; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.project.DumbAware; +import com.intellij.vcs.log.VcsLogDataKeys; +import com.intellij.vcs.log.VcsLogUi; +import org.jetbrains.annotations.NotNull; + +public class EnableMatchCaseAction extends ToggleAction implements DumbAware { + + @Override + public boolean isSelected(AnActionEvent e) { + VcsLogUi ui = e.getData(VcsLogDataKeys.VCS_LOG_UI); + if (ui == null) return false; + return ui.getTextFilterSettings().isMatchCaseEnabled(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + VcsLogUi ui = e.getData(VcsLogDataKeys.VCS_LOG_UI); + if (ui != null) { + ui.getTextFilterSettings().setMatchCaseEnabled(state); + } + } + + @Override + public void update(@NotNull AnActionEvent e) { + e.getPresentation().setEnabledAndVisible(e.getData(VcsLogDataKeys.VCS_LOG_UI) != null); + + super.update(e); + } +} 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 14f42547ee1c..56c62ebf9fad 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 @@ -131,7 +131,8 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi { public VcsLogFilterCollection getFilters() { ApplicationManager.getApplication().assertIsDispatchThread(); Pair filtersFromText = - getFiltersFromTextArea(myTextFilterModel.getFilter(), myUiProperties.isFilterByRegexEnabled()); + getFiltersFromTextArea(myTextFilterModel.getFilter(), myUiProperties.getTextFilterSettings().isFilterByRegexEnabled(), + myUiProperties.getTextFilterSettings().isMatchCaseEnabled()); return new VcsLogFilterCollectionBuilder().with(myBranchFilterModel.getFilter()) .with(myUserFilterModel.getFilter()) .with(filtersFromText.second) @@ -147,7 +148,8 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi { @NotNull private static Pair getFiltersFromTextArea(@Nullable VcsLogTextFilter filter, - boolean isRegexAllowed) { + boolean isRegexAllowed, + boolean matchesCase) { if (filter == null) { return Pair.empty(); } @@ -172,7 +174,7 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi { hashFilter = new VcsLogHashFilterImpl(hashes); } else { - textFilter = new VcsLogTextFilterImpl(text, isRegexAllowed); + textFilter = new VcsLogTextFilterImpl(text, isRegexAllowed, matchesCase); hashFilter = null; } return Pair.create(textFilter, hashFilter); @@ -285,7 +287,8 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi { @Override protected VcsLogTextFilter createFilter(@NotNull List values) { return new VcsLogTextFilterImpl(ObjectUtils.assertNotNull(ContainerUtil.getFirstItem(values)), - myUiProperties.isFilterByRegexEnabled()); + myUiProperties.getTextFilterSettings().isFilterByRegexEnabled(), + myUiProperties.getTextFilterSettings().isMatchCaseEnabled()); } @NotNull @@ -462,7 +465,10 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi { } protected void applyFilter() { - myTextFilterModel.setFilter(new VcsLogTextFilterImpl(getText(), myTextFilterModel.myUiProperties.isFilterByRegexEnabled())); + myTextFilterModel.setFilter(new VcsLogTextFilterImpl(getText(), + myTextFilterModel.myUiProperties.getTextFilterSettings() + .isFilterByRegexEnabled(), + myTextFilterModel.myUiProperties.getTextFilterSettings().isMatchCaseEnabled())); addCurrentTextToHistory(); } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogTextFilterImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogTextFilterImpl.java index 715c7e026a30..b5074ad0a819 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogTextFilterImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogTextFilterImpl.java @@ -27,23 +27,34 @@ public class VcsLogTextFilterImpl implements VcsLogDetailsFilter, VcsLogTextFilt @NotNull private final String myText; private final boolean myIsRegexAllowed; + private final boolean myMatchCase; - public VcsLogTextFilterImpl(@NotNull String text, boolean isRegexAllowed) { + public VcsLogTextFilterImpl(@NotNull String text, boolean isRegexAllowed, boolean matchCase) { myText = text; myIsRegexAllowed = isRegexAllowed; + myMatchCase = matchCase; } // used in upsource @SuppressWarnings("unused") public VcsLogTextFilterImpl(@NotNull String text) { - this(text, false); + this(text, false, false); } @Override public boolean matches(@NotNull VcsCommitMetadata details) { if (isRegex()) { - return Pattern.compile(myText, Pattern.CASE_INSENSITIVE).matcher(details.getFullMessage()).find(); + Pattern pattern; + if (myMatchCase) { + pattern = Pattern.compile(myText); + } + else { + pattern = Pattern.compile(myText, Pattern.CASE_INSENSITIVE); + } + return pattern.matcher(details.getFullMessage()).find(); } + + if (myMatchCase) return details.getFullMessage().contains(myText); return details.getFullMessage().toLowerCase().contains(myText.toLowerCase()); } @@ -58,8 +69,13 @@ public class VcsLogTextFilterImpl implements VcsLogDetailsFilter, VcsLogTextFilt return myIsRegexAllowed && VcsLogUtil.isRegexp(myText); } + @Override + public boolean matchesCase() { + return myMatchCase; + } + @Override public String toString() { - return (isRegex() ? "matching " : "containing ") + myText; + return (isRegex() ? "matching " : "containing ") + myText + " (case " + (myMatchCase ? "sensitive" : "insensitive") + ")"; } } diff --git a/plugins/git4idea/src/git4idea/log/GitBekParentFixer.java b/plugins/git4idea/src/git4idea/log/GitBekParentFixer.java index e93341125ca2..9e38ce925dd0 100644 --- a/plugins/git4idea/src/git4idea/log/GitBekParentFixer.java +++ b/plugins/git4idea/src/git4idea/log/GitBekParentFixer.java @@ -89,6 +89,11 @@ class GitBekParentFixer { private static VcsLogFilterCollection createVcsLogFilterCollection() { final VcsLogTextFilter textFilter = new VcsLogTextFilter() { + @Override + public boolean matchesCase() { + return false; + } + @Override public boolean isRegex() { return false; From 1f2dc75b069609cb63f1f278e7657b77350faf46 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Wed, 16 Nov 2016 20:50:27 +0300 Subject: [PATCH 296/558] [git] add test for text filtering with different combinations of "Regex" and "Match Case" options --- .../git4idea/log/GitLogProviderTest.java | 61 +++++++++++++++---- 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/plugins/git4idea/tests/git4idea/log/GitLogProviderTest.java b/plugins/git4idea/tests/git4idea/log/GitLogProviderTest.java index 21a48d40c5f5..2fd71b223f96 100644 --- a/plugins/git4idea/tests/git4idea/log/GitLogProviderTest.java +++ b/plugins/git4idea/tests/git4idea/log/GitLogProviderTest.java @@ -26,13 +26,14 @@ import com.intellij.vcs.log.*; import com.intellij.vcs.log.data.VcsLogBranchFilterImpl; import com.intellij.vcs.log.impl.*; import com.intellij.vcs.log.impl.VcsLogFilterCollectionImpl.VcsLogFilterCollectionBuilder; +import com.intellij.vcs.log.ui.filter.VcsLogTextFilterImpl; import com.intellij.vcsUtil.VcsFileUtil; import git4idea.test.GitSingleRepoTest; import git4idea.test.GitTestUtil; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.io.IOException; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Set; @@ -60,7 +61,8 @@ public class GitLogProviderTest extends GitSingleRepoTest { List expectedLogWithoutTaggedBranch = log(); createTaggedBranch(); - VcsLogProvider.DetailedLogData block = myLogProvider.readFirstBlock(myProjectRoot,new RequirementsImpl(1000, false, Collections.emptySet())); + VcsLogProvider.DetailedLogData block = + myLogProvider.readFirstBlock(myProjectRoot, new RequirementsImpl(1000, false, Collections.emptySet())); assertOrderedEquals(block.getCommits(), expectedLogWithoutTaggedBranch); } @@ -139,23 +141,60 @@ public class GitLogProviderTest extends GitSingleRepoTest { } public void test_filter_by_branch() throws Exception { - List hashes = generateHistoryForFilters(true); + List hashes = generateHistoryForFilters(true, false); VcsLogBranchFilter branchFilter = VcsLogBranchFilterImpl.fromBranch("feature"); - List actualHashes = getFilteredHashes(branchFilter, null); + List actualHashes = getFilteredHashes(new VcsLogFilterCollectionBuilder().with(branchFilter).build()); assertEquals(hashes, actualHashes); } public void test_filter_by_branch_and_user() throws Exception { - List hashes = generateHistoryForFilters(false); + List hashes = generateHistoryForFilters(false, false); VcsLogBranchFilter branchFilter = VcsLogBranchFilterImpl.fromBranch("feature"); VcsUserImpl user = new VcsUserImpl(GitTestUtil.USER_NAME, GitTestUtil.USER_EMAIL); VcsLogUserFilter userFilter = new VcsLogUserFilterImpl(singleton(GitTestUtil.USER_NAME), Collections.emptyMap(), singleton(user)); - List actualHashes = getFilteredHashes(branchFilter, userFilter); + List actualHashes = getFilteredHashes(new VcsLogFilterCollectionBuilder().with(branchFilter).with(userFilter).build()); assertEquals(hashes, actualHashes); } + public void test_filter_by_text() throws Exception { + String initial = last(); + + String fileName = "f"; + + touch(fileName, "content" + Math.random()); + String smallBrackets = addCommit("[git] " + fileName); + touch(fileName, "content" + Math.random()); + String bigBrackets = addCommit("[GIT] " + fileName); + touch(fileName, "content" + Math.random()); + String smallNoBrackets = addCommit("git " + fileName); + touch(fileName, "content" + Math.random()); + String bigNoBrackets = addCommit("GIT " + fileName); + + String text = "[git]"; + assertEquals(Collections.singletonList(smallBrackets), getFilteredHashes( + new VcsLogFilterCollectionBuilder().with(new VcsLogTextFilterImpl(text, false, true)).build())); + assertEquals(Arrays.asList(bigBrackets, smallBrackets), getFilteredHashes( + new VcsLogFilterCollectionBuilder().with(new VcsLogTextFilterImpl(text, false, false)).build())); + assertEquals(Arrays.asList(bigNoBrackets, smallNoBrackets, bigBrackets, smallBrackets, initial), + getFilteredHashes(new VcsLogFilterCollectionBuilder().with(new VcsLogTextFilterImpl(text, true, false)).build())); + assertEquals(Arrays.asList(smallNoBrackets, smallBrackets, initial), + getFilteredHashes(new VcsLogFilterCollectionBuilder().with(new VcsLogTextFilterImpl(text, true, true)).build())); + } + + public void test_filter_by_text_and_user() throws Exception { + List hashes = generateHistoryForFilters(false, true); + VcsUserImpl user = new VcsUserImpl(GitTestUtil.USER_NAME, GitTestUtil.USER_EMAIL); + VcsLogUserFilter userFilter = new VcsLogUserFilterImpl(singleton(GitTestUtil.USER_NAME), + Collections.emptyMap(), + singleton(user)); + assertEquals(hashes, getFilteredHashes( + new VcsLogFilterCollectionBuilder().with(userFilter).with(new VcsLogTextFilterImpl("", false, false)).build())); + assertEquals(hashes, getFilteredHashes( + new VcsLogFilterCollectionBuilder().with(userFilter).with(new VcsLogTextFilterImpl("", true, false)).build())); + } + public void test_short_details() throws Exception { prepareLongHistory(VcsFileUtil.FILE_PATH_LIMIT * 2 / 40); List log = log(); @@ -204,9 +243,10 @@ public class GitLogProviderTest extends GitSingleRepoTest { /** * Generates some history with two branches: master and feature, and made by two users. * Returns hashes of this history filtered by the given parameters: - * @param takeAllUsers if true, don't filter by users, otherwise filter by default user. + * + * @param takeAllUsers if true, don't filter by users, otherwise filter by default user. */ - private List generateHistoryForFilters(boolean takeAllUsers) { + private List generateHistoryForFilters(boolean takeAllUsers, boolean allBranches) { List hashes = ContainerUtil.newArrayList(); hashes.add(last()); @@ -223,6 +263,7 @@ public class GitLogProviderTest extends GitSingleRepoTest { hashes.add(commitOnlyInFeature); git("checkout master"); String commitOnlyInMaster = tac("master.txt"); + if (allBranches) hashes.add(commitOnlyInMaster); Collections.reverse(hashes); refresh(); @@ -230,9 +271,7 @@ public class GitLogProviderTest extends GitSingleRepoTest { } @NotNull - private List getFilteredHashes(@Nullable VcsLogBranchFilter branchFilter, - @Nullable VcsLogUserFilter userFilter) throws VcsException { - VcsLogFilterCollection filters = new VcsLogFilterCollectionBuilder().with(branchFilter).with(userFilter).build(); + private List getFilteredHashes(@NotNull VcsLogFilterCollection filters) throws VcsException { List commits = myLogProvider.getCommitsMatchingFilter(myProjectRoot, filters, -1); return ContainerUtil.map(commits, commit -> commit.getId().asString()); } From d6b7e5022ea3679bfb60511187236a415fefbe3f Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Wed, 16 Nov 2016 20:51:06 +0300 Subject: [PATCH 297/558] [git] git log provider recognizes "Regex" and "Match case" options in text filter --- .../src/git4idea/log/GitLogProvider.java | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/plugins/git4idea/src/git4idea/log/GitLogProvider.java b/plugins/git4idea/src/git4idea/log/GitLogProvider.java index 9aaa0dc9dc22..09abfd74aec3 100644 --- a/plugins/git4idea/src/git4idea/log/GitLogProvider.java +++ b/plugins/git4idea/src/git4idea/log/GitLogProvider.java @@ -444,16 +444,6 @@ public class GitLogProvider implements VcsLogProvider { filterParameters.addAll(GitHistoryUtils.LOG_ALL); } - if (filterCollection.getUserFilter() != null) { - List authors = ContainerUtil.map(filterCollection.getUserFilter().getUserNames(root), UserNameRegex.BASIC_INSTANCE); - if (GitVersionSpecialty.LOG_AUTHOR_FILTER_SUPPORTS_VERTICAL_BAR.existsIn(myVcs.getVersion())) { - filterParameters.add(prepareParameter("author", StringUtil.join(authors, "\\|"))); - } - else { - filterParameters.addAll(authors.stream().map(a -> prepareParameter("author", a)).collect(Collectors.toList())); - } - } - if (filterCollection.getDateFilter() != null) { // assuming there is only one date filter, until filter expressions are defined VcsLogDateFilter filter = filterCollection.getDateFilter(); @@ -465,12 +455,35 @@ public class GitLogProvider implements VcsLogProvider { } } + boolean regexp = true; + boolean caseSensitive = false; if (filterCollection.getTextFilter() != null) { + regexp = filterCollection.getTextFilter().isRegex(); + caseSensitive = filterCollection.getTextFilter().matchesCase(); String textFilter = filterCollection.getTextFilter().getText(); - filterParameters.add(prepareParameter("grep", StringUtil.escapeChars(textFilter, '[', ']'))); + filterParameters.add(prepareParameter("grep", textFilter)); + } + filterParameters.add(regexp ? "--extended-regexp" : "--fixed-strings"); + if (!caseSensitive) { + filterParameters.add("--regexp-ignore-case"); // affects case sensitivity of any filter (except file filter) + } + + if (filterCollection.getUserFilter() != null) { + Collection names = filterCollection.getUserFilter().getUserNames(root); + if (regexp) { + List authors = ContainerUtil.map(names, UserNameRegex.EXTENDED_INSTANCE); + if (GitVersionSpecialty.LOG_AUTHOR_FILTER_SUPPORTS_VERTICAL_BAR.existsIn(myVcs.getVersion())) { + filterParameters.add(prepareParameter("author", StringUtil.join(authors, "|"))); + } + else { + filterParameters.addAll(authors.stream().map(a -> prepareParameter("author", a)).collect(Collectors.toList())); + } + } + else { + filterParameters.addAll(ContainerUtil.map(names, a -> prepareParameter("author", StringUtil.escapeBackSlashes(a)))); + } } - filterParameters.add("--regexp-ignore-case"); // affects case sensitivity of any filter (except file filter) if (maxCount > 0) { filterParameters.add(prepareParameter("max-count", String.valueOf(maxCount))); } From 3d1ea33bb47d648d40e66ea7cc525781ff35d970 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Wed, 16 Nov 2016 23:32:16 +0300 Subject: [PATCH 298/558] [hg] add log text filter test with regex and matching case --- .../hg4idea/test/log/HgTextFilterTest.java | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 plugins/hg4idea/testSrc/hg4idea/test/log/HgTextFilterTest.java diff --git a/plugins/hg4idea/testSrc/hg4idea/test/log/HgTextFilterTest.java b/plugins/hg4idea/testSrc/hg4idea/test/log/HgTextFilterTest.java new file mode 100644 index 000000000000..affa188d8469 --- /dev/null +++ b/plugins/hg4idea/testSrc/hg4idea/test/log/HgTextFilterTest.java @@ -0,0 +1,109 @@ +/* + * Copyright 2000-2016 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 hg4idea.test.log; + +import com.intellij.openapi.vcs.VcsException; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.vcs.log.TimedVcsCommit; +import com.intellij.vcs.log.impl.VcsLogFilterCollectionImpl; +import com.intellij.vcs.log.ui.filter.VcsLogTextFilterImpl; +import hg4idea.test.HgPlatformTest; +import org.jetbrains.annotations.NotNull; +import org.zmlx.hg4idea.command.HgWorkingCopyRevisionsCommand; +import org.zmlx.hg4idea.log.HgLogProvider; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static com.intellij.openapi.vcs.Executor.cd; +import static com.intellij.openapi.vcs.Executor.overwrite; +import static hg4idea.test.HgExecutor.hg; +import static hg4idea.test.log.HgUserFilterTest.findLogProvider; + +public class HgTextFilterTest extends HgPlatformTest { + @Override + public void setUp() throws Exception { + super.setUp(); + cd(myProject.getBaseDir()); + } + + public void testSubstringCaseSensitivity() throws Exception { + String smallBrackets = commit("[hg]"); + String bigBrackets = commit("[HG]"); + String smallNoBrackets = commit("letter h"); + String bigNoBrackets = commit("letter G"); + + HgLogProvider provider = findLogProvider(myProject); + String text = "[hg]"; + + assertSameElements(Arrays.asList(bigBrackets, smallBrackets), + getFilteredCommits(provider, new VcsLogTextFilterImpl(text, false, false))); + assertSameElements(Collections.singletonList(smallBrackets), + getFilteredCommits(provider, new VcsLogTextFilterImpl(text, false, true))); + assertSameElements(Arrays.asList(bigNoBrackets, smallNoBrackets, bigBrackets, smallBrackets), + getFilteredCommits(provider, new VcsLogTextFilterImpl(text, true, false))); + } + + public void testRegexp() throws Exception { + String numberedBigBug = commit("Bug 12345"); + commit("bug 12345"); + commit("just a bug"); + String bigBug = commit("not just a bug, but a BUG"); + commit("that\\047s nothing"); + + HgLogProvider provider = findLogProvider(myProject); + + assertSameElements(Collections.singletonList(numberedBigBug), + getFilteredCommits(provider, new VcsLogTextFilterImpl("Bug \\d+", true, true))); + assertSameElements(Collections.singletonList(bigBug), + getFilteredCommits(provider, new VcsLogTextFilterImpl("BUG.*", true, true))); + } + + public void _testRegexpCaseInsensitive() throws Exception { + String numberedBigBug = commit("Bug 12345"); + String numberedSmallBug = commit("bug 12345"); + String smallBug = commit("just a bug"); + String bigBug = commit("not just a bug, but a BUG"); + commit("that\\047s nothing"); + + HgLogProvider provider = findLogProvider(myProject); + + assertSameElements(Arrays.asList(numberedSmallBug, numberedBigBug), + getFilteredCommits(provider, new VcsLogTextFilterImpl("Bug \\d+", true, false))); + assertSameElements(Arrays.asList(numberedBigBug, numberedSmallBug, smallBug, bigBug), + getFilteredCommits(provider, new VcsLogTextFilterImpl("BUG.*", true, false))); + } + + @NotNull + private List getFilteredCommits(@NotNull HgLogProvider provider, @NotNull VcsLogTextFilterImpl filter) throws VcsException { + VcsLogFilterCollectionImpl filterCollection = new VcsLogFilterCollectionImpl(null, null, null, null, + filter, null, null); + List commits = provider.getCommitsMatchingFilter(myProject.getBaseDir(), filterCollection, -1); + return ContainerUtil.map(commits, commit -> commit.getId().asString()); + } + + @NotNull + private String commit(@NotNull String message) throws IOException { + String file = "file.txt"; + overwrite(file, "content" + Math.random()); + myProject.getBaseDir().refresh(false, true); + hg("add " + file); + hg("commit -m '" + message + "'"); + return new HgWorkingCopyRevisionsCommand(myProject).tip(myProject.getBaseDir()).getChangeset(); + } +} From e2e62000370dca11dd9b03b5260f18dce41b8540 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Wed, 16 Nov 2016 23:33:05 +0300 Subject: [PATCH 299/558] [hg] partially support regular expressions and matching case options in hg log Case-insensitive regular expressions do not work. --- .../src/org/zmlx/hg4idea/log/HgLogProvider.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java b/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java index 36b0f8226d4d..5cba891ac3cf 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java @@ -275,7 +275,17 @@ public class HgLogProvider implements VcsLogProvider { if (filterCollection.getTextFilter() != null) { String textFilter = filterCollection.getTextFilter().getText(); - filterParameters.add(HgHistoryUtil.prepareParameter("keyword", textFilter)); + if (filterCollection.getTextFilter().isRegex()) { + filterParameters.add("-r"); + filterParameters.add("grep(r'" + textFilter + "')"); + } + else if (filterCollection.getTextFilter().matchesCase()) { + filterParameters.add("-r"); + filterParameters.add("grep(r'" + StringUtil.escapeChars(textFilter, UserNameRegex.EXTENDED_REGEX_CHARS) + "')"); + } + else { + filterParameters.add(HgHistoryUtil.prepareParameter("keyword", textFilter)); + } } if (filterCollection.getStructureFilter() != null) { From b716a35f9128d4ac4ebba25f8fd1c8d5ad0567fe Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Thu, 17 Nov 2016 18:42:41 +0300 Subject: [PATCH 300/558] [vcs-log] disable "Match Case" when "Regex" is enabled and VCS does not support that If some vcs-es have support, and others do not, show that in action presentation. --- .../intellij/vcs/log/VcsLogProperties.java | 1 + .../log/ui/actions/EnableMatchCaseAction.java | 34 ++++++++++++++++++- .../org/zmlx/hg4idea/log/HgLogProvider.java | 3 ++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogProperties.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogProperties.java index caf064c515da..07c7d0f74d23 100644 --- a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogProperties.java +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogProperties.java @@ -28,6 +28,7 @@ public class VcsLogProperties { @NotNull public static final VcsLogProperty LIGHTWEIGHT_BRANCHES = new VcsLogProperty<>(false); @NotNull public static final VcsLogProperty SUPPORTS_INDEXING = new VcsLogProperty<>(false); + @NotNull public static final VcsLogProperty CASE_INSENSITIVE_REGEX = new VcsLogProperty<>(true); @NotNull public static T get(@NotNull VcsLogProvider provider, VcsLogProperty property) { diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/EnableMatchCaseAction.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/EnableMatchCaseAction.java index 8d3a1d8e43bc..ab35a99a0a79 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/EnableMatchCaseAction.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/EnableMatchCaseAction.java @@ -18,11 +18,20 @@ package com.intellij.vcs.log.ui.actions; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.ToggleAction; import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.VcsLogDataKeys; +import com.intellij.vcs.log.VcsLogProperties; +import com.intellij.vcs.log.VcsLogProvider; import com.intellij.vcs.log.VcsLogUi; import org.jetbrains.annotations.NotNull; +import java.util.Collection; +import java.util.List; + public class EnableMatchCaseAction extends ToggleAction implements DumbAware { + @NotNull + private static final String MATCH_CASE = "Match Case"; @Override public boolean isSelected(AnActionEvent e) { @@ -41,7 +50,30 @@ public class EnableMatchCaseAction extends ToggleAction implements DumbAware { @Override public void update(@NotNull AnActionEvent e) { - e.getPresentation().setEnabledAndVisible(e.getData(VcsLogDataKeys.VCS_LOG_UI) != null); + VcsLogUi ui = e.getData(VcsLogDataKeys.VCS_LOG_UI); + if (ui == null) { + e.getPresentation().setEnabledAndVisible(false); + } + else { + boolean regexEnabled = ui.getTextFilterSettings().isFilterByRegexEnabled(); + if (!regexEnabled) { + e.getPresentation().setEnabledAndVisible(true); + e.getPresentation().setText(MATCH_CASE); + } + else { + Collection providers = ContainerUtil.newLinkedHashSet(ui.getDataPack().getLogProviders().values()); + List supported = + ContainerUtil.filter(providers, p -> VcsLogProperties.get(p, VcsLogProperties.CASE_INSENSITIVE_REGEX)); + e.getPresentation().setEnabledAndVisible(!supported.isEmpty()); + if (providers.size() == supported.size()) { + e.getPresentation().setText(MATCH_CASE); + } + else { + String supportedText = StringUtil.join(ContainerUtil.map(supported, p -> p.getSupportedVcs().getName().toLowerCase()), ", "); + e.getPresentation().setText(MATCH_CASE + " (" + supportedText + " only)"); + } + } + } super.update(e); } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java b/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java index 5cba891ac3cf..92a43bfcf23b 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java @@ -335,6 +335,9 @@ public class HgLogProvider implements VcsLogProvider { @Nullable @Override public T getPropertyValue(VcsLogProperties.VcsLogProperty property) { + if (property == VcsLogProperties.CASE_INSENSITIVE_REGEX) { + return (T)Boolean.FALSE; + } return null; } } From 50d6538817bab81b2d127a8c4ea9407e305d32a2 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Thu, 24 Nov 2016 20:31:40 +0300 Subject: [PATCH 301/558] [vcs-log] reuse matching code from text filter in indexes --- .../log/data/index/VcsLogPersistentIndex.java | 31 +++--------- .../com/intellij/vcs/log/impl/VcsLogUtil.java | 16 +----- .../log/ui/filter/VcsLogTextFilterImpl.java | 49 +++++++++++++------ 3 files changed, 42 insertions(+), 54 deletions(-) diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java index 81bc6ef9852c..d6cf41f4119c 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/index/VcsLogPersistentIndex.java @@ -23,7 +23,6 @@ import com.intellij.openapi.progress.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VirtualFile; @@ -38,6 +37,7 @@ import com.intellij.vcs.log.*; import com.intellij.vcs.log.data.*; import com.intellij.vcs.log.impl.FatalErrorHandler; import com.intellij.vcs.log.impl.VcsLogUserFilterImpl; +import com.intellij.vcs.log.ui.filter.VcsLogTextFilterImpl; import com.intellij.vcs.log.util.PersistentSet; import com.intellij.vcs.log.util.PersistentSetImpl; import com.intellij.vcs.log.util.StopWatch; @@ -53,8 +53,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; import java.util.stream.IntStream; import static com.intellij.vcs.log.data.index.VcsLogFullDetailsIndex.INDEX; @@ -261,32 +259,18 @@ public class VcsLogPersistentIndex implements VcsLogIndex, Disposable { } @NotNull - public TIntHashSet filterMessages(@NotNull String text, boolean isRegex, boolean isMatchCase) { + public TIntHashSet filterMessages(@NotNull VcsLogTextFilter filter) { if (myIndexStorage != null) { try { - if (isRegex) { - try { - Pattern pattern; - if (isMatchCase) { - pattern = Pattern.compile(text); - } - else { - pattern = Pattern.compile(text, Pattern.CASE_INSENSITIVE); - } - return filter(myIndexStorage.messages, message -> pattern.matcher(message).find()); - } - catch (PatternSyntaxException ignored) { - } - } - else { - TIntHashSet commitsForSearch = myIndexStorage.trigrams.getCommitsForSubstring(text); + if (!filter.isRegex()) { + TIntHashSet commitsForSearch = myIndexStorage.trigrams.getCommitsForSubstring(filter.getText()); if (commitsForSearch != null) { TIntHashSet result = new TIntHashSet(); commitsForSearch.forEach(commit -> { try { String value = myIndexStorage.messages.get(commit); if (value != null) { - if (isMatchCase ? StringUtil.contains(value, text) : StringUtil.containsIgnoreCase(value, text)) { + if (VcsLogTextFilterImpl.matches(filter, value)) { result.add(commit); } } @@ -305,8 +289,7 @@ public class VcsLogPersistentIndex implements VcsLogIndex, Disposable { myFatalErrorsConsumer.consume(this, e); } - return filter(myIndexStorage.messages, - message -> isMatchCase ? StringUtil.contains(message, text) : StringUtil.containsIgnoreCase(message, text)); + return filter(myIndexStorage.messages, message -> VcsLogTextFilterImpl.matches(filter, message)); } return EmptyIntHashSet.INSTANCE; @@ -335,7 +318,7 @@ public class VcsLogPersistentIndex implements VcsLogIndex, Disposable { TIntHashSet filteredByMessage = null; if (textFilter != null) { - filteredByMessage = filterMessages(textFilter.getText(), textFilter.isRegex(), textFilter.matchesCase()); + filteredByMessage = filterMessages(textFilter); } TIntHashSet filteredByUser = null; 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 1c3da74fc9d2..cadbceca9ac8 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 @@ -34,8 +34,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; import static com.intellij.util.ObjectUtils.notNull; import static com.intellij.util.containers.ContainerUtil.getFirstItem; @@ -208,18 +206,8 @@ public class VcsLogUtil { UsageTrigger.trigger("vcs.log." + ConvertUsagesUtil.ensureProperKey(text).replace(" ", "")); } - public static boolean isRegexp(@NotNull String text) { - if (!StringUtil.containsAnyChar(text, "()[]{}.*?+^$\\|")) { - return false; - } - try { - //noinspection ResultOfMethodCallIgnored - Pattern.compile(text); - return true; - } - catch (PatternSyntaxException ignored) { - } - return false; + public static boolean maybeRegexp(@NotNull String text) { + return StringUtil.containsAnyChar(text, "()[]{}.*?+^$\\|"); } @NotNull diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogTextFilterImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogTextFilterImpl.java index b5074ad0a819..51a82a77a053 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogTextFilterImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogTextFilterImpl.java @@ -15,24 +15,39 @@ */ package com.intellij.vcs.log.ui.filter; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.vcs.log.VcsCommitMetadata; import com.intellij.vcs.log.VcsLogDetailsFilter; import com.intellij.vcs.log.VcsLogTextFilter; import com.intellij.vcs.log.impl.VcsLogUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; public class VcsLogTextFilterImpl implements VcsLogDetailsFilter, VcsLogTextFilter { @NotNull private final String myText; - private final boolean myIsRegexAllowed; private final boolean myMatchCase; + @Nullable private final Pattern myPattern; public VcsLogTextFilterImpl(@NotNull String text, boolean isRegexAllowed, boolean matchCase) { myText = text; - myIsRegexAllowed = isRegexAllowed; myMatchCase = matchCase; + myPattern = createPattern(myText, isRegexAllowed, myMatchCase); + } + + @Nullable + private static Pattern createPattern(@NotNull String text, boolean isRegexAllowed, boolean matchCase) { + if (isRegexAllowed && VcsLogUtil.maybeRegexp(text)) { + try { + return matchCase ? Pattern.compile(text) : Pattern.compile(text, Pattern.CASE_INSENSITIVE); + } + catch (PatternSyntaxException ignored) { + } + } + return null; } // used in upsource @@ -43,19 +58,7 @@ public class VcsLogTextFilterImpl implements VcsLogDetailsFilter, VcsLogTextFilt @Override public boolean matches(@NotNull VcsCommitMetadata details) { - if (isRegex()) { - Pattern pattern; - if (myMatchCase) { - pattern = Pattern.compile(myText); - } - else { - pattern = Pattern.compile(myText, Pattern.CASE_INSENSITIVE); - } - return pattern.matcher(details.getFullMessage()).find(); - } - - if (myMatchCase) return details.getFullMessage().contains(myText); - return details.getFullMessage().toLowerCase().contains(myText.toLowerCase()); + return matches(this, details.getFullMessage()); } @Override @@ -66,7 +69,7 @@ public class VcsLogTextFilterImpl implements VcsLogDetailsFilter, VcsLogTextFilt @Override public boolean isRegex() { - return myIsRegexAllowed && VcsLogUtil.isRegexp(myText); + return myPattern != null; } @Override @@ -78,4 +81,18 @@ public class VcsLogTextFilterImpl implements VcsLogDetailsFilter, VcsLogTextFilt public String toString() { return (isRegex() ? "matching " : "containing ") + myText + " (case " + (myMatchCase ? "sensitive" : "insensitive") + ")"; } + + public static boolean matches(@NotNull VcsLogTextFilter filter, @NotNull String message) { + Pattern pattern; + if (filter instanceof VcsLogTextFilterImpl) { + pattern = ((VcsLogTextFilterImpl)filter).myPattern; + } + else { + pattern = createPattern(filter.getText(), filter.isRegex(), filter.matchesCase()); + } + if (pattern != null) return pattern.matcher(message).find(); + + if (filter.matchesCase()) return message.contains(filter.getText()); + return StringUtil.containsIgnoreCase(message, filter.getText()); + } } From fa235511f9cdbd29cf1cc1756b39612d34eee790 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Thu, 24 Nov 2016 22:11:53 +0300 Subject: [PATCH 302/558] [vcs-log] when filtering by regex, "Match Case" option is visible but disabled when only Mercurial roots are present --- .../intellij/vcs/log/ui/actions/EnableMatchCaseAction.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/EnableMatchCaseAction.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/EnableMatchCaseAction.java index ab35a99a0a79..ab2de6e446f1 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/EnableMatchCaseAction.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/EnableMatchCaseAction.java @@ -64,8 +64,9 @@ public class EnableMatchCaseAction extends ToggleAction implements DumbAware { Collection providers = ContainerUtil.newLinkedHashSet(ui.getDataPack().getLogProviders().values()); List supported = ContainerUtil.filter(providers, p -> VcsLogProperties.get(p, VcsLogProperties.CASE_INSENSITIVE_REGEX)); - e.getPresentation().setEnabledAndVisible(!supported.isEmpty()); - if (providers.size() == supported.size()) { + e.getPresentation().setVisible(true); + e.getPresentation().setEnabled(!supported.isEmpty()); + if (providers.size() == supported.size() || supported.isEmpty()) { e.getPresentation().setText(MATCH_CASE); } else { From 41e33b9197da3d8a97ed8ff22955f5b71d4c8e14 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Sat, 17 Dec 2016 00:25:38 +0300 Subject: [PATCH 303/558] [vcs-log] statistic for text filter settings --- .../intellij/vcs/log/statistics/VcsLogFeaturesCollector.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/statistics/VcsLogFeaturesCollector.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/statistics/VcsLogFeaturesCollector.java index 98ab43f804cc..8266fef4cf24 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/statistics/VcsLogFeaturesCollector.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/statistics/VcsLogFeaturesCollector.java @@ -60,6 +60,9 @@ public class VcsLogFeaturesCollector extends AbstractApplicationUsagesCollector usages.add(StatisticsUtilKt.getBooleanUsage("ui.labels.compact", ui.isCompactReferencesView())); usages.add(StatisticsUtilKt.getBooleanUsage("ui.labels.showTagNames", ui.isShowTagNames())); + usages.add(StatisticsUtilKt.getBooleanUsage("ui.textFilter.regex", ui.getTextFilterSettings().isFilterByRegexEnabled())); + usages.add(StatisticsUtilKt.getBooleanUsage("ui.textFilter.matchCase", ui.getTextFilterSettings().isMatchCaseEnabled())); + for (VcsLogHighlighterFactory factory : Extensions.getExtensions(LOG_HIGHLIGHTER_FACTORY_EP, project)) { if (factory.showMenuItem()) { usages.add(StatisticsUtilKt.getBooleanUsage("ui.highlighter." + ConvertUsagesUtil From ceca7519e99c155e6f80704c40dee0ae20320db4 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Sat, 17 Dec 2016 00:29:28 +0300 Subject: [PATCH 304/558] [vcs-log] statistic for number of branches --- .../intellij/vcs/log/statistics/VcsLogRepoSizeCollector.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/statistics/VcsLogRepoSizeCollector.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/statistics/VcsLogRepoSizeCollector.java index 719e826e7102..7e3dde61bc56 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/statistics/VcsLogRepoSizeCollector.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/statistics/VcsLogRepoSizeCollector.java @@ -57,6 +57,8 @@ public class VcsLogRepoSizeCollector extends AbstractApplicationUsagesCollector Set usages = ContainerUtil.newHashSet(); usages.add(StatisticsUtilKt.getCountingUsage("data.commit.count", permanentGraph.getAllCommits().size(), asList(0, 1, 100, 1000, 10 * 1000, 100 * 1000, 500 * 1000, 1000 * 1000))); + usages.add(StatisticsUtilKt.getCountingUsage("data.branches.count", dataPack.getRefsModel().getBranches().size(), + asList(0, 1, 10, 50, 100, 500, 1000, 5 * 1000, 10 * 1000, 20 * 1000, 50 * 1000))); for (VcsKey vcs : groupedRoots.keySet()) { usages.add(StatisticsUtilKt.getCountingUsage("data." + vcs.getName().toLowerCase() + ".root.count", groupedRoots.get(vcs).size(), asList(0, 1, 2, 5, 8, 15, 30, 50, 100, 300, 500))); From 05c68faf411321b2f61538da939f53dc225f8f1b Mon Sep 17 00:00:00 2001 From: Anton Tarasov Date: Sat, 17 Dec 2016 00:32:39 +0300 Subject: [PATCH 305/558] IDEA-165448 [partially reverted due to EditorPaintingTest.testBorderAtLastLine] --- .../openapi/editor/impl/view/SimpleTextFragment.java | 2 +- .../com/intellij/openapi/editor/EditorPaintingTest.java | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/SimpleTextFragment.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/SimpleTextFragment.java index cd47c50aa035..d13967700b70 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/SimpleTextFragment.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/SimpleTextFragment.java @@ -36,7 +36,7 @@ class SimpleTextFragment extends TextFragment { myFont = fontInfo.getFont(); float x = 0; for (int i = 0; i < myText.length; i++) { - x += fontInfo.charWidth2D(myText[i]); + x += fontInfo.charWidth/*2D*/(myText[i]); myCharPositions[i] = x; } } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/EditorPaintingTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/editor/EditorPaintingTest.java index 5e4173fbb4a2..c7ba089525c7 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/editor/EditorPaintingTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/EditorPaintingTest.java @@ -267,6 +267,14 @@ public class EditorPaintingTest extends AbstractEditorTest { } } + @Override + public void drawString(String str, float x, float y) { + for (int i = 0; i < str.length(); i++) { + drawChar(str.charAt(i), (int)x, (int)y); + x += BitmapFont.CHAR_WIDTH; + } + } + private void drawChar(char c, int x, int y) { (((getFont().getStyle() & Font.BOLD) == 0) ? myPlainFont : myBoldFont).draw(myDelegate, c, x, y); } From cec6f42f4c523d2d3713622e582abbc8de36b7ff Mon Sep 17 00:00:00 2001 From: Anton Tarasov Date: Sat, 17 Dec 2016 01:01:53 +0300 Subject: [PATCH 306/558] Fix division by zero --- platform/util/src/com/intellij/util/JBHiDPIScaledImage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/util/src/com/intellij/util/JBHiDPIScaledImage.java b/platform/util/src/com/intellij/util/JBHiDPIScaledImage.java index 0d9e91c77d43..9feb4d9091b3 100644 --- a/platform/util/src/com/intellij/util/JBHiDPIScaledImage.java +++ b/platform/util/src/com/intellij/util/JBHiDPIScaledImage.java @@ -80,7 +80,7 @@ public class JBHiDPIScaledImage extends BufferedImage { myImage = image; myUserWidth = width; myUserHeight = height; - myScale = myImage.getWidth(null) / myUserWidth; + myScale = myUserWidth > 0 ? myImage.getWidth(null) / myUserWidth : 1f; } public float getScale() { From b5ce9665c0853d102b4d84821299ae1367fda783 Mon Sep 17 00:00:00 2001 From: Eldar Abusalimov Date: Wed, 14 Dec 2016 20:57:07 +0300 Subject: [PATCH 307/558] execution: Add OSProcessUtil.killProcess(int pid) --- .../execution/process/OSProcessUtil.java | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/execution/process/OSProcessUtil.java b/platform/platform-impl/src/com/intellij/execution/process/OSProcessUtil.java index 6c201d28b0e4..bb7e0640a0e4 100644 --- a/platform/platform-impl/src/com/intellij/execution/process/OSProcessUtil.java +++ b/platform/platform-impl/src/com/intellij/execution/process/OSProcessUtil.java @@ -75,13 +75,17 @@ public class OSProcessUtil { } public static void killProcess(@NotNull Process process) { + killProcess(getProcessID(process)); + } + + public static void killProcess(int pid) { if (SystemInfo.isWindows) { try { if (Registry.is("disable.winp")) { - WinProcessManager.kill(process, false); + WinProcessManager.kill(pid, false); } else { - createWinProcess(process).kill(); + createWinProcess(pid).kill(); } } catch (Throwable e) { @@ -89,7 +93,7 @@ public class OSProcessUtil { } } else if (SystemInfo.isUnix) { - UnixProcessManager.sendSignal(UnixProcessManager.getProcessPid(process), UnixProcessManager.SIGKILL); + UnixProcessManager.sendSignal(pid, UnixProcessManager.SIGKILL); } } @@ -104,8 +108,8 @@ public class OSProcessUtil { } } catch (Throwable e) { - LOG.info("Cannot get process id", e); - return -1; + throw new IllegalStateException("Cannot get PID from instance of " + process.getClass() + + ", OS: " + SystemInfo.OS_NAME, e); } } else if (SystemInfo.isUnix) { @@ -120,7 +124,12 @@ public class OSProcessUtil { if (process instanceof RunnerWinProcess) process = ((RunnerWinProcess)process).getOriginalProcess(); return new WinProcess(process); } - + + @NotNull + private static WinProcess createWinProcess(int pid) { + return new WinProcess(pid); + } + @Nullable public static List getCommandLinesOfRunningProcesses() { List result = new ArrayList<>(); From 3f4e4989d0ecaeb92b3a61546ef08a0a4a39db1e Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Sat, 17 Dec 2016 16:57:56 +0300 Subject: [PATCH 308/558] store stepic user in study task manager (per project) due to some strange behaviour on startup --- .../actions/CCGetCourseFromStepic.java | 6 +- .../coursecreator/actions/CCPushCourse.java | 2 +- .../coursecreator/actions/CCPushLesson.java | 2 +- .../learning/StudyCourseResourceManager.java | 26 ++++++++ .../edu/learning/StudyProjectComponent.java | 2 +- .../edu/learning/StudyTaskManager.java | 11 ++++ .../jetbrains/edu/learning/StudyUtils.java | 5 +- .../edu/learning/checker/StudyCheckTask.java | 3 +- .../StudyProjectGenerator.java | 34 +++++----- .../learning/stepic/CCStepicConnector.java | 38 ++++++------ .../stepic/EduAdaptiveStepicConnector.java | 62 ++++++++++--------- .../stepic/EduStepicAuthorizedClient.java | 18 ++++-- .../learning/stepic/EduStepicConnector.java | 47 +++++++------- .../learning/stepic/StepicStudyOptions.java | 14 ++--- .../learning/stepic/StepicUpdateSettings.java | 11 ---- .../edu/learning/ui/StudyNewProjectPanel.java | 2 +- .../PyStudyDirectoryProjectGenerator.java | 4 +- 17 files changed, 161 insertions(+), 126 deletions(-) create mode 100644 python/educational-core/student/src/com/jetbrains/edu/learning/StudyCourseResourceManager.java diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCGetCourseFromStepic.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCGetCourseFromStepic.java index 1e8c547709ac..f8b62a2e58c4 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCGetCourseFromStepic.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCGetCourseFromStepic.java @@ -61,14 +61,14 @@ public class CCGetCourseFromStepic extends DumbAwareAction { private static void createCourse(Project project, String courseId) { final VirtualFile baseDir = project.getBaseDir(); - final CourseInfo info = CCStepicConnector.getCourseInfo(courseId); + final CourseInfo info = CCStepicConnector.getCourseInfo(project, courseId); if (info == null) return; final Course course = EduStepicConnector.getCourse(project, info); if (course != null) { - flushCourse(course); + flushCourse(project, course); - final File courseDirectory = StudyUtils.getCourseDirectory(course); + final File courseDirectory = StudyUtils.getCourseDirectory(project, course); ApplicationManager.getApplication().invokeAndWait(() -> ApplicationManager.getApplication().runWriteAction(() -> { final VirtualFile[] children = baseDir.getChildren(); for (VirtualFile child : children) { diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java index 9e5ecfed340a..87a4e51f3af0 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java @@ -66,7 +66,7 @@ public class CCPushCourse extends DumbAwareAction { final int lessonId = CCStepicConnector.postLesson(project, lesson, indicator); final List sections = info.getSections(); final Integer sectionId = sections.get(sections.size() - 1); - CCStepicConnector.postUnit(lessonId, lesson.getIndex(), sectionId); + CCStepicConnector.postUnit(project, lessonId, lesson.getIndex(), sectionId); } } } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java index da28246d5f07..2122709f977e 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java @@ -84,7 +84,7 @@ public class CCPushLesson extends DumbAwareAction { final int lessonId = CCStepicConnector.postLesson(project, lesson, indicator); final List sections = info.getSections(); final Integer sectionId = sections.get(sections.size()-1); - CCStepicConnector.postUnit(lessonId, lesson.getIndex(), sectionId); + CCStepicConnector.postUnit(project, lessonId, lesson.getIndex(), sectionId); } }}); } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyCourseResourceManager.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyCourseResourceManager.java new file mode 100644 index 000000000000..546d2406bad2 --- /dev/null +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyCourseResourceManager.java @@ -0,0 +1,26 @@ +package com.jetbrains.edu.learning; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.edu.learning.courseFormat.Task; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class StudyCourseResourceManager { + + /** + * Gets text of resource file such as test input file or task text in needed format + * + * @param fileName name of resource file which should exist in task directory + * @param wrapHTML if it's necessary to wrap text with html tags + * @return text of resource file wrapped with html tags if necessary + */ + @Nullable + public String getResourceText(@NotNull final Project project, @NotNull final Task task, @NotNull final String fileName, boolean wrapHTML) { + VirtualFile taskDir = task.getTaskDir(project); + if (taskDir != null) { + return StudyUtils.getFileText(taskDir.getCanonicalPath(), fileName, wrapHTML, "UTF-8"); + } + return null; + } +} diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java index f546c900c152..3764d5cf34d3 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java @@ -148,7 +148,7 @@ public class StudyProjectComponent implements ProjectComponent { final Course course = EduStepicConnector.getCourse(myProject, info); if (course == null) return; - flushCourse(course); + flushCourse(myProject, course); course.initCourse(false); StudyLanguageManager manager = StudyUtils.getLanguageManager(course); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java index 574981ead8dd..6c04cd9b1c9b 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java @@ -13,6 +13,7 @@ import com.intellij.util.xmlb.XmlSerializer; import com.intellij.util.xmlb.annotations.Transient; import com.jetbrains.edu.learning.courseFormat.*; import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; +import com.jetbrains.edu.learning.stepic.StepicUser; import com.jetbrains.edu.learning.ui.StudyToolWindow; import org.jdom.Element; import org.jdom.output.XMLOutputter; @@ -34,6 +35,7 @@ import java.util.Map; public class StudyTaskManager implements PersistentStateComponent, DumbAware { private static final Logger LOG = Logger.getInstance(StudyTaskManager.class); public static final int CURRENT_VERSION = 4; + private StepicUser myUser = new StepicUser(); private Course myCourse; public int VERSION = 4; @@ -197,6 +199,15 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb myTurnEditingMode = turnEditingMode; } + @NotNull + public StepicUser getUser() { + return myUser; + } + + public void setUser(@NotNull final StepicUser user) { + myUser = user; + } + public boolean isEnableTestingFromSamples() { return myEnableTestingFromSamples; } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java index 6ac29a52f8de..99d184d14744 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java @@ -61,7 +61,6 @@ import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.*; import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; import com.jetbrains.edu.learning.editor.StudyEditor; -import com.jetbrains.edu.learning.stepic.StepicUpdateSettings; import com.jetbrains.edu.learning.ui.StudyToolWindow; import com.jetbrains.edu.learning.ui.StudyToolWindowFactory; import com.petebevin.markdown.MarkdownProcessor; @@ -628,12 +627,12 @@ public class StudyUtils { } @NotNull - public static File getCourseDirectory(Course course) { + public static File getCourseDirectory(@NotNull Project project, Course course) { final File courseDirectory; if (course.isAdaptive()) { courseDirectory = new File(StudyProjectGenerator.OUR_COURSES_DIR, StudyProjectGenerator.ADAPTIVE_COURSE_PREFIX + course.getName() - + "_" + StepicUpdateSettings.getInstance().getUser().getEmail()); + + "_" + StudyTaskManager.getInstance(project).getUser().getEmail()); } else { courseDirectory = new File(StudyProjectGenerator.OUR_COURSES_DIR, course.getName()); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java index b2ee1e17b4b3..bb51a047753a 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java @@ -23,7 +23,6 @@ import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.*; import com.jetbrains.edu.learning.stepic.EduAdaptiveStepicConnector; import com.jetbrains.edu.learning.stepic.EduStepicConnector; -import com.jetbrains.edu.learning.stepic.StepicUpdateSettings; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -105,7 +104,7 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun runAfterTaskCheckedActions(); final Course course = StudyTaskManager.getInstance(myProject).getCourse(); if (course != null && EduNames.STUDY.equals(course.getCourseMode())) { - if (StepicUpdateSettings.getInstance().getUser().getAccessToken() != null) { + if (StudyTaskManager.getInstance(myProject).getUser().getAccessToken() != null) { EduStepicConnector.postSolution(myTask, testsOutput.isSuccess(), myProject); } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java index 5029459a06c2..2b6f2ab6530c 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java @@ -14,6 +14,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.startup.StartupManager; 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.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; @@ -36,7 +37,6 @@ import com.jetbrains.edu.learning.editor.StudyEditor; import com.jetbrains.edu.learning.statistics.EduUsagesCollector; import com.jetbrains.edu.learning.stepic.CourseInfo; import com.jetbrains.edu.learning.stepic.EduStepicConnector; -import com.jetbrains.edu.learning.stepic.StepicUpdateSettings; import com.jetbrains.edu.learning.stepic.StepicUser; import org.apache.commons.codec.binary.Base64; import org.jetbrains.annotations.NotNull; @@ -57,6 +57,7 @@ public class StudyProjectGenerator { private static final String COURSE_DESCRIPTION = "description"; private static final String CACHE_NAME = "courseNames.txt"; private final List myListeners = ContainerUtil.newArrayList(); + @Nullable public StepicUser myUser; private List myCourses = new ArrayList<>(); private List myEnrolledCoursesIds = new ArrayList<>(); protected CourseInfo mySelectedCourseInfo; @@ -66,8 +67,7 @@ public class StudyProjectGenerator { } public boolean isLoggedIn() { - final StepicUser user = StepicUpdateSettings.getInstance().getUser(); - return user.getAccessToken() != null; + return myUser != null && !StringUtil.isEmptyOrSpaces(myUser.getPassword()) && !StringUtil.isEmptyOrSpaces(myUser.getEmail()); } public void setEnrolledCoursesIds(@NotNull final List coursesIds) { @@ -88,13 +88,16 @@ public class StudyProjectGenerator { } public void generateProject(@NotNull final Project project, @NotNull final VirtualFile baseDir) { + if (myUser != null) { + StudyTaskManager.getInstance(project).setUser(myUser); + } final Course course = getCourse(project); if (course == null) { LOG.warn("Course is null"); Messages.showWarningDialog("Some problems occurred while creating the course", "Error in Course Creation"); return; } - final File courseDirectory = StudyUtils.getCourseDirectory(course); + final File courseDirectory = StudyUtils.getCourseDirectory(project, course); StudyTaskManager.getInstance(project).setCourse(course); ApplicationManager.getApplication().runWriteAction(() -> { StudyGenerator.createCourse(course, baseDir, courseDirectory, project); @@ -117,15 +120,12 @@ public class StudyProjectGenerator { } return getCourseFromStepic(project); } - else { - final StepicUser user = StepicUpdateSettings.getInstance().getUser(); - if (user.getAccessToken() != null) { - final File adaptiveCourseFile = new File(new File(OUR_COURSES_DIR, ADAPTIVE_COURSE_PREFIX + - mySelectedCourseInfo.getName() + "_" + - user.getEmail()), EduNames.COURSE_META_FILE); - if (adaptiveCourseFile.exists()) { - return readCourseFromCache(adaptiveCourseFile, true); - } + else if (myUser != null) { + final File adaptiveCourseFile = new File(new File(OUR_COURSES_DIR, ADAPTIVE_COURSE_PREFIX + + mySelectedCourseInfo.getName() + "_" + + myUser.getEmail()), EduNames.COURSE_META_FILE); + if (adaptiveCourseFile.exists()) { + return readCourseFromCache(adaptiveCourseFile, true); } } return getCourseFromStepic(project); @@ -137,7 +137,7 @@ public class StudyProjectGenerator { return execCancelable(() -> { final Course course = EduStepicConnector.getCourse(project, mySelectedCourseInfo); if (course != null) { - flushCourse(course); + flushCourse(project, course); course.initCourse(false); } return course; @@ -216,8 +216,8 @@ public class StudyProjectGenerator { } } - public static void flushCourse(@NotNull final Course course) { - final File courseDirectory = StudyUtils.getCourseDirectory(course); + public static void flushCourse(@NotNull final Project project, @NotNull final Course course) { + final File courseDirectory = StudyUtils.getCourseDirectory(project, course); FileUtil.createDirectory(courseDirectory); flushCourseJson(course, courseDirectory); @@ -423,7 +423,7 @@ public class StudyProjectGenerator { myCourses = getCoursesFromCache(); } if (force || myCourses.isEmpty()) { - myCourses = execCancelable(() -> EduStepicConnector.getCourses(StepicUpdateSettings.getInstance().getUser())); + myCourses = execCancelable(() -> EduStepicConnector.getCourses(myUser)); flushCache(myCourses); } if (myCourses.isEmpty() || (myCourses.size() == 1 && myCourses.contains(CourseInfo.INVALID_COURSE))) { diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CCStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CCStepicConnector.java index 246a48fb1850..bd150917850c 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CCStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CCStepicConnector.java @@ -43,11 +43,11 @@ public class CCStepicConnector { private CCStepicConnector() { } - public static CourseInfo getCourseInfo(String courseId) { + public static CourseInfo getCourseInfo(Project project, String courseId) { final String url = EduStepicNames.COURSES + "/" + courseId; try { final StepicWrappers.CoursesContainer coursesContainer = - EduStepicAuthorizedClient.getFromStepic(url, StepicWrappers.CoursesContainer.class); + EduStepicAuthorizedClient.getFromStepic(url, StepicWrappers.CoursesContainer.class, project); return coursesContainer.courses.get(0); } catch (IOException e) { @@ -69,7 +69,7 @@ public class CCStepicConnector { indicator.setText("Uploading course to " + EduStepicNames.STEPIC_URL); final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/courses"); - final StepicUser currentUser = EduStepicAuthorizedClient.getCurrentUser(EduStepicAuthorizedClient.getHttpClient()); + final StepicUser currentUser = EduStepicAuthorizedClient.getCurrentUser(EduStepicAuthorizedClient.getHttpClient(project)); if (currentUser != null) { final List courseAuthors = course.getAuthors(); for (int i = 0; i < courseAuthors.size(); i++) { @@ -86,7 +86,7 @@ public class CCStepicConnector { request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; @@ -98,12 +98,12 @@ public class CCStepicConnector { } final CourseInfo postedCourse = new Gson().fromJson(responseString, StepicWrappers.CoursesContainer.class).courses.get(0); course.setId(postedCourse.id); - final int sectionId = postModule(postedCourse.id, 1, String.valueOf(postedCourse.getName())); + final int sectionId = postModule(project, postedCourse.id, 1, String.valueOf(postedCourse.getName())); int position = 1; for (Lesson lesson : course.getLessons()) { indicator.checkCanceled(); final int lessonId = postLesson(project, lesson, indicator); - postUnit(lessonId, position, sectionId); + postUnit(project, lessonId, position, sectionId); position += 1; } ApplicationManager.getApplication().runReadAction(() -> postAdditionalFiles(project, postedCourse.id, indicator)); @@ -125,7 +125,7 @@ public class CCStepicConnector { }); if (!files.isEmpty()) { - final int sectionId = postModule(id, 2, EduNames.PYCHARM_ADDITIONAL); + final int sectionId = postModule(project, id, 2, EduNames.PYCHARM_ADDITIONAL); final Lesson lesson = new Lesson(); lesson.setName(EduNames.PYCHARM_ADDITIONAL); final Task task = new Task(); @@ -151,11 +151,11 @@ public class CCStepicConnector { lesson.addTask(task); lesson.setIndex(1); final int lessonId = postLesson(project, lesson, indicator); - postUnit(lessonId, 1, sectionId); + postUnit(project, lessonId, 1, sectionId); } } - public static void postUnit(int lessonId, int position, int sectionId) { + public static void postUnit(@NotNull final Project project, int lessonId, int position, int sectionId) { final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.UNITS); final StepicWrappers.UnitWrapper unitWrapper = new StepicWrappers.UnitWrapper(); unitWrapper.unit = new StepicWrappers.Unit(); @@ -167,7 +167,7 @@ public class CCStepicConnector { request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; @@ -182,7 +182,7 @@ public class CCStepicConnector { } } - private static int postModule(int courseId, int position, @NotNull final String title) { + private static int postModule(@NotNull final Project project, int courseId, int position, @NotNull final String title) { final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/sections"); final StepicWrappers.Section section = new StepicWrappers.Section(); section.course = courseId; @@ -194,7 +194,7 @@ public class CCStepicConnector { request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; @@ -226,7 +226,7 @@ public class CCStepicConnector { request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; @@ -250,7 +250,7 @@ public class CCStepicConnector { request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; @@ -262,7 +262,7 @@ public class CCStepicConnector { } final Lesson postedLesson = new Gson().fromJson(responseString, Course.class).getLessons().get(0); for (Integer step : postedLesson.steps) { - deleteTask(step); + deleteTask(step, project); } for (Task task : lesson.getTaskList()) { @@ -284,7 +284,7 @@ public class CCStepicConnector { request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; @@ -308,11 +308,11 @@ public class CCStepicConnector { return -1; } - public static void deleteTask(@NotNull final Integer task) { + public static void deleteTask(@NotNull final Integer task, Project project) { final HttpDelete request = new HttpDelete(EduStepicNames.STEPIC_API_URL + EduStepicNames.STEP_SOURCES + task); ApplicationManager.getApplication().invokeLater(() -> { try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; @@ -337,7 +337,7 @@ public class CCStepicConnector { request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); final CloseableHttpResponse response = client.execute(request); final StatusLine line = response.getStatusLine(); final HttpEntity responseEntity = response.getEntity(); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java index 28ead09933d2..ef9e9e156ab1 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java @@ -67,7 +67,7 @@ public class EduAdaptiveStepicConnector { @Nullable public static Task getNextRecommendation(@NotNull Project project, @NotNull Course course) { try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); final URI uri = new URIBuilder(EduStepicNames.STEPIC_API_URL + EduStepicNames.RECOMMENDATIONS_URL) .addParameter(EduNames.COURSE, String.valueOf(course.getId())) .build(); @@ -88,7 +88,8 @@ public class EduAdaptiveStepicConnector { final StepicWrappers.Recommendation recommendation = recomWrapper.recommendations[0]; final String lessonId = recommendation.lesson; final StepicWrappers.LessonContainer lessonContainer = EduStepicAuthorizedClient.getFromStepic(EduStepicNames.LESSONS + lessonId, - StepicWrappers.LessonContainer.class); + StepicWrappers.LessonContainer.class, + project); if (lessonContainer.lessons.size() == 1) { final Lesson realLesson = lessonContainer.lessons.get(0); course.getLessons().get(0).setId(Integer.parseInt(lessonId)); @@ -98,8 +99,8 @@ public class EduAdaptiveStepicConnector { if (taskFromStep != null) return taskFromStep; } - final StepicUser user = StepicUpdateSettings.getInstance().getUser(); - postRecommendationReaction(lessonId, + final StepicUser user = StudyTaskManager.getInstance(project).getUser(); + postRecommendationReaction(project, lessonId, String.valueOf(user.getId()), -1); return getNextRecommendation(project, course); } @@ -136,29 +137,30 @@ public class EduAdaptiveStepicConnector { @Nullable private static Task getTask(@NotNull Project project, @NotNull String name, int stepId) throws IOException { - final StepicWrappers.StepSource step = getStep(stepId); + final StepicWrappers.StepSource step = getStep(project, stepId); final String stepType = step.block.name; if (stepType.equals(CODE_TASK_TYPE)) { return getCodeTaskFromStep(project, step.block, name, stepId); } else if (stepType.equals(CHOICE_TYPE_TEXT)) { - return getChoiceTaskFromStep(name, step.block, stepId); + return getChoiceTaskFromStep(project, name, step.block, stepId); } else if (stepType.startsWith(EduStepicConnector.PYCHARM_PREFIX)) { - return EduStepicConnector.createTask(stepId); + return EduStepicConnector.createTask(project, stepId); } return null; } - private static Task getChoiceTaskFromStep(@NotNull String lessonName, + private static Task getChoiceTaskFromStep(@NotNull Project project, + @NotNull String lessonName, @NotNull StepicWrappers.Step block, int stepId) { final Task task = new Task(lessonName); task.setStepId(stepId); task.setText(block.text); - final StepicWrappers.AdaptiveAttemptWrapper.Attempt attempt = getAttemptForStep(stepId); + final StepicWrappers.AdaptiveAttemptWrapper.Attempt attempt = getAttemptForStep(project, stepId); if (attempt != null) { final StepicWrappers.AdaptiveAttemptWrapper.Dataset dataset = attempt.dataset; if (dataset != null) { @@ -182,16 +184,16 @@ public class EduAdaptiveStepicConnector { } @Nullable - private static StepicWrappers.AdaptiveAttemptWrapper.Attempt getAttemptForStep(int id) { - final StepicUser user = StepicUpdateSettings.getInstance().getUser(); + private static StepicWrappers.AdaptiveAttemptWrapper.Attempt getAttemptForStep(@NotNull Project project, int id) { + final StepicUser user = StudyTaskManager.getInstance(project).getUser(); try { final List attempts = getAttempts(user, id); if (attempts != null && attempts.size() > 0) { final StepicWrappers.AdaptiveAttemptWrapper.Attempt attempt = attempts.get(0); - return attempt.isActive() ? attempt : createNewAttempt(id); + return attempt.isActive() ? attempt : createNewAttempt(project, id); } else { - return createNewAttempt(id); + return createNewAttempt(project, id); } } catch (URISyntaxException | IOException e) { @@ -200,8 +202,8 @@ public class EduAdaptiveStepicConnector { return null; } - private static StepicWrappers.AdaptiveAttemptWrapper.Attempt createNewAttempt(int id) throws IOException { - final String response = EduStepicConnector.postAttempt(id); + private static StepicWrappers.AdaptiveAttemptWrapper.Attempt createNewAttempt(@NotNull Project project, int id) throws IOException { + final String response = EduStepicConnector.postAttempt(project, id); final StepicWrappers.AdaptiveAttemptContainer attempt = new Gson().fromJson(response, StepicWrappers.AdaptiveAttemptContainer.class); return attempt.attempts.get(0); } @@ -235,14 +237,15 @@ public class EduAdaptiveStepicConnector { request.setConfig(requestConfig); } - public static boolean postRecommendationReaction(@NotNull String lessonId, + public static boolean postRecommendationReaction(@NotNull Project project, + @NotNull String lessonId, @NotNull String user, int reaction) { final HttpPost post = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.RECOMMENDATION_REACTIONS_URL); final String json = new Gson() .toJson(new StepicWrappers.RecommendationReactionWrapper(new StepicWrappers.RecommendationReaction(reaction, user, lessonId))); post.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON)); - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); setTimeout(post); try { final CloseableHttpResponse execute = client.execute(post); @@ -271,10 +274,10 @@ public class EduAdaptiveStepicConnector { final Course course = StudyTaskManager.getInstance(project).getCourse(); if (course != null && editor != null && editor.getTaskFile() != null) { indicator.checkCanceled(); - final StepicUser user = StepicUpdateSettings.getInstance().getUser(); + final StepicUser user = StudyTaskManager.getInstance(project).getUser(); final boolean recommendationReaction = - postRecommendationReaction(String.valueOf(editor.getTaskFile().getTask().getLesson().getId()), + postRecommendationReaction(project, String.valueOf(editor.getTaskFile().getTask().getLesson().getId()), String.valueOf(user.getId()), reaction); if (recommendationReaction) { indicator.checkCanceled(); @@ -470,7 +473,7 @@ public class EduAdaptiveStepicConnector { public static Pair checkChoiceTask(@NotNull Project project, @NotNull Task task) { if (task.getSelectedVariants().isEmpty()) return Pair.create(false, "No variants selected"); - final StepicWrappers.AdaptiveAttemptWrapper.Attempt attempt = getAttemptForStep(task.getStepId()); + final StepicWrappers.AdaptiveAttemptWrapper.Attempt attempt = getAttemptForStep(project, task.getStepId()); if (attempt != null) { final int attemptId = attempt.id; @@ -480,10 +483,10 @@ public class EduAdaptiveStepicConnector { if (!isActiveAttempt) return Pair.create(false, "Your solution is out of date. Please try again"); final StepicWrappers.SubmissionToPostWrapper wrapper = new StepicWrappers.SubmissionToPostWrapper(String.valueOf(attemptId), createChoiceTaskAnswerArray(task)); - final Pair pair = doAdaptiveCheck(wrapper, attemptId); + final Pair pair = doAdaptiveCheck(project, wrapper, attemptId); if (!pair.getFirst()) { try { - createNewAttempt(task.getStepId()); + createNewAttempt(project, task.getStepId()); final Task updatedTask = getTask(project, task.getName(), task.getStepId()); if (updatedTask != null) { final List variants = updatedTask.getChoiceVariants(); @@ -514,7 +517,7 @@ public class EduAdaptiveStepicConnector { public static Pair checkCodeTask(@NotNull Project project, @NotNull Task task) { int attemptId = -1; try { - attemptId = getAttemptId(task); + attemptId = getAttemptId(project, task); } catch (IOException e) { LOG.warn(e.getMessage()); @@ -526,7 +529,7 @@ public class EduAdaptiveStepicConnector { final String answer = PYCHARM_COMMENT + editor.getDocument().getText(); final StepicWrappers.SubmissionToPostWrapper submissionToPost = new StepicWrappers.SubmissionToPostWrapper(String.valueOf(attemptId), language, answer); - return doAdaptiveCheck(submissionToPost, attemptId); + return doAdaptiveCheck(project, submissionToPost, attemptId); } } else { @@ -535,10 +538,11 @@ public class EduAdaptiveStepicConnector { return Pair.create(false, ""); } - private static Pair doAdaptiveCheck(@NotNull StepicWrappers.SubmissionToPostWrapper submission, + private static Pair doAdaptiveCheck(@NotNull Project project, + @NotNull StepicWrappers.SubmissionToPostWrapper submission, int attemptId) { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); - final StepicUser user = StepicUpdateSettings.getInstance().getUser(); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final StepicUser user = StudyTaskManager.getInstance(project).getUser(); StepicWrappers.ResultSubmissionWrapper wrapper = postResultsForCheck(client, submission); if (wrapper != null) { wrapper = getCheckResults(client, wrapper, attemptId, user.getId()); @@ -636,13 +640,13 @@ public class EduAdaptiveStepicConnector { return null; } - private static int getAttemptId(@NotNull Task task) throws IOException { + private static int getAttemptId(@NotNull final Project project, @NotNull Task task) throws IOException { final StepicWrappers.AdaptiveAttemptWrapper attemptWrapper = new StepicWrappers.AdaptiveAttemptWrapper(task.getStepId()); final HttpPost post = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.ATTEMPTS); post.setEntity(new StringEntity(new Gson().toJson(attemptWrapper))); - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); setTimeout(post); final CloseableHttpResponse httpResponse = client.execute(post); final int statusCode = httpResponse.getStatusLine().getStatusCode(); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java index de79476be1c0..c197c91e7f32 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java @@ -5,9 +5,12 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; 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.util.net.HttpConfigurable; import com.intellij.util.net.ssl.CertificateManager; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyUtils; import org.apache.http.*; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; @@ -45,24 +48,24 @@ public class EduStepicAuthorizedClient { } @NotNull - public static CloseableHttpClient getHttpClient() { + public static CloseableHttpClient getHttpClient(@NotNull final Project project) { if (ourClient != null) { return ourClient; } - final StepicUser stepicUser = StepicUpdateSettings.getInstance().getUser(); + final StepicUser stepicUser = StudyTaskManager.getInstance(project).getUser(); ourClient = initializeClient(stepicUser); if (ourClient == null) { final StepicUser user = login(stepicUser); if (user != null) { - StepicUpdateSettings.getInstance().setUser(user); + StudyTaskManager.getInstance(project).setUser(user); ourClient = initializeClient(stepicUser); } } return ourClient; } - public static T getFromStepic(String link, final Class container) throws IOException { - return EduStepicClient.getFromStepic(link, container, getHttpClient()); + public static T getFromStepic(String link, final Class container, @NotNull final Project project) throws IOException { + return EduStepicClient.getFromStepic(link, container, getHttpClient(project)); } /* @@ -101,7 +104,10 @@ public class EduStepicAuthorizedClient { else { final StepicUser authorizedUser = login(stepicUser); if (authorizedUser != null) { - StepicUpdateSettings.getInstance().setUser(authorizedUser); + final Project project = StudyUtils.getStudyProject(); + if (project != null) { + StudyTaskManager.getInstance(project).setUser(authorizedUser); + } return initializeClient(authorizedUser); } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java index 3af448d0da98..d41cca60dff3 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java @@ -9,6 +9,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; import com.jetbrains.edu.learning.courseFormat.Task; @@ -203,7 +204,7 @@ public class EduStepicConnector { course.setLanguage(language); try { for (Integer section : info.sections) { - course.addLessons(getLessons(section)); + course.addLessons(getLessons(project, section)); } return course; } @@ -229,23 +230,23 @@ public class EduStepicConnector { return null; } - public static List getLessons(int sectionId) throws IOException { + public static List getLessons(@NotNull Project project, int sectionId) throws IOException { final StepicWrappers.SectionContainer sectionContainer = getFromStepic(EduStepicNames.SECTIONS + String.valueOf(sectionId), - StepicWrappers.SectionContainer.class); + StepicWrappers.SectionContainer.class, project); List unitIds = sectionContainer.sections.get(0).units; final List lessons = new ArrayList<>(); for (Integer unitId : unitIds) { StepicWrappers.UnitContainer - unit = getFromStepic(EduStepicNames.UNITS + "/" + String.valueOf(unitId), StepicWrappers.UnitContainer.class); + unit = getFromStepic(EduStepicNames.UNITS + "/" + String.valueOf(unitId), StepicWrappers.UnitContainer.class, project); int lessonID = unit.units.get(0).lesson; StepicWrappers.LessonContainer lessonContainer = getFromStepic(EduStepicNames.LESSONS + String.valueOf(lessonID), - StepicWrappers.LessonContainer.class); + StepicWrappers.LessonContainer.class, project); Lesson lesson = lessonContainer.lessons.get(0); lesson.taskList = new ArrayList<>(); for (int stepId : lesson.steps) { - final Task task = createTask(stepId); + final Task task = createTask(project, stepId); if (task != null) { lesson.addTask(task); } @@ -258,18 +259,20 @@ public class EduStepicConnector { return lessons; } - private static T getFromStepic(String link, final Class container) throws IOException{ - final StepicUser user = StepicUpdateSettings.getInstance().getUser(); - final boolean isAuthorized = user.getAccessToken() != null; - if (isAuthorized) { - return EduStepicAuthorizedClient.getFromStepic(link, container, user); + private static T getFromStepic(String link, final Class container, @Nullable final Project project) throws IOException{ + if (project != null) { + final StepicUser user = StudyTaskManager.getInstance(project).getUser(); + final boolean isAuthorized = user.getAccessToken() != null; + if (isAuthorized) { + return EduStepicAuthorizedClient.getFromStepic(link, container, user); + } } return EduStepicClient.getFromStepic(link, container); } @Nullable - public static Task createTask(int stepicId) throws IOException { - final StepicWrappers.StepSource step = getStep(stepicId); + public static Task createTask(@NotNull Project project, int stepicId) throws IOException { + final StepicWrappers.StepSource step = getStep(project, stepicId); final StepicWrappers.Step block = step.block; if (!block.name.startsWith(PYCHARM_PREFIX)) { LOG.error("Got a block with non-pycharm prefix: " + block.name + " for step: " + stepicId); @@ -300,9 +303,9 @@ public class EduStepicConnector { return task; } - public static StepicWrappers.StepSource getStep(int step) throws IOException { + public static StepicWrappers.StepSource getStep(@NotNull Project project, int step) throws IOException { return getFromStepic(EduStepicNames.STEPS + String.valueOf(step), - StepicWrappers.StepContainer.class).steps.get(0); + StepicWrappers.StepContainer.class, project).steps.get(0); } public static void postSolution(@NotNull final Task task, boolean passed, @NotNull final Project project) { @@ -311,7 +314,7 @@ public class EduStepicConnector { } try { - final String response = postAttempt(task.getStepId()); + final String response = postAttempt(project, task.getStepId()); if (response.isEmpty()) return; final StepicWrappers.AttemptWrapper.Attempt attempt = new Gson().fromJson(response, StepicWrappers.AttemptContainer.class).attempts.get(0); @@ -335,16 +338,16 @@ public class EduStepicConnector { } } - postSubmission(passed, attempt, files); + postSubmission(passed, attempt, project, files); } catch (IOException e) { LOG.error(e.getMessage()); } } - public static String postAttempt(int id) throws IOException { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); - if (StepicUpdateSettings.getInstance().getUser().getAccessToken() == null) return ""; + public static String postAttempt(@NotNull Project project, int id) throws IOException { + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + if (StudyTaskManager.getInstance(project).getUser().getAccessToken() == null) return ""; final HttpPost attemptRequest = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.ATTEMPTS); String attemptRequestBody = new Gson().toJson(new StepicWrappers.AttemptWrapper(id)); attemptRequest.setEntity(new StringEntity(attemptRequestBody, ContentType.APPLICATION_JSON)); @@ -361,12 +364,12 @@ public class EduStepicConnector { } private static void postSubmission(boolean passed, StepicWrappers.AttemptWrapper.Attempt attempt, - ArrayList files) throws IOException { + Project project, ArrayList files) throws IOException { final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.SUBMISSIONS); String requestBody = new Gson().toJson(new StepicWrappers.SubmissionWrapper(attempt.id, passed ? "1" : "0", files)); request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicStudyOptions.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicStudyOptions.java index bc6fcc98d668..607dcc6400d4 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicStudyOptions.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicStudyOptions.java @@ -92,7 +92,7 @@ public class StepicStudyOptions implements StudyOptionsProvider { Project project = StudyUtils.getStudyProject(); if (project != null) { StudyTaskManager taskManager = StudyTaskManager.getInstance(project); - final StepicUser user = StepicUpdateSettings.getInstance().getUser(); + final StepicUser user = taskManager.getUser(); setLogin(user.getEmail()); setPassword(user.getPassword()); myEnableTestingFromSamples.setSelected(taskManager.isEnableTestingFromSamples()); @@ -116,7 +116,7 @@ public class StepicStudyOptions implements StudyOptionsProvider { taskManager.setEnableTestingFromSamples(isTestingFromSamplesEnabled()); } - final StepicUser user = StepicUpdateSettings.getInstance().getUser(); + final StepicUser user = taskManager.getUser(); final boolean isCredentialsModified = !getLogin().equals(user.getEmail()) || !getPassword().equals(user.getPassword()); if (isCredentialsModified) { final String login = getLogin(); @@ -132,14 +132,14 @@ public class StepicStudyOptions implements StudyOptionsProvider { myProject); if (stepicUser[0] != null && stepicUser[0].getAccessToken() != null) { - StepicUpdateSettings.getInstance().setUser(stepicUser[0]); + taskManager.setUser(stepicUser[0]); } else { throw new ConfigurationException("Unable to login"); } } else { - removeCredentials(); + removeCredentials(taskManager); } } } @@ -148,8 +148,8 @@ public class StepicStudyOptions implements StudyOptionsProvider { } } - private static void removeCredentials() { - StepicUpdateSettings.getInstance().setUser(new StepicUser()); + private static void removeCredentials(StudyTaskManager taskManager) { + taskManager.setUser(new StepicUser()); } @Nullable @@ -163,7 +163,7 @@ public class StepicStudyOptions implements StudyOptionsProvider { if (project == null) return false; final StudyTaskManager taskManager = StudyTaskManager.getInstance(project); - final StepicUser user = StepicUpdateSettings.getInstance().getUser(); + final StepicUser user = taskManager.getUser(); return !getLogin().equals(user.getEmail()) || !getPassword().equals(user.getPassword()) diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicUpdateSettings.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicUpdateSettings.java index daa80c2ae446..241418d0f672 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicUpdateSettings.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicUpdateSettings.java @@ -5,12 +5,10 @@ import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; import com.intellij.util.xmlb.XmlSerializerUtil; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @State(name = "StepicUpdateSettings", storages = @Storage("other.xml")) public class StepicUpdateSettings implements PersistentStateComponent { - private StepicUser myUser = new StepicUser(); public long LAST_TIME_CHECKED = 0; public StepicUpdateSettings() { @@ -39,13 +37,4 @@ public class StepicUpdateSettings implements PersistentStateComponent courses = myGenerator.getCourses(true); if (courses != null && myRefreshCourseList) { diff --git a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java index 40b306cb8d01..eba2f1d3bd04 100644 --- a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java +++ b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java @@ -27,7 +27,6 @@ import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; import com.jetbrains.edu.learning.stepic.CourseInfo; import com.jetbrains.edu.learning.stepic.EduStepicConnector; -import com.jetbrains.edu.learning.stepic.StepicUpdateSettings; import com.jetbrains.edu.learning.ui.StudyNewProjectPanel; import com.jetbrains.python.configuration.PyConfigurableInterpreterList; import com.jetbrains.python.newProject.PyNewProjectSettings; @@ -189,8 +188,7 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator 0 && !enrolledCoursesIds.contains(course.getId())) { ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> { ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); - return StudyUtils.execCancelable(() -> EduStepicConnector.enrollToCourse(course.getId(), - StepicUpdateSettings.getInstance().getUser())); + return StudyUtils.execCancelable(() -> EduStepicConnector.enrollToCourse(course.getId(), myGenerator.myUser)); }, "Creating Course", true, ProjectManager.getInstance().getDefaultProject()); } From d88724cc7b76fce33d2ce92dff2c1e6e144dd2d7 Mon Sep 17 00:00:00 2001 From: Sergey Ignatov Date: Sat, 17 Dec 2016 18:05:52 +0300 Subject: [PATCH 309/558] fix NPE ava.lang.NullPointerException: element cannot be mapped to a null key at java.util.Objects.requireNonNull(Objects.java:228) at java.util.stream.Collectors.lambda$groupingBy$45(Collectors.java:907) at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169) at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374) at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481) at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499) at com.intellij.openapi.vcs.changes.ChangesUtil$4.compute(ChangesUtil.java:304) at com.intellij.openapi.vcs.changes.ChangesUtil$4.compute(ChangesUtil.java:300) at com.intellij.openapi.application.impl.ApplicationImpl.runReadAction(ApplicationImpl.java:895) at com.intellij.openapi.vcs.changes.ChangesUtil.processItemsByVcs(ChangesUtil.java:299) at com.intellij.openapi.vcs.changes.ChangesUtil.processVirtualFilesByVcs(ChangesUtil.java:324) at com.intellij.openapi.vcs.changes.actions.EditAction.editFiles(EditAction.java:55) at com.intellij.openapi.vcs.changes.actions.EditAction.editFilesAndShowErrors(EditAction.java:48) --- .../openapi/vcs/changes/ChangesUtil.java | 42 +++++++------------ 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangesUtil.java b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangesUtil.java index dc3455576985..f52397b69f91 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangesUtil.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangesUtil.java @@ -201,13 +201,9 @@ public class ChangesUtil { public static FilePath getLocalPath(@NotNull Project project, FilePath filePath) { // check if the file has just been renamed (IDEADEV-15494) - Change change = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - @Nullable - public Change compute() { - if (project.isDisposed()) throw new ProcessCanceledException(); - return ChangeListManager.getInstance(project).getChange(filePath); - } + Change change = ApplicationManager.getApplication().runReadAction((Computable)() -> { + if (project.isDisposed()) throw new ProcessCanceledException(); + return ChangeListManager.getInstance(project).getChange(filePath); }); if (change != null) { ContentRevision beforeRevision = change.getBeforeRevision(); @@ -242,20 +238,17 @@ public class ChangesUtil { @Nullable private static VirtualFile getValidParentUnderReadAction(@NotNull FilePath filePath) { - return ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public VirtualFile compute() { - VirtualFile result = null; - FilePath parent = filePath; - LocalFileSystem lfs = LocalFileSystem.getInstance(); + return ApplicationManager.getApplication().runReadAction((Computable)() -> { + VirtualFile result = null; + FilePath parent = filePath; + LocalFileSystem lfs = LocalFileSystem.getInstance(); - while (result == null && parent != null) { - result = lfs.findFileByPath(parent.getPath()); - parent = parent.getParentPath(); - } - - return result; + while (result == null && parent != null) { + result = lfs.findFileByPath(parent.getPath()); + parent = parent.getParentPath(); } + + return result; }); } @@ -297,13 +290,10 @@ public class ChangesUtil { @NotNull VcsSeparator separator, @NotNull PerVcsProcessor processor) { Map> changesByVcs = ApplicationManager.getApplication().runReadAction( - new NotNullComputable>>() { - @NotNull - @Override - public Map> compute() { - return items.stream().collect(groupingBy(separator::getVcsFor)); - } - }); + (NotNullComputable>>)() -> + items.stream() + .filter(it -> separator.getVcsFor(it) != null) + .collect(groupingBy(separator::getVcsFor))); changesByVcs.forEach((vcs, vcsItems) -> { if (vcs != null) { From 554947d2e8bcaf7619d37c3b272ad4c36b7ebad3 Mon Sep 17 00:00:00 2001 From: Valentina Kiryushkina Date: Sat, 17 Dec 2016 19:57:24 +0300 Subject: [PATCH 310/558] Add darcula laf for choice variants panel --- .../resources/style/buttonsDarcula.css | 64 +++++++++++++++++++ .../editor/StudyChoiceVariantsPanel.kt | 41 +++++++++++- 2 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 python/educational-core/student/resources/style/buttonsDarcula.css diff --git a/python/educational-core/student/resources/style/buttonsDarcula.css b/python/educational-core/student/resources/style/buttonsDarcula.css new file mode 100644 index 000000000000..f287398222a6 --- /dev/null +++ b/python/educational-core/student/resources/style/buttonsDarcula.css @@ -0,0 +1,64 @@ +.radio-button { + -fx-text-fill: #bababa; +} + +.radio-button:selected .radio { + -fx-border-radius: 15px; + -fx-padding: 5px; + -fx-background-color: #4f5254; + -fx-border-color: #589df6; +} + +.radio-button .radio { + -fx-border-radius: 15px; + -fx-padding: 5px; + -fx-background-color: #4f5254; + -fx-border-color: #323232; +} + +.radio-button:selected .dot { + -fx-background-color: #bababa; +} + +.toggle-button:selected { + -fx-background-color: #3c3f41; + -fx-background-insets: 0 0 0 0, 0, 0; +} +.toggle-button:selected:focused { + -fx-background-color: #3c3f41; + -fx-background-insets: 1, 1, 1, 1; + -fx-background-radius: 2, 2, 2, 2; +} + +.radio-button .dot { + -fx-background-radius: 15px; + -fx-padding: 4px; + -fx-background-color: transparent; +} + +.check-box { + -fx-text-fill: #bababa; +} + +.check-box .box { + -fx-background-color: #4f5254; + + /*-fx-background-color: -fx-shadow-highlight-color, -fx-outer-border, -fx-inner-border, -fx-body-color;*/ + -fx-background-insets: 0 0 -1 0, 0, 1, 2; + -fx-background-radius: 2, 2, 1, 1; + -fx-padding: 0.25em; /* 3 -- padding from the outside edge to the mark */ +} + +.check-box .mark { + -fx-background-color: transparent; + -fx-background-insets: 1 0 -1 0, 0; + -fx-padding: 0.333333em; /* 4 -- this is half the size of the mark */ + -fx-shape: "M14.596,2.055L12.455,0C9.687,2.884,6.011,6.714,5.158,7.602L2.055,4.624L0,6.765l5.242,5.037l0.003-0.004 l0.003,0.004L14.596,2.055z"; +} + +.check-box:selected .mark { + -fx-background-color: #bababa; + -fx-background-insets: 1 0 -1 0, 0; + -fx-padding: 0.333333em; /* 4 -- this is half the size of the mark */ + -fx-shape: "M14.596,2.055L12.455,0C9.687,2.884,6.011,6.714,5.158,7.602L2.055,4.624L0,6.765l5.242,5.037l0.003-0.004 l0.003,0.004L14.596,2.055z"; +} \ No newline at end of file diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/editor/StudyChoiceVariantsPanel.kt b/python/educational-core/student/src/com/jetbrains/edu/learning/editor/StudyChoiceVariantsPanel.kt index 6ea73cb6438c..0f2b2aa2a74e 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/editor/StudyChoiceVariantsPanel.kt +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/editor/StudyChoiceVariantsPanel.kt @@ -1,6 +1,10 @@ package com.jetbrains.edu.learning.editor +import com.intellij.ide.ui.LafManager +import com.intellij.ide.ui.LafManagerListener +import com.intellij.ide.ui.laf.darcula.DarculaLookAndFeelInfo import com.intellij.openapi.editor.colors.EditorColorsManager +import com.intellij.util.ui.UIUtil import com.jetbrains.edu.learning.courseFormat.Task import javafx.application.Platform import javafx.beans.value.ObservableValue @@ -13,7 +17,9 @@ import javafx.scene.control.CheckBox import javafx.scene.control.RadioButton import javafx.scene.control.ToggleGroup import javafx.scene.layout.VBox +import javafx.scene.paint.Color import javafx.scene.text.Font +import java.util.* import javax.swing.JScrollPane import javax.swing.ScrollPaneConstants @@ -24,12 +30,15 @@ class StudyChoiceVariantsPanel(task: Task) : JScrollPane() { private val RIGHT_INSET = 10.0 private val TOP_INSET = 15.0 private val BOTTOM_INSET = 10.0 + + private val buttons: ArrayList = ArrayList() init { val jfxPanel = JFXPanel() + LafManager.getInstance().addLafManagerListener(StudyLafManagerListener(jfxPanel)) Platform.runLater { val group = Group() - val scene = Scene(group) + val scene = Scene(group, getSceneBackground()) jfxPanel.scene = scene val vBox = VBox() vBox.spacing = 10.0 @@ -42,6 +51,7 @@ class StudyChoiceVariantsPanel(task: Task) : JScrollPane() { checkBox.selectedProperty().addListener(createSelectionListener(task, index)) setUpButtonStyle(checkBox, scene) vBox.children.add(checkBox) + buttons.add(checkBox) } } else { @@ -54,6 +64,7 @@ class StudyChoiceVariantsPanel(task: Task) : JScrollPane() { radioButton.selectedProperty().addListener(createSelectionListener(task, index)) setUpButtonStyle(radioButton, scene) vBox.children.add(radioButton) + buttons.add(radioButton) } } @@ -63,6 +74,11 @@ class StudyChoiceVariantsPanel(task: Task) : JScrollPane() { setViewportView(jfxPanel) } + private fun getSceneBackground(): Color{ + val panelBackground = UIUtil.getPanelBackground() + return Color.rgb(panelBackground.red, panelBackground.green, panelBackground.blue) + } + private fun createSelectionListener(task: Task, index: Int): (ObservableValue, Boolean, Boolean) -> Unit { return { observableValue, wasSelected, isSelected -> if (isSelected) { @@ -78,6 +94,27 @@ class StudyChoiceVariantsPanel(task: Task) : JScrollPane() { button.isWrapText = true button.maxWidthProperty().bind(scene.widthProperty().subtract(LEFT_INSET).subtract(RIGHT_INSET)) button.font = Font.font((EditorColorsManager.getInstance().globalScheme.editorFontSize + 2).toDouble()) - button.stylesheets.add(javaClass.getResource("/style/buttons.css").toExternalForm()) + + setButtonLaf(button) + } + + private fun setButtonLaf(button: ButtonBase) { + val darcula = LafManager.getInstance().currentLookAndFeel is DarculaLookAndFeelInfo + val stylesheetPath = if (darcula) "/style/buttonsDarcula.css" else "/style/buttons.css" + button.stylesheets.removeAll() + button.stylesheets.add(javaClass.getResource(stylesheetPath).toExternalForm()) + } + + private inner class StudyLafManagerListener(val jfxPanel: JFXPanel) : LafManagerListener { + override fun lookAndFeelChanged(manager: LafManager) { + Platform.runLater { + val panelBackground = UIUtil.getPanelBackground() + jfxPanel.scene.fill = Color.rgb(panelBackground.red, panelBackground.green, panelBackground.blue) + for (button in buttons) { + setButtonLaf(button) + } + jfxPanel.repaint() + } + } } } From 9bd5077e94450cd0b837429e45a3635bf387cb89 Mon Sep 17 00:00:00 2001 From: "Dmitry.Krasilschikov" Date: Sat, 17 Dec 2016 20:14:57 +0300 Subject: [PATCH 311/558] RUBY-5449 find class related tests and vise versa for appropriate element --- .../src/com/intellij/testIntegration/TestFinder.java | 5 +++++ .../src/com/intellij/testIntegration/TestFinderHelper.java | 6 ++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/platform/lang-api/src/com/intellij/testIntegration/TestFinder.java b/platform/lang-api/src/com/intellij/testIntegration/TestFinder.java index d956d465398e..f62c477a5ab9 100644 --- a/platform/lang-api/src/com/intellij/testIntegration/TestFinder.java +++ b/platform/lang-api/src/com/intellij/testIntegration/TestFinder.java @@ -35,4 +35,9 @@ public interface TestFinder { Collection findClassesForTest(@NotNull PsiElement element); boolean isTest(@NotNull PsiElement element); + + @Nullable + default PsiElement findSelectedElement(@NotNull final PsiElement element) { + return element; + } } diff --git a/platform/lang-impl/src/com/intellij/testIntegration/TestFinderHelper.java b/platform/lang-impl/src/com/intellij/testIntegration/TestFinderHelper.java index 67318ceb1238..675cce74e813 100644 --- a/platform/lang-impl/src/com/intellij/testIntegration/TestFinderHelper.java +++ b/platform/lang-impl/src/com/intellij/testIntegration/TestFinderHelper.java @@ -39,7 +39,8 @@ public class TestFinderHelper { public static Collection findTestsForClass(PsiElement element) { Collection result = new LinkedHashSet<>(); for (TestFinder each : getFinders()) { - result.addAll(each.findTestsForClass(element)); + final PsiElement selectedElement = each.findSelectedElement(element); + if (selectedElement != null) result.addAll(each.findTestsForClass(selectedElement)); } return result; } @@ -47,7 +48,8 @@ public class TestFinderHelper { public static Collection findClassesForTest(PsiElement element) { Collection result = new LinkedHashSet<>(); for (TestFinder each : getFinders()) { - result.addAll(each.findClassesForTest(element)); + final PsiElement selectedElement = each.findSelectedElement(element); + if (selectedElement != null) result.addAll(each.findClassesForTest(selectedElement)); } return result; } From 95261654cc8211cd5436b6efa2f535b3d23f3985 Mon Sep 17 00:00:00 2001 From: "Dmitry.Krasilschikov" Date: Sat, 17 Dec 2016 20:22:54 +0300 Subject: [PATCH 312/558] TestFinder methods @NotNull annotating --- .../src/com/intellij/testIntegration/TestFinderHelper.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/testIntegration/TestFinderHelper.java b/platform/lang-impl/src/com/intellij/testIntegration/TestFinderHelper.java index 675cce74e813..ed7cafc6c288 100644 --- a/platform/lang-impl/src/com/intellij/testIntegration/TestFinderHelper.java +++ b/platform/lang-impl/src/com/intellij/testIntegration/TestFinderHelper.java @@ -23,12 +23,13 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiNamedElement; import com.intellij.psi.codeStyle.NameUtil; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; public class TestFinderHelper { - public static PsiElement findSourceElement(PsiElement from) { + public static PsiElement findSourceElement(@NotNull final PsiElement from) { for (TestFinder each : getFinders()) { PsiElement result = each.findSourceElement(from); if (result != null) return result; @@ -36,7 +37,7 @@ public class TestFinderHelper { return null; } - public static Collection findTestsForClass(PsiElement element) { + public static Collection findTestsForClass(@NotNull final PsiElement element) { Collection result = new LinkedHashSet<>(); for (TestFinder each : getFinders()) { final PsiElement selectedElement = each.findSelectedElement(element); @@ -45,7 +46,7 @@ public class TestFinderHelper { return result; } - public static Collection findClassesForTest(PsiElement element) { + public static Collection findClassesForTest(@NotNull final PsiElement element) { Collection result = new LinkedHashSet<>(); for (TestFinder each : getFinders()) { final PsiElement selectedElement = each.findSelectedElement(element); From c84920c28d2184e10c97f1049f107f5146f8f7d1 Mon Sep 17 00:00:00 2001 From: Alexander Zolotov Date: Sat, 17 Dec 2016 19:13:49 +0300 Subject: [PATCH 313/558] Allow to dispose actions in Create Project dialogs --- .../util/projectWizard/AbstractNewProjectDialog.java | 2 +- .../wm/impl/welcomeScreen/FlatWelcomeFrame.java | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/ide/util/projectWizard/AbstractNewProjectDialog.java b/platform/lang-impl/src/com/intellij/ide/util/projectWizard/AbstractNewProjectDialog.java index 126b5734b0c3..b0f74b0a547b 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/projectWizard/AbstractNewProjectDialog.java +++ b/platform/lang-impl/src/com/intellij/ide/util/projectWizard/AbstractNewProjectDialog.java @@ -53,7 +53,7 @@ public abstract class AbstractNewProjectDialog extends DialogWrapper { setTitle(generators.length == 0 ? "Create Project" : "New Project"); DefaultActionGroup root = createRootStep(); - Pair pair = FlatWelcomeFrame.createActionGroupPanel(root, getRootPane(), null); + Pair pair = FlatWelcomeFrame.createActionGroupPanel(root, getRootPane(), null, getDisposable()); Dimension size = JBUI.size(666, 385); JPanel component = pair.first; component.setMinimumSize(size); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java index 243236bb6475..94a373466875 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java @@ -470,7 +470,7 @@ public class FlatWelcomeFrame extends JFrame implements IdeFrame, Disposable, Ac private AnAction wrapGroups(AnAction action) { if (action instanceof ActionGroup && ((ActionGroup)action).isPopup()) { - final Pair panel = createActionGroupPanel((ActionGroup)action, mySlidingPanel, () -> goBack()); + final Pair panel = createActionGroupPanel((ActionGroup)action, mySlidingPanel, () -> goBack(), this); final Runnable onDone = () -> { setTitle("New Project"); final JBList list = panel.second; @@ -786,11 +786,19 @@ public class FlatWelcomeFrame extends JFrame implements IdeFrame, Disposable, Ac private JPanel actions; } - public static Pair createActionGroupPanel(final ActionGroup action, final JComponent parent, final Runnable backAction) { + public static Pair createActionGroupPanel(final ActionGroup action, + final JComponent parent, + final Runnable backAction, + @NotNull Disposable parentDisposable) { JPanel actionsListPanel = new JPanel(new BorderLayout()); actionsListPanel.setBackground(getProjectsBackground()); final List groups = flattenActionGroups(action); final JBList list = new JBList<>(groups); + for (AnAction group : groups) { + if (group instanceof Disposable) { + Disposer.register(parentDisposable, (Disposable)group); + } + } list.setBackground(getProjectsBackground()); list.setCellRenderer(new GroupedItemsListRenderer(new ListItemDescriptorAdapter() { From 3ae85b7253c23c052c670fbd177c8d2edf046db0 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Mon, 28 Nov 2016 16:52:09 +0300 Subject: [PATCH 314/558] [git] extract MyGitLineHandlerListener class (cherry picked from commit a20cc27) --- .../src/git4idea/history/GitHistoryUtils.java | 209 ++++++++++-------- 1 file changed, 115 insertions(+), 94 deletions(-) diff --git a/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java b/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java index b73587d70f5f..68320224cbdb 100644 --- a/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java +++ b/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java @@ -64,7 +64,6 @@ import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import static com.intellij.util.ObjectUtils.notNull; @@ -539,100 +538,9 @@ public class GitHistoryUtils { @NotNull Consumer recordConsumer, int bufferSize) throws VcsException { - final StringBuilder output = new StringBuilder(); - final StringBuilder errors = new StringBuilder(); - final Ref foundRecordEnd = Ref.create(false); - final Ref ex = new Ref<>(); - final AtomicInteger records = new AtomicInteger(); - handler.addLineListener(new GitLineHandlerListener() { - @Override - public void onLineAvailable(String line, Key outputType) { - if (outputType == ProcessOutputTypes.STDERR) { - errors.append(line).append("\n"); - } - else if (outputType == ProcessOutputTypes.STDOUT) { - try { - // format of the record is .*.* - // then next record goes - // (rather inconveniently, after RECORD_END there is a list of modified files) - // so here I'm trying to find text between two RECORD_START symbols - // that simultaneously contains a RECORD_END - // this helps to deal with commits like a929478f6720ac15d949117188cd6798b4a9c286 in linux repo that have RECORD_START symbols in the message - // wont help with RECORD_END symbols in the message however (have not seen those yet) - - String tail = null; - if (!foundRecordEnd.get()) { - int recordEnd = line.indexOf(GitLogParser.RECORD_END); - if (recordEnd != -1) { - foundRecordEnd.set(true); - output.append(line.substring(0, recordEnd + 1)); - line = line.substring(recordEnd + 1); - } - else { - output.append(line).append("\n"); - } - } - - if (foundRecordEnd.get()) { - int nextRecordStart = line.indexOf(GitLogParser.RECORD_START); - if (nextRecordStart == -1) { - output.append(line).append("\n"); - } - else if (nextRecordStart == 0) { - tail = line + "\n"; - } - else { - output.append(line.substring(0, nextRecordStart)); - tail = line.substring(nextRecordStart) + "\n"; - } - } - - if (tail != null) { - if (records.incrementAndGet() > bufferSize) { - recordConsumer.consume(output); - output.setLength(0); - } - output.append(tail); - foundRecordEnd.set(tail.contains(GitLogParser.RECORD_END)); - } - } - catch (Exception e) { - ex.set(new VcsException(e)); - } - } - } - - @Override - public void processTerminated(int exitCode) { - if (exitCode != 0) { - String errorMessage = errors.toString(); - if (errorMessage.isEmpty()) { - errorMessage = GitBundle.message("git.error.exit", exitCode); - } - ex.set(new VcsException(errorMessage + "\nCommand line: [" + handler.printableCommandLine() + "]")); - } - else { - try { - recordConsumer.consume(output); - } - catch (Exception e) { - ex.set(new VcsException(e)); - } - } - } - - @Override - public void startFailed(Throwable exception) { - ex.set(new VcsException(exception)); - } - }); + MyGitLineHandlerListener handlerListener = new MyGitLineHandlerListener(handler, recordConsumer, bufferSize); handler.runInCurrentThread(null); - if (!ex.isNull()) { - if (ex.get().getCause() instanceof ProcessCanceledException) { - throw (ProcessCanceledException)ex.get().getCause(); - } - throw ex.get(); - } + handlerListener.reportErrors(); } public static void readCommits(@NotNull Project project, @@ -1095,4 +1003,117 @@ public class GitHistoryUtils { return GitRevisionNumber.resolve(project, root, output); } } + + private static class MyGitLineHandlerListener implements GitLineHandlerListener { + @NotNull private final GitLineHandler myHandler; + @NotNull private final Consumer myRecordConsumer; + private final int myBufferSize; + + @NotNull private final StringBuilder myOutput = new StringBuilder(); + @NotNull private final StringBuilder myErrors = new StringBuilder(); + @Nullable private VcsException myException = null; + + private int myRecords = 0; + private boolean myFoundRecordEnd = false; + + public MyGitLineHandlerListener(@NotNull GitLineHandler handler, + @NotNull Consumer recordConsumer, + int bufferSize) { + myHandler = handler; + myRecordConsumer = recordConsumer; + myBufferSize = bufferSize; + + myHandler.addLineListener(this); + } + + @Override + public void onLineAvailable(String line, Key outputType) { + if (outputType == ProcessOutputTypes.STDERR) { + myErrors.append(line).append("\n"); + } + else if (outputType == ProcessOutputTypes.STDOUT) { + try { + // format of the record is .*.* + // then next record goes + // (rather inconveniently, after RECORD_END there is a list of modified files) + // so here I'm trying to find text between two RECORD_START symbols + // that simultaneously contains a RECORD_END + // this helps to deal with commits like a929478f6720ac15d949117188cd6798b4a9c286 in linux repo that have RECORD_START symbols in the message + // wont help with RECORD_END symbols in the message however (have not seen those yet) + + String tail = null; + if (!myFoundRecordEnd) { + int recordEnd = line.indexOf(GitLogParser.RECORD_END); + if (recordEnd != -1) { + myFoundRecordEnd = true; + myOutput.append(line.substring(0, recordEnd + 1)); + line = line.substring(recordEnd + 1); + } + else { + myOutput.append(line).append("\n"); + } + } + + if (myFoundRecordEnd) { + int nextRecordStart = line.indexOf(GitLogParser.RECORD_START); + if (nextRecordStart == -1) { + myOutput.append(line).append("\n"); + } + else if (nextRecordStart == 0) { + tail = line + "\n"; + } + else { + myOutput.append(line.substring(0, nextRecordStart)); + tail = line.substring(nextRecordStart) + "\n"; + } + } + + if (tail != null) { + if (++myRecords > myBufferSize) { + myRecordConsumer.consume(myOutput); + myOutput.setLength(0); + } + myOutput.append(tail); + myFoundRecordEnd = tail.contains(GitLogParser.RECORD_END); + } + } + catch (Exception e) { + myException = new VcsException(e); + } + } + } + + @Override + public void processTerminated(int exitCode) { + if (exitCode != 0) { + String errorMessage = myErrors.toString(); + if (errorMessage.isEmpty()) { + errorMessage = GitBundle.message("git.error.exit", exitCode); + } + myException = new VcsException(errorMessage + "\nCommand line: [" + myHandler.printableCommandLine() + "]"); + } + else { + try { + myRecordConsumer.consume(myOutput); + } + catch (Exception e) { + myException = new VcsException(e); + } + } + } + + @Override + public void startFailed(Throwable exception) { + myException = new VcsException(exception); + } + + public void reportErrors() throws VcsException { + if (myException != null) { + if (myException.getCause() instanceof ProcessCanceledException) { + throw (ProcessCanceledException)myException.getCause(); + } + throw myException; + } + } + } } From c468cd513b44eb496498818d1e2ce11fda91c25d Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Thu, 24 Nov 2016 04:48:36 +0300 Subject: [PATCH 315/558] [git] simplify lines processing (cherry picked from commit 0426492) --- .../src/git4idea/history/GitHistoryUtils.java | 84 +++++++++---------- 1 file changed, 40 insertions(+), 44 deletions(-) diff --git a/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java b/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java index 68320224cbdb..1b8fbace07ef 100644 --- a/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java +++ b/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java @@ -1014,7 +1014,7 @@ public class GitHistoryUtils { @Nullable private VcsException myException = null; private int myRecords = 0; - private boolean myFoundRecordEnd = false; + private boolean myIsInsideBody = true; public MyGitLineHandlerListener(@NotNull GitLineHandler handler, @NotNull Consumer recordConsumer, @@ -1033,49 +1033,7 @@ public class GitHistoryUtils { } else if (outputType == ProcessOutputTypes.STDOUT) { try { - // format of the record is .*.* - // then next record goes - // (rather inconveniently, after RECORD_END there is a list of modified files) - // so here I'm trying to find text between two RECORD_START symbols - // that simultaneously contains a RECORD_END - // this helps to deal with commits like a929478f6720ac15d949117188cd6798b4a9c286 in linux repo that have RECORD_START symbols in the message - // wont help with RECORD_END symbols in the message however (have not seen those yet) - - String tail = null; - if (!myFoundRecordEnd) { - int recordEnd = line.indexOf(GitLogParser.RECORD_END); - if (recordEnd != -1) { - myFoundRecordEnd = true; - myOutput.append(line.substring(0, recordEnd + 1)); - line = line.substring(recordEnd + 1); - } - else { - myOutput.append(line).append("\n"); - } - } - - if (myFoundRecordEnd) { - int nextRecordStart = line.indexOf(GitLogParser.RECORD_START); - if (nextRecordStart == -1) { - myOutput.append(line).append("\n"); - } - else if (nextRecordStart == 0) { - tail = line + "\n"; - } - else { - myOutput.append(line.substring(0, nextRecordStart)); - tail = line.substring(nextRecordStart) + "\n"; - } - } - - if (tail != null) { - if (++myRecords > myBufferSize) { - myRecordConsumer.consume(myOutput); - myOutput.setLength(0); - } - myOutput.append(tail); - myFoundRecordEnd = tail.contains(GitLogParser.RECORD_END); - } + processOutputLine(line); } catch (Exception e) { myException = new VcsException(e); @@ -1083,6 +1041,44 @@ public class GitHistoryUtils { } } + private void processOutputLine(@NotNull String line) { + // format of the record is + // then next record goes + // (rather inconveniently, after RECORD_END there is a list of modified files) + // so here I'm trying to find text between two RECORD_START symbols + // that simultaneously contains a RECORD_END + // this helps to deal with commits like a929478f6720ac15d949117188cd6798b4a9c286 in linux repo that have RECORD_START symbols in the message + // wont help with RECORD_END symbols in the message however (have not seen those yet) + + if (myIsInsideBody) { + // find body + int bodyEnd = line.indexOf(GitLogParser.RECORD_END); + if (bodyEnd >= 0) { + myIsInsideBody = false; + myOutput.append(line.substring(0, bodyEnd + 1)); + processOutputLine(line.substring(bodyEnd + 1)); + } + else { + myOutput.append(line).append("\n"); + } + } + else { + int nextRecordStart = line.indexOf(GitLogParser.RECORD_START); + if (nextRecordStart >= 0) { + myOutput.append(line.substring(0, nextRecordStart)); + if (++myRecords > myBufferSize) { + myRecordConsumer.consume(myOutput); + myOutput.setLength(0); + } + myIsInsideBody = true; + processOutputLine(line.substring(nextRecordStart)); + } + else { + myOutput.append(line).append("\n"); + } + } + } + @Override public void processTerminated(int exitCode) { if (exitCode != 0) { From a7b0203b630a22db94b2459942cb7b85c0b8b493 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Fri, 16 Dec 2016 10:38:07 +0100 Subject: [PATCH 316/558] =?UTF-8?q?avoid=20linear=20search=20=E2=80=94=20m?= =?UTF-8?q?yFailedModulePaths?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/intellij/openapi/module/impl/ModuleManagerImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java index e6164fe0224e..e208cefad6f1 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java @@ -39,7 +39,6 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.StandardFileSystems; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; -import com.intellij.util.SmartList; import com.intellij.util.concurrency.AppExecutorUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashMap; @@ -49,6 +48,7 @@ import com.intellij.util.graph.*; import com.intellij.util.io.URLUtil; import com.intellij.util.messages.MessageBus; import gnu.trove.THashMap; +import gnu.trove.THashSet; import gnu.trove.TObjectHashingStrategy; import org.jdom.Element; import org.jetbrains.annotations.NonNls; @@ -76,7 +76,7 @@ public abstract class ModuleManagerImpl extends ModuleManager implements Project @NonNls public static final String COMPONENT_NAME = "ProjectModuleManager"; private static final String MODULE_GROUP_SEPARATOR = "/"; private LinkedHashSet myModulePathsToLoad; - private final List myFailedModulePaths = new SmartList<>(); + private final Set myFailedModulePaths = new THashSet<>(); @NonNls public static final String ELEMENT_MODULES = "modules"; @NonNls public static final String ELEMENT_MODULE = "module"; @NonNls private static final String ATTRIBUTE_FILEURL = "fileurl"; From 8cddb9a432f2a190193f6eda15e3ff0ae52cfa05 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Fri, 16 Dec 2016 11:09:02 +0100 Subject: [PATCH 317/558] =?UTF-8?q?cleanup=20=E2=80=94=20ModuleListener?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../compiler/CompilerConfigurationImpl.java | 4 ++-- .../roots/MultiModuleEditingTest.java | 4 ++-- .../testSrc/ModuleStoreRenameTest.kt | 4 ++-- .../impl/ModuleRunConfigurationManager.java | 4 ++-- ...ProjectWideFacetListenersRegistryImpl.java | 4 ++-- .../pointers/FacetPointersManagerImpl.java | 4 ++-- .../openapi/project/ModuleAdapter.java | 19 +------------------ .../openapi/project/ModuleListener.java | 12 ++++++++---- .../module/impl/ModulePointerManagerImpl.java | 4 ++-- .../testFramework/LightPlatformTestCase.java | 4 ++-- .../openapi/vcs/impl/ModuleVcsDetector.java | 4 ++-- .../CloudGitChooseAccountStepImpl.java | 4 ++-- ...esourceCompilerConfigurationGenerator.java | 4 ++-- .../mvc/MvcModuleStructureSynchronizer.java | 4 ++-- .../project/MavenProjectsManagerWatcher.java | 4 ++-- .../jetbrains/python/PyModuleListener.java | 4 ++-- 16 files changed, 37 insertions(+), 50 deletions(-) diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerConfigurationImpl.java b/java/compiler/impl/src/com/intellij/compiler/CompilerConfigurationImpl.java index 09ab3995dc41..7815ab2d4d6e 100644 --- a/java/compiler/impl/src/com/intellij/compiler/CompilerConfigurationImpl.java +++ b/java/compiler/impl/src/com/intellij/compiler/CompilerConfigurationImpl.java @@ -38,7 +38,7 @@ import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; -import com.intellij.openapi.project.ModuleAdapter; +import com.intellij.openapi.project.ModuleListener; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.ui.InputValidator; @@ -114,7 +114,7 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements myExcludesConfiguration = new ExcludesConfigNotificationsWrapper<>(project, new ExcludedEntriesConfiguration()); Disposer.register(project, myExcludesConfiguration.getDelegate()); MessageBusConnection connection = project.getMessageBus().connect(project); - connection.subscribe(ProjectTopics.MODULES, new ModuleAdapter() { + connection.subscribe(ProjectTopics.MODULES, new ModuleListener() { @Override public void beforeModuleRemoved(@NotNull Project project, @NotNull Module module) { getAnnotationProcessingConfiguration(module).removeModuleName(module.getName()); diff --git a/java/java-tests/testSrc/com/intellij/roots/MultiModuleEditingTest.java b/java/java-tests/testSrc/com/intellij/roots/MultiModuleEditingTest.java index 4cb6f86dfd03..3d68da397085 100644 --- a/java/java-tests/testSrc/com/intellij/roots/MultiModuleEditingTest.java +++ b/java/java-tests/testSrc/com/intellij/roots/MultiModuleEditingTest.java @@ -23,7 +23,7 @@ import com.intellij.openapi.module.ModifiableModuleModel; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.module.StdModuleTypes; -import com.intellij.openapi.project.ModuleAdapter; +import com.intellij.openapi.project.ModuleListener; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ContentEntry; import com.intellij.openapi.roots.ModifiableRootModel; @@ -189,7 +189,7 @@ public class MultiModuleEditingTest extends ModuleTestCase { } - private static class MyModuleListener extends ModuleAdapter { + private static class MyModuleListener implements ModuleListener { private final List myLog = new ArrayList<>(); @Override diff --git a/platform/configuration-store-impl/testSrc/ModuleStoreRenameTest.kt b/platform/configuration-store-impl/testSrc/ModuleStoreRenameTest.kt index 105cfb8e35ad..cf0797c25888 100644 --- a/platform/configuration-store-impl/testSrc/ModuleStoreRenameTest.kt +++ b/platform/configuration-store-impl/testSrc/ModuleStoreRenameTest.kt @@ -7,7 +7,7 @@ import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.components.StoragePathMacros import com.intellij.openapi.components.stateStore import com.intellij.openapi.module.Module -import com.intellij.openapi.project.ModuleAdapter +import com.intellij.openapi.project.ModuleListener import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.project.modifyModules @@ -49,7 +49,7 @@ internal class ModuleStoreRenameTest { module = projectRule.createModule(tempDirManager.newPath(refreshVfs = true).resolve("m.iml")) } - module.messageBus.connect().subscribe(ProjectTopics.MODULES, object : ModuleAdapter() { + module.messageBus.connect().subscribe(ProjectTopics.MODULES, object : ModuleListener { override fun modulesRenamed(project: Project, modules: MutableList, oldNameProvider: Function) { assertThat(modules).containsOnly(module) oldModuleNames.add(oldNameProvider.`fun`(module)) diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ModuleRunConfigurationManager.java b/platform/lang-impl/src/com/intellij/execution/impl/ModuleRunConfigurationManager.java index 77680aec1db4..c0c01209af7f 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ModuleRunConfigurationManager.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ModuleRunConfigurationManager.java @@ -24,7 +24,7 @@ import com.intellij.openapi.components.State; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleComponent; -import com.intellij.openapi.project.ModuleAdapter; +import com.intellij.openapi.project.ModuleListener; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Condition; @@ -43,7 +43,7 @@ import java.util.List; import java.util.Set; @State(name = ModuleRunConfigurationManager.COMPONENT_NAME) -public final class ModuleRunConfigurationManager extends ModuleAdapter implements ModuleComponent, PersistentStateComponent { +public final class ModuleRunConfigurationManager implements ModuleComponent, ModuleListener, PersistentStateComponent { private static final Logger LOG = Logger.getInstance(ModuleRunConfigurationManager.class); @NonNls static final String COMPONENT_NAME = "ModuleRunConfigurationManager"; @NotNull diff --git a/platform/lang-impl/src/com/intellij/facet/impl/ProjectWideFacetListenersRegistryImpl.java b/platform/lang-impl/src/com/intellij/facet/impl/ProjectWideFacetListenersRegistryImpl.java index d0c3e1407e6e..ae0b0cc5c6ad 100644 --- a/platform/lang-impl/src/com/intellij/facet/impl/ProjectWideFacetListenersRegistryImpl.java +++ b/platform/lang-impl/src/com/intellij/facet/impl/ProjectWideFacetListenersRegistryImpl.java @@ -20,7 +20,7 @@ import com.intellij.ProjectTopics; import com.intellij.facet.*; import com.intellij.openapi.Disposable; import com.intellij.openapi.module.Module; -import com.intellij.openapi.project.ModuleAdapter; +import com.intellij.openapi.project.ModuleListener; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.util.EventDispatcher; @@ -44,7 +44,7 @@ public class ProjectWideFacetListenersRegistryImpl extends ProjectWideFacetListe public ProjectWideFacetListenersRegistryImpl(MessageBus messageBus) { myFacetListener = new MyFacetManagerAdapter(); - messageBus.connect().subscribe(ProjectTopics.MODULES, new ModuleAdapter() { + messageBus.connect().subscribe(ProjectTopics.MODULES, new ModuleListener() { @Override public void moduleAdded(@NotNull Project project, @NotNull Module module) { onModuleAdded(module); diff --git a/platform/lang-impl/src/com/intellij/facet/impl/pointers/FacetPointersManagerImpl.java b/platform/lang-impl/src/com/intellij/facet/impl/pointers/FacetPointersManagerImpl.java index aa7742d750d2..a8caed31a641 100644 --- a/platform/lang-impl/src/com/intellij/facet/impl/pointers/FacetPointersManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/facet/impl/pointers/FacetPointersManagerImpl.java @@ -28,7 +28,7 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.components.ProjectComponent; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; -import com.intellij.openapi.project.ModuleAdapter; +import com.intellij.openapi.project.ModuleListener; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; @@ -95,7 +95,7 @@ public class FacetPointersManagerImpl extends FacetPointersManager implements Pr @Override public void initComponent() { MessageBusConnection connection = myProject.getMessageBus().connect(); - connection.subscribe(ProjectTopics.MODULES, new ModuleAdapter() { + connection.subscribe(ProjectTopics.MODULES, new ModuleListener() { @Override public void moduleAdded(@NotNull Project project, @NotNull Module module) { refreshPointers(module); diff --git a/platform/projectModel-api/src/com/intellij/openapi/project/ModuleAdapter.java b/platform/projectModel-api/src/com/intellij/openapi/project/ModuleAdapter.java index f293b3dc92f4..e469cf3338fd 100644 --- a/platform/projectModel-api/src/com/intellij/openapi/project/ModuleAdapter.java +++ b/platform/projectModel-api/src/com/intellij/openapi/project/ModuleAdapter.java @@ -15,25 +15,8 @@ */ package com.intellij.openapi.project; -import com.intellij.openapi.module.Module; -import com.intellij.util.Function; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - /** - * @author nik + * @deprecated Please use ModuleListener directly. */ public abstract class ModuleAdapter implements ModuleListener { - @Override - public void moduleAdded(@NotNull Project project, @NotNull Module module) { } - - @Override - public void beforeModuleRemoved(@NotNull Project project, @NotNull Module module) { } - - @Override - public void moduleRemoved(@NotNull Project project, @NotNull Module module) { } - - @Override - public void modulesRenamed(@NotNull Project project, @NotNull List modules, @NotNull Function oldNameProvider) { } } diff --git a/platform/projectModel-api/src/com/intellij/openapi/project/ModuleListener.java b/platform/projectModel-api/src/com/intellij/openapi/project/ModuleListener.java index 78ebff886f42..311c7635d72c 100644 --- a/platform/projectModel-api/src/com/intellij/openapi/project/ModuleListener.java +++ b/platform/projectModel-api/src/com/intellij/openapi/project/ModuleListener.java @@ -26,11 +26,15 @@ import java.util.List; * @author max */ public interface ModuleListener extends EventListener { - void moduleAdded(@NotNull Project project, @NotNull Module module); + default void moduleAdded(@NotNull Project project, @NotNull Module module) { + } - void beforeModuleRemoved(@NotNull Project project, @NotNull Module module); + default void beforeModuleRemoved(@NotNull Project project, @NotNull Module module) { + } - void moduleRemoved(@NotNull Project project, @NotNull Module module); + default void moduleRemoved(@NotNull Project project, @NotNull Module module) { + } - void modulesRenamed(@NotNull Project project, @NotNull List modules, @NotNull Function oldNameProvider); + default void modulesRenamed(@NotNull Project project, @NotNull List modules, @NotNull Function oldNameProvider) { + } } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModulePointerManagerImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModulePointerManagerImpl.java index b2507ea0860c..0ce85e403320 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModulePointerManagerImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModulePointerManagerImpl.java @@ -21,7 +21,7 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.module.ModulePointer; import com.intellij.openapi.module.ModulePointerManager; -import com.intellij.openapi.project.ModuleAdapter; +import com.intellij.openapi.project.ModuleListener; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.util.Function; @@ -41,7 +41,7 @@ public class ModulePointerManagerImpl extends ModulePointerManager { public ModulePointerManagerImpl(Project project) { myProject = project; - project.getMessageBus().connect().subscribe(ProjectTopics.MODULES, new ModuleAdapter() { + project.getMessageBus().connect().subscribe(ProjectTopics.MODULES, new ModuleListener() { @Override public void beforeModuleRemoved(@NotNull Project project, @NotNull Module module) { unregisterPointer(module); diff --git a/platform/testFramework/src/com/intellij/testFramework/LightPlatformTestCase.java b/platform/testFramework/src/com/intellij/testFramework/LightPlatformTestCase.java index 302b06286973..eae62f7e653c 100644 --- a/platform/testFramework/src/com/intellij/testFramework/LightPlatformTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/LightPlatformTestCase.java @@ -52,7 +52,7 @@ import com.intellij.openapi.module.EmptyModuleType; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleType; import com.intellij.openapi.project.DumbService; -import com.intellij.openapi.project.ModuleAdapter; +import com.intellij.openapi.project.ModuleListener; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.project.ex.ProjectManagerEx; @@ -305,7 +305,7 @@ public abstract class LightPlatformTestCase extends UsefulTestCase implements Da } MessageBusConnection connection = ourProject.getMessageBus().connect(parentDisposable); - connection.subscribe(ProjectTopics.MODULES, new ModuleAdapter() { + connection.subscribe(ProjectTopics.MODULES, new ModuleListener() { @Override public void moduleAdded(@NotNull Project project, @NotNull Module module) { fail("Adding modules is not permitted in LightIdeaTestCase."); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ModuleVcsDetector.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ModuleVcsDetector.java index 9a65d8d5debb..9fd464a1f6b7 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ModuleVcsDetector.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ModuleVcsDetector.java @@ -21,7 +21,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ProjectComponent; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; -import com.intellij.openapi.project.ModuleAdapter; +import com.intellij.openapi.project.ModuleListener; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModuleRootEvent; import com.intellij.openapi.roots.ModuleRootListener; @@ -82,7 +82,7 @@ public class ModuleVcsDetector implements ProjectComponent { }); } - private class MyModulesListener extends ModuleAdapter implements ModuleRootListener { + private class MyModulesListener implements ModuleRootListener, ModuleListener { private final List> myMappingsForRemovedModules = new ArrayList<>(); @Override diff --git a/plugins/git4idea/remote-servers-git-java/src/com/intellij/remoteServer/util/importProject/CloudGitChooseAccountStepImpl.java b/plugins/git4idea/remote-servers-git-java/src/com/intellij/remoteServer/util/importProject/CloudGitChooseAccountStepImpl.java index ba5340e1c362..4e273c4fc9c5 100644 --- a/plugins/git4idea/remote-servers-git-java/src/com/intellij/remoteServer/util/importProject/CloudGitChooseAccountStepImpl.java +++ b/plugins/git4idea/remote-servers-git-java/src/com/intellij/remoteServer/util/importProject/CloudGitChooseAccountStepImpl.java @@ -24,7 +24,7 @@ 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.project.ModuleAdapter; +import com.intellij.openapi.project.ModuleListener; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.startup.StartupManager; @@ -111,7 +111,7 @@ public class CloudGitChooseAccountStepImpl extends CloudGitChooseAccountStepBase @Override public void update(final @NotNull Module module, @NotNull ModifiableRootModel rootModel) { final MessageBusConnection connection = module.getProject().getMessageBus().connect(); - connection.subscribe(ProjectTopics.MODULES, new ModuleAdapter() { + connection.subscribe(ProjectTopics.MODULES, new ModuleListener() { @Override public void moduleAdded(@NotNull Project project, @NotNull Module addedModule) { diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/config/GradleResourceCompilerConfigurationGenerator.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/config/GradleResourceCompilerConfigurationGenerator.java index 00b8aab44712..14abae3cb9e5 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/config/GradleResourceCompilerConfigurationGenerator.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/config/GradleResourceCompilerConfigurationGenerator.java @@ -25,7 +25,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.externalSystem.model.project.ExternalSystemSourceType; import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; import com.intellij.openapi.module.Module; -import com.intellij.openapi.project.ModuleAdapter; +import com.intellij.openapi.project.ModuleListener; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectType; import com.intellij.openapi.project.ProjectTypeService; @@ -78,7 +78,7 @@ public class GradleResourceCompilerConfigurationGenerator { externalProjectDataCache = ExternalProjectDataCache.getInstance(project); assert externalProjectDataCache != null; - project.getMessageBus().connect(project).subscribe(ProjectTopics.MODULES, new ModuleAdapter() { + project.getMessageBus().connect(project).subscribe(ProjectTopics.MODULES, new ModuleListener() { @Override public void moduleRemoved(@NotNull Project project, @NotNull Module module) { myModulesConfigurationHash.remove(module.getName()); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcModuleStructureSynchronizer.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcModuleStructureSynchronizer.java index a1f9b8563b36..229c6aadbbad 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcModuleStructureSynchronizer.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcModuleStructureSynchronizer.java @@ -29,7 +29,7 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.util.ProgressIndicatorUtils; import com.intellij.openapi.progress.util.ReadTask; import com.intellij.openapi.project.DumbAwareRunnable; -import com.intellij.openapi.project.ModuleAdapter; +import com.intellij.openapi.project.ModuleListener; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; import com.intellij.openapi.startup.StartupManager; @@ -96,7 +96,7 @@ public class MvcModuleStructureSynchronizer extends AbstractProjectComponent { } }); - connection.subscribe(ProjectTopics.MODULES, new ModuleAdapter() { + connection.subscribe(ProjectTopics.MODULES, new ModuleListener() { @Override public void moduleAdded(@NotNull Project project, @NotNull Module module) { queue(SyncAction.UpdateProjectStructure, module); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManagerWatcher.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManagerWatcher.java index 122bad170bfc..f5c73ea84791 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManagerWatcher.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManagerWatcher.java @@ -26,7 +26,7 @@ import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl; import com.intellij.openapi.module.Module; -import com.intellij.openapi.project.ModuleAdapter; +import com.intellij.openapi.project.ModuleListener; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModuleRootEvent; import com.intellij.openapi.roots.ModuleRootListener; @@ -115,7 +115,7 @@ public class MavenProjectsManagerWatcher { myChangedDocumentsQueue.makeUserAware(myProject); myChangedDocumentsQueue.activate(); - myBusConnection.subscribe(ProjectTopics.MODULES, new ModuleAdapter() { + myBusConnection.subscribe(ProjectTopics.MODULES, new ModuleListener() { @Override public void moduleRemoved(@NotNull Project project, @NotNull Module module) { MavenProject mavenProject = myManager.findProject(module); diff --git a/python/ide/src/com/jetbrains/python/PyModuleListener.java b/python/ide/src/com/jetbrains/python/PyModuleListener.java index 31ffc87fa056..2646174ac178 100644 --- a/python/ide/src/com/jetbrains/python/PyModuleListener.java +++ b/python/ide/src/com/jetbrains/python/PyModuleListener.java @@ -19,7 +19,7 @@ import com.intellij.ProjectTopics; import com.intellij.execution.RunManagerEx; import com.intellij.execution.RunnerAndConfigurationSettings; import com.intellij.openapi.module.Module; -import com.intellij.openapi.project.ModuleAdapter; +import com.intellij.openapi.project.ModuleListener; import com.intellij.openapi.project.Project; import com.intellij.util.messages.MessageBus; import com.jetbrains.python.run.AbstractPythonRunConfiguration; @@ -33,7 +33,7 @@ import java.util.Collection; */ public class PyModuleListener { public PyModuleListener(MessageBus messageBus) { - messageBus.connect().subscribe(ProjectTopics.MODULES, new ModuleAdapter() { + messageBus.connect().subscribe(ProjectTopics.MODULES, new ModuleListener() { @Override public void beforeModuleRemoved(@NotNull Project project, @NotNull Module module) { final RunManagerEx runManager = RunManagerEx.getInstanceEx(project); From b556edfa9768091efa510779fb8cd3ef95bb5fdf Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Fri, 16 Dec 2016 11:29:13 +0100 Subject: [PATCH 318/558] =?UTF-8?q?avoid=20linear=20search=20=E2=80=94=20a?= =?UTF-8?q?dded/removed=20modules?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/impl/ModuleManagerImpl.java | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java index e208cefad6f1..2be30baf2073 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/ModuleManagerImpl.java @@ -934,10 +934,21 @@ public abstract class ModuleManagerImpl extends ModuleManager implements Project ApplicationManager.getApplication().assertWriteAccessAllowed(); final Collection oldModules = myModuleModel.myModules.values(); final Collection newModules = moduleModel.myModules.values(); - final List removedModules = new ArrayList<>(oldModules); - removedModules.removeAll(newModules); - final List addedModules = new ArrayList<>(newModules); - addedModules.removeAll(oldModules); + + final Collection addedModules; + final Collection removedModules; + if (oldModules.isEmpty()) { + // create immutable copy + addedModules = new ArrayList<>(newModules); + removedModules = Collections.emptyList(); + } + else { + addedModules = new THashSet<>(newModules); + addedModules.removeAll(oldModules); + + removedModules = new THashSet<>(oldModules); + removedModules.removeAll(newModules); + } ProjectRootManagerEx.getInstanceEx(myProject).makeRootsChange(() -> { for (Module removedModule : removedModules) { @@ -945,11 +956,13 @@ public abstract class ModuleManagerImpl extends ModuleManager implements Project cleanCachedStuff(); } - List neverAddedModules = new ArrayList<>(moduleModel.myModulesToDispose); - neverAddedModules.removeAll(myModuleModel.myModules.values()); - for (final Module neverAddedModule : neverAddedModules) { - neverAddedModule.putUserData(DISPOSED_MODULE_NAME, neverAddedModule.getName()); - Disposer.dispose(neverAddedModule); + if (!moduleModel.myModulesToDispose.isEmpty()) { + List neverAddedModules = new ArrayList<>(moduleModel.myModulesToDispose); + neverAddedModules.removeAll(myModuleModel.myModules.values()); + for (final Module neverAddedModule : neverAddedModules) { + neverAddedModule.putUserData(DISPOSED_MODULE_NAME, neverAddedModule.getName()); + Disposer.dispose(neverAddedModule); + } } if (runnable != null) { From 8719a0be53e26773584ae00b1c3558ad2f54aae6 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Sun, 18 Dec 2016 20:34:27 +0300 Subject: [PATCH 319/558] separate config for pycharm edu 3.5 --- .../intellij/build/pycharm/edu/PyCharmEduProperties.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/educational-python/build/groovy/org/jetbrains/intellij/build/pycharm/edu/PyCharmEduProperties.groovy b/python/educational-python/build/groovy/org/jetbrains/intellij/build/pycharm/edu/PyCharmEduProperties.groovy index 7c8723e1c5fc..8693c4b16143 100644 --- a/python/educational-python/build/groovy/org/jetbrains/intellij/build/pycharm/edu/PyCharmEduProperties.groovy +++ b/python/educational-python/build/groovy/org/jetbrains/intellij/build/pycharm/edu/PyCharmEduProperties.groovy @@ -38,7 +38,7 @@ class PyCharmEduProperties extends PyCharmPropertiesBase { @Override String getSystemSelector(ApplicationInfoProperties applicationInfo) { - "PyCharmEdu${applicationInfo.majorVersion}0" + "PyCharmEdu${applicationInfo.majorVersion}.${applicationInfo.minorVersionMainPart}" } @Override From 265d54fd9ec1702831df5a12c774f4f6bcc7aff1 Mon Sep 17 00:00:00 2001 From: "Gregory.Shrago" Date: Sun, 18 Dec 2016 03:37:05 +0300 Subject: [PATCH 320/558] mem leaks: cleanup rootPane (Cancel action leaked via javax.swing.KeyboardManager) 2 --- .../platform-api/src/com/intellij/openapi/ui/DialogWrapper.java | 2 +- .../intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java b/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java index 5d21ad4102d1..9822661c6c48 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java @@ -967,7 +967,7 @@ public abstract class DialogWrapper { }); } - private static void unregisterKeyboardActions(@Nullable JRootPane rootPane) { + public static void unregisterKeyboardActions(@Nullable Component rootPane) { int[] flags = {JComponent.WHEN_FOCUSED, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, JComponent.WHEN_IN_FOCUSED_WINDOW}; for (JComponent eachComp : UIUtil.uiTraverser(rootPane).traverse().filter(JComponent.class)) { ActionMap actionMap = eachComp.getActionMap(); diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java b/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java index fc22282153dc..cbea6dbd2ac4 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java @@ -580,6 +580,7 @@ public class GlassPaneDialogWrapperPeer extends DialogWrapperPeer implements Foc public void dispose() { setVisible(false); remove(getContentPane()); + DialogWrapper.unregisterKeyboardActions(myWrapperPane); myRootPane = null; } From 606895246b993d1dfb7a63c7101f9ce4fb367100 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Mon, 19 Dec 2016 09:59:13 +0700 Subject: [PATCH 321/558] StreamToLoopInspection: better solution for type correction on mutable collection results (IDEA-CR-16837) --- .../streamToLoop/TerminalOperation.java | 110 +++++++----------- 1 file changed, 42 insertions(+), 68 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInspection/streamToLoop/TerminalOperation.java b/java/java-impl/src/com/intellij/codeInspection/streamToLoop/TerminalOperation.java index 07989090d82c..294e6150d40d 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamToLoop/TerminalOperation.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamToLoop/TerminalOperation.java @@ -17,12 +17,12 @@ package com.intellij.codeInspection.streamToLoop; import com.intellij.codeInspection.streamToLoop.StreamToLoopInspection.StreamToLoopReplacementContext; import com.intellij.codeInspection.util.OptionalUtil; +import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTypesUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; -import com.intellij.util.ArrayUtil; import com.siyeh.ig.psiutils.BoolUtils; import com.siyeh.ig.psiutils.ExpressionUtils; import one.util.streamex.StreamEx; @@ -31,10 +31,11 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Arrays; +import java.util.Collections; +import java.util.Map; import java.util.Objects; import java.util.function.Consumer; import java.util.function.Function; -import java.util.stream.Stream; /** * @author Tagir Valeev @@ -286,64 +287,39 @@ abstract class TerminalOperation extends Operation { } /** - * Eliminates <? extends> wildcards which correspond to the first supplied superclass. - * If there are more than one superclass supplied, performs the same operation for the last generic argument. - * Do not touch unrelated generic arguments which don't map to superclass type parameters. + * Eliminates <? extends> wildcards from type parameters which directly map to the supplied superclass + * type parameters and performs downstream correction steps if necessary. * - * E.g.: - *
    {@code
    -   * (List, Collection) -> List
    -   * (MyList, Collection) -> MyList (assuming MyList extends List)
    -   * (HashMap, Map, Collection) -> HashMap>
    -   * (HashMap, Map) -> HashMap>
    -   * }
    - * - * @param type - * @param superClasses - * @return + * @param type type to process + * @param superClass superclass which type parameters should be corrected + * @param downstreamCorrectors Map which keys are superclass type parameter names and values are functions to perform additional + * superclass type parameter correction if necessary + * @return the corrected type. */ @NotNull - static PsiType eliminateCollectionWildcards(PsiType type, String... superClasses) { - if(superClasses.length == 0) return type; - String superClass = superClasses[0]; + static PsiType correctTypeParameters(PsiType type, String superClass, Map> downstreamCorrectors) { PsiClass aClass = PsiTypesUtil.getPsiClass(type); if(aClass == null) return type; - PsiTypeParameter[] parameters = aClass.getTypeParameters(); - if(parameters.length == 0) return type; - PsiSubstitutor substitutor = ((PsiClassType)type).resolveGenerics().getSubstitutor(); - PsiElementFactory factory = JavaPsiFacade.getElementFactory(aClass.getProject()); - PsiClassType classType = factory.createType(aClass, Stream.of(parameters).map(factory::createType).toArray(PsiType[]::new)); - PsiClass baseClass = JavaPsiFacade.getInstance(aClass.getProject()).findClass(superClass, aClass.getResolveScope()); + PsiSubstitutor origSubstitutor = ((PsiClassType)type).resolveGenerics().getSubstitutor(); + PsiSubstitutor substitutor = origSubstitutor; + Project project = aClass.getProject(); + PsiClass baseClass = JavaPsiFacade.getInstance(project).findClass(superClass, aClass.getResolveScope()); if(baseClass == null) return type; - PsiTypeParameter[] baseClassTypeParameters = baseClass.getTypeParameters(); - - for (int idx = 0; idx < baseClassTypeParameters.length; idx++) { - PsiClass parameter = PsiTypesUtil.getPsiClass(PsiUtil.substituteTypeParameter(classType, superClass, idx, false)); - if(parameter instanceof PsiTypeParameter) { - PsiType origType = PsiUtil.substituteTypeParameter(type, superClass, idx, false); - PsiType replacedType = origType; - if(origType instanceof PsiCapturedWildcardType) { - replacedType = ((PsiCapturedWildcardType)origType).getUpperBound(); - } - if(origType instanceof PsiWildcardType) { - replacedType = ((PsiWildcardType)origType).getExtendsBound(); - } - if (idx == baseClassTypeParameters.length - 1) { - replacedType = eliminateCollectionWildcards(replacedType, Arrays.copyOfRange(superClasses, 1, superClasses.length)); - } + PsiSubstitutor superClassSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(baseClass, aClass, PsiSubstitutor.EMPTY); + for (PsiTypeParameter baseParameter : baseClass.getTypeParameters()) { + PsiClass substitution = PsiTypesUtil.getPsiClass(superClassSubstitutor.substitute(baseParameter)); + if(substitution instanceof PsiTypeParameter) { + PsiTypeParameter subClassParameter = (PsiTypeParameter)substitution; + PsiType origType = origSubstitutor.substitute(subClassParameter); + PsiType replacedType = GenericsUtil.eliminateWildcards(origType, false, true); + replacedType = downstreamCorrectors.getOrDefault(subClassParameter.getName(), Function.identity()).apply(replacedType); if(replacedType != origType) { - substitutor = substitutor.put((PsiTypeParameter)parameter, replacedType); + substitutor = substitutor.put(subClassParameter, replacedType); } } } - return factory.createType(aClass, substitutor); - } - - @NotNull - static String eliminateCollectionWildcards(StreamToLoopReplacementContext context, - String typeText, String... superClasses) { - return eliminateCollectionWildcards(context.createType(typeText), superClasses).getCanonicalText(); + return substitutor == origSubstitutor ? type : JavaPsiFacade.getElementFactory(project).createType(aClass, substitutor); } static class ReduceTerminalOperation extends TerminalOperation { @@ -577,10 +553,7 @@ abstract class TerminalOperation extends Operation { String getSupplier(); String getAccumulator(String acc, String item); - // Returns array of base classes for which "? extends" should be eliminated from generic arguments - // Several elements are used for downstream collection, in this case the first element is java.util.Map - // and second one corresponds to the map value - default String[] getBaseClassChain() {return ArrayUtil.EMPTY_STRING_ARRAY;} + default PsiType correctReturnType(PsiType type) {return type;} } abstract static class CollectorBasedTerminalOperation extends TerminalOperation implements CollectorOperation { @@ -598,8 +571,8 @@ abstract class TerminalOperation extends Operation { @Override String generate(StreamVariable inVar, StreamToLoopReplacementContext context) { transform(context, inVar.getName()); - String acc = context.declareResult(myAccNameSupplier.apply(context), - eliminateCollectionWildcards(context, myType, getBaseClassChain()), getSupplier(), true); + PsiType resultType = correctReturnType(context.createType(myType)); + String acc = context.declareResult(myAccNameSupplier.apply(context), resultType.getCanonicalText(), getSupplier(), true); return getAccumulator(acc, inVar.getName()); } @@ -697,8 +670,8 @@ abstract class TerminalOperation extends Operation { } @Override - public String[] getBaseClassChain() { - return new String[] {CommonClassNames.JAVA_UTIL_COLLECTION}; + public PsiType correctReturnType(PsiType type) { + return correctTypeParameters(type, CommonClassNames.JAVA_UTIL_COLLECTION, Collections.emptyMap()); } @NotNull @@ -786,8 +759,8 @@ abstract class TerminalOperation extends Operation { } @Override - public String[] getBaseClassChain() { - return new String[] {CommonClassNames.JAVA_UTIL_MAP}; + public PsiType correctReturnType(PsiType type) { + return correctTypeParameters(type, CommonClassNames.JAVA_UTIL_MAP, Collections.emptyMap()); } @Override @@ -854,8 +827,8 @@ abstract class TerminalOperation extends Operation { } @Override - public String[] getBaseClassChain() { - return ArrayUtil.prepend(CommonClassNames.JAVA_UTIL_MAP, myCollector.getBaseClassChain()); + public PsiType correctReturnType(PsiType type) { + return correctTypeParameters(type, CommonClassNames.JAVA_UTIL_MAP, Collections.singletonMap("V", myCollector::correctReturnType)); } @Override @@ -911,13 +884,14 @@ abstract class TerminalOperation extends Operation { @Override String generate(StreamVariable inVar, StreamToLoopReplacementContext context) { - String resultType = eliminateCollectionWildcards(context, myResultType, - ArrayUtil.prepend(CommonClassNames.JAVA_UTIL_MAP, myCollector.getBaseClassChain())); - String map = context.declareResult("map", resultType, "new java.util.HashMap<>()", true); + PsiType resultType = context.createType(myResultType); + resultType = correctTypeParameters(resultType, CommonClassNames.JAVA_UTIL_MAP, + Collections.singletonMap("V", myCollector::correctReturnType)); + String map = context.declareResult("map", resultType.getCanonicalText(), "new java.util.HashMap<>()", true); myPredicate.transform(context, inVar.getName()); myCollector.transform(context, inVar.getName()); - context.addInitStep(map+".put(false, "+myCollector.getSupplier()+");"); - context.addInitStep(map+".put(true, "+myCollector.getSupplier()+");"); + context.addInitStep(map + ".put(false, " + myCollector.getSupplier() + ");"); + context.addInitStep(map + ".put(true, " + myCollector.getSupplier() + ");"); return myCollector.getAccumulator(map + ".get(" + myPredicate.getText() + ")", inVar.getName()); } } @@ -945,8 +919,8 @@ abstract class TerminalOperation extends Operation { } @Override - public String[] getBaseClassChain() { - return myDownstreamCollector.getBaseClassChain(); + public PsiType correctReturnType(PsiType type) { + return myDownstreamCollector.correctReturnType(type); } @Override From 0880fab77164f7cd58a531fee31fee74444d47d7 Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Fri, 16 Dec 2016 14:56:48 +0300 Subject: [PATCH 322/558] printing code cleanup --- .../codeEditor/printing/MultiFilePainter.java | 12 ++-- .../codeEditor/printing/PrintManager.java | 57 +++++++++---------- .../codeEditor/printing/TextPainter.java | 6 +- 3 files changed, 33 insertions(+), 42 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeEditor/printing/MultiFilePainter.java b/platform/lang-impl/src/com/intellij/codeEditor/printing/MultiFilePainter.java index 280614d0b1fc..d40f50231e34 100644 --- a/platform/lang-impl/src/com/intellij/codeEditor/printing/MultiFilePainter.java +++ b/platform/lang-impl/src/com/intellij/codeEditor/printing/MultiFilePainter.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -15,9 +15,7 @@ */ package com.intellij.codeEditor.printing; -import com.intellij.openapi.editor.Editor; import com.intellij.openapi.progress.ProcessCanceledException; -import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiFile; import java.awt.*; @@ -27,12 +25,12 @@ import java.awt.print.PrinterException; import java.util.List; class MultiFilePainter extends BasePainter { - private final List> myFilesList; + private final List myFilesList; private int myFileIndex = 0; private int myStartPageIndex = 0; private TextPainter myTextPainter = null; - public MultiFilePainter(List> filesList) { + public MultiFilePainter(List filesList) { myFilesList = filesList; } @@ -43,8 +41,8 @@ class MultiFilePainter extends BasePainter { } while (myFileIndex < myFilesList.size()) { if (myTextPainter == null) { - Pair pair = myFilesList.get(myFileIndex); - myTextPainter = PrintManager.initTextPainter(pair.first, pair.second); + PsiFile psiFile = myFilesList.get(myFileIndex); + myTextPainter = PrintManager.initTextPainter(psiFile); } if (myTextPainter != null) { myTextPainter.setProgress(myProgress); diff --git a/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintManager.java b/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintManager.java index 382a03a15ff6..670270666b07 100644 --- a/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintManager.java +++ b/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 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,18 +31,20 @@ import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.progress.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; -import com.intellij.psi.util.PsiUtilBase; import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; -import java.awt.print.*; +import java.awt.print.PageFormat; +import java.awt.print.Paper; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; +import java.util.Collections; import java.util.List; class PrintManager { @@ -52,24 +54,24 @@ class PrintManager { final Project project = CommonDataKeys.PROJECT.getData(dataContext); if (project == null) return; - PsiDirectory[] psiDirectory = new PsiDirectory[1]; + PsiDirectory psiDirectory = null; PsiElement psiElement = CommonDataKeys.PSI_ELEMENT.getData(dataContext); if (psiElement instanceof PsiDirectory) { - psiDirectory[0] = (PsiDirectory)psiElement; + psiDirectory = (PsiDirectory)psiElement; } PsiFile psiFile = CommonDataKeys.PSI_FILE.getData(dataContext); - String[] shortFileName = new String[1]; - String[] directoryName = new String[1]; - if (psiFile != null || psiDirectory[0] != null) { + String shortFileName = null; + String directoryName = null; + if (psiFile != null || psiDirectory != null) { if (psiFile != null) { - shortFileName[0] = psiFile.getName(); - if (psiDirectory[0] == null) { - psiDirectory[0] = psiFile.getContainingDirectory(); + shortFileName = psiFile.getName(); + if (psiDirectory == null) { + psiDirectory = psiFile.getContainingDirectory(); } } - if (psiDirectory[0] != null) { - directoryName[0] = psiDirectory[0].getVirtualFile().getPresentableUrl(); + if (psiDirectory != null) { + directoryName = psiDirectory.getVirtualFile().getPresentableUrl(); } } @@ -84,7 +86,7 @@ class PrintManager { } } - PrintDialog printDialog = new PrintDialog(shortFileName[0], directoryName[0], text, project); + PrintDialog printDialog = new PrintDialog(shortFileName, directoryName, text, project); printDialog.reset(); if (!printDialog.showAndGet()) { return; @@ -98,7 +100,7 @@ class PrintManager { if (printSettings.getPrintScope() != PrintSettings.PRINT_DIRECTORY) { if (psiFile == null && editor == null) return; TextPainter textPainter = - psiFile != null ? initTextPainter(psiFile, editor) : initTextPainter((DocumentEx)editor.getDocument(), project); + psiFile != null ? initTextPainter(psiFile) : initTextPainter((DocumentEx)editor.getDocument(), project); if (textPainter == null) return; if (printSettings.getPrintScope() == PrintSettings.PRINT_SELECTED_TEXT && @@ -109,9 +111,9 @@ class PrintManager { painter = textPainter; } else { - List> filesList = ContainerUtil.newArrayList(); + List filesList = ContainerUtil.newArrayList(); boolean isRecursive = printSettings.isIncludeSubdirectories(); - addToPsiFileList(psiDirectory[0], filesList, isRecursive); + addToPsiFileList(psiDirectory, filesList, isRecursive); painter = new MultiFilePainter(filesList); } @@ -155,11 +157,9 @@ class PrintManager { }); } - private static void addToPsiFileList(PsiDirectory psiDirectory, List> filesList, boolean isRecursive) { + private static void addToPsiFileList(PsiDirectory psiDirectory, List filesList, boolean isRecursive) { PsiFile[] files = psiDirectory.getFiles(); - for (PsiFile file : files) { - filesList.add(Pair.create(file, PsiUtilBase.findEditor(file))); - } + Collections.addAll(filesList, files); if (isRecursive) { for (PsiDirectory directory : psiDirectory.getSubdirectories()) { if (!Project.DIRECTORY_STORE_FOLDER.equals(directory.getName())) { @@ -198,16 +198,11 @@ class PrintManager { return pageFormat; } - static TextPainter initTextPainter(final PsiFile psiFile, final Editor editor) { - return ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public TextPainter compute() { - return doInitTextPainter(psiFile, editor); - } - }); + static TextPainter initTextPainter(final PsiFile psiFile) { + return ApplicationManager.getApplication().runReadAction((Computable)() -> doInitTextPainter(psiFile)); } - private static TextPainter doInitTextPainter(final PsiFile psiFile, final Editor editor) { + private static TextPainter doInitTextPainter(final PsiFile psiFile) { VirtualFile virtualFile = psiFile.getVirtualFile(); if (virtualFile == null) return null; DocumentEx doc = (DocumentEx)PsiDocumentManager.getInstance(psiFile.getProject()).getDocument(psiFile); @@ -215,7 +210,7 @@ class PrintManager { EditorHighlighter highlighter = HighlighterFactory.createHighlighter(psiFile.getProject(), virtualFile); highlighter.setText(doc.getCharsSequence()); return new TextPainter(doc, highlighter, virtualFile.getPresentableUrl(), virtualFile.getPresentableName(), - psiFile, psiFile.getFileType(), editor); + psiFile, psiFile.getFileType()); } private static TextPainter initTextPainter(@NotNull final DocumentEx doc, final Project project) { diff --git a/platform/lang-impl/src/com/intellij/codeEditor/printing/TextPainter.java b/platform/lang-impl/src/com/intellij/codeEditor/printing/TextPainter.java index 726d5cfb9736..2cc29e92a811 100644 --- a/platform/lang-impl/src/com/intellij/codeEditor/printing/TextPainter.java +++ b/platform/lang-impl/src/com/intellij/codeEditor/printing/TextPainter.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 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,7 +17,6 @@ package com.intellij.codeEditor.printing; import com.intellij.codeInsight.daemon.LineMarkerInfo; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.editor.ex.LineIterator; @@ -97,8 +96,7 @@ class TextPainter extends BasePainter { String fullFileName, String shortFileName, @NotNull PsiFile psiFile, - FileType fileType, - Editor editor) { + FileType fileType) { this(editorDocument, highlighter, fullFileName, shortFileName, psiFile.getProject(), fileType, FileSeparatorProvider.getFileSeparators(psiFile, editorDocument)); } From 376ca21484ba00bf9603aecdc58c213d1975a5a4 Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Fri, 16 Dec 2016 16:26:24 +0300 Subject: [PATCH 323/558] allow to print arbitrary number of selected files (IDEA-164472) --- .../codeEditor/printing/PrintAction.java | 21 ++++----- .../codeEditor/printing/PrintDialog.java | 19 +++++--- .../codeEditor/printing/PrintManager.java | 43 +++++++++++++------ .../src/messages/CodeEditorBundle.properties | 1 + 4 files changed, 52 insertions(+), 32 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintAction.java b/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintAction.java index 6e32fb0b98f1..c181f59a9cd6 100644 --- a/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintAction.java +++ b/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,25 +24,20 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; public class PrintAction extends AnAction implements DumbAware { - public PrintAction() { - super(); - - } - @Override public void actionPerformed(AnActionEvent e) { - DataContext dataContext = e.getDataContext(); - Project project = CommonDataKeys.PROJECT.getData(dataContext); - if (project == null) { - return; - } - PrintManager.executePrint(dataContext); + PrintManager.executePrint(e.getDataContext()); } @Override public void update(AnActionEvent event){ Presentation presentation = event.getPresentation(); DataContext dataContext = event.getDataContext(); + Project project = CommonDataKeys.PROJECT.getData(dataContext); + if (project == null) { + presentation.setEnabled(false); + return; + } VirtualFile file = CommonDataKeys.VIRTUAL_FILE.getData(dataContext); if(file != null && file.isDirectory()) { presentation.setEnabled(true); @@ -50,7 +45,7 @@ public class PrintAction extends AnAction implements DumbAware { } Editor editor = CommonDataKeys.EDITOR.getData(dataContext); PsiFile psiFile = CommonDataKeys.PSI_FILE.getData(dataContext); - presentation.setEnabled(psiFile != null || editor != null); + presentation.setEnabled(psiFile != null || editor != null || !PrintManager.getSelectedPsiFiles(dataContext).isEmpty()); } } \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintDialog.java b/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintDialog.java index 47dc9246a0c2..79d05866df61 100644 --- a/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintDialog.java +++ b/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintDialog.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 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,7 +31,8 @@ import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.*; +import java.util.HashMap; +import java.util.Map; class PrintDialog extends DialogWrapper { private JRadioButton myRbCurrentFile = null; @@ -74,6 +75,7 @@ class PrintDialog extends DialogWrapper { private String myFileName = null; private String myDirectoryName = null; private final boolean isSelectedTextEnabled; + private final int mySelectedFileCount; private static final Map PLACEMENT_MAP = new HashMap<>(); private static final Map ALIGNMENT_MAP = new HashMap<>(); @@ -89,13 +91,14 @@ class PrintDialog extends DialogWrapper { } - public PrintDialog(String fileName, String directoryName, String selectedText, Project project) { + public PrintDialog(String fileName, String directoryName, String selectedText, int selectedFileCount, Project project) { super(project, true); mySelectedText = selectedText; setOKButtonText(CodeEditorBundle.message("print.print.button")); myFileName = fileName; myDirectoryName = directoryName; - this.isSelectedTextEnabled = selectedText != null; + isSelectedTextEnabled = selectedText != null; + mySelectedFileCount = selectedFileCount; setTitle(CodeEditorBundle.message("print.title")); init(); } @@ -115,7 +118,9 @@ class PrintDialog extends DialogWrapper { gbConstraints.fill = GridBagConstraints.BOTH; gbConstraints.insets = new Insets(0,0,0,0); - myRbCurrentFile = new JRadioButton(CodeEditorBundle.message("print.file.name.radio", (myFileName != null ? myFileName : ""))); + myRbCurrentFile = new JRadioButton(mySelectedFileCount > 1 ? CodeEditorBundle.message("print.files.radio", mySelectedFileCount) + : CodeEditorBundle.message("print.file.name.radio", + (myFileName != null ? myFileName : ""))); panel.add(myRbCurrentFile, gbConstraints); myRbSelectedText = new JRadioButton(mySelectedText != null ? mySelectedText : CodeEditorBundle.message("print.selected.text.radio")); @@ -504,8 +509,8 @@ class PrintDialog extends DialogWrapper { myRbSelectedText.setEnabled(isSelectedTextEnabled); myRbSelectedText.setSelected(isSelectedTextEnabled); - myRbCurrentFile.setEnabled(myFileName != null); - myRbCurrentFile.setSelected(myFileName != null && !isSelectedTextEnabled); + myRbCurrentFile.setEnabled(myFileName != null || mySelectedFileCount > 1); + myRbCurrentFile.setSelected(myFileName != null && !isSelectedTextEnabled || mySelectedFileCount > 1); myRbCurrentPackage.setEnabled(myDirectoryName != null); myRbCurrentPackage.setSelected(myDirectoryName != null && !isSelectedTextEnabled && myFileName == null); diff --git a/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintManager.java b/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintManager.java index 670270666b07..84d41922548f 100644 --- a/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintManager.java +++ b/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintManager.java @@ -32,10 +32,7 @@ import com.intellij.openapi.progress.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiDirectory; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; +import com.intellij.psi.*; import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -44,6 +41,7 @@ import java.awt.print.PageFormat; import java.awt.print.Paper; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -86,7 +84,9 @@ class PrintManager { } } - PrintDialog printDialog = new PrintDialog(shortFileName, directoryName, text, project); + List psiFiles = getSelectedPsiFiles(dataContext); + + PrintDialog printDialog = new PrintDialog(shortFileName, directoryName, text, psiFiles.size(), project); printDialog.reset(); if (!printDialog.showAndGet()) { return; @@ -97,7 +97,16 @@ class PrintManager { final BasePainter painter; PrintSettings printSettings = PrintSettings.getInstance(); - if (printSettings.getPrintScope() != PrintSettings.PRINT_DIRECTORY) { + if (printSettings.getPrintScope() == PrintSettings.PRINT_FILE && psiFiles.size() > 1) { + painter = new MultiFilePainter(psiFiles); + } + else if (printSettings.getPrintScope() == PrintSettings.PRINT_DIRECTORY) { + List filesList = ContainerUtil.newArrayList(); + boolean isRecursive = printSettings.isIncludeSubdirectories(); + addToPsiFileList(psiDirectory, filesList, isRecursive); + painter = new MultiFilePainter(filesList); + } + else { if (psiFile == null && editor == null) return; TextPainter textPainter = psiFile != null ? initTextPainter(psiFile) : initTextPainter((DocumentEx)editor.getDocument(), project); @@ -110,12 +119,6 @@ class PrintManager { } painter = textPainter; } - else { - List filesList = ContainerUtil.newArrayList(); - boolean isRecursive = printSettings.isIncludeSubdirectories(); - addToPsiFileList(psiDirectory, filesList, isRecursive); - painter = new MultiFilePainter(filesList); - } final PrinterJob printerJob = PrinterJob.getPrinterJob(); try { @@ -169,6 +172,22 @@ class PrintManager { } } + static List getSelectedPsiFiles(DataContext dataContext) { + Project project = CommonDataKeys.PROJECT.getData(dataContext); + if (project == null) return Collections.emptyList(); + VirtualFile[] virtualFiles = CommonDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext); + if (virtualFiles == null) return Collections.emptyList(); + PsiManager psiManager = PsiManager.getInstance(project); + List psiFiles = new ArrayList<>(virtualFiles.length); + for (VirtualFile virtualFile : virtualFiles) { + if (virtualFile.isDirectory()) return Collections.emptyList(); + PsiFile psiFile = psiManager.findFile(virtualFile); + if (psiFile == null) return Collections.emptyList(); + psiFiles.add(psiFile); + } + return psiFiles; + } + private static PageFormat createPageFormat() { PrintSettings printSettings = PrintSettings.getInstance(); PageFormat pageFormat = new PageFormat(); diff --git a/platform/platform-resources-en/src/messages/CodeEditorBundle.properties b/platform/platform-resources-en/src/messages/CodeEditorBundle.properties index bf6fa5ebef76..1146885224df 100644 --- a/platform/platform-resources-en/src/messages/CodeEditorBundle.properties +++ b/platform/platform-resources-en/src/messages/CodeEditorBundle.properties @@ -17,6 +17,7 @@ print.page.size.unit.mm=mm print.page.size.unit.in=in print.print.button=Print print.title=Print +print.files.radio=Selected files ({0}) print.file.name.radio=File {0} print.selected.text.radio=Selected text print.all.files.in.directory.radio=All files in directory {0} From 5cbcf18d96762a8409fbedc20f5476bdbfbd70ec Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Fri, 16 Dec 2016 18:40:29 +0300 Subject: [PATCH 324/558] convenience option for duplex printing of multiple files (IDEA-164472) --- .../codeEditor/printing/MultiFilePainter.java | 11 ++++++++- .../codeEditor/printing/PrintDialog.java | 23 ++++++++++++------- .../codeEditor/printing/PrintManager.java | 4 ++-- .../codeEditor/printing/PrintSettings.java | 2 ++ .../src/messages/CodeEditorBundle.properties | 1 + 5 files changed, 30 insertions(+), 11 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeEditor/printing/MultiFilePainter.java b/platform/lang-impl/src/com/intellij/codeEditor/printing/MultiFilePainter.java index d40f50231e34..e423a28ca22b 100644 --- a/platform/lang-impl/src/com/intellij/codeEditor/printing/MultiFilePainter.java +++ b/platform/lang-impl/src/com/intellij/codeEditor/printing/MultiFilePainter.java @@ -26,12 +26,15 @@ import java.util.List; class MultiFilePainter extends BasePainter { private final List myFilesList; + private final boolean myEvenNumberOfPagesPerFile; private int myFileIndex = 0; private int myStartPageIndex = 0; private TextPainter myTextPainter = null; + private int myLargestPrintedPage = -1; - public MultiFilePainter(List filesList) { + public MultiFilePainter(List filesList, boolean evenNumberOfPagesPerFile) { myFilesList = filesList; + myEvenNumberOfPagesPerFile = evenNumberOfPagesPerFile; } @Override @@ -57,13 +60,19 @@ class MultiFilePainter extends BasePainter { return Printable.NO_SUCH_PAGE; } if (ret == Printable.PAGE_EXISTS) { + myLargestPrintedPage = pageIndex; return Printable.PAGE_EXISTS; } + if (myEvenNumberOfPagesPerFile && pageIndex == (myLargestPrintedPage + 1) && (pageIndex % 2) == 1 && + myFileIndex < (myFilesList.size() - 1)) { + return PAGE_EXISTS; + } myTextPainter.dispose(); myTextPainter = null; myStartPageIndex = pageIndex; } myFileIndex++; + myLargestPrintedPage = -1; } return Printable.NO_SUCH_PAGE; } diff --git a/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintDialog.java b/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintDialog.java index 79d05866df61..0d994b1221bd 100644 --- a/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintDialog.java +++ b/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintDialog.java @@ -63,6 +63,7 @@ class PrintDialog extends DialogWrapper { private JTextField myRightMarginField = null; private JCheckBox myCbDrawBorder = null; + private JCheckBox myCbEvenNumberOfPages = null; private JTextField myLineTextField1 = null; private JComboBox myLinePlacementCombo1 = null; @@ -144,12 +145,7 @@ class PrintDialog extends DialogWrapper { buttonGroup.add(myRbSelectedText); buttonGroup.add(myRbCurrentPackage); - ActionListener actionListener = new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - myCbIncludeSubpackages.setEnabled(myRbCurrentPackage.isSelected()); - } - }; + ActionListener actionListener = e -> updateDependentComponents(); myRbCurrentFile.addActionListener(actionListener); myRbSelectedText.addActionListener(actionListener); @@ -158,6 +154,11 @@ class PrintDialog extends DialogWrapper { return panel; } + private void updateDependentComponents() { + myCbIncludeSubpackages.setEnabled(myRbCurrentPackage.isSelected()); + myCbEvenNumberOfPages.setVisible(myRbCurrentFile.isSelected() && mySelectedFileCount > 1 || myRbCurrentPackage.isSelected()); + } + @Override protected JComponent createCenterPanel() { TabbedPaneWrapper tabbedPaneWrapper = new TabbedPaneWrapper(myDisposable); @@ -211,6 +212,10 @@ class PrintDialog extends DialogWrapper { gbConstraints.gridy++; panel.add(myCbDrawBorder, gbConstraints); + myCbEvenNumberOfPages = new JCheckBox(CodeEditorBundle.message("print.settings.even.number.of.pages")); + gbConstraints.gridy++; + panel.add(myCbEvenNumberOfPages, gbConstraints); + gbConstraints.insets = new Insets(0, 0, 6, 4); gbConstraints.gridx = 0; gbConstraints.gridwidth = 3; @@ -515,7 +520,8 @@ class PrintDialog extends DialogWrapper { myRbCurrentPackage.setSelected(myDirectoryName != null && !isSelectedTextEnabled && myFileName == null); myCbIncludeSubpackages.setSelected(printSettings.isIncludeSubdirectories()); - myCbIncludeSubpackages.setEnabled(myRbCurrentPackage.isSelected()); + + updateDependentComponents(); Object selectedPageSize = PageSizes.getItem(printSettings.PAPER_SIZE); if(selectedPageSize != null) { @@ -549,7 +555,7 @@ class PrintDialog extends DialogWrapper { myRightMarginField.setText(String.valueOf(printSettings.RIGHT_MARGIN)); myCbDrawBorder.setSelected(printSettings.DRAW_BORDER); - + myCbEvenNumberOfPages.setSelected(printSettings.EVEN_NUMBER_OF_PAGES); myLineTextField1.setText(printSettings.FOOTER_HEADER_TEXT1); myLinePlacementCombo1.setSelectedItem(printSettings.FOOTER_HEADER_PLACEMENT1); @@ -618,6 +624,7 @@ class PrintDialog extends DialogWrapper { catch(NumberFormatException ignored) { } printSettings.DRAW_BORDER = myCbDrawBorder.isSelected(); + printSettings.EVEN_NUMBER_OF_PAGES = myCbEvenNumberOfPages.isSelected(); printSettings.FOOTER_HEADER_TEXT1 = myLineTextField1.getText(); printSettings.FOOTER_HEADER_ALIGNMENT1 = (String)myLineAlignmentCombo1.getSelectedItem(); printSettings.FOOTER_HEADER_PLACEMENT1 = (String)myLinePlacementCombo1.getSelectedItem(); diff --git a/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintManager.java b/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintManager.java index 84d41922548f..9ea449c91ba5 100644 --- a/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintManager.java +++ b/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintManager.java @@ -98,13 +98,13 @@ class PrintManager { PrintSettings printSettings = PrintSettings.getInstance(); if (printSettings.getPrintScope() == PrintSettings.PRINT_FILE && psiFiles.size() > 1) { - painter = new MultiFilePainter(psiFiles); + painter = new MultiFilePainter(psiFiles, printSettings.EVEN_NUMBER_OF_PAGES); } else if (printSettings.getPrintScope() == PrintSettings.PRINT_DIRECTORY) { List filesList = ContainerUtil.newArrayList(); boolean isRecursive = printSettings.isIncludeSubdirectories(); addToPsiFileList(psiDirectory, filesList, isRecursive); - painter = new MultiFilePainter(filesList); + painter = new MultiFilePainter(filesList, printSettings.EVEN_NUMBER_OF_PAGES); } else { if (psiFile == null && editor == null) return; diff --git a/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintSettings.java b/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintSettings.java index ee5d4ae74d4a..1f5d96ae0b35 100644 --- a/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintSettings.java +++ b/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintSettings.java @@ -47,6 +47,8 @@ public class PrintSettings implements PersistentStateComponent { public boolean DRAW_BORDER = true; + public boolean EVEN_NUMBER_OF_PAGES = false; + public String FOOTER_HEADER_TEXT1 = CodeEditorBundle.message("print.header.default.line.1"); public String FOOTER_HEADER_PLACEMENT1 = HEADER; public String FOOTER_HEADER_ALIGNMENT1 = LEFT; diff --git a/platform/platform-resources-en/src/messages/CodeEditorBundle.properties b/platform/platform-resources-en/src/messages/CodeEditorBundle.properties index 1146885224df..62995621785c 100644 --- a/platform/platform-resources-en/src/messages/CodeEditorBundle.properties +++ b/platform/platform-resources-en/src/messages/CodeEditorBundle.properties @@ -29,6 +29,7 @@ print.settings.paper.size.label=Paper size print.settings.font.label=Font print.settings.show.line.numbers.checkbox=Show line numbers print.settings.draw.border.checkbox=Draw border +print.settings.even.number.of.pages=Even number of pages per file (for duplex printing) print.orientation.group=Orientation print.orientation.portrait.radio=Portrait print.orientation.landscape.radio=Landscape From 12e86f8f1d0ac47ab6a65112ba00710e1d5fc8fa Mon Sep 17 00:00:00 2001 From: nik Date: Mon, 19 Dec 2016 11:13:14 +0300 Subject: [PATCH 325/558] disable 'Use compiler from module JDK' option in IDEA community project Currently some of modules in IDEA project have JDK 1.6 as module JDK but depend on libraries with newer versions of class-files, so they cannot be compiled by javac from their target JDK. Also build is slightly faster if the same javac is used to compile all modules. --- .idea/compiler.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/.idea/compiler.xml b/.idea/compiler.xml index d6f90132a937..f0b2306fe0a7 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -43,6 +43,7 @@