Skip to content

Commit

Permalink
[gh-4] Initial version of breakpoint overlays.
Browse files Browse the repository at this point in the history
  • Loading branch information
ashmind committed Jan 3, 2015
1 parent c1a4cfe commit de6b372
Show file tree
Hide file tree
Showing 20 changed files with 285 additions and 50 deletions.
Binary file added #assets/BreakpointOverlay.pdn
Binary file not shown.
1 change: 1 addition & 0 deletions ExceptionBreaker.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<s:String x:Key="/Default/CodeStyle/CSharpUsing/KeepImports/=System/@EntryIndexedValue">System</s:String>
<s:String x:Key="/Default/CodeStyle/CSharpUsing/KeepImports/=System_002ECollections_002EGeneric/@EntryIndexedValue">System.Collections.Generic</s:String>
<s:String x:Key="/Default/CodeStyle/CSharpUsing/KeepImports/=System_002ELinq/@EntryIndexedValue">System.Linq</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DTE/@EntryIndexedValue">DTE</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=VS/@EntryIndexedValue">VS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=LocalConstants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
8 changes: 4 additions & 4 deletions ExceptionBreaker/Breakpoints/BreakpointEventProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
namespace ExceptionBreaker.Breakpoints {
public class BreakpointEventProcessor : IDebugEventCallback2, IDisposable {
private readonly IVsDebugger _debugger;
private readonly BreakpointExtraDataProvider _extraDataProvider;
private readonly BreakpointExtraDataStore _extraDataStore;
private readonly ExceptionBreakManager _breakManager;
private readonly IDiagnosticLogger _logger;

public BreakpointEventProcessor(IVsDebugger debugger, BreakpointExtraDataProvider extraDataProvider, ExceptionBreakManager breakManager, IDiagnosticLogger logger) {
public BreakpointEventProcessor(IVsDebugger debugger, BreakpointExtraDataStore extraDataStore, ExceptionBreakManager breakManager, IDiagnosticLogger logger) {
_debugger = debugger;
_extraDataProvider = extraDataProvider;
_extraDataStore = extraDataStore;
_breakManager = breakManager;
_logger = logger;

Expand Down Expand Up @@ -46,7 +46,7 @@ private void ProcessEvent(IDebugEvent2 pEvent) {

_logger.WriteLine("Event: Breakpoint reached.");
foreach (var breakpoint in breakpointEvent.GetBreakpointsAsArraySafe()) {
var extraData = _extraDataProvider.GetData(breakpoint);
var extraData = _extraDataStore.GetData(breakpoint);
if (extraData == null || extraData.ExceptionBreakChange == ExceptionBreakChange.NoChange)
continue;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using AshMind.Extensions;
using EnvDTE80;
using ExceptionBreaker.Core;
using JetBrains.Annotations;
using Microsoft.VisualStudio.Debugger.Interop;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace ExceptionBreaker.Breakpoints {
public class BreakpointExtraDataProvider : ISolutionDataPersister {
[Export]
public class BreakpointExtraDataStore : ISolutionDataPersister {
private readonly BreakpointKeyProvider _keyProvider;
private readonly BreakpointFinder _finder;
private readonly JsonSerializer _jsonSerializer;
private readonly IDiagnosticLogger _logger;
private readonly ConcurrentDictionary<string, BreakpointExtraData> _store = new ConcurrentDictionary<string, BreakpointExtraData>(StringComparer.InvariantCultureIgnoreCase);

public BreakpointExtraDataProvider(BreakpointKeyProvider keyProvider, BreakpointFinder finder, JsonSerializer jsonSerializer, IDiagnosticLogger logger) {
[ImportingConstructor]
public BreakpointExtraDataStore(BreakpointKeyProvider keyProvider, BreakpointFinder finder, IDiagnosticLogger logger) {
_keyProvider = keyProvider;
_finder = finder;
_jsonSerializer = jsonSerializer;
_jsonSerializer = new JsonSerializer {
Converters = { new StringEnumConverter() },
Formatting = Formatting.None
};
_logger = logger;
}

Expand Down
27 changes: 18 additions & 9 deletions ExceptionBreaker/Breakpoints/BreakpointFinder.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using EnvDTE;
using EnvDTE80;
using ExceptionBreaker.Core;
using Microsoft.VisualStudio.Text;

namespace ExceptionBreaker.Breakpoints {
[Export]
public class BreakpointFinder {
private readonly DTE _dte;

public BreakpointFinder(DTE dte) {
_dte = dte;
[ImportingConstructor]
public BreakpointFinder(DTEImport dteImport) {
_dte = dteImport.DTE;
}

public IEnumerable<Breakpoint2> GetAllBreakpoints() {
Expand All @@ -19,15 +24,19 @@ public IEnumerable<Breakpoint2> GetAllBreakpoints() {
public Breakpoint2 GetBreakpointForCommand() {
var path = _dte.ActiveDocument.FullName;
var line = ((TextSelection)_dte.ActiveDocument.Selection).CurrentLine;
return GetBreakpointFromLocation(path, line);
}

var breakpoints = _dte.Debugger.Breakpoints;
for (var i = 1; i <= breakpoints.Count; i++) {
var breakpoint = breakpoints.Item(i);
if (breakpoint.File == path && breakpoint.FileLine == line)
return (Breakpoint2)breakpoint;
}
public Breakpoint2 GetBreakpointFromSpan(SnapshotSpan span, ITextDocument document) {
var path = document.FilePath;
var line = span.Snapshot.GetLineNumberFromPosition(span.Span.Start) + 1;
return GetBreakpointFromLocation(path, line);
}

return null;
private Breakpoint2 GetBreakpointFromLocation(string path, int line) {
return GetAllBreakpoints().FirstOrDefault(
breakpoint => breakpoint.File == path && breakpoint.FileLine == line
);
}
}
}
3 changes: 2 additions & 1 deletion ExceptionBreaker/Breakpoints/BreakpointKeyProvider.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using EnvDTE80;
using ExceptionBreaker.Core;
using JetBrains.Annotations;
using Microsoft.VisualStudio.Debugger.Interop;

namespace ExceptionBreaker.Breakpoints {
[Export]
public class BreakpointKeyProvider {
[NotNull]
public string GetKey(Breakpoint2 breakpoint) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,27 @@
namespace ExceptionBreaker.Breakpoints {
public class BreakpointSetupExceptionsController {
private readonly BreakpointFinder _breakpointFinder;
private readonly BreakpointExtraDataProvider _extraDataProvider;
private readonly BreakpointExtraDataStore _extraDataStore;
private readonly IDiagnosticLogger _logger;
private readonly MenuCommand _command;

public BreakpointSetupExceptionsController(CommandInitializer commandInitializer,
BreakpointFinder breakpointFinder,
BreakpointExtraDataProvider extraDataProvider,
BreakpointExtraDataStore extraDataStore,
IDiagnosticLogger logger)
{
_breakpointFinder = breakpointFinder;
_extraDataProvider = extraDataProvider;
_extraDataStore = extraDataStore;
_logger = logger;
_command = commandInitializer.InitializeCommand(command_Callback, command_BeforeQueryStatus);
}

private void command_BeforeQueryStatus(object sender, EventArgs e) {
try {
var breakpoint = _breakpointFinder.GetBreakpointForCommand();
var extraData = _extraDataProvider.GetData(breakpoint);
var extraData = _extraDataStore.GetData(breakpoint);

var @checked = (extraData != null && extraData.ExceptionBreakChange != ExceptionBreakChange.NoChange);
var @checked = extraData.ExceptionBreakChange != ExceptionBreakChange.NoChange;
if (@checked == _command.Checked)
return;

Expand All @@ -55,10 +55,10 @@ private void command_Callback(object sender, EventArgs e) {
}

private void RequestAndUpdateExceptionSettings(Breakpoint2 breakpoint) {
var extraData = _extraDataProvider.GetData(breakpoint);
var extraData = _extraDataStore.GetData(breakpoint);
var dialog = new BreakpointExceptionsDialog {
ViewModel = new BreakpointExceptionSettings {
Change = extraData != null ? extraData.ExceptionBreakChange : ExceptionBreakChange.NoChange,
Change = extraData.ExceptionBreakChange,
ContinueExecution = !breakpoint.BreakWhenHit
}
};
Expand Down
42 changes: 42 additions & 0 deletions ExceptionBreaker/Breakpoints/Glyphs/BreakpointGlyphFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Formatting;

namespace ExceptionBreaker.Breakpoints.Glyphs {
public class BreakpointGlyphFactory : IGlyphFactory {
public const string Name = "ExceptionBreaker.BreakpointGlyphFactory";
private const int OverlayGlyphSize = 9;
private readonly double _breakpointGlyphSize;

private readonly BitmapImage _image;

public BreakpointGlyphFactory(IWpfTextViewMargin margin) {
_breakpointGlyphSize = margin.MarginSize;

var assemblyName = Assembly.GetExecutingAssembly().GetName().Name;
_image = new BitmapImage(new Uri("pack://application:,,,/" + assemblyName + ";component/Resources/BreakpointOverlay.png"));
_image.Freeze();
}

public UIElement GenerateGlyph(IWpfTextViewLine line, IGlyphTag tag) {
if (!(tag is BreakpointTag))
return null;

return new Image {
Source = _image,
Margin = new Thickness {
Left = _breakpointGlyphSize - OverlayGlyphSize,
Top = _breakpointGlyphSize - OverlayGlyphSize
},
Width = 9,
Height = 9
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Tagging;
using Microsoft.VisualStudio.Utilities;

namespace ExceptionBreaker.Breakpoints.Glyphs {
[Export(typeof(IGlyphFactoryProvider))]
[Name(BreakpointGlyphFactory.Name)]
[Order(After = "VsTextMarker")]
[ContentType("code")]
[TextViewRole(PredefinedTextViewRoles.Debuggable)]
[TagType(typeof(BreakpointTag))]
public class BreakpointGlyphFactoryProvider : IGlyphFactoryProvider {
public IGlyphFactory GetGlyphFactory(IWpfTextView view, IWpfTextViewMargin margin) {
return new BreakpointGlyphFactory(margin);
}
}
}
11 changes: 11 additions & 0 deletions ExceptionBreaker/Breakpoints/Glyphs/BreakpointTag.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using Microsoft.VisualStudio.Text.Editor;

namespace ExceptionBreaker.Breakpoints.Glyphs {
public class BreakpointTag : IGlyphTag {
public BreakpointExtraData BreakpointExtraData { get; private set; }

public BreakpointTag(BreakpointExtraData breakpointExtraData) {
BreakpointExtraData = breakpointExtraData;
}
}
}
35 changes: 35 additions & 0 deletions ExceptionBreaker/Breakpoints/Glyphs/BreakpointTagger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Tagging;

namespace ExceptionBreaker.Breakpoints.Glyphs {
public class BreakpointTagger : ITagger<BreakpointTag> {
private readonly ITextDocument _document;
private readonly BreakpointFinder _finder;
private readonly BreakpointExtraDataStore _extraDataStore;

public BreakpointTagger(ITextDocument document, BreakpointFinder finder, BreakpointExtraDataStore extraDataStore) {
_document = document;
_finder = finder;
_extraDataStore = extraDataStore;
}

public IEnumerable<ITagSpan<BreakpointTag>> GetTags(NormalizedSnapshotSpanCollection spans) {
foreach (var span in spans) {
var breakpoint = _finder.GetBreakpointFromSpan(span, _document);
if (breakpoint == null)
continue;

var extraData = _extraDataStore.GetData(breakpoint);
if (extraData.ExceptionBreakChange == ExceptionBreakChange.NoChange)
continue;

yield return new TagSpan<BreakpointTag>(new SnapshotSpan(span.Start, span.Length), new BreakpointTag(extraData));
}
}

public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
}
}
32 changes: 32 additions & 0 deletions ExceptionBreaker/Breakpoints/Glyphs/BreakpointTaggerProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Windows.Media.TextFormatting;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Tagging;
using Microsoft.VisualStudio.Utilities;

namespace ExceptionBreaker.Breakpoints.Glyphs {
[Export(typeof(ITaggerProvider))]
[Order(After = "VsTextMarker")]
[ContentType("code")]
[TextViewRole(PredefinedTextViewRoles.Debuggable)]
[TagType(typeof(BreakpointTag))]
public class BreakpointTaggerProvider : ITaggerProvider {
private readonly BreakpointFinder _finder;
private readonly BreakpointExtraDataStore _extraDataStore;

[ImportingConstructor]
public BreakpointTaggerProvider(BreakpointFinder finder, BreakpointExtraDataStore extraDataStore) {
_finder = finder;
_extraDataStore = extraDataStore;
}

public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag {
var document = (ITextDocument)buffer.Properties[typeof(ITextDocument)];
return (ITagger<T>)new BreakpointTagger(document, _finder, _extraDataStore);
}
}
}
18 changes: 18 additions & 0 deletions ExceptionBreaker/Core/DTEImport.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using EnvDTE;
using Microsoft.VisualStudio.Shell;

namespace ExceptionBreaker.Core {
[Export]
public class DTEImport {
[ImportingConstructor]
public DTEImport(SVsServiceProvider serviceProvider) {
DTE = (DTE) serviceProvider.GetService(typeof (DTE));
}

public DTE DTE { get; private set; }
}
}
Loading

0 comments on commit de6b372

Please sign in to comment.