1
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
2
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
5
using System.Collections.Generic;
7
using System.Windows.Forms;
8
using ICSharpCode.Core;
9
using ICSharpCode.NRefactory;
10
using ICSharpCode.SharpDevelop.Bookmarks;
11
using ICSharpCode.SharpDevelop.Dom;
12
using ICSharpCode.SharpDevelop.Dom.VBNet;
13
using ICSharpCode.SharpDevelop.Editor;
14
using ICSharpCode.SharpDevelop.Gui;
15
using ICSharpCode.SharpDevelop.Project;
17
namespace ICSharpCode.SharpDevelop.Debugging
19
public static class DebuggerService
21
static IDebugger currentDebugger;
22
static DebuggerDescriptor[] debuggers;
23
static string oldLayoutConfiguration = "Default";
25
static DebuggerService()
27
ProjectService.SolutionLoaded += delegate {
31
ProjectService.BeforeSolutionClosing += OnBeforeSolutionClosing;
33
BookmarkManager.Added += BookmarkAdded;
34
BookmarkManager.Removed += BookmarkRemoved;
37
static void GetDescriptors()
39
if (debuggers == null) {
40
debuggers = AddInTree.BuildItems<DebuggerDescriptor>("/SharpDevelop/Services/DebuggerService/Debugger", null, false).ToArray();
44
static IDebugger GetCompatibleDebugger()
47
IProject project = null;
48
if (ProjectService.OpenSolution != null) {
49
project = ProjectService.OpenSolution.StartupProject;
51
foreach (DebuggerDescriptor d in debuggers) {
52
if (d.Debugger != null && d.Debugger.CanDebug(project)) {
56
return new DefaultDebugger();
60
/// Gets the current debugger. The debugger addin is loaded on demand; so if you
61
/// just want to check a property like IsDebugging, check <see cref="IsDebuggerLoaded"/>
62
/// before using this property.
64
public static IDebugger CurrentDebugger {
66
if (currentDebugger == null) {
67
currentDebugger = GetCompatibleDebugger();
68
currentDebugger.DebugStarting += new EventHandler(OnDebugStarting);
69
currentDebugger.DebugStarted += new EventHandler(OnDebugStarted);
70
currentDebugger.DebugStopped += new EventHandler(OnDebugStopped);
72
return currentDebugger;
76
public static DebuggerDescriptor Descriptor {
79
if (debuggers.Length > 0)
86
/// Returns true if debugger is already loaded.
88
public static bool IsDebuggerLoaded {
90
return currentDebugger != null;
94
static bool debuggerStarted;
97
/// Gets whether the debugger is currently active.
99
public static bool IsDebuggerStarted {
100
get { return debuggerStarted; }
103
public static event EventHandler DebugStarting;
104
public static event EventHandler DebugStarted;
105
public static event EventHandler DebugStopped;
107
static IAnalyticsMonitorTrackedFeature debugFeature;
109
static void OnDebugStarting(object sender, EventArgs e)
111
WorkbenchSingleton.Workbench.WorkbenchLayout.StoreConfiguration();
112
LayoutConfiguration.CurrentLayoutName = "Debug";
114
debugFeature = AnalyticsMonitorService.TrackFeature("Debugger");
116
ClearDebugMessages();
118
if (DebugStarting != null)
119
DebugStarting(null, e);
122
static void OnDebugStarted(object sender, EventArgs e)
124
debuggerStarted = true;
125
if (DebugStarted != null)
126
DebugStarted(null, e);
129
static void OnDebugStopped(object sender, EventArgs e)
131
debuggerStarted = false;
132
if (debugFeature != null)
133
debugFeature.EndTracking();
135
RemoveCurrentLineMarker();
136
WorkbenchSingleton.Workbench.WorkbenchLayout.StoreConfiguration();
137
LayoutConfiguration.CurrentLayoutName = oldLayoutConfiguration;
138
if (DebugStopped != null)
139
DebugStopped(null, e);
142
static MessageViewCategory debugCategory = null;
144
static void EnsureDebugCategory()
146
if (debugCategory == null) {
147
MessageViewCategory.Create(ref debugCategory, "Debug", "${res:MainWindow.Windows.OutputWindow.DebugCategory}");
151
public static void ClearDebugMessages()
153
EnsureDebugCategory();
154
debugCategory.ClearText();
157
public static void PrintDebugMessage(string msg)
159
EnsureDebugCategory();
160
debugCategory.AppendText(msg);
163
public static event EventHandler<BreakpointBookmarkEventArgs> BreakPointChanged;
164
public static event EventHandler<BreakpointBookmarkEventArgs> BreakPointAdded;
165
public static event EventHandler<BreakpointBookmarkEventArgs> BreakPointRemoved;
167
static void OnBreakPointChanged(BreakpointBookmarkEventArgs e)
169
if (BreakPointChanged != null) {
170
BreakPointChanged(null, e);
174
static void OnBreakPointAdded(BreakpointBookmarkEventArgs e)
176
if (BreakPointAdded != null) {
177
BreakPointAdded(null, e);
181
static void OnBreakPointRemoved(BreakpointBookmarkEventArgs e)
183
if (BreakPointRemoved != null) {
184
BreakPointRemoved(null, e);
188
public static IList<BreakpointBookmark> Breakpoints {
190
List<BreakpointBookmark> breakpoints = new List<BreakpointBookmark>();
191
foreach (SDBookmark bookmark in BookmarkManager.Bookmarks) {
192
BreakpointBookmark breakpoint = bookmark as BreakpointBookmark;
193
if (breakpoint != null) {
194
breakpoints.Add(breakpoint);
197
return breakpoints.AsReadOnly();
201
static void BookmarkAdded(object sender, BookmarkEventArgs e)
203
BreakpointBookmark bb = e.Bookmark as BreakpointBookmark;
205
bb.LineNumberChanged += BookmarkChanged;
206
OnBreakPointAdded(new BreakpointBookmarkEventArgs(bb));
210
static void BookmarkRemoved(object sender, BookmarkEventArgs e)
212
BreakpointBookmark bb = e.Bookmark as BreakpointBookmark;
215
OnBreakPointRemoved(new BreakpointBookmarkEventArgs(bb));
219
static void BookmarkChanged(object sender, EventArgs e)
221
BreakpointBookmark bb = sender as BreakpointBookmark;
223
OnBreakPointChanged(new BreakpointBookmarkEventArgs(bb));
227
static void OnBeforeSolutionClosing(object sender, SolutionCancelEventArgs e)
229
if (currentDebugger == null)
232
if (currentDebugger.IsDebugging) {
233
string caption = StringParser.Parse("${res:XML.MainMenu.DebugMenu.Stop}");
234
string message = StringParser.Parse("${res:MainWindow.Windows.Debug.StopDebugging.Message}");
235
string[] buttonLabels = new string[] { StringParser.Parse("${res:Global.Yes}"), StringParser.Parse("${res:Global.No}") };
236
int result = MessageService.ShowCustomDialog(caption,
243
currentDebugger.Stop();
250
public static void ToggleBreakpointAt(ITextEditor editor, int lineNumber)
252
BookmarkManager.ToggleBookmark(
254
b => b.CanToggle && b is BreakpointBookmark,
255
location => new BreakpointBookmark(editor.FileName, location, BreakpointAction.Break, "", ""));
258
/* TODO: reimplement this stuff
259
static void ViewContentOpened(object sender, ViewContentEventArgs e)
261
textArea.IconBarMargin.MouseDown += IconBarMouseDown;
262
textArea.ToolTipRequest += TextAreaToolTipRequest;
263
textArea.MouseLeave += TextAreaMouseLeave;
266
public static void RemoveCurrentLineMarker()
268
CurrentLineBookmark.Remove();
271
public static void JumpToCurrentLine(string SourceFullFilename, int StartLine, int StartColumn, int EndLine, int EndColumn)
273
IViewContent viewContent = FileService.OpenFile(SourceFullFilename);
274
if (viewContent is ITextEditorProvider)
275
((ITextEditorProvider)viewContent).TextEditor.JumpTo(StartLine, StartColumn);
276
CurrentLineBookmark.SetPosition(viewContent, StartLine, StartColumn, EndLine, EndColumn);
281
/// Gets debugger tooltip information for the specified position.
282
/// A descriptive string for the element or a DebuggerTooltipControl
283
/// showing its current value (when in debugging mode) can be returned
284
/// through the ToolTipRequestEventArgs.SetTooltip() method.
286
internal static void HandleToolTipRequest(ToolTipRequestEventArgs e)
290
Location logicPos = e.LogicalPosition;
291
var doc = e.Editor.Document;
292
IExpressionFinder expressionFinder = ParserService.GetExpressionFinder(e.Editor.FileName);
293
if (expressionFinder == null)
295
var currentLine = doc.GetLine(logicPos.Y);
296
if (logicPos.X > currentLine.Length)
298
string textContent = doc.Text;
299
ExpressionResult expressionResult = expressionFinder.FindFullExpression(textContent, doc.PositionToOffset(logicPos.Line, logicPos.Column));
300
string expression = (expressionResult.Expression ?? "").Trim();
301
if (expression.Length > 0) {
302
// Look if it is variable
303
ResolveResult result = ParserService.Resolve(expressionResult, logicPos.Y, logicPos.X, e.Editor.FileName, textContent);
304
bool debuggerCanShowValue;
305
string toolTipText = GetText(result, expression, out debuggerCanShowValue);
306
if (Control.ModifierKeys == Keys.Control) {
307
toolTipText = "expr: " + expressionResult.ToString() + "\n" + toolTipText;
308
debuggerCanShowValue = false;
310
if (toolTipText != null) {
311
if (debuggerCanShowValue && currentDebugger != null) {
312
object toolTip = currentDebugger.GetTooltipControl(e.LogicalPosition, expressionResult.Expression);
314
e.SetToolTip(toolTip);
316
e.SetToolTip(toolTipText);
318
e.SetToolTip(toolTipText);
323
if (Control.ModifierKeys == Keys.Control) {
324
e.SetToolTip("no expr: " + expressionResult.ToString());
330
static string GetText(ResolveResult result, string expression, out bool debuggerCanShowValue)
332
debuggerCanShowValue = false;
333
if (result == null) {
334
// when pressing control, show the expression even when it could not be resolved
335
return (Control.ModifierKeys == Keys.Control) ? "" : null;
337
if (result is MixedResolveResult)
338
return GetText(((MixedResolveResult)result).PrimaryResult, expression, out debuggerCanShowValue);
339
else if (result is DelegateCallResolveResult)
340
return GetText(((DelegateCallResolveResult)result).Target, expression, out debuggerCanShowValue);
342
IAmbience ambience = AmbienceService.GetCurrentAmbience();
343
ambience.ConversionFlags = ConversionFlags.StandardConversionFlags | ConversionFlags.UseFullyQualifiedMemberNames;
344
if (result is MemberResolveResult) {
345
return GetMemberText(ambience, ((MemberResolveResult)result).ResolvedMember, expression, out debuggerCanShowValue);
346
} else if (result is LocalResolveResult) {
347
LocalResolveResult rr = (LocalResolveResult)result;
348
ambience.ConversionFlags = ConversionFlags.UseFullyQualifiedTypeNames
349
| ConversionFlags.ShowReturnType | ConversionFlags.ShowDefinitionKeyWord;
350
StringBuilder b = new StringBuilder();
352
b.Append("parameter ");
354
b.Append("local variable ");
355
b.Append(ambience.Convert(rr.Field));
356
if (currentDebugger != null) {
357
string currentValue = currentDebugger.GetValueAsString(rr.VariableName);
358
if (currentValue != null) {
359
debuggerCanShowValue = true;
361
if (currentValue.Length > 256)
362
currentValue = currentValue.Substring(0, 256) + "...";
363
b.Append(currentValue);
367
} else if (result is NamespaceResolveResult) {
368
return "namespace " + ((NamespaceResolveResult)result).Name;
369
} else if (result is TypeResolveResult) {
370
IClass c = ((TypeResolveResult)result).ResolvedClass;
372
return GetMemberText(ambience, c, expression, out debuggerCanShowValue);
374
return ambience.Convert(result.ResolvedType);
375
} else if (result is MethodGroupResolveResult) {
376
MethodGroupResolveResult mrr = result as MethodGroupResolveResult;
377
IMethod m = mrr.GetMethodIfSingleOverload();
378
IMethod m2 = mrr.GetMethodWithEmptyParameterList();
380
return GetMemberText(ambience, m, expression, out debuggerCanShowValue);
381
else if (ambience is VBNetAmbience && m2 != null)
382
return GetMemberText(ambience, m2, expression, out debuggerCanShowValue);
384
return "Overload of " + ambience.Convert(mrr.ContainingType) + "." + mrr.Name;
386
if (Control.ModifierKeys == Keys.Control) {
387
if (result.ResolvedType != null)
388
return "expression of type " + ambience.Convert(result.ResolvedType);
390
return "ResolveResult without ResolvedType";
397
static string GetMemberText(IAmbience ambience, IEntity member, string expression, out bool debuggerCanShowValue)
399
bool tryDisplayValue = false;
400
debuggerCanShowValue = false;
401
StringBuilder text = new StringBuilder();
402
if (member is IField) {
403
text.Append(ambience.Convert(member as IField));
404
tryDisplayValue = true;
405
} else if (member is IProperty) {
406
text.Append(ambience.Convert(member as IProperty));
407
tryDisplayValue = true;
408
} else if (member is IEvent) {
409
text.Append(ambience.Convert(member as IEvent));
410
} else if (member is IMethod) {
411
text.Append(ambience.Convert(member as IMethod));
412
} else if (member is IClass) {
413
text.Append(ambience.Convert(member as IClass));
415
text.Append("unknown member ");
416
text.Append(member.ToString());
418
if (tryDisplayValue && currentDebugger != null) {
419
LoggingService.Info("asking debugger for value of '" + expression + "'");
420
string currentValue = currentDebugger.GetValueAsString(expression);
421
if (currentValue != null) {
422
debuggerCanShowValue = true;
424
text.Append(currentValue);
427
string documentation = member.Documentation;
428
if (documentation != null && documentation.Length > 0) {
430
text.Append(ICSharpCode.SharpDevelop.Editor.CodeCompletion.CodeCompletionItem.ConvertDocumentation(documentation));
432
return text.ToString();
438
/// Provides the default debugger tooltips on the text area.
441
/// This class must be public because it is accessed via the AddInTree.
443
public class DebuggerTextAreaToolTipProvider : ITextAreaToolTipProvider
445
public void HandleToolTipRequest(ToolTipRequestEventArgs e)
447
DebuggerService.HandleToolTipRequest(e);