SixPairs

July 20, 2014

SmartTag: UpperCase

Filed under: VS Editor — Ceyhun Ciper @ 11:49

From MSDN:

namespace Ciper
{
  using Microsoft.VisualStudio.Language.Intellisense;
  using Microsoft.VisualStudio.Text;
  using Microsoft.VisualStudio.Text.Editor;
  using Microsoft.VisualStudio.Text.Operations;
  using Microsoft.VisualStudio.Text.Tagging;
  using Microsoft.VisualStudio.Utilities;
  using System;
  using System.Collections.Generic;
  using System.Collections.ObjectModel;
  using System.ComponentModel.Composition;
  using System.Windows.Media;
 
  class TestSmartTag : SmartTag
  {
    public TestSmartTag(ReadOnlyCollection<SmartTagActionSet> actionSets) :
      base(SmartTagType.Factoid, actionSets) { }
  }
 
  class TestSmartTagger : ITagger<TestSmartTag>, IDisposable
  {
    private ITextBuffer m_buffer;
    private ITextView m_view;
    private TestSmartTaggerProvider m_provider;
    private bool m_disposed;
 
    public TestSmartTagger(ITextBuffer buffer, ITextView view, TestSmartTaggerProvider provider)
    {
      m_buffer = buffer;
      m_view = view;
      m_provider = provider;
      m_view.LayoutChanged += OnLayoutChanged;
    }
 
    public IEnumerable<ITagSpan<TestSmartTag>> GetTags(NormalizedSnapshotSpanCollection spans)
    {
      ITextSnapshot snapshot = m_buffer.CurrentSnapshot;
      if (snapshot.Length == 0)
        yield break//don't do anything if the buffer is empty 
 
      //set up the navigator
      ITextStructureNavigator navigator = m_provider.NavigatorService.GetTextStructureNavigator(m_buffer);
 
      foreach (var span in spans)
      {
        ITextCaret caret = m_view.Caret;
        SnapshotPoint point;
 
        if (caret.Position.BufferPosition > 0)
          point = caret.Position.BufferPosition - 1;
        else
          yield break;
 
        TextExtent extent = navigator.GetExtentOfWord(point);
        //don't display the tag if the extent has whitespace 
        if (extent.IsSignificant)
          yield return new TagSpan<TestSmartTag>(extent.Span, new TestSmartTag(GetSmartTagActions(extent.Span)));
        else yield break;
      }
    }
 
    private ReadOnlyCollection<SmartTagActionSet> GetSmartTagActions(SnapshotSpan span)
    {
      List<SmartTagActionSet> actionSetList = new List<SmartTagActionSet>();
      List<ISmartTagAction> actionList = new List<ISmartTagAction>();
 
      ITrackingSpan trackingSpan = span.Snapshot.CreateTrackingSpan(span, SpanTrackingMode.EdgeInclusive);
      actionList.Add(new UpperCaseSmartTagAction(trackingSpan));
      SmartTagActionSet actionSet = new SmartTagActionSet(actionList.AsReadOnly());
      actionSetList.Add(actionSet);
      return actionSetList.AsReadOnly();
    }
 
    public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
 
    private void OnLayoutChanged(object sender, TextViewLayoutChangedEventArgs e)
    {
      ITextSnapshot snapshot = e.NewSnapshot;
      //don't do anything if this is just a change in case 
      if (!snapshot.GetText().ToLower().Equals(e.OldSnapshot.GetText().ToLower()))
      {
        SnapshotSpan span = new SnapshotSpan(snapshot, new Span(0, snapshot.Length));
        EventHandler<SnapshotSpanEventArgs> handler = this.TagsChanged;
        if (handler != null)
        {
          handler(thisnew SnapshotSpanEventArgs(span));
        }
      }
    }
 
    public void Dispose()
    {
      Dispose(true);
      GC.SuppressFinalize(this);
    }
 
    private void Dispose(bool disposing)
    {
      if (!this.m_disposed)
      {
        if (disposing)
        {
          m_view.LayoutChanged -= OnLayoutChanged;
          m_view = null;
        }
 
        m_disposed = true;
      }
    }
 
  }
 
  [Export(typeof(IViewTaggerProvider))]
  [ContentType("text")]
  [Order(Before = "default")]
  [TagType(typeof(SmartTag))]
  class TestSmartTaggerProvider : IViewTaggerProvider
  {
    [Import(typeof(ITextStructureNavigatorSelectorService))]
    internal ITextStructureNavigatorSelectorService NavigatorService { getset; }
 
    public ITagger<T> CreateTagger<T>(ITextView textView, ITextBuffer buffer) where T : ITag
    {
      if (buffer == null || textView == null)
      {
        return null;
      }
 
      //make sure we are tagging only the top buffer 
      if (buffer == textView.TextBuffer)
      {
        return new TestSmartTagger(buffer, textView, thisas ITagger<T>;
      }
      else return null;
    }
  }
 
  class UpperCaseSmartTagAction : ISmartTagAction
  {
    private ITrackingSpan m_span;
    private string m_upper;
    private string m_display;
    private ITextSnapshot m_snapshot;
 
    public UpperCaseSmartTagAction(ITrackingSpan span)
    {
      m_span = span;
      m_snapshot = span.TextBuffer.CurrentSnapshot;
      m_upper = span.GetText(m_snapshot).ToUpper();
      m_display = "Convert to upper case";
    }
 
    public string DisplayText
    {
      get { return m_display; }
    }
    public ImageSource Icon
    {
      get { return null; }
    }
    public bool IsEnabled
    {
      get { return true; }
    }
 
    public ISmartTagSource Source
    {
      get;
      private set;
    }
 
    public ReadOnlyCollection<SmartTagActionSet> ActionSets
    {
      get { return null; }
    }
 
    public void Invoke()
    {
      m_span.TextBuffer.Replace(m_span.GetSpan(m_snapshot), m_upper);
    }
  }
 
}

July 15, 2014

Simpler VS Editor Classifier

Filed under: VS Editor — Ceyhun Ciper @ 20:18
namespace Ciper
{
  using EnvDTE;
  using EnvDTE80;
  using Microsoft.VisualStudio.Language.StandardClassification;
  using Microsoft.VisualStudio.Shell;
  using Microsoft.VisualStudio.Text;
  using Microsoft.VisualStudio.Text.Classification;
  using System;
  using System.ComponentModel.Composition;
 
  [Export(typeof(IClassifierProvider))]
  partial class SimpleClassifier : IClassifierProviderIClassifier
  {
    [Import]
    IClassificationTypeRegistryService classificationTypeRegistry = null;
 
    [Import]
    IStandardClassificationService standardClassifications = null;
 
    [Import]
    SVsServiceProvider serviceProvider = null;
 
    DTE2 dte { get { return (DTE2)serviceProvider.GetService(typeof(DTE)); } }
    OutputWindowPane outputWindowPane { get { return dte.ToolWindows.OutputWindow.ActivePane; } }
 
    public IClassifier GetClassifier(ITextBuffer textBuffer)
    {
      return this;
    }
 
    public event EventHandler<ClassificationChangedEventArgs> ClassificationChanged;
  }
}
 
namespace Ciper
{
  using Microsoft.VisualStudio.Text;
  using Microsoft.VisualStudio.Text.Classification;
  using Microsoft.VisualStudio.Utilities;
  using System.Collections.Generic;
  using System.ComponentModel.Composition;
  using System.Text.RegularExpressions;
 
  [ContentType("csharp")]
  partial class SimpleClassifier
  {
    [ExportName("kelime")]
    ClassificationTypeDefinition kelime;
 
    public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
    {
      var l = new List<ClassificationSpan>();
      var text = span.GetText();
      foreach (Match m in Regex.Matches(text, @"\w+"))
      {
        var wordSpan = new SnapshotSpan(span.Start + m.Index, m.Length);
        l.Add(new ClassificationSpan(wordSpan, classificationTypeRegistry.GetClassificationType("kelime")));
      }
      return l;
    }
  }
}

Exercises

  1. “// re: ” ile baslayan her comment’in sag tarafini “re sample” olarak classify et.
    namespace Ciper
    {
      using Microsoft.VisualStudio.Text;
      using Microsoft.VisualStudio.Text.Classification;
      using Microsoft.VisualStudio.Utilities;
      using System.Collections.Generic;
      using System.ComponentModel.Composition;
      using System.Text.RegularExpressions;
     
      [ContentType("csharp")]
      partial class SimpleClassifier
      {
        [ExportName("re sample")]
        ClassificationTypeDefinition reSample;
     
        public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
        {
          var l = new List<ClassificationSpan>();
          var text = span.GetText();
          foreach (Match m in Regex.Matches(text, @"//\s*re\:?\s*(.+)")) // Dikkat, burada re'yi $ ile bitirme!
          {
            var sampleSpan = new SnapshotSpan(span.Start + m.Groups[1].Index, m.Groups[1].Length);
            l.Add(new ClassificationSpan(sampleSpan, classificationTypeRegistry.GetClassificationType("re sample")));
          }
          return l;
        }
      }
    }
    
  2. IP ve email’leri classify et.
    namespace Ciper
    {
      using Microsoft.VisualStudio.Text;
      using Microsoft.VisualStudio.Text.Classification;
      using Microsoft.VisualStudio.Utilities;
      using System.Collections.Generic;
      using System.ComponentModel.Composition;
      using System.Text.RegularExpressions;
     
      [ContentType("csharp")]
      partial class SimpleClassifier
      {
        [ExportName("ip")]
        ClassificationTypeDefinition ip;
        [ExportName("email")]
        ClassificationTypeDefinition email;
     
        public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
        {
          var l = new List<ClassificationSpan>();
          var text = span.GetText();
          foreach (Match m in Regex.Matches(text, @"\d+\.\d+\.\d+\.\d+"))
          {
            var ipSpan = new SnapshotSpan(span.Start + m.Index, m.Length);
            l.Add(new ClassificationSpan(ipSpan, classificationTypeRegistry.GetClassificationType("ip")));
          }
          foreach (Match m in Regex.Matches(text, @"\w+\@\w+\.\w+"))
          {
            var emailSpan = new SnapshotSpan(span.Start + m.Index, m.Length);
            l.Add(new ClassificationSpan(emailSpan, classificationTypeRegistry.GetClassificationType("email")));
          }
          return l;
        }
      }
    }
    

July 10, 2014

Simplest Margin Glyph

Filed under: VS Editor — Ceyhun Ciper @ 05:59
namespace Microsoft.VisualStudio.Text.Editor
{
  using Microsoft.VisualStudio.Text.Formatting;
  using Microsoft.VisualStudio.Text.Tagging;
  using Microsoft.VisualStudio.Utilities;
  using System;
  using System.Collections.Generic;
  using System.ComponentModel.Composition;
  using System.Linq;
  using System.Windows;
  using System.Windows.Media;
  using System.Windows.Shapes;
 
  [Export(typeof(IGlyphFactoryProvider))]
  [Name("AGlyph")]
  [Order(After = "VsTextMarker")]
  [ContentType("code")]
  [TagType(typeof(ATag))]
  internal sealed class AGlyphFactory : IGlyphFactoryProviderIGlyphFactory
  {
    public IGlyphFactory GetGlyphFactory(IWpfTextView view, IWpfTextViewMargin margin)
    {
      return this;
    }
 
    public UIElement GenerateGlyph(IWpfTextViewLine line, IGlyphTag tag)
    {
      if (tag == null || !(tag is ATag)) return null;
      return new Ellipse { Fill = Brushes.Blue, Height = 16, Width = 16 };
    }
  }
 
  internal class ATag : IGlyphTag { }
 
  [Export(typeof(ITaggerProvider))]
  [ContentType("code")]
  [TagType(typeof(ATag))]
  class ATagger : ITaggerProviderITagger<ATag>
  {
    public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag
    {
      return this as ITagger<T>;
    }
 
    IEnumerable<ITagSpan<ATag>> ITagger<ATag>.GetTags(NormalizedSnapshotSpanCollection spans)
    {
      return
        from span in spans
        where span.GetText().Contains("a")
        select new TagSpan<ATag>(span, new ATag());
    }
 
    public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
  }
}

ToDo Margin Glyph

Filed under: VS Editor — Ceyhun Ciper @ 02:16

From MSDN…

namespace Microsoft.VisualStudio.Text.Editor
{
  using Microsoft.VisualStudio.Text.Classification;
  using Microsoft.VisualStudio.Text.Formatting;
  using Microsoft.VisualStudio.Text.Tagging;
  using Microsoft.VisualStudio.Utilities;
  using System;
  using System.Collections.Generic;
  using System.ComponentModel.Composition;
  using System.Windows;
  using System.Windows.Media;
  using System.Windows.Shapes;
 
  [Export(typeof(IGlyphFactoryProvider))]
  [Name("TodoGlyph")]
  [Order(After = "VsTextMarker")]
  [ContentType("code")]
  [TagType(typeof(TodoTag))]
  internal sealed class TodoGlyphFactoryProvider : IGlyphFactoryProvider
  {
    public IGlyphFactory GetGlyphFactory(IWpfTextView view, IWpfTextViewMargin margin)
    {
      return new TodoGlyphFactory();
    }
  }
 
  internal class TodoGlyphFactory : IGlyphFactory
  {
    public UIElement GenerateGlyph(IWpfTextViewLine line, IGlyphTag tag)
    {
      if (tag == null || !(tag is TodoTag)) return null;
      return new Ellipse { Fill = Brushes.Blue, Height = 16, Width = 16 };
    }
  }
 
  internal class TodoTag : IGlyphTag { }
 
  internal class TodoTagger : ITagger<TodoTag>
  {
    private IClassifier m_classifier;
    private const string m_searchText = "todo";
    internal TodoTagger(IClassifier classifier)
    {
      m_classifier = classifier;
    }
 
    IEnumerable<ITagSpan<TodoTag>> ITagger<TodoTag>.GetTags(NormalizedSnapshotSpanCollection spans)
    {
      foreach (SnapshotSpan span in spans)
      {
        //look at each classification span 
        foreach (ClassificationSpan classification in m_classifier.GetClassificationSpans(span))
        {
          //if the classification is a comment 
          if (classification.ClassificationType.Classification.ToLower().Contains("comment"))
          {
            //if the word "todo" is in the comment, create a new TodoTag TagSpan 
            int index = classification.Span.GetText().ToLower().IndexOf(m_searchText);
            if (index != -1)
            {
              yield return new TagSpan<TodoTag>(new SnapshotSpan(classification.Span.Start + index, m_searchText.Length), new TodoTag());
            }
          }
        }
      }
    }
 
    public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
  }
 
  [Export(typeof(ITaggerProvider))]
  [ContentType("code")]
  [TagType(typeof(TodoTag))]
  class TodoTaggerProvider : ITaggerProvider
  {
    [Import]
    internal IClassifierAggregatorService AggregatorService { getset; }
 
    public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag
    {
      return new TodoTagger(AggregatorService.GetClassifier(buffer)) as ITagger<T>;
    }
  }
}

July 9, 2014

Simplest WpfTextViewCreationListener

Filed under: VS Editor — Ceyhun Ciper @ 17:32
namespace Ciper
{
  using Microsoft.VisualStudio.Text;
  using Microsoft.VisualStudio.Text.Classification;
  using Microsoft.VisualStudio.Text.Editor;
  using System.Collections.Generic;
  using System.ComponentModel.Composition;
  using System.Linq;
 
  [Export(typeof(IWpfTextViewCreationListener)), TextViewRole(PredefinedTextViewRoles.Interactive)]
  public partial class WpfTextViewCreationListener : IWpfTextViewCreationListener
  {
    IWpfTextView view;
    IClassifier classifier;
    ITextSnapshot snapshot { get { return view.TextSnapshot; } }
 
    [Import]
    IViewClassifierAggregatorService classifierAggregator { getset; }
 
    SnapshotSpan GetSnapshotSpan()
    {
      return new SnapshotSpan(snapshot, Span.FromBounds(0, snapshot.Length));
    }
 
    IEnumerable<ClassificationSpan> GetClassificationSpans()
    {
      return classifier.GetClassificationSpans(GetSnapshotSpan());
    }
 
    IEnumerable<ClassificationSpan> GetClassificationSpans(string type)
    {
      return GetClassificationSpans().Where(s => s.ClassificationType.IsOfType(type));
    }
 
    public void TextViewCreated(IWpfTextView textView)
    {
      view = textView;
      classifier = classifierAggregator.GetClassifier(view);
    }
  }
}
 
namespace Ciper
{
  using Microsoft.VisualStudio.Utilities;
 
  [ContentType("csharp")]
  partial class WpfTextViewCreationListener
  {
  }
}

July 6, 2014

Simplest Editor TextAdornment

Filed under: VS Editor — Ceyhun Ciper @ 10:31

Add a border around each line.

namespace Microsoft.VisualStudio.Text.Editor
{
  using Microsoft.VisualStudio.Text.Formatting;
  using Microsoft.VisualStudio.Utilities;
  using System.ComponentModel.Composition;
  using System.Windows;
  using System.Windows.Controls;
  using System.Windows.Media;

  [Export(typeof(IWpfTextViewCreationListener))]
  [ContentType("text")]
  [TextViewRole(PredefinedTextViewRoles.Document)]
  internal sealed class TextAdornment1Factory : IWpfTextViewCreationListener
  {
    [Export(typeof(AdornmentLayerDefinition))]
    [Name("TextAdornment1")]
    [Order(After = PredefinedAdornmentLayers.Selection, Before = PredefinedAdornmentLayers.Text)]
    public AdornmentLayerDefinition editorAdornmentLayer = null;

    public void TextViewCreated(IWpfTextView textView)
    {
      new TextAdornment1(textView);
    }
  }

  public class TextAdornment1
  {
    IAdornmentLayer layer;
    IWpfTextView view;

    public TextAdornment1(IWpfTextView view)
    {
      this.view = view;
      layer = view.GetAdornmentLayer("TextAdornment1");

      this.view.LayoutChanged += OnLayoutChanged;
    }

    private void OnLayoutChanged(object sender, TextViewLayoutChangedEventArgs e)
    {
      foreach (ITextViewLine line in e.NewOrReformattedLines)
      {
        this.CreateVisuals(line);
      }
    }

    private void CreateVisuals(ITextViewLine line)
    {
      IWpfTextViewLineCollection textViewLines = view.TextViewLines;

      Geometry g = textViewLines.GetMarkerGeometry(line.Extent);
      if (g != null)
      {
        var border = new Border
        {
          Width = g.Bounds.Width,
          Height = g.Bounds.Height,
          BorderBrush = Brushes.Violet,
          BorderThickness = new Thickness(0.8),
        };

        Canvas.SetLeft(border, g.Bounds.Left);
        Canvas.SetTop(border, g.Bounds.Top);

        layer.AddAdornment(AdornmentPositioningBehavior.TextRelative, line.Extent, null, border, null);
      }
    }
  }
}

Exercises

  1. Add a border around each line, bypassing initial spaces.

    namespace Microsoft.VisualStudio.Text.Editor
    {
      using Microsoft.VisualStudio.Text.Formatting;
      using Microsoft.VisualStudio.Utilities;
      using System.ComponentModel.Composition;
      using System.Text.RegularExpressions;
      using System.Windows;
      using System.Windows.Controls;
      using System.Windows.Media;
    
      [Export(typeof(IWpfTextViewCreationListener))]
      [ContentType("text")]
      [TextViewRole(PredefinedTextViewRoles.Document)]
      internal sealed class TextAdornment1Factory : IWpfTextViewCreationListener
      {
        [Export(typeof(AdornmentLayerDefinition))]
        [Name("TextAdornment1")]
        [Order(After = PredefinedAdornmentLayers.Selection, Before = PredefinedAdornmentLayers.Text)]
        public AdornmentLayerDefinition editorAdornmentLayer = null;
    
        public void TextViewCreated(IWpfTextView textView)
        {
          new TextAdornment1(textView);
        }
      }
    
      public class TextAdornment1
      {
        IAdornmentLayer layer;
        IWpfTextView view;
    
        public TextAdornment1(IWpfTextView view)
        {
          this.view = view;
          layer = view.GetAdornmentLayer("TextAdornment1");
    
          this.view.LayoutChanged += OnLayoutChanged;
        }
    
        private void OnLayoutChanged(object sender, TextViewLayoutChangedEventArgs e)
        {
          foreach (ITextViewLine line in e.NewOrReformattedLines)
          {
            this.CreateVisuals(line);
          }
        }
    
        private void CreateVisuals(ITextViewLine line)
        {
          IWpfTextViewLineCollection textViewLines = view.TextViewLines;
          int start = line.Start;
    
          var m = new Regex(@"\S").Match(line.Extent.GetText());
          if (m.Success) start += m.Index;
          
          var span = new SnapshotSpan(line.Snapshot, Span.FromBounds(start, line.End));
    
          Geometry g = textViewLines.GetMarkerGeometry(span);
          if (g != null)
          {
            var border = new Border
            {
              Width = g.Bounds.Width,
              Height = g.Bounds.Height,
              BorderBrush = Brushes.Violet,
              BorderThickness = new Thickness(0.8),
            };
    
            Canvas.SetLeft(border, g.Bounds.Left);
            Canvas.SetTop(border, g.Bounds.Top);
    
            layer.AddAdornment(AdornmentPositioningBehavior.TextRelative, span, null, border, null);
          }
        }
      }
    }
        
  2. Highlight spaces.

    namespace Microsoft.VisualStudio.Text.Editor
    {
      using Microsoft.VisualStudio.Text.Formatting;
      using Microsoft.VisualStudio.Utilities;
      using System.ComponentModel.Composition;
      using System.Text.RegularExpressions;
      using System.Windows;
      using System.Windows.Controls;
      using System.Windows.Media;
    
      [Export(typeof(IWpfTextViewCreationListener))]
      [ContentType("text")]
      [TextViewRole(PredefinedTextViewRoles.Document)]
      internal sealed class TextAdornment1Factory : IWpfTextViewCreationListener
      {
        [Export(typeof(AdornmentLayerDefinition))]
        [Name("TextAdornment1")]
        [Order(After = PredefinedAdornmentLayers.Selection, Before = PredefinedAdornmentLayers.Text)]
        public AdornmentLayerDefinition editorAdornmentLayer = null;
    
        public void TextViewCreated(IWpfTextView textView)
        {
          new TextAdornment1(textView);
        }
      }
    
      public class TextAdornment1
      {
        IAdornmentLayer layer;
        IWpfTextView view;
    
        public TextAdornment1(IWpfTextView view)
        {
          this.view = view;
          layer = view.GetAdornmentLayer("TextAdornment1");
    
          this.view.LayoutChanged += OnLayoutChanged;
        }
    
        private void OnLayoutChanged(object sender, TextViewLayoutChangedEventArgs e)
        {
          foreach (ITextViewLine line in e.NewOrReformattedLines)
          {
            this.CreateVisuals(line);
          }
        }
    
        private void CreateVisuals(ITextViewLine line)
        {
          IWpfTextViewLineCollection textViewLines = view.TextViewLines;
          int start = line.Start;
          int end = line.End;
    
          for (int i = start; i < end; i++)
          {
            if (line.Snapshot[i] == ' ')
            {
              var span = new SnapshotSpan(line.Snapshot, Span.FromBounds(i, i + 1));
    
              Geometry g = textViewLines.GetMarkerGeometry(span);
              if (g != null)
              {
                var border = new Border
                {
                  Width = g.Bounds.Width,
                  Height = g.Bounds.Height / 8,
                  BorderBrush = Brushes.Black,
                  BorderThickness = new Thickness(0.8, 0, 0.8, 0.8),
                };
    
                Canvas.SetLeft(border, g.Bounds.Left);
                Canvas.SetTop(border, g.Bounds.Bottom - g.Bounds.Height / 8);
    
                layer.AddAdornment(AdornmentPositioningBehavior.TextRelative, span, null, border, null);
              }
            }
          }
        }
      }
    }
        
  3. TBD

        
  4. TBD

        

Create a free website or blog at WordPress.com.