From c9bea6a597990424fa1ab6b68c4462ebd932d131 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 16 Jun 2012 12:39:18 +0200 Subject: [PATCH] reimplement tooltips for simple expressions --- .../Debugger.AddIn/Debugger.AddIn.csproj | 5 + .../NRefactory/ExpressionEvaluationVisitor.cs | 143 +++++++++++++++++- .../Debugger.AddIn/Service/WindowsDebugger.cs | 32 +++- .../Tooltips/DebuggerTooltipControl.xaml.cs | 5 + .../Tooltips/VisualizerPicker.cs | 24 +-- .../Debugger.AddIn/TreeModel/ValueNode.cs | 14 +- 6 files changed, 193 insertions(+), 30 deletions(-) diff --git a/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj b/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj index ee5543a4f4..7e53f03c4b 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj +++ b/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj @@ -144,6 +144,8 @@ EditBreakpointScriptWindow.xaml Code + + @@ -221,9 +223,12 @@ + + + {857CA1A3-FC88-4BE0-AB6A-D1EE772AB288} ICSharpCode.Core.WinForms diff --git a/src/AddIns/Debugger/Debugger.AddIn/NRefactory/ExpressionEvaluationVisitor.cs b/src/AddIns/Debugger/Debugger.AddIn/NRefactory/ExpressionEvaluationVisitor.cs index 21650d1229..6977997b31 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/NRefactory/ExpressionEvaluationVisitor.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/NRefactory/ExpressionEvaluationVisitor.cs @@ -59,8 +59,11 @@ namespace Debugger.AddIn StackFrame context; ICompilation debuggerTypeSystem; Thread evalThread; + bool allowMethodInvoke; + bool allowSetValue; - public ExpressionEvaluationVisitor(StackFrame context, Thread evalThread, ICompilation debuggerTypeSystem) + public ExpressionEvaluationVisitor(StackFrame context, Thread evalThread, ICompilation debuggerTypeSystem, + bool allowMethodInvoke = false, bool allowSetValue = false) { if (evalThread == null) throw new ArgumentNullException("evalThread"); @@ -71,6 +74,8 @@ namespace Debugger.AddIn this.context = context; this.debuggerTypeSystem = debuggerTypeSystem; this.evalThread = evalThread; + this.allowMethodInvoke = allowMethodInvoke; + this.allowSetValue = allowSetValue; } public Value Convert(ResolveResult result) @@ -121,6 +126,8 @@ namespace Debugger.AddIn { switch (result.OperatorType) { case ExpressionType.Assign: + if (!allowSetValue) + throw new InvalidOperationException("Setting values is not allowed in the current context!"); Debug.Assert(result.Operands.Count == 2); return VisitAssignment((dynamic)result.Operands[0], (dynamic)result.Operands[1]); case ExpressionType.Add: @@ -300,7 +307,7 @@ namespace Debugger.AddIn Value Visit(ArrayAccessResolveResult result) { - var val = Convert(result.Array); + var val = Convert(result.Array).GetPermanentReference(evalThread); return val.GetArrayElement(result.Indexes.Select(rr => (int)Convert(rr).PrimitiveValue).ToArray()); } @@ -321,10 +328,9 @@ namespace Debugger.AddIn return Eval.CreateValue(evalThread, convVal); } else if (result.Conversion.IsUserDefined) return InvokeMethod(null, result.Conversion.Method, val); - else if (result.Conversion.IsReferenceConversion && result.Conversion.IsImplicit) { + else if (result.Conversion.IsReferenceConversion && result.Conversion.IsImplicit) return val; - } else - throw new NotImplementedException(); + throw new NotImplementedException(); } Value Visit(LocalResolveResult result) @@ -351,13 +357,15 @@ namespace Debugger.AddIn throw new GetValueException("Indexer does not have a getter."); usedMethod = prop.Getter; } else if (importedMember is IMethod) { + if (!allowMethodInvoke) + throw new InvalidOperationException("Method invocation not allowed in the current context!"); usedMethod = (IMethod)importedMember; } else throw new GetValueException("Invoked member must be a method or property"); Value target = null; if (!usedMethod.IsStatic) - target = Convert(result.TargetResult); - return InvokeMethod(target, usedMethod, result.Arguments.Select(rr => Convert(rr)).ToArray()); + target = Convert(result.TargetResult).GetPermanentReference(evalThread); + return InvokeMethod(target, usedMethod, result.Arguments.Select(rr => Convert(rr).GetPermanentReference(evalThread)).ToArray()); } Value Visit(NamespaceResolveResult result) @@ -416,4 +424,125 @@ namespace Debugger.AddIn } } + + public class ResolveResultPrettyPrinter + { + public ResolveResultPrettyPrinter() + { + + } + + public string Print(ResolveResult result) + { + if (result == null) + return ""; + if (result.IsError) + return "{Error}"; + return Visit((dynamic)result); + } + + string Visit(ResolveResult result) + { + return "Not supported: " + result.GetType().Name; + } + +// string Visit(ValueResolveResult result) +// { +// throw new NotImplementedException(); +// } + + string Visit(ThisResolveResult result) + { + return "this"; + } + + string Visit(MemberResolveResult result) + { + return Print(result.TargetResult) + "." + result.Member.Name; + } + + string Visit(OperatorResolveResult result) + { + throw new NotImplementedException(); + } + + string Visit(TypeIsResolveResult result) + { + throw new NotImplementedException(); + } + + string Visit(TypeOfResolveResult result) + { + throw new NotImplementedException(); + } + + string Visit(TypeResolveResult result) + { + throw new NotImplementedException(); + } + + string Visit(UnknownMemberResolveResult result) + { + return result.MemberName; + } + + string Visit(UnknownIdentifierResolveResult result) + { + return result.Identifier; + } + + string Visit(ArrayAccessResolveResult result) + { + throw new NotImplementedException(); + } + + string Visit(ArrayCreateResolveResult result) + { + throw new NotImplementedException(); + } + + string Visit(ConversionResolveResult result) + { + throw new NotImplementedException(); + } + + string Visit(LocalResolveResult result) + { + if (result.IsParameter) + return result.Variable.Name; + return result.Variable.Name; + } + + string Visit(AmbiguousMemberResolveResult result) + { + throw new NotImplementedException(); + } + + string Visit(InvocationResolveResult result) + { + StringBuilder sb = new StringBuilder(); + + sb.Append(Print(result.TargetResult)); + sb.Append('.'); + sb.Append(result.Member.Name); + + sb.Append(result.Member is IProperty ? "[" : "("); + + bool first = true; + foreach (var p in result.Member.Parameters) { + if (first) first = false; + else sb.Append(", "); + sb.Append(p.Name); + } + + sb.Append(result.Member is IProperty ? "]" : ")"); + + return sb.ToString(); + } + + string Visit(NamespaceResolveResult result) + { + return "namespace " + result.NamespaceName; + } + } } diff --git a/src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs b/src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs index d15ccfa6de..9c3d436a37 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs @@ -13,6 +13,7 @@ using System.Text; using System.Windows.Forms; using Debugger; using Debugger.AddIn; +using Debugger.AddIn.Tooltips; using Debugger.AddIn.TreeModel; using Debugger.Interop.CorPublish; using Debugger.MetaData; @@ -20,6 +21,7 @@ using ICSharpCode.Core; using ICSharpCode.Core.WinForms; using ICSharpCode.NRefactory; using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.Semantics; using ICSharpCode.SharpDevelop.Bookmarks; using ICSharpCode.SharpDevelop.Debugging; using ICSharpCode.SharpDevelop.Editor; @@ -613,11 +615,11 @@ namespace ICSharpCode.SharpDevelop.Services WorkbenchSingleton.MainWindow.Activate(); // if (debuggedProcess.IsSelectedFrameForced()) { - if (CurrentThread != null && CurrentStackFrame.HasSymbols) { - JumpToSourceCode(); - } else { - #warning JumpToDecompiledCode(CurrentStackFrame); - } + if (CurrentThread != null && CurrentStackFrame.HasSymbols) { + JumpToSourceCode(); + } else { + #warning JumpToDecompiledCode(CurrentStackFrame); + } // } else { // var frame = debuggedProcess.SelectedThread.MostRecentStackFrame; // // other pause reasons @@ -686,7 +688,7 @@ namespace ICSharpCode.SharpDevelop.Services } } - */ + */ StopAttachedProcessDialogResult ShowStopAttachedProcessDialog() { @@ -708,7 +710,23 @@ namespace ICSharpCode.SharpDevelop.Services public void HandleToolTipRequest(ToolTipRequestEventArgs e) { -// throw new NotImplementedException(); + if (e.ResolveResult == null) + return; + if (e.ResolveResult is LocalResolveResult || e.ResolveResult is MemberResolveResult || e.ResolveResult is InvocationResolveResult) { + Value result; + string text; + try { + ExpressionEvaluationVisitor eval = new ExpressionEvaluationVisitor(CurrentStackFrame, EvalThread, CurrentStackFrame.AppDomain.Compilation); + result = eval.Convert(e.ResolveResult); + text = new ResolveResultPrettyPrinter().Print(e.ResolveResult); + } catch (GetValueException ex) { + text = ex.Message; + result = null; + } catch (InvalidOperationException) { + return; + } + e.SetToolTip(new DebuggerTooltipControl(ValueNode.GetTooltipFor(text, result))); + } } } } diff --git a/src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml.cs b/src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml.cs index 9785ef6763..995387b0e7 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml.cs @@ -36,6 +36,11 @@ namespace Debugger.AddIn.Tooltips this.Placement = PlacementMode.Absolute; } + public DebuggerTooltipControl(params TreeNode[] treeNodes) + : this((IEnumerable)treeNodes) + { + } + private void Expand_Click(object sender, RoutedEventArgs e) { var clickedButton = (ToggleButton)e.OriginalSource; diff --git a/src/AddIns/Debugger/Debugger.AddIn/Tooltips/VisualizerPicker.cs b/src/AddIns/Debugger/Debugger.AddIn/Tooltips/VisualizerPicker.cs index 82606236aa..d36e7d9003 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Tooltips/VisualizerPicker.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Tooltips/VisualizerPicker.cs @@ -26,23 +26,23 @@ namespace Debugger.AddIn.Tooltips if (this.SelectedItem == null) { return; } - var clickedCommand = this.SelectedItem as IVisualizerCommand; - if (clickedCommand == null) { - throw new InvalidOperationException( - string.Format("{0} clicked, only instances of {1} must be present in {2}.", - this.SelectedItem.GetType().ToString(), typeof(IVisualizerCommand).Name, typeof(VisualizerPicker).Name)); - } +// var clickedCommand = this.SelectedItem as IVisualizerCommand; +// if (clickedCommand == null) { +// throw new InvalidOperationException( +// string.Format("{0} clicked, only instances of {1} must be present in {2}.", +// this.SelectedItem.GetType().ToString(), typeof(IVisualizerCommand).Name, typeof(VisualizerPicker).Name)); +// } - clickedCommand.Execute(); +// clickedCommand.Execute(); // Make no item selected, so that multiple selections of the same item always execute the command. // This triggers VisualizerPicker_SelectionChanged again, which returns immediately. this.SelectedIndex = -1; } - public new IEnumerable ItemsSource - { - get { return (IEnumerable)base.ItemsSource; } - set { base.ItemsSource = value; } - } +// public new IEnumerable ItemsSource +// { +// get { return (IEnumerable)base.ItemsSource; } +// set { base.ItemsSource = value; } +// } } } diff --git a/src/AddIns/Debugger/Debugger.AddIn/TreeModel/ValueNode.cs b/src/AddIns/Debugger/Debugger.AddIn/TreeModel/ValueNode.cs index 82e7e35092..51ef3ac74f 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/TreeModel/ValueNode.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/TreeModel/ValueNode.cs @@ -14,6 +14,7 @@ using System.Text; using System.Windows.Forms; using Debugger.MetaData; using ICSharpCode.Core; +using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.SharpDevelop.Debugging; using ICSharpCode.SharpDevelop.Gui.Pads; @@ -169,20 +170,20 @@ namespace Debugger.AddIn.TreeModel this.Type = val.Type.Name; if (!val.IsNull) { -#warning this.VisualizerCommands = VisualizerDescriptors.GetAllDescriptors() + #warning this.VisualizerCommands = VisualizerDescriptors.GetAllDescriptors() // .Where(descriptor => descriptor.IsVisualizerAvailable(val.Type)) // .Select(descriptor => descriptor.CreateVisualizerCommand(this.Name, this.GetValue)) // .ToList(); } LoggingService.InfoFormatted("Evaluated node '{0}' in {1} ms", this.Name, watch.ElapsedMilliseconds); - + } catch (GetValueException e) { error = e; this.Value = e.Message; this.Type = string.Empty; this.GetChildren = null; -#warning this.VisualizerCommands = null; + #warning this.VisualizerCommands = null; return; } } @@ -276,6 +277,11 @@ namespace Debugger.AddIn.TreeModel return WindowsDebugger.CurrentStackFrame; } + public static TreeNode GetTooltipFor(string text, Value value) + { + return new ValueNode("Icons.16x16.Local", text, () => value); + } + public static IEnumerable GetLocalVariables() { var stackFrame = GetCurrentStackFrame(); @@ -306,7 +312,7 @@ namespace Debugger.AddIn.TreeModel }); } } - */ + */ } }