1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.Text;
using Mono.Debugging.Client;
using MonoDevelop.Core;
using System.Linq;
namespace MonoDevelop.Debugger
{
class BreakpointManager
{
private ITextBuffer textBuffer;
private readonly ITextDocument textDocument;
public BreakpointManager (ITextBuffer textBuffer)
{
this.textBuffer = textBuffer;
if (textBuffer.Properties.TryGetProperty (typeof (ITextDocument), out textDocument) && textDocument.FilePath != null) {
textDocument.FileActionOccurred += TextDocument_FileActionOccurred;
} else {
LoggingService.LogWarning ("Failed to get filename of textbuffer, breakpoints integration will not work.");
return;
}
textBuffer.Changed += TextBuffer_Changed;
DebuggingService.Breakpoints.Changed += OnBreakpointsChanged;
DebuggingService.Breakpoints.BreakpointStatusChanged += OnBreakpointsChanged;
DebuggingService.Breakpoints.BreakpointModified += OnBreakpointsChanged;
OnBreakpointsChanged (null, null);
}
private void TextDocument_FileActionOccurred (object sender, TextDocumentFileActionEventArgs e)
{
if (e.FileActionType == FileActionTypes.DocumentRenamed)
OnBreakpointsChanged (null, null);
}
void TextBuffer_Changed (object sender, TextContentChangedEventArgs e)
{
foreach (var breakpoint in breakpoints.Values) {
var newSpan = breakpoint.TrackingSpan.GetSpan (e.After);
if (newSpan.IsEmpty) {
DebuggingService.Breakpoints.Remove (breakpoint.Breakpoint);
continue;
}
var newLineNumber = e.After.GetLineFromPosition (newSpan.Start).LineNumber + 1;
if (breakpoint.Breakpoint.Line != newLineNumber) {
DebuggingService.Breakpoints.UpdateBreakpointLine (breakpoint.Breakpoint, newLineNumber);
}
}
}
class ManagerBreakpoint
{
public Breakpoint Breakpoint { get; set; }
public ITrackingSpan TrackingSpan { get; set; }
public Span Span { get; set; }
}
private Dictionary<Breakpoint, ManagerBreakpoint> breakpoints = new Dictionary<Breakpoint, ManagerBreakpoint> ();
private void OnBreakpointsChanged (object sender, EventArgs eventArgs)
{
var snapshot = textBuffer.CurrentSnapshot;
var newBreakpoints = new Dictionary<Breakpoint, ManagerBreakpoint> ();
bool needsUpdate = false;
foreach (var breakpoint in DebuggingService.Breakpoints.GetBreakpointsAtFile (textDocument.FilePath)) {
if (breakpoint.Line > snapshot.LineCount)
continue;
if (eventArgs is BreakpointEventArgs breakpointEventArgs && breakpointEventArgs.Breakpoint == breakpoint)
needsUpdate = true;
var newSpan = snapshot.GetLineFromLineNumber (breakpoint.Line - 1).Extent;
if (breakpoints.TryGetValue (breakpoint, out var existingBreakpoint)) {
newBreakpoints.Add (breakpoint, existingBreakpoint);
if (existingBreakpoint.Span != newSpan.Span) {
// Update if anything was modifed
needsUpdate = true;
existingBreakpoint.Span = newSpan.Span;
}
} else {
// Update if anything was added
needsUpdate = true;
newBreakpoints.Add (breakpoint, new ManagerBreakpoint () {
Breakpoint = breakpoint,
TrackingSpan = snapshot.CreateTrackingSpan (newSpan, SpanTrackingMode.EdgeExclusive),
Span = newSpan.Span
});
}
}
// Update if anything was removed
if (needsUpdate || breakpoints.Keys.Except (newBreakpoints.Keys).Any ())
needsUpdate = true;
breakpoints = newBreakpoints;
if (needsUpdate)
BreakpointsChanged?.Invoke (this, new SnapshotSpanEventArgs (new SnapshotSpan (snapshot, 0, snapshot.Length)));
}
public event EventHandler<SnapshotSpanEventArgs> BreakpointsChanged;
public void Dispose ()
{
BreakpointsChanged = null;
textBuffer.Changed -= TextBuffer_Changed;
DebuggingService.Breakpoints.Changed -= OnBreakpointsChanged;
DebuggingService.Breakpoints.BreakpointStatusChanged -= OnBreakpointsChanged;
DebuggingService.Breakpoints.BreakpointModified -= OnBreakpointsChanged;
if (textDocument != null)
textDocument.FileActionOccurred -= TextDocument_FileActionOccurred;
}
public IEnumerable<BreakpointSpan> GetBreakpoints (ITextSnapshot snapshot)
{
foreach (var item in breakpoints.Values) {
yield return new BreakpointSpan (item.Breakpoint, item.TrackingSpan.GetSpan (snapshot));
}
}
}
class BreakpointSpan
{
public Breakpoint Breakpoint { get; }
public SnapshotSpan Span { get; }
public BreakpointSpan (Breakpoint breakpoint, SnapshotSpan span)
{
Breakpoint = breakpoint;
Span = span;
}
}
}
|