Permalink
Browse files

Refactored the external edit support so that it's agnostic of the

different implementations.  Planning on supporting several more types of
external edits in the upcoming release and needed this abstraction to be
cleaner
  • Loading branch information...
1 parent 621f2d7 commit f9042d995d07bc433068f65b9736f7e376e226c9 @jaredpar committed Mar 30, 2012
View
29 VsVimShared/ExternalEdit/ExternalEditMonitor.cs
@@ -30,7 +30,7 @@ internal enum CheckKind
private readonly ITextView _textView;
private readonly IProtectedOperations _protectedOperations;
private readonly Result<IVsTextLines> _vsTextLines;
- private readonly Result<ITagger<ITag>> _tagger;
+ private readonly ReadOnlyCollection<ITagger<ITag>> _taggerCollection;
private readonly ReadOnlyCollection<IExternalEditAdapter> _externalEditorAdapters;
private readonly List<ITrackingSpan> _ignoredExternalEditSpans = new List<ITrackingSpan>();
private CheckKind? _queuedCheckKind;
@@ -58,30 +58,30 @@ internal IEnumerable<ITrackingSpan> IgnoredExternalEditSpans
IVimBuffer buffer,
IProtectedOperations protectedOperations,
Result<IVsTextLines> vsTextLines,
- Result<ITagger<ITag>> tagger,
+ ReadOnlyCollection<ITagger<ITag>> taggerCollection,
ReadOnlyCollection<IExternalEditAdapter> externalEditorAdapters)
{
_vsTextLines = vsTextLines;
_protectedOperations = protectedOperations;
_externalEditorAdapters = externalEditorAdapters;
- _tagger = tagger;
+ _taggerCollection = taggerCollection;
_buffer = buffer;
_buffer.TextView.LayoutChanged += OnLayoutChanged;
_buffer.SwitchedMode += OnSwitchedMode;
_textView = _buffer.TextView;
- if (_tagger.IsSuccess)
+ foreach (var tagger in _taggerCollection)
{
- _tagger.Value.TagsChanged += OnTagsChanged;
+ tagger.TagsChanged += OnTagsChanged;
}
}
internal void Close()
{
_buffer.TextView.LayoutChanged -= OnLayoutChanged;
- if (_tagger.IsSuccess)
+ foreach (var tagger in _taggerCollection)
{
- _tagger.Value.TagsChanged -= OnTagsChanged;
+ tagger.TagsChanged -= OnTagsChanged;
}
}
@@ -290,20 +290,23 @@ private void GetExternalEditSpans(List<SnapshotSpan> list, CheckKind kind)
/// </summary>
private void GetExternalEditSpansFromTags(SnapshotSpan span, List<SnapshotSpan> list)
{
- if (!_tagger.IsSuccess)
+ if (_taggerCollection.Count == 0)
{
return;
}
var collection = new NormalizedSnapshotSpanCollection(span);
- var tags = _tagger.Value.GetTags(collection);
- foreach (var cur in tags)
+ foreach (var tagger in _taggerCollection)
{
- foreach (var adapter in _externalEditorAdapters)
+ var tags = tagger.GetTags(collection);
+ foreach (var cur in tags)
{
- if (adapter.IsExternalEditTag(cur.Tag))
+ foreach (var adapter in _externalEditorAdapters)
{
- list.Add(cur.Span);
+ if (adapter.IsExternalEditTag(cur.Tag))
+ {
+ list.Add(cur.Span);
+ }
}
}
}
View
112 VsVimShared/ExternalEdit/ExternalEditorManager.cs
@@ -1,115 +1,61 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
+using System.Collections.Generic;
using System.ComponentModel.Composition;
-using Microsoft.VisualStudio.Shell;
-using Microsoft.VisualStudio.Shell.Interop;
-using Microsoft.VisualStudio.Text;
+using System.Linq;
+using EditorUtils;
using Microsoft.VisualStudio.Text.Tagging;
using Vim;
using Vim.UI.Wpf;
namespace VsVim.ExternalEdit
{
- [Export(typeof(IResharperUtil))]
[Export(typeof(IVimBufferCreationListener))]
- internal sealed class ExternalEditorManager : IResharperUtil, IVimBufferCreationListener
+ internal sealed class ExternalEditorManager : IVimBufferCreationListener
{
- private static readonly Guid Resharper5Guid = new Guid("0C6E6407-13FC-4878-869A-C8B4016C57FE");
- private static readonly string ResharperTaggerName = "VsDocumentMarkupTaggerProvider";
-
private readonly IProtectedOperations _protectedOperations;
private readonly IVsAdapter _vsAdapter;
- private readonly IVsShell _vsShell;
private readonly List<IExternalEditAdapter> _adapterList = new List<IExternalEditAdapter>();
private readonly Dictionary<IVimBuffer, ExternalEditMonitor> _monitorMap = new Dictionary<IVimBuffer, ExternalEditMonitor>();
- private readonly bool _isResharperInstalled;
-
- /// <summary>
- /// Need to have an Import on a property vs. a constructor parameter to break a dependency
- /// loop.
- /// </summary>
- [ImportMany(typeof(ITaggerProvider))]
- internal List<Lazy<ITaggerProvider>> TaggerProviders { get; set; }
[ImportingConstructor]
- internal ExternalEditorManager(SVsServiceProvider serviceProvider, IVsAdapter vsAdapter, IProtectedOperations protectedOperations)
+ internal ExternalEditorManager(
+ IVsAdapter vsAdapter,
+ IProtectedOperations protectedOperations,
+ [ImportMany] IEnumerable<IExternalEditAdapter> adapters)
{
_vsAdapter = vsAdapter;
_protectedOperations = protectedOperations;
- _vsShell = serviceProvider.GetService<SVsShell, IVsShell>();
- _adapterList.Add(new SnippetExternalEditAdapter());
- _isResharperInstalled = CheckResharperInstalled();
- if (_isResharperInstalled)
- {
- _adapterList.Add(new ResharperExternalEditAdapter());
- }
- }
-
- public void VimBufferCreated(IVimBuffer buffer)
- {
- Result<ITagger<ITag>> tagger = Result.Error;
- if (_isResharperInstalled)
- {
- tagger = GetResharperTagger(buffer.TextBuffer);
- }
-
- _monitorMap[buffer] = new ExternalEditMonitor(
- buffer,
- _protectedOperations,
- _vsAdapter.GetTextLines(buffer.TextBuffer),
- tagger,
- new ReadOnlyCollection<IExternalEditAdapter>(_adapterList));
- buffer.Closed += delegate { _monitorMap.Remove(buffer); };
+ _adapterList = adapters.ToList();
}
- /// <summary>
- /// Get the R# tagger for the ITextBuffer if it exists
- /// </summary>
- private Result<ITagger<ITag>> GetResharperTagger(ITextBuffer textBuffer)
+ public void VimBufferCreated(IVimBuffer vimBuffer)
{
- Contract.Assert(_isResharperInstalled);
-
- // This is available as a post construction MEF import so it's very possible
- // that this is null if it's not initialized
- if (TaggerProviders == null)
- {
- return Result.Error;
- }
-
- // R# exposes it's ITaggerProvider instances for the "text" content type. As much as
- // I would like to query to make sure they always support the content type we don't
- // have access to the metadata and have to hard code "text" here
- if (!textBuffer.ContentType.IsOfType("text"))
- {
- return Result.Error;
- }
+ var taggerList = new List<ITagger<ITag>>();
+ var bufferAdapterList = new List<IExternalEditAdapter>();
+ var textView = vimBuffer.TextView;
- foreach (var pair in TaggerProviders)
+ foreach (var adapter in _adapterList)
{
- var provider = pair.Value;
- var name = provider.GetType().Name;
- if (name == ResharperTaggerName)
+ ITagger<ITag> tagger;
+ if (adapter.IsInterested(textView, out tagger))
{
- var tagger = provider.SafeCreateTagger<ITag>(textBuffer);
- if (tagger.IsSuccess)
+ bufferAdapterList.Add(adapter);
+ if (tagger != null)
{
- return tagger;
+ taggerList.Add(tagger);
}
}
}
- return Result.Error;
- }
-
- private bool CheckResharperInstalled()
- {
- return _vsShell.IsPackageInstalled(Resharper5Guid);
- }
-
- bool IResharperUtil.IsInstalled
- {
- get { return _isResharperInstalled; }
+ if (bufferAdapterList.Count > 0)
+ {
+ _monitorMap[vimBuffer] = new ExternalEditMonitor(
+ vimBuffer,
+ _protectedOperations,
+ _vsAdapter.GetTextLines(vimBuffer.TextBuffer),
+ taggerList.ToReadOnlyCollectionShallow(),
+ bufferAdapterList.ToReadOnlyCollectionShallow());
+ vimBuffer.Closed += delegate { _monitorMap.Remove(vimBuffer); };
+ }
}
}
}
View
7 VsVimShared/ExternalEdit/IExternalEditAdapter.cs
@@ -1,11 +1,18 @@
using Microsoft.VisualStudio.Text.Tagging;
using Microsoft.VisualStudio.TextManager.Interop;
+using Microsoft.VisualStudio.Text.Editor;
namespace VsVim.ExternalEdit
{
internal interface IExternalEditAdapter
{
/// <summary>
+ /// Is the external editor interested in this ITextView. Can return a particular
+ /// ITagger implementation that it's interested in in the ITextView
+ /// </summary>
+ bool IsInterested(ITextView textView, out ITagger<ITag> tagger);
+
+ /// <summary>
/// Does this IVsTextLineMarker represent an external edit
/// </summary>
bool IsExternalEditMarker(IVsTextLineMarker marker);
View
116 VsVimShared/ExternalEdit/ResharperExternalEditAdapter.cs
@@ -1,26 +1,63 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel.Composition;
using System.Reflection;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.Shell.Interop;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Tagging;
using Microsoft.VisualStudio.TextManager.Interop;
+using Vim;
namespace VsVim.ExternalEdit
{
- internal sealed class ResharperExternalEditAdapter : IExternalEditAdapter
+ [Export(typeof(IResharperUtil))]
+ [Export(typeof(IExternalEditAdapter))]
+ internal sealed class ResharperExternalEditAdapter : IExternalEditAdapter, IResharperUtil
{
internal const string ExternalEditAttribute1 = "ReSharper Template Editor Template Keyword";
internal const string ExternalEditAttribute2 = "ReSharper LiveTemplates Current HotSpot";
internal const string ExternalEditAttribute3 = "ReSharper LiveTemplates Current HotSpot mirror";
+ private static readonly Guid Resharper5Guid = new Guid("0C6E6407-13FC-4878-869A-C8B4016C57FE");
+ private static readonly string ResharperTaggerName = "VsDocumentMarkupTaggerProvider";
private static FieldInfo AttributeIdFieldInfo;
+
private readonly Dictionary<Type,bool> _tagMap = new Dictionary<Type,bool>();
+ private readonly bool _isResharperInstalled;
+
+ /// <summary>
+ /// Need to have an Import on a property vs. a constructor parameter to break a dependency
+ /// loop.
+ /// </summary>
+ [ImportMany(typeof(ITaggerProvider))]
+ internal List<Lazy<ITaggerProvider>> TaggerProviders { get; set; }
- public bool IsExternalEditMarker(IVsTextLineMarker marker)
+ [ImportingConstructor]
+ internal ResharperExternalEditAdapter(SVsServiceProvider serviceProvider)
{
- return false;
+ var vsShell = serviceProvider.GetService<SVsShell, IVsShell>();
+ _isResharperInstalled = vsShell.IsPackageInstalled(Resharper5Guid);
+ }
+
+ internal ResharperExternalEditAdapter(bool isResharperInstalled)
+ {
+ _isResharperInstalled = isResharperInstalled;
}
- public bool IsExternalEditTag(ITag tag)
+ internal bool IsInterested(ITextView textView, out ITagger<ITag> tagger)
+ {
+ if (!_isResharperInstalled)
+ {
+ tagger = null;
+ return false;
+ }
+
+ return TryGetResharperTagger(textView.TextBuffer, out tagger);
+ }
+
+ internal bool IsExternalEditTag(ITag tag)
{
return IsVsAdornmentTagType(tag.GetType()) && IsEditTag(tag);
}
@@ -38,6 +75,49 @@ private bool IsVsAdornmentTagType(Type type)
return isType;
}
+ /// <summary>
+ /// Get the R# tagger for the ITextBuffer if it exists
+ /// </summary>
+ private bool TryGetResharperTagger(ITextBuffer textBuffer, out ITagger<ITag> tagger)
+ {
+ Contract.Assert(_isResharperInstalled);
+
+ // This is available as a post construction MEF import so it's very possible
+ // that this is null if it's not initialized
+ if (TaggerProviders == null)
+ {
+ tagger = null;
+ return false;
+ }
+
+ // R# exposes it's ITaggerProvider instances for the "text" content type. As much as
+ // I would like to query to make sure they always support the content type we don't
+ // have access to the metadata and have to hard code "text" here
+ if (!textBuffer.ContentType.IsOfType("text"))
+ {
+ tagger = null;
+ return false;
+ }
+
+ foreach (var pair in TaggerProviders)
+ {
+ var provider = pair.Value;
+ var name = provider.GetType().Name;
+ if (name == ResharperTaggerName)
+ {
+ var taggerResult = provider.SafeCreateTagger<ITag>(textBuffer);
+ if (taggerResult.IsSuccess)
+ {
+ tagger = taggerResult.Value;
+ return true;
+ }
+ }
+ }
+
+ tagger = null;
+ return false;
+ }
+
private static bool IsEditTag(ITag tag)
{
// Cache the FieldInfo since we will be using it a lot
@@ -67,5 +147,33 @@ private static bool IsEditTag(ITag tag)
return false;
}
}
+
+ #region IResharperUtil
+
+ bool IResharperUtil.IsInstalled
+ {
+ get { return _isResharperInstalled; }
+ }
+
+ #endregion
+
+ #region IExternalEditAdapter
+
+ bool IExternalEditAdapter.IsInterested(ITextView textView, out ITagger<ITag> tagger)
+ {
+ return IsInterested(textView, out tagger);
+ }
+
+ bool IExternalEditAdapter.IsExternalEditMarker(IVsTextLineMarker marker)
+ {
+ return false;
+ }
+
+ bool IExternalEditAdapter.IsExternalEditTag(ITag tag)
+ {
+ return IsExternalEditTag(tag);
+ }
+
+ #endregion
}
}
View
22 VsVimShared/ExternalEdit/SnippetExternalEditAdapter.cs
@@ -1,8 +1,11 @@
-using Microsoft.VisualStudio.Text.Tagging;
+using System.ComponentModel.Composition;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Tagging;
using Microsoft.VisualStudio.TextManager.Interop;
namespace VsVim.ExternalEdit
{
+ [Export(typeof(IExternalEditAdapter))]
internal sealed class SnippetExternalEditAdapter : IExternalEditAdapter
{
public bool IsExternalEditMarker(IVsTextLineMarker marker)
@@ -32,16 +35,31 @@ public bool IsExternalEditMarker(IVsTextLineMarker marker)
return true;
case 25:
// Kind currently unknown.
+ // Used at least for brace matching
return false;
default:
return false;
}
}
+ #region IExternalEditAdapter
- public bool IsExternalEditTag(ITag tag)
+ bool IExternalEditAdapter.IsInterested(ITextView textView, out ITagger<ITag> tagger)
+ {
+ tagger = null;
+ return true;
+ }
+
+ bool IExternalEditAdapter.IsExternalEditMarker(IVsTextLineMarker marker)
+ {
+ return IsExternalEditMarker(marker);
+ }
+
+ bool IExternalEditAdapter.IsExternalEditTag(ITag tag)
{
return false;
}
+
+ #endregion
}
}
View
13 VsVimSharedTest/ExternalEditMonitorTest.cs
@@ -1,8 +1,8 @@
using System.Collections.Generic;
-using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Windows.Threading;
+using EditorUtils;
using EditorUtils.UnitTest;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
@@ -50,6 +50,7 @@ public void Create(bool hasTextLines, bool hasTagger, params string[] lines)
_adapter = _factory.Create<IExternalEditAdapter>(MockBehavior.Strict);
_adapter.Setup(x => x.IsExternalEditTag(It.IsAny<ITag>())).Returns(false);
_adapter.Setup(x => x.IsExternalEditMarker(It.IsAny<IVsTextLineMarker>())).Returns(false);
+ var adapterList = new List<IExternalEditAdapter> { _adapter.Object };
Result<IVsTextLines> textLines = Result.Error;
if (hasTextLines)
@@ -59,22 +60,20 @@ public void Create(bool hasTextLines, bool hasTagger, params string[] lines)
textLines = Result.CreateSuccess(_vsTextLines.Object);
}
- Result<ITagger<ITag>> tagger = Result.Error;
+ var taggerList = new List<ITagger<ITag>>();
if (hasTagger)
{
_tagger = _factory.Create<ITagger<ITag>>(MockBehavior.Loose);
_tagger.Setup(x => x.GetTags(It.IsAny<NormalizedSnapshotSpanCollection>())).Returns(new List<ITagSpan<ITag>>());
- tagger = Result.CreateSuccess(_tagger.Object);
+ taggerList.Add(_tagger.Object);
}
- var list = new List<IExternalEditAdapter> { _adapter.Object };
- var adapters = new ReadOnlyCollection<IExternalEditAdapter>(list);
_monitor = new ExternalEditMonitor(
_buffer,
ProtectedOperations,
textLines,
- tagger,
- adapters);
+ taggerList.ToReadOnlyCollectionShallow(),
+ adapterList.ToReadOnlyCollectionShallow());
}
[TearDown]
View
7 VsVimSharedTest/ResharperExternalEditAdapterTest.cs
@@ -27,10 +27,13 @@ public void Create(params string[] lines)
{
_textBuffer = CreateTextBuffer(lines);
_factory = new MockRepository(MockBehavior.Strict);
- _adapterRaw = new ResharperExternalEditAdapter();
+ _adapterRaw = new ResharperExternalEditAdapter(true);
_adapter = _adapterRaw;
}
+ /// <summary>
+ /// Ensure that the R# adapter doesn't pick up on IVsTextMarker instances
+ /// </summary>
[Test]
public void IsExternalEditMarker_None()
{
@@ -39,7 +42,7 @@ public void IsExternalEditMarker_None()
{
var span = _textBuffer.GetLineRange(0).Extent.ToTextSpan();
var marker = MockObjectFactory.CreateVsTextLineMarker(span, i, _factory);
- Assert.IsFalse(_adapterRaw.IsExternalEditMarker(marker.Object));
+ Assert.IsFalse(_adapter.IsExternalEditMarker(marker.Object));
}
}

0 comments on commit f9042d9

Please sign in to comment.