Merge remote-tracking branch 'origin/master'

This commit is contained in:
Ekaterina Tuzova
2014-11-05 15:40:38 +03:00
13 changed files with 183 additions and 736 deletions
@@ -59,6 +59,7 @@ public class JBTable extends JTable implements ComponentWithEmptyText, Component
private AsyncProcessIcon myBusyIcon;
private boolean myBusy;
private int myMaxItemsForSizeCalculation = Integer.MAX_VALUE;
public JBTable() {
this(new DefaultTableModel());
@@ -153,8 +154,8 @@ public class JBTable extends JTable implements ComponentWithEmptyText, Component
if (myRowHeight < 0) {
try {
myRowHeightIsComputing = true;
for (int row = 0; row < getRowCount(); row++) {
for (int column = 0; column < getColumnCount(); column++) {
for (int row = 0; row < Math.min(getRowCount(), myMaxItemsForSizeCalculation); row++) {
for (int column = 0; column < Math.min(getColumnCount(), myMaxItemsForSizeCalculation); column++) {
final TableCellRenderer renderer = getCellRenderer(row, column);
if (renderer != null) {
final Object value = getValueAt(row, column);
@@ -818,4 +819,15 @@ public class JBTable extends JTable implements ComponentWithEmptyText, Component
return resizingAllowed && columnModel.getColumn(columnIdx).getResizable();
}
}
/**
* JTable gets table data from model lazily - only for a table part to be shown.
* JBTable loads <i>all</i> the data on initialization to calculate cell size.
* This methods provides possibility to calculate size without loading all the table data.
*
* @param maxItemsForSizeCalculation maximum number ot items in table to be loaded for size calculation
*/
public void setMaxItemsForSizeCalculation(int maxItemsForSizeCalculation) {
myMaxItemsForSizeCalculation = maxItemsForSizeCalculation;
}
}
@@ -20,4 +20,6 @@ public interface PyFrameAccessor {
@Nullable
PyReferrersLoader getReferrersLoader();
Object[][] getArrayItems(PyDebugValue var, int colOffset, int rowOffset, int cols, int rows, String format);
}
@@ -531,6 +531,11 @@ public class PydevConsoleCommunication extends AbstractConsoleCommunication impl
return null;
}
@Override
public Object[][] getArrayItems(PyDebugValue var, int colOffset, int rowOffset, int cols, int rows, String format) {
return new Object[][]{new Object[]{1, 2}, new Object[]{3, 4}};
}
/**
* Request that pydevconsole connect (with pydevd) to the specified port
*
@@ -571,6 +571,11 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
return myReferrersProvider;
}
@Override
public Object[][] getArrayItems(PyDebugValue var, int colOffset, int rowOffset, int cols, int rows, String format) {
return new Object[][]{new Object[]{1, 2}, new Object[]{3, 4}};
}
@Nullable
public String loadSource(String path) {
return myDebugger.loadSource(path);
@@ -1,96 +0,0 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.debugger.array;
import org.jetbrains.annotations.NotNull;
/**
* @author amarch
*/
public abstract class ArrayChunk implements Comparable<ArrayChunk> {
private final String myBaseSlice;
private final int myColumns;
private final int myRows;
private final int myColOffset;
private final int myRowOffset;
private Object[][] myData;
public ArrayChunk(String baseSlice, int rows, int columns, int rOffset, int cOffset) {
myBaseSlice = baseSlice;
myColumns = columns;
myRows = rows;
myRowOffset = rOffset;
myColOffset = cOffset;
}
public int getRows() {
return myRows;
}
public int getColumns() {
return myColumns;
}
public int getColOffset() {
return myColOffset;
}
public int getRowOffset() {
return myRowOffset;
}
public Object[][] getData() {
return myData;
}
public String getBaseSlice() {
return myBaseSlice;
}
public String getPresentation() {
return "";
}
abstract void fillData(Runnable callback);
public boolean contains(int row, int col) {
return myRowOffset <= row && row < myRowOffset + myRows && myColOffset <= col && col < myColOffset + myColumns;
}
public boolean equals(Object o) {
return o instanceof ArrayChunk &&
myBaseSlice == ((ArrayChunk)o).myBaseSlice &&
myColumns == ((ArrayChunk)o).myColumns &&
myRows == ((ArrayChunk)o).myRows &&
myColOffset == ((ArrayChunk)o).myColOffset &&
myRowOffset == ((ArrayChunk)o).myRowOffset;
}
@Override
public int compareTo(@NotNull ArrayChunk other) {
int compRow = myRowOffset - other.myRowOffset;
int compCol = myColOffset - other.myColOffset;
return compRow != 0 ? compRow : compCol;
}
public boolean isOneRow() {
return getRows() == 1;
}
public boolean isOneColumn() {
return getColumns() == 1;
}
}
@@ -62,7 +62,7 @@ class ArrayTableCellRenderer extends DefaultTableCellRenderer {
if (myMax != myMin) {
if (myColored && value != null) {
try {
double rangedValue = NumpyArrayValueProvider.getRangedValue(value.toString(), myType, myMin, myMax, myComplexMax, myComplexMin);
double rangedValue = NumpyArrayTable.getRangedValue(value.toString(), myType, myMin, myMax, myComplexMax, myComplexMin);
this.setBackground(
new JBColor(new Color((int)Math.round(255 * rangedValue), 0, (int)Math.round(255 * (1 - rangedValue)), 130),
new Color((int)Math.round(255 * rangedValue), 0, (int)Math.round(255 * (1 - rangedValue)), 130)));
@@ -21,6 +21,7 @@ import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.table.JBTable;
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl;
import com.jetbrains.python.PythonFileType;
import com.jetbrains.python.debugger.PyDebugValue;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
@@ -42,7 +43,7 @@ public class ArrayTableForm {
private JLabel myFormatLabel;
private JPanel myFormatPanel;
private JPanel myMainPanel;
private JBTable myTable;
private JTable myTable;
private JBTable myBusyTable;
private final Project myProject;
@@ -58,19 +59,8 @@ public class ArrayTableForm {
mySliceTextField = new EditorTextField("", myProject, PythonFileType.INSTANCE);
myTable = new JBTableWithRowHeaders();
myTable.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
myTable.setRowSelectionAllowed(false);
myTable.setTableHeader(new CustomTableHeader(myTable));
myTable.getTableHeader().setDefaultRenderer(new ColumnHeaderRenderer());
myTable.getTableHeader().setReorderingAllowed(false);
myScrollPane = PagingTableModel.LazyViewport.createLazyScrollPaneFor(myTable);
JTable rowTable = new JBTableWithRowHeaders.RowHeaderTable(myTable);
myScrollPane.setRowHeaderView(rowTable);
myScrollPane.setCorner(ScrollPaneConstants.UPPER_LEFT_CORNER,
rowTable.getTableHeader());
((JBTableWithRowHeaders)myTable).setRowHeaderTable((JBTableWithRowHeaders.RowHeaderTable)rowTable);
myScrollPane = ((JBTableWithRowHeaders)myTable).getScrollPane();
myFormatTextField = new EditorTextField("", myProject, PythonFileType.INSTANCE);
@@ -88,7 +78,7 @@ public class ArrayTableForm {
return myFormatTextField;
}
public JBTable getTable() {
public JTable getTable() {
return myTable;
}
@@ -96,15 +86,15 @@ public class ArrayTableForm {
return myColoredCheckbox;
}
public void setDefaultStatus() {
if (myTable != null) {
myTable.getEmptyText().setText(DATA_LOADING_IN_PROCESS);
}
}
//public void setDefaultStatus() {
// if (myTable != null) {
//myTable.getEmptyText().setText(DATA_LOADING_IN_PROCESS);
//}
//}
public void setNotApplicableStatus(XValueNodeImpl node) {
myTable.getEmptyText().setText(NOT_APPLICABLE + node.getName());
}
//public void setNotApplicableStatus(PyDebugValue node) {
// myTable.getEmptyText().setText(NOT_APPLICABLE + node.getName());
//}
public JComponent getMainPanel() {
return myMainPanel;
@@ -118,25 +108,6 @@ public class ArrayTableForm {
myBusyTable.setPaintBusy(busy);
}
public static class CustomTableHeader extends JTableHeader {
public CustomTableHeader(JTable table) {
super();
setColumnModel(table.getColumnModel());
table.getColumnModel().getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
repaint();
}
});
}
@Override
public void columnSelectionChanged(ListSelectionEvent e) {
repaint();
}
}
public static class ColumnHeaderRenderer extends DefaultTableHeaderCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focused, int row, int column) {
@@ -1,43 +0,0 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.debugger.array;
import com.intellij.xdebugger.frame.XValueNode;
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree;
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl;
import org.jetbrains.annotations.NotNull;
/**
* @author amarch
*/
abstract class ArrayValueProvider {
@NotNull protected final XValueNode myBaseNode;
public ArrayValueProvider(@NotNull XValueNode node) {
myBaseNode = node;
}
public abstract boolean isNumeric();
public String getNodeName() {
return ((XValueNodeImpl)myBaseNode).getName();
}
public XDebuggerTree getTree() {
return ((XValueNodeImpl)myBaseNode).getTree();
}
}
@@ -15,10 +15,13 @@
*/
package com.jetbrains.python.debugger.array;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.table.JBTable;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableCellRenderer;
@@ -33,8 +36,30 @@ import java.beans.PropertyChangeListener;
* @author amarch
*/
public class JBTableWithRowHeaders extends JBTable {
private final JBScrollPane myScrollPane;
private RowHeaderTable myRowHeaderTable;
public JBScrollPane getScrollPane() {
return myScrollPane;
}
public JBTableWithRowHeaders() {
setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
setRowSelectionAllowed(false);
setMaxItemsForSizeCalculation(50);
setTableHeader(new CustomTableHeader(this));
getTableHeader().setDefaultRenderer(new ArrayTableForm.ColumnHeaderRenderer());
getTableHeader().setReorderingAllowed(false);
myScrollPane = new JBScrollPane(this);
JBTableWithRowHeaders.RowHeaderTable rowTable = new JBTableWithRowHeaders.RowHeaderTable(this);
myScrollPane.setRowHeaderView(rowTable);
myScrollPane.setCorner(ScrollPaneConstants.UPPER_LEFT_CORNER,
rowTable.getTableHeader());
setRowHeaderTable(rowTable);
}
public boolean getScrollableTracksViewportWidth() {
return getPreferredSize().width < getParent().getWidth();
}
@@ -48,10 +73,10 @@ public class JBTableWithRowHeaders extends JBTable {
}
public static class RowHeaderTable extends JBTable implements PropertyChangeListener, TableModelListener {
private JBTable myMainTable;
private JTable myMainTable;
private int myRowShift = 0;
public RowHeaderTable(JBTable table) {
public RowHeaderTable(JTable table) {
myMainTable = table;
myMainTable.getModel().addTableModelListener(this);
@@ -158,4 +183,23 @@ public class JBTableWithRowHeaders extends JBTable {
}
}
}
public static class CustomTableHeader extends JTableHeader {
public CustomTableHeader(JTable table) {
super();
setColumnModel(table.getColumnModel());
table.getColumnModel().getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
repaint();
}
});
}
@Override
public void columnSelectionChanged(ListSelectionEvent e) {
repaint();
}
}
}
@@ -1,228 +0,0 @@
package com.jetbrains.python.debugger.array;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.xdebugger.evaluation.XDebuggerEvaluator;
import com.intellij.xdebugger.frame.XValue;
import com.intellij.xdebugger.impl.ui.DebuggerUIUtil;
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeListener;
import com.intellij.xdebugger.impl.ui.tree.nodes.RestorableStateNode;
import com.intellij.xdebugger.impl.ui.tree.nodes.XDebuggerTreeNode;
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueContainerNode;
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl;
import com.jetbrains.python.debugger.PyDebugValue;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* @author amarch
*/
public class NumpyArraySlice extends ArrayChunk {
private NumpyArrayValueProvider myValueProvider;
private DataEvaluator myDataEvaluator;
private String myFormat;
private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.debugger.array.NumpyArraySlice");
public NumpyArraySlice(String baseSlice,
int rows,
int columns,
int rOffset,
int cOffset,
String format,
@NotNull NumpyArrayValueProvider valueProvider) {
super(baseSlice, rows, columns, rOffset, cOffset);
myValueProvider = valueProvider;
myDataEvaluator = new DataEvaluator();
myFormat = format;
}
public String getPresentation() {
String onlyChunkSlice =
"[" + getRowOffset() + ":" + (getRowOffset() + getRows()) + ", " + getColOffset() + ":" + (getColOffset() + getColumns()) + "]";
if (isOneColumn() && isOneRow()) {
onlyChunkSlice = "";
}
else if (isOneRow()) {
onlyChunkSlice = "[" + getColOffset() + ":" + (getColOffset() + getColumns()) + "]";
}
else if (isOneColumn()) {
onlyChunkSlice = "[" + getRowOffset() + ":" + (getRowOffset() + getRows()) + "]";
}
if (getBaseSlice().endsWith(onlyChunkSlice)) {
return getBaseSlice();
}
return getBaseSlice() + onlyChunkSlice;
}
@Override
public void fillData(Runnable callback) {
myDataEvaluator.evaluateData(callback);
}
public NumpyArraySlice getInstance() {
return this;
}
public boolean dataFilled() {
return myDataEvaluator.dataFilled();
}
private class DataEvaluator {
private Object[][] myData;
private int myFilledRows = 0;
private int nextRow = 0;
public Object[][] getData() {
return myData;
}
public boolean dataFilled() {
return getRows() > 0 && myFilledRows == getRows();
}
public void evaluateData(final Runnable callback) {
final XDebuggerEvaluator.XEvaluationCallback computeChildrenCallback = new XDebuggerEvaluator.XEvaluationCallback() {
@Override
public void evaluated(@NotNull final XValue result) {
final String name = ((PyDebugValue)result).getName();
DebuggerUIUtil.invokeLater(new Runnable() {
@Override
public void run() {
XValueNodeImpl node = new XValueNodeImpl(myValueProvider.getTree(), null, name, result);
node.startComputingChildren();
}
});
}
@Override
public void errorOccurred(@NotNull String errorMessage) {
myValueProvider.showError(errorMessage, getInstance());
}
};
XDebuggerTreeListener treeListener = new XDebuggerTreeListener() {
@Override
public void nodeLoaded(@NotNull RestorableStateNode node, String name) {
}
@Override
public void childrenLoaded(@NotNull XDebuggerTreeNode node, @NotNull List<XValueContainerNode<?>> children, boolean last) {
if (!(node instanceof XValueNodeImpl)) {
return;
}
String fullName = ((XValueNodeImpl)node).getName();
if (fullName == null || !fullName.contains(getPresentation())) {
return;
}
int row;
if (isOneRow()) {
row = 0;
}
else {
if (isOneColumn()) {
row = Integer.parseInt(fullName.substring(fullName.lastIndexOf('[') + 1, fullName.length() - 1));
}
else {
fullName = fullName.substring(0, fullName.lastIndexOf(","));
row = Integer.parseInt(fullName.substring(fullName.lastIndexOf('[') + 1, fullName.length()));
}
}
if (row > getRows()) {
LOG.error("Row " + row + " is out of range for " + getPresentation() + ".");
return;
}
if (row != -1 && myData[row][0] == null) {
for (int i = 0; i < node.getChildCount() - 1; i++) {
String rawValue = ((XValueNodeImpl)node.getChildAt(i + 1)).getRawValue();
if (rawValue == null) {
return;
}
if (myValueProvider.isNumeric()) {
//remove str quotes in case of numeric
rawValue = rawValue.substring(1, rawValue.length() - 1);
}
myData[row][i] = rawValue;
}
if (node.getChildCount() == 0) {
String rawValue = ((XValueNodeImpl)node).getRawValue();
if (rawValue == null) {
return;
}
if (myValueProvider.isNumeric()) {
//remove str quotes in case of numeric
rawValue = rawValue.substring(1, rawValue.length() - 1);
}
myData[row][0] = rawValue;
}
myFilledRows += 1;
if (myFilledRows == getRows()) {
node.getTree().removeTreeListener(this);
callback.run();
}
else {
nextRow += 1;
startEvalNextRow(computeChildrenCallback);
}
}
}
};
myData =
new Object[getRows()][getColumns()];
myValueProvider.getTree().addTreeListener(treeListener);
nextRow = 0;
myFilledRows = 0;
startEvalNextRow(computeChildrenCallback);
}
private void startEvalNextRow(XDebuggerEvaluator.XEvaluationCallback callback) {
if (nextRow >= getRows()) {
LOG.error("Row " + nextRow + " is out of range for " + getPresentation() + ".");
return;
}
String evalRowCommand = "map(lambda l: " + myValueProvider.evalTypeFunc(myFormat) + ", list(" + getPresentation();
if (!isOneRow() && !isOneColumn()) {
evalRowCommand += "[" + nextRow + ", 0:" + getColumns() + "]))";
}
if (isOneRow() && isOneColumn()) {
evalRowCommand = "\'" + myFormat + "\'" + " % " + getPresentation();
}
else if (isOneColumn()) {
evalRowCommand = "\'" + myFormat + "\'" + " % " + getPresentation();
evalRowCommand += "[" + nextRow + "]";
}
else if (isOneRow()) {
evalRowCommand += "[0:" + getColumns() + "]))";
}
myValueProvider.getEvaluator().evaluate(evalRowCommand, callback, null);
}
}
public Object[][] getData() {
return myDataEvaluator.getData();
}
public String getFormat() {
return myFormat;
}
public void setFormat(String format) {
myFormat = format;
}
@Override
public String toString() {
return super.toString() + " : " + getPresentation();
}
}
@@ -22,20 +22,15 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.ui.AppUIUtil;
import com.intellij.ui.table.JBTable;
import com.intellij.xdebugger.evaluation.XDebuggerEvaluator;
import com.intellij.xdebugger.frame.XValue;
import com.intellij.xdebugger.frame.XValueNode;
import com.intellij.xdebugger.impl.ui.DebuggerUIUtil;
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.jetbrains.python.debugger.PyDebugValue;
import com.jetbrains.python.debugger.PyDebuggerEvaluator;
import org.jetbrains.annotations.NotNull;
import javax.management.InvalidAttributeValueException;
import javax.swing.*;
import javax.swing.table.TableCellEditor;
import java.awt.*;
import java.awt.event.*;
@@ -47,18 +42,17 @@ import java.util.regex.Pattern;
/**
* @author amarch
*/
public class NumpyArrayValueProvider extends ArrayValueProvider {
private PyViewArrayAction.MyDialog myDialog;
private ArrayTableForm myComponent;
private JBTable myTable;
public class NumpyArrayTable {
private final PyDebugValue myValue;
private final PyViewArrayAction.ViewArrayDialog myDialog;
private final ArrayTableForm myComponent;
private final JTable myTable;
private Project myProject;
private PyDebuggerEvaluator myEvaluator;
private NumpyArraySlice myLastPresentation;
private String myDtypeKind;
private int[] myShape;
private ArrayTableCellRenderer myTableCellRenderer;
private PagingTableModel myPagingModel;
private boolean lastSuccessful = false;
private final static int COLUMNS_IN_DEFAULT_SLICE = 40;
private final static int ROWS_IN_DEFAULT_SLICE = 40;
@@ -75,62 +69,28 @@ public class NumpyArrayValueProvider extends ArrayValueProvider {
private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.debugger.array.NumpyArrayValueProvider");
public NumpyArrayValueProvider(@NotNull XValueNode node, PyViewArrayAction.MyDialog dialog, @NotNull Project project) {
super(node);
public NumpyArrayTable(@NotNull Project project,
@NotNull PyViewArrayAction.ViewArrayDialog dialog, @NotNull PyDebugValue value) {
myValue = value;
myDialog = dialog;
if (dialog != null) {
myComponent = dialog.getComponent();
myTable = myComponent.getTable();
}
myComponent = new ArrayTableForm(project);
myTable = myComponent.getTable();
myProject = project;
myEvaluator = new PyDebuggerEvaluator(project, getValueContainer().getFrameAccessor());
myEvaluator = new PyDebuggerEvaluator(project, getDebugValue().getFrameAccessor());
}
private PagingTableModel getPagingModel(@NotNull int[] shape, boolean rendered, final NumpyArraySlice mainSlice) {
public ArrayTableForm getComponent() {
return myComponent;
}
private PagingTableModel createTableModel(@NotNull int[] shape) {
final int columns = Math.min(getMaxColumn(shape), COLUMNS_IN_DEFAULT_VIEW);
int rows = Math.min(getMaxRow(shape), ROWS_IN_DEFAULT_VIEW);
if (columns == 0 || rows == 0) {
showError("Slice with zero axis shape.");
}
return new PagingTableModel(rows, columns, rendered, this, mainSlice) {
private final String myFormat = mainSlice.getFormat();
private final String myBaseSlice = mainSlice.getBaseSlice();
@Override
protected NumpyArraySlice createChunk(int rows, int columns, int rOffset, int cOffset) {
return new NumpyArraySlice(myBaseSlice, rows, columns, rOffset, cOffset, myFormat, getInstance());
}
@Override
protected Runnable getDataEvaluator(final ArrayChunk chunk) {
final NumpyArraySlice arraySlice =
new NumpyArraySlice(chunk.getBaseSlice(), chunk.getRows(), chunk.getColumns(), chunk.getRowOffset(), chunk.getColOffset(),
myFormat, getInstance());
if (arraySlice.getFormat().isEmpty()) {
arraySlice.setFormat(getDefaultFormat());
}
return new Runnable() {
public void run() {
if (!arraySlice.dataFilled()) {
arraySlice.fillData(new Runnable() {
@Override
public void run() {
//check that we still running on the right model
if (!myBaseSlice.equals(getModelFullChunk().getBaseSlice()) || !myFormat.equals(getModelFullChunk().getFormat())) {
return;
}
notifyChunkLoaded(arraySlice);
}
});
}
}
};
}
};
return new PagingTableModel(rows, columns, this);
}
private void initComponent() {
@@ -232,23 +192,19 @@ public class NumpyArrayValueProvider extends ArrayValueProvider {
});
}
public NumpyArrayValueProvider getInstance() {
return this;
}
public PyDebugValue getValueContainer() {
return (PyDebugValue)((XValueNodeImpl)myBaseNode).getValueContainer();
public PyDebugValue getDebugValue() {
return myValue;
}
public PyDebuggerEvaluator getEvaluator() {
return myEvaluator;
}
public void startFillTable() {
public void init() {
Runnable returnToFillTable = new Runnable() {
@Override
public void run() {
startFillTable();
init();
}
};
@@ -282,9 +238,7 @@ public class NumpyArrayValueProvider extends ArrayValueProvider {
}
}
});
startFillTable(new NumpyArraySlice(getDefaultPresentation(), Math.min(getMaxRow(myShape), ROWS_IN_DEFAULT_VIEW),
Math.min(getMaxColumn(myShape), COLUMNS_IN_DEFAULT_VIEW), 0, 0, getDefaultFormat(), getInstance()),
false, false);
initTableModel(false);
}
private static String getTitlePresentation(String slice) {
@@ -466,7 +420,6 @@ public class NumpyArrayValueProvider extends ArrayValueProvider {
}
}
@Override
public boolean isNumeric() {
if (myDtypeKind != null) {
return "biufc".contains(myDtypeKind.substring(0, 1));
@@ -474,15 +427,8 @@ public class NumpyArrayValueProvider extends ArrayValueProvider {
return false;
}
private void startFillTable(final NumpyArraySlice arraySlice, boolean rendered, final boolean inPlace) {
if (myLastPresentation != null &&
arraySlice.getBaseSlice().equals(myLastPresentation.getBaseSlice()) &&
arraySlice.getFormat().equals(myLastPresentation.getFormat()) && lastSuccessful) {
return;
}
lastSuccessful = false;
myPagingModel = getPagingModel(myShape, rendered, arraySlice);
private void initTableModel(final boolean inPlace) {
myPagingModel = createTableModel(myShape);
DebuggerUIUtil.invokeLater(new Runnable() {
@Override
@@ -544,15 +490,6 @@ public class NumpyArrayValueProvider extends ArrayValueProvider {
myEvaluator.evaluate(changeValExpression(), new XDebuggerEvaluator.XEvaluationCallback() {
@Override
public void evaluated(@NotNull XValue result) {
AppUIUtil.invokeOnEdt(new Runnable() {
@Override
public void run() {
XDebuggerTree tree = ((XValueNodeImpl)myBaseNode).getTree();
final XDebuggerTreeState treeState = XDebuggerTreeState.saveState(tree);
tree.rebuildAndRestore(treeState);
}
});
XDebuggerEvaluator.XEvaluationCallback callback = new XDebuggerEvaluator.XEvaluationCallback() {
@Override
public void evaluated(@NotNull XValue value) {
@@ -616,31 +553,11 @@ public class NumpyArrayValueProvider extends ArrayValueProvider {
this.myShape = shape;
}
public void completeCurrentLoad() {
setBusy(false);
if (myPagingModel.getColumnCount() < getMaxColumn(myShape) || myPagingModel.getRowCount() < getMaxRow(myShape)) {
String hintMessage = LOAD_SMALLER_SLICE;
if (isNumeric()) {
hintMessage += "\n" + DISABLE_COLOR_FOR_HUGE_ARRAY;
}
showInfoHint(hintMessage);
}
}
public void showError(String message) {
myDialog.setError(message);
setBusy(false);
}
public void showError(String message, NumpyArraySlice chunk) {
if (!chunk.getFormat().equals(getModelFullChunk().getFormat()) ||
!chunk.getBaseSlice().equals(getModelFullChunk().getBaseSlice())) {
//ignore error message from previous task
return;
}
showError(message);
}
public void showInfoHint(final String message) {
DebuggerUIUtil.invokeLater(new Runnable() {
@Override
@@ -710,7 +627,7 @@ public class NumpyArrayValueProvider extends ArrayValueProvider {
}
myShape = shape;
startRefillTable(false);
reset();
}
private static String getEvalShapeCommand(@NotNull String slice) {
@@ -737,20 +654,12 @@ public class NumpyArrayValueProvider extends ArrayValueProvider {
}
private void doApplyFormat() {
startRefillTable(true);
reset();
}
private void startRefillTable(boolean inPlace) {
clearTableData();
Rectangle vr = myTable.getVisibleRect();
int rOffset = myTable.rowAtPoint(vr.getLocation());
int cOffset = myTable.columnAtPoint(vr.getLocation());
startFillTable(new NumpyArraySlice(getSliceText(), 0, 0, rOffset, cOffset, getFormat(), this), true, inPlace);
}
private void clearTableData() {
private void reset() {
clearErrorMessage();
((PagingTableModel)myTable.getModel()).clearCached();
initTableModel(true);
}
public String evalTypeFunc(String format) {
@@ -771,24 +680,6 @@ public class NumpyArrayValueProvider extends ArrayValueProvider {
return 0;
}
public void notifyChunkLoaded(final NumpyArraySlice arraySlice) {
myLastPresentation = arraySlice;
lastSuccessful = true;
myPagingModel.addDataInCache(arraySlice.getRowOffset(), arraySlice.getColOffset(), arraySlice.getData());
myPagingModel.runNextLoadingTask();
DebuggerUIUtil.invokeLater(new Runnable() {
public void run() {
myTable.setDefaultRenderer(myTable.getColumnClass(0), myTableCellRenderer);
myDialog.setTitle(getTitlePresentation(getSliceText()));
for (int r = 0; r < arraySlice.getRows(); r++) {
for (int c = 0; c < arraySlice.getColumns(); c++) {
myPagingModel.fireTableCellUpdated(r + arraySlice.getRowOffset(), c + arraySlice.getColOffset());
}
}
}
});
}
public void setBusy(final boolean busy) {
DebuggerUIUtil.invokeLater(new Runnable() {
@Override
@@ -798,10 +689,6 @@ public class NumpyArrayValueProvider extends ArrayValueProvider {
});
}
private NumpyArraySlice getModelFullChunk() {
return (NumpyArraySlice)myPagingModel.getFullChunk();
}
/**
* @return double presentation from [0:1] range
*/
@@ -852,4 +739,8 @@ public class NumpyArrayValueProvider extends ArrayValueProvider {
throw new IllegalArgumentException("Not a valid python complex value: " + pyComplexValue);
}
}
public String getNodeName() {
return (myValue).getName();
}
}
@@ -15,74 +15,86 @@
*/
package com.jetbrains.python.debugger.array;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.Pair;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.components.JBViewport;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.Queue;
import com.jetbrains.python.debugger.PyDebugValue;
import javax.swing.table.AbstractTableModel;
import java.awt.*;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ExecutionException;
public abstract class PagingTableModel extends AbstractTableModel {
private static final int CHUNK_COL_SIZE = 50;
private static final int CHUNK_ROW_SIZE = 50;
/**
* @author traff
*/
public class PagingTableModel extends AbstractTableModel {
private static final int CHUNK_COL_SIZE = 2; //TODO set to 100
private static final int CHUNK_ROW_SIZE = 2;
private static final int DEFAULT_MAX_CACHED_SIZE = 100;
public static final String EMPTY_CELL_VALUE = "...";
private HashMap<String, Object[][]> myCachedData = new HashMap<String, Object[][]>();
private SortedSet<ArrayChunk> myPendingSet = new TreeSet<ArrayChunk>();
private Queue<String> cachedChunkKeys = new Queue<String>(DEFAULT_MAX_CACHED_SIZE + 1);
private LoadingCache<Pair<Integer, Integer>, Object[][]> myChunkCache = CacheBuilder.newBuilder().build(
new CacheLoader<Pair<Integer, Integer>, Object[][]>() {
@Override
public Object[][] load(Pair<Integer, Integer> key) throws Exception {
PyDebugValue value = myProvider.getDebugValue();
value = new PyDebugValue(myProvider.getSliceText(), value.getType(), value.getValue(), value.isContainer(), value.isErrorOnEval(),
value.getFrameAccessor());
return value.getFrameAccessor().getArrayItems(value, key.first, key.second, CHUNK_COL_SIZE, CHUNK_ROW_SIZE, myProvider.getFormat());
}
});
private ArrayChunk evaluatedChunk;
private boolean myRendered;
private int myRows = 0;
private int myColumns = 0;
private NumpyArrayValueProvider myProvider;
private ArrayChunk myFullChunk;
private NumpyArrayTable myProvider;
public PagingTableModel(int rows, int columns, boolean rendered, NumpyArrayValueProvider provider, ArrayChunk fullChunk) {
public PagingTableModel(int rows, int columns, NumpyArrayTable provider) {
myRows = rows;
myColumns = columns;
myRendered = rendered;
myProvider = provider;
myFullChunk = fullChunk;
}
@Override
public boolean isCellEditable(int row, int column) {
if (getValueAt(row, column).equals(EMPTY_CELL_VALUE)) {
return false;
}
return true;
return !getValueAt(row, column).equals(EMPTY_CELL_VALUE);
}
public Object getValueAt(int row, int col) {
//prevent data evaluation for full table
if (row == (getRowCount() - 1) && col == (getColumnCount() - 1) && !myRendered) {
myRendered = true;
return EMPTY_CELL_VALUE;
}
Pair<Integer, Integer> key = itemToChunkKey(row, col);
if (!myRendered) {
return EMPTY_CELL_VALUE;
}
try {
Object[][] chunk = myChunkCache.get(key);
String key = formMapKey(row, col);
if (!myCachedData.containsKey(key)) {
scheduleChunkAt(row, col);
return EMPTY_CELL_VALUE;
}
int r = row % CHUNK_ROW_SIZE;
int c = col % CHUNK_COL_SIZE;
return myCachedData.get(key)[row % CHUNK_ROW_SIZE][col % CHUNK_COL_SIZE];
if (r < chunk.length) {
if (c < chunk[r].length) {
return chunk[r][c];
}
else {
return EMPTY_CELL_VALUE;
}
}
else {
return EMPTY_CELL_VALUE;
}
}
catch (ExecutionException e) {
return EMPTY_CELL_VALUE; //TODO: handle it
}
}
private static String formMapKey(int row, int col) {
return "[" + getPageRowStart(row) + "," + getPageColStart(col) + "]";
private static Pair<Integer, Integer> itemToChunkKey(int row, int col) {
return Pair.create(getPageRowStart(row), getPageColStart(col));
}
private static int getPageRowStart(int rowOffset) {
@@ -93,78 +105,6 @@ public abstract class PagingTableModel extends AbstractTableModel {
return colOffset - (colOffset % CHUNK_COL_SIZE);
}
private void scheduleChunkAt(int rOffset, int cOffset) {
if (isPending(rOffset, cOffset)) {
return;
}
int startROffset = getPageRowStart(rOffset);
int rLength = Math.min(CHUNK_ROW_SIZE, myRows - startROffset);
int startCOffset = getPageColStart(cOffset);
int cLength = Math.min(CHUNK_COL_SIZE, myColumns - startCOffset);
scheduleLoadData(startROffset, rLength, startCOffset, cLength);
}
private boolean isPending(int rOffset, int cOffset) {
int sz = myPendingSet.size();
if (sz == 0) return false;
if (sz == 1) {
// special case (for speed)
ArrayChunk seg = myPendingSet.first();
return seg.contains(rOffset, cOffset);
}
ArrayChunk lo = createChunk(0, 0, getPageRowStart(rOffset), getPageColStart(cOffset));
ArrayChunk hi = createChunk(0, 0, getPageRowStart(rOffset + CHUNK_ROW_SIZE), getPageColStart(cOffset + CHUNK_COL_SIZE));
for (ArrayChunk seg : myPendingSet.subSet(lo, hi)) {
if (seg.contains(rOffset, cOffset)) return true;
}
return false;
}
protected abstract ArrayChunk createChunk(int rows, int columns, int rOffset, int cOffset);
protected abstract Runnable getDataEvaluator(final ArrayChunk chunk);
public void runNextLoadingTask() {
if (evaluatedChunk != null) {
myPendingSet.remove(evaluatedChunk);
evaluatedChunk = null;
}
if (myPendingSet.size() > 0) {
evaluatedChunk = myPendingSet.first();
ApplicationManager.getApplication().executeOnPooledThread(getDataEvaluator(evaluatedChunk));
myProvider.setBusy(true);
}
else {
myProvider.completeCurrentLoad();
}
}
private void scheduleLoadData(final int rOffset, final int rLength, final int cOffset, final int cLength) {
final ArrayChunk segment = createChunk(rLength, cLength, rOffset, cOffset);
myPendingSet.add(segment);
if (evaluatedChunk == null) {
runNextLoadingTask();
}
}
protected void addDataInCache(int rOffset, int cOffset, Object[][] newData) {
String key = formMapKey(rOffset, cOffset);
myCachedData.put(key, newData);
cachedChunkKeys.addLast(key);
if (myCachedData.size() == DEFAULT_MAX_CACHED_SIZE) {
String old = cachedChunkKeys.pullFirst();
myCachedData.remove(old);
}
}
public int getColumnCount() {
return myColumns;
}
@@ -177,46 +117,13 @@ public abstract class PagingTableModel extends AbstractTableModel {
return myRows;
}
public void clearCached() {
myCachedData = new HashMap<String, Object[][]>();
myPendingSet = new TreeSet<ArrayChunk>();
cachedChunkKeys = new Queue<String>(DEFAULT_MAX_CACHED_SIZE + 1);
evaluatedChunk = null;
}
public static class LazyViewport extends JBViewport {
public static JBScrollPane createLazyScrollPaneFor(Component view) {
LazyViewport vp = new LazyViewport();
vp.setView(view);
JBScrollPane scrollpane = new JBScrollPane();
scrollpane.setViewport(vp);
return scrollpane;
}
public void setViewPosition(Point p) {
Component parent = getParent();
if (parent instanceof JBScrollPane &&
(((JBScrollPane)parent).getVerticalScrollBar().getValueIsAdjusting() ||
((JBScrollPane)parent).getHorizontalScrollBar().getValueIsAdjusting())) {
return;
}
super.setViewPosition(p);
}
}
public void forcedChange(int row, int col, Object value) {
String key = formMapKey(row, col);
if (myCachedData.containsKey(key)) {
Object[][] cachedData = myCachedData.get(key);
cachedData[row - getPageRowStart(row)][col - getPageColStart(col)] = value;
myCachedData.put(key, cachedData);
Object[][] chunk = myChunkCache.getIfPresent(itemToChunkKey(row, col));
if (chunk != null) {
chunk[row - getPageRowStart(row)][col - getPageColStart(col)] = value;
}
else {
throw new IllegalArgumentException("Forced to change empty cell in " + row + " row and " + col + "column.");
}
}
public ArrayChunk getFullChunk() {
return myFullChunk;
}
}
@@ -22,7 +22,6 @@ import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree;
import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase;
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl;
import com.jetbrains.python.console.PydevConsoleCommunication;
import com.jetbrains.python.debugger.PyDebugValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -38,9 +37,11 @@ public class PyViewArrayAction extends XDebuggerTreeActionBase {
@Override
protected void perform(XValueNodeImpl node, @NotNull String nodeName, AnActionEvent e) {
final MyDialog dialog = new MyDialog(e.getProject());
dialog.setValue(node);
dialog.show();
Project p = e.getProject();
if (p != null && node != null && node.getValueContainer() instanceof PyDebugValue && node.isComputed()) {
final ViewArrayDialog dialog = new ViewArrayDialog(p, (PyDebugValue)(node.getValueContainer()));
dialog.show();
}
}
@Nullable
@@ -53,18 +54,14 @@ public class PyViewArrayAction extends XDebuggerTreeActionBase {
public void update(AnActionEvent e) {
TreePath[] paths = getSelectedNodes(e.getDataContext());
if (paths != null) {
if(paths.length > 1) {
if (paths.length > 1) {
e.getPresentation().setVisible(false);
return;
}
XValueNodeImpl node = getSelectedNode(e.getDataContext());
XValueNodeImpl node = getSelectedNode(e.getDataContext());
if (node != null && node.getValueContainer() instanceof PyDebugValue && node.isComputed()) {
PyDebugValue debugValue = (PyDebugValue) node.getValueContainer();
if (debugValue.getFrameAccessor() instanceof PydevConsoleCommunication) {
e.getPresentation().setVisible(false);
return;
}
PyDebugValue debugValue = (PyDebugValue)node.getValueContainer();
String nodeType = debugValue.getType();
if ("ndarray".equals(nodeType)) {
@@ -76,43 +73,23 @@ public class PyViewArrayAction extends XDebuggerTreeActionBase {
e.getPresentation().setVisible(false);
}
protected class MyDialog extends DialogWrapper {
protected static class ViewArrayDialog extends DialogWrapper {
private Project myProject;
private ArrayTableForm myComponent;
private NumpyArrayTable myNumpyArrayTable;
private MyDialog(Project project) {
private ViewArrayDialog(@NotNull Project project, PyDebugValue debugValue) {
super(project, false);
setModal(false);
setCancelButtonText("Close");
setCrossClosesWindow(true);
myProject = project;
myComponent = new ArrayTableForm(project);
myNumpyArrayTable = new NumpyArrayTable(myProject, this, debugValue);
myNumpyArrayTable.init();
init();
}
public void setValue(XValueNodeImpl node) {
if (node.getValueContainer() instanceof PyDebugValue) {
PyDebugValue debugValue = (PyDebugValue)node.getValueContainer();
if ("ndarray".equals(debugValue.getType())) {
myComponent.setDefaultStatus();
final NumpyArrayValueProvider valueProvider = new NumpyArrayValueProvider(node, this, myProject);
try {
valueProvider.startFillTable();
}
catch (Exception e) {
setErrorText(e.getMessage());
}
}
else {
myComponent.setNotApplicableStatus(node);
}
}
}
public void setError(String text) {
//todo: think about this usage
setErrorText(text);
@@ -131,11 +108,11 @@ public class PyViewArrayAction extends XDebuggerTreeActionBase {
@Override
protected JComponent createCenterPanel() {
return myComponent.getMainPanel();
return myNumpyArrayTable.getComponent().getMainPanel();
}
public ArrayTableForm getComponent() {
return myComponent;
return myNumpyArrayTable.getComponent();
}
}
}