#develop (short for SharpDevelop) is a free IDE for .NET programming languages.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

466 lines
15 KiB

// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.TextEditor.Document;
using ICSharpCode.TextEditor;
using System.Drawing;
using System.Windows.Forms;
using BM = ICSharpCode.SharpDevelop.Bookmarks;
namespace ICSharpCode.Core
{
public static class DebuggerService
{
static IDebugger currentDebugger;
static DebuggerDescriptor[] debuggers;
static string oldLayoutConfiguration = "Default";
static DebuggerService()
{
ProjectService.SolutionLoaded += delegate {
ClearDebugMessages();
};
WorkbenchSingleton.WorkbenchCreated += new EventHandler(WorkspaceCreated);
BM.BookmarkManager.Added += BookmarkAdded;
BM.BookmarkManager.Removed += BookmarkRemoved;
}
static void GetDescriptors()
{
if (debuggers == null) {
debuggers = (DebuggerDescriptor[])AddInTree.BuildItems("/SharpDevelop/Services/DebuggerService/Debugger", null, false).ToArray(typeof(DebuggerDescriptor));
}
}
static IDebugger GetCompatibleDebugger()
{
GetDescriptors();
IProject project = null;
if (ProjectService.OpenSolution != null) {
project = ProjectService.OpenSolution.StartupProject;
}
foreach (DebuggerDescriptor d in debuggers) {
if (d.Debugger != null && d.Debugger.CanDebug(project)) {
return d.Debugger;
}
}
return new DefaultDebugger();
}
/// <summary>
/// Gets the current debugger. The debugger addin is loaded on demand; so if you
/// just want to check a property like IsDebugging, check <see cref="IsDebuggerLoaded"/>
/// before using this property.
/// </summary>
public static IDebugger CurrentDebugger {
get {
if (currentDebugger == null) {
currentDebugger = GetCompatibleDebugger();
currentDebugger.DebugStarted += new EventHandler(OnDebugStarted);
currentDebugger.DebugStopped += new EventHandler(OnDebugStopped);
}
return currentDebugger;
}
}
public static DebuggerDescriptor Descriptor {
get {
GetDescriptors();
if (debuggers.Length > 0)
return debuggers[0];
return null;
}
}
/// <summary>
/// Returns true if debugger is already loaded.
/// </summary>
public static bool IsDebuggerLoaded {
get {
return currentDebugger != null;
}
}
public static event EventHandler DebugStarted;
public static event EventHandler DebugStopped;
static void OnDebugStarted(object sender, EventArgs e)
{
WorkbenchSingleton.Workbench.WorkbenchLayout.StoreConfiguration();
oldLayoutConfiguration = LayoutConfiguration.CurrentLayoutName;
LayoutConfiguration.CurrentLayoutName = "Debug";
ClearDebugMessages();
if (DebugStarted != null)
DebugStarted(null, e);
}
static void OnDebugStopped(object sender, EventArgs e)
{
CurrentLineBookmark.Remove();
WorkbenchSingleton.Workbench.WorkbenchLayout.StoreConfiguration();
LayoutConfiguration.CurrentLayoutName = oldLayoutConfiguration;
if (DebugStopped != null)
DebugStopped(null, e);
}
static MessageViewCategory debugCategory = null;
static void EnsureDebugCategory()
{
if (debugCategory == null) {
debugCategory = new MessageViewCategory("Debug", "${res:MainWindow.Windows.OutputWindow.DebugCategory}");
CompilerMessageView compilerMessageView = (CompilerMessageView)WorkbenchSingleton.Workbench.GetPad(typeof(CompilerMessageView)).PadContent;
compilerMessageView.AddCategory(debugCategory);
}
}
public static void ClearDebugMessages()
{
EnsureDebugCategory();
debugCategory.ClearText();
}
public static void PrintDebugMessage(string msg)
{
EnsureDebugCategory();
debugCategory.AppendText(msg);
}
public static event EventHandler<BreakpointBookmarkEventArgs> BreakPointChanged;
public static event EventHandler<BreakpointBookmarkEventArgs> BreakPointAdded;
public static event EventHandler<BreakpointBookmarkEventArgs> BreakPointRemoved;
static void OnBreakPointChanged(BreakpointBookmarkEventArgs e)
{
if (BreakPointChanged != null) {
BreakPointChanged(null, e);
}
}
static void OnBreakPointAdded(BreakpointBookmarkEventArgs e)
{
if (BreakPointAdded != null) {
BreakPointAdded(null, e);
}
}
static void OnBreakPointRemoved(BreakpointBookmarkEventArgs e)
{
if (BreakPointRemoved != null) {
BreakPointRemoved(null, e);
}
}
public static IList<BreakpointBookmark> Breakpoints {
get {
List<BreakpointBookmark> breakpoints = new List<BreakpointBookmark>();
foreach (BM.SDBookmark bookmark in BM.BookmarkManager.Bookmarks) {
BreakpointBookmark breakpoint = bookmark as BreakpointBookmark;
if (breakpoint != null) {
breakpoints.Add(breakpoint);
}
}
return breakpoints.AsReadOnly();
}
}
static void BookmarkAdded(object sender, BM.BookmarkEventArgs e)
{
BreakpointBookmark bb = e.Bookmark as BreakpointBookmark;
if (bb != null) {
bb.LineNumberChanged += BookmarkChanged;
OnBreakPointAdded(new BreakpointBookmarkEventArgs(bb));
}
}
static void BookmarkRemoved(object sender, BM.BookmarkEventArgs e)
{
BreakpointBookmark bb = e.Bookmark as BreakpointBookmark;
if (bb != null) {
bb.RemoveMarker();
OnBreakPointRemoved(new BreakpointBookmarkEventArgs(bb));
}
}
static void BookmarkChanged(object sender, EventArgs e)
{
BreakpointBookmark bb = sender as BreakpointBookmark;
if (bb != null) {
OnBreakPointChanged(new BreakpointBookmarkEventArgs(bb));
}
}
public static void ToggleBreakpointAt(IDocument document, string fileName, int lineNumber)
{
foreach (Bookmark m in document.BookmarkManager.Marks) {
BreakpointBookmark breakpoint = m as BreakpointBookmark;
if (breakpoint != null) {
if (breakpoint.LineNumber == lineNumber) {
document.BookmarkManager.RemoveMark(m);
return;
}
}
}
foreach (char ch in document.GetText(document.GetLineSegment(lineNumber))) {
if (!char.IsWhiteSpace(ch)) {
document.BookmarkManager.AddMark(new BreakpointBookmark(fileName, document, lineNumber));
document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, lineNumber));
document.CommitUpdate();
break;
}
}
}
static void WorkspaceCreated(object sender, EventArgs args)
{
WorkbenchSingleton.Workbench.ViewOpened += new ViewContentEventHandler(ViewContentOpened);
WorkbenchSingleton.Workbench.ViewClosed += new ViewContentEventHandler(ViewContentClosed);
}
static void ViewContentOpened(object sender, ViewContentEventArgs e)
{
if (e.Content.Control is TextEditor.TextEditorControl) {
TextArea textArea = ((TextEditor.TextEditorControl)e.Content.Control).ActiveTextAreaControl.TextArea;
textArea.IconBarMargin.MouseDown += IconBarMouseDown;
textArea.ToolTipRequest += TextAreaToolTipRequest;
textArea.MouseLeave += TextAreaMouseLeave;
}
}
static void ViewContentClosed(object sender, ViewContentEventArgs e)
{
if (e.Content.Control is TextEditor.TextEditorControl) {
TextArea textArea = ((TextEditor.TextEditorControl)e.Content.Control).ActiveTextAreaControl.TextArea;
textArea.IconBarMargin.MouseDown -= IconBarMouseDown;
textArea.ToolTipRequest -= TextAreaToolTipRequest;
textArea.MouseLeave -= TextAreaMouseLeave;
}
}
public static void RemoveCurrentLineMarker()
{
CurrentLineBookmark.Remove();
}
public static void JumpToCurrentLine(string SourceFullFilename, int StartLine, int StartColumn, int EndLine, int EndColumn)
{
IViewContent viewContent = FileService.JumpToFilePosition(SourceFullFilename, StartLine - 1, StartColumn - 1);
CurrentLineBookmark.SetPosition(viewContent, StartLine, StartColumn, EndLine, EndColumn);
}
static void IconBarMouseDown(AbstractMargin iconBar, Point mousepos, MouseButtons mouseButtons)
{
if (mouseButtons != MouseButtons.Left) return;
Rectangle viewRect = iconBar.TextArea.TextView.DrawingPosition;
Point logicPos = iconBar.TextArea.TextView.GetLogicalPosition(0, mousepos.Y - viewRect.Top);
if (logicPos.Y >= 0 && logicPos.Y < iconBar.TextArea.Document.TotalNumberOfLines) {
ToggleBreakpointAt(iconBar.TextArea.Document, iconBar.TextArea.MotherTextEditorControl.FileName, logicPos.Y);
iconBar.TextArea.Refresh(iconBar);
}
}
#region Tool tips
static DebuggerGridControl oldToolTipControl;
/// <summary>
/// This function shows variable values as tooltips
/// </summary>
static void TextAreaToolTipRequest(object sender, ToolTipRequestEventArgs e)
{
DebuggerGridControl toolTipControl = null;
try {
TextArea textArea = (TextArea)sender;
if (e.ToolTipShown) return;
if (oldToolTipControl != null && !oldToolTipControl.AllowClose) return;
if (!CodeCompletionOptions.TooltipsEnabled) return;
if (CodeCompletionOptions.TooltipsOnlyWhenDebugging) {
if (currentDebugger == null) return;
if (!currentDebugger.IsDebugging) return;
}
if (e.InDocument) {
Point logicPos = e.LogicalPosition;
IDocument doc = textArea.Document;
IExpressionFinder expressionFinder = ParserService.GetExpressionFinder(textArea.MotherTextEditorControl.FileName);
if (expressionFinder == null)
return;
LineSegment seg = doc.GetLineSegment(logicPos.Y);
if (logicPos.X > seg.Length - 1)
return;
string textContent = doc.TextContent;
ExpressionResult expressionResult = expressionFinder.FindFullExpression(textContent, seg.Offset + logicPos.X);
string expression = (expressionResult.Expression ?? "").Trim();
if (expression.Length > 0) {
// Look if it is variable
ResolveResult result = ParserService.Resolve(expressionResult, logicPos.Y + 1, logicPos.X + 1, textArea.MotherTextEditorControl.FileName, textContent);
bool debuggerCanShowValue;
string toolTipText = GetText(result, expression, out debuggerCanShowValue);
if (toolTipText != null) {
if (Control.ModifierKeys == Keys.Control) {
toolTipText = "expr: " + expressionResult.ToString() + "\n" + toolTipText;
} else if (debuggerCanShowValue && currentDebugger != null) {
toolTipControl = currentDebugger.GetTooltipControl(expressionResult.Expression);
toolTipText = null;
}
}
if (toolTipText != null) {
e.ShowToolTip(toolTipText);
}
CloseOldToolTip();
if (toolTipControl != null) {
toolTipControl.ShowForm(textArea, logicPos);
}
oldToolTipControl = toolTipControl;
}
}
} catch (Exception ex) {
ICSharpCode.Core.MessageService.ShowError(ex);
} finally {
if (toolTipControl == null && CanCloseOldToolTip)
CloseOldToolTip();
}
}
static bool CanCloseOldToolTip {
get {
return oldToolTipControl != null && oldToolTipControl.AllowClose;
}
}
static void CloseOldToolTip()
{
if (oldToolTipControl != null) {
Form frm = oldToolTipControl.FindForm();
if (frm != null) frm.Close();
oldToolTipControl = null;
}
}
static void TextAreaMouseLeave(object source, EventArgs e)
{
if (CanCloseOldToolTip && !oldToolTipControl.IsMouseOver)
CloseOldToolTip();
}
static string GetText(ResolveResult result, string expression, out bool debuggerCanShowValue)
{
debuggerCanShowValue = false;
if (result == null) {
// when pressing control, show the expression even when it could not be resolved
return (Control.ModifierKeys == Keys.Control) ? "" : null;
}
if (result is MixedResolveResult)
return GetText(((MixedResolveResult)result).PrimaryResult, expression, out debuggerCanShowValue);
IAmbience ambience = AmbienceService.CurrentAmbience;
ambience.ConversionFlags = ConversionFlags.StandardConversionFlags | ConversionFlags.ShowAccessibility;
if (result is MemberResolveResult) {
return GetMemberText(ambience, ((MemberResolveResult)result).ResolvedMember, expression, out debuggerCanShowValue);
} else if (result is LocalResolveResult) {
LocalResolveResult rr = (LocalResolveResult)result;
ambience.ConversionFlags = ConversionFlags.UseFullyQualifiedNames
| ConversionFlags.ShowReturnType
| ConversionFlags.QualifiedNamesOnlyForReturnTypes;
StringBuilder b = new StringBuilder();
if (rr.IsParameter)
b.Append("parameter ");
else
b.Append("local variable ");
b.Append(ambience.Convert(rr.Field));
if (currentDebugger != null) {
string currentValue = currentDebugger.GetValueAsString(rr.Field.Name);
if (currentValue != null) {
debuggerCanShowValue = true;
b.Append(" = ");
b.Append(currentValue);
}
}
return b.ToString();
} else if (result is NamespaceResolveResult) {
return "namespace " + ((NamespaceResolveResult)result).Name;
} else if (result is TypeResolveResult) {
IClass c = ((TypeResolveResult)result).ResolvedClass;
if (c != null)
return GetMemberText(ambience, c, expression, out debuggerCanShowValue);
else
return ambience.Convert(result.ResolvedType);
} else if (result is MethodResolveResult) {
MethodResolveResult mrr = result as MethodResolveResult;
IMethod m = mrr.GetMethodIfSingleOverload();
if (m != null)
return GetMemberText(ambience, m, expression, out debuggerCanShowValue);
else
return "Overload of " + ambience.Convert(mrr.ContainingType) + "." + mrr.Name;
} else {
if (Control.ModifierKeys == Keys.Control) {
if (result.ResolvedType != null)
return "expression of type " + ambience.Convert(result.ResolvedType);
else
return "ResolveResult without ResolvedType";
} else {
return null;
}
}
}
static string GetMemberText(IAmbience ambience, IDecoration member, string expression, out bool debuggerCanShowValue)
{
bool tryDisplayValue = false;
debuggerCanShowValue = false;
StringBuilder text = new StringBuilder();
if (member is IField) {
text.Append(ambience.Convert(member as IField));
tryDisplayValue = true;
} else if (member is IProperty) {
text.Append(ambience.Convert(member as IProperty));
tryDisplayValue = true;
} else if (member is IEvent) {
text.Append(ambience.Convert(member as IEvent));
} else if (member is IMethod) {
text.Append(ambience.Convert(member as IMethod));
} else if (member is IClass) {
text.Append(ambience.Convert(member as IClass));
} else {
text.Append("unknown member ");
text.Append(member.ToString());
}
if (tryDisplayValue && currentDebugger != null) {
LoggingService.Info("asking debugger for value of '" + expression + "'");
string currentValue = currentDebugger.GetValueAsString(expression);
if (currentValue != null) {
debuggerCanShowValue = true;
text.Append(" = ");
text.Append(currentValue);
}
}
string documentation = member.Documentation;
if (documentation != null && documentation.Length > 0) {
text.Append('\n');
text.Append(ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor.CodeCompletionData.GetDocumentation(documentation));
}
return text.ToString();
}
#endregion
}
}