mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -279,7 +279,7 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable {
|
||||
}
|
||||
|
||||
private void commitNotFound(@NotNull String commitHash) {
|
||||
if (getFilters().isEmpty()) {
|
||||
if (myMainFrame.getFilterUi().getFilters().isEmpty()) {
|
||||
showMessage(MessageType.WARNING, "Commit " + commitHash + " not found");
|
||||
}
|
||||
else {
|
||||
@@ -315,7 +315,7 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable {
|
||||
}
|
||||
|
||||
public void applyFiltersAndUpdateUi() {
|
||||
myFilterer.onFiltersChange(getFilters());
|
||||
myFilterer.onFiltersChange(myMainFrame.getFilterUi().getFilters());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -323,11 +323,6 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable {
|
||||
return myFilterer;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public VcsLogFilterCollection getFilters() {
|
||||
return myMainFrame.getFilterUi().getFilters();
|
||||
}
|
||||
|
||||
public VcsLogGraphTable getTable() {
|
||||
return myMainFrame.getGraphTable();
|
||||
}
|
||||
|
||||
+27
-22
@@ -32,19 +32,33 @@ import java.util.*;
|
||||
|
||||
public class BranchFilterPopupComponent extends MultipleValueFilterPopupComponent<VcsLogBranchFilter> {
|
||||
|
||||
@NotNull private final VcsLogUiProperties myUiProperties;
|
||||
|
||||
@NotNull private VcsLogDataPack myDataPack;
|
||||
|
||||
public BranchFilterPopupComponent(@NotNull VcsLogClassicFilterUi filterUi, @NotNull VcsLogDataPack dataPack,
|
||||
@NotNull VcsLogUiProperties uiProperties) {
|
||||
super(filterUi, "Branch");
|
||||
myDataPack = dataPack;
|
||||
myUiProperties = uiProperties;
|
||||
public BranchFilterPopupComponent(@NotNull VcsLogUiProperties uiProperties,
|
||||
@NotNull FilterModel<VcsLogBranchFilter> filterModel) {
|
||||
super("Branch", uiProperties, filterModel);
|
||||
}
|
||||
|
||||
void updateDataPack(@NotNull VcsLogDataPack dataPack) {
|
||||
myDataPack = dataPack;
|
||||
@NotNull
|
||||
@Override
|
||||
protected String getText(@NotNull VcsLogBranchFilter filter) {
|
||||
return displayableText(filter.getBranchNames());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected String getToolTip(@NotNull VcsLogBranchFilter filter) {
|
||||
return tooltip(filter.getBranchNames());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected VcsLogBranchFilter createFilter(@NotNull Collection<String> values) {
|
||||
return new VcsLogBranchFilterImpl(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected Collection<String> getValues(@Nullable VcsLogBranchFilter filter) {
|
||||
return filter == null ? Collections.<String>emptySet() : filter.getBranchNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,7 +68,7 @@ public class BranchFilterPopupComponent extends MultipleValueFilterPopupComponen
|
||||
actionGroup.add(createAllAction());
|
||||
actionGroup.add(createSelectMultipleValuesAction());
|
||||
|
||||
actionGroup.add(constructActionGroup(myDataPack, createRecentItemsActionGroup(), new Function<String, AnAction>() {
|
||||
actionGroup.add(constructActionGroup(myFilterModel.getDataPack(), createRecentItemsActionGroup(), new Function<String, AnAction>() {
|
||||
@Override
|
||||
public AnAction fun(String name) {
|
||||
return createPredefinedValueAction(Collections.singleton(name));
|
||||
@@ -151,15 +165,6 @@ public class BranchFilterPopupComponent extends MultipleValueFilterPopupComponen
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected VcsLogBranchFilter getFilter() {
|
||||
if (getSelectedValues() == null) {
|
||||
return null;
|
||||
}
|
||||
return new VcsLogBranchFilterImpl(getSelectedValues());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected List<List<String>> getRecentValuesFromSettings() {
|
||||
@@ -176,7 +181,7 @@ public class BranchFilterPopupComponent extends MultipleValueFilterPopupComponen
|
||||
@NotNull
|
||||
@Override
|
||||
protected List<String> getAllValues() {
|
||||
return ContainerUtil.map(myDataPack.getRefs().getBranches(), new Function<VcsRef, String>() {
|
||||
return ContainerUtil.map(myFilterModel.getDataPack().getRefs().getBranches(), new Function<VcsRef, String>() {
|
||||
@Override
|
||||
public String fun(VcsRef ref) {
|
||||
return ref.getName();
|
||||
|
||||
+40
-53
@@ -22,6 +22,7 @@ import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.ui.DialogBuilder;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.vcs.versionBrowser.DateFilterComponent;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.text.DateFormatUtil;
|
||||
import com.intellij.vcs.log.VcsLogDateFilter;
|
||||
import com.intellij.vcs.log.data.VcsLogDateFilterImpl;
|
||||
@@ -33,11 +34,30 @@ import java.util.Date;
|
||||
|
||||
class DateFilterPopupComponent extends FilterPopupComponent<VcsLogDateFilter> {
|
||||
|
||||
private Date myAfter;
|
||||
private Date myBefore;
|
||||
public DateFilterPopupComponent(FilterModel<VcsLogDateFilter> filterModel) {
|
||||
super("Date", filterModel);
|
||||
}
|
||||
|
||||
DateFilterPopupComponent(@NotNull VcsLogClassicFilterUi filterUi) {
|
||||
super(filterUi, "Date");
|
||||
@NotNull
|
||||
@Override
|
||||
protected String getText(@NotNull VcsLogDateFilter filter) {
|
||||
Date after = filter.getAfter();
|
||||
Date before = filter.getBefore();
|
||||
if (after != null && before != null) {
|
||||
return DateFormatUtil.formatDate(after) + "-" + DateFormatUtil.formatDate(before);
|
||||
}
|
||||
else if (after != null) {
|
||||
return "Since " + DateFormatUtil.formatDate(after);
|
||||
}
|
||||
else {
|
||||
return "Until " + DateFormatUtil.formatDate(ObjectUtils.assertNotNull(before));
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected String getToolTip(@NotNull VcsLogDateFilter filter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,48 +69,24 @@ class DateFilterPopupComponent extends FilterPopupComponent<VcsLogDateFilter> {
|
||||
cal.add(Calendar.DAY_OF_YEAR, -6);
|
||||
Date oneWeekBefore = cal.getTime();
|
||||
|
||||
DumbAwareAction allAction = new DumbAwareAction(ALL) {
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
myAfter = null;
|
||||
myBefore = null;
|
||||
applyFilters();
|
||||
setValue(ALL);
|
||||
}
|
||||
};
|
||||
return new DefaultActionGroup(allAction,
|
||||
return new DefaultActionGroup(createAllAction(),
|
||||
new DateAction(oneDayBefore, "Last 24 hours"),
|
||||
new DateAction(oneWeekBefore, "Last 7 days"),
|
||||
new SelectAction());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected VcsLogDateFilter getFilter() {
|
||||
return myAfter == null && myBefore == null ? null : new VcsLogDateFilterImpl(myAfter, myBefore);
|
||||
}
|
||||
|
||||
private void setOnlyAfter(Date after) {
|
||||
myAfter = after;
|
||||
myBefore = null;
|
||||
}
|
||||
|
||||
private class DateAction extends DumbAwareAction {
|
||||
|
||||
private final Date mySince;
|
||||
private final String myText;
|
||||
@NotNull private final Date mySince;
|
||||
|
||||
DateAction(Date since, String text) {
|
||||
DateAction(@NotNull Date since, @NotNull String text) {
|
||||
super(text);
|
||||
mySince = since;
|
||||
myText = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
setValue(myText);
|
||||
setOnlyAfter(mySince);
|
||||
applyFilters();
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
myFilterModel.setFilter(new VcsLogDateFilterImpl(mySince, null));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,13 +97,16 @@ class DateFilterPopupComponent extends FilterPopupComponent<VcsLogDateFilter> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
final DateFilterComponent dateComponent = new DateFilterComponent(false, DateFormatUtil.getDateFormat().getDelegate());
|
||||
if (myBefore != null) {
|
||||
dateComponent.setBefore(myBefore.getTime());
|
||||
}
|
||||
if (myAfter != null) {
|
||||
dateComponent.setAfter(myAfter.getTime());
|
||||
VcsLogDateFilter currentFilter = myFilterModel.getFilter();
|
||||
if (currentFilter != null) {
|
||||
if (currentFilter.getBefore() != null) {
|
||||
dateComponent.setBefore(currentFilter.getBefore().getTime());
|
||||
}
|
||||
if (currentFilter.getAfter() != null) {
|
||||
dateComponent.setAfter(currentFilter.getAfter().getTime());
|
||||
}
|
||||
}
|
||||
|
||||
DialogBuilder db = new DialogBuilder(DateFilterPopupComponent.this);
|
||||
@@ -118,20 +117,8 @@ class DateFilterPopupComponent extends FilterPopupComponent<VcsLogDateFilter> {
|
||||
if (DialogWrapper.OK_EXIT_CODE == db.show()) {
|
||||
long after = dateComponent.getAfter();
|
||||
long before = dateComponent.getBefore();
|
||||
myAfter = after > 0 ? new Date(after) : null;
|
||||
myBefore = before > 0 ? new Date(before) : null;
|
||||
|
||||
if (myAfter != null && myBefore != null) {
|
||||
setValue(DateFormatUtil.formatDate(after) + "-" + DateFormatUtil.formatDate(before));
|
||||
}
|
||||
else if (myAfter != null) {
|
||||
setValue("After " + DateFormatUtil.formatDate(after));
|
||||
}
|
||||
else {
|
||||
setValue("Before " + DateFormatUtil.formatDate(before));
|
||||
}
|
||||
|
||||
applyFilters();
|
||||
VcsLogDateFilter filter = new VcsLogDateFilterImpl(after > 0 ? new Date(after) : null, before > 0 ? new Date(before) : null);
|
||||
myFilterModel.setFilter(filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.vcs.log.ui.filter;
|
||||
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.vcs.log.VcsLogDataPack;
|
||||
import com.intellij.vcs.log.VcsLogFilter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
class FilterModel<Filter extends VcsLogFilter> {
|
||||
|
||||
@NotNull private final Computable<VcsLogDataPack> myDataPackProvider;
|
||||
@NotNull private final Collection<Runnable> mySetFilterListeners = ContainerUtil.newArrayList();
|
||||
|
||||
@Nullable private Filter myFilter;
|
||||
|
||||
FilterModel(@NotNull Computable<VcsLogDataPack> provider) {
|
||||
myDataPackProvider = provider;
|
||||
}
|
||||
|
||||
void setFilter(@Nullable Filter filter) {
|
||||
myFilter = filter;
|
||||
for (Runnable listener : mySetFilterListeners) {
|
||||
listener.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Filter getFilter() {
|
||||
return myFilter;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
VcsLogDataPack getDataPack() {
|
||||
return myDataPackProvider.compute();
|
||||
}
|
||||
|
||||
void addSetFilterListener(@NotNull Runnable runnable) {
|
||||
mySetFilterListeners.add(runnable);
|
||||
}
|
||||
|
||||
}
|
||||
+46
-52
@@ -52,12 +52,19 @@ abstract class FilterPopupComponent<Filter extends VcsLogFilter> extends JPanel
|
||||
|
||||
@NotNull private final JLabel myFilterNameLabel;
|
||||
@NotNull private final JLabel myFilterValueLabel;
|
||||
@NotNull private final VcsLogClassicFilterUi myFilterUi;
|
||||
@NotNull protected final FilterModel<Filter> myFilterModel;
|
||||
|
||||
FilterPopupComponent(@NotNull VcsLogClassicFilterUi filterUi, @NotNull String filterName) {
|
||||
myFilterUi = filterUi;
|
||||
FilterPopupComponent(@NotNull String filterName,
|
||||
@NotNull FilterModel<Filter> filterModel) {
|
||||
myFilterModel = filterModel;
|
||||
myFilterNameLabel = new JLabel(filterName + ": ");
|
||||
myFilterValueLabel = new JLabel(ALL);
|
||||
myFilterValueLabel = new JLabel() {
|
||||
@Override
|
||||
public String getText() {
|
||||
Filter filter = myFilterModel.getFilter();
|
||||
return filter == null ? ALL : FilterPopupComponent.this.getText(filter);
|
||||
}
|
||||
};
|
||||
setDefaultForeground();
|
||||
setFocusable(true);
|
||||
setBorder(UNFOCUSED_BORDER);
|
||||
@@ -68,12 +75,35 @@ abstract class FilterPopupComponent<Filter extends VcsLogFilter> extends JPanel
|
||||
add(Box.createHorizontalStrut(GAP_BEFORE_ARROW));
|
||||
add(new JLabel(AllIcons.Ide.Statusbar_arrows));
|
||||
|
||||
updateLabelOnFilterChange();
|
||||
showPopupMenuOnClick();
|
||||
showPopupMenuFromKeyboard();
|
||||
indicateHovering();
|
||||
indicateFocusing();
|
||||
}
|
||||
|
||||
private void updateLabelOnFilterChange() {
|
||||
myFilterModel.addSetFilterListener(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myFilterValueLabel.revalidate();
|
||||
myFilterValueLabel.repaint();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected abstract String getText(@NotNull Filter filter);
|
||||
|
||||
@Nullable
|
||||
protected abstract String getToolTip(@NotNull Filter filter);
|
||||
|
||||
@Override
|
||||
public String getToolTipText() {
|
||||
Filter filter = myFilterModel.getFilter();
|
||||
return filter == null ? null : getToolTip(filter);
|
||||
}
|
||||
|
||||
private static Border createFocusedBorder() {
|
||||
return BorderFactory.createCompoundBorder(new RoundedLineBorder(UIUtil.getHeaderActiveColor(), 10, BORDER_SIZE),
|
||||
INNER_MARGIN_BORDER);
|
||||
@@ -84,52 +114,29 @@ abstract class FilterPopupComponent<Filter extends VcsLogFilter> extends JPanel
|
||||
INNER_MARGIN_BORDER);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create popup actions available under this filter.
|
||||
*/
|
||||
protected abstract ActionGroup createActionGroup();
|
||||
|
||||
/**
|
||||
* Return the filter currently selected via this component or null if no filters are selected via this component.
|
||||
*/
|
||||
@Nullable
|
||||
protected abstract Filter getFilter();
|
||||
|
||||
protected void setValue(@NotNull String newValue) {
|
||||
setValue(newValue, newValue);
|
||||
}
|
||||
|
||||
protected void setValue(@NotNull String value, @NotNull String tooltip) {
|
||||
myFilterValueLabel.setText(value);
|
||||
setToolTipText(tooltip.equals(value) ? null : tooltip);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getValue() {
|
||||
return myFilterValueLabel.getText();
|
||||
}
|
||||
|
||||
protected void applyFilters() {
|
||||
myFilterUi.applyFilters();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the special action that indicates that no filtering is selected in this component.
|
||||
*/
|
||||
@NotNull
|
||||
protected AnAction createAllAction() {
|
||||
return new AllAction(this);
|
||||
return new AllAction();
|
||||
}
|
||||
|
||||
private void indicateFocusing() {
|
||||
addFocusListener(new FocusAdapter() {
|
||||
@Override
|
||||
public void focusGained(FocusEvent e) {
|
||||
public void focusGained(@NotNull FocusEvent e) {
|
||||
setBorder(FOCUSED_BORDER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focusLost(FocusEvent e) {
|
||||
public void focusLost(@NotNull FocusEvent e) {
|
||||
setBorder(UNFOCUSED_BORDER);
|
||||
}
|
||||
});
|
||||
@@ -138,7 +145,7 @@ abstract class FilterPopupComponent<Filter extends VcsLogFilter> extends JPanel
|
||||
private void showPopupMenuFromKeyboard() {
|
||||
addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
public void keyPressed(@NotNull KeyEvent e) {
|
||||
if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_DOWN) {
|
||||
showPopupMenu();
|
||||
}
|
||||
@@ -159,12 +166,12 @@ abstract class FilterPopupComponent<Filter extends VcsLogFilter> extends JPanel
|
||||
private void indicateHovering() {
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
public void mouseEntered(@NotNull MouseEvent e) {
|
||||
setOnHoverForeground();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e) {
|
||||
public void mouseExited(@NotNull MouseEvent e) {
|
||||
setDefaultForeground();
|
||||
}
|
||||
});
|
||||
@@ -188,28 +195,15 @@ abstract class FilterPopupComponent<Filter extends VcsLogFilter> extends JPanel
|
||||
popup.showUnderneathOf(this);
|
||||
}
|
||||
|
||||
private static class AllAction extends SetValueAction {
|
||||
AllAction(@NotNull FilterPopupComponent component) {
|
||||
super(ALL, component);
|
||||
}
|
||||
}
|
||||
private class AllAction extends DumbAwareAction {
|
||||
|
||||
protected static class SetValueAction extends DumbAwareAction {
|
||||
|
||||
private final String myValue;
|
||||
private final FilterPopupComponent myFilterComponent;
|
||||
|
||||
SetValueAction(String value, FilterPopupComponent filterComponent) {
|
||||
super(value);
|
||||
myValue = value;
|
||||
myFilterComponent = filterComponent;
|
||||
AllAction() {
|
||||
super(ALL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
myFilterComponent.setValue(myValue);
|
||||
myFilterComponent.applyFilters();
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
myFilterModel.setFilter(null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ class MultilinePopupBuilder {
|
||||
popup.setMinimumSize(new Dimension(200, 90));
|
||||
AnAction okAction = new DumbAwareAction() {
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
unregisterCustomShortcutSet(popup.getContent());
|
||||
popup.closeOk(e.getInputEvent());
|
||||
}
|
||||
@@ -131,6 +131,7 @@ class MultilinePopupBuilder {
|
||||
return lastPosition;
|
||||
}
|
||||
|
||||
@SuppressWarnings("StringToUpperCaseOrToLowerCaseWithoutLocale")
|
||||
@Override
|
||||
protected void addCompletionVariants(@NotNull String text, int offset, @NotNull String prefix,
|
||||
@NotNull CompletionResultSet result) {
|
||||
|
||||
+21
-33
@@ -26,6 +26,7 @@ import com.intellij.openapi.ui.popup.JBPopupAdapter;
|
||||
import com.intellij.openapi.ui.popup.LightweightWindowEvent;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.vcs.log.VcsLogFilter;
|
||||
import com.intellij.vcs.log.data.VcsLogUiProperties;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -36,12 +37,18 @@ abstract class MultipleValueFilterPopupComponent<Filter extends VcsLogFilter> ex
|
||||
|
||||
private static final int MAX_FILTER_VALUE_LENGTH = 30;
|
||||
|
||||
@Nullable private Collection<String> mySelectedValues;
|
||||
@NotNull protected final VcsLogUiProperties myUiProperties;
|
||||
|
||||
MultipleValueFilterPopupComponent(@NotNull VcsLogClassicFilterUi filterUi, @NotNull String filterName) {
|
||||
super(filterUi, filterName);
|
||||
MultipleValueFilterPopupComponent(@NotNull String filterName,
|
||||
@NotNull VcsLogUiProperties uiProperties,
|
||||
@NotNull FilterModel<Filter> filterModel) {
|
||||
super(filterName, filterModel);
|
||||
myUiProperties = uiProperties;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected abstract Collection<String> getValues(@Nullable Filter filter);
|
||||
|
||||
@NotNull
|
||||
protected abstract List<List<String>> getRecentValuesFromSettings();
|
||||
|
||||
@@ -50,10 +57,8 @@ abstract class MultipleValueFilterPopupComponent<Filter extends VcsLogFilter> ex
|
||||
@NotNull
|
||||
protected abstract List<String> getAllValues();
|
||||
|
||||
@Nullable
|
||||
protected Collection<String> getSelectedValues() {
|
||||
return mySelectedValues;
|
||||
}
|
||||
@NotNull
|
||||
protected abstract Filter createFilter(@NotNull Collection<String> values);
|
||||
|
||||
@NotNull
|
||||
protected ActionGroup createRecentItemsActionGroup() {
|
||||
@@ -69,15 +74,6 @@ abstract class MultipleValueFilterPopupComponent<Filter extends VcsLogFilter> ex
|
||||
return group;
|
||||
}
|
||||
|
||||
void apply(@Nullable Collection<String> values, @NotNull String text, @NotNull String tooltip) {
|
||||
mySelectedValues = values;
|
||||
applyFilters();
|
||||
setValue(text, tooltip);
|
||||
if (values != null) {
|
||||
rememberValuesInSettings(values);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static String displayableText(@NotNull Collection<String> values) {
|
||||
if (values.size() == 1) {
|
||||
@@ -91,17 +87,6 @@ abstract class MultipleValueFilterPopupComponent<Filter extends VcsLogFilter> ex
|
||||
return StringUtil.join(values, ", ");
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected AnAction createAllAction() {
|
||||
return new DumbAwareAction(ALL) {
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
apply(null, ALL, ALL);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected AnAction createPredefinedValueAction(@NotNull Collection<String> values) {
|
||||
return new PredefinedValueAction(values);
|
||||
@@ -122,8 +107,9 @@ abstract class MultipleValueFilterPopupComponent<Filter extends VcsLogFilter> ex
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
apply(myValues, displayableText(myValues), tooltip(myValues));
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
myFilterModel.setFilter(createFilter(myValues));
|
||||
rememberValuesInSettings(myValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,13 +123,14 @@ abstract class MultipleValueFilterPopupComponent<Filter extends VcsLogFilter> ex
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
Project project = e.getProject();
|
||||
if (project == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final MultilinePopupBuilder popupBuilder = new MultilinePopupBuilder(project, myVariants, getPopupText(mySelectedValues));
|
||||
Filter filter = myFilterModel.getFilter();
|
||||
final MultilinePopupBuilder popupBuilder = new MultilinePopupBuilder(project, myVariants, getPopupText(getValues(filter)));
|
||||
JBPopup popup = popupBuilder.createPopup();
|
||||
popup.addListener(new JBPopupAdapter() {
|
||||
@Override
|
||||
@@ -151,10 +138,11 @@ abstract class MultipleValueFilterPopupComponent<Filter extends VcsLogFilter> ex
|
||||
if (event.isOk()) {
|
||||
Collection<String> selectedValues = popupBuilder.getSelectedValues();
|
||||
if (selectedValues.isEmpty()) {
|
||||
apply(null, ALL, ALL);
|
||||
myFilterModel.setFilter(null);
|
||||
}
|
||||
else {
|
||||
apply(selectedValues, displayableText(selectedValues), tooltip(selectedValues));
|
||||
myFilterModel.setFilter(createFilter(selectedValues));
|
||||
rememberValuesInSettings(selectedValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+47
-41
@@ -17,6 +17,7 @@ package com.intellij.vcs.log.ui.filter;
|
||||
|
||||
import com.intellij.openapi.actionSystem.ActionGroup;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -24,26 +25,52 @@ import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.vcs.log.VcsLogDataPack;
|
||||
import com.intellij.vcs.log.VcsLogStructureFilter;
|
||||
import com.intellij.vcs.log.data.VcsLogStructureFilterImpl;
|
||||
import com.intellij.vcs.log.ui.VcsStructureChooser;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
class StructureFilterPopupComponent extends FilterPopupComponent<VcsLogStructureFilter> {
|
||||
|
||||
public static final int FILTER_LABEL_LENGTH = 20;
|
||||
@NotNull private final Collection<VirtualFile> myRoots;
|
||||
@NotNull private final Collection<VirtualFile> myFiles;
|
||||
private static final int FILTER_LABEL_LENGTH = 20;
|
||||
|
||||
public StructureFilterPopupComponent(@NotNull VcsLogClassicFilterUi filterUi, @NotNull Collection<VirtualFile> roots) {
|
||||
super(filterUi, "Structure");
|
||||
myRoots = roots;
|
||||
myFiles = ContainerUtil.newArrayList();
|
||||
public StructureFilterPopupComponent(@NotNull FilterModel<VcsLogStructureFilter> filterModel) {
|
||||
super("Structure", filterModel);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String getText(@NotNull VcsLogStructureFilter filter) {
|
||||
Collection<VirtualFile> files = getAllFiles(myFilterModel.getDataPack(), filter);
|
||||
if (files.size() == 0) {
|
||||
return ALL;
|
||||
}
|
||||
else if (files.size() == 1) {
|
||||
VirtualFile file = files.iterator().next();
|
||||
return StringUtil.shortenPathWithEllipsis(file.getPresentableUrl(), FILTER_LABEL_LENGTH);
|
||||
}
|
||||
else {
|
||||
return files.size() + " items";
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected String getToolTip(@NotNull VcsLogStructureFilter filter) {
|
||||
return getToolTip(getAllFiles(myFilterModel.getDataPack(), filter));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Collection<VirtualFile> getAllFiles(@NotNull VcsLogDataPack dataPack, @NotNull VcsLogStructureFilter filter) {
|
||||
Collection<VirtualFile> result = ContainerUtil.newArrayList();
|
||||
for (VirtualFile root : dataPack.getLogProviders().keySet()) {
|
||||
result.addAll(filter.getFiles(root));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,27 +78,8 @@ class StructureFilterPopupComponent extends FilterPopupComponent<VcsLogStructure
|
||||
return new DefaultActionGroup(createAllAction(), new SelectAction());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected VcsLogStructureFilter getFilter() {
|
||||
return getValue() == ALL || myFiles.isEmpty() ? null : new VcsLogStructureFilterImpl(myFiles, myRoots);
|
||||
}
|
||||
|
||||
private void setValue(@NotNull Collection<VirtualFile> files) {
|
||||
if (files.size() == 0) {
|
||||
setValue(ALL);
|
||||
}
|
||||
else if (files.size() == 1) {
|
||||
VirtualFile file = files.iterator().next();
|
||||
setValue(StringUtil.shortenPathWithEllipsis(file.getPresentableUrl(), FILTER_LABEL_LENGTH), getTooltip(files));
|
||||
}
|
||||
else {
|
||||
setValue(files.size() + " items", getTooltip(files));
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getTooltip(@NotNull Collection<VirtualFile> files) {
|
||||
private static String getToolTip(@NotNull Collection<VirtualFile> files) {
|
||||
List<VirtualFile> filesToDisplay = new ArrayList<VirtualFile>(files);
|
||||
if (files.size() > 10) {
|
||||
filesToDisplay = filesToDisplay.subList(0, 10);
|
||||
@@ -94,19 +102,17 @@ class StructureFilterPopupComponent extends FilterPopupComponent<VcsLogStructure
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
Project project = e.getProject();
|
||||
assert project != null;
|
||||
VcsStructureChooser chooser = new VcsStructureChooser(project, "Select Files or Folders to Filter", myFiles,
|
||||
new ArrayList<VirtualFile>(myRoots));
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
Project project = e.getRequiredData(CommonDataKeys.PROJECT);
|
||||
VcsLogDataPack dataPack = myFilterModel.getDataPack();
|
||||
VcsLogStructureFilter filter = myFilterModel.getFilter();
|
||||
Collection<VirtualFile> files = filter == null ? Collections.<VirtualFile>emptySet() : getAllFiles(dataPack, filter);
|
||||
Set<VirtualFile> roots = dataPack.getLogProviders().keySet();
|
||||
VcsStructureChooser chooser = new VcsStructureChooser(project, "Select Files or Folders to Filter", files,
|
||||
new ArrayList<VirtualFile>(roots));
|
||||
if (chooser.showAndGet()) {
|
||||
myFiles.clear();
|
||||
myFiles.addAll(chooser.getSelectedFiles());
|
||||
setValue(myFiles);
|
||||
applyFilters();
|
||||
myFilterModel.setFilter(new VcsLogStructureFilterImpl(chooser.getSelectedFiles(), roots));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+37
-13
@@ -38,16 +38,29 @@ class UserFilterPopupComponent extends MultipleValueFilterPopupComponent<VcsLogU
|
||||
|
||||
private static final String ME = "me";
|
||||
|
||||
private final VcsLogDataHolder myDataHolder;
|
||||
private final VcsLogUiProperties myUiProperties;
|
||||
@NotNull private final VcsLogDataHolder myDataHolder;
|
||||
@NotNull private final VcsLogUiProperties myUiProperties;
|
||||
|
||||
UserFilterPopupComponent(@NotNull VcsLogClassicFilterUi filterUi, @NotNull VcsLogDataHolder dataHolder,
|
||||
@NotNull VcsLogUiProperties uiProperties) {
|
||||
super(filterUi, "User");
|
||||
UserFilterPopupComponent(@NotNull VcsLogUiProperties uiProperties,
|
||||
@NotNull VcsLogDataHolder dataHolder,
|
||||
@NotNull FilterModel<VcsLogUserFilter> filterModel) {
|
||||
super("User", uiProperties, filterModel);
|
||||
myDataHolder = dataHolder;
|
||||
myUiProperties = uiProperties;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String getText(@NotNull VcsLogUserFilter filter) {
|
||||
return displayableText(getValues(filter));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected String getToolTip(@NotNull VcsLogUserFilter filter) {
|
||||
return tooltip(getValues(filter));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ActionGroup createActionGroup() {
|
||||
DefaultActionGroup group = new DefaultActionGroup();
|
||||
@@ -62,17 +75,21 @@ class UserFilterPopupComponent extends MultipleValueFilterPopupComponent<VcsLogU
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected List<List<String>> getRecentValuesFromSettings() {
|
||||
return myUiProperties.getRecentlyFilteredUserGroups();
|
||||
protected Collection<String> getValues(@Nullable VcsLogUserFilter filter) {
|
||||
if (filter == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
Set<String> result = ContainerUtil.newHashSet();
|
||||
for (VirtualFile root : myFilterModel.getDataPack().getLogProviders().keySet()) {
|
||||
result.addAll(filter.getUserNames(root));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@NotNull
|
||||
@Override
|
||||
protected VcsLogUserFilter getFilter() {
|
||||
if (getSelectedValues() == null) {
|
||||
return null;
|
||||
}
|
||||
return new VcsLogUserFilterImpl(getSelectedValues(), myDataHolder.getCurrentUser());
|
||||
protected List<List<String>> getRecentValuesFromSettings() {
|
||||
return myUiProperties.getRecentlyFilteredUserGroups();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -91,6 +108,12 @@ class UserFilterPopupComponent extends MultipleValueFilterPopupComponent<VcsLogU
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected VcsLogUserFilter createFilter(@NotNull Collection<String> values) {
|
||||
return new VcsLogUserFilterImpl(values, myDataHolder.getCurrentUser());
|
||||
}
|
||||
|
||||
private static class VcsLogUserFilterImpl implements VcsLogUserFilter {
|
||||
|
||||
@NotNull private final Collection<String> myUsers;
|
||||
@@ -119,6 +142,7 @@ class UserFilterPopupComponent extends MultipleValueFilterPopupComponent<VcsLogU
|
||||
@Override
|
||||
public boolean matches(@NotNull final VcsCommitMetadata commit) {
|
||||
return ContainerUtil.exists(getUserNames(commit.getRoot()), new Condition<String>() {
|
||||
@SuppressWarnings("StringToUpperCaseOrToLowerCaseWithoutLocale")
|
||||
@Override
|
||||
public boolean value(String user) {
|
||||
String lowerUser = user.toLowerCase();
|
||||
|
||||
+132
-65
@@ -20,11 +20,13 @@ import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup;
|
||||
import com.intellij.openapi.actionSystem.Presentation;
|
||||
import com.intellij.openapi.actionSystem.ex.CustomComponentAction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.NotNullComputable;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.ui.SearchTextField;
|
||||
import com.intellij.ui.DocumentAdapter;
|
||||
import com.intellij.ui.SearchTextFieldWithStoredHistory;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
@@ -35,91 +37,129 @@ import com.intellij.vcs.log.impl.VcsLogFilterCollectionImpl;
|
||||
import com.intellij.vcs.log.impl.VcsLogHashFilterImpl;
|
||||
import com.intellij.vcs.log.ui.VcsLogUiImpl;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class VcsLogClassicFilterUi implements VcsLogFilterUi {
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(VcsLogClassicFilterUi.class);
|
||||
private static final String HASH_PATTERN = "[a-fA-F0-9]{7,}";
|
||||
|
||||
@NotNull private final SearchTextField myTextFilter;
|
||||
@NotNull private final VcsLogUiImpl myUi;
|
||||
@NotNull private final DefaultActionGroup myActionGroup;
|
||||
|
||||
@NotNull private final BranchFilterPopupComponent myBranchFilterComponent;
|
||||
@NotNull private final UserFilterPopupComponent myUserFilterComponent;
|
||||
@NotNull private final DateFilterPopupComponent myDateFilterComponent;
|
||||
@NotNull private final StructureFilterPopupComponent myStructureFilterComponent;
|
||||
@NotNull private final VcsLogDataHolder myLogDataHolder;
|
||||
@NotNull private final VcsLogUiProperties myUiProperties;
|
||||
|
||||
public VcsLogClassicFilterUi(@NotNull VcsLogUiImpl ui, @NotNull VcsLogDataHolder logDataHolder, @NotNull VcsLogUiProperties uiProperties,
|
||||
@NotNull private VcsLogDataPack myDataPack;
|
||||
|
||||
@NotNull private final FilterModel<VcsLogBranchFilter> myBranchFilterModel;
|
||||
@NotNull private final FilterModel<VcsLogUserFilter> myUserFilterModel;
|
||||
@NotNull private final FilterModel<VcsLogDateFilter> myDateFilterModel;
|
||||
@NotNull private final FilterModel<VcsLogStructureFilter> myStructureFilterModel;
|
||||
@NotNull private final FilterModel<VcsLogTextFilter> myTextFilterModel;
|
||||
|
||||
public VcsLogClassicFilterUi(@NotNull VcsLogUiImpl ui,
|
||||
@NotNull VcsLogDataHolder logDataHolder,
|
||||
@NotNull VcsLogUiProperties uiProperties,
|
||||
@NotNull VcsLogDataPack initialDataPack) {
|
||||
myUi = ui;
|
||||
myLogDataHolder = logDataHolder;
|
||||
myUiProperties = uiProperties;
|
||||
myDataPack = initialDataPack;
|
||||
|
||||
myTextFilter = new SearchTextFieldWithStoredHistory("Vcs.Log.Text.Filter.History") {
|
||||
NotNullComputable<VcsLogDataPack> dataPackGetter = new NotNullComputable<VcsLogDataPack>() {
|
||||
@NotNull
|
||||
@Override
|
||||
protected void onFieldCleared() {
|
||||
applyFilters();
|
||||
public VcsLogDataPack compute() {
|
||||
return myDataPack;
|
||||
}
|
||||
};
|
||||
myTextFilter.getTextEditor().addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
applyFilters();
|
||||
myTextFilter.addCurrentTextToHistory();
|
||||
}
|
||||
});
|
||||
myBranchFilterModel = new FilterModel<VcsLogBranchFilter>(dataPackGetter);
|
||||
myUserFilterModel = new FilterModel<VcsLogUserFilter>(dataPackGetter);
|
||||
myDateFilterModel = new FilterModel<VcsLogDateFilter>(dataPackGetter);
|
||||
myStructureFilterModel = new FilterModel<VcsLogStructureFilter>(dataPackGetter);
|
||||
myTextFilterModel = new FilterModel<VcsLogTextFilter>(dataPackGetter);
|
||||
|
||||
myBranchFilterComponent = new BranchFilterPopupComponent(this, initialDataPack, uiProperties);
|
||||
myUserFilterComponent = new UserFilterPopupComponent(this, logDataHolder, uiProperties);
|
||||
myDateFilterComponent = new DateFilterPopupComponent(this);
|
||||
myStructureFilterComponent = new StructureFilterPopupComponent(this, logDataHolder.getRoots());
|
||||
updateUiOnFilterChange();
|
||||
}
|
||||
|
||||
myActionGroup = new DefaultActionGroup();
|
||||
myActionGroup.add(new TextFilterComponent(myTextFilter));
|
||||
myActionGroup.add(new FilterActionComponent(myBranchFilterComponent));
|
||||
myActionGroup.add(new FilterActionComponent(myUserFilterComponent));
|
||||
myActionGroup.add(new FilterActionComponent(myDateFilterComponent));
|
||||
myActionGroup.add(new FilterActionComponent(myStructureFilterComponent));
|
||||
private void updateUiOnFilterChange() {
|
||||
FilterModel[] models = {myBranchFilterModel, myUserFilterModel, myDateFilterModel, myStructureFilterModel, myTextFilterModel};
|
||||
for (FilterModel<?> model : models) {
|
||||
model.addSetFilterListener(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myUi.applyFiltersAndUpdateUi();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void updateDataPack(@NotNull VcsLogDataPack dataPack) {
|
||||
myBranchFilterComponent.updateDataPack(dataPack);
|
||||
myDataPack = dataPack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns filter components which will be added to the Log toolbar.
|
||||
*/
|
||||
@NotNull
|
||||
public ActionGroup getActionGroup() {
|
||||
return myActionGroup;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<JComponent> getComponents() {
|
||||
return Arrays.<JComponent>asList(myTextFilter.getTextEditor(), myBranchFilterComponent, myUserFilterComponent,
|
||||
myDateFilterComponent, myStructureFilterComponent);
|
||||
public ActionGroup createActionGroup() {
|
||||
DefaultActionGroup actionGroup = new DefaultActionGroup();
|
||||
actionGroup.add(new TextFilterComponent(myTextFilterModel));
|
||||
actionGroup.add(new FilterActionComponent(new Computable<JComponent>() {
|
||||
@Override
|
||||
public JComponent compute() {
|
||||
return new BranchFilterPopupComponent(myUiProperties, myBranchFilterModel);
|
||||
}
|
||||
}));
|
||||
actionGroup.add(new FilterActionComponent(new Computable<JComponent>() {
|
||||
@Override
|
||||
public JComponent compute() {
|
||||
return new UserFilterPopupComponent(myUiProperties, myLogDataHolder, myUserFilterModel);
|
||||
}
|
||||
}));
|
||||
actionGroup.add(new FilterActionComponent(new Computable<JComponent>() {
|
||||
@Override
|
||||
public JComponent compute() {
|
||||
return new DateFilterPopupComponent(myDateFilterModel);
|
||||
}
|
||||
}));
|
||||
actionGroup.add(new FilterActionComponent(new Computable<JComponent>() {
|
||||
@Override
|
||||
public JComponent compute() {
|
||||
return new StructureFilterPopupComponent(myStructureFilterModel);
|
||||
}
|
||||
}));
|
||||
return actionGroup;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public VcsLogFilterCollection getFilters() {
|
||||
Pair<VcsLogTextFilter, VcsLogHashFilter> filtersFromText = getFiltersFromTextArea(myTextFilter.getText().trim());
|
||||
return new VcsLogFilterCollectionImpl(myBranchFilterComponent.getFilter(), myUserFilterComponent.getFilter(),
|
||||
filtersFromText.second, myDateFilterComponent.getFilter(),
|
||||
filtersFromText.first, myStructureFilterComponent.getFilter());
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
Pair<VcsLogTextFilter, VcsLogHashFilter> filtersFromText = getFiltersFromTextArea(myTextFilterModel.getFilter());
|
||||
return new VcsLogFilterCollectionImpl(myBranchFilterModel.getFilter(),
|
||||
myUserFilterModel.getFilter(),
|
||||
filtersFromText.second,
|
||||
myDateFilterModel.getFilter(),
|
||||
filtersFromText.first,
|
||||
myStructureFilterModel.getFilter());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Pair<VcsLogTextFilter, VcsLogHashFilter> getFiltersFromTextArea(@NotNull String text) {
|
||||
if (text.isEmpty()) {
|
||||
private static Pair<VcsLogTextFilter, VcsLogHashFilter> getFiltersFromTextArea(@Nullable VcsLogTextFilter filter) {
|
||||
if (filter == null) {
|
||||
return Pair.empty();
|
||||
}
|
||||
String text = filter.getText().trim();
|
||||
if (StringUtil.isEmptyOrSpaces(text)) {
|
||||
return Pair.empty();
|
||||
}
|
||||
List<String> hashes = ContainerUtil.newArrayList();
|
||||
@@ -147,23 +187,21 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi {
|
||||
|
||||
@Override
|
||||
public void setFilter(@NotNull VcsLogFilter filter) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
if (filter instanceof VcsLogBranchFilter) {
|
||||
Collection<String> values = ((VcsLogBranchFilter)filter).getBranchNames();
|
||||
myBranchFilterComponent.apply(values, MultipleValueFilterPopupComponent.displayableText(values),
|
||||
MultipleValueFilterPopupComponent.tooltip(values));
|
||||
myBranchFilterModel.setFilter((VcsLogBranchFilter)filter);
|
||||
JComponent toolbar = myUi.getMainFrame().getToolbar();
|
||||
toolbar.revalidate();
|
||||
toolbar.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void applyFilters() {
|
||||
myUi.applyFiltersAndUpdateUi();
|
||||
}
|
||||
|
||||
private static class TextFilterComponent extends DumbAwareAction implements CustomComponentAction {
|
||||
|
||||
private final SearchTextField mySearchField;
|
||||
private final FilterModel<VcsLogTextFilter> myFilterModel;
|
||||
|
||||
TextFilterComponent(SearchTextField searchField) {
|
||||
mySearchField = searchField;
|
||||
public TextFilterComponent(FilterModel<VcsLogTextFilter> filterModel) {
|
||||
myFilterModel = filterModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -172,30 +210,59 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi {
|
||||
JLabel filterCaption = new JLabel("Filter:");
|
||||
filterCaption.setForeground(UIUtil.isUnderDarcula() ? UIUtil.getLabelForeground() : UIUtil.getInactiveTextColor());
|
||||
panel.add(filterCaption);
|
||||
panel.add(mySearchField);
|
||||
panel.add(createSearchField());
|
||||
return panel;
|
||||
}
|
||||
|
||||
private Component createSearchField() {
|
||||
final SearchTextFieldWithStoredHistory textFilter = new SearchTextFieldWithStoredHistory("Vcs.Log.Text.Filter.History") {
|
||||
@Override
|
||||
protected void onFieldCleared() {
|
||||
myFilterModel.setFilter(null);
|
||||
}
|
||||
};
|
||||
textFilter.getTextEditor().addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull ActionEvent e) {
|
||||
myFilterModel.setFilter(new VcsLogTextFilterImpl(textFilter.getText()));
|
||||
textFilter.addCurrentTextToHistory();
|
||||
}
|
||||
});
|
||||
resetFilterOnTextClear(textFilter);
|
||||
return textFilter;
|
||||
}
|
||||
|
||||
private void resetFilterOnTextClear(@NotNull SearchTextFieldWithStoredHistory textFilter) {
|
||||
textFilter.addDocumentListener(new DocumentAdapter() {
|
||||
@Override
|
||||
protected void textChanged(DocumentEvent e) {
|
||||
if (e.getDocument().getLength() == 0) {
|
||||
myFilterModel.setFilter(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
}
|
||||
}
|
||||
|
||||
private static class FilterActionComponent extends DumbAwareAction implements CustomComponentAction {
|
||||
private final FilterPopupComponent myComponent;
|
||||
|
||||
public FilterActionComponent(FilterPopupComponent component) {
|
||||
myComponent = component;
|
||||
@NotNull private final Computable<JComponent> myComponentCreator;
|
||||
|
||||
public FilterActionComponent(@NotNull Computable<JComponent> componentCreator) {
|
||||
myComponentCreator = componentCreator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent createCustomComponent(Presentation presentation) {
|
||||
return myComponent;
|
||||
return myComponentCreator.compute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider {
|
||||
toolbarGroup.add(ActionManager.getInstance().getAction(VcsLogUiImpl.TOOLBAR_ACTION_GROUP));
|
||||
|
||||
DefaultActionGroup mainGroup = new DefaultActionGroup();
|
||||
mainGroup.add(myFilterUi.getActionGroup());
|
||||
mainGroup.add(myFilterUi.createActionGroup());
|
||||
mainGroup.addSeparator();
|
||||
mainGroup.add(toolbarGroup);
|
||||
ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.CHANGES_VIEW_TOOLBAR, mainGroup, true);
|
||||
@@ -264,7 +264,8 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider {
|
||||
}
|
||||
}
|
||||
|
||||
public Component getToolbar() {
|
||||
@NotNull
|
||||
public JComponent getToolbar() {
|
||||
return myToolbar;
|
||||
}
|
||||
|
||||
@@ -382,8 +383,7 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider {
|
||||
@NotNull
|
||||
@Override
|
||||
protected List<Component> getOrderedComponents() {
|
||||
return ContainerUtil.concat(Arrays.<Component>asList(myGraphTable, myChangesBrowser.getPreferredFocusedComponent()),
|
||||
myFilterUi.getComponents());
|
||||
return Arrays.<Component>asList(myGraphTable, myChangesBrowser.getPreferredFocusedComponent());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -387,9 +387,13 @@ class BaseInterpreterInterface:
|
||||
|
||||
def getArray(self, attr, roffset, coffset, rows, cols, format):
|
||||
xml = "<xml>"
|
||||
array = pydevd_vars.evalInContext(attr, self.getNamespace(), self.getNamespace())
|
||||
name = ".".join(attr.split("\t"))
|
||||
array = pydevd_vars.evalInContext(name, self.getNamespace(), self.getNamespace())
|
||||
|
||||
xml += pydevd_vars.array_to_xml(array, roffset, coffset, rows, cols, format)
|
||||
if rows == -1 and cols == -1:
|
||||
xml += pydevd_vars.array_to_meta_xml(array, name, format)
|
||||
else:
|
||||
xml += pydevd_vars.array_to_xml(array, roffset, coffset, rows, cols, format)
|
||||
|
||||
xml += "</xml>"
|
||||
|
||||
|
||||
@@ -974,27 +974,33 @@ class InternalGetArray(InternalThreadCommand):
|
||||
self.thread_id = thread_id
|
||||
self.frame_id = frame_id
|
||||
self.scope = scope
|
||||
self.name = attrs[-1]
|
||||
self.attrs = attrs;
|
||||
self.name = ".".join(attrs.split("\t"))
|
||||
self.attrs = attrs
|
||||
self.roffset = int(roffset)
|
||||
self.coffset = int(coffset)
|
||||
self.rows = int(rows)
|
||||
self.cols = int(cols)
|
||||
self.format = '\'' + format + '\''
|
||||
self.format = format
|
||||
if hasattr(self.format, 'decode'):
|
||||
self.format = self.format.decode('utf-8')
|
||||
|
||||
def doIt(self, dbg):
|
||||
try:
|
||||
var = getVariable(self.thread_id, self.frame_id, 'EXPRESSION', self.attrs)
|
||||
frame = pydevd_vars.findFrame(self.thread_id, self.frame_id)
|
||||
var = pydevd_vars.evalInContext(self.name, frame.f_globals, frame.f_locals)
|
||||
|
||||
xml = "<xml>"
|
||||
|
||||
xml += pydevd_vars.array_to_xml(var, self.roffset, self.coffset, self.rows, self.cols, self.format)
|
||||
|
||||
# need metadata
|
||||
if self.rows == -1 and self.cols == -1:
|
||||
xml += pydevd_vars.array_to_meta_xml(var, self.name, self.format)
|
||||
else:
|
||||
xml += pydevd_vars.array_to_xml(var, self.roffset, self.coffset, self.rows, self.cols, self.format)
|
||||
xml += "</xml>"
|
||||
cmd = dbg.cmdFactory.makeGetArrayMessage(self.sequence, xml)
|
||||
dbg.writer.addCommand(cmd)
|
||||
except:
|
||||
cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error resolving array " + GetExceptionTracebackStr())
|
||||
cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error resolving array: " + GetExceptionTracebackStr())
|
||||
dbg.writer.addCommand(cmd)
|
||||
|
||||
#=======================================================================================================================
|
||||
|
||||
@@ -388,44 +388,45 @@ def changeAttrExpression(thread_id, frame_id, attr, expression, dbg):
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
MAXIMUM_ARRAY_SIZE = 300
|
||||
MAXIMUM_ARRAY_SIZE = 100
|
||||
MAX_SLICE_SIZE = 1000
|
||||
|
||||
def array_to_xml(array, roffset, coffset, rows, cols, format):
|
||||
xml = ""
|
||||
rows = min(rows, MAXIMUM_ARRAY_SIZE)
|
||||
cols = min(cols, MAXIMUM_ARRAY_SIZE)
|
||||
if rows == 1 and cols == 1:
|
||||
rows = 1
|
||||
cols = 1
|
||||
elif rows == 1 or cols == 1:
|
||||
is_row = True if (rows == 1) else False
|
||||
if is_row:
|
||||
array = array[roffset:]
|
||||
else:
|
||||
array = array[coffset:]
|
||||
|
||||
if len(array) == 1:
|
||||
array = array[0]
|
||||
|
||||
if is_row:
|
||||
cols = min(cols, len(array))
|
||||
else:
|
||||
rows = min(rows, len(array))
|
||||
else:
|
||||
#there is no obvious rule for slicing (at least 5 choices)
|
||||
if len(array) == 1 and (rows > 1 or cols > 1):
|
||||
array = array[0]
|
||||
if array.size > len(array):
|
||||
array = array[roffset:, coffset:]
|
||||
rows = min(rows, len(array))
|
||||
cols = min(cols, len(array[0]))
|
||||
xml += "<array rows=\"%s\" cols=\"%s\"/>" % (rows, cols)
|
||||
if len(array) == 1:
|
||||
array = array[0]
|
||||
elif array.size == len(array):
|
||||
if roffset == 0 and rows == 1:
|
||||
array = array[coffset:]
|
||||
cols = min(cols, len(array))
|
||||
elif coffset == 0 and cols == 1:
|
||||
array = array[roffset:]
|
||||
rows = min(rows, len(array))
|
||||
|
||||
xml += "<array meta=\"%s\" rows=\"%s\" cols=\"%s\"/>" % (False, rows, cols)
|
||||
for row in range(rows):
|
||||
xml += "<row index=\"%s\"/>" % to_string(row)
|
||||
for col in range(cols):
|
||||
value = array
|
||||
if rows == 1 or cols == 1:
|
||||
if rows == 1 and cols == 1:
|
||||
value = array
|
||||
value = array[0]
|
||||
else:
|
||||
dim = col if (rows == 1) else row
|
||||
value = array[dim]
|
||||
if "ndarray" in str(type(value)):
|
||||
value = value[0]
|
||||
else:
|
||||
value = array[row][col]
|
||||
value = format % value
|
||||
@@ -433,6 +434,66 @@ def array_to_xml(array, roffset, coffset, rows, cols, format):
|
||||
return xml
|
||||
|
||||
|
||||
def array_to_meta_xml(array, name, format):
|
||||
type = array.dtype.kind
|
||||
slice = name
|
||||
l = len(array.shape)
|
||||
|
||||
# initial load, compute slice
|
||||
if format == '%':
|
||||
if l > 2:
|
||||
slice += '[0]' * (l - 2)
|
||||
for r in range(l - 2):
|
||||
array = array[0]
|
||||
if type == 'f':
|
||||
format = '.5f'
|
||||
elif type == 'i' or type == 'u':
|
||||
format = 'd'
|
||||
else:
|
||||
format = 's'
|
||||
else:
|
||||
format = format.replace('%', '')
|
||||
|
||||
l = len(array.shape)
|
||||
reslice = ""
|
||||
if l > 2:
|
||||
raise Exception("%s has more than 2 dimensions." % slice)
|
||||
elif l == 1:
|
||||
# special case with 1D arrays arr[i, :] - row, but arr[:, i] - column with equal shape and ndim
|
||||
# http://stackoverflow.com/questions/16837946/numpy-a-2-rows-1-column-file-loadtxt-returns-1row-2-columns
|
||||
# explanation: http://stackoverflow.com/questions/15165170/how-do-i-maintain-row-column-orientation-of-vectors-in-numpy?rq=1
|
||||
# we use kind of a hack - get information about memory from C_CONTIGUOUS
|
||||
is_row = array.flags['C_CONTIGUOUS']
|
||||
|
||||
if is_row:
|
||||
rows = 1
|
||||
cols = min(len(array), MAX_SLICE_SIZE)
|
||||
if cols < len(array):
|
||||
reslice = '[0:%s]' % (cols)
|
||||
array = array[0:cols]
|
||||
else:
|
||||
cols = 1
|
||||
rows = min(len(array), MAX_SLICE_SIZE)
|
||||
if rows < len(array):
|
||||
reslice = '[0:%s]' % (rows)
|
||||
array = array[0:rows]
|
||||
elif l == 2:
|
||||
rows = min(array.shape[-2], MAX_SLICE_SIZE)
|
||||
cols = min(array.shape[-1], MAX_SLICE_SIZE)
|
||||
if cols < array.shape[-1] or rows < array.shape[-2]:
|
||||
reslice = '[0:%s, 0:%s]' % (rows, cols)
|
||||
array = array[0:rows, 0:cols]
|
||||
|
||||
#avoid slice duplication
|
||||
if not slice.endswith(reslice):
|
||||
slice += reslice
|
||||
|
||||
bounds = (0, 0)
|
||||
if type in "biufc":
|
||||
bounds = (array.min(), array.max())
|
||||
xml = '<array meta=\"%s\" slice=\"%s\" rows=\"%s\" cols=\"%s\" format=\"%s\" type=\"%s\" max=\"%s\" min=\"%s\"/>' % \
|
||||
(True, slice, rows, cols, format, type, bounds[1], bounds[0])
|
||||
return xml
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.jetbrains.python.debugger.PyDebugValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author amarch
|
||||
*/
|
||||
public class ArrayChunk {
|
||||
private final PyDebugValue myValue;
|
||||
private final String mySlicePresentation;
|
||||
private final int myRows;
|
||||
private final int myColumns;
|
||||
private final String myMax;
|
||||
private final String myMin;
|
||||
private final String myFormat;
|
||||
private final String myType;
|
||||
private final Object[][] myData;
|
||||
|
||||
public ArrayChunk(@NotNull PyDebugValue value,
|
||||
String slicePresentation,
|
||||
int rows,
|
||||
int columns,
|
||||
String max,
|
||||
String min,
|
||||
String format,
|
||||
String type,
|
||||
@Nullable Object[][] data) {
|
||||
myValue = value;
|
||||
mySlicePresentation = slicePresentation;
|
||||
myRows = rows;
|
||||
myColumns = columns;
|
||||
myMax = max;
|
||||
myMin = min;
|
||||
myFormat = format;
|
||||
myType = type;
|
||||
myData = data;
|
||||
}
|
||||
|
||||
public PyDebugValue getValue() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
public String getSlicePresentation() {
|
||||
return mySlicePresentation;
|
||||
}
|
||||
|
||||
public int getRows() {
|
||||
return myRows;
|
||||
}
|
||||
|
||||
public int getColumns() {
|
||||
return myColumns;
|
||||
}
|
||||
|
||||
public String getMax() {
|
||||
return myMax;
|
||||
}
|
||||
|
||||
public String getMin() {
|
||||
return myMin;
|
||||
}
|
||||
|
||||
public String getFormat() {
|
||||
return myFormat;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return myType;
|
||||
}
|
||||
|
||||
public Object[][] getData() {
|
||||
return myData;
|
||||
}
|
||||
|
||||
public boolean metaOnly() {
|
||||
return myData == null;
|
||||
}
|
||||
|
||||
public boolean containsMeta() {
|
||||
return !mySlicePresentation.isEmpty() &&
|
||||
myRows > 0 &&
|
||||
myColumns > 0 &&
|
||||
!myFormat.isEmpty() &&
|
||||
!myType.isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -21,5 +21,5 @@ public interface PyFrameAccessor {
|
||||
@Nullable
|
||||
PyReferrersLoader getReferrersLoader();
|
||||
|
||||
Object[][] getArrayItems(PyDebugValue var, int rowOffset, int colOffset, int rows, int cols, String format) throws PyDebuggerException;
|
||||
ArrayChunk getArrayItems(PyDebugValue var, int rowOffset, int colOffset, int rows, int cols, String format) throws PyDebuggerException;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.jetbrains.python.debugger.pydev;
|
||||
|
||||
import com.jetbrains.python.debugger.ArrayChunk;
|
||||
import com.jetbrains.python.debugger.PyDebugValue;
|
||||
import com.jetbrains.python.debugger.PyDebuggerException;
|
||||
|
||||
@@ -15,7 +16,7 @@ public class GetArrayCommand extends GetFrameCommand {
|
||||
private final int myRows;
|
||||
private final int myColumns;
|
||||
private final String myFormat;
|
||||
private Object[][] myArrayItems;
|
||||
private ArrayChunk myChunk;
|
||||
|
||||
public GetArrayCommand(final RemoteDebugger debugger, final String threadId, final String frameId, PyDebugValue var, int rowOffset, int colOffset, int rows, int cols, String format) {
|
||||
super(debugger, GET_ARRAY, threadId, frameId);
|
||||
@@ -54,10 +55,10 @@ public class GetArrayCommand extends GetFrameCommand {
|
||||
if (response.getCommand() >= 900 && response.getCommand() < 1000) {
|
||||
throw new PyDebuggerException(response.getPayload());
|
||||
}
|
||||
myArrayItems = ProtocolParser.parseArrayValues(response.getPayload(), myDebugProcess);
|
||||
myChunk = ProtocolParser.parseArrayValues(response.getPayload(), myDebugProcess);
|
||||
}
|
||||
|
||||
public Object[][] getArray(){
|
||||
return myArrayItems;
|
||||
public ArrayChunk getArray(){
|
||||
return myChunk;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ public class MultiProcessDebugger implements ProcessDebugger {
|
||||
return debugger(threadId).loadVariable(threadId, frameId, var);
|
||||
}
|
||||
|
||||
public Object[][] loadArrayItems(String threadId,
|
||||
public ArrayChunk loadArrayItems(String threadId,
|
||||
String frameId,
|
||||
PyDebugValue var,
|
||||
int rowOffset,
|
||||
|
||||
@@ -2,10 +2,7 @@ package com.jetbrains.python.debugger.pydev;
|
||||
|
||||
import com.intellij.xdebugger.frame.XValueChildrenList;
|
||||
import com.jetbrains.python.console.pydev.PydevCompletionVariant;
|
||||
import com.jetbrains.python.debugger.PyDebugValue;
|
||||
import com.jetbrains.python.debugger.PyDebuggerException;
|
||||
import com.jetbrains.python.debugger.PyReferringObjectsValue;
|
||||
import com.jetbrains.python.debugger.PyThreadInfo;
|
||||
import com.jetbrains.python.debugger.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -36,7 +33,7 @@ public interface ProcessDebugger {
|
||||
// todo: don't generate temp variables for qualified expressions - just split 'em
|
||||
XValueChildrenList loadVariable(String threadId, String frameId, PyDebugValue var) throws PyDebuggerException;
|
||||
|
||||
Object[][] loadArrayItems(String threadId, String frameId, PyDebugValue var, int rowOffset, int colOffset, int rows, int cols, String format) throws PyDebuggerException;
|
||||
ArrayChunk loadArrayItems(String threadId, String frameId, PyDebugValue var, int rowOffset, int colOffset, int rows, int cols, String format) throws PyDebuggerException;
|
||||
|
||||
void loadReferrers(String threadId, String frameId, PyReferringObjectsValue var, PyDebugCallback<XValueChildrenList> callback);
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ public class ProtocolParser {
|
||||
return new PyDebugValue(name, type, value, "True".equals(isContainer), "True".equals(isErrorOnEval), frameAccessor);
|
||||
}
|
||||
|
||||
public static Object[][] parseArrayValues(final String text, final PyFrameAccessor frameAccessor) throws PyDebuggerException {
|
||||
public static ArrayChunk parseArrayValues(final String text, final PyFrameAccessor frameAccessor) throws PyDebuggerException {
|
||||
final XppReader reader = openReader(text, false);
|
||||
int cols = 0;
|
||||
int rows = 0;
|
||||
@@ -189,6 +189,17 @@ public class ProtocolParser {
|
||||
if (!"array".equals(reader.getNodeName())) {
|
||||
throw new PyDebuggerException("Expected <array> at first node, found " + reader.getNodeName());
|
||||
}
|
||||
boolean meta = readString(reader, "meta", null).equals("True");
|
||||
if (meta) {
|
||||
String slice = readString(reader, "slice", null);
|
||||
rows = readInt(reader, "rows", null);
|
||||
cols = readInt(reader, "cols", null);
|
||||
String format = "%" + readString(reader, "format", null);
|
||||
String type = readString(reader, "type", null);
|
||||
String max = readString(reader, "max", null);
|
||||
String min = readString(reader, "min", null);
|
||||
return new ArrayChunk(new PyDebugValue(slice, null, null, false, false, frameAccessor), slice, rows, cols, max, min, format, type, null);
|
||||
}
|
||||
rows = readInt(reader, "rows", null);
|
||||
cols = readInt(reader, "cols", null);
|
||||
reader.moveUp();
|
||||
@@ -197,7 +208,7 @@ public class ProtocolParser {
|
||||
return parseArrayValues(reader, frameAccessor, cols, rows);
|
||||
}
|
||||
|
||||
public static Object[][] parseArrayValues(final XppReader reader, final PyFrameAccessor frameAccessor, final int cols, final int rows) throws PyDebuggerException {
|
||||
public static ArrayChunk parseArrayValues(final XppReader reader, final PyFrameAccessor frameAccessor, final int cols, final int rows) throws PyDebuggerException {
|
||||
if (rows <= 0 || cols <= 0) {
|
||||
throw new PyDebuggerException("Array xml: bad rows or columns number: (" + rows + ", " + cols + ")");
|
||||
}
|
||||
@@ -228,7 +239,7 @@ public class ProtocolParser {
|
||||
reader.moveUp();
|
||||
}
|
||||
|
||||
return values;
|
||||
return new ArrayChunk(new PyDebugValue("", null, null, false, false, frameAccessor), "", rows, cols, null, null, null, null, values);
|
||||
}
|
||||
|
||||
private static XppReader openReader(final String text, final boolean checkForContent) throws PyDebuggerException {
|
||||
|
||||
@@ -155,7 +155,7 @@ public class RemoteDebugger implements ProcessDebugger {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[][] loadArrayItems(String threadId, String frameId, PyDebugValue var, int rowOffset, int colOffset, int rows, int cols, String format) throws PyDebuggerException {
|
||||
public ArrayChunk loadArrayItems(String threadId, String frameId, PyDebugValue var, int rowOffset, int colOffset, int rows, int cols, String format) throws PyDebuggerException {
|
||||
final GetArrayCommand command = new GetArrayCommand(this, threadId, frameId, var, rowOffset, colOffset, rows, cols, format);
|
||||
command.execute();
|
||||
return command.getArray();
|
||||
|
||||
@@ -550,11 +550,18 @@ public class PydevConsoleCommunication extends AbstractConsoleCommunication impl
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[][] getArrayItems(PyDebugValue var, int rowOffset, int colOffset, int rows, int cols, String format)
|
||||
public ArrayChunk getArrayItems(PyDebugValue var, int rowOffset, int colOffset, int rows, int cols, String format)
|
||||
throws PyDebuggerException {
|
||||
if (myClient != null) {
|
||||
try {
|
||||
Object ret = myClient.execute(GET_ARRAY, new Object[]{var.getName(), rowOffset, colOffset, rows, cols, format});
|
||||
String fullName = var.getName();
|
||||
PyDebugValue child = var;
|
||||
while (child.getParent() != null) {
|
||||
child = child.getParent();
|
||||
fullName = child.getName() + "\t" + fullName;
|
||||
}
|
||||
|
||||
Object ret = myClient.execute(GET_ARRAY, new Object[]{fullName, rowOffset, colOffset, rows, cols, format});
|
||||
if (ret instanceof String) {
|
||||
return ProtocolParser.parseArrayValues((String)ret, this);
|
||||
}
|
||||
|
||||
@@ -572,7 +572,7 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[][] getArrayItems(PyDebugValue var, int rowOffset, int colOffset, int rows, int cols, String format)
|
||||
public ArrayChunk getArrayItems(PyDebugValue var, int rowOffset, int colOffset, int rows, int cols, String format)
|
||||
throws PyDebuggerException {
|
||||
final PyStackFrame frame = currentFrame();
|
||||
return myDebugger.loadArrayItems(frame.getThreadId(), frame.getFrameId(), var, rowOffset, colOffset, rows, cols, format);
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.ListenableFutureTask;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.jetbrains.python.debugger.ArrayChunk;
|
||||
import com.jetbrains.python.debugger.PyDebugValue;
|
||||
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
@@ -35,26 +36,26 @@ public class AsyncArrayTableModel extends AbstractTableModel {
|
||||
private static final int CHUNK_ROW_SIZE = 30;
|
||||
public static final String EMPTY_CELL_VALUE = "";
|
||||
|
||||
private final int myRows;
|
||||
private final int myColumns;
|
||||
private int myRows;
|
||||
private int myColumns;
|
||||
private final NumpyArrayTable myProvider;
|
||||
|
||||
|
||||
private final ExecutorService myExecutorService = Executors.newSingleThreadExecutor();
|
||||
|
||||
|
||||
private LoadingCache<Pair<Integer, Integer>, ListenableFuture<Object[][]>> myChunkCache = CacheBuilder.newBuilder().build(
|
||||
new CacheLoader<Pair<Integer, Integer>, ListenableFuture<Object[][]>>() {
|
||||
private LoadingCache<Pair<Integer, Integer>, ListenableFuture<ArrayChunk>> myChunkCache = CacheBuilder.newBuilder().build(
|
||||
new CacheLoader<Pair<Integer, Integer>, ListenableFuture<ArrayChunk>>() {
|
||||
@Override
|
||||
public ListenableFuture<Object[][]> load(final Pair<Integer, Integer> key) throws Exception {
|
||||
public ListenableFuture<ArrayChunk> load(final Pair<Integer, Integer> key) throws Exception {
|
||||
final PyDebugValue value = myProvider.getDebugValue();
|
||||
final PyDebugValue slicedValue =
|
||||
new PyDebugValue(myProvider.getSliceText(), value.getType(), value.getValue(), value.isContainer(), value.isErrorOnEval(),
|
||||
value.getFrameAccessor());
|
||||
|
||||
ListenableFutureTask<Object[][]> task = ListenableFutureTask.create(new Callable<Object[][]>() {
|
||||
ListenableFutureTask<ArrayChunk> task = ListenableFutureTask.create(new Callable<ArrayChunk>() {
|
||||
@Override
|
||||
public Object[][] call() throws Exception {
|
||||
public ArrayChunk call() throws Exception {
|
||||
return value.getFrameAccessor()
|
||||
.getArrayItems(slicedValue, key.first, key.second, Math.min(CHUNK_ROW_SIZE, getRowCount() - key.first),
|
||||
Math.min(CHUNK_COL_SIZE, getColumnCount() - key.second),
|
||||
@@ -91,15 +92,16 @@ public class AsyncArrayTableModel extends AbstractTableModel {
|
||||
Pair<Integer, Integer> key = itemToChunkKey(row, col);
|
||||
|
||||
try {
|
||||
ListenableFuture<Object[][]> chunk = myChunkCache.get(key);
|
||||
ListenableFuture<ArrayChunk> chunk = myChunkCache.get(key);
|
||||
|
||||
if (chunk.isDone()) {
|
||||
Object[][] data = chunk.get().getData();
|
||||
int r = row % CHUNK_ROW_SIZE;
|
||||
int c = col % CHUNK_COL_SIZE;
|
||||
|
||||
if (r < chunk.get().length) {
|
||||
if (c < chunk.get()[r].length) {
|
||||
return myProvider.correctStringValue(chunk.get()[r][c]);
|
||||
if (r < data.length) {
|
||||
if (c < data[r].length) {
|
||||
return myProvider.correctStringValue(data[r][c]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,17 +151,21 @@ public class AsyncArrayTableModel extends AbstractTableModel {
|
||||
}
|
||||
|
||||
public void changeValue(int row, int col, Object value) {
|
||||
Future<Object[][]> chunk = myChunkCache.getIfPresent(itemToChunkKey(row, col));
|
||||
Future<ArrayChunk> chunk = myChunkCache.getIfPresent(itemToChunkKey(row, col));
|
||||
if (chunk != null && chunk.isDone()) {
|
||||
try {
|
||||
chunk.get()[row - getPageRowStart(row)][col - getPageColStart(col)] = value;
|
||||
chunk.get().getData()[row - getPageRowStart(row)][col - getPageColStart(col)] = value;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Forced to change empty cell in " + row + " row and " + col + "column.");
|
||||
throw new IllegalArgumentException("Forced to change empty cell in " + row + " row and " + col + " column.");
|
||||
}
|
||||
}
|
||||
|
||||
public void addToCache(ArrayChunk chunk) {
|
||||
//TODO: add preloaded values
|
||||
}
|
||||
}
|
||||
@@ -15,11 +15,12 @@
|
||||
*/
|
||||
package com.jetbrains.python.debugger.array;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFutureTask;
|
||||
import com.intellij.codeInsight.hint.HintManager;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.Result;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.RangeMarker;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
@@ -27,10 +28,7 @@ import com.intellij.util.Consumer;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.intellij.xdebugger.evaluation.XDebuggerEvaluator;
|
||||
import com.intellij.xdebugger.frame.XValue;
|
||||
import com.jetbrains.python.debugger.PyDebugValue;
|
||||
import com.jetbrains.python.debugger.PyDebuggerEvaluator;
|
||||
import com.jetbrains.python.debugger.PyDebuggerException;
|
||||
import com.jetbrains.python.debugger.PyFrameAccessor;
|
||||
import com.jetbrains.python.debugger.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.management.InvalidAttributeValueException;
|
||||
@@ -40,6 +38,10 @@ import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -54,7 +56,6 @@ public class NumpyArrayTable {
|
||||
private Project myProject;
|
||||
private PyDebuggerEvaluator myEvaluator;
|
||||
private String myDtypeKind;
|
||||
private int[] myShape;
|
||||
private ArrayTableCellRenderer myTableCellRenderer;
|
||||
private AsyncArrayTableModel myPagingModel;
|
||||
|
||||
@@ -81,7 +82,7 @@ public class NumpyArrayTable {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
|
||||
doReslice(getSliceText(), null);
|
||||
doReslice();
|
||||
}
|
||||
}
|
||||
}, new KeyAdapter() {
|
||||
@@ -101,57 +102,39 @@ public class NumpyArrayTable {
|
||||
return myComponent;
|
||||
}
|
||||
|
||||
private AsyncArrayTableModel 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 AsyncArrayTableModel(rows, columns, this);
|
||||
}
|
||||
|
||||
private void initComponent() {
|
||||
//add table renderer
|
||||
myTableCellRenderer = new ArrayTableCellRenderer(Double.MIN_VALUE, Double.MIN_VALUE, myDtypeKind);
|
||||
myComponent.getColoredCheckbox().setEnabled(false);
|
||||
myComponent.getColoredCheckbox().addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
if (e.getSource() == myComponent.getColoredCheckbox()) {
|
||||
|
||||
//add color checkbox listener
|
||||
if (!isNumeric()) {
|
||||
disableColor();
|
||||
}
|
||||
else {
|
||||
myComponent.getColoredCheckbox().addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
if (e.getSource() == myComponent.getColoredCheckbox()) {
|
||||
|
||||
if (myTable.getRowCount() > 0 &&
|
||||
myTable.getColumnCount() > 0 &&
|
||||
myTable.getCellRenderer(0, 0) instanceof ArrayTableCellRenderer) {
|
||||
ArrayTableCellRenderer renderer = (ArrayTableCellRenderer)myTable.getCellRenderer(0, 0);
|
||||
if (myComponent.getColoredCheckbox().isSelected()) {
|
||||
renderer.setColored(true);
|
||||
}
|
||||
else {
|
||||
renderer.setColored(false);
|
||||
}
|
||||
if (myTable.getRowCount() > 0 &&
|
||||
myTable.getColumnCount() > 0 &&
|
||||
myTable.getCellRenderer(0, 0) instanceof ArrayTableCellRenderer) {
|
||||
ArrayTableCellRenderer renderer = (ArrayTableCellRenderer)myTable.getCellRenderer(0, 0);
|
||||
if (myComponent.getColoredCheckbox().isSelected()) {
|
||||
renderer.setColored(true);
|
||||
}
|
||||
else {
|
||||
renderer.setColored(false);
|
||||
}
|
||||
myComponent.getScrollPane().repaint();
|
||||
}
|
||||
myComponent.getScrollPane().repaint();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//make value name read-only
|
||||
myComponent.getSliceTextField().addFocusListener(new FocusListener() {
|
||||
@Override
|
||||
public void focusGained(FocusEvent e) {
|
||||
myComponent.getSliceTextField().getDocument().createGuardedBlock(0, getNodeName().length());
|
||||
myComponent.getSliceTextField().getDocument().createGuardedBlock(0, getNodeFullName().length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focusLost(FocusEvent e) {
|
||||
RangeMarker block = myComponent.getSliceTextField().getDocument().getRangeGuard(0, getNodeName().length());
|
||||
RangeMarker block = myComponent.getSliceTextField().getDocument().getRangeGuard(0, getNodeFullName().length());
|
||||
if (block != null) {
|
||||
myComponent.getSliceTextField().getDocument().removeGuardedBlock(block);
|
||||
}
|
||||
@@ -184,213 +167,130 @@ public class NumpyArrayTable {
|
||||
}
|
||||
|
||||
public void init() {
|
||||
Runnable returnToFillTable = new Runnable() {
|
||||
init(myValue.getName(), false);
|
||||
}
|
||||
|
||||
public void init(final String slice, final boolean inPlace) {
|
||||
initComponent();
|
||||
|
||||
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
|
||||
|
||||
final ExecutorService myExecutorService = Executors.newSingleThreadExecutor();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
init();
|
||||
}
|
||||
};
|
||||
final PyDebugValue value = getDebugValue();
|
||||
PyDebugValue parent = value.getParent();
|
||||
String sl = slice;
|
||||
|
||||
if (myDtypeKind == null) {
|
||||
fillType(returnToFillTable);
|
||||
return;
|
||||
}
|
||||
if (slice.contains(".")) {
|
||||
sl = slice.substring(slice.lastIndexOf(".") + 1);
|
||||
}
|
||||
|
||||
if (myShape == null) {
|
||||
fillShape(returnToFillTable);
|
||||
return;
|
||||
}
|
||||
final PyDebugValue slicedValue =
|
||||
new PyDebugValue(sl, value.getType(), value.getValue(), value.isContainer(), value.isErrorOnEval(),
|
||||
parent, value.getFrameAccessor());
|
||||
|
||||
if (myTableCellRenderer == null) {
|
||||
initComponent();
|
||||
}
|
||||
final String format = getFormat().isEmpty() ? "%" : getFormat();
|
||||
|
||||
if (isNumeric() && myTableCellRenderer.getMax() == Double.MIN_VALUE && myTableCellRenderer.getMin() == Double.MIN_VALUE) {
|
||||
fillColorRange(returnToFillTable);
|
||||
return;
|
||||
}
|
||||
final ListenableFutureTask<ArrayChunk> task = ListenableFutureTask.create(new Callable<ArrayChunk>() {
|
||||
@Override
|
||||
public ArrayChunk call() throws Exception {
|
||||
return value.getFrameAccessor()
|
||||
.getArrayItems(slicedValue, 0, 0, -1, -1, format);
|
||||
}
|
||||
});
|
||||
|
||||
UIUtil.invokeLaterIfNeeded(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myComponent.getSliceTextField().setText(getDefaultPresentation());
|
||||
myComponent.getFormatTextField().setText(getDefaultFormat());
|
||||
myDialog.setTitle(getTitlePresentation(getDefaultPresentation()));
|
||||
task.addListener(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (task.isDone()) {
|
||||
try {
|
||||
initUi(task.get(), inPlace);
|
||||
}
|
||||
catch (Exception e) {
|
||||
showError(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}, myExecutorService);
|
||||
|
||||
myExecutorService.execute(task);
|
||||
}
|
||||
});
|
||||
initTableModel(false);
|
||||
}
|
||||
|
||||
private void initUi(@NotNull final ArrayChunk chunk, final boolean inPlace) {
|
||||
if (chunk.containsMeta()) {
|
||||
myPagingModel = new AsyncArrayTableModel(Math.min(chunk.getRows(), ROWS_IN_DEFAULT_VIEW),
|
||||
Math.min(chunk.getColumns(), COLUMNS_IN_DEFAULT_VIEW), this);
|
||||
myPagingModel.addToCache(chunk);
|
||||
myDtypeKind = chunk.getType();
|
||||
|
||||
UIUtil.invokeLaterIfNeeded(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myTable.setModel(myPagingModel);
|
||||
myComponent.getSliceTextField().setText(chunk.getSlicePresentation());
|
||||
myComponent.getFormatTextField().setText(chunk.getFormat());
|
||||
myDialog.setTitle(getTitlePresentation(chunk.getSlicePresentation()));
|
||||
myTableCellRenderer = new ArrayTableCellRenderer(Double.MIN_VALUE, Double.MIN_VALUE, chunk.getType());
|
||||
fillColorRange(chunk.getMin(), chunk.getMax());
|
||||
if (!isNumeric()) {
|
||||
disableColor();
|
||||
}
|
||||
else {
|
||||
myComponent.getColoredCheckbox().setEnabled(true);
|
||||
}
|
||||
|
||||
if (!inPlace) {
|
||||
myComponent.getScrollPane().getViewport().setViewPosition(new Point(0, 0));
|
||||
JBTableWithRowHeaders.RowHeaderTable rowTable = ((JBTableWithRowHeaders)myTable).getRowHeaderTable();
|
||||
rowTable.setRowShift(0);
|
||||
}
|
||||
((AsyncArrayTableModel)myTable.getModel()).fireTableDataChanged();
|
||||
((AsyncArrayTableModel)myTable.getModel()).fireTableCellUpdated(0, 0);
|
||||
if (myTable.getColumnCount() > 0) {
|
||||
myTable.setDefaultRenderer(myTable.getColumnClass(0), myTableCellRenderer);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
showError("Bad metadata for array " + chunk.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private static String getTitlePresentation(String slice) {
|
||||
return "Array View: " + slice;
|
||||
}
|
||||
|
||||
private void fillColorRange(@NotNull final Runnable returnToMain) {
|
||||
Consumer<PyDebugValue> callback = new Consumer<PyDebugValue>() {
|
||||
@Override
|
||||
public void consume(@NotNull PyDebugValue result) {
|
||||
String rawValue = result.getValue();
|
||||
double min;
|
||||
double max;
|
||||
String minValue = rawValue.substring(1, rawValue.indexOf(","));
|
||||
String maxValue = rawValue.substring(rawValue.indexOf(", ") + 2, rawValue.length() - 1);
|
||||
if ("c".equals(myDtypeKind)) {
|
||||
min = 0;
|
||||
max = 1;
|
||||
myTableCellRenderer.setComplexMin(minValue);
|
||||
myTableCellRenderer.setComplexMax(maxValue);
|
||||
}
|
||||
else if ("b".equals(myDtypeKind)) {
|
||||
min = minValue.equals("True") ? 1 : 0;
|
||||
max = maxValue.equals("True") ? 1 : 0;
|
||||
}
|
||||
else {
|
||||
min = Double.parseDouble(minValue);
|
||||
max = Double.parseDouble(maxValue);
|
||||
}
|
||||
|
||||
myTableCellRenderer.setMin(min);
|
||||
myTableCellRenderer.setMax(max);
|
||||
returnToMain.run();
|
||||
}
|
||||
};
|
||||
|
||||
if (getMaxRow(myShape) * getMaxColumn(myShape) > HUGE_ARRAY_SIZE) {
|
||||
disableColor();
|
||||
returnToMain.run();
|
||||
private void fillColorRange(String minValue, String maxValue) {
|
||||
double min;
|
||||
double max;
|
||||
if ("c".equals(myDtypeKind)) {
|
||||
min = 0;
|
||||
max = 1;
|
||||
myTableCellRenderer.setComplexMin(minValue);
|
||||
myTableCellRenderer.setComplexMax(maxValue);
|
||||
}
|
||||
|
||||
String evalTypeCommand = "[" + getNodeName() + ".min(), " + getNodeName() + ".max()]";
|
||||
try {
|
||||
PyDebugValue value = getEvaluator().evaluate(evalTypeCommand, false, false);
|
||||
callback.consume(value);
|
||||
}
|
||||
catch (PyDebuggerException e) {
|
||||
showError(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public String getDefaultPresentation() {
|
||||
List<Pair<Integer, Integer>> defaultSlice = getDefaultSlice();
|
||||
String mySlicePresentation = getNodeName();
|
||||
for (int index = 0; index < defaultSlice.size() - 2; index++) {
|
||||
mySlicePresentation += "[" + defaultSlice.get(index).getFirst() + "]";
|
||||
}
|
||||
|
||||
// fill current slice
|
||||
final int columns = Math.min(getMaxColumn(myShape), COLUMNS_IN_DEFAULT_VIEW);
|
||||
int rows = Math.min(getMaxRow(myShape), ROWS_IN_DEFAULT_VIEW);
|
||||
if (rows == 1 && columns == 1) {
|
||||
return mySlicePresentation;
|
||||
}
|
||||
|
||||
if (rows == 1) {
|
||||
mySlicePresentation += "[0:" + columns + "]";
|
||||
}
|
||||
else if (columns == 1) {
|
||||
mySlicePresentation += "[0:" + rows + "]";
|
||||
else if ("b".equals(myDtypeKind)) {
|
||||
min = minValue.equals("True") ? 1 : 0;
|
||||
max = maxValue.equals("True") ? 1 : 0;
|
||||
}
|
||||
else {
|
||||
mySlicePresentation += "[0:" + rows + ", 0:" + columns + "]";
|
||||
min = Double.parseDouble(minValue);
|
||||
max = Double.parseDouble(maxValue);
|
||||
}
|
||||
|
||||
return mySlicePresentation;
|
||||
}
|
||||
|
||||
private List<Pair<Integer, Integer>> getDefaultSlice() {
|
||||
return getSlice(COLUMNS_IN_DEFAULT_SLICE, ROWS_IN_DEFAULT_SLICE);
|
||||
}
|
||||
|
||||
private List<Pair<Integer, Integer>> getSlice(int columns, int rows) {
|
||||
List<Pair<Integer, Integer>> slices = new ArrayList<Pair<Integer, Integer>>();
|
||||
for (int i = 0; i < myShape.length; i++) {
|
||||
Pair<Integer, Integer> slice = new Pair<Integer, Integer>(0, 0);
|
||||
if (i == myShape.length - 1) {
|
||||
slice = new Pair<Integer, Integer>(0, Math.min(myShape[i], columns));
|
||||
}
|
||||
else if (i == myShape.length - 2) {
|
||||
slice = new Pair<Integer, Integer>(0, Math.min(myShape[i], rows));
|
||||
}
|
||||
slices.add(slice);
|
||||
}
|
||||
return slices;
|
||||
myTableCellRenderer.setMin(min);
|
||||
myTableCellRenderer.setMax(max);
|
||||
}
|
||||
|
||||
public String getSliceText() {
|
||||
if (myComponent.getSliceTextField().getText().isEmpty()) {
|
||||
return getDefaultPresentation();
|
||||
}
|
||||
return myComponent.getSliceTextField().getText();
|
||||
}
|
||||
|
||||
private void fillType(@NotNull final Runnable returnToMain) {
|
||||
String evalTypeCommand = getNodeName() + ".dtype.kind";
|
||||
try {
|
||||
PyDebugValue value = getEvaluator().evaluate(evalTypeCommand, false, false);
|
||||
setDtypeKind(value.getValue());
|
||||
returnToMain.run();
|
||||
}
|
||||
catch (PyDebuggerException e) {
|
||||
showError(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void fillShape(@NotNull final Runnable returnToMain) {
|
||||
String evalShapeCommand = getEvalShapeCommand(getNodeName());
|
||||
try {
|
||||
PyDebugValue value = getEvaluator().evaluate(evalShapeCommand, false, false);
|
||||
setShape(parseShape(value.getValue()));
|
||||
returnToMain.run();
|
||||
}
|
||||
catch (Exception e) {
|
||||
showError(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private int[] parseShape(String value) throws InvalidAttributeValueException {
|
||||
int index = value.indexOf('#');
|
||||
if (index == -1) {
|
||||
LOG.error("Wrong shape format: " + value);
|
||||
return new int[]{0, 0};
|
||||
}
|
||||
String shape = value.substring(0, index);
|
||||
if (shape.equals("()")) {
|
||||
return new int[]{1, 1};
|
||||
}
|
||||
|
||||
String[] dimensions = shape.substring(1, shape.length() - 1).trim().split(",");
|
||||
if (dimensions.length > 1) {
|
||||
int[] result = new int[dimensions.length];
|
||||
for (int i = 0; i < dimensions.length; i++) {
|
||||
result[i] = Integer.parseInt(dimensions[i].trim());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else if (dimensions.length == 1) {
|
||||
|
||||
//special case with 1D arrays arr[i, :] - row,
|
||||
//but arr[:, i] - column with equal shape and ndim
|
||||
//http://stackoverflow.com/questions/16837946/numpy-a-2-rows-1-column-file-loadtxt-returns-1row-2-columns
|
||||
//explanation: http://stackoverflow.com/questions/15165170/how-do-i-maintain-row-column-orientation-of-vectors-in-numpy?rq=1
|
||||
//we use kind of a hack - use information about memory from C_CONTIGUOUS
|
||||
|
||||
boolean isRow = value.substring(value.indexOf("#") + 1).equals("True");
|
||||
int[] result = new int[2];
|
||||
if (isRow) {
|
||||
result[0] = 1;
|
||||
result[1] = Integer.parseInt(dimensions[0].trim());
|
||||
}
|
||||
else {
|
||||
result[1] = 1;
|
||||
result[0] = Integer.parseInt(dimensions[0].trim());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
throw new InvalidAttributeValueException("Invalid shape string for " + getNodeName() + ".");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isNumeric() {
|
||||
if (myDtypeKind != null) {
|
||||
return "biufc".contains(myDtypeKind.substring(0, 1));
|
||||
@@ -399,7 +299,7 @@ public class NumpyArrayTable {
|
||||
}
|
||||
|
||||
private void initTableModel(final boolean inPlace) {
|
||||
myPagingModel = createTableModel(myShape);
|
||||
myPagingModel = new AsyncArrayTableModel(myPagingModel.getRowCount(), myPagingModel.getColumnCount(), this);
|
||||
|
||||
UIUtil.invokeLaterIfNeeded(new Runnable() {
|
||||
@Override
|
||||
@@ -419,102 +319,6 @@ public class NumpyArrayTable {
|
||||
});
|
||||
}
|
||||
|
||||
private TableCellEditor getArrayTableCellEditor() {
|
||||
return new ArrayTableCellEditor(myProject) {
|
||||
|
||||
private String getCellSlice() {
|
||||
String expression = getSliceText();
|
||||
if (myTable.getRowCount() == 1) {
|
||||
expression += "[" + myTable.getSelectedColumn() + "]";
|
||||
}
|
||||
else if (myTable.getColumnCount() == 1) {
|
||||
expression += "[" + myTable.getSelectedRow() + "]";
|
||||
}
|
||||
else {
|
||||
expression += "[" + myTable.getSelectedRow() + "][" + myTable.getSelectedColumn() + "]";
|
||||
}
|
||||
if (myTable.getRowCount() == 1 && myTable.getColumnCount() == 1) {
|
||||
return getSliceText();
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
|
||||
private String changeValExpression() {
|
||||
if (getEditor().getEditor() == null) {
|
||||
throw new IllegalStateException("Null editor in table cell.");
|
||||
}
|
||||
|
||||
return getCellSlice() + " = " + getEditor().getEditor().getDocument().getText();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void cancelEditing() {
|
||||
super.cancelEditing();
|
||||
clearErrorMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doOKAction(final int row, final int col) {
|
||||
|
||||
if (getEditor().getEditor() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
myEvaluator.evaluate(changeValExpression(), new XDebuggerEvaluator.XEvaluationCallback() {
|
||||
@Override
|
||||
public void evaluated(@NotNull XValue result) {
|
||||
XDebuggerEvaluator.XEvaluationCallback callback = new XDebuggerEvaluator.XEvaluationCallback() {
|
||||
@Override
|
||||
public void evaluated(@NotNull XValue value) {
|
||||
|
||||
//todo: compute presentation and work with
|
||||
String text = ((PyDebugValue)value).getValue();
|
||||
final String corrected;
|
||||
if (!isNumeric()) {
|
||||
if (!text.startsWith("\\\'") && !text.startsWith("\\\"")) {
|
||||
corrected = "\'" + text + "\'";
|
||||
}
|
||||
else {
|
||||
corrected = text;
|
||||
}
|
||||
}
|
||||
else {
|
||||
corrected = text;
|
||||
disableColor();
|
||||
}
|
||||
|
||||
new WriteCommandAction(null) {
|
||||
protected void run(@NotNull Result result) throws Throwable {
|
||||
if (getEditor().getEditor() != null) {
|
||||
getEditor().getEditor().getDocument().setText(corrected);
|
||||
((AsyncArrayTableModel)myTable.getModel()).changeValue(row, col, corrected);
|
||||
cancelEditing();
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
setLastValue(corrected);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void errorOccurred(@NotNull String errorMessage) {
|
||||
showError(errorMessage);
|
||||
}
|
||||
};
|
||||
|
||||
myEvaluator.evaluate(getCellSlice(), callback, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void errorOccurred(@NotNull String errorMessage) {
|
||||
showError(errorMessage);
|
||||
}
|
||||
}, null);
|
||||
super.doOKAction(row, col);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public String correctStringValue(@NotNull Object value) {
|
||||
if (value instanceof String) {
|
||||
String corrected = (String)value;
|
||||
@@ -535,14 +339,6 @@ public class NumpyArrayTable {
|
||||
this.myDtypeKind = dtype;
|
||||
}
|
||||
|
||||
public int[] getShape() {
|
||||
return myShape;
|
||||
}
|
||||
|
||||
public void setShape(int[] shape) {
|
||||
this.myShape = shape;
|
||||
}
|
||||
|
||||
public void showError(String message) {
|
||||
myDialog.setError(message);
|
||||
}
|
||||
@@ -558,80 +354,15 @@ public class NumpyArrayTable {
|
||||
});
|
||||
}
|
||||
|
||||
public String getDefaultFormat() {
|
||||
if (isNumeric()) {
|
||||
if (myDtypeKind.equals("f")) {
|
||||
return "%.5f";
|
||||
}
|
||||
|
||||
if (myDtypeKind.equals("i") || myDtypeKind.equals("u")) {
|
||||
return "%d";
|
||||
}
|
||||
|
||||
if (myDtypeKind.equals("b") || myDtypeKind.equals("c")) {
|
||||
UIUtil.invokeLaterIfNeeded(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myComponent.getFormatTextField().getComponent().setEnabled(false);
|
||||
}
|
||||
});
|
||||
return "%s";
|
||||
}
|
||||
}
|
||||
return "%s";
|
||||
}
|
||||
|
||||
public String getFormat() {
|
||||
if (myComponent.getFormatTextField().getText().isEmpty()) {
|
||||
return getDefaultFormat();
|
||||
}
|
||||
return myComponent.getFormatTextField().getText();
|
||||
}
|
||||
|
||||
private void doReslice(final String newSlice, int[] shape) {
|
||||
if (shape == null) {
|
||||
String evalShapeCommand = getEvalShapeCommand(newSlice);
|
||||
try {
|
||||
PyDebugValue result = getEvaluator().evaluate(evalShapeCommand, false, false);
|
||||
shape = parseShape(((PyDebugValue)result).getValue());
|
||||
if (!is2DShape(shape)) {
|
||||
showError("Incorrect slice shape " + ((PyDebugValue)result).getValue() + ".");
|
||||
}
|
||||
doReslice(newSlice, shape);
|
||||
}
|
||||
catch (Exception e) {
|
||||
showError(e.getMessage());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
myShape = shape;
|
||||
reset();
|
||||
}
|
||||
|
||||
private static String getEvalShapeCommand(@NotNull String slice) {
|
||||
//add information about memory, see #parseShape comments
|
||||
return "repr(" + slice + ".shape)+'#'+repr(" + slice + ".flags['C_CONTIGUOUS'])";
|
||||
private void doReslice() {
|
||||
clearErrorMessage();
|
||||
init(getSliceText(), false);
|
||||
}
|
||||
|
||||
private void clearErrorMessage() {
|
||||
showError(null);
|
||||
}
|
||||
|
||||
private static boolean is2DShape(int[] shape) {
|
||||
if (shape.length < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < shape.length - 2; i++) {
|
||||
if (shape[i] != 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void doApplyFormat() {
|
||||
reset();
|
||||
}
|
||||
@@ -641,24 +372,6 @@ public class NumpyArrayTable {
|
||||
initTableModel(true);
|
||||
}
|
||||
|
||||
public String evalTypeFunc(String format) {
|
||||
return "\'" + format + "\' % l";
|
||||
}
|
||||
|
||||
public int getMaxRow(int[] shape) {
|
||||
if (shape != null && shape.length >= 2) {
|
||||
return shape[shape.length - 2];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int getMaxColumn(int[] shape) {
|
||||
if (shape != null && shape.length >= 2) {
|
||||
return shape[shape.length - 1];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void setBusy(final boolean busy) {
|
||||
UIUtil.invokeLaterIfNeeded(new Runnable() {
|
||||
@Override
|
||||
@@ -719,7 +432,18 @@ public class NumpyArrayTable {
|
||||
}
|
||||
}
|
||||
|
||||
public String getNodeName() {
|
||||
return (myValue).getName();
|
||||
public String getNodeFullName() {
|
||||
String fullName = getDebugValue().getName();
|
||||
PyDebugValue child = getDebugValue();
|
||||
while (child.getParent() != null) {
|
||||
child = child.getParent();
|
||||
fullName = child.getName() + "." + fullName;
|
||||
}
|
||||
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public String getFormat() {
|
||||
return myComponent.getFormatTextField().getText();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user