diff options
author | Jeffrey Stedfast <jestedfa@microsoft.com> | 2019-06-05 23:06:47 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-06-05 23:06:47 +0300 |
commit | b129692b19639678c4165966762541d063a123e2 (patch) | |
tree | ef02b3cf4eece77c357a1c780e642dd3aef57661 /main/src/addins | |
parent | ed196e749cbb86fab1d19f1108bd210fa487febf (diff) | |
parent | a94aba185efcb7fd3aa2f977ba241623a4197dc9 (diff) |
Merge pull request #7787 from mono/jstedfast-breakpoint-span-resolver
[Debugger] Added new breakpoint span resolver for C#
Diffstat (limited to 'main/src/addins')
7 files changed, 206 insertions, 18 deletions
diff --git a/main/src/addins/CSharpBinding/CSharpBinding.csproj b/main/src/addins/CSharpBinding/CSharpBinding.csproj index 2027c0db24..3766cbf2b2 100644 --- a/main/src/addins/CSharpBinding/CSharpBinding.csproj +++ b/main/src/addins/CSharpBinding/CSharpBinding.csproj @@ -291,6 +291,7 @@ <Compile Include="MonoDevelop.CSharp.Refactoring\CommandArgsFactories.cs" /> <Compile Include="MonoDevelop.CSharp.Debugger\DebuggerCompletionController.cs" /> <Compile Include="MonoDevelop.CSharp.ClassOutline\CSharpOutlineTextEditorExtensionProvider.cs" /> + <Compile Include="MonoDevelop.CSharp.Debugger\CSharpBreakpointSpanResolver.cs" /> </ItemGroup> <ItemGroup> <None Include="MonoDevelop.CSharp.CodeRefactorings\ConvertToEnum\ConvertToEnumCodeRefactoringProvider.cs" /> diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Debugger/CSharpBreakpointSpanResolver.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Debugger/CSharpBreakpointSpanResolver.cs new file mode 100644 index 0000000000..992f77529d --- /dev/null +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Debugger/CSharpBreakpointSpanResolver.cs @@ -0,0 +1,58 @@ +// +// CSharpBreakpointSpanResolver.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.Threading; +using System.Threading.Tasks; + +using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Text; +using Microsoft.CodeAnalysis.CSharp.EditAndContinue; + +using MonoDevelop.Debugger; +using MonoDevelop.Ide.Gui.Documents; + +namespace MonoDevelop.CSharp.Debugger +{ + [ExportDocumentControllerExtension (MimeType = "text/x-csharp")] + public class CSharpBreakpointSpanResolver : DocumentControllerExtension, IBreakpointSpanResolver + { + public override Task<bool> SupportsController (DocumentController controller) + { + return Task.FromResult (controller.GetContent<ITextBuffer> () != null); + } + + public async Task<Span> GetBreakpointSpanAsync (ITextBuffer buffer, int position, CancellationToken cancellationToken) + { + var document = buffer.AsTextContainer ().GetOpenDocumentInCurrentContext (); + var tree = await document.GetSyntaxTreeAsync (cancellationToken); + + BreakpointSpans.TryGetBreakpointSpan (tree, position, cancellationToken, out var span); + + return new Span (span.Start, span.Length); + } + } +} diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/BreakpointManager.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/BreakpointManager.cs index f3f690e450..93058fe629 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/BreakpointManager.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/BreakpointManager.cs @@ -37,15 +37,17 @@ namespace MonoDevelop.Debugger void TextBuffer_Changed (object sender, TextContentChangedEventArgs e) { foreach (var breakpoint in breakpoints.Values) { - var newSpan = breakpoint.TrackingSpan.GetSpan (e.After); - if (newSpan.IsEmpty) { + var span = breakpoint.TrackingSpan.GetSpan (e.After); + + if (span.IsEmpty || string.IsNullOrWhiteSpace (span.GetText ())) { DebuggingService.Breakpoints.Remove (breakpoint.Breakpoint); continue; } - var newLineNumber = e.After.GetLineFromPosition (newSpan.Start).LineNumber + 1; - if (breakpoint.Breakpoint.Line != newLineNumber) { + + var newLineNumber = e.After.GetLineFromPosition (span.Start).LineNumber + 1; + + if (breakpoint.Breakpoint.Line != newLineNumber) DebuggingService.Breakpoints.UpdateBreakpointLine (breakpoint.Breakpoint, newLineNumber); - } } } @@ -58,38 +60,47 @@ namespace MonoDevelop.Debugger private Dictionary<Breakpoint, ManagerBreakpoint> breakpoints = new Dictionary<Breakpoint, ManagerBreakpoint> (); - private void OnBreakpointsChanged (object sender, EventArgs eventArgs) + private async void OnBreakpointsChanged (object sender, EventArgs eventArgs) { - var snapshot = textBuffer.CurrentSnapshot; var newBreakpoints = new Dictionary<Breakpoint, ManagerBreakpoint> (); - bool needsUpdate = false; + var snapshot = textBuffer.CurrentSnapshot; + var 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; + + var line = snapshot.GetLineFromLineNumber (breakpoint.Line - 1); + var position = line.Start.Position + breakpoint.Column; + var span = await DebuggingService.GetBreakpointSpanAsync (textDocument, position); + if (breakpoints.TryGetValue (breakpoint, out var existingBreakpoint)) { newBreakpoints.Add (breakpoint, existingBreakpoint); - if (existingBreakpoint.Span != newSpan.Span) { - // Update if anything was modifed + if (existingBreakpoint.Span != span) { + // Update if anything was modified + existingBreakpoint.Span = span; needsUpdate = true; - existingBreakpoint.Span = newSpan.Span; } } else { // Update if anything was added - needsUpdate = true; - newBreakpoints.Add (breakpoint, new ManagerBreakpoint () { + newBreakpoints.Add (breakpoint, new ManagerBreakpoint { Breakpoint = breakpoint, - TrackingSpan = snapshot.CreateTrackingSpan (newSpan, SpanTrackingMode.EdgeExclusive), - Span = newSpan.Span + TrackingSpan = snapshot.CreateTrackingSpan (span, SpanTrackingMode.EdgeExclusive), + Span = span }); + needsUpdate = true; } } + // 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))); } diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj index 12b7e203fb..bdecc781e7 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj @@ -174,6 +174,8 @@ <Compile Include="MonoDevelop.Debugger.VSTextView\QuickInfo\IDebugInfoProvider.cs" /> <Compile Include="MonoDevelop.Debugger.VSTextView\BreakpointManagerService.cs" /> <Compile Include="MonoDevelop.Debugger.VSTextView\BreakpointManager.cs" /> + <Compile Include="MonoDevelop.Debugger\IBreakpointSpanResolver.cs" /> + <Compile Include="MonoDevelop.Debugger\DefaultBreakpointSpanResolver.cs" /> </ItemGroup> <ItemGroup Condition="$(OS) != 'Windows_NT'"> <Compile Include="MonoDevelop.Debugger.VSTextView\ExceptionCaught\ExceptionCaughtProvider.cs" /> diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs index 950385ee18..9e19487fc4 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs @@ -1331,11 +1331,19 @@ namespace MonoDevelop.Debugger static void OnLineCountChanged (object ob, LineCountEventArgs a) { lock (breakpoints) { - foreach (Breakpoint bp in breakpoints.GetBreakpoints ()) { + foreach (var bp in breakpoints.GetBreakpoints ()) { if (bp.FileName == a.TextFile.Name) { if (bp.Line > a.LineNumber) { + var startIndex = a.TextFile.GetPositionFromLineColumn (bp.Line, bp.Column); + var endIndex = a.TextFile.GetPositionFromLineColumn (bp.Line + 1, 0) - 1; + + if (endIndex < startIndex) + endIndex = startIndex; + + var text = a.TextFile.GetText (startIndex, endIndex); + // If the line that has the breakpoint is deleted, delete the breakpoint, otherwise update the line #. - if (bp.Line + a.LineCount >= a.LineNumber) + if (bp.Line + a.LineCount >= a.LineNumber && !string.IsNullOrWhiteSpace (text)) breakpoints.UpdateBreakpointLine (bp, bp.Line + a.LineCount); else breakpoints.Remove (bp); @@ -1482,6 +1490,19 @@ namespace MonoDevelop.Debugger return result; return frame.GetExpressionCompletionData (exp); } + + public static Task<Span> GetBreakpointSpanAsync (ITextDocument document, int position, CancellationToken cancellationToken = default (CancellationToken)) + { + var doc = IdeApp.Workbench.GetDocument (document.FilePath); + IBreakpointSpanResolver resolver = null; + + if (doc != null) + resolver = doc.GetContent<IBreakpointSpanResolver> (); + + resolver = resolver ?? new DefaultBreakpointSpanResolver (); + + return resolver.GetBreakpointSpanAsync (document.TextBuffer, position, cancellationToken); + } } class FeatureCheckerHandlerFactory : IExecutionHandler diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DefaultBreakpointSpanResolver.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DefaultBreakpointSpanResolver.cs new file mode 100644 index 0000000000..19c245fafb --- /dev/null +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DefaultBreakpointSpanResolver.cs @@ -0,0 +1,47 @@ +// +// DefaultBreakpointSpanResolver.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.Threading; +using System.Threading.Tasks; + +using Microsoft.VisualStudio.Text; + +namespace MonoDevelop.Debugger +{ + public class DefaultBreakpointSpanResolver : IBreakpointSpanResolver + { + public Task<Span> GetBreakpointSpanAsync (ITextBuffer buffer, int position, CancellationToken cancellationToken) + { + try { + var line = buffer.CurrentSnapshot.GetLineFromPosition (position); + + return Task.FromResult (Span.FromBounds (line.Start.Position, line.End.Position)); + } catch { + return Task.FromResult (default (Span)); + } + } + } +} diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/IBreakpointSpanResolver.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/IBreakpointSpanResolver.cs new file mode 100644 index 0000000000..25f337ac54 --- /dev/null +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/IBreakpointSpanResolver.cs @@ -0,0 +1,48 @@ +// +// IBreakpointSpanResolver.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.Threading; +using System.Threading.Tasks; + +using Microsoft.VisualStudio.Text; + +namespace MonoDevelop.Debugger +{ + /// <summary> + /// An interface for resolving the span of a breakpoint. + /// </summary> + public interface IBreakpointSpanResolver + { + /// <summary> + /// Resolve the span of a breakpoint. + /// </summary> + /// <param name="buffer">The text buffer.</param> + /// <param name="position">The cursor position.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>The span of the breakpoint.</returns> + Task<Span> GetBreakpointSpanAsync (ITextBuffer buffer, int position, CancellationToken cancellationToken); + } +} |