Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/monodevelop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'main/src/addins')
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/QuickInfo/DebuggerQuickInfoSource.cs7
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj28
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebugValueWindow.cs106
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ExceptionCaughtDialog.cs313
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/LocalsPad.cs36
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/DebuggerObjectValueNode.cs197
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Gtk/GtkObjectValueTreeView.cs2337
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/IDebuggerService.cs38
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/IEvaluatingGroupObjectValueNode.cs47
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/IObjectValueTreeView.cs157
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/IStackFrame.cs40
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ISupportChildObjectValueNodeReplacement.cs40
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueDebuggerService.cs70
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueNode.cs257
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueNodeEventArgs.cs92
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueStackFrame.cs102
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeView.cs (renamed from main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValueTreeView.cs)7
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeViewController.cs970
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeViewFakes.cs243
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/RootObjectValueNode.cs82
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ShowMoreValuesObjectValueNode.cs43
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValuePad.cs73
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/WatchPad.cs104
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/DebugValueTooltipProvider.cs13
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/PinnedWatchWidget.cs106
-rw-r--r--main/src/addins/MonoDevelop.TextEditor/MonoDevelop.TextEditor.Cocoa/MonoDevelop.TextEditor.Cocoa.csproj2
26 files changed, 5229 insertions, 281 deletions
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/QuickInfo/DebuggerQuickInfoSource.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/QuickInfo/DebuggerQuickInfoSource.cs
index e4d83b1a14..04e0429947 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/QuickInfo/DebuggerQuickInfoSource.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/QuickInfo/DebuggerQuickInfoSource.cs
@@ -38,7 +38,8 @@ namespace MonoDevelop.Debugger.VSTextView.QuickInfo
{
if (window == null)
return;
- var debuggerSession = window.Tree.Frame?.DebuggerSession;
+
+ var debuggerSession = window.GetDebuggerSession ();
if (debuggerSession == null || debuggerSession == sender) {
DestroyWindow ();
}
@@ -139,7 +140,7 @@ namespace MonoDevelop.Debugger.VSTextView.QuickInfo
var bounds = view.TextViewLines.GetCharacterBounds (point);
view.LayoutChanged += LayoutChanged;
#if CLOSE_ON_FOCUS_LOST
- view.LostAggregateFocus += View_LostAggregateFocus;
+ view.LostAggregateFocus += View_LostAggregateFocus;
#endif
RegisterForHiddenAsync (view).Ignore ();
window.LeaveNotifyEvent += LeaveNotifyEvent;
@@ -149,7 +150,7 @@ namespace MonoDevelop.Debugger.VSTextView.QuickInfo
cgPoint.Y = cocoaView.VisualElement.Superview.Frame.Height - cgPoint.Y;
window.ShowPopup (gtkParent, new Gdk.Rectangle ((int)cgPoint.X, (int)cgPoint.Y, (int)bounds.Width, (int)bounds.Height), Components.PopupPosition.TopLeft);
#else
- throw new NotImplementedException ();
+ throw new NotImplementedException ();
#endif
}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj
index effd5fb8e4..924762a652 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj
@@ -75,7 +75,6 @@
<ItemGroup>
<Compile Include="AssemblyInfo.cs" />
<Compile Include="MonoDevelop.Debugger\StackTracePad.cs" />
- <Compile Include="MonoDevelop.Debugger\ObjectValueTreeView.cs" />
<Compile Include="MonoDevelop.Debugger\WatchPad.cs" />
<Compile Include="MonoDevelop.Debugger\Initializer.cs" />
<Compile Include="MonoDevelop.Debugger\DisassemblyView.cs" />
@@ -175,6 +174,22 @@
<Compile Include="MonoDevelop.Debugger.VSTextView\BreakpointManager.cs" />
<Compile Include="MonoDevelop.Debugger\IBreakpointSpanResolver.cs" />
<Compile Include="MonoDevelop.Debugger\DefaultBreakpointSpanResolver.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\ObjectValueTreeView.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\ObjectValueTreeViewController.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\ObjectValueTreeViewFakes.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\ObjectValueNode.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\DebuggerObjectValueNode.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\RootObjectValueNode.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\IEvaluatingGroupObjectValueNode.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\ISupportChildObjectValueNodeReplacement.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\ShowMoreValuesObjectValueNode.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\IDebuggerService.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\IStackFrame.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\ObjectValueDebuggerService.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\ObjectValueStackFrame.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\ObjectValueNodeEventArgs.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\Gtk\GtkObjectValueTreeView.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\IObjectValueTreeView.cs" />
</ItemGroup>
<ItemGroup Condition="$(OS) != 'Windows_NT'">
<Compile Include="MonoDevelop.Debugger.VSTextView\ExceptionCaught\ExceptionCaughtProvider.cs" />
@@ -363,9 +378,20 @@
<Folder Include="MonoDevelop.Debugger.VSTextView\Tags\" />
<Folder Include="MonoDevelop.Debugger.VSTextView\QuickInfo\" />
<Folder Include="MonoDevelop.Debugger.VSTextView\ExceptionCaught\" />
+ <Folder Include="MonoDevelop.Debugger\ObjectValue\" />
+ <Folder Include="MonoDevelop.Debugger\ObjectValue\Gtk\" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="MonoDevelop.SourceEditor" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <ProjectExtensions>
+ <MonoDevelop>
+ <Properties>
+ <Policies>
+ <DotNetNamingPolicy DirectoryNamespaceAssociation="None" ResourceNamePolicy="MSBuild" />
+ </Policies>
+ </Properties>
+ </MonoDevelop>
+ </ProjectExtensions>
</Project>
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebugValueWindow.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebugValueWindow.cs
index 6303475bb1..48ecc3bbfe 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebugValueWindow.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebugValueWindow.cs
@@ -36,10 +36,13 @@ namespace MonoDevelop.Debugger
{
class DebugValueWindow : PopoverWindow
{
- public ObjectValueTreeView Tree { get; }
- ScrolledWindow sw;
+ readonly ObjectValueTreeViewController controller;
+ readonly ObjectValueTreeView objValueTreeView;
+ readonly TreeView treeView;
+ readonly ScrolledWindow sw;
static readonly string innerTreeName = "MonoDevelop.SourceEditor.DebugValueWindow.ObjectValueTreeView";
+ static bool UseNewTreeView = true;
static string currentBgColor;
static DebugValueWindow ()
@@ -73,10 +76,10 @@ namespace MonoDevelop.Debugger
public DebugValueWindow (Gtk.Window transientFor, string pinnedWatchFileName, int pinnedWatchLine, StackFrame frame, ObjectValue value, PinnedWatch watch) : base (Gtk.WindowType.Toplevel)
{
- this.TypeHint = WindowTypeHint.PopupMenu;
- this.AllowShrink = false;
- this.AllowGrow = false;
- this.Decorated = false;
+ TypeHint = WindowTypeHint.PopupMenu;
+ AllowShrink = false;
+ AllowGrow = false;
+ Decorated = false;
TransientFor = transientFor;
// Avoid getting the focus when the window is shown. We'll get it when the mouse enters the window
@@ -87,38 +90,67 @@ namespace MonoDevelop.Debugger
sw.VscrollbarPolicy = PolicyType.Never;
UpdateTreeStyle (Theme.BackgroundColor);
- Tree = new ObjectValueTreeView ();
- Tree.Name = innerTreeName;
- sw.Add (Tree);
- ContentBox.Add (sw);
+ if (UseNewTreeView) {
+ controller = new ObjectValueTreeViewController ();
+ controller.SetStackFrame (frame);
+ controller.AllowEditing = true;
- Tree.Frame = frame;
- Tree.CompactView = true;
- Tree.AllowAdding = false;
- Tree.AllowEditing = true;
- Tree.HeadersVisible = false;
- Tree.AllowPinning = true;
- Tree.RootPinAlwaysVisible = true;
- Tree.PinnedWatch = watch;
- Tree.PinnedWatchLine = pinnedWatchLine;
- Tree.PinnedWatchFile = pinnedWatchFileName;
-
- Tree.AddValue (value);
- Tree.Selection.UnselectAll ();
- Tree.SizeAllocated += OnTreeSizeChanged;
- Tree.PinStatusChanged += OnPinStatusChanged;
+ treeView = (TreeView) controller.GetControl (headersVisible: false, allowPinning: true, compactView: true, rootPinVisible: true);
- sw.ShowAll ();
+ controller.PinnedWatch = watch;
+ controller.PinnedWatchLine = pinnedWatchLine;
+ controller.PinnedWatchFile = pinnedWatchFileName;
- Tree.StartEditing += OnStartEditing;
- Tree.EndEditing += OnEndEditing;
+ if (treeView is IObjectValueTreeView ovtv) {
+ ovtv.StartEditing += OnStartEditing;
+ ovtv.EndEditing += OnEndEditing;
+ ovtv.NodePinned += OnPinStatusChanged;
+ }
+
+ controller.AddValue (value);
+ } else {
+ objValueTreeView = new ObjectValueTreeView ();
+ objValueTreeView.RootPinAlwaysVisible = true;
+ objValueTreeView.HeadersVisible = false;
+ objValueTreeView.AllowEditing = true;
+ objValueTreeView.AllowPinning = true;
+ objValueTreeView.CompactView = true;
+ objValueTreeView.PinnedWatch = watch;
+ objValueTreeView.PinnedWatchLine = pinnedWatchLine;
+ objValueTreeView.PinnedWatchFile = pinnedWatchFileName;
+ objValueTreeView.Frame = frame;
+
+ objValueTreeView.AddValue (value);
+
+ objValueTreeView.PinStatusChanged += OnPinStatusChanged;
+ objValueTreeView.StartEditing += OnStartEditing;
+ objValueTreeView.EndEditing += OnEndEditing;
+
+ treeView = objValueTreeView;
+ }
+
+ treeView.Name = innerTreeName;
+ treeView.Selection.UnselectAll ();
+ treeView.SizeAllocated += OnTreeSizeChanged;
+
+ sw.Add (treeView);
+ ContentBox.Add (sw);
+ sw.ShowAll ();
ShowArrow = true;
Theme.CornerRadius = 3;
PreviewWindowManager.WindowClosed += PreviewWindowManager_WindowClosed;
}
+ public DebuggerSession GetDebuggerSession ()
+ {
+ if (UseNewTreeView)
+ return controller.GetStackFrame ()?.DebuggerSession;
+
+ return objValueTreeView.Frame?.DebuggerSession;
+ }
+
void OnStartEditing (object sender, EventArgs args)
{
Modal = true;
@@ -136,10 +168,20 @@ namespace MonoDevelop.Debugger
protected override void OnDestroyed ()
{
- Tree.StartEditing -= OnStartEditing;
- Tree.EndEditing -= OnEndEditing;
- Tree.PinStatusChanged -= OnPinStatusChanged;
- Tree.SizeAllocated -= OnTreeSizeChanged;
+ if (UseNewTreeView) {
+ if (treeView is IObjectValueTreeView ovtv) {
+ ovtv.StartEditing -= OnStartEditing;
+ ovtv.EndEditing -= OnEndEditing;
+ ovtv.NodePinned -= OnPinStatusChanged;
+ }
+ } else {
+ objValueTreeView.PinStatusChanged -= OnPinStatusChanged;
+ objValueTreeView.StartEditing -= OnStartEditing;
+ objValueTreeView.EndEditing -= OnEndEditing;
+ }
+
+ treeView.SizeAllocated -= OnTreeSizeChanged;
+
PreviewWindowManager.WindowClosed -= PreviewWindowManager_WindowClosed;
base.OnDestroyed ();
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ExceptionCaughtDialog.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ExceptionCaughtDialog.cs
index 5a2fe3c146..e575a93232 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ExceptionCaughtDialog.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ExceptionCaughtDialog.cs
@@ -28,6 +28,7 @@
using System;
using System.IO;
using System.Linq;
+using System.Collections.Generic;
using Gtk;
@@ -37,43 +38,34 @@ using MonoDevelop.Ide;
using MonoDevelop.Core;
using MonoDevelop.Components;
using MonoDevelop.Ide.TextEditing;
-using MonoDevelop.Ide.Gui.Content;
using MonoDevelop.Ide.Editor.Extension;
-using MonoDevelop.Ide.Fonts;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Text;
namespace MonoDevelop.Debugger
{
class ExceptionCaughtDialog : Gtk.Window
{
- VBox VBox;
static readonly Xwt.Drawing.Image WarningIconPixbuf = Xwt.Drawing.Image.FromResource ("toolbar-icon.png");
static readonly Xwt.Drawing.Image WarningIconPixbufInner = Xwt.Drawing.Image.FromResource ("exception-outline-16.png");
+ static bool UseNewTreeView = true;
- protected ObjectValueTreeView ExceptionValueTreeView { get; private set; }
-
- protected TreeView StackTraceTreeView { get; private set; }
-
- protected CheckButton OnlyShowMyCodeCheckbox { get; private set; }
-
- protected Label ExceptionMessageLabel { get; private set; }
-
- protected Button ExceptionHelpLinkButton { get; private set; }
-
- protected Label ExceptionTypeLabel { get; private set; }
-
+ readonly Dictionary<ExceptionInfo, ExceptionInfo> reverseInnerExceptions = new Dictionary<ExceptionInfo, ExceptionInfo> ();
readonly ExceptionCaughtMessage message;
readonly ExceptionInfo exception;
+
+ Label exceptionMessageLabel, exceptionTypeLabel, innerExceptionTypeLabel, innerExceptionMessageLabel;
+ VBox vboxAroundInnerExceptionMessage, rightVBox, container;
+ Button close, helpLinkButton, innerExceptionHelpLinkButton;
+ TreeView exceptionValueTreeView, stackTraceTreeView;
+ Expander expanderProperties, expanderStacktrace;
+ InnerExceptionsTree innerExceptionsTreeView;
+ ObjectValueTreeViewController controller;
+ CheckButton onlyShowMyCodeCheckbox;
+ bool destroyed, hadInnerException;
+ TreeStore innerExceptionsStore;
+ string innerExceptionHelpLink;
+ string exceptionHelpLink;
ExceptionInfo selected;
- bool destroyed;
VPanedThin paned;
- Expander expanderProperties;
- Expander expanderStacktrace;
- Button close;
- VBox rightVBox;
- VBox vboxAroundInnerExceptionMessage;
protected enum ModelColumn
{
@@ -85,8 +77,8 @@ namespace MonoDevelop.Debugger
public ExceptionCaughtDialog (ExceptionInfo ex, ExceptionCaughtMessage msg)
: base (WindowType.Toplevel)
{
- this.Child = VBox = new VBox ();
- VBox.Show ();
+ Child = container = new VBox ();
+ container.Show ();
this.Name = "wizard_dialog";
this.ApplyTheme ();
selected = exception = ex;
@@ -103,10 +95,10 @@ namespace MonoDevelop.Debugger
var icon = new ImageView (WarningIconPixbuf);
icon.Yalign = 0;
- ExceptionTypeLabel = new Label { Xalign = 0.0f, Selectable = true, CanFocus = false };
- ExceptionMessageLabel = new Label { Wrap = true, Xalign = 0.0f, Selectable = true, CanFocus = false };
- ExceptionHelpLinkButton = new Button { HasFocus = true, Xalign = 0, Relief = ReliefStyle.None, BorderWidth = 0 };
- ExceptionHelpLinkButton.Name = "exception_help_link_label";
+ exceptionTypeLabel = new Label { Xalign = 0.0f, Selectable = true, CanFocus = false };
+ exceptionMessageLabel = new Label { Wrap = true, Xalign = 0.0f, Selectable = true, CanFocus = false };
+ helpLinkButton = new Button { HasFocus = true, Xalign = 0, Relief = ReliefStyle.None, BorderWidth = 0 };
+ helpLinkButton.Name = "exception_help_link_label";
Gtk.Rc.ParseString (@"style ""exception-help-link-label""
{
GtkWidget::link-color = ""#ffffff""
@@ -117,23 +109,23 @@ widget ""*.exception_help_link_label"" style ""exception-help-link-label""
var textColor = Styles.ExceptionCaughtDialog.HeaderTextColor.ToGdkColor ();
var headerColor = Styles.ExceptionCaughtDialog.HeaderBackgroundColor.ToGdkColor ();
- ExceptionHelpLinkButton.ModifyBg (StateType.Selected, headerColor);
+ helpLinkButton.ModifyBg (StateType.Selected, headerColor);
- ExceptionHelpLinkButton.Clicked += ExceptionHelpLinkLabel_Clicked;
- ExceptionHelpLinkButton.KeyPressEvent += EventBoxLink_KeyPressEvent;
+ helpLinkButton.Clicked += ExceptionHelpLinkLabel_Clicked;
+ helpLinkButton.KeyPressEvent += EventBoxLink_KeyPressEvent;
- ExceptionTypeLabel.ModifyFg (StateType.Normal, textColor);
- ExceptionMessageLabel.ModifyFg (StateType.Normal, textColor);
- ExceptionHelpLinkButton.ModifyFg (StateType.Normal, textColor);
+ exceptionTypeLabel.ModifyFg (StateType.Normal, textColor);
+ exceptionMessageLabel.ModifyFg (StateType.Normal, textColor);
+ helpLinkButton.ModifyFg (StateType.Normal, textColor);
if (Platform.IsWindows) {
- ExceptionTypeLabel.ModifyFont (Pango.FontDescription.FromString ("bold 19"));
- ExceptionMessageLabel.ModifyFont (Pango.FontDescription.FromString ("10"));
- ExceptionHelpLinkButton.ModifyFont (Pango.FontDescription.FromString ("10"));
+ exceptionTypeLabel.ModifyFont (Pango.FontDescription.FromString ("bold 19"));
+ exceptionMessageLabel.ModifyFont (Pango.FontDescription.FromString ("10"));
+ helpLinkButton.ModifyFont (Pango.FontDescription.FromString ("10"));
} else {
- ExceptionTypeLabel.ModifyFont (Pango.FontDescription.FromString ("21"));
- ExceptionMessageLabel.ModifyFont (Pango.FontDescription.FromString ("12"));
- ExceptionHelpLinkButton.ModifyFont (Pango.FontDescription.FromString ("12"));
+ exceptionTypeLabel.ModifyFont (Pango.FontDescription.FromString ("21"));
+ exceptionMessageLabel.ModifyFont (Pango.FontDescription.FromString ("12"));
+ helpLinkButton.ModifyFont (Pango.FontDescription.FromString ("12"));
}
//Force rendering of background with EventBox
@@ -143,13 +135,13 @@ widget ""*.exception_help_link_label"" style ""exception-help-link-label""
rightVBox = new VBox ();
leftVBox.PackStart (icon, false, false, (uint)(Platform.IsWindows ? 5 : 0)); // as we change frame.BorderWidth below, we need to compensate
- rightVBox.PackStart (ExceptionTypeLabel, false, false, (uint)(Platform.IsWindows ? 0 : 2));
+ rightVBox.PackStart (exceptionTypeLabel, false, false, (uint)(Platform.IsWindows ? 0 : 2));
var exceptionHContainer = new HBox ();
- exceptionHContainer.PackStart (ExceptionHelpLinkButton, false, false, 0);
+ exceptionHContainer.PackStart (helpLinkButton, false, false, 0);
exceptionHContainer.PackStart (new Fixed (), true, true, 0);
- rightVBox.PackStart (ExceptionMessageLabel, true, true, (uint)(Platform.IsWindows ? 6 : 5));
+ rightVBox.PackStart (exceptionMessageLabel, true, true, (uint)(Platform.IsWindows ? 6 : 5));
rightVBox.PackStart (exceptionHContainer, false, false, 2);
hBox.PackStart (leftVBox, false, false, (uint)(Platform.IsWindows ? 5 : 0)); // as we change frame.BorderWidth below, we need to compensate
@@ -181,28 +173,35 @@ widget ""*.exception_help_link_label"" style ""exception-help-link-label""
protected override void OnSizeAllocated (Gdk.Rectangle allocation)
{
base.OnSizeAllocated (allocation);
- ExceptionMessageLabel.WidthRequest = rightVBox.Allocation.Width;
+ exceptionMessageLabel.WidthRequest = rightVBox.Allocation.Width;
if (vboxAroundInnerExceptionMessage != null) {
- InnerExceptionMessageLabel.WidthRequest = vboxAroundInnerExceptionMessage.Allocation.Width;
+ innerExceptionMessageLabel.WidthRequest = vboxAroundInnerExceptionMessage.Allocation.Width;
}
}
Widget CreateExceptionValueTreeView ()
{
- ExceptionValueTreeView = new ObjectValueTreeView ();
- ExceptionValueTreeView.Frame = DebuggingService.CurrentFrame;
- ExceptionValueTreeView.ModifyBase (StateType.Normal, Styles.ExceptionCaughtDialog.ValueTreeBackgroundColor.ToGdkColor ());
- ExceptionValueTreeView.AllowPopupMenu = false;
- ExceptionValueTreeView.AllowExpanding = true;
- ExceptionValueTreeView.AllowPinning = false;
- ExceptionValueTreeView.AllowEditing = false;
- ExceptionValueTreeView.CanFocus = true;
- ExceptionValueTreeView.AllowAdding = false;
- ExceptionValueTreeView.RulesHint = true;
- ExceptionValueTreeView.ModifyFont (Pango.FontDescription.FromString (Platform.IsWindows ? "9" : "11"));
- ExceptionValueTreeView.RulesHint = false;
-
- ExceptionValueTreeView.Show ();
+ if (UseNewTreeView) {
+ controller = new ObjectValueTreeViewController ();
+ controller.SetStackFrame (DebuggingService.CurrentFrame);
+ controller.AllowExpanding = true;
+
+ exceptionValueTreeView = (TreeView) controller.GetControl (allowPopupMenu: false);
+ } else {
+ var objValueTreeView = new ObjectValueTreeView ();
+ objValueTreeView.Frame = DebuggingService.CurrentFrame;
+ objValueTreeView.AllowPopupMenu = false;
+ objValueTreeView.AllowExpanding = true;
+ objValueTreeView.AllowPinning = false;
+ objValueTreeView.AllowEditing = false;
+ objValueTreeView.AllowAdding = false;
+ }
+
+ exceptionValueTreeView.ModifyBase (StateType.Normal, Styles.ExceptionCaughtDialog.ValueTreeBackgroundColor.ToGdkColor ());
+ exceptionValueTreeView.ModifyFont (Pango.FontDescription.FromString (Platform.IsWindows ? "9" : "11"));
+ exceptionValueTreeView.RulesHint = false;
+ exceptionValueTreeView.CanFocus = true;
+ exceptionValueTreeView.Show ();
var scrolled = new ScrolledWindow {
HeightRequest = 180,
@@ -212,13 +211,15 @@ widget ""*.exception_help_link_label"" style ""exception-help-link-label""
};
scrolled.ShadowType = ShadowType.None;
- scrolled.Add (ExceptionValueTreeView);
+ scrolled.Add (exceptionValueTreeView);
scrolled.Show ();
+
var vbox = new VBox ();
expanderProperties = WrapInExpander (GettextCatalog.GetString ("Properties"), scrolled);
vbox.PackStart (new VBox (), false, false, 5);
vbox.PackStart (expanderProperties, true, true, 0);
vbox.ShowAll ();
+
return vbox;
}
@@ -285,19 +286,19 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander""
Widget CreateStackTraceTreeView ()
{
var store = new ListStore (typeof (ExceptionStackFrame), typeof (string), typeof (bool));
- StackTraceTreeView = new TreeView (store);
- StackTraceTreeView.SearchColumn = -1; // disable the interactive search
- StackTraceTreeView.FixedHeightMode = false;
- StackTraceTreeView.HeadersVisible = false;
- StackTraceTreeView.ShowExpanders = false;
- StackTraceTreeView.RulesHint = false;
- StackTraceTreeView.Show ();
+ stackTraceTreeView = new TreeView (store);
+ stackTraceTreeView.SearchColumn = -1; // disable the interactive search
+ stackTraceTreeView.FixedHeightMode = false;
+ stackTraceTreeView.HeadersVisible = false;
+ stackTraceTreeView.ShowExpanders = false;
+ stackTraceTreeView.RulesHint = false;
+ stackTraceTreeView.Show ();
- var renderer = new StackFrameCellRenderer (StackTraceTreeView.PangoContext);
+ var renderer = new StackFrameCellRenderer (stackTraceTreeView.PangoContext);
- StackTraceTreeView.AppendColumn ("", renderer, (CellLayoutDataFunc)StackFrameLayout);
+ stackTraceTreeView.AppendColumn ("", renderer, (CellLayoutDataFunc)StackFrameLayout);
- StackTraceTreeView.RowActivated += StackFrameActivated;
+ stackTraceTreeView.RowActivated += StackFrameActivated;
var scrolled = new ScrolledWindow {
HeightRequest = 180,
@@ -305,7 +306,7 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander""
VscrollbarPolicy = PolicyType.Automatic
};
scrolled.ShadowType = ShadowType.None;
- scrolled.Add (StackTraceTreeView);
+ scrolled.Add (stackTraceTreeView);
scrolled.Show ();
var vbox = new VBox ();
vbox.PackStart (CreateSeparator (), false, true, 0);
@@ -354,8 +355,6 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander""
return exception.InnerException != null;
}
- bool hadInnerException;
-
void Build ()
{
Title = GettextCatalog.GetString ("Exception Caught");
@@ -363,8 +362,8 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander""
DefaultHeight = 500;
HeightRequest = 350;
WidthRequest = 350;
- VBox.Foreach (VBox.Remove);
- VBox.PackStart (CreateExceptionHeader (), false, true, 0);
+ container.Foreach (container.Remove);
+ container.PackStart (CreateExceptionHeader (), false, true, 0);
paned = new VPanedThin ();
paned.GrabAreaSize = 10;
paned.Pack1 (CreateStackTraceTreeView (), true, false);
@@ -389,22 +388,22 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander""
box.PackStart (CreateInnerExceptionsTree (), false, false, 0);
box.PackStart (whiteBackground, true, true, 0);
box.Show ();
- VBox.PackStart (box, true, true, 0);
+ vbox.PackStart (box, true, true, 0);
DefaultWidth = 900;
DefaultHeight = 700;
WidthRequest = 550;
HeightRequest = 450;
} else {
- VBox.PackStart (whiteBackground, true, true, 0);
+ vbox.PackStart (whiteBackground, true, true, 0);
}
var actionArea = new HBox (false, 0) { BorderWidth = 14 };
- OnlyShowMyCodeCheckbox = new CheckButton (GettextCatalog.GetString ("_Only show my code."));
- OnlyShowMyCodeCheckbox.Toggled += OnlyShowMyCodeToggled;
- OnlyShowMyCodeCheckbox.Show ();
- OnlyShowMyCodeCheckbox.Active = DebuggingService.GetUserOptions ().ProjectAssembliesOnly;
+ onlyShowMyCodeCheckbox = new CheckButton (GettextCatalog.GetString ("_Only show my code."));
+ onlyShowMyCodeCheckbox.Toggled += OnlyShowMyCodeToggled;
+ onlyShowMyCodeCheckbox.Show ();
+ onlyShowMyCodeCheckbox.Active = DebuggingService.GetUserOptions ().ProjectAssembliesOnly;
- var alignment = new Alignment (0.0f, 0.5f, 0.0f, 0.0f) { Child = OnlyShowMyCodeCheckbox };
+ var alignment = new Alignment (0.0f, 0.5f, 0.0f, 0.0f) { Child = onlyShowMyCodeCheckbox };
alignment.Show ();
actionArea.PackStart (alignment, true, true, 0);
@@ -412,13 +411,9 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander""
actionArea.PackStart (new VBox (), false, true, 3); // dummy just to take extra 6px at end to make it 20pixels
actionArea.ShowAll ();
- VBox.PackStart (actionArea, false, true, 0);
+ vbox.PackStart (actionArea, false, true, 0);
}
- Label InnerExceptionTypeLabel;
- Label InnerExceptionMessageLabel;
- Button InnerExceptionHelpLinkButton;
-
Widget CreateInnerExceptionMessage ()
{
var hboxMain = new HBox ();
@@ -429,38 +424,38 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander""
icon.Yalign = 0;
hbox.PackStart (icon, false, false, 0);
- InnerExceptionTypeLabel = new Label ();
- InnerExceptionTypeLabel.UseMarkup = true;
- InnerExceptionTypeLabel.Xalign = 0;
- InnerExceptionTypeLabel.Selectable = true;
- InnerExceptionTypeLabel.CanFocus = false;
- hbox.PackStart (InnerExceptionTypeLabel, false, true, 4);
-
- InnerExceptionMessageLabel = new Label ();
- InnerExceptionMessageLabel.Wrap = true;
- InnerExceptionMessageLabel.Selectable = true;
- InnerExceptionMessageLabel.CanFocus = false;
- InnerExceptionMessageLabel.Xalign = 0;
- InnerExceptionMessageLabel.ModifyFont (Pango.FontDescription.FromString (Platform.IsWindows ? "9" : "11"));
-
- InnerExceptionHelpLinkButton = new Button {
+ innerExceptionTypeLabel = new Label ();
+ innerExceptionTypeLabel.UseMarkup = true;
+ innerExceptionTypeLabel.Xalign = 0;
+ innerExceptionTypeLabel.Selectable = true;
+ innerExceptionTypeLabel.CanFocus = false;
+ hbox.PackStart (innerExceptionTypeLabel, false, true, 4);
+
+ innerExceptionMessageLabel = new Label ();
+ innerExceptionMessageLabel.Wrap = true;
+ innerExceptionMessageLabel.Selectable = true;
+ innerExceptionMessageLabel.CanFocus = false;
+ innerExceptionMessageLabel.Xalign = 0;
+ innerExceptionMessageLabel.ModifyFont (Pango.FontDescription.FromString (Platform.IsWindows ? "9" : "11"));
+
+ innerExceptionHelpLinkButton = new Button {
CanFocus = true,
BorderWidth = 0,
Relief = ReliefStyle.Half,
Xalign = 0
};
- InnerExceptionHelpLinkButton.ModifyFont (Pango.FontDescription.FromString (Platform.IsWindows ? "9" : "11"));
- InnerExceptionHelpLinkButton.KeyPressEvent += InnerExceptionHelpLinkLabel_KeyPressEvent;
- InnerExceptionHelpLinkButton.Clicked += InnerExceptionHelpLinkLabel_Pressed;
+ innerExceptionHelpLinkButton.ModifyFont (Pango.FontDescription.FromString (Platform.IsWindows ? "9" : "11"));
+ innerExceptionHelpLinkButton.KeyPressEvent += InnerExceptionHelpLinkLabel_KeyPressEvent;
+ innerExceptionHelpLinkButton.Clicked += InnerExceptionHelpLinkLabel_Pressed;
- InnerExceptionHelpLinkButton.ModifyBg (StateType.Selected, Styles.ExceptionCaughtDialog.TreeSelectedBackgroundColor.ToGdkColor ());
+ innerExceptionHelpLinkButton.ModifyBg (StateType.Selected, Styles.ExceptionCaughtDialog.TreeSelectedBackgroundColor.ToGdkColor ());
vboxAroundInnerExceptionMessage.PackStart (hbox, false, true, 0);
- vboxAroundInnerExceptionMessage.PackStart (InnerExceptionMessageLabel, true, true, 10);
+ vboxAroundInnerExceptionMessage.PackStart (innerExceptionMessageLabel, true, true, 10);
var innerExceptionHContainer = new HBox ();
- innerExceptionHContainer.PackStart (InnerExceptionHelpLinkButton, false, false, 0);
+ innerExceptionHContainer.PackStart (innerExceptionHelpLinkButton, false, false, 0);
innerExceptionHContainer.PackStart (new Fixed (), true, true, 0);
vboxAroundInnerExceptionMessage.PackStart (innerExceptionHContainer, true, true, 2);
@@ -476,8 +471,6 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander""
IdeServices.DesktopService.ShowUrl (innerExceptionHelpLink);
}
- TreeStore InnerExceptionsStore;
-
class InnerExceptionsTree : TreeView
{
public InnerExceptionsTree ()
@@ -496,31 +489,27 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander""
}
}
- InnerExceptionsTree InnerExceptionsTreeView;
-
- Dictionary<ExceptionInfo, ExceptionInfo> ReverseInnerExceptions = new Dictionary<ExceptionInfo, ExceptionInfo> ();
-
Widget CreateInnerExceptionsTree ()
{
- InnerExceptionsTreeView = new InnerExceptionsTree ();
- InnerExceptionsTreeView.ModifyBase (StateType.Normal, Styles.ExceptionCaughtDialog.TreeBackgroundColor.ToGdkColor ()); // background
- InnerExceptionsTreeView.ModifyBase (StateType.Selected, Styles.ExceptionCaughtDialog.TreeSelectedBackgroundColor.ToGdkColor ()); // selected
- InnerExceptionsTreeView.HeadersVisible = false;
- InnerExceptionsStore = new TreeStore (typeof (ExceptionInfo));
-
- FillInnerExceptionsStore (InnerExceptionsStore, exception);
- InnerExceptionsTreeView.AppendColumn ("Exception", new CellRendererInnerException (), new TreeCellDataFunc ((tree_column, cell, tree_model, iter) => {
+ innerExceptionsTreeView = new InnerExceptionsTree ();
+ innerExceptionsTreeView.ModifyBase (StateType.Normal, Styles.ExceptionCaughtDialog.TreeBackgroundColor.ToGdkColor ()); // background
+ innerExceptionsTreeView.ModifyBase (StateType.Selected, Styles.ExceptionCaughtDialog.TreeSelectedBackgroundColor.ToGdkColor ()); // selected
+ innerExceptionsTreeView.HeadersVisible = false;
+ innerExceptionsStore = new TreeStore (typeof (ExceptionInfo));
+
+ FillInnerExceptionsStore (innerExceptionsStore, exception);
+ innerExceptionsTreeView.AppendColumn ("Exception", new CellRendererInnerException (), new TreeCellDataFunc ((tree_column, cell, tree_model, iter) => {
var c = (CellRendererInnerException)cell;
c.Text = ((ExceptionInfo)tree_model.GetValue (iter, 0)).Type;
}));
- InnerExceptionsTreeView.ShowExpanders = false;
- InnerExceptionsTreeView.LevelIndentation = 10;
- InnerExceptionsTreeView.Model = InnerExceptionsStore;
- InnerExceptionsTreeView.ExpandAll ();
- InnerExceptionsTreeView.Selection.Changed += (sender, e) => {
+ innerExceptionsTreeView.ShowExpanders = false;
+ innerExceptionsTreeView.LevelIndentation = 10;
+ innerExceptionsTreeView.Model = innerExceptionsStore;
+ innerExceptionsTreeView.ExpandAll ();
+ innerExceptionsTreeView.Selection.Changed += (sender, e) => {
TreeIter selectedIter;
- if (InnerExceptionsTreeView.Selection.GetSelected (out selectedIter)) {
- UpdateSelectedException ((ExceptionInfo)InnerExceptionsTreeView.Model.GetValue (selectedIter, 0));
+ if (innerExceptionsTreeView.Selection.GetSelected (out selectedIter)) {
+ UpdateSelectedException ((ExceptionInfo)innerExceptionsTreeView.Model.GetValue (selectedIter, 0));
}
};
var eventBox = new EventBox ();
@@ -528,7 +517,7 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander""
var vbox = new VBox ();
var scroll = new ScrolledWindow ();
scroll.WidthRequest = 200;
- scroll.Child = InnerExceptionsTreeView;
+ scroll.Child = innerExceptionsTreeView;
vbox.PackStart (scroll, true, true, 12);
eventBox.Add (vbox);
eventBox.ShowAll ();
@@ -540,13 +529,13 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander""
TreeIter iter;
if (parentIter.Equals (TreeIter.Zero)) {
iter = store.AppendValues (exception);
- ReverseInnerExceptions [exception] = null;
+ reverseInnerExceptions [exception] = null;
} else {
- ReverseInnerExceptions [exception] = (ExceptionInfo)store.GetValue (parentIter, 0);
+ reverseInnerExceptions [exception] = (ExceptionInfo)store.GetValue (parentIter, 0);
iter = store.AppendValues (parentIter, exception);
}
var updateInnerExceptions = new System.Action (() => {
- if (!InnerExceptionsStore.IterHasChild (iter)) {
+ if (!innerExceptionsStore.IterHasChild (iter)) {
var innerExceptions = exception.InnerExceptions;
if (innerExceptions != null && innerExceptions.Count > 0) {
foreach (var inner in innerExceptions) {
@@ -561,9 +550,9 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander""
});
exception.Changed += delegate {
Application.Invoke ((o, args) => {
- InnerExceptionsStore.EmitRowChanged (InnerExceptionsStore.GetPath (iter), iter);
+ innerExceptionsStore.EmitRowChanged (innerExceptionsStore.GetPath (iter), iter);
updateInnerExceptions ();
- InnerExceptionsTreeView.ExpandRow (InnerExceptionsStore.GetPath (iter), true);
+ innerExceptionsTreeView.ExpandRow (innerExceptionsStore.GetPath (iter), true);
});
};
updateInnerExceptions ();
@@ -571,7 +560,7 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander""
async void StackFrameActivated (object o, RowActivatedArgs args)
{
- var model = StackTraceTreeView.Model;
+ var model = stackTraceTreeView.Model;
TreeIter iter;
if (!model.GetIter (out iter, args.Path))
@@ -598,7 +587,7 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander""
void UpdateSelectedException (ExceptionInfo ex)
{
selected = ex;
- var model = (ListStore)StackTraceTreeView.Model;
+ var model = (ListStore)stackTraceTreeView.Model;
bool external = false;
model.Clear ();
@@ -607,7 +596,7 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander""
foreach (var frame in parentException.StackTrace) {
bool isUserCode = IsUserCode (frame);
- if (OnlyShowMyCodeCheckbox.Active && !isUserCode) {
+ if (onlyShowMyCodeCheckbox.Active && !isUserCode) {
if (!external) {
var str = "<b>" + GettextCatalog.GetString ("[External Code]") + "</b>";
model.AppendValues (null, str, false);
@@ -620,14 +609,27 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander""
model.AppendValues (frame, null, isUserCode);
external = false;
}
- if (!ReverseInnerExceptions.TryGetValue (parentException, out parentException))
+ if (!reverseInnerExceptions.TryGetValue (parentException, out parentException))
parentException = null;
}
- ExceptionValueTreeView.ClearAll ();
+
+ if (UseNewTreeView) {
+ controller.ClearAll ();
+ } else {
+ ((ObjectValueTreeView) exceptionValueTreeView).ClearAll ();
+ }
+
if (!ex.IsEvaluating && ex.Instance != null) {
var opts = DebuggingService.GetUserOptions ().EvaluationOptions.Clone ();
opts.FlattenHierarchy = true;
- ExceptionValueTreeView.AddValues (ex.Instance.GetAllChildren (opts));
+
+ var values = ex.Instance.GetAllChildren (opts);
+
+ if (UseNewTreeView) {
+ controller.AddValues (values);
+ } else {
+ ((ObjectValueTreeView) exceptionValueTreeView).AddValues (values);
+ }
}
if (ex.StackIsEvaluating) {
@@ -635,36 +637,33 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander""
model.AppendValues (null, str, false);
}
- if (InnerExceptionTypeLabel != null) {
- InnerExceptionTypeLabel.Markup = "<b>" + GLib.Markup.EscapeText (ex.Type) + "</b>";
- InnerExceptionMessageLabel.Text = ex.Message;
+ if (innerExceptionTypeLabel != null) {
+ innerExceptionTypeLabel.Markup = "<b>" + GLib.Markup.EscapeText (ex.Type) + "</b>";
+ innerExceptionMessageLabel.Text = ex.Message;
if (!string.IsNullOrEmpty (ex.HelpLink)) {
- InnerExceptionHelpLinkButton.Label = GettextCatalog.GetString ("Read More…");
+ innerExceptionHelpLinkButton.Label = GettextCatalog.GetString ("Read More…");
innerExceptionHelpLink = ex.HelpLink;
- InnerExceptionHelpLinkButton.Show ();
+ innerExceptionHelpLinkButton.Show ();
} else {
innerExceptionHelpLink = string.Empty;
- InnerExceptionHelpLinkButton.Hide ();
+ innerExceptionHelpLinkButton.Hide ();
}
}
}
- string innerExceptionHelpLink;
- string exceptionHelpLink;
-
void UpdateDisplay ()
{
if (destroyed)
return;
- ExceptionTypeLabel.Text = exception.Type;
- ExceptionMessageLabel.Text = exception.Message ?? string.Empty;
+ exceptionTypeLabel.Text = exception.Type;
+ exceptionMessageLabel.Text = exception.Message ?? string.Empty;
if (!string.IsNullOrEmpty (exception.HelpLink)) {
- ExceptionHelpLinkButton.Show ();
+ helpLinkButton.Show ();
exceptionHelpLink = exception.HelpLink;
- ExceptionHelpLinkButton.Label = GettextCatalog.GetString ("More information");
+ helpLinkButton.Label = GettextCatalog.GetString ("More information");
} else {
- ExceptionHelpLinkButton.Hide ();
+ helpLinkButton.Hide ();
}
UpdateSelectedException (exception);
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/LocalsPad.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/LocalsPad.cs
index f8c4ea4717..9ca1ceacd2 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/LocalsPad.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/LocalsPad.cs
@@ -31,10 +31,14 @@ namespace MonoDevelop.Debugger
{
public class LocalsPad : ObjectValuePad
{
- public LocalsPad ()
+ public LocalsPad () : base (true)
{
- tree.AllowEditing = true;
- tree.AllowAdding = false;
+ if (UseNewTreeView) {
+ controller.AllowEditing = true;
+ } else {
+ tree.AllowEditing = true;
+ tree.AllowAdding = false;
+ }
}
void ReloadValues ()
@@ -44,8 +48,30 @@ namespace MonoDevelop.Debugger
if (frame == null)
return;
- tree.ClearValues ();
- tree.AddValues (frame.GetAllLocals ().Where (l => !string.IsNullOrWhiteSpace (l.Name) && l.Name != "?").ToArray ());
+ var locals = frame.GetAllLocals ().Where (l => !string.IsNullOrWhiteSpace (l.Name) && l.Name != "?").ToArray();
+ if (UseNewTreeView) {
+ controller.ClearValues ();
+ controller.AddValues (locals);
+
+ //var xx = new System.Collections.Generic.List<ObjectValueNode> ();
+
+ //xx.Add (new FakeObjectValueNode ("f1"));
+ //xx.Add (new FakeIsImplicitNotSupportedObjectValueNode ());
+
+ //xx.Add (new FakeEvaluatingGroupObjectValueNode (1));
+ //xx.Add (new FakeEvaluatingGroupObjectValueNode (0));
+ //xx.Add (new FakeEvaluatingGroupObjectValueNode (5));
+
+ //xx.Add (new FakeEvaluatingObjectValueNode ());
+ //xx.Add (new FakeEnumerableObjectValueNode (10));
+ //xx.Add (new FakeEnumerableObjectValueNode (20));
+ //xx.Add (new FakeEnumerableObjectValueNode (23));
+
+ //controller.AddValues (xx);
+ } else {
+ tree.ClearValues ();
+ tree.AddValues (locals);
+ }
}
public override void OnUpdateFrame ()
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/DebuggerObjectValueNode.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/DebuggerObjectValueNode.cs
new file mode 100644
index 0000000000..697d61da53
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/DebuggerObjectValueNode.cs
@@ -0,0 +1,197 @@
+//
+// DebuggerObjectValueNode.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+
+using Mono.Debugging.Client;
+
+using MonoDevelop.Core;
+
+namespace MonoDevelop.Debugger
+{
+ /// <summary>
+ /// Represents a node in a tree structure that holds an ObjectValue from the debugger.
+ /// </summary>
+ class DebuggerObjectValueNode : ObjectValueNode, IEvaluatingGroupObjectValueNode
+ {
+ public DebuggerObjectValueNode (ObjectValue value) : base (value.Name)
+ {
+ DebuggerObject = value;
+
+ value.ValueChanged += OnDebuggerValueChanged;
+ }
+
+ // TODO: try and make this private
+ public ObjectValue DebuggerObject { get; }
+
+ /// <summary>
+ /// Gets the expression for the node that can be used when pinning node
+ /// </summary>
+ public override string Expression {
+ get {
+ string expression = "";
+
+ var node = this;
+ var name = node.Name;
+ while (node != null && node.Parent is DebuggerObjectValueNode) {
+ expression = node.DebuggerObject.ChildSelector + expression;
+ node = (DebuggerObjectValueNode)node.Parent;
+ name = node.Name;
+ }
+
+ return name + expression;
+ }
+ }
+
+ public override bool HasChildren => DebuggerObject.HasChildren;
+ public override bool IsEnumerable => DebuggerObject.Flags.HasFlag (ObjectValueFlags.IEnumerable);
+ public override bool IsEvaluating => DebuggerObject.IsEvaluating;
+ public override bool CanEdit => GetCanEdit ();
+
+ public override bool IsUnknown => DebuggerObject.IsUnknown;
+ public override bool IsReadOnly => DebuggerObject.IsReadOnly;
+ public override bool IsError => DebuggerObject.IsError;
+ public override bool IsNotSupported => DebuggerObject.IsNotSupported;
+ public override string Value => DebuggerObject.Value;
+ public override bool IsImplicitNotSupported => DebuggerObject.IsImplicitNotSupported;
+ public override ObjectValueFlags Flags => DebuggerObject.Flags;
+ public override bool IsNull => DebuggerObject.IsNull;
+ public override bool IsPrimitive => DebuggerObject.IsPrimitive;
+ public override string TypeName => DebuggerObject.TypeName;
+ public override string DisplayValue => DebuggerObject.DisplayValue;
+ public override bool CanRefresh => DebuggerObject.CanRefresh;
+ public override bool HasFlag (ObjectValueFlags flag) => DebuggerObject.HasFlag (flag);
+
+ public override void SetValue (string newValue)
+ {
+ DebuggerObject.Value = newValue;
+ }
+
+ public override void Refresh ()
+ {
+ DebuggerObject.Refresh ();
+ }
+
+ public override void Refresh (EvaluationOptions options)
+ {
+ DebuggerObject.Refresh (options);
+ }
+
+ #region IEvaluatingGroupObjectValueNode
+ bool IEvaluatingGroupObjectValueNode.IsEvaluatingGroup => DebuggerObject.IsEvaluatingGroup;
+
+ ObjectValueNode [] IEvaluatingGroupObjectValueNode.GetEvaluationGroupReplacementNodes ()
+ {
+ var replacementNodes = new ObjectValueNode[DebuggerObject.ArrayCount];
+
+ for (int i = 0; i < replacementNodes.Length; i++) {
+ replacementNodes[i] = new DebuggerObjectValueNode (DebuggerObject.GetArrayItem (i)) {
+ Parent = Parent
+ };
+ }
+
+ return replacementNodes;
+ }
+ #endregion
+
+ protected override async Task<IEnumerable<ObjectValueNode>> OnLoadChildrenAsync (CancellationToken cancellationToken)
+ {
+ var childValues = await GetChildrenAsync (DebuggerObject, cancellationToken);
+
+ return childValues.Select (x => new DebuggerObjectValueNode (x));
+ }
+
+ protected override async Task<Tuple<IEnumerable<ObjectValueNode>, bool>> OnLoadChildrenAsync (int index, int count, CancellationToken cancellationToken)
+ {
+ var values = await GetChildrenAsync (DebuggerObject, index, count, cancellationToken);
+ var nodes = values.Select (value => new DebuggerObjectValueNode (value));
+
+ // if we returned less that we asked for, we assume we've now loaded all children
+ return Tuple.Create<IEnumerable<ObjectValueNode>, bool> (nodes, values.Length < count);
+ }
+
+ static Task<ObjectValue[]> GetChildrenAsync (ObjectValue value, CancellationToken cancellationToken)
+ {
+ return Task.Run (() => {
+ try {
+ return value.GetAllChildren ();
+ } catch (Exception ex) {
+ // Note: this should only happen if someone breaks ObjectValue.GetAllChildren()
+ LoggingService.LogError ("Failed to get ObjectValue children.", ex);
+ return new ObjectValue[0];
+ }
+ }, cancellationToken);
+ }
+
+ static Task<ObjectValue []> GetChildrenAsync (ObjectValue value, int index, int count, CancellationToken cancellationToken)
+ {
+ return Task.Run(() => {
+ try {
+ return value.GetRangeOfChildren (index, count);
+ } catch (Exception ex) {
+ // Note: this should only happen if someone breaks ObjectValue.GetAllChildren()
+ LoggingService.LogError ("Failed to get ObjectValue range of children.", ex);
+ return new ObjectValue[0];
+ }
+ }, cancellationToken);
+ }
+
+ void OnDebuggerValueChanged (object sender, EventArgs e)
+ {
+ OnValueChanged (e);
+ }
+
+ bool GetCanEdit ()
+ {
+ var val = DebuggerObject;
+ bool canEdit;
+
+ if (val.IsUnknown) {
+ //if (frame != null) {
+ // canEdit = false;
+ //} else {
+ canEdit = !val.IsReadOnly;
+ //}
+ } else if (val.IsError || val.IsNotSupported) {
+ canEdit = false;
+ } else if (val.IsImplicitNotSupported) {
+ canEdit = false;
+ } else if (val.IsEvaluating) {
+ canEdit = false;
+ } else if (val.Flags.HasFlag (ObjectValueFlags.IEnumerable)) {
+ canEdit = false;
+ } else {
+ canEdit = val.IsPrimitive && !val.IsReadOnly;
+ }
+
+ return canEdit;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Gtk/GtkObjectValueTreeView.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Gtk/GtkObjectValueTreeView.cs
new file mode 100644
index 0000000000..f4a4ce4ea2
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Gtk/GtkObjectValueTreeView.cs
@@ -0,0 +1,2337 @@
+//
+// GtkObjectValueTreeView.cs
+//
+// Author:
+// gregm <gregm@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+
+using Gdk;
+using Gtk;
+
+using Mono.Debugging.Client;
+
+using MonoDevelop.Components;
+using MonoDevelop.Core;
+using MonoDevelop.Ide;
+using MonoDevelop.Ide.CodeCompletion;
+using MonoDevelop.Components.Commands;
+using MonoDevelop.Ide.Commands;
+using MonoDevelop.Ide.Editor.Extension;
+using MonoDevelop.Ide.Fonts;
+
+namespace MonoDevelop.Debugger
+{
+ [System.ComponentModel.ToolboxItem (true)]
+ public class GtkObjectValueTreeView : TreeView, ICompletionWidget, IObjectValueTreeView
+ {
+ static readonly Gtk.TargetEntry [] DropTargets = {
+ new Gtk.TargetEntry ("text/plain;charset=utf-8", Gtk.TargetFlags.App, 0)
+ };
+
+ readonly IObjectValueDebuggerService debuggerService;
+ readonly ObjectValueTreeViewController controller;
+
+ /// <summary>
+ /// The root node
+ /// </summary>
+ ObjectValueNode root;
+
+ /// <summary>
+ /// If we allow pinning, this is the single pinned value that a view can support
+ /// </summary>
+ PinnedWatch pinnedWatch;
+
+ // mapping of a node to the node's location in the tree view
+ readonly Dictionary<ObjectValueNode, TreeRowReference> allNodes = new Dictionary<ObjectValueNode, TreeRowReference> ();
+
+ readonly bool compactView;
+ readonly bool allowPinning;
+ readonly bool allowPopupMenu;
+ readonly bool rootPinVisible;
+
+ readonly Xwt.Drawing.Image noLiveIcon;
+ readonly Xwt.Drawing.Image liveIcon;
+
+ readonly TreeViewState state;
+ readonly TreeStore store;
+ readonly string createMsg;
+ bool restoringState;
+ bool disposed;
+
+ bool columnsAdjusted;
+ bool columnSizesUpdating;
+ bool allowStoreColumnSizes;
+ double expColWidth;
+ double valueColWidth;
+ double typeColWidth;
+
+ int expanderSize;
+ int horizontal_separator;
+ int grid_line_width;
+ int focus_line_width;
+ Gdk.Rectangle startPreviewCaret;
+ double startHAdj;
+ double startVAdj;
+ TreeIter lastPinIter;
+ bool editing;
+
+ bool allowEditing;
+ bool allowWatchExpressions;
+ bool wasHandled;
+ CodeCompletionContext ctx;
+ Gdk.Key key;
+ char keyChar;
+ Gdk.ModifierType modifierState;
+ uint keyValue;
+ PreviewButtonIcons iconBeforeSelected;
+ PreviewButtonIcons currentIcon;
+ TreeIter currentHoverIter = TreeIter.Zero;
+ Adjustment oldHadjustment;
+ Adjustment oldVadjustment;
+
+ readonly CellRendererTextWithIcon crtExp;
+ readonly ValueCellRenderer crtValue;
+ readonly CellRendererText crtType;
+ readonly CellRendererRoundedButton crpButton;
+ readonly CellRendererImage evaluateStatusCell;
+ readonly CellRendererImage crpPin;
+ readonly CellRendererImage crpLiveUpdate;
+ readonly CellRendererImage crpViewer;
+ Entry editEntry;
+ Mono.Debugging.Client.CompletionData currentCompletionData;
+
+ readonly TreeViewColumn expCol;
+ readonly TreeViewColumn valueCol;
+ readonly TreeViewColumn typeCol;
+ readonly TreeViewColumn pinCol;
+
+ static readonly CommandEntrySet menuSet;
+
+ const int NameColumn = 0;
+ const int ValueColumn = 1;
+ const int TypeColumn = 2;
+ const int NameEditableColumn = 3;
+ const int ValueEditableColumn = 4;
+ const int IconColumn = 5;
+ const int NameColorColumn = 6;
+ const int ValueColorColumn = 7;
+ const int ValueButtonVisibleColumn = 8;
+ const int PinIconColumn = 9;
+ const int LiveUpdateIconColumn = 10;
+ const int ViewerButtonVisibleColumn = 11;
+ const int PreviewIconColumn = 12;
+ const int EvaluateStatusIconColumn = 13;
+ const int EvaluateStatusIconVisibleColumn = 14;
+ const int ValueButtonTextColumn = 15;
+ const int ObjectNodeColumn = 16;
+
+ enum LocalCommands
+ {
+ AddWatch
+ }
+
+ static GtkObjectValueTreeView ()
+ {
+ // Context menu definition
+
+ menuSet = new CommandEntrySet ();
+ menuSet.AddItem (DebugCommands.AddWatch);
+ menuSet.AddSeparator ();
+ menuSet.AddItem (EditCommands.Copy);
+ menuSet.AddItem (EditCommands.Rename);
+ menuSet.AddItem (EditCommands.DeleteKey);
+ }
+
+ public GtkObjectValueTreeView (
+ IObjectValueDebuggerService debuggerService,
+ ObjectValueTreeViewController controller,
+ bool allowEditing,
+ bool headersVisible,
+ bool allowWatchExpressions,
+ bool compactView,
+ bool allowPinning,
+ bool allowPopupMenu,
+ bool rootPinVisible)
+ {
+ this.compactView = compactView;
+ this.allowPinning = allowPinning;
+ this.allowPopupMenu = allowPopupMenu;
+ this.rootPinVisible = rootPinVisible;
+
+ // ensure this is set when we set up the view, don't try and refresh just yet
+ this.allowEditing = allowEditing;
+ this.allowWatchExpressions = allowWatchExpressions;
+
+ this.debuggerService = debuggerService;
+ this.controller = controller;
+
+ store = new TreeStore (typeof (string), typeof (string), typeof (string), typeof (bool), typeof (bool), typeof (string), typeof (string), typeof (string), typeof (bool), typeof (string), typeof (Xwt.Drawing.Image), typeof (bool), typeof (string), typeof (Xwt.Drawing.Image), typeof (bool), typeof (string), typeof (ObjectValueNode));
+ Model = store;
+ SearchColumn = -1; // disable the interactive search
+ RulesHint = true;
+ HeadersVisible = headersVisible;
+ EnableSearch = false;
+ Selection.Mode = Gtk.SelectionMode.Multiple;
+ Selection.Changed += HandleSelectionChanged;
+ ResetColumnSizes ();
+
+ EnableModelDragDest (DropTargets, Gdk.DragAction.Copy);
+ DragDataReceived += OnDragDataReceived;
+
+ Pango.FontDescription newFont;
+
+ if (compactView) {
+ newFont = IdeServices.FontService.SansFont.CopyModified (Ide.Gui.Styles.FontScale11);
+ } else {
+ newFont = IdeServices.FontService.SansFont.CopyModified (Ide.Gui.Styles.FontScale12);
+ }
+
+ liveIcon = ImageService.GetIcon ("md-live", IconSize.Menu);
+ noLiveIcon = liveIcon.WithAlpha (0.5);
+
+ expCol = new TreeViewColumn ();
+ expCol.Title = GettextCatalog.GetString ("Name");
+ var crp = new CellRendererImage ();
+ expCol.PackStart (crp, false);
+ expCol.AddAttribute (crp, "stock_id", IconColumn);
+ crtExp = new CellRendererTextWithIcon ();
+ crtExp.FontDesc = newFont;
+ expCol.PackStart (crtExp, true);
+ expCol.AddAttribute (crtExp, "text", NameColumn);
+ expCol.AddAttribute (crtExp, "editable", NameEditableColumn);
+ expCol.AddAttribute (crtExp, "foreground", NameColorColumn);
+ expCol.AddAttribute (crtExp, "icon", PreviewIconColumn);
+ expCol.Resizable = true;
+ expCol.Sizing = TreeViewColumnSizing.Fixed;
+ expCol.MinWidth = 15;
+ expCol.AddNotification ("width", OnColumnWidthChanged);
+ AppendColumn (expCol);
+
+ valueCol = new TreeViewColumn ();
+ valueCol.Title = GettextCatalog.GetString ("Value");
+ valueCol.MaxWidth = compactView ? 800 : int.MaxValue;
+ evaluateStatusCell = new CellRendererImage ();
+ valueCol.PackStart (evaluateStatusCell, false);
+ valueCol.AddAttribute (evaluateStatusCell, "visible", EvaluateStatusIconVisibleColumn);
+ valueCol.AddAttribute (evaluateStatusCell, "image", EvaluateStatusIconColumn);
+ var crColorPreview = new CellRendererColorPreview ();
+ valueCol.PackStart (crColorPreview, false);
+ valueCol.SetCellDataFunc (crColorPreview, ValueDataFunc);
+ crpButton = new CellRendererRoundedButton ();
+ crpButton.FontDesc = newFont;
+ valueCol.PackStart (crpButton, false);
+ valueCol.AddAttribute (crpButton, "visible", ValueButtonVisibleColumn);
+ valueCol.AddAttribute (crpButton, "text", ValueButtonTextColumn);
+ crpViewer = new CellRendererImage ();
+ if (compactView)
+ crpViewer.Image = ImageService.GetIcon (Stock.Edit).WithSize (12, 12);
+ else
+ crpViewer.Image = ImageService.GetIcon (Stock.Edit, IconSize.Menu);
+ valueCol.PackStart (crpViewer, false);
+ valueCol.AddAttribute (crpViewer, "visible", ViewerButtonVisibleColumn);
+ crtValue = new ValueCellRenderer ();
+ crtValue.Ellipsize = Pango.EllipsizeMode.End;
+ crtValue.Compact = compactView;
+ crtValue.FontDesc = newFont;
+ valueCol.PackStart (crtValue, true);
+ valueCol.AddAttribute (crtValue, "texturl", ValueColumn);
+ valueCol.AddAttribute (crtValue, "editable", ValueEditableColumn);
+ valueCol.AddAttribute (crtValue, "foreground", ValueColorColumn);
+ valueCol.Resizable = true;
+ valueCol.MinWidth = 15;
+ valueCol.AddNotification ("width", OnColumnWidthChanged);
+ // valueCol.Expand = true;
+ valueCol.Sizing = TreeViewColumnSizing.Fixed;
+ AppendColumn (valueCol);
+
+ typeCol = new TreeViewColumn ();
+ typeCol.Title = GettextCatalog.GetString ("Type");
+ typeCol.Visible = !compactView;
+ crtType = new CellRendererText ();
+ crtType.FontDesc = newFont;
+ typeCol.PackStart (crtType, true);
+ typeCol.AddAttribute (crtType, "text", TypeColumn);
+ typeCol.Resizable = true;
+ typeCol.Sizing = TreeViewColumnSizing.Fixed;
+ typeCol.MinWidth = 15;
+ typeCol.AddNotification ("width", OnColumnWidthChanged);
+ // typeCol.Expand = true;
+ AppendColumn (typeCol);
+
+ pinCol = new TreeViewColumn ();
+ crpPin = new CellRendererImage ();
+ pinCol.PackStart (crpPin, false);
+ pinCol.AddAttribute (crpPin, "stock_id", PinIconColumn);
+ crpLiveUpdate = new CellRendererImage ();
+ pinCol.PackStart (crpLiveUpdate, false);
+ pinCol.AddAttribute (crpLiveUpdate, "image", LiveUpdateIconColumn);
+ pinCol.Resizable = false;
+ pinCol.Visible = allowPinning;
+ pinCol.Expand = false;
+ pinCol.Sizing = TreeViewColumnSizing.Fixed;
+ pinCol.FixedWidth = 16;
+ AppendColumn (pinCol);
+
+ state = new TreeViewState (this, NameColumn);
+
+ crtExp.Edited += OnExpressionEdited;
+ crtExp.EditingStarted += OnExpressionStartedEditing;
+ crtExp.EditingCanceled += OnEditingCancelled;
+ crtValue.EditingStarted += OnValueEditing;
+ crtValue.Edited += OnValueEdited;
+ crtValue.EditingCanceled += OnEditingCancelled;
+
+ createMsg = GettextCatalog.GetString ("Click here to add a new watch");
+ CompletionWindowManager.WindowClosed += HandleCompletionWindowClosed;
+ PreviewWindowManager.WindowClosed += HandlePreviewWindowClosed;
+ ScrollAdjustmentsSet += HandleScrollAdjustmentsSet;
+
+ expanderSize = (int)StyleGetProperty ("expander-size") + 4; //+4 is hardcoded in gtk.c code
+ horizontal_separator = (int)StyleGetProperty ("horizontal-separator");
+ grid_line_width = (int)StyleGetProperty ("grid-line-width");
+ focus_line_width = (int)StyleGetProperty ("focus-line-width") * 2; //we just use *2 version in GetMaxWidth
+
+ AdjustColumnSizes ();
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether the user should be able to edit values in the tree
+ /// </summary>
+ public bool AllowEditing {
+ get => allowEditing;
+ set {
+ if (allowEditing != value) {
+ allowEditing = value;
+ Refresh (false);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether or not the user should be able to expand nodes in the tree.
+ /// </summary>
+ public bool AllowExpanding { get; set; }
+
+ /// <summary>
+ /// Gets a value indicating whether the user should be able to add watch expressions to the tree
+ /// </summary>
+ public bool AllowWatchExpressions {
+ get => allowWatchExpressions;
+ set {
+ if (allowWatchExpressions != value) {
+ allowWatchExpressions = value;
+ Refresh (false);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the pinned watch for the view. When a watch is pinned, the view should display only this value
+ /// </summary>
+ public PinnedWatch PinnedWatch {
+ get => pinnedWatch;
+ set {
+ if (pinnedWatch != value) {
+ pinnedWatch = value;
+ Runtime.RunInMainThread (() => {
+ if (value == null) {
+ pinCol.FixedWidth = 16;
+ } else {
+ pinCol.FixedWidth = 38;
+ }
+ }).Ignore();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating the offset required for pinned watches
+ /// </summary>
+ public int PinnedWatchOffset {
+ get {
+ return SizeRequest ().Height;
+ }
+ }
+
+ /// <summary>
+ /// Triggered when the view tries to expand a node. This may trigger a load of
+ /// the node's children
+ /// </summary>
+ public event EventHandler<ObjectValueNodeEventArgs> NodeExpand;
+
+ /// <summary>
+ /// Triggered when the view tries to collapse a node.
+ /// </summary>
+
+ public event EventHandler<ObjectValueNodeEventArgs> NodeCollapse;
+
+ /// <summary>
+ /// Triggered when the view requests a node to fetch more of it's children
+ /// </summary>
+ public event EventHandler<ObjectValueNodeEventArgs> NodeLoadMoreChildren;
+
+ /// <summary>
+ /// Triggered when the view needs the node to be refreshed
+ /// </summary>
+ public event EventHandler<ObjectValueNodeEventArgs> NodeRefresh;
+
+ /// <summary>
+ /// Triggered when the view needs to know if the node can be edited
+ /// </summary>
+ public event EventHandler<ObjectValueNodeEventArgs> NodeGetCanEdit;
+
+ /// <summary>
+ /// Triggered when the node's value has been edited by the user
+ /// </summary>
+ public event EventHandler<ObjectValueEditEventArgs> NodeEditValue;
+
+ /// <summary>
+ /// Triggered when the user removes a node (an expression)
+ /// </summary>
+ public event EventHandler<ObjectValueNodeEventArgs> NodeRemoved;
+
+ /// <summary>
+ /// Triggered when the user pins the node
+ /// </summary>
+ public event EventHandler<ObjectValueNodeEventArgs> NodePinned;
+
+ /// <summary>
+ /// Triggered when the pinned watch is removed by the user
+ /// </summary>
+ public event EventHandler<EventArgs> NodeUnpinned;
+
+ /// <summary>
+ /// Triggered when the visualiser for the node should be shown
+ /// </summary>
+ public event EventHandler<ObjectValueNodeEventArgs> NodeShowVisualiser;
+
+ /// <summary>
+ /// Triggered when an expression is added to the tree by the user
+ /// </summary>
+ public event EventHandler<ObjectValueExpressionEventArgs> ExpressionAdded;
+
+ /// <summary>
+ /// Triggered when an expression is edited by the user
+ /// </summary>
+ public event EventHandler<ObjectValueExpressionEventArgs> ExpressionEdited;
+
+ /// <summary>
+ /// Triggered when the user starts editing a node
+ /// </summary>
+ public event EventHandler StartEditing;
+
+ /// <summary>
+ /// Triggered when the user stops editing a node
+ /// </summary>
+ public event EventHandler EndEditing;
+
+ protected override void OnDestroyed ()
+ {
+ CompletionWindowManager.WindowClosed -= HandleCompletionWindowClosed;
+ PreviewWindowManager.WindowClosed -= HandlePreviewWindowClosed;
+ PreviewWindowManager.DestroyWindow ();
+ crtExp.Edited -= OnExpressionEdited;
+ crtExp.EditingStarted -= OnExpressionStartedEditing;
+ crtExp.EditingCanceled -= OnEditingCancelled;
+ crtValue.EditingStarted -= OnValueEditing;
+ crtValue.Edited -= OnValueEdited;
+ crtValue.EditingCanceled -= OnEditingCancelled;
+
+ typeCol.RemoveNotification ("width", OnColumnWidthChanged);
+ valueCol.RemoveNotification ("width", OnColumnWidthChanged);
+ expCol.RemoveNotification ("width", OnColumnWidthChanged);
+
+ ScrollAdjustmentsSet -= HandleScrollAdjustmentsSet;
+ if (oldHadjustment != null) {
+ oldHadjustment.ValueChanged -= UpdatePreviewPosition;
+ oldVadjustment.ValueChanged -= UpdatePreviewPosition;
+ oldHadjustment = null;
+ oldVadjustment = null;
+ }
+
+ disposed = true;
+ controller.CancelAsyncTasks ();
+
+ base.OnDestroyed ();
+ }
+
+ protected override void OnSizeAllocated (Gdk.Rectangle allocation)
+ {
+ base.OnSizeAllocated (allocation);
+ AdjustColumnSizes ();
+ UpdatePreviewPosition ();
+ }
+
+ protected override void OnShown ()
+ {
+ base.OnShown ();
+ AdjustColumnSizes ();
+ if (compactView)
+ RecalculateWidth ();
+ }
+
+ protected override void OnRealized ()
+ {
+ base.OnRealized ();
+ AdjustColumnSizes ();
+ }
+
+ void OnDragDataReceived (object o, DragDataReceivedArgs args)
+ {
+ if (!AllowWatchExpressions)
+ return;
+
+ var text = args.SelectionData.Text;
+
+ args.RetVal = true;
+
+ if (string.IsNullOrEmpty (text))
+ return;
+
+ foreach (var expression in text.Split (new [] { '\n' })) {
+ if (string.IsNullOrWhiteSpace (expression))
+ continue;
+
+ ExpressionAdded?.Invoke (this, new ObjectValueExpressionEventArgs(null, expression.Trim ()));
+ }
+ }
+
+ /// <summary>
+ /// Reloads the tree from the root node
+ /// </summary>
+ public void Reload (ObjectValueNode root)
+ {
+ // TODO: how to tell whether to reset scroll position or not?
+ this.root = root;
+ Refresh (false);
+ }
+
+ /// <summary>
+ /// Informs the view to load the children of the given node
+ /// </summary>
+ public void LoadNodeChildren (ObjectValueNode node, int startIndex, int count)
+ {
+ OnChildrenLoaded (node, startIndex, count);
+ }
+
+ /// <summary>
+ /// Informs the view to load the new values into the given node, optionally replacing that node with
+ /// the set of replacement nodes. Handles the case where, for example, the "locals" is replaced
+ /// with the set of local values
+ /// </summary>
+ public void LoadEvaluatedNode (ObjectValueNode node, ObjectValueNode [] replacementNodes)
+ {
+ OnEvaluationCompleted (node, replacementNodes);
+ }
+
+ void OnChildrenLoaded (ObjectValueNode node, int index, int count)
+ {
+ if (disposed)
+ return;
+
+ // the children of a specific node changed
+ // remove the children for that node, then reload the children
+ if (GetTreeIterFromNode (node, out TreeIter iter, out TreeIter parent)) {
+ // rather than simply replacing the children of this node we will merge
+ // them in so that the tree does not collapse the row when the last child is removed
+ MergeChildrenIntoTree (node, iter, index, count);
+
+ // if we did not load all the children, add a More node
+ if (!node.ChildrenLoaded) {
+ AppendNodeToTreeModel (iter, null, new ShowMoreValuesObjectValueNode (node));
+ }
+ }
+
+ if (compactView) {
+ RecalculateWidth ();
+ }
+ }
+
+ // TODO: if we don't want the scrolling, we can probably get rid of this
+ /// <summary>
+ /// Informs the view that the node was expanded and children have been loaded.
+ /// </summary>
+ public void OnNodeExpanded (ObjectValueNode node)
+ {
+ if (disposed)
+ return;
+
+ if (node.IsExpanded) {
+ // if the node is _still_ expanded then adjust UI and scroll
+ var path = GetTreePathForNode (node);
+
+ if (!GetRowExpanded (path)) {
+ ExpandRow (path, false);
+ }
+
+ if (compactView)
+ RecalculateWidth ();
+
+ // TODO: all this scrolling kind of seems awkward
+ //if (path != null)
+ // ScrollToCell (path, expCol, true, 0f, 0f);
+ }
+ }
+
+ /// <summary>
+ /// Merge the node's children as children of the node in the tree
+ /// </summary>
+ void MergeChildrenIntoTree (ObjectValueNode node, TreeIter nodeIter, int index, int count)
+ {
+ var nodeChildren = node.Children.ToList ();
+
+ if (nodeChildren.Count == 0) {
+ RemoveChildren (nodeIter);
+ return;
+ }
+
+ var visibleChildrenCount = store.IterNChildren (nodeIter);
+
+ int ix = 0;
+ while (ix < nodeChildren.Count) {
+ // if we have existing visible rows in the tree, update the values and remove children
+ if (ix < visibleChildrenCount) {
+ if (store.IterNthChild (out TreeIter childIter, nodeIter, ix)) {
+ RemoveChildren (childIter);
+ SetValues (nodeIter, childIter, null, nodeChildren [ix]);
+ }
+ } else {
+ AppendNodeToTreeModel (nodeIter, null, nodeChildren [ix]);
+ }
+
+ ix++;
+ }
+
+ if (ix < visibleChildrenCount) {
+ // remove extra nodes we don't need anymore
+ while (store.IterNthChild (out TreeIter childIter, nodeIter, ix))
+ Remove (ref childIter);
+ }
+ }
+
+ /// <summary>
+ /// Updates or replaces the node with the given replacement nodes when the debugger notifies
+ /// that the node has completed evaulation
+ /// </summary>
+ void OnEvaluationCompleted (ObjectValueNode node, ObjectValueNode[] replacementNodes)
+ {
+ if (disposed)
+ return;
+
+ if (GetTreeIterFromNode (node, out TreeIter iter, out TreeIter parent)) {
+ RemoveChildren (iter);
+
+ if (replacementNodes.Length == 0) {
+ // we can remove the node altogether, eg there are no local variables to show
+ Remove (ref iter);
+ } else if (replacementNodes.Length > 0) {
+ node = replacementNodes [0];
+ SetValues (parent, iter, node.Name, node);
+
+ for (int n = 1; n < replacementNodes.Length; n++) {
+ iter = store.InsertNodeAfter (iter);
+ SetValues (parent, iter, null, replacementNodes[n]);
+ }
+ }
+ }
+
+ if (compactView) {
+ RecalculateWidth ();
+ }
+ }
+
+ bool Remove (ref TreeIter iter)
+ {
+ var node = GetNodeAtIter (iter);
+
+ if (node != null && allNodes.TryGetValue (node, out TreeRowReference row)) {
+ allNodes.Remove (node);
+ row.Dispose ();
+ }
+
+ return store.Remove (ref iter);
+ }
+
+ void RemoveChildren (TreeIter iter)
+ {
+ while (store.IterChildren (out TreeIter child, iter)) {
+ RemoveChildren (child);
+ Remove (ref child);
+ }
+ }
+
+ void SaveState ()
+ {
+ state.Save ();
+ }
+
+ void LoadState ()
+ {
+ restoringState = true;
+ state.Load ();
+ restoringState = false;
+ }
+
+ //public void Update ()
+ //{
+ // //cachedValues.Clear ();
+ // Refresh (true);
+ //}
+
+ void Refresh (bool resetScrollPosition)
+ {
+ // Note: this is a hack that ideally we could get rid of...
+ if (IsRealized && resetScrollPosition)
+ ScrollToPoint (0, 0);
+
+ SaveState ();
+
+ CleanPinIcon ();
+ store.Clear ();
+
+ bool showExpanders = AllowWatchExpressions;
+
+ if (root != null) {
+ if (LoadNode (root, TreeIter.Zero)) {
+ showExpanders = true;
+ }
+ }
+
+ if (showExpanders)
+ ShowExpanders = true;
+
+ if (AllowWatchExpressions) {
+ store.AppendValues (createMsg, "", "", true, true, null, Ide.Gui.Styles.ColorGetHex (Styles.ObjectValueTreeValueDisabledText), Ide.Gui.Styles.ColorGetHex (Styles.ObjectValueTreeValueDisabledText));
+ }
+
+ LoadState ();
+ }
+
+ bool LoadNode (ObjectValueNode node, TreeIter parent)
+ {
+ var result = false;
+ foreach (var val in node.Children) {
+ // append value calls setvalues which adds a dummy row for new and unloaded children.
+ var iter = AppendNodeToTreeModel (parent, null, val);
+ if (val.HasChildren) {
+ result = true;
+ if (val.ChildrenLoaded || val.Children.Count > 0) {
+ // if any children for the node have already been loaded then add
+ // these to the tree immediately instead of adding a dummy loading node
+ LoadNode (val, iter);
+
+ // make sure the load more button is enabled for enumerable nodes that are not fully loaded
+ if (!val.ChildrenLoaded) {
+ AppendNodeToTreeModel (iter, null, new ShowMoreValuesObjectValueNode (node));
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /// <summary>
+ /// Fired when the user clicks on the value button, eg "Show Value", 'More Values", "Show Values"
+ /// </summary>
+ void HandleValueButton (TreeIter it)
+ {
+ var node = GetNodeAtIter (it);
+ HideValueButton (it);
+
+ if (node.IsEnumerable) {
+ if (node is ShowMoreValuesObjectValueNode moreNode) {
+ NodeLoadMoreChildren?.Invoke (this, new ObjectValueNodeEventArgs (moreNode.EnumerableNode));
+ } else {
+ // use ExpandRow to expand so we see the loading message, expanding the node will trigger a fetch of the children
+ var treePath = GetTreePathForNode (node);
+ ExpandRow (treePath, false);
+ }
+ } else {
+ // this is likely to support IsImplicitNotSupported
+ NodeRefresh?.Invoke (this, new ObjectValueNodeEventArgs (node));
+
+ // update the tree
+ if (store.IterParent (out TreeIter parentIter, it)) {
+ SetValues (parentIter, it, null, node);
+ } else {
+ SetValues (TreeIter.Zero, it, null, node);
+ }
+ }
+ }
+
+ void HideValueButton(TreeIter iter)
+ {
+ store.SetValue (iter, ValueButtonTextColumn, string.Empty);
+ }
+
+ TreeIter AppendNodeToTreeModel (TreeIter parent, string name, ObjectValueNode valueNode)
+ {
+ TreeIter iter;
+
+ if (parent.Equals (TreeIter.Zero))
+ iter = store.AppendNode ();
+ else
+ iter = store.AppendNode (parent);
+
+ SetValues (parent, iter, name, valueNode);
+ return iter;
+ }
+
+ // TODO: refactor this so that we can update a node without needing to know the parent iter all the time
+ void SetValues (TreeIter parent, TreeIter it, string name, ObjectValueNode val, bool updateJustValue = false)
+ {
+ // create a link to the node in the tree view and it's path
+ allNodes [val] = new TreeRowReference (store, store.GetPath (it));
+
+
+ string strval;
+ string nameColor = null;
+ string valueColor = null;
+ string valueButton = null;
+ string evaluateStatusIcon = null;
+
+
+ name = name ?? val.Name;
+
+ bool hasParent = !parent.Equals (TreeIter.Zero);
+ bool showViewerButton = false;
+
+ string valPath;
+ if (!hasParent)
+ valPath = "/" + name;
+ else
+ valPath = GetIterPath (parent) + "/" + name;
+
+ if (val.IsUnknown) {
+ if (debuggerService.Frame != null) {
+ strval = GettextCatalog.GetString ("The name '{0}' does not exist in the current context.", val.Name);
+ nameColor = Ide.Gui.Styles.ColorGetHex (Styles.ObjectValueTreeValueDisabledText);
+ } else {
+ strval = string.Empty;
+ }
+ evaluateStatusIcon = MonoDevelop.Ide.Gui.Stock.Warning;
+ } else if (val.IsError || val.IsNotSupported) {
+ evaluateStatusIcon = MonoDevelop.Ide.Gui.Stock.Warning;
+ strval = val.Value;
+ int i = strval.IndexOf ('\n');
+ if (i != -1)
+ strval = strval.Substring (0, i);
+ valueColor = Ide.Gui.Styles.ColorGetHex (Styles.ObjectValueTreeValueErrorText);
+ } else if (val.IsImplicitNotSupported) {
+ strval = "";//val.Value; with new "Show Value" button we don't want to display message "Implicit evaluation is disabled"
+ valueColor = Ide.Gui.Styles.ColorGetHex (Styles.ObjectValueTreeValueDisabledText);
+ if (val.CanRefresh)
+ valueButton = GettextCatalog.GetString ("Show Value");
+ } else if (val.IsEvaluating) {
+ strval = GettextCatalog.GetString ("Evaluating...");
+
+ evaluateStatusIcon = "md-spinner-16";
+
+ valueColor = Ide.Gui.Styles.ColorGetHex (Styles.ObjectValueTreeValueDisabledText);
+ if (val.GetIsEvaluatingGroup ()) {
+ nameColor = Ide.Gui.Styles.ColorGetHex (Styles.ObjectValueTreeValueDisabledText);
+ name = val.Name;
+ }
+ } else if (val.IsEnumerable) {
+ if (val is ShowMoreValuesObjectValueNode) {
+ valueButton = GettextCatalog.GetString ("Show More");
+ } else {
+ valueButton = GettextCatalog.GetString ("Show Values");
+ }
+ strval = "";
+ } else {
+ strval = controller.GetDisplayValueWithVisualisers (val, out showViewerButton);
+
+ if (controller.GetNodeHasChangedSinceLastCheckpoint(val)) {
+ nameColor = valueColor = Ide.Gui.Styles.ColorGetHex (Styles.ObjectValueTreeValueModifiedText);
+ }
+ }
+
+ strval = strval.Replace ("\r\n", " ").Replace ("\n", " ");
+
+ store.SetValue (it, ValueColumn, strval);
+ if (updateJustValue)
+ return;
+
+ bool canEdit = GetCanEditNode (val);
+ string icon = ObjectValueTreeViewController.GetIcon (val.Flags);
+
+ store.SetValue (it, NameColumn, name);
+ store.SetValue (it, TypeColumn, val.TypeName);
+ store.SetValue (it, ObjectNodeColumn, val);
+ store.SetValue (it, NameEditableColumn, !hasParent && AllowWatchExpressions);
+ store.SetValue (it, ValueEditableColumn, canEdit);
+ store.SetValue (it, IconColumn, icon);
+ store.SetValue (it, NameColorColumn, nameColor);
+ store.SetValue (it, ValueColorColumn, valueColor);
+ store.SetValue (it, EvaluateStatusIconVisibleColumn, evaluateStatusIcon != null);
+ store.LoadIcon (it, EvaluateStatusIconColumn, evaluateStatusIcon, IconSize.Menu);
+ store.SetValue (it, ValueButtonVisibleColumn, valueButton != null);
+ store.SetValue (it, ValueButtonTextColumn, valueButton);
+ store.SetValue (it, ViewerButtonVisibleColumn, showViewerButton);
+
+
+ if (ValidObjectForPreviewIcon (it))
+ store.SetValue (it, PreviewIconColumn, "md-empty");
+
+ if (!hasParent && PinnedWatch != null) {
+ store.SetValue (it, PinIconColumn, "md-pin-down");
+ if (PinnedWatch.LiveUpdate)
+ store.SetValue (it, LiveUpdateIconColumn, liveIcon);
+ else
+ store.SetValue (it, LiveUpdateIconColumn, noLiveIcon);
+ }
+ if (rootPinVisible && (!hasParent && PinnedWatch == null && allowPinning))
+ store.SetValue (it, PinIconColumn, "md-pin-up");
+
+ if (val.HasChildren && val.Children.Count == 0) {
+ // Add dummy node, we need this or the expander isn't shown, but only if the children are not
+ // already loaded
+ store.AppendValues (it, GettextCatalog.GetString ("Loading\u2026"), "", "", false);
+ if (!ShowExpanders) {
+ ShowExpanders = true;
+ }
+
+ if (controller.GetNodeWasExpandedAtLastCheckpoint (val)) {
+ ExpandRow (store.GetPath (it), false);
+ }
+ }
+ }
+
+ bool GetCanEditNode(ObjectValueNode node)
+ {
+ var args = new ObjectValueNodeEventArgs (node);
+ NodeGetCanEdit?.Invoke (this, args);
+ return args.Response is bool b && b;
+ }
+
+ protected override bool OnTestExpandRow (TreeIter iter, TreePath path)
+ {
+ if (!restoringState) {
+ if (!AllowExpanding)
+ return true;
+
+ if (GetRowExpanded (path))
+ return true;
+
+ TreeIter parent;
+ if (store.IterParent (out parent, iter)) {
+ if (!GetRowExpanded (store.GetPath (parent)))
+ return true;
+ }
+ }
+
+ return base.OnTestExpandRow (iter, path);
+ }
+
+ protected override void OnRowExpanded (TreeIter iter, TreePath path)
+ {
+ var node = GetNodeAtIter (iter);
+
+ base.OnRowExpanded (iter, path);
+
+ if (compactView)
+ RecalculateWidth ();
+
+ HideValueButton (iter);
+
+ NodeExpand?.Invoke (this, new ObjectValueNodeEventArgs (node));
+ }
+
+ protected override void OnRowCollapsed (TreeIter iter, TreePath path)
+ {
+ var node = GetNodeAtIter (iter);
+
+ base.OnRowCollapsed (iter, path);
+
+ if (compactView)
+ RecalculateWidth ();
+
+ NodeCollapse?.Invoke (this, new ObjectValueNodeEventArgs (node));
+
+ // TODO: all this scrolling kind of seems awkward
+ //ScrollToCell (path, expCol, true, 0f, 0f);
+ }
+
+ string GetIterPath (TreeIter iter)
+ {
+ var path = new StringBuilder ();
+
+ do {
+ string name = (string) store.GetValue (iter, NameColumn);
+ path.Insert (0, "/" + name);
+ } while (store.IterParent (out iter, iter));
+
+ return path.ToString ();
+ }
+
+ void OnExpressionStartedEditing (object s, EditingStartedArgs args)
+ {
+ if (!store.GetIterFromString (out TreeIter iter, args.Path))
+ return;
+
+ var entry = (Entry) args.Editable;
+ if (entry.Text == createMsg)
+ entry.Text = string.Empty;
+
+ OnStartEditing (args);
+ }
+
+ void OnExpressionEdited (object s, EditedArgs args)
+ {
+ OnEndEditing ();
+
+ if (!store.GetIterFromString (out TreeIter iter, args.Path))
+ return;
+
+ var node = GetNodeAtIter (iter);
+
+ if (node == null) {
+ if (args.NewText.Length > 0) {
+ ExpressionAdded?.Invoke (this, new ObjectValueExpressionEventArgs (null, args.NewText));
+ }
+ } else {
+ ExpressionEdited?.Invoke (this, new ObjectValueExpressionEventArgs (node, args.NewText));
+ }
+ }
+
+ void OnValueEditing (object s, EditingStartedArgs args)
+ {
+ TreeIter it;
+ if (!store.GetIterFromString (out it, args.Path))
+ return;
+
+ var entry = (Entry)args.Editable;
+
+ var val = GetDebuggerObjectValueAtIter (it);
+ string strVal = null;
+ if (val != null) {
+ if (val.TypeName == "string") {
+ // HACK: we need a better abstraction of the stack frame, better yet would be to not really need it in the view
+ var opt = debuggerService.Frame.GetStackFrame().DebuggerSession.Options.EvaluationOptions.Clone ();
+ opt.EllipsizeStrings = false;
+ strVal = '"' + Mono.Debugging.Evaluation.ExpressionEvaluator.EscapeString ((string)val.GetRawValue (opt)) + '"';
+ } else {
+ strVal = val.Value;
+ }
+ }
+ if (!string.IsNullOrEmpty (strVal))
+ entry.Text = strVal;
+
+ entry.GrabFocus ();
+ OnStartEditing (args);
+ }
+
+ void OnValueEdited (object s, EditedArgs args)
+ {
+ OnEndEditing ();
+
+ if (!store.GetIterFromString (out TreeIter iter, args.Path))
+ return;
+
+ // get the node that we just edited
+ var val = GetNodeAtIter (iter);
+ var editArgs = new ObjectValueEditEventArgs (val, args.NewText);
+ NodeEditValue?.Invoke (this, editArgs);
+ if (editArgs.Response is bool b && b) {
+ SetValues (TreeIter.Zero, iter, null, val);
+ }
+ }
+
+ void OnEditingCancelled (object s, EventArgs args)
+ {
+ OnEndEditing ();
+ }
+
+ void OnStartEditing (EditingStartedArgs args)
+ {
+ editing = true;
+ editEntry = (Entry)args.Editable;
+ editEntry.KeyPressEvent += OnEditKeyPress;
+ editEntry.KeyReleaseEvent += OnEditKeyRelease;
+
+ StartEditing?.Invoke(this, EventArgs.Empty);
+ }
+
+ void OnEndEditing ()
+ {
+ editing = false;
+ editEntry.KeyPressEvent -= OnEditKeyPress;
+ editEntry.KeyReleaseEvent -= OnEditKeyRelease;
+
+ CompletionWindowManager.HideWindow ();
+ currentCompletionData = null;
+
+ EndEditing?.Invoke (this, EventArgs.Empty);
+ }
+
+ void OnEditKeyRelease (object sender, EventArgs e)
+ {
+ if (!wasHandled) {
+ CompletionWindowManager.PostProcessKeyEvent (KeyDescriptor.FromGtk (key, keyChar, modifierState));
+ PopupCompletion ((Entry) sender);
+ }
+ }
+
+ [GLib.ConnectBeforeAttribute]
+ void OnEditKeyPress (object s, KeyPressEventArgs args)
+ {
+ wasHandled = false;
+ key = args.Event.Key;
+ keyChar = (char)args.Event.Key;
+ modifierState = args.Event.State;
+ keyValue = args.Event.KeyValue;
+
+ if (currentCompletionData != null) {
+ wasHandled = CompletionWindowManager.PreProcessKeyEvent (KeyDescriptor.FromGtk (key, keyChar, modifierState));
+ args.RetVal = wasHandled;
+ }
+ }
+
+ static bool IsCompletionChar (char c)
+ {
+ return char.IsLetter (c) || c == '_' || c == '.';
+ }
+
+ CancellationTokenSource cts = new CancellationTokenSource ();
+ async void PopupCompletion (Entry entry)
+ {
+ try {
+ char c = (char)Gdk.Keyval.ToUnicode (keyValue);
+ if (currentCompletionData == null && IsCompletionChar (c)) {
+ string expr = entry.Text.Substring (0, entry.CursorPosition);
+ cts.Cancel ();
+ cts = new CancellationTokenSource ();
+ currentCompletionData = await debuggerService.GetCompletionDataAsync (expr, cts.Token);
+ if (currentCompletionData != null) {
+ var dataList = new DebugCompletionDataList (currentCompletionData);
+ ctx = ((ICompletionWidget)this).CreateCodeCompletionContext (expr.Length - currentCompletionData.ExpressionLength);
+ CompletionWindowManager.ShowWindow (null, c, dataList, this, ctx);
+ }
+ }
+
+ } catch (OperationCanceledException) {
+ }
+ }
+
+ enum PreviewButtonIcons
+ {
+ None,
+ Hidden,
+ RowHover,
+ Hover,
+ Active,
+ Selected,
+ }
+
+ bool ValidObjectForPreviewIcon (TreeIter it)
+ {
+ var obj = GetDebuggerObjectValueAtIter (it);
+ if (obj == null) {
+ return false;
+ } else {
+ if (obj.IsNull)
+ return false;
+ if (obj.IsPrimitive) {
+ //obj.DisplayValue.Contains ("|") is special case to detect enum with [Flags]
+ return obj.TypeName == "string" || (obj.DisplayValue != null && obj.DisplayValue.Contains ("|"));
+ }
+ if (string.IsNullOrEmpty (obj.TypeName))
+ return false;
+ }
+ return true;
+ }
+
+
+ protected override bool OnMotionNotifyEvent (Gdk.EventMotion evnt)
+ {
+ TreePath path;
+ if (!editing && GetPathAtPos ((int)evnt.X, (int)evnt.Y, out path)) {
+
+ TreeIter it;
+ if (store.GetIter (out it, path)) {
+ TreeViewColumn col;
+ CellRenderer cr;
+ if (GetCellAtPos ((int)evnt.X, (int)evnt.Y, out path, out col, out cr) && cr == crtExp) {
+ using (var layout = new Pango.Layout (PangoContext)) {
+ layout.FontDescription = crtExp.FontDesc.Copy ();
+ layout.FontDescription.Family = crtExp.Family;
+
+ var name = (string) store.GetValue (it, NameColumn);
+ if (!string.IsNullOrEmpty (name))
+ layout.SetText (name);
+
+ int w, h;
+ layout.GetPixelSize (out w, out h);
+ var cellArea = GetCellRendererArea (path, col, cr);
+ var iconXOffset = cellArea.X + w + cr.Xpad * 3;
+ if (iconXOffset < evnt.X &&
+ iconXOffset + 16 > evnt.X) {
+ SetPreviewButtonIcon (PreviewButtonIcons.Hover, it);
+ } else {
+ SetPreviewButtonIcon (PreviewButtonIcons.RowHover, it);
+ }
+ }
+ } else {
+ SetPreviewButtonIcon (PreviewButtonIcons.RowHover, it);
+ }
+
+ if (allowPinning) {
+ if (path.Depth > 1 || PinnedWatch == null) {
+ if (!it.Equals (lastPinIter)) {
+ store.SetValue (it, PinIconColumn, "md-pin-up");
+ CleanPinIcon ();
+ if (path.Depth > 1 || !rootPinVisible)
+ lastPinIter = it;
+ }
+ }
+ }
+ }
+ } else {
+ SetPreviewButtonIcon (PreviewButtonIcons.Hidden);
+ }
+ return base.OnMotionNotifyEvent (evnt);
+ }
+
+ void CleanPinIcon ()
+ {
+ if (!lastPinIter.Equals (TreeIter.Zero) && store.IterIsValid (lastPinIter)) {
+ store.SetValue (lastPinIter, PinIconColumn, null);
+ }
+ lastPinIter = TreeIter.Zero;
+ }
+
+ protected override bool OnLeaveNotifyEvent (Gdk.EventCrossing evnt)
+ {
+ if (!editing)
+ CleanPinIcon ();
+ SetPreviewButtonIcon (PreviewButtonIcons.Hidden);
+ return base.OnLeaveNotifyEvent (evnt);
+ }
+
+ protected override bool OnKeyPressEvent (Gdk.EventKey evnt)
+ {
+ // Ignore if editing a cell
+ if (editing)
+ return base.OnKeyPressEvent (evnt);
+
+ TreePath [] selected = Selection.GetSelectedRows ();
+ bool changed = false;
+ TreePath lastPath;
+
+ if (selected == null || selected.Length < 1)
+ return base.OnKeyPressEvent (evnt);
+
+ switch (evnt.Key) {
+ case Gdk.Key.Left:
+ case Gdk.Key.KP_Left:
+ foreach (var path in selected) {
+ lastPath = path.Copy ();
+ if (GetRowExpanded (path)) {
+ CollapseRow (path);
+ changed = true;
+ } else if (path.Up ()) {
+ Selection.UnselectPath (lastPath);
+ Selection.SelectPath (path);
+ changed = true;
+ }
+ }
+ break;
+ case Gdk.Key.Right:
+ case Gdk.Key.KP_Right:
+ foreach (var path in selected) {
+ if (!GetRowExpanded (path)) {
+ ExpandRow (path, false);
+ changed = true;
+ } else {
+ lastPath = path.Copy ();
+ path.Down ();
+ if (lastPath.Compare (path) != 0) {
+ Selection.UnselectPath (lastPath);
+ Selection.SelectPath (path);
+ changed = true;
+ }
+ }
+ }
+ break;
+ case Gdk.Key.Delete:
+ case Gdk.Key.KP_Delete:
+ case Gdk.Key.BackSpace:
+ string expression;
+ ObjectValue val;
+ TreeIter iter;
+
+ if (!AllowEditing || !AllowWatchExpressions)
+ return base.OnKeyPressEvent (evnt);
+
+ // Note: since we'll be modifying the tree, we need to make changes from bottom to top
+ Array.Sort (selected, new TreePathComparer (true));
+
+ foreach (var path in selected) {
+ if (!Model.GetIter (out iter, path))
+ continue;
+
+ var node = GetNodeAtIter (iter);
+ NodeRemoved?.Invoke (this, new ObjectValueNodeEventArgs (node));
+ changed = true;
+
+ //val = GetDebuggerObjectValueAtIter (iter);
+ //expression = GetFullExpression (iter);
+
+ //// FIXME: expressions use ObjectValues now, so this logic probably needs to change...
+
+ //// Lookup and remove
+ //if (val != null && values.Contains (val)) {
+ // RemoveValue (val);
+ // changed = true;
+ //} else if (!string.IsNullOrEmpty (expression) && controller.RemoveExpression (expression)) {
+ // changed = true;
+ //}
+ }
+ break;
+ }
+
+ return changed || base.OnKeyPressEvent (evnt);
+ }
+
+ Gdk.Rectangle GetCellRendererArea (TreePath path, TreeViewColumn col, CellRenderer cr)
+ {
+ var rect = this.GetCellArea (path, col);
+ int x, width;
+ col.CellGetPosition (cr, out x, out width);
+ return new Gdk.Rectangle (rect.X + x, rect.Y, width, rect.Height);
+ }
+
+ protected override bool OnButtonPressEvent (Gdk.EventButton evnt)
+ {
+ allowStoreColumnSizes = true;
+
+ TreeViewColumn col;
+ CellRenderer cr;
+ TreePath path;
+ bool closePreviewWindow = true;
+ bool clickProcessed = false;
+
+ TreeIter it;
+ if (this.debuggerService.CanQueryDebugger && evnt.Button == 1 && GetCellAtPos ((int)evnt.X, (int)evnt.Y, out path, out col, out cr) && store.GetIter (out it, path)) {
+ if (cr == crpViewer) {
+ clickProcessed = true;
+ var node = GetNodeAtIter (it);
+
+ var nodeArgs = new ObjectValueNodeEventArgs (node);
+ NodeShowVisualiser?.Invoke (this, nodeArgs);
+ if (nodeArgs.Response is bool b && b) {
+ SetValues (TreeIter.Zero, it, null, node);
+ }
+ } else if (cr == crtExp && !PreviewWindowManager.IsVisible && ValidObjectForPreviewIcon (it)) {
+ var val = GetDebuggerObjectValueAtIter (it);
+ startPreviewCaret = GetCellRendererArea (path, col, cr);
+ startHAdj = Hadjustment.Value;
+ startVAdj = Vadjustment.Value;
+ int w, h;
+ using (var layout = new Pango.Layout (PangoContext)) {
+ layout.FontDescription = crtExp.FontDesc.Copy ();
+ layout.FontDescription.Family = crtExp.Family;
+ layout.SetText ((string) store.GetValue (it, NameColumn));
+ layout.GetPixelSize (out w, out h);
+ }
+ startPreviewCaret.X += (int)(w + cr.Xpad * 3);
+ startPreviewCaret.Width = 16;
+ ConvertTreeToWidgetCoords (startPreviewCaret.X, startPreviewCaret.Y, out startPreviewCaret.X, out startPreviewCaret.Y);
+ startPreviewCaret.X += (int)Hadjustment.Value;
+ startPreviewCaret.Y += (int)Vadjustment.Value;
+ if (startPreviewCaret.X < evnt.X &&
+ startPreviewCaret.X + 16 > evnt.X) {
+ clickProcessed = true;
+ if (compactView) {
+ SetPreviewButtonIcon (PreviewButtonIcons.Active, it);
+ } else {
+ SetPreviewButtonIcon (PreviewButtonIcons.Selected, it);
+ }
+ DebuggingService.ShowPreviewVisualizer (val, this, startPreviewCaret);
+ closePreviewWindow = false;
+ } else {
+ if (editing)
+ base.OnButtonPressEvent (evnt);//End current editing
+ if (!Selection.IterIsSelected (it))
+ base.OnButtonPressEvent (evnt);//Select row, so base.OnButtonPressEvent below starts editing
+ }
+ } else if (cr == crtValue) {
+ if ((Platform.IsMac && ((evnt.State & Gdk.ModifierType.Mod2Mask) > 0)) ||
+ (!Platform.IsMac && ((evnt.State & Gdk.ModifierType.ControlMask) > 0))) {
+ var url = crtValue.Text.Trim ('"', '{', '}');
+ Uri uri;
+ if (url != null && Uri.TryCreate (url, UriKind.Absolute, out uri) && (uri.Scheme == "http" || uri.Scheme == "https")) {
+ clickProcessed = true;
+ IdeServices.DesktopService.ShowUrl (url);
+ }
+ }
+ } else if (cr == crtExp) {
+ if (editing)
+ base.OnButtonPressEvent (evnt);//End current editing
+ if (!Selection.IterIsSelected (it))
+ base.OnButtonPressEvent (evnt);//Select row, so base.OnButtonPressEvent below starts editing
+ } else if (!editing) {
+ if (cr == crpButton) {
+ clickProcessed = true;
+ HandleValueButton (it);
+ } else if (cr == crpPin) {
+ clickProcessed = true;
+ TreeIter pi;
+ if (PinnedWatch != null && !store.IterParent (out pi, it)) {
+ NodeUnpinned?.Invoke (this, EventArgs.Empty);
+ } else {
+ CreatePinnedWatch (it);
+ }
+ } else if (cr == crpLiveUpdate) {
+ clickProcessed = true;
+ TreeIter pi;
+ if (PinnedWatch != null && !store.IterParent (out pi, it)) {
+ DebuggingService.SetLiveUpdateMode (PinnedWatch, !PinnedWatch.LiveUpdate);
+ if (PinnedWatch.LiveUpdate)
+ store.SetValue (it, LiveUpdateIconColumn, liveIcon);
+ else
+ store.SetValue (it, LiveUpdateIconColumn, noLiveIcon);
+ }
+ }
+ }
+ }
+
+ if (closePreviewWindow) {
+ PreviewWindowManager.DestroyWindow ();
+ }
+
+ if (clickProcessed)
+ return true;
+
+ //HACK: show context menu in release event instead of show event to work around gtk bug
+ if (evnt.TriggersContextMenu ()) {
+ // ShowPopup (evnt);
+ if (!this.IsClickedNodeSelected ((int)evnt.X, (int)evnt.Y)) {
+ //pass click to base so it can update the selection
+ //unless the node is already selected, in which case we don't want to change the selection(deselect multi selection)
+ base.OnButtonPressEvent (evnt);
+ }
+ return true;
+ } else {
+ return base.OnButtonPressEvent (evnt);
+ }
+ }
+
+ protected override bool OnButtonReleaseEvent (Gdk.EventButton evnt)
+ {
+ allowStoreColumnSizes = false;
+ var res = base.OnButtonReleaseEvent (evnt);
+
+ //HACK: show context menu in release event instead of show event to work around gtk bug
+ if (evnt.IsContextMenuButton ()) {
+ ShowPopup (evnt);
+ return true;
+ }
+ return res;
+ }
+
+ protected override bool OnPopupMenu ()
+ {
+ ShowPopup (null);
+ return true;
+ }
+
+ void ShowPopup (Gdk.EventButton evt)
+ {
+ if (allowPopupMenu)
+ this.ShowContextMenu (evt, menuSet, this);
+ }
+
+ [CommandUpdateHandler (EditCommands.SelectAll)]
+ protected void UpdateSelectAll (CommandInfo cmd)
+ {
+ if (editing) {
+ cmd.Bypass = true;
+ return;
+ }
+ TreeIter iter;
+
+ cmd.Enabled = store.GetIterFirst (out iter);
+ }
+
+ [CommandHandler (EditCommands.SelectAll)]
+ protected new void OnSelectAll ()
+ {
+ if (editing) {
+ base.OnSelectAll ();
+ return;
+ }
+ Selection.SelectAll ();
+ }
+
+ [CommandHandler (EditCommands.Copy)]
+ protected void OnCopy ()
+ {
+ TreePath [] selected = Selection.GetSelectedRows ();
+ TreeIter iter;
+
+ if (selected == null || selected.Length == 0)
+ return;
+
+ if (selected.Length == 1) {
+ var editable = IdeApp.Workbench.RootWindow.Focus as Editable;
+
+ if (editable != null) {
+ editable.CopyClipboard ();
+ return;
+ }
+ }
+
+ var str = new StringBuilder ();
+ bool needsNewLine = false;
+ for (int i = 0; i < selected.Length; i++) {
+ if (!store.GetIter (out iter, selected [i]))
+ continue;
+ if (needsNewLine)
+ str.AppendLine ();
+ needsNewLine = true;
+
+ string value = (string) store.GetValue (iter, ValueColumn);
+ string type = (string) store.GetValue (iter, TypeColumn);
+ if (type == "string") {
+ var objVal = GetDebuggerObjectValueAtIter (iter);
+ if (objVal != null) {
+ // HACK: we need a better abstraction of the stack frame, better yet would be to not really need it in the view
+ var opt = debuggerService.Frame.GetStackFrame().DebuggerSession.Options.EvaluationOptions.Clone ();
+ opt.EllipsizeStrings = false;
+ value = '"' + Mono.Debugging.Evaluation.ExpressionEvaluator.EscapeString ((string)objVal.GetRawValue (opt)) + '"';
+ }
+ }
+ str.Append (value);
+ }
+
+ Clipboard.Get (Gdk.Selection.Clipboard).Text = str.ToString ();
+ }
+
+ [CommandHandler (EditCommands.Delete)]
+ [CommandHandler (EditCommands.DeleteKey)]
+ protected void OnDelete ()
+ {
+ var nodesToDelete = new List<ObjectValueNode> ();
+ foreach (var path in Selection.GetSelectedRows ()) {
+ if (!store.GetIter (out TreeIter iter, path))
+ continue;
+
+ var node = GetNodeAtIter (iter);
+ nodesToDelete.Add (node);
+ }
+
+ foreach (var node in nodesToDelete) {
+ NodeRemoved?.Invoke (this, new ObjectValueNodeEventArgs (node));
+ }
+ }
+
+ [CommandUpdateHandler (EditCommands.Delete)]
+ [CommandUpdateHandler (EditCommands.DeleteKey)]
+ protected void OnUpdateDelete (CommandInfo cinfo)
+ {
+ if (editing) {
+ cinfo.Bypass = true;
+ return;
+ }
+
+ if (!AllowWatchExpressions) {
+ cinfo.Visible = false;
+ return;
+ }
+
+ TreePath [] sel = Selection.GetSelectedRows ();
+ if (sel.Length == 0) {
+ cinfo.Enabled = false;
+ return;
+ }
+
+ foreach (TreePath tp in sel) {
+ if (tp.Depth > 1) {
+ cinfo.Enabled = false;
+ return;
+ }
+ }
+ }
+
+ [CommandHandler (DebugCommands.AddWatch)]
+ protected void OnAddWatch ()
+ {
+ var expressions = new List<string> ();
+
+ foreach (var tp in Selection.GetSelectedRows ()) {
+ TreeIter it;
+
+ if (store.GetIter (out it, tp)) {
+ var node = GetNodeAtIter (it);
+ var expression = node.Expression;
+
+ if (!string.IsNullOrEmpty (expression))
+ expressions.Add (expression);
+ }
+ }
+
+ foreach (var expression in expressions)
+ DebuggingService.AddWatch (expression);
+ }
+
+ [CommandUpdateHandler (DebugCommands.AddWatch)]
+ protected void OnUpdateAddWatch (CommandInfo cinfo)
+ {
+ cinfo.Enabled = Selection.GetSelectedRows ().Length > 0;
+ }
+
+ [CommandHandler (EditCommands.Rename)]
+ protected void OnRename ()
+ {
+ TreeIter it;
+ if (store.GetIter (out it, Selection.GetSelectedRows () [0]))
+ SetCursor (store.GetPath (it), Columns [0], true);
+ }
+
+ [CommandUpdateHandler (EditCommands.Rename)]
+ protected void OnUpdateRename (CommandInfo cinfo)
+ {
+ cinfo.Visible = AllowWatchExpressions;
+ cinfo.Enabled = Selection.GetSelectedRows ().Length == 1;
+ }
+
+ protected override void OnRowActivated (TreePath path, TreeViewColumn column)
+ {
+ base.OnRowActivated (path, column);
+
+ if (!debuggerService.CanQueryDebugger)
+ return;
+
+ TreePath [] selected = Selection.GetSelectedRows ();
+ TreeIter iter;
+
+ if (!store.GetIter (out iter, selected [0]))
+ return;
+
+ var val = GetDebuggerObjectValueAtIter (iter);
+ if (val != null && val.Name == DebuggingService.DebuggerSession.EvaluationOptions.CurrentExceptionTag)
+ DebuggingService.ShowExceptionCaughtDialog ();
+
+ if (val != null && DebuggingService.HasValueVisualizers (val))
+ DebuggingService.ShowValueVisualizer (val);
+ }
+
+
+ bool GetCellAtPos (int x, int y, out TreePath path, out TreeViewColumn col, out CellRenderer cellRenderer)
+ {
+ if (GetPathAtPos (x, y, out path, out col)) {
+ var cellArea = GetCellArea (path, col);
+ x -= cellArea.X;
+ foreach (var cr in col.CellRenderers) {
+ int xo, w;
+ col.CellGetPosition (cr, out xo, out w);
+ var visible = cr.Visible;
+ if (cr == crpViewer) {
+ if (store.GetIter (out var it, path)) {
+ visible = (bool) store.GetValue (it, ViewerButtonVisibleColumn);
+ }
+ } else if (cr == evaluateStatusCell) {
+ if (store.GetIter (out var it, path)) {
+ visible = (bool) store.GetValue (it, EvaluateStatusIconVisibleColumn);
+ }
+ } else if (cr == crpButton) {
+ if (store.GetIter (out var it, path)) {
+ visible = (bool) store.GetValue (it, ValueButtonVisibleColumn);
+ }
+ }
+ if (visible && x >= xo && x < xo + w) {
+ cellRenderer = cr;
+ return true;
+ }
+ }
+ }
+ cellRenderer = null;
+ return false;
+ }
+
+ void CreatePinnedWatch (TreeIter it)
+ {
+ var node = GetNodeAtIter (it);
+ var expression = node.Expression;
+
+ if (string.IsNullOrEmpty (expression))
+ return;
+
+ if (PinnedWatch != null)
+ CollapseAll ();
+
+ NodePinned?.Invoke (this, new ObjectValueNodeEventArgs(node));
+ }
+
+ #region ICompletionWidget implementation
+
+ CodeCompletionContext ICompletionWidget.CurrentCodeCompletionContext {
+ get {
+ return ((ICompletionWidget)this).CreateCodeCompletionContext (editEntry.Position);
+ }
+ }
+
+ public double ZoomLevel {
+ get {
+ return 1;
+ }
+ }
+
+ public event EventHandler CompletionContextChanged;
+
+ protected virtual void OnCompletionContextChanged (EventArgs e)
+ {
+ var handler = CompletionContextChanged;
+
+ if (handler != null)
+ handler (this, e);
+ }
+
+ string ICompletionWidget.GetText (int startOffset, int endOffset)
+ {
+ string text = editEntry.Text;
+
+ if (startOffset < 0 || endOffset < 0 || startOffset > endOffset || startOffset >= text.Length)
+ return "";
+
+ int length = Math.Min (endOffset - startOffset, text.Length - startOffset);
+
+ return text.Substring (startOffset, length);
+ }
+
+ void ICompletionWidget.Replace (int offset, int count, string text)
+ {
+ if (count > 0)
+ editEntry.Text = editEntry.Text.Remove (offset, count);
+ if (!string.IsNullOrEmpty (text))
+ editEntry.Text = editEntry.Text.Insert (offset, text);
+ }
+
+ int ICompletionWidget.CaretOffset {
+ get {
+ return editEntry.Position;
+ }
+ set {
+ editEntry.Position = value;
+ }
+ }
+
+ char ICompletionWidget.GetChar (int offset)
+ {
+ string txt = editEntry.Text;
+
+ return offset >= txt.Length ? '\0' : txt [offset];
+ }
+
+ CodeCompletionContext ICompletionWidget.CreateCodeCompletionContext (int triggerOffset)
+ {
+ int x, y;
+ editEntry.GdkWindow.GetOrigin (out x, out y);
+ editEntry.GetLayoutOffsets (out int tx, out int ty);
+ int cp = editEntry.TextIndexToLayoutIndex (editEntry.Position);
+ Pango.Rectangle rect = editEntry.Layout.IndexToPos (cp);
+ x += Pango.Units.ToPixels (rect.X) + tx;
+ y += editEntry.Allocation.Height;
+
+ return new CodeCompletionContext (
+ x, y, editEntry.SizeRequest ().Height,
+ triggerOffset, 0, triggerOffset,
+ currentCompletionData.ExpressionLength
+ );
+ }
+
+ string ICompletionWidget.GetCompletionText (CodeCompletionContext ctx)
+ {
+ return editEntry.Text.Substring (ctx.TriggerOffset, ctx.TriggerWordLength);
+ }
+
+ void ICompletionWidget.SetCompletionText (CodeCompletionContext ctx, string partial_word, string complete_word)
+ {
+ int cursorOffset = editEntry.Position - (ctx.TriggerOffset + partial_word.Length);
+ int sp = ctx.TriggerOffset;
+ editEntry.DeleteText (sp, sp + partial_word.Length);
+ editEntry.InsertText (complete_word, ref sp);
+ editEntry.Position = sp + cursorOffset; // sp is incremented by InsertText
+ }
+
+ void ICompletionWidget.SetCompletionText (CodeCompletionContext ctx, string partial_word, string complete_word, int offset)
+ {
+ int cursorOffset = editEntry.Position - (ctx.TriggerOffset + partial_word.Length);
+ int sp = ctx.TriggerOffset;
+ editEntry.DeleteText (sp, sp + partial_word.Length);
+ editEntry.InsertText (complete_word, ref sp);
+ editEntry.Position = sp + offset + cursorOffset; // sp is incremented by InsertText
+ }
+
+ int ICompletionWidget.TextLength {
+ get {
+ return editEntry.Text.Length;
+ }
+ }
+
+ int ICompletionWidget.SelectedLength {
+ get {
+ return 0;
+ }
+ }
+
+ Style ICompletionWidget.GtkStyle {
+ get {
+ return editEntry.Style;
+ }
+ }
+
+ #endregion
+
+ internal void SetCustomFont (Pango.FontDescription font)
+ {
+ crpButton.FontDesc = crtExp.FontDesc = crtType.FontDesc = crtValue.FontDesc = font;
+ }
+
+ #region UI support
+
+ static void ValueDataFunc (Gtk.TreeViewColumn tree_column, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter)
+ {
+ Xwt.Drawing.Color? color;
+ ObjectValue val = null;
+
+ var node = (ObjectValueNode) model.GetValue (iter, ObjectNodeColumn);
+ if (node != null) {
+ val = node.GetDebuggerObjectValue ();
+ }
+
+ if (val == null) {
+ val = GetDebuggerObjectValueAtIter (iter, model);
+ }
+
+ if (val != null && !val.IsNull && DebuggingService.HasGetConverter<Xwt.Drawing.Color> (val)) {
+ try {
+ color = DebuggingService.GetGetConverter<Xwt.Drawing.Color> (val).GetValue (val);
+ } catch (Exception) {
+ color = null;
+ }
+ } else {
+ color = null;
+ }
+
+ if (color != null) {
+ ((CellRendererColorPreview)cell).Color = (Xwt.Drawing.Color)color;
+ cell.Visible = true;
+ } else {
+ cell.Visible = false;
+ }
+ }
+
+ int GetMaxWidth (TreeViewColumn column, TreeIter iter)
+ {
+ var path = Model.GetPath (iter);
+ int x, y, w, h;
+ int columnWidth = 0;
+ column.CellSetCellData (Model, iter, false, false);
+ var area = new Gdk.Rectangle (0, 0, 1000, 1000);
+ bool firstCell = true;
+ foreach (var cellRenderer in column.CellRenderers) {
+ if (!cellRenderer.Visible)
+ continue;
+ if (!firstCell && columnWidth > 0)
+ columnWidth += column.Spacing;
+ cellRenderer.GetSize (this, ref area, out x, out y, out w, out h);
+ columnWidth += w + focus_line_width;
+ firstCell = false;
+ }
+ if (ExpanderColumn == column) {
+ columnWidth += horizontal_separator + (path.Depth - 1) * LevelIndentation;
+ if (ShowExpanders)
+ columnWidth += path.Depth * expanderSize;
+ } else {
+ columnWidth += horizontal_separator;
+ }
+ if (this.GetRowExpanded (path)) {
+ var childrenCount = Model.IterNChildren (iter);
+ for (int i = 0; i < childrenCount; i++) {
+ TreeIter childIter;
+ if (!Model.IterNthChild (out childIter, iter, i))
+ break;
+ columnWidth = Math.Max (columnWidth, GetMaxWidth (column, childIter));
+ }
+ }
+ return columnWidth;
+ }
+
+ void RecalculateWidth ()
+ {
+ if (!Model.GetIterFirst (out TreeIter iter))
+ return;
+
+ foreach (var column in new [] { expCol, valueCol }) {//No need to calculate for Type and PinIcon columns
+ // +1 is here because apperently when we calculate MaxWidth and set to FixedWidth
+ // later GTK when cacluate needed width for Label it doesn't have enough space
+ // and puts "..." to end of text thinking there is not enough space
+ // I assume this is because rounding(floating point) calculation errors
+ // hence do +1 and avoid such problems.
+ column.FixedWidth = GetMaxWidth (column, iter) + 1;
+ }
+ }
+
+ void SetPreviewButtonIcon (PreviewButtonIcons icon, TreeIter it = default (TreeIter))
+ {
+ if (PreviewWindowManager.IsVisible || editing) {
+ return;
+ }
+ if (!it.Equals (TreeIter.Zero)) {
+ if (!ValidObjectForPreviewIcon (it)) {
+ icon = PreviewButtonIcons.None;
+ }
+ }
+ if (!currentHoverIter.Equals (it)) {
+ if (!currentHoverIter.Equals (TreeIter.Zero) && store.IterIsValid (currentHoverIter)) {
+ if (ValidObjectForPreviewIcon (currentHoverIter)) {
+ if ((string) store.GetValue (currentHoverIter, PreviewIconColumn) != "md-empty")
+ store.SetValue (currentHoverIter, PreviewIconColumn, "md-empty");
+ }
+ }
+ }
+ if (!it.Equals (TreeIter.Zero) && store.IterIsValid (it)) {
+ if (icon == PreviewButtonIcons.Selected) {
+ if ((currentIcon == PreviewButtonIcons.Active ||
+ currentIcon == PreviewButtonIcons.Hover ||
+ currentIcon == PreviewButtonIcons.RowHover) && it.Equals (TreeIter.Zero)) {
+ iconBeforeSelected = currentIcon;
+ }
+ } else if (icon == PreviewButtonIcons.Active ||
+ icon == PreviewButtonIcons.Hover ||
+ icon == PreviewButtonIcons.RowHover) {
+ iconBeforeSelected = icon;
+ if (Selection.IterIsSelected (it)) {
+ icon = PreviewButtonIcons.Selected;
+ }
+ }
+
+ switch (icon) {
+ case PreviewButtonIcons.None:
+ if (store.GetValue (it, PreviewIconColumn) != null)
+ store.SetValue (it, PreviewIconColumn, null);
+ break;
+ case PreviewButtonIcons.Hidden:
+ if ((string) store.GetValue (it, PreviewIconColumn) != "md-empty")
+ store.SetValue (it, PreviewIconColumn, "md-empty");
+ break;
+ case PreviewButtonIcons.RowHover:
+ if ((string) store.GetValue (it, PreviewIconColumn) != "md-preview-normal")
+ store.SetValue (it, PreviewIconColumn, "md-preview-normal");
+ break;
+ case PreviewButtonIcons.Hover:
+ if ((string) store.GetValue (it, PreviewIconColumn) != "md-preview-hover")
+ store.SetValue (it, PreviewIconColumn, "md-preview-hover");
+ break;
+ case PreviewButtonIcons.Active:
+ if ((string) store.GetValue (it, PreviewIconColumn) != "md-preview-active")
+ store.SetValue (it, PreviewIconColumn, "md-preview-active");
+ break;
+ case PreviewButtonIcons.Selected:
+ if ((string) store.GetValue (it, PreviewIconColumn) != "md-preview-selected") {
+ store.SetValue (it, PreviewIconColumn, "md-preview-selected");
+ }
+ break;
+ }
+ currentIcon = icon;
+ currentHoverIter = it;
+ } else {
+ currentIcon = PreviewButtonIcons.None;
+ currentHoverIter = TreeIter.Zero;
+ }
+ }
+
+ void HandleSelectionChanged (object sender, EventArgs e)
+ {
+ if (!currentHoverIter.Equals (TreeIter.Zero) && store.IterIsValid (currentHoverIter)) {
+ if (Selection.IterIsSelected (currentHoverIter)) {
+ SetPreviewButtonIcon (PreviewButtonIcons.Selected, currentHoverIter);
+ } else {
+ SetPreviewButtonIcon (iconBeforeSelected, currentHoverIter);
+ }
+ }
+ }
+
+ //Don't convert this event handler to override OnSetScrollAdjustments as it causes problems
+ void HandleScrollAdjustmentsSet (object o, ScrollAdjustmentsSetArgs args)
+ {
+ if (oldHadjustment != null) {
+ oldHadjustment.ValueChanged -= UpdatePreviewPosition;
+ oldVadjustment.ValueChanged -= UpdatePreviewPosition;
+ }
+ oldHadjustment = Hadjustment;
+ oldVadjustment = Vadjustment;
+ oldHadjustment.ValueChanged += UpdatePreviewPosition;
+ oldVadjustment.ValueChanged += UpdatePreviewPosition;
+ }
+
+ void UpdatePreviewPosition (object sender, EventArgs e)
+ {
+ UpdatePreviewPosition ();
+ }
+
+ void UpdatePreviewPosition ()
+ {
+ if (startPreviewCaret.IsEmpty)
+ return;
+ var newCaret = new Gdk.Rectangle (
+ (int)(startPreviewCaret.Left + (startHAdj - Hadjustment.Value)),
+ (int)(startPreviewCaret.Top + (startVAdj - Vadjustment.Value)),
+ startPreviewCaret.Width,
+ startPreviewCaret.Height);
+ var treeViewRectangle = new Gdk.Rectangle (
+ this.VisibleRect.X - (int)Hadjustment.Value,
+ this.VisibleRect.Y - (int)Vadjustment.Value,
+ this.VisibleRect.Width,
+ this.VisibleRect.Height);
+ if (treeViewRectangle.Contains (new Gdk.Point (
+ newCaret.X + newCaret.Width / 2,
+ newCaret.Y + newCaret.Height / 2 - (compactView ? 0 : 30)))) {
+ PreviewWindowManager.RepositionWindow (newCaret);
+ } else {
+ PreviewWindowManager.DestroyWindow ();
+ }
+ }
+
+ void HandlePreviewWindowClosed (object sender, EventArgs e)
+ {
+ SetPreviewButtonIcon (PreviewButtonIcons.Hidden);
+ }
+
+ void HandleCompletionWindowClosed (object sender, EventArgs e)
+ {
+ currentCompletionData = null;
+ }
+
+ void OnColumnWidthChanged (object o, GLib.NotifyArgs args)
+ {
+ if (!columnSizesUpdating && allowStoreColumnSizes) {
+ StoreColumnSizes ();
+ }
+ }
+
+ void AdjustColumnSizes ()
+ {
+ if (!Visible || Allocation.Width <= 0 || columnSizesUpdating || compactView)
+ return;
+
+ columnSizesUpdating = true;
+
+ double width = (double)Allocation.Width;
+
+ int texp = Math.Max ((int)(width * expColWidth), 1);
+ if (texp != expCol.FixedWidth) {
+ expCol.FixedWidth = texp;
+ }
+
+ if (typeCol.Visible) {
+ int ttype = Math.Max ((int)(width * typeColWidth), 1);
+ if (ttype != typeCol.FixedWidth) {
+ typeCol.FixedWidth = ttype;
+ }
+ }
+
+ int tval = Math.Max ((int)(width * valueColWidth), 1);
+
+ if (tval != valueCol.FixedWidth) {
+ valueCol.FixedWidth = tval;
+ Application.Invoke ((o, args) => { QueueResize (); });
+ }
+
+ columnSizesUpdating = false;
+ columnsAdjusted = true;
+ }
+
+ void StoreColumnSizes ()
+ {
+ if (!IsRealized || !Visible || !columnsAdjusted || compactView)
+ return;
+
+ double width = (double)Allocation.Width;
+ expColWidth = ((double)expCol.Width) / width;
+ valueColWidth = ((double)valueCol.Width) / width;
+ if (typeCol.Visible)
+ typeColWidth = ((double)typeCol.Width) / width;
+ }
+
+ void ResetColumnSizes ()
+ {
+ expColWidth = 0.3;
+ valueColWidth = 0.5;
+ typeColWidth = 0.2;
+ }
+
+ #endregion
+
+ #region Cell renderers
+ class CellRendererTextWithIcon : CellRendererText
+ {
+ IconId icon;
+
+ [GLib.Property ("icon")]
+ public string Icon {
+ get {
+ return icon;
+ }
+ set {
+ icon = value;
+ }
+ }
+
+ Xwt.Drawing.Image img {
+ get {
+ return ImageService.GetIcon (icon, IconSize.Menu);
+ }
+ }
+
+ public override void GetSize (Widget widget, ref Gdk.Rectangle cell_area, out int x_offset, out int y_offset, out int width, out int height)
+ {
+ base.GetSize (widget, ref cell_area, out x_offset, out y_offset, out width, out height);
+ if (!icon.IsNull)
+ width += (int)(Xpad * 2 + img.Width);
+ }
+
+ protected override void Render (Gdk.Drawable window, Widget widget, Gdk.Rectangle background_area, Gdk.Rectangle cell_area, Gdk.Rectangle expose_area, CellRendererState flags)
+ {
+ base.Render (window, widget, background_area, cell_area, expose_area, flags);
+ if (!icon.IsNull) {
+ using (var ctx = Gdk.CairoHelper.Create (window)) {
+ using (var layout = new Pango.Layout (widget.PangoContext)) {
+ layout.FontDescription = IdeServices.FontService.SansFont.CopyModified (Ide.Gui.Styles.FontScale11);
+ layout.FontDescription.Family = Family;
+ layout.SetText (Text);
+ int w, h;
+ layout.GetPixelSize (out w, out h);
+ var x = cell_area.X + w + 3 * Xpad;
+ var y = cell_area.Y + cell_area.Height / 2 - (int)(img.Height / 2);
+ ctx.DrawImage (widget, img, x, y);
+ }
+ }
+ }
+ }
+ }
+
+ class ValueCellRenderer : CellRendererText
+ {
+ public bool Compact;
+
+ [GLib.Property ("texturl")]
+ public string TextUrl {
+ get {
+ return Text;
+ }
+ set {
+ Uri uri;
+
+ try {
+ if (value != null && Uri.TryCreate (value.Trim ('"', '{', '}'), UriKind.Absolute, out uri) && (uri.Scheme == "http" || uri.Scheme == "https")) {
+ Underline = Pango.Underline.Single;
+ Foreground = Ide.Gui.Styles.LinkForegroundColor.ToHexString (false);
+ } else {
+ Underline = Pango.Underline.None;
+ }
+ } catch (Exception) {
+ // MONO BUG: Uri.TryCreate() throws when unicode characters are encountered. See bug #47364
+ Underline = Pango.Underline.None;
+ }
+
+ Text = value;
+ }
+ }
+
+ public override void GetSize (Widget widget, ref Gdk.Rectangle cell_area, out int x_offset, out int y_offset, out int width, out int height)
+ {
+ if (Compact)
+ this.Ellipsize = Pango.EllipsizeMode.None;
+ base.GetSize (widget, ref cell_area, out x_offset, out y_offset, out width, out height);
+ if (Compact)
+ this.Ellipsize = Pango.EllipsizeMode.End;
+ }
+ }
+
+ class CellRendererColorPreview : CellRenderer
+ {
+ protected override void Render (Gdk.Drawable window, Widget widget, Gdk.Rectangle background_area, Gdk.Rectangle cell_area, Gdk.Rectangle expose_area, CellRendererState flags)
+ {
+ var darkColor = Color.WithIncreasedLight (-0.15);
+
+ using (Cairo.Context cr = Gdk.CairoHelper.Create (window)) {
+ double center_x = cell_area.X + Math.Round ((double)(cell_area.Width / 2d));
+ double center_y = cell_area.Y + Math.Round ((double)(cell_area.Height / 2d));
+
+ // TODO: VV: On retina this should be LineWidth = 0.5 and Arc size needs to match
+
+ // @1x:
+ cr.LineWidth = 1;
+ cr.Arc (center_x, center_y, 5.5f, 0, 2 * Math.PI);
+
+ cr.SetSourceRGBA (Color.Red, Color.Green, Color.Blue, 1);
+ cr.FillPreserve ();
+ cr.SetSourceRGBA (darkColor.Red, darkColor.Green, darkColor.Blue, 1);
+ cr.Stroke ();
+ }
+ }
+
+ public override void GetSize (Widget widget, ref Gdk.Rectangle cell_area, out int x_offset, out int y_offset, out int width, out int height)
+ {
+ x_offset = y_offset = 0;
+ height = width = 16;
+ }
+
+ public Xwt.Drawing.Color Color { get; set; }
+ }
+
+ class CellRendererRoundedButton : CellRendererText
+ {
+ const int TopBottomPadding = 1;
+
+ protected override void Render (Gdk.Drawable window, Widget widget, Gdk.Rectangle background_area, Gdk.Rectangle cell_area, Gdk.Rectangle expose_area, CellRendererState flags)
+ {
+ if (string.IsNullOrEmpty (Text)) {
+ return;
+ }
+ using (var cr = Gdk.CairoHelper.Create (window)) {
+ using (var layout = new Pango.Layout (widget.PangoContext)) {
+ layout.SetText (Text);
+ layout.FontDescription = FontDesc;
+ layout.FontDescription.Family = Family;
+ int w, h;
+ layout.GetPixelSize (out w, out h);
+ int xpad = (int)Xpad;
+ cr.RoundedRectangle (
+ cell_area.X + xpad + 0.5,
+ cell_area.Y + TopBottomPadding + 0.5,
+ w + (cell_area.Height - 2 * TopBottomPadding) - 1,
+ cell_area.Height - TopBottomPadding * 2 - 1,
+ (cell_area.Height - (TopBottomPadding * 2)) / 2);
+ cr.LineWidth = 1;
+ cr.SetSourceColor (Styles.ObjectValueTreeValuesButtonBackground.ToCairoColor ());
+ cr.FillPreserve ();
+ cr.SetSourceColor (Styles.ObjectValueTreeValuesButtonBorder.ToCairoColor ());
+ cr.Stroke ();
+
+ int YOffset = (cell_area.Height - h) / 2;
+ if (((GtkObjectValueTreeView)widget).compactView && !Platform.IsWindows)
+ YOffset += 1;
+ cr.SetSourceColor (Styles.ObjectValueTreeValuesButtonText.ToCairoColor ());
+ cr.MoveTo (cell_area.X + (cell_area.Height - TopBottomPadding * 2 + 1) / 2 + xpad,
+ cell_area.Y + YOffset);
+ cr.ShowLayout (layout);
+ }
+ }
+ }
+
+ public override void GetSize (Widget widget, ref Gdk.Rectangle cell_area, out int x_offset, out int y_offset, out int width, out int height)
+ {
+ base.GetSize (widget, ref cell_area, out x_offset, out y_offset, out width, out height);
+ x_offset = y_offset = 0;
+ if (string.IsNullOrEmpty (Text)) {
+ width = 0;
+ height = 0;
+ return;
+ }
+ using (var layout = new Pango.Layout (widget.PangoContext)) {
+ layout.SetText (Text);
+ layout.FontDescription = FontDesc;
+ layout.FontDescription.Family = Family;
+ int w, h;
+ layout.GetPixelSize (out w, out h);
+ width = w + (height - 2 * TopBottomPadding) + 2 * (int)Xpad;
+ }
+ }
+ }
+
+ #endregion
+
+ //==================================================================================================================
+
+ #region Locator methods
+ static ObjectValueNode GetNodeAtIter (TreeIter iter, TreeModel model)
+ {
+ return (ObjectValueNode) model.GetValue (iter, ObjectNodeColumn);
+ }
+
+ // TODO: clean up, maybe even remove this method
+ static ObjectValue GetDebuggerObjectValueAtIter (TreeIter iter, TreeModel model)
+ {
+ var node = GetNodeAtIter (iter, model);
+
+ return node?.GetDebuggerObjectValue ();
+ }
+
+ ObjectValueNode GetNodeAtIter (TreeIter iter)
+ {
+ return (ObjectValueNode) store.GetValue (iter, ObjectNodeColumn);
+ }
+
+ ObjectValue GetDebuggerObjectValueAtIter (TreeIter iter)
+ {
+ return GetDebuggerObjectValueAtIter (iter, store);
+ }
+
+ TreePath GetTreePathForNode (ObjectValueNode node)
+ {
+ if (allNodes.TryGetValue (node, out TreeRowReference treeRef)) {
+ if (treeRef.Valid ()) {
+ return treeRef.Path;
+ }
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Returns true if the iter of a node and it's parent can be found given the path of the node
+ /// </summary>
+ bool GetTreeIterFromNode (ObjectValueNode node, out TreeIter iter, out TreeIter parentIter)
+ {
+ parentIter = TreeIter.Zero;
+ iter = TreeIter.Zero;
+
+ if (allNodes.TryGetValue (node, out TreeRowReference treeRef)) {
+ if (treeRef.Valid ()) {
+ if (store.GetIter (out iter, treeRef.Path)) {
+ store.IterParent (out parentIter, iter);
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+ #endregion
+
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/IDebuggerService.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/IDebuggerService.cs
new file mode 100644
index 0000000000..741471c8fc
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/IDebuggerService.cs
@@ -0,0 +1,38 @@
+//
+// IDebuggerService.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+namespace MonoDevelop.Debugger
+{
+ public interface IDebuggerService
+ {
+ bool IsConnected { get; }
+ bool IsPaused { get; }
+ void NotifyVariableChanged ();
+ bool HasValueVisualizers (ObjectValueNode node);
+ bool HasInlineVisualizer (ObjectValueNode node);
+ bool ShowValueVisualizer (ObjectValueNode node);
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/IEvaluatingGroupObjectValueNode.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/IEvaluatingGroupObjectValueNode.cs
new file mode 100644
index 0000000000..e30373976e
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/IEvaluatingGroupObjectValueNode.cs
@@ -0,0 +1,47 @@
+//
+// IEvaluatingGroupObjectValueNode.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+namespace MonoDevelop.Debugger
+{
+ /// <summary>
+ /// Internal interface to support the notion that the debugging service might return a placeholder
+ /// object for all locals for instance.
+ /// </summary>
+ interface IEvaluatingGroupObjectValueNode
+ {
+ /// <summary>
+ /// Gets a value indicating whether this object that was evaulating was evaluating a group
+ /// of objects, such as locals, and whether we should replace the node with a set of new
+ /// nodes once evaulation has completed
+ /// </summary>
+ bool IsEvaluatingGroup { get; }
+
+ /// <summary>
+ /// Get an array of new objectvalue nodes that should replace the current node in the tree
+ /// </summary>
+ ObjectValueNode[] GetEvaluationGroupReplacementNodes ();
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/IObjectValueTreeView.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/IObjectValueTreeView.cs
new file mode 100644
index 0000000000..2bc96db71c
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/IObjectValueTreeView.cs
@@ -0,0 +1,157 @@
+//
+// IObjectValueTreeView.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+
+namespace MonoDevelop.Debugger
+{
+ /// <summary>
+ /// Defines the interface to the view that ObjectValueTreeViewController can interact with
+ /// </summary>
+ public interface IObjectValueTreeView
+ {
+ /// <summary>
+ /// Gets or sets a value indicating whether the user should be able to edit values in the tree
+ /// </summary>
+ bool AllowEditing { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether or not the user should be able to expand nodes in the tree
+ /// </summary>
+ bool AllowExpanding { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the user should be able to add watch expressions to the tree
+ /// </summary>
+ bool AllowWatchExpressions { get; set; }
+
+ /// <summary>
+ /// Gets or sets the pinned watch for the view. When a watch is pinned, the view should display only this value
+ /// </summary>
+ PinnedWatch PinnedWatch { get; set; }
+
+ /// <summary>
+ /// Gets a value indicating the offset required for pinned watches
+ /// </summary>
+ int PinnedWatchOffset { get; }
+
+ /// <summary>
+ /// Reloads the tree from the root node
+ /// </summary>
+ void Reload (ObjectValueNode root);
+
+ /// <summary>
+ /// Informs the view to load the children of the given node. startIndex and count may specify a range of
+ /// the children of the node to load (for when children are being paged in from an enumerable for example).
+ /// </summary>
+ void LoadNodeChildren (ObjectValueNode node, int startIndex, int count);
+
+ /// <summary>
+ /// Informs the view to load the new values into the given node, optionally replacing that node with
+ /// the set of replacement nodes. Handles the case where, for example, the "locals" is replaced
+ /// with the set of local values
+ /// </summary>
+ void LoadEvaluatedNode (ObjectValueNode node, ObjectValueNode [] replacementNodes);
+
+ /// <summary>
+ /// Triggered when the view tries to expand a node. This may trigger a load of
+ /// the node's children
+ /// </summary>
+ event EventHandler<ObjectValueNodeEventArgs> NodeExpand;
+
+ /// <summary>
+ /// Triggered when the view tries to collapse a node.
+ /// </summary>
+ event EventHandler<ObjectValueNodeEventArgs> NodeCollapse;
+
+ /// <summary>
+ /// Triggered when the view requests a node to fetch more of it's children
+ /// </summary>
+ event EventHandler<ObjectValueNodeEventArgs> NodeLoadMoreChildren;
+
+ /// <summary>
+ /// Triggered when the view needs the node to be refreshed
+ /// </summary>
+ event EventHandler<ObjectValueNodeEventArgs> NodeRefresh;
+
+ /// <summary>
+ /// Triggered when the view needs to know if the node can be edited
+ /// </summary>
+ event EventHandler<ObjectValueNodeEventArgs> NodeGetCanEdit;
+
+ /// <summary>
+ /// Triggered when the node's value has been edited by the user
+ /// </summary>
+ event EventHandler<ObjectValueEditEventArgs> NodeEditValue;
+
+ /// <summary>
+ /// Triggered when the user removes a node (an expression)
+ /// </summary>
+ event EventHandler<ObjectValueNodeEventArgs> NodeRemoved;
+
+ /// <summary>
+ /// Triggered when the user pins the node
+ /// </summary>
+ event EventHandler<ObjectValueNodeEventArgs> NodePinned;
+
+ /// <summary>
+ /// Triggered when the pinned watch is removed by the user
+ /// </summary>
+ event EventHandler<EventArgs> NodeUnpinned;
+
+ /// <summary>
+ /// Triggered when the visualiser for the node should be shown
+ /// </summary>
+ event EventHandler<ObjectValueNodeEventArgs> NodeShowVisualiser;
+
+ //event EventHandler<ObjectValueDisplayEventArgs> NodeGetDisplayText;
+
+ /// <summary>
+ /// Triggered when an expression is added to the tree by the user
+ /// </summary>
+ event EventHandler<ObjectValueExpressionEventArgs> ExpressionAdded;
+
+ /// <summary>
+ /// Triggered when an expression is edited by the user
+ /// </summary>
+ event EventHandler<ObjectValueExpressionEventArgs> ExpressionEdited;
+
+ /// <summary>
+ /// Informs the view that the node was expanded and children have been loaded.
+ /// </summary>
+ void OnNodeExpanded (ObjectValueNode node);
+
+ /// <summary>
+ /// Triggered when the user starts editing a node
+ /// </summary>
+ event EventHandler StartEditing;
+
+ /// <summary>
+ /// Triggered when the user stops editing a node
+ /// </summary>
+ event EventHandler EndEditing;
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/IStackFrame.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/IStackFrame.cs
new file mode 100644
index 0000000000..d603fb3d44
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/IStackFrame.cs
@@ -0,0 +1,40 @@
+//
+// IStackFrame.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+
+using Mono.Debugging.Client;
+
+namespace MonoDevelop.Debugger
+{
+ public interface IStackFrame
+ {
+ EvaluationOptions CloneSessionEvaluationOpions ();
+ ObjectValueNode EvaluateExpression (string expression);
+ ObjectValueNode[] EvaluateExpressions (IList<string> expressions);
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ISupportChildObjectValueNodeReplacement.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ISupportChildObjectValueNodeReplacement.cs
new file mode 100644
index 0000000000..b83b68df80
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ISupportChildObjectValueNodeReplacement.cs
@@ -0,0 +1,40 @@
+//
+// ISupportChildObjectValueNodeReplacement.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+namespace MonoDevelop.Debugger
+{
+ /// <summary>
+ /// Internal interface to support the notion that the debugging service might return a placeholder
+ /// object for all locals for instance.
+ /// </summary>
+ interface ISupportChildObjectValueNodeReplacement
+ {
+ /// <summary>
+ /// Replaces the given child node with a new set of nodes
+ /// </summary>
+ void ReplaceChildNode (ObjectValueNode node, ObjectValueNode[] newNodes);
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueDebuggerService.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueDebuggerService.cs
new file mode 100644
index 0000000000..4c40fa14ab
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueDebuggerService.cs
@@ -0,0 +1,70 @@
+//
+// ObjectValueDebuggerService.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+namespace MonoDevelop.Debugger
+{
+ sealed class ObjectValueDebuggerService : IDebuggerService
+ {
+ public bool IsConnected => DebuggingService.IsConnected;
+
+ public bool IsPaused => DebuggingService.IsPaused;
+
+ public void NotifyVariableChanged ()
+ {
+ DebuggingService.NotifyVariableChanged ();
+ }
+
+ public bool HasValueVisualizers (ObjectValueNode node)
+ {
+ var val = node.GetDebuggerObjectValue ();
+ if (val != null) {
+ return DebuggingService.HasValueVisualizers (val);
+ }
+
+ return false;
+ }
+
+ public bool HasInlineVisualizer (ObjectValueNode node)
+ {
+ var val = node.GetDebuggerObjectValue ();
+ if (val != null) {
+ return DebuggingService.HasInlineVisualizer (val);
+ }
+
+ return false;
+ }
+
+ public bool ShowValueVisualizer (ObjectValueNode node)
+ {
+ var val = node.GetDebuggerObjectValue ();
+ if (val != null) {
+ return DebuggingService.ShowValueVisualizer (val);
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueNode.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueNode.cs
new file mode 100644
index 0000000000..a30a300b0b
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueNode.cs
@@ -0,0 +1,257 @@
+//
+// ObjectValueNode.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+
+using Mono.Debugging.Client;
+
+namespace MonoDevelop.Debugger
+{
+ /// <summary>
+ /// Base class for ObjectValue nodes.
+ /// </summary>
+ public abstract class ObjectValueNode
+ {
+ readonly List<ObjectValueNode> children = new List<ObjectValueNode> ();
+
+ protected ObjectValueNode (string name)
+ {
+ Name = name;
+ }
+
+ /// <summary>
+ /// Gets the parent node.
+ /// </summary>
+ public ObjectValueNode Parent { get; set; }
+
+ /// <summary>
+ /// Gets the name of the object
+ /// </summary>
+ public string Name { get; }
+
+ /// <summary>
+ /// Gets the "path" of the object ("root object/parent object/variable name").
+ /// </summary>
+ public string Path {
+ get {
+ if (Parent != null)
+ return Parent.Path + "/" + Name;
+
+ return "/" + Name;
+ }
+ }
+
+ /// <summary>
+ /// Gets the expression for the node that can be used when pinning node
+ /// </summary>
+ public virtual string Expression {
+ get {
+ return Name;
+ }
+ }
+
+ /// <summary>
+ /// Gets the collection of children that have been loaded from the debugger
+ /// </summary>
+ public IReadOnlyList<ObjectValueNode> Children => children;
+
+ /// <summary>
+ /// Gets a value indicating whether the node has children or not.
+ /// The children may not yet be loaded and Children may return 0 items if not loaded
+ /// </summary>
+ public virtual bool HasChildren => false;
+
+ /// <summary>
+ /// Gets a value indicating whether all children for this node have been loaded from the debugger
+ /// </summary>
+ public bool ChildrenLoaded { get; private set; }
+
+ // TODO: make the setter private and get the node to do the expansion
+ /// <summary>
+ /// Gets or sets a value indicating whether the node is expanded
+ /// </summary>
+ public virtual bool IsExpanded { get; set; }
+
+ /// <summary>
+ /// Gets a value indicating whether the object is an enumerable
+ /// </summary>
+ public virtual bool IsEnumerable => false;
+
+ /// <summary>
+ /// Gets a value indicating whether the debugger is still evaluating the object
+ /// </summary>
+ public virtual bool IsEvaluating => false;
+
+ /// <summary>
+ /// Gets a value indicating whether the value can be edited by the user or not
+ /// </summary>
+ public virtual bool CanEdit => false;
+
+ public virtual string DisplayValue => string.Empty;
+
+ public virtual bool IsUnknown => false;
+ public virtual bool IsReadOnly => false;
+ public virtual bool IsError => false;
+ public virtual bool IsNotSupported => false;
+ public virtual string Value => string.Empty;
+ public virtual bool IsImplicitNotSupported => false;
+ public virtual ObjectValueFlags Flags => ObjectValueFlags.None;
+ public virtual bool IsNull => false;
+ public virtual bool IsPrimitive => false;
+ public virtual string TypeName => string.Empty;
+ public virtual bool CanRefresh => false;
+ public virtual bool HasFlag (ObjectValueFlags flag) => false;
+
+ /// <summary>
+ /// Fired when the value of the object has changed
+ /// </summary>
+ public event EventHandler ValueChanged;
+
+ /// <summary>
+ /// Attempts to set the value of the node to newValue
+ /// </summary>
+ public virtual void SetValue (string newValue)
+ {
+ }
+
+ /// <summary>
+ /// Tells the object to refresh its values from the debugger
+ /// </summary>
+ public virtual void Refresh ()
+ {
+ }
+
+ /// <summary>
+ /// Tells the object to refresh its values from the debugger
+ /// </summary>
+ public virtual void Refresh (EvaluationOptions options)
+ {
+ }
+
+ /// <summary>
+ /// Asynchronously loads all children for the node into Children.
+ /// The task will complete immediately if all the children have previously been loaded and
+ /// the debugger will not be re-queried
+ /// </summary>
+ public async Task<int> LoadChildrenAsync (CancellationToken cancellationToken)
+ {
+ if (!ChildrenLoaded) {
+ var loadedChildren = await OnLoadChildrenAsync (cancellationToken);
+ AddChildren (loadedChildren);
+ ChildrenLoaded = true;
+
+ return loadedChildren.Count ();
+ }
+
+ return 0;
+ }
+
+ /// <summary>
+ /// Asynchronously loads a range of at most count children for the node into Children.
+ /// Subsequent calls will load an additional count children into Children until all children
+ /// have been loaded.
+ /// The task will complete immediately if all the children have previously been loaded and
+ /// the debugger will not be re-queried.
+ /// </summary>
+ public async Task<int> LoadChildrenAsync (int count, CancellationToken cancellationToken)
+ {
+ if (!ChildrenLoaded) {
+ var loadedChildren = await OnLoadChildrenAsync (children.Count, count, cancellationToken);
+ AddChildren (loadedChildren.Item1);
+ ChildrenLoaded = loadedChildren.Item2;
+
+ return loadedChildren.Item1.Count ();
+ }
+
+ return 0;
+ }
+
+ protected void AddChild (ObjectValueNode value)
+ {
+ value.Parent = this;
+ children.Add (value);
+ }
+
+ protected void AddChildren (IEnumerable<ObjectValueNode> values)
+ {
+ foreach (var value in values)
+ AddChild (value);
+ }
+
+ protected void RemoveChildAt (int index)
+ {
+ var child = children[index];
+ children.RemoveAt (index);
+ child.Parent = null;
+ }
+
+ protected void ReplaceChildAt (int index, ObjectValueNode value)
+ {
+ var child = children[index];
+ children[index] = value;
+ value.Parent = this;
+ child.Parent = null;
+ }
+
+ protected void InsertChildAt (int index, ObjectValueNode value)
+ {
+ children.Insert (index, value);
+ value.Parent = this;
+ }
+
+ protected void ClearChildren ()
+ {
+ foreach (var child in children)
+ child.Parent = null;
+
+ children.Clear ();
+
+ ChildrenLoaded = false;
+ }
+
+ protected virtual Task<IEnumerable<ObjectValueNode>> OnLoadChildrenAsync (CancellationToken cancellationToken)
+ {
+ return Task.FromResult (Enumerable.Empty<ObjectValueNode> ());
+ }
+
+ /// <summary>
+ /// Returns the children that were loaded and a bool indicating whether all children have now been loaded
+ /// </summary>
+ protected virtual Task<Tuple<IEnumerable<ObjectValueNode>, bool>> OnLoadChildrenAsync (int index, int count, CancellationToken cancellationToken)
+ {
+ return Task.FromResult (Tuple.Create (Enumerable.Empty<ObjectValueNode> (), true));
+ }
+
+ protected void OnValueChanged (EventArgs e)
+ {
+ ValueChanged?.Invoke (this, e);
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueNodeEventArgs.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueNodeEventArgs.cs
new file mode 100644
index 0000000000..64951fbd09
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueNodeEventArgs.cs
@@ -0,0 +1,92 @@
+//
+// ObjectValueNodeEventArgs.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+
+namespace MonoDevelop.Debugger
+{
+ public class ObjectValueNodeEventArgs : EventArgs
+ {
+ public ObjectValueNodeEventArgs (ObjectValueNode node)
+ {
+ Node = node;
+ }
+
+ public ObjectValueNode Node {
+ get; private set;
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating a response to the caller of the event
+ /// </summary>
+ public object Response {
+ get; set;
+ }
+ }
+
+ public sealed class ObjectValueEditEventArgs : ObjectValueNodeEventArgs
+ {
+ public ObjectValueEditEventArgs (ObjectValueNode node, string newValue) : base (node)
+ {
+ NewValue = newValue;
+ }
+
+ public string NewValue {
+ get; private set;
+ }
+ }
+
+ public sealed class ObjectValueDisplayEventArgs : ObjectValueNodeEventArgs
+ {
+ public ObjectValueDisplayEventArgs (ObjectValueNode node) : base (node)
+ {
+ }
+
+ public string DisplayValue {
+ get; private set;
+ }
+
+ public bool HasVisualisers {
+ get; set;
+ }
+
+ public bool HasChangedSinceLastCheckpoint {
+ get; set;
+ }
+ }
+
+ public sealed class ObjectValueExpressionEventArgs : ObjectValueNodeEventArgs
+ {
+ public ObjectValueExpressionEventArgs (ObjectValueNode node, string expression) : base(node)
+ {
+ Expression = expression;
+ }
+
+ public string Expression {
+ get; private set;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueStackFrame.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueStackFrame.cs
new file mode 100644
index 0000000000..7e8880ab22
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueStackFrame.cs
@@ -0,0 +1,102 @@
+//
+// ObjectValueStackFrame.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+using Mono.Debugging.Client;
+
+namespace MonoDevelop.Debugger
+{
+ sealed class ProxyStackFrame : IStackFrame
+ {
+ readonly Dictionary<string, ObjectValue> cachedValues = new Dictionary<string, ObjectValue> ();
+
+ public ProxyStackFrame (StackFrame frame)
+ {
+ StackFrame = frame;
+ }
+
+ public StackFrame StackFrame {
+ get; private set;
+ }
+
+ public EvaluationOptions CloneSessionEvaluationOpions ()
+ {
+ return StackFrame.DebuggerSession.Options.EvaluationOptions.Clone ();
+ }
+
+ public ObjectValueNode EvaluateExpression (string expression)
+ {
+ if (cachedValues.TryGetValue (expression, out var value))
+ return new DebuggerObjectValueNode (value);
+
+ if (StackFrame != null)
+ value = StackFrame.GetExpressionValue (expression, true);
+ else
+ value = ObjectValue.CreateUnknown (expression);
+
+ cachedValues[expression] = value;
+
+ return new DebuggerObjectValueNode (value);
+ }
+
+ public ObjectValueNode[] EvaluateExpressions (IList<string> expressions)
+ {
+ var values = new ObjectValue[expressions.Count];
+ var unknown = new List<string> ();
+
+ for (int i = 0; i < expressions.Count; i++) {
+ if (!cachedValues.TryGetValue (expressions[i], out var value))
+ unknown.Add (expressions[i]);
+ else
+ values[i] = value;
+ }
+
+ ObjectValue[] qvalues;
+
+ if (StackFrame != null) {
+ qvalues = StackFrame.GetExpressionValues (unknown.ToArray (), true);
+ } else {
+ qvalues = new ObjectValue[unknown.Count];
+ for (int i = 0; i < qvalues.Length; i++)
+ qvalues[i] = ObjectValue.CreateUnknown (unknown[i]);
+ }
+
+ for (int i = 0, v = 0; i < values.Length; i++) {
+ if (values[i] == null) {
+ var value = qvalues[v++];
+
+ cachedValues[expressions[i]] = value;
+ values[i] = value;
+ }
+ }
+
+ return values.Select (v => new DebuggerObjectValueNode (v)).ToArray ();
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValueTreeView.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeView.cs
index d0eea4577f..e275996801 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValueTreeView.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeView.cs
@@ -314,6 +314,7 @@ namespace MonoDevelop.Debugger
Model = store;
SearchColumn = -1; // disable the interactive search
RulesHint = true;
+ HeadersVisible = true;
EnableSearch = false;
AllowPopupMenu = true;
Selection.Mode = Gtk.SelectionMode.Multiple;
@@ -627,7 +628,7 @@ namespace MonoDevelop.Debugger
{
if (!Visible || Allocation.Width <= 0 || columnSizesUpdating || compact)
return;
-
+
columnSizesUpdating = true;
double width = (double) Allocation.Width;
@@ -684,12 +685,12 @@ namespace MonoDevelop.Debugger
}
}
- public void SaveState ()
+ void SaveState ()
{
state.Save ();
}
- public void LoadState ()
+ void LoadState ()
{
restoringState = true;
state.Load ();
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeViewController.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeViewController.cs
new file mode 100644
index 0000000000..94339d561c
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeViewController.cs
@@ -0,0 +1,970 @@
+//
+// ObjectValueTreeViewController.cs
+//
+// Author:
+// gregm <gregm@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+
+using Mono.Debugging.Client;
+
+using MonoDevelop.Core;
+
+namespace MonoDevelop.Debugger
+{
+ public interface IObjectValueDebuggerService
+ {
+ bool CanQueryDebugger { get; }
+ IStackFrame Frame { get; }
+ Task<Mono.Debugging.Client.CompletionData> GetCompletionDataAsync (string expression, CancellationToken token);
+ }
+
+ public class ObjectValueTreeViewController : IObjectValueDebuggerService
+ {
+ readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource ();
+ public const int MaxEnumerableChildrenToFetch = 20;
+
+ IObjectValueTreeView view;
+ IDebuggerService debuggerService;
+ bool allowWatchExpressions;
+ bool allowEditing;
+ bool allowExpanding = true;
+
+ /// <summary>
+ /// Holds a dictionary of tasks that are fetching children values of the given node
+ /// </summary>
+ readonly Dictionary<ObjectValueNode, Task<int>> childFetchTasks = new Dictionary<ObjectValueNode, Task<int>> ();
+
+ /// <summary>
+ /// Holds a dictionary of arbitrary objects for nodes that are currently "Evaluating" by the debugger
+ /// When the node has completed evaluation ValueUpdated event will be fired, passing the given object
+ /// </summary>
+ readonly Dictionary<ObjectValueNode, object> evaluationWatches = new Dictionary<ObjectValueNode, object> ();
+
+ /// <summary>
+ /// Holds a dictionary of node paths and the values. Used to show values that have changed from one frame to the next.
+ /// </summary>
+ readonly Dictionary<string, CheckpointState> oldValues = new Dictionary<string, CheckpointState> ();
+
+ public ObjectValueTreeViewController ()
+ {
+ }
+
+ public IDebuggerService Debugger {
+ get {
+ if (debuggerService == null) {
+ debuggerService = OnGetDebuggerService ();
+ }
+
+ return debuggerService;
+ }
+ }
+
+ public ObjectValueNode Root { get; private set; }
+
+ public IStackFrame Frame { get; set; }
+
+ /// <summary>
+ /// Gets a value indicating whether the user should be able to edit values in the tree
+ /// </summary>
+ public bool AllowEditing {
+ get => allowEditing;
+ set {
+ allowEditing = value;
+ if (view != null) {
+ view.AllowEditing = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether or not the user should be able to expand nodes in the tree.
+ /// </summary>
+ public bool AllowExpanding {
+ get => allowExpanding;
+ set {
+ allowExpanding = value;
+ if (view != null) {
+ view.AllowExpanding = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether the user should be able to add watch expressions to the tree
+ /// </summary>
+ public bool AllowWatchExpressions {
+ get => allowWatchExpressions;
+ set {
+ allowWatchExpressions = value;
+ if (view != null) {
+ view.AllowWatchExpressions = value;
+ }
+ }
+ }
+
+ public PinnedWatch PinnedWatch {
+ get { return view?.PinnedWatch; }
+ set {
+ if (view != null) {
+ view.PinnedWatch = value;
+ }
+ }
+ }
+
+ public string PinnedWatchFile {
+ get; set;
+ }
+
+ public int PinnedWatchLine {
+ get; set;
+ }
+
+ public bool CanQueryDebugger {
+ get {
+ return Debugger.IsConnected && Debugger.IsPaused;
+ }
+ }
+
+ public object GetControl (bool headersVisible = true, bool compactView = false, bool allowPinning = false, bool allowPopupMenu = true, bool rootPinVisible = false)
+ {
+ if (view != null)
+ throw new InvalidOperationException ("You can only get the control once for each controller instance");
+
+ view = new GtkObjectValueTreeView (this, this, AllowEditing, headersVisible, AllowWatchExpressions, compactView, allowPinning, allowPopupMenu, rootPinVisible) {
+ AllowExpanding = this.AllowExpanding,
+ PinnedWatch = this.PinnedWatch,
+ };
+
+
+ view.NodeExpand += OnViewNodeExpand;
+ view.NodeCollapse += OnViewNodeCollapse;
+ view.NodeLoadMoreChildren += OnViewNodeLoadMoreChildren;
+ view.ExpressionAdded += OnViewExpressionAdded;
+ view.ExpressionEdited += OnViewExpressionEdited;
+ view.NodeRefresh += OnViewNodeRefresh;
+ view.NodeGetCanEdit += OnViewNodeCanEdit;
+ view.NodeEditValue += OnViewNodeEditValue;
+ view.NodeRemoved += OnViewNodeRemoved;
+ view.NodePinned += OnViewNodePinned;
+ view.NodeUnpinned += OnViewNodeUnpinned;
+ view.NodeShowVisualiser += OnViewNodeShowVisualiser;
+
+ return view;
+ }
+
+ public void CancelAsyncTasks ()
+ {
+ cancellationTokenSource.Cancel ();
+ }
+
+ /// <summary>
+ /// Clears the controller of nodes and resets the root to a new empty node
+ /// </summary>
+ public void ClearValues ()
+ {
+ Root = OnCreateRoot ();
+
+ Runtime.RunInMainThread (() => {
+ view.Reload (Root);
+ }).Ignore ();
+ }
+
+ /// <summary>
+ /// Clear everything
+ /// </summary>
+ public void ClearAll ()
+ {
+ ClearEvaluationCompletionRegistrations ();
+ ClearValues ();
+ }
+
+ /// <summary>
+ /// Adds values to the root node, eg locals or watch expressions
+ /// </summary>
+ public void AddValue (ObjectValueNode value)
+ {
+ if (Root == null) {
+ Root = OnCreateRoot ();
+ }
+
+ ((RootObjectValueNode)Root).AddValue (value);
+ RegisterNode (value);
+
+ Runtime.RunInMainThread (() => {
+ view.Reload (Root);
+ }).Ignore ();
+ }
+
+ /// <summary>
+ /// Adds values to the root node, eg locals or watch expressions
+ /// </summary>
+ public void AddValues (IEnumerable<ObjectValueNode> values)
+ {
+ if (Root == null) {
+ Root = OnCreateRoot ();
+ }
+
+ var nodes = values.ToList ();
+ ((RootObjectValueNode)Root).AddValues (nodes);
+
+ foreach (var node in nodes) {
+ RegisterNode (node);
+ }
+
+ Runtime.RunInMainThread (() => {
+ view.Reload (Root);
+ }).Ignore ();
+ }
+
+ public async Task<Mono.Debugging.Client.CompletionData> GetCompletionDataAsync (string expression, CancellationToken token)
+ {
+ if (CanQueryDebugger && Frame != null) {
+ // TODO: improve how we get at the underlying real stack frame
+ return await DebuggingService.GetCompletionDataAsync (Frame.GetStackFrame (), expression, token);
+ }
+
+ return null;
+ }
+
+ void CreatePinnedWatch (string expression, int height)
+ {
+ var watch = new PinnedWatch ();
+
+ if (PinnedWatch != null) {
+ watch.File = PinnedWatch.File;
+ watch.Line = PinnedWatch.Line;
+ watch.OffsetX = PinnedWatch.OffsetX;
+ watch.OffsetY = PinnedWatch.OffsetY + height + 5;
+ } else {
+ watch.File = PinnedWatchFile;
+ watch.Line = PinnedWatchLine;
+ watch.OffsetX = -1; // means that the watch should be placed at the line coordinates defined by watch.Line
+ watch.OffsetY = -1;
+ }
+
+ watch.Expression = expression;
+ DebuggingService.PinnedWatches.Add (watch);
+ }
+
+ void RemovePinnedWatch ()
+ {
+ DebuggingService.PinnedWatches.Remove (PinnedWatch);
+ }
+
+ void RemoveValue (ObjectValueNode node)
+ {
+ UnregisterNode (node);
+ OnEvaluationCompleted (node, new ObjectValueNode [0]);
+ }
+
+ // TODO: can we improve this
+ public string GetDisplayValueWithVisualisers (ObjectValueNode node, out bool showViewerButton)
+ {
+ showViewerButton = false;
+ if (node == null)
+ return null;
+
+ string result;
+ showViewerButton = !node.IsNull && Debugger.HasValueVisualizers (node);
+
+ if (!node.IsNull && Debugger.HasInlineVisualizer (node)) {
+ try {
+ result = node.GetInlineVisualisation ();
+ } catch (Exception) {
+ result = node.GetDisplayValue ();
+ }
+ } else {
+ result = node.GetDisplayValue ();
+ }
+
+ return result;
+ }
+
+ #region Checkpoints
+ public void ChangeCheckpoint ()
+ {
+ // clear old values,
+ // iterate over all the nodes and store the values so we can compare
+ // on the next update
+ oldValues.Clear ();
+ if (Root != null) {
+ ChangeCheckpoint (Root);
+ }
+ }
+
+ public void ResetChangeTracking ()
+ {
+ oldValues.Clear ();
+ }
+
+ /// <summary>
+ /// Returns true if the value of the node is different from it's last value
+ /// at the last checkpoint. Returns false if the node was not scanned at the
+ /// last checkpoint
+ /// </summary>
+ public bool GetNodeHasChangedSinceLastCheckpoint (ObjectValueNode node)
+ {
+ if (oldValues.TryGetValue (node.Path, out CheckpointState checkpointState)) {
+ return node.Value != checkpointState.Value;
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Returns true if the node was expanded when the last checkpoint was made
+ /// </summary>
+ public bool GetNodeWasExpandedAtLastCheckpoint (ObjectValueNode node)
+ {
+ if (oldValues.TryGetValue (node.Path, out CheckpointState checkpointState)) {
+ return checkpointState.Expanded;
+ }
+
+ return false;
+ }
+ #endregion
+
+ #region Expressions
+
+ public void AddExpression (string expression)
+ {
+ if (!AllowWatchExpressions)
+ return;
+
+ var node = Frame.EvaluateExpression (expression);
+ AddValue (node);
+ }
+
+ public void AddExpressions (IList<string> expressions)
+ {
+ if (!AllowWatchExpressions)
+ return;
+
+ if (Frame != null) {
+ var nodes = Frame.EvaluateExpressions (expressions);
+ AddValues (nodes);
+ }
+ }
+
+ bool EditExpression (ObjectValueNode node, string newExpression)
+ {
+ if (node.Name == newExpression)
+ return false;
+
+ UnregisterNode (node);
+ if (string.IsNullOrEmpty (newExpression)) {
+ // we want the expression removed from the tree
+ OnEvaluationCompleted (node, new ObjectValueNode [0]);
+ return true;
+ }
+
+ var expressionNode = Frame.EvaluateExpression (newExpression);
+ RegisterNode (expressionNode);
+ OnEvaluationCompleted (node, new ObjectValueNode [1] { expressionNode });
+
+ return true;
+ }
+ #endregion
+
+ /// <summary>
+ /// Returns true if the node can be edited
+ /// </summary>
+ bool CanEditObject (ObjectValueNode node)
+ {
+ if (AllowEditing) {
+ if (node.IsUnknown) {
+ if (Frame != null) {
+ return false;
+ }
+ }
+
+ return node.CanEdit;
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Edits the value of the node and returns a value indicating whether the node's value changed from
+ /// when the node was initially loaded from the debugger
+ /// </summary>
+ bool EditNodeValue (ObjectValueNode node, string newValue)
+ {
+ if (node == null || !AllowEditing)
+ return false;
+
+ try {
+ if (node.Value == newValue)
+ return false;
+
+ // make sure we set an old value for this node so we can show that it has changed
+ if (!oldValues.TryGetValue (node.Path, out CheckpointState state)) {
+ oldValues [node.Path] = new CheckpointState (node);
+ }
+
+ // ensure the parent and node are in the checkpoint and expanded
+ // so that the tree expands the node we just edited when refreshed
+ EnsureNodeIsExpandedInCheckpoint (node);
+
+ node.SetValue (newValue);
+ } catch (Exception ex) {
+ LoggingService.LogError ($"Could not set value for object '{node.Name}'", ex);
+ return false;
+ }
+
+ // now, refresh the parent
+ var parent = node.Parent; /*FindNode (node.ParentId);*/
+ if (parent != null) {
+ parent.Refresh ();
+ RegisterForEvaluationCompletion (parent, true);
+ }
+
+ // the locals pad, for example, will reload all the values once this is fired
+ // prior to reloading, a new checkpoint will be made
+ Debugger.NotifyVariableChanged ();
+
+ return true;
+ }
+
+ bool ShowNodeValueVisualizer (ObjectValueNode node)
+ {
+ if (node != null) {
+
+ // make sure we set an old value for this node so we can show that it has changed
+ if (!oldValues.TryGetValue (node.Path, out CheckpointState state)) {
+ oldValues [node.Path] = new CheckpointState (node);
+ }
+
+ // ensure the parent and node are in the checkpoint and expanded
+ // so that the tree expands the node we just edited when refreshed
+ EnsureNodeIsExpandedInCheckpoint (node);
+
+ if (Debugger.ShowValueVisualizer (node)) {
+ // the value of the node changed so now refresh the parent
+ var parent = node.Parent; /*FindNode (node.ParentId);*/
+ if (parent != null) {
+ parent.Refresh ();
+ RegisterForEvaluationCompletion (parent, true);
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ void EnsureNodeIsExpandedInCheckpoint (ObjectValueNode node)
+ {
+ var parent = node.Parent; /*FindNode (node.ParentId);*/
+
+ while (parent != null && parent != Root) {
+ if (oldValues.TryGetValue (parent.Path, out CheckpointState state)) {
+ state.Expanded = true;
+ } else {
+ oldValues [parent.Path] = new CheckpointState (parent) { Expanded = true };
+ }
+
+ parent = parent.Parent; /*FindNode (parent.ParentId);*/
+ }
+ }
+
+ void RefreshNode (ObjectValueNode node)
+ {
+ if (node == null)
+ return;
+
+ if (CanQueryDebugger && Frame != null) {
+ UnregisterForEvaluationCompletion (node);
+
+ var options = Frame.CloneSessionEvaluationOpions ();
+ options.AllowMethodEvaluation = true;
+ options.AllowToStringCalls = true;
+ options.AllowTargetInvoke = true;
+ options.EllipsizeStrings = false;
+
+ node.Refresh (options);
+
+ RegisterForEvaluationCompletion (node);
+ }
+ }
+
+ #region View event handlers
+ void OnViewNodeExpand (object sender, ObjectValueNodeEventArgs e)
+ {
+ ExpandNodeAsync (e.Node).Ignore ();
+ }
+
+ void OnViewNodeCollapse (object sender, ObjectValueNodeEventArgs e)
+ {
+ e.Node.IsExpanded = false;
+ }
+
+ void OnViewNodeLoadMoreChildren (object sender, ObjectValueNodeEventArgs e)
+ {
+ FetchMoreChildrenAsync (e.Node).Ignore ();
+ }
+
+ void OnViewExpressionAdded (object sender, ObjectValueExpressionEventArgs e)
+ {
+ AddExpression (e.Expression);
+ }
+
+ void OnViewExpressionEdited (object sender, ObjectValueExpressionEventArgs e)
+ {
+ EditExpression (e.Node, e.Expression);
+ }
+
+ void OnViewNodeRefresh (object sender, ObjectValueNodeEventArgs e)
+ {
+ RefreshNode (e.Node);
+ }
+
+ void OnViewNodeCanEdit (object sender, ObjectValueNodeEventArgs e)
+ {
+ e.Response = CanEditObject (e.Node);
+ }
+
+ void OnViewNodeEditValue (object sender, ObjectValueEditEventArgs e)
+ {
+ e.Response = EditNodeValue (e.Node, e.NewValue);
+ }
+
+ void OnViewNodeRemoved (object sender, ObjectValueNodeEventArgs e)
+ {
+ RemoveValue (e.Node);
+ }
+
+ void OnViewNodeShowVisualiser (object sender, ObjectValueNodeEventArgs e)
+ {
+ e.Response = ShowNodeValueVisualizer (e.Node);
+ }
+
+ void OnViewNodePinned (object sender, ObjectValueNodeEventArgs e)
+ {
+ CreatePinnedWatch (e.Node.Expression, view.PinnedWatchOffset);
+ }
+
+ void OnViewNodeUnpinned (object sender, EventArgs e)
+ {
+ RemovePinnedWatch ();
+ }
+
+ #endregion
+
+ #region Fetching and loading children
+ /// <summary>
+ /// Marks a node as expanded and fetches children for the node if they have not been already fetched
+ /// </summary>
+ async Task ExpandNodeAsync (ObjectValueNode node)
+ {
+ // if we think the node is expanded already, no need to trigger this again
+ if (node.IsExpanded)
+ return;
+
+ node.IsExpanded = true;
+
+ int loadedCount = 0;
+ if (node.IsEnumerable) {
+ // if we already have some loaded, don't load more - that is a specific user gesture
+ if (node.Children.Count == 0) {
+ // page the children in, instead of loading them all at once
+ loadedCount = await FetchChildrenAsync (node, MaxEnumerableChildrenToFetch, cancellationTokenSource.Token);
+ }
+ } else {
+ loadedCount = await FetchChildrenAsync (node, 0, cancellationTokenSource.Token);
+ }
+
+ await Runtime.RunInMainThread (() => {
+ if (loadedCount > 0) {
+ view.LoadNodeChildren (node, 0, node.Children.Count);
+ }
+
+ view.OnNodeExpanded (node);
+ });
+ }
+
+ async Task<int> FetchMoreChildrenAsync (ObjectValueNode node)
+ {
+ if (node.ChildrenLoaded) {
+ return 0;
+ }
+
+ try {
+ if (childFetchTasks.TryGetValue (node, out Task<int> task)) {
+ // there is already a task to fetch the children
+ return await task;
+ } else {
+ try {
+ var oldCount = node.Children.Count;
+ var result = await node.LoadChildrenAsync (MaxEnumerableChildrenToFetch, cancellationTokenSource.Token);
+
+ // if any of them are still evaluating register for
+ // a completion event so that we can tell the UI
+ for (int i = oldCount; i < oldCount + result; i++) {
+ var c = node.Children [i];
+ RegisterNode (c);
+ }
+
+ // always send the event so that the UI can determine if the node has finished loading.
+ OnChildrenLoaded (node, oldCount, result);
+
+ return result;
+ } finally {
+ childFetchTasks.Remove (node);
+ }
+ }
+ } catch (Exception ex) {
+ LoggingService.LogInternalError (ex);
+ }
+
+ return 0;
+ }
+
+ /// <summary>
+ /// Fetches the child nodes and returns the count of new children that were loaded.
+ /// The children will be in node.Children.
+ /// </summary>
+ async Task<int> FetchChildrenAsync (ObjectValueNode node, int count, CancellationToken cancellationToken)
+ {
+ if (node.ChildrenLoaded) {
+ return 0;
+ }
+
+ try {
+ if (childFetchTasks.TryGetValue (node, out Task<int> task)) {
+ // there is already a task to fetch the children
+ return await task;
+ } else {
+ try {
+ int result = 0;
+ if (count > 0) {
+ var oldCount = node.Children.Count;
+ result = await node.LoadChildrenAsync (count, cancellationToken);
+
+ // if any of them are still evaluating register for
+ // a completion event so that we can tell the UI
+ for (int i = oldCount; i < oldCount + result; i++) {
+ var c = node.Children [i];
+ RegisterNode (c);
+ }
+ } else {
+ result = await node.LoadChildrenAsync (cancellationToken);
+
+ // if any of them are still evaluating register for
+ // a completion event so that we can tell the UI
+ foreach (var c in node.Children) {
+ RegisterNode (c);
+ }
+ }
+
+ return result;
+ } finally {
+ childFetchTasks.Remove (node);
+ }
+ }
+ } catch (Exception ex) {
+ LoggingService.LogInternalError (ex);
+ }
+
+ return 0;
+ }
+ #endregion
+
+ #region Evaluation watches
+ /// <summary>
+ /// Registers the ValueChanged event for a node where IsEvaluating is true. If the node is not evaluating, and
+ /// sendImmediatelyIfNotEvaulating is true, then fire OnEvaluatingNodeValueChanged immediately
+ /// </summary>
+ void RegisterForEvaluationCompletion (ObjectValueNode node, bool sendImmediatelyIfNotEvaulating = false)
+ {
+ if (node.IsEvaluating) {
+ evaluationWatches [node] = null;
+ node.ValueChanged += OnEvaluatingNodeValueChanged;
+ } else if (sendImmediatelyIfNotEvaulating) {
+ OnEvaluatingNodeValueChanged (node, EventArgs.Empty);
+ }
+ }
+
+ /// <summary>
+ /// Removes the ValueChanged handler from the node
+ /// </summary>
+ void UnregisterForEvaluationCompletion (ObjectValueNode node)
+ {
+ if (node != null) {
+ node.ValueChanged -= OnEvaluatingNodeValueChanged;
+ evaluationWatches.Remove (node);
+ }
+ }
+
+ /// <summary>
+ /// Removes all ValueChanged handlers for evaluating nodes
+ /// </summary>
+ void ClearEvaluationCompletionRegistrations ()
+ {
+ foreach (var node in evaluationWatches.Keys) {
+ node.ValueChanged -= OnEvaluatingNodeValueChanged;
+ }
+
+ evaluationWatches.Clear ();
+ }
+
+ #endregion
+
+
+ /// <summary>
+ /// Called when clearing, by default sets the root to a new ObjectValueNode
+ /// </summary>
+ protected virtual ObjectValueNode OnCreateRoot ()
+ {
+ return new RootObjectValueNode ();
+ }
+
+ protected virtual IDebuggerService OnGetDebuggerService ()
+ {
+ return new ObjectValueDebuggerService ();
+ }
+
+ /// <summary>
+ /// Registers the node in the index and sets a watch for evaluating nodes
+ /// </summary>
+ void RegisterNode (ObjectValueNode node)
+ {
+ if (node != null) {
+ RegisterForEvaluationCompletion (node);
+ }
+ }
+
+ void UnregisterNode (ObjectValueNode node)
+ {
+ if (node != null) {
+ UnregisterForEvaluationCompletion (node);
+ }
+ }
+
+ /// <summary>
+ /// Creates a checkpoint of the value of the node and any children that are expanded
+ /// </summary>
+ void ChangeCheckpoint (ObjectValueNode node)
+ {
+ oldValues [node.Path] = new CheckpointState (node);
+
+ if (node.IsExpanded) {
+ foreach (var child in node.Children) {
+ ChangeCheckpoint (child);
+ }
+ }
+ }
+
+ #region Event triggers
+ void OnChildrenLoaded (ObjectValueNode node, int index, int count)
+ {
+ Runtime.RunInMainThread (() => {
+ view.LoadNodeChildren (node, index, count);
+ }).Ignore ();
+ }
+
+ /// <summary>
+ /// Triggered in response to ValueChanged on a node
+ /// </summary>
+ void OnEvaluatingNodeValueChanged (object sender, EventArgs e)
+ {
+ if (sender is ObjectValueNode node) {
+ UnregisterForEvaluationCompletion (node);
+
+ if (sender is IEvaluatingGroupObjectValueNode evalGroupNode) {
+ if (evalGroupNode.IsEvaluatingGroup) {
+ var replacementNodes = evalGroupNode.GetEvaluationGroupReplacementNodes ();
+
+ foreach (var newNode in replacementNodes) {
+ RegisterNode (newNode);
+ }
+
+ OnEvaluationCompleted (sender as ObjectValueNode, replacementNodes);
+ } else {
+ OnEvaluationCompleted (sender as ObjectValueNode);
+ }
+ } else {
+ OnEvaluationCompleted (sender as ObjectValueNode);
+ }
+ }
+ }
+
+ void OnEvaluationCompleted (ObjectValueNode node)
+ {
+ Runtime.RunInMainThread (() => {
+ view.LoadEvaluatedNode (node, new ObjectValueNode [1] { node });
+ }).Ignore ();
+ }
+
+ void OnEvaluationCompleted (ObjectValueNode node, ObjectValueNode [] replacementNodes)
+ {
+ // `node` returns us a set of new nodes that need to be replaced into the children
+ // of node.parent. This should only be applicable to direct children of the root since
+ // this construct is to support placehold values for "locals" etc
+ if (node.Parent is ISupportChildObjectValueNodeReplacement replacerParent) {
+ replacerParent.ReplaceChildNode (node, replacementNodes);
+ }
+
+ Runtime.RunInMainThread (() => {
+ view.LoadEvaluatedNode (node, replacementNodes);
+ }).Ignore ();
+ }
+ #endregion
+
+ class CheckpointState
+ {
+ public CheckpointState (ObjectValueNode node)
+ {
+ Expanded = node.IsExpanded;
+ Value = node.Value;
+ }
+
+ public bool Expanded { get; set; }
+ public string Value { get; set; }
+ }
+
+ public static string GetIcon (ObjectValueFlags flags)
+ {
+ if ((flags & ObjectValueFlags.Field) != 0 && (flags & ObjectValueFlags.ReadOnly) != 0)
+ return "md-literal";
+
+ string global = (flags & ObjectValueFlags.Global) != 0 ? "static-" : string.Empty;
+ string source;
+
+ switch (flags & ObjectValueFlags.OriginMask) {
+ case ObjectValueFlags.Property: source = "property"; break;
+ case ObjectValueFlags.Type: source = "class"; global = string.Empty; break;
+ case ObjectValueFlags.Method: source = "method"; break;
+ case ObjectValueFlags.Literal: return "md-literal";
+ case ObjectValueFlags.Namespace: return "md-name-space";
+ case ObjectValueFlags.Group: return "md-open-resource-folder";
+ case ObjectValueFlags.Field: source = "field"; break;
+ case ObjectValueFlags.Variable: return "md-variable";
+ default: return "md-empty";
+ }
+
+ string access;
+ switch (flags & ObjectValueFlags.AccessMask) {
+ case ObjectValueFlags.Private: access = "private-"; break;
+ case ObjectValueFlags.Internal: access = "internal-"; break;
+ case ObjectValueFlags.InternalProtected:
+ case ObjectValueFlags.Protected: access = "protected-"; break;
+ default: access = string.Empty; break;
+ }
+
+ return "md-" + access + global + source;
+ }
+ }
+
+ #region Extension methods and helpers
+ /// <summary>
+ /// Helper class to mimic existing API
+ /// </summary>
+ public static class ObjectValueTreeViewControllerExtensions
+ {
+ public static void SetStackFrame (this ObjectValueTreeViewController controller, StackFrame frame)
+ {
+ controller.Frame = new ProxyStackFrame (frame);
+ }
+
+ public static StackFrame GetStackFrame (this ObjectValueTreeViewController controller)
+ {
+ return (controller.Frame as ProxyStackFrame)?.StackFrame;
+ }
+
+ public static StackFrame GetStackFrame (this IStackFrame frame)
+ {
+ return (frame as ProxyStackFrame)?.StackFrame;
+ }
+
+ public static void AddValue (this ObjectValueTreeViewController controller, ObjectValue value)
+ {
+ controller.AddValue (new DebuggerObjectValueNode (value));
+ }
+
+ public static void AddValues (this ObjectValueTreeViewController controller, IEnumerable<ObjectValue> values)
+ {
+ controller.AddValues (values.Select (value => new DebuggerObjectValueNode (value)));
+ }
+
+ public static string[] GetExpressions (this ObjectValueTreeViewController controller)
+ {
+ // given that expressions are only supported by themselves (ie not mixed with locals for example)
+ // and they are all children of the root, we can mimic a list of expressions by just grabbing the
+ // name property of the root children
+ if (controller.Root == null)
+ return new string [0];
+
+ return controller.Root.Children.Select (c => c.Name).ToArray ();
+ }
+ }
+
+ public static class ObjectValueNodeExtensions
+ {
+ public static string GetDisplayValue (this ObjectValueNode node)
+ {
+ if (node.DisplayValue == null)
+ return "(null)";
+
+ if (node.DisplayValue.Length > 1000) {
+ // Truncate the string to stop the UI from hanging
+ // when calculating the size for very large amounts
+ // of text.
+ return node.DisplayValue.Substring (0, 1000) + "…";
+ }
+
+ return node.DisplayValue;
+ }
+
+ public static ObjectValue GetDebuggerObjectValue (this ObjectValueNode node)
+ {
+ if (node != null && node is DebuggerObjectValueNode val) {
+ return val.DebuggerObject;
+ }
+
+ return null;
+ }
+
+ public static bool GetIsEvaluatingGroup (this ObjectValueNode node)
+ {
+ return (node is IEvaluatingGroupObjectValueNode evg && evg.IsEvaluatingGroup);
+ }
+
+ public static string GetInlineVisualisation (this ObjectValueNode node)
+ {
+ // TODO: this is not possible to mock as it is
+ if (node is DebuggerObjectValueNode val) {
+ return DebuggingService.GetInlineVisualizer (val.DebuggerObject).InlineVisualize (val.DebuggerObject);
+ }
+
+ return node.GetDisplayValue ();
+ }
+ }
+ #endregion
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeViewFakes.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeViewFakes.cs
new file mode 100644
index 0000000000..508d98b878
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeViewFakes.cs
@@ -0,0 +1,243 @@
+//
+// ObjectValueTreeViewFakes.cs
+//
+// Author:
+// gregm <gregm@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Mono.Debugging.Client;
+
+namespace MonoDevelop.Debugger
+{
+ /// <summary>
+ /// An AbstractObjectValueNode used for debugging
+ /// </summary>
+ abstract class DebugObjectValueNode : ObjectValueNode
+ {
+ protected DebugObjectValueNode (string name) : base (name)
+ {
+ }
+
+ public override string Value => "none";
+ public override string DisplayValue => "dummy";
+ public override string TypeName => GetType ().ToString ();
+
+ public override bool HasChildren => true;
+ }
+
+ /// <summary>
+ /// An AbstractObjectValueNode used for debugging
+ /// </summary>
+ sealed class FakeIndexedObjectValueNode : DebugObjectValueNode
+ {
+ public FakeIndexedObjectValueNode (int index) : base ($"indexed[{index}]")
+ {
+ Value = $"indexed[{index}]";
+ DisplayValue = $"indexed[{index}]";
+ }
+
+ public override string Value { get; }
+ public override string DisplayValue { get; }
+
+ public override bool HasChildren => false;
+ }
+
+ /// <summary>
+ /// An AbstractObjectValueNode used for debugging
+ /// </summary>
+ sealed class FakeIsImplicitNotSupportedObjectValueNode : DebugObjectValueNode
+ {
+ string value;
+ bool isImplicitNotSupported;
+ public FakeIsImplicitNotSupportedObjectValueNode () : base ($"implicit")
+ {
+ value = $"implicit";
+ isImplicitNotSupported = true;
+ }
+
+ public override string Value => value;
+ public override string DisplayValue => value;
+
+ public override bool HasChildren => false;
+ public override bool IsImplicitNotSupported => isImplicitNotSupported;
+ public override bool CanRefresh => true;
+
+ public override void Refresh (EvaluationOptions options)
+ {
+ value = "refreshed";
+ //IsImplicitNotSupported should be false once we forcibly refresh the node
+ isImplicitNotSupported = false;
+ }
+ }
+
+ /// <summary>
+ /// An AbstractObjectValueNode used for debugging
+ /// </summary>
+ sealed class FakeObjectValueNode : DebugObjectValueNode
+ {
+ bool hasChildren;
+
+ public FakeObjectValueNode (string name, bool children = true) : base (name)
+ {
+ hasChildren = children;
+ }
+
+ public override string Value => "none";
+ public override string DisplayValue => "dummy";
+
+ public override bool HasChildren => true;
+
+ protected override async Task<IEnumerable<ObjectValueNode>> OnLoadChildrenAsync (CancellationToken cancellationToken)
+ {
+ // TODO: do some sleeping...
+ await Task.Delay (1000);
+ return new [] { new FakeObjectValueNode ($"child of {Name}") };
+ }
+ }
+
+ /// <summary>
+ /// An AbstractObjectValueNode used for debugging
+ /// </summary>
+ sealed class FakeEnumerableObjectValueNode : DebugObjectValueNode
+ {
+ readonly int maxItems;
+
+ public FakeEnumerableObjectValueNode (int count) : base ($"enumerable {count}")
+ {
+ maxItems = count;
+ }
+
+ public override string Value => $"Enumerable{maxItems}";
+ public override string DisplayValue => $"Enumerable{maxItems}";
+
+ public override bool HasChildren => true;
+ public override bool IsEnumerable => true;
+
+ protected override async Task<IEnumerable<ObjectValueNode>> OnLoadChildrenAsync (CancellationToken cancellationToken)
+ {
+ await Task.Delay (1000);
+ var result = new List<ObjectValueNode> ();
+ for (int i = 0; i < maxItems; i++) {
+ result.Add (new FakeIndexedObjectValueNode (i));
+ }
+
+ return result;
+ }
+
+ protected override async Task<Tuple<IEnumerable<ObjectValueNode>, bool>> OnLoadChildrenAsync (int index, int count, CancellationToken cancellationToken)
+ {
+ await Task.Delay (1000);
+ var max = Math.Min (maxItems, index+count);
+ var result = new List<ObjectValueNode> ();
+ for (int i = index; i < max; i++) {
+ result.Add (new FakeIndexedObjectValueNode (i));
+ }
+
+ return Tuple.Create<IEnumerable<ObjectValueNode>, bool> (result, result.Count < count);
+ }
+ }
+
+ /// <summary>
+ /// An AbstractObjectValueNode used for debugging
+ /// </summary>
+ sealed class FakeEvaluatingObjectValueNode : DebugObjectValueNode
+ {
+ bool isEvaluating;
+ bool hasChildren;
+
+ public FakeEvaluatingObjectValueNode () : base ("evaluating")
+ {
+ isEvaluating = true;
+ DoTest ();
+ }
+
+ public override string Value => "none";
+ public override string DisplayValue => "dummy";
+
+ public override bool HasChildren => hasChildren;
+ public override bool IsEvaluating => isEvaluating;
+
+ protected override async Task<IEnumerable<ObjectValueNode>> OnLoadChildrenAsync (CancellationToken cancellationToken)
+ {
+ // TODO: do some sleeping...
+ await Task.Delay (1000);
+
+ return new [] { new FakeObjectValueNode ($"child of {Name}", true) };
+ }
+
+ async void DoTest ()
+ {
+ await Task.Delay (3000);
+ isEvaluating = false;
+ hasChildren = true;
+ OnValueChanged (EventArgs.Empty);
+ }
+ }
+
+ /// <summary>
+ /// An AbstractObjectValueNode used for debugging
+ /// </summary>
+ sealed class FakeEvaluatingGroupObjectValueNode : DebugObjectValueNode, IEvaluatingGroupObjectValueNode
+ {
+ bool isEvaluating;
+ int evalNodes;
+
+ public FakeEvaluatingGroupObjectValueNode (int nodes) : base ($"eval group {nodes}")
+ {
+ isEvaluating = true;
+ evalNodes = nodes;
+ DoTest ();
+ }
+
+ public override string Value => "none";
+ public override string DisplayValue => $"evg {evalNodes}";
+
+ public override bool IsEvaluating => isEvaluating;
+
+ #region IEvaluatingGroupObjectValueNode
+ bool IEvaluatingGroupObjectValueNode.IsEvaluatingGroup => true;
+
+ ObjectValueNode [] IEvaluatingGroupObjectValueNode.GetEvaluationGroupReplacementNodes ()
+ {
+ var replacementNodes = new ObjectValueNode [evalNodes];
+
+ for (int i = 0; i < evalNodes; i++) {
+ replacementNodes [i] = new FakeObjectValueNode ($"child of {Name}", false) {
+ Parent = Parent
+ };
+ }
+
+ return replacementNodes;
+ }
+ #endregion
+
+ async void DoTest ()
+ {
+ await Task.Delay (5000);
+ isEvaluating = false;
+ OnValueChanged (EventArgs.Empty);
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/RootObjectValueNode.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/RootObjectValueNode.cs
new file mode 100644
index 0000000000..c20c63fef5
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/RootObjectValueNode.cs
@@ -0,0 +1,82 @@
+//
+// RootObjectValueNode.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Diagnostics;
+using System.Collections.Generic;
+
+namespace MonoDevelop.Debugger
+{
+ /// <summary>
+ /// Special node used as the root of the treeview.
+ /// </summary>
+ sealed class RootObjectValueNode : ObjectValueNode, ISupportChildObjectValueNodeReplacement
+ {
+ public RootObjectValueNode () : base (string.Empty)
+ {
+ IsExpanded = true;
+ }
+
+ public override bool HasChildren => true;
+
+ public void AddValue (ObjectValueNode value)
+ {
+ AddChild (value);
+ }
+
+ public void AddValues (IEnumerable<ObjectValueNode> values)
+ {
+ AddChildren (values);
+ }
+
+ public void RemoveValueAt (int index)
+ {
+ RemoveChildAt (index);
+ }
+
+ public void ReplaceValueAt (int index, ObjectValueNode value)
+ {
+ ReplaceChildAt (index, value);
+ }
+
+ void ISupportChildObjectValueNodeReplacement.ReplaceChildNode (ObjectValueNode node, ObjectValueNode[] newNodes)
+ {
+ var index = Children.IndexOf (node);
+
+ Debug.Assert (index >= 0, "The node being replaced should be a child of this node");
+
+ if (newNodes.Length == 0) {
+ RemoveChildAt (index);
+ return;
+ }
+
+ ReplaceChildAt (index, newNodes [0]);
+
+ for (int i = 1; i < newNodes.Length; i++)
+ InsertChildAt (++index, newNodes[i]);
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ShowMoreValuesObjectValueNode.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ShowMoreValuesObjectValueNode.cs
new file mode 100644
index 0000000000..db2f0fb642
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ShowMoreValuesObjectValueNode.cs
@@ -0,0 +1,43 @@
+//
+// ShowMoreValuesObjectValueNode.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+namespace MonoDevelop.Debugger
+{
+ /// <summary>
+ /// Special node used to indicate that more values are available.
+ /// </summary>
+ sealed class ShowMoreValuesObjectValueNode : ObjectValueNode
+ {
+ public ShowMoreValuesObjectValueNode (ObjectValueNode enumerableNode) : base (string.Empty)
+ {
+ EnumerableNode = enumerableNode;
+ }
+
+ public override bool IsEnumerable => true;
+
+ public ObjectValueNode EnumerableNode { get; }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValuePad.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValuePad.cs
index 2f4e1dbab9..91124d04a8 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValuePad.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValuePad.cs
@@ -26,7 +26,6 @@
//
using System;
-using System.Collections.Generic;
using Gtk;
using MonoDevelop.Ide.Gui;
@@ -37,7 +36,11 @@ namespace MonoDevelop.Debugger
{
public class ObjectValuePad : PadContent
{
+ protected bool UseNewTreeView = false;
+
+ protected ObjectValueTreeViewController controller;
protected ObjectValueTreeView tree;
+
readonly ScrolledWindow scrolled;
bool needsUpdateValues;
bool needsUpdateFrame;
@@ -51,21 +54,32 @@ namespace MonoDevelop.Debugger
}
}
- public ObjectValuePad ()
+ public ObjectValuePad (bool useNewTreeView = false)
{
+ UseNewTreeView = useNewTreeView;
+
scrolled = new ScrolledWindow ();
scrolled.HscrollbarPolicy = PolicyType.Automatic;
scrolled.VscrollbarPolicy = PolicyType.Automatic;
- tree = new ObjectValueTreeView ();
+ if (UseNewTreeView) {
+ controller = new ObjectValueTreeViewController ();
+ controller.AllowEditing = true;
- fontChanger = new PadFontChanger (tree, tree.SetCustomFont, tree.QueueResize);
+ var treeView = controller.GetControl () as GtkObjectValueTreeView;
+ fontChanger = new PadFontChanger (treeView, treeView.SetCustomFont, treeView.QueueResize);
+
+ scrolled.Add (treeView);
+ } else {
+ tree = new ObjectValueTreeView ();
+ tree.AllowEditing = true;
+ tree.AllowAdding = false;
+
+ fontChanger = new PadFontChanger (tree, tree.SetCustomFont, tree.QueueResize);
+
+ scrolled.Add (tree);
+ }
- tree.AllowEditing = true;
- tree.AllowAdding = false;
- tree.HeadersVisible = true;
- tree.RulesHint = true;
- scrolled.Add (tree);
scrolled.ShowAll ();
DebuggingService.CurrentFrameChanged += OnFrameChanged;
@@ -98,9 +112,9 @@ namespace MonoDevelop.Debugger
base.Dispose ();
}
- protected override void Initialize (IPadWindow container)
+ protected override void Initialize (IPadWindow window)
{
- container.PadContentShown += delegate {
+ window.PadContentShown += delegate {
if (needsUpdateFrame)
OnUpdateFrame ();
else if (needsUpdateValues)
@@ -113,8 +127,14 @@ namespace MonoDevelop.Debugger
needsUpdateValues = false;
needsUpdateFrame = false;
- if (DebuggingService.CurrentFrame != lastFrame)
- tree.Frame = DebuggingService.CurrentFrame;
+ if (DebuggingService.CurrentFrame != lastFrame) {
+ if (UseNewTreeView) {
+ controller.SetStackFrame (DebuggingService.CurrentFrame);
+ } else {
+ tree.Frame = DebuggingService.CurrentFrame;
+ }
+ }
+
lastFrame = DebuggingService.CurrentFrame;
}
@@ -148,10 +168,20 @@ namespace MonoDevelop.Debugger
protected virtual void OnDebuggerResumed (object s, EventArgs a)
{
- if (!initialResume)
- tree.ChangeCheckpoint ();
+ if (UseNewTreeView) {
+ if (!initialResume) {
+ controller.ChangeCheckpoint ();
+ }
+
+ controller.ClearValues ();
+ } else {
+ if (!initialResume) {
+ tree.ChangeCheckpoint ();
+ }
+
+ tree.ClearValues ();
+ }
- tree.ClearValues ();
initialResume = false;
}
@@ -159,8 +189,15 @@ namespace MonoDevelop.Debugger
{
if (DebuggingService.IsDebugging)
return;
- tree.ResetChangeTracking ();
- tree.ClearAll ();
+
+ if (UseNewTreeView) {
+ controller.ResetChangeTracking ();
+ controller.ClearAll ();
+ } else {
+ tree.ResetChangeTracking ();
+ tree.ClearAll ();
+ }
+
lastFrame = null;
initialResume = true;
}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/WatchPad.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/WatchPad.cs
index 60b2e82043..b8e1bf6884 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/WatchPad.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/WatchPad.cs
@@ -37,18 +37,24 @@ namespace MonoDevelop.Debugger
{
public class WatchPad : ObjectValuePad, IMementoCapable, ICustomXmlSerializer
{
+ // Note: This can be removed once we make the switch to UseNewTreeView
static readonly Gtk.TargetEntry[] DropTargets = {
new Gtk.TargetEntry ("text/plain;charset=utf-8", Gtk.TargetFlags.App, 0)
};
- List<string> storedVars;
+ readonly List<string> expressions = new List<string> ();
- public WatchPad ()
+ public WatchPad () : base (true)
{
- tree.EnableModelDragDest (DropTargets, Gdk.DragAction.Copy);
- tree.DragDataReceived += HandleDragDataReceived;
- tree.AllowAdding = true;
+ if (UseNewTreeView) {
+ controller.AllowWatchExpressions = true;
+ } else {
+ tree.EnableModelDragDest (DropTargets, Gdk.DragAction.Copy);
+ tree.DragDataReceived += HandleDragDataReceived;
+ tree.AllowAdding = true;
+ }
}
+ // Note: This can be removed once we make the switch to UseNewTreeView
void HandleDragDataReceived (object o, Gtk.DragDataReceivedArgs args)
{
var text = args.SelectionData.Text;
@@ -65,16 +71,58 @@ namespace MonoDevelop.Debugger
AddWatch (expr.Trim ());
}
}
-
+
public void AddWatch (string expression)
{
- tree.AddExpression (expression);
+ if (UseNewTreeView) {
+ controller.AddExpression (expression);
+ } else {
+ tree.AddExpression (expression);
+ }
+ }
+
+ void ReloadValues ()
+ {
+ // clone the list of expressions
+// expressions.Clear ();
+// expressions.AddRange (controller.GetExpressions());
+
+ // remove the expressions because we're going to rebuild them
+ controller.ClearAll ();
+
+ // re-add the expressions which will reevaluate the expressions and repopulate the treeview
+ controller.AddExpressions (expressions);
+ }
+
+ public override void OnUpdateFrame ()
+ {
+ base.OnUpdateFrame ();
+
+ if (UseNewTreeView)
+ ReloadValues ();
}
public override void OnUpdateValues ()
{
base.OnUpdateValues ();
- tree.Update ();
+
+ if (UseNewTreeView) {
+ ReloadValues ();
+ } else {
+ tree.Update ();
+ }
+ }
+
+ protected override void OnDebuggerResumed (object s, EventArgs a)
+ {
+ // base will clear the controller, which removes all values and expressions
+
+ if (UseNewTreeView) {
+ expressions.Clear ();
+ expressions.AddRange (controller.GetExpressions ());
+ }
+
+ base.OnDebuggerResumed (s, a);
}
#region IMementoCapable implementation
@@ -84,27 +132,42 @@ namespace MonoDevelop.Debugger
return this;
}
set {
- if (tree != null) {
- tree.ClearExpressions ();
- if (storedVars != null)
- tree.AddExpressions (storedVars);
+ if (UseNewTreeView) {
+ if (controller != null) {
+ controller.ClearAll ();
+ controller.AddExpressions (expressions);
+ }
+ } else {
+ if (tree != null) {
+ tree.ClearExpressions ();
+ tree.AddExpressions (expressions);
+ }
}
}
}
void ICustomXmlSerializer.WriteTo (XmlWriter writer)
{
- if (tree != null) {
- writer.WriteStartElement ("Values");
- foreach (string name in tree.Expressions)
- writer.WriteElementString ("Value", name);
- writer.WriteEndElement ();
+ if (UseNewTreeView) {
+ if (controller != null) {
+ writer.WriteStartElement ("Values");
+ foreach (var expression in controller.GetExpressions())
+ writer.WriteElementString ("Value", expression);
+ writer.WriteEndElement ();
+ }
+ } else {
+ if (tree != null) {
+ writer.WriteStartElement ("Values");
+ foreach (var name in tree.Expressions)
+ writer.WriteElementString ("Value", name);
+ writer.WriteEndElement ();
+ }
}
}
ICustomXmlSerializer ICustomXmlSerializer.ReadFrom (XmlReader reader)
{
- storedVars = new List<string> ();
+ expressions.Clear ();
reader.MoveToContent ();
if (reader.IsEmptyElement) {
@@ -115,9 +178,10 @@ namespace MonoDevelop.Debugger
reader.MoveToContent ();
while (reader.NodeType != XmlNodeType.EndElement) {
if (reader.NodeType == XmlNodeType.Element) {
- storedVars.Add (reader.ReadElementString ());
- } else
+ expressions.Add (reader.ReadElementString ());
+ } else {
reader.Skip ();
+ }
}
reader.ReadEndElement ();
return null;
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/DebugValueTooltipProvider.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/DebugValueTooltipProvider.cs
index 7258d4ed36..1cec611cda 100644
--- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/DebugValueTooltipProvider.cs
+++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/DebugValueTooltipProvider.cs
@@ -27,17 +27,15 @@
//
using System;
-using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Mono.Debugging.Client;
using MonoDevelop.Ide;
-using MonoDevelop.Ide.Gui;
using MonoDevelop.Debugger;
using MonoDevelop.Components;
-using Mono.Debugging.Client;
-
using MonoDevelop.Ide.Editor;
-using System.Threading.Tasks;
-using System.Threading;
namespace MonoDevelop.SourceEditor
{
@@ -61,7 +59,8 @@ namespace MonoDevelop.SourceEditor
{
if (tooltip == null)
return;
- var debuggerSession = tooltip.Tree.Frame?.DebuggerSession;
+
+ var debuggerSession = tooltip.GetDebuggerSession ();
if (debuggerSession == null || debuggerSession == sender) {
tooltip.Destroy ();
tooltip = null;
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/PinnedWatchWidget.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/PinnedWatchWidget.cs
index 1c2d9779d2..1f4cd28845 100644
--- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/PinnedWatchWidget.cs
+++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/PinnedWatchWidget.cs
@@ -34,9 +34,15 @@ namespace MonoDevelop.SourceEditor
{
class PinnedWatchWidget : EventBox
{
+ static bool UseNewTreeView = true;
+
+ readonly ObjectValueTreeViewController controller;
+ readonly TreeView treeView;
+
readonly ObjectValueTreeView valueTree;
- ScrolledWindow sw;
+
ObjectValue objectValue;
+ ScrolledWindow sw;
MonoTextEditor Editor {
get; set;
@@ -54,12 +60,19 @@ namespace MonoDevelop.SourceEditor
if (objectValue == value)
return;
- if (objectValue != null && value != null) {
- valueTree.ReplaceValue (objectValue, value);
- } else {
- valueTree.ClearValues ();
+ if (UseNewTreeView) {
+ controller.ClearAll ();
+
if (value != null)
- valueTree.AddValue (value);
+ controller.AddValue (value);
+ } else {
+ if (objectValue != null && value != null) {
+ valueTree.ReplaceValue (objectValue, value);
+ } else {
+ valueTree.ClearValues ();
+ if (value != null)
+ valueTree.AddValue (value);
+ }
}
objectValue = value;
@@ -72,27 +85,43 @@ namespace MonoDevelop.SourceEditor
Editor = editor;
Watch = watch;
- valueTree = new ObjectValueTreeView ();
- valueTree.AllowAdding = false;
- valueTree.AllowEditing = true;
- valueTree.AllowPinning = true;
- valueTree.HeadersVisible = false;
- valueTree.CompactView = true;
- valueTree.PinnedWatch = watch;
- if (objectValue != null)
- valueTree.AddValue (objectValue);
+ if (UseNewTreeView) {
+ controller = new ObjectValueTreeViewController ();
+ controller.AllowEditing = true;
+
+ treeView = (TreeView) controller.GetControl (headersVisible: false, compactView: true, allowPinning: true);
+
+ controller.PinnedWatch = watch;
+ valueTree = null;
+
+ if (objectValue != null)
+ controller.AddValue (objectValue);
+ } else {
+ valueTree = new ObjectValueTreeView ();
+ valueTree.AllowAdding = false;
+ valueTree.AllowEditing = true;
+ valueTree.AllowPinning = true;
+ valueTree.HeadersVisible = false;
+ valueTree.CompactView = true;
+ valueTree.PinnedWatch = watch;
+ if (objectValue != null)
+ valueTree.AddValue (objectValue);
+
+ treeView = valueTree;
+ controller = null;
+ }
- valueTree.ButtonPressEvent += HandleValueTreeButtonPressEvent;
- valueTree.ButtonReleaseEvent += HandleValueTreeButtonReleaseEvent;
- valueTree.MotionNotifyEvent += HandleValueTreeMotionNotifyEvent;
- valueTree.SizeAllocated += OnTreeSizeChanged;
+ treeView.ButtonPressEvent += HandleValueTreeButtonPressEvent;
+ treeView.ButtonReleaseEvent += HandleValueTreeButtonReleaseEvent;
+ treeView.MotionNotifyEvent += HandleValueTreeMotionNotifyEvent;
+ treeView.SizeAllocated += OnTreeSizeChanged;
sw = new ScrolledWindow ();
sw.HscrollbarPolicy = PolicyType.Never;
sw.VscrollbarPolicy = PolicyType.Never;
- sw.Add (valueTree);
+ sw.Add (treeView);
- Frame fr = new Frame ();
+ var fr = new Frame ();
fr.ShadowType = ShadowType.Out;
fr.Add (sw);
Add (fr);
@@ -106,7 +135,7 @@ namespace MonoDevelop.SourceEditor
void OnTreeSizeChanged (object s, SizeAllocatedArgs a)
{
const int maxHeight = 240;
- var treeHeight = valueTree.SizeRequest ().Height;
+ var treeHeight = treeView.SizeRequest ().Height;
if (treeHeight > maxHeight && sw.VscrollbarPolicy == PolicyType.Never) {
sw.VscrollbarPolicy = PolicyType.Always;
sw.HeightRequest = maxHeight;
@@ -120,15 +149,26 @@ namespace MonoDevelop.SourceEditor
void HandleDebuggingServiceResumedEvent (object sender, EventArgs e)
{
- valueTree.ChangeCheckpoint ();
- valueTree.AllowEditing = false;
- valueTree.AllowExpanding = false;
+ if (UseNewTreeView) {
+ controller.ChangeCheckpoint ();
+ controller.AllowExpanding = false;
+ controller.AllowEditing = false;
+ } else {
+ valueTree.ChangeCheckpoint ();
+ valueTree.AllowEditing = false;
+ valueTree.AllowExpanding = false;
+ }
}
void HandleDebuggingServicePausedEvent (object sender, EventArgs e)
{
- valueTree.AllowExpanding = true;
- valueTree.AllowEditing = true;
+ if (UseNewTreeView) {
+ controller.AllowExpanding = true;
+ controller.AllowEditing = true;
+ } else {
+ valueTree.AllowExpanding = true;
+ valueTree.AllowEditing = true;
+ }
}
protected override void OnDestroyed ()
@@ -137,10 +177,10 @@ namespace MonoDevelop.SourceEditor
DebuggingService.PausedEvent -= HandleDebuggingServicePausedEvent;
DebuggingService.ResumedEvent -= HandleDebuggingServiceResumedEvent;
- valueTree.ButtonPressEvent -= HandleValueTreeButtonPressEvent;
- valueTree.ButtonReleaseEvent -= HandleValueTreeButtonReleaseEvent;
- valueTree.MotionNotifyEvent -= HandleValueTreeMotionNotifyEvent;
- valueTree.SizeAllocated -= OnTreeSizeChanged;
+ treeView.ButtonPressEvent -= HandleValueTreeButtonPressEvent;
+ treeView.ButtonReleaseEvent -= HandleValueTreeButtonReleaseEvent;
+ treeView.MotionNotifyEvent -= HandleValueTreeMotionNotifyEvent;
+ treeView.SizeAllocated -= OnTreeSizeChanged;
}
@@ -157,9 +197,9 @@ namespace MonoDevelop.SourceEditor
TreePath path;
TreeViewColumn col;
int cx, cy;
- valueTree.GetPathAtPos ((int)args.Event.X, (int)args.Event.Y, out path, out col, out cx, out cy);
+ treeView.GetPathAtPos ((int)args.Event.X, (int)args.Event.Y, out path, out col, out cx, out cy);
//Gdk.Rectangle rect = valueTree.GetCellArea (path, col);
- if (!mousePressed && valueTree.Columns[0] == col) {
+ if (!mousePressed && treeView.Columns[0] == col) {
mousePressed = true;
Editor.TextArea.MoveToTop (this);
}
diff --git a/main/src/addins/MonoDevelop.TextEditor/MonoDevelop.TextEditor.Cocoa/MonoDevelop.TextEditor.Cocoa.csproj b/main/src/addins/MonoDevelop.TextEditor/MonoDevelop.TextEditor.Cocoa/MonoDevelop.TextEditor.Cocoa.csproj
index d431330ebf..b2494e44ec 100644
--- a/main/src/addins/MonoDevelop.TextEditor/MonoDevelop.TextEditor.Cocoa/MonoDevelop.TextEditor.Cocoa.csproj
+++ b/main/src/addins/MonoDevelop.TextEditor/MonoDevelop.TextEditor.Cocoa/MonoDevelop.TextEditor.Cocoa.csproj
@@ -45,8 +45,6 @@
<Private>False</Private>
</Reference>
<ProjectReference Include="$(MdAddinsDirectory)Xamarin.Designer\external\xwt\Xwt\Xwt.csproj">
- <Project>{92494904-35FA-4DC9-BDE9-3A3E87AC49D3}</Project>
- <Name>Xwt</Name>
<Private>False</Private>
</ProjectReference>
<ProjectReference Include="$(VSEditorCoreDirectory)src\Roslyn\EditorFeatures\Cocoa\Microsoft.CodeAnalysis.EditorFeatures.Cocoa.csproj" Private="True" />