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

        

June 30, 2014

Simplest LineTransformSource

Filed under: VS Editor — Ceyhun Ciper @ 17:44
namespace Microsoft.VisualStudio.Text.Formatting
{
  using Microsoft.VisualStudio.Text.Editor;
  using Microsoft.VisualStudio.Utilities;
  using System.ComponentModel.Composition;

  [Export(typeof(ILineTransformSourceProvider))]
  [ContentType("text")]
  [TextViewRole(PredefinedTextViewRoles.Interactive)]
  public partial class LineTransformSource : ILineTransformSourceProvider, ILineTransformSource
  {
    IWpfTextView view;
    public ILineTransformSource Create(IWpfTextView textView)
    {
      this.view = textView;
      return this;
    }

    public LineTransform GetLineTransform(ITextViewLine line, double yPosition, Microsoft.VisualStudio.Text.Editor.ViewRelativePosition placement)
    {
      return GetLineTransform(line);
    }
  }
}

namespace Microsoft.VisualStudio.Text.Formatting
{
  public partial class LineTransformSource
  {
    public LineTransform GetLineTransform(ITextViewLine line)
    {
      return line.DefaultLineTransform;
    }
  }
}

Exercises

  1. Make all lines double-spaced.
    namespace Microsoft.VisualStudio.Text.Formatting
    {
      public partial class LineTransformSource
      {
        public LineTransform GetLineTransform(ITextViewLine line)
        {
          var currentTransform = line.LineTransform;
    
          // line.Height would not work here!
          var transform = new LineTransform(currentTransform.TopSpace, line.TextHeight,
            currentTransform.VerticalScale, currentTransform.Right);
    
          return transform;
        }
      }
    }
    

    Another version:

    namespace Microsoft.VisualStudio.Text.Formatting
    {
      public partial class LineTransformSource
      {
        public LineTransform GetLineTransform(ITextViewLine line)
        {
          var currentTransform = line.LineTransform;
    
          if (line.Change == TextViewLineChange.NewOrReformatted)
            return new LineTransform(currentTransform.TopSpace, line.TextHeight,
              currentTransform.VerticalScale, currentTransform.Right);
          else
            return line.DefaultLineTransform;
        }
      }
    }
    
  2. Sadece brace olan satirlari kucult.
    namespace Microsoft.VisualStudio.Text.Formatting
    {
      public partial class LineTransformSource
      {
        public LineTransform GetLineTransform(ITextViewLine line)
        {
          var s = line.Extent.GetText().Trim();
    
          if (line.Change == TextViewLineChange.NewOrReformatted &&
            (s == "{" || s == "}" || s == "};"))
          {
            return new LineTransform(0.5);
          }
    
          return line.DefaultLineTransform;
        }
      }
    }
    
  3. Collapse empty lines.
    namespace Microsoft.VisualStudio.Text.Formatting
    {
      public partial class LineTransformSource
      {
        public LineTransform GetLineTransform(ITextViewLine line)
        {
          if (string.IsNullOrWhiteSpace(line.Extent.GetText()))
            return new LineTransform(0.001);
          else
            return line.DefaultLineTransform;
        }
      }
    }
    
  4. Comment’li olan satirlari hide et.
    namespace Microsoft.VisualStudio.Text.Formatting
    {
      public partial class LineTransformSource
      {
        public LineTransform GetLineTransform(ITextViewLine line)
        {
          if (System.Text.RegularExpressions.Regex.IsMatch(line.Extent.GetText(), @"\s*//"))
            return new LineTransform(0.001);
          else
            return line.DefaultLineTransform;
        }
      }
    }
    
  5. Selection olan satirlari 1.61 (golden ratio) scale et.
    
    
  6. Caret’in oldugu line’i 1.61 (golden ratio) scale et.
    
    
  7. Satirlarin en sagina line number ekle, ama her number ayni hizada olsun.
  8. Ilk satirdan once copyright goster.
  9. Son satirdan sonra file istatistiklerini goster.

March 31, 2012

Highlight Classifier for Visual Studio Editor

Filed under: VS Editor — Tags: — Ceyhun Ciper @ 23:48

Sometimes you just want to see the data in a C# file:

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using Microsoft.VisualStudio.Utilities;
 
namespace Microsoft.VisualStudio.Text.Classification
{
	public static class Definitions
	{
		[Export(typeof(ClassificationTypeDefinition))]
		[Name("hilite-data")]
		internal static ClassificationTypeDefinition CTD;
 
		[Export(typeof(EditorFormatDefinition))]
		[ClassificationType(ClassificationTypeNames = "hilite-data")]
		[Name("hilite-data")]
		[DisplayName("Highlight Data")]
		[UserVisible(true)]
		[Order(Before = Priority.High)]
		internal sealed class CFD : ClassificationFormatDefinition
		{
			public CFD()
			{
				this.ForegroundColor = System.Windows.Media.Colors.LightGray;
			}
		}
 
		public static bool IsToBeHilited(this string classification)
		{
			var hilites = new[] { "string", "comment" };
			return hilites.Contains(classification.ToLower());
		}
	}
 
	[Export(typeof(IClassifierProvider))]
	[ContentType("CSharp")]
	internal sealed class HiliteDataClassifierProvider : IClassifierProvider
	{
		[Import]
		IClassificationTypeRegistryService ctrs = null;
		[Import]
		internal IClassifierAggregatorService AggregatorService = null;
 
		internal static bool ignoreRequest = false;
 
		public IClassifier GetClassifier(ITextBuffer textBuffer)
		{
			if (ignoreRequest)
				return null;
 
			try
			{
				ignoreRequest = true;
				return textBuffer.Properties.GetOrCreateSingletonProperty(
					() => new HiliteDataClassifier(AggregatorService.GetClassifier(textBuffer), ctrs));
			}
			finally
			{
				ignoreRequest = false;
			}
		}
	}
 
	internal sealed class HiliteDataClassifier : IClassifier
	{
		private IClassifier aggregator;
		private IClassificationTypeRegistryService ctrs;
 
		public HiliteDataClassifier(IClassifier aggregator, IClassificationTypeRegistryService ctrs)
		{
			this.aggregator = aggregator;
			this.ctrs = ctrs;
		}
 
		public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
		{
			var classifiedSpans =
				from cs in aggregator.GetClassificationSpans(span)
				let c = cs.ClassificationType.Classification
				where c.IsToBeHilited()
				select cs.Span;
 
			NormalizedSnapshotSpanCollection ignored = new NormalizedSnapshotSpanCollection(classifiedSpans);
			NormalizedSnapshotSpanCollection request = new NormalizedSnapshotSpanCollection(span);
 
			var spansToIgnore = NormalizedSnapshotSpanCollection.Difference(request, ignored);
 
			IClassificationType ct = ctrs.GetClassificationType("hilite-data");
 
			return spansToIgnore.Select(s => new ClassificationSpan(s, ct)).ToList();
		}
 
		public event EventHandler<ClassificationChangedEventArgs> ClassificationChanged;
	}
}

 

This is for my own consumption (being a blog and what not); use it at your own peril.

Simple Colorizer for Visual Studio Editor

Filed under: VS Editor — Tags: — Ceyhun Ciper @ 16:48

Here is probably the simplest colorizer code:

namespace Ciper
{
	[Export(typeof(IClassifierProvider)), ContentType("text")]
	internal class CProvider : IClassifierProvider
	{
		[Import]
		IClassificationTypeRegistryService ctrs = null;
 
		[Export, Name("ciper.text"), BaseDefinition("text")]
		internal static ClassificationTypeDefinition CTD;
 
		[Export(typeof(EditorFormatDefinition)), Name("ciper.text.format"), ClassificationType(ClassificationTypeNames = "ciper.text")]
		internal class CFD : ClassificationFormatDefinition
		{
			public CFD()
			{
				ForegroundColor = System.Windows.Media.Colors.DarkMagenta;
			}
		}
 
		public IClassifier GetClassifier(ITextBuffer buffer)
		{
			return new C(ctrs.GetClassificationType("ciper.text"));
		}
	}
 
	class C : IClassifier
	{
		IClassificationType ct;
		public C(IClassificationType ct)
		{
			this.ct = ct;
		}
		public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
		{
			return new[] { new ClassificationSpan(span, ct) };
		}
 
		public event EventHandler<ClassificationChangedEventArgs> ClassificationChanged;
	}
}

 

This will paint everything that is not yet classified in magenta.

March 26, 2012

Simplest VS Editor Classifier

Filed under: VS Editor — Tags: — Ceyhun Ciper @ 20:59

Burada sadece daha once belirlenmis olan ClassificationType’larini kullaniyorum.

Here is a minimal VS Editor classifier that doesn’t do anything (not even classify):

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 DummyClassifier : 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;
 
  [ContentType("csharp")]
  partial class DummyClassifier
  {
    public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
    {
      return new List<ClassificationSpan>();
    }
  }
}

Go figure why you might find it useful…

Exercises

  1. Tum span’lerin background’unu yellow yap
    namespace Ciper
    {
      using Microsoft.VisualStudio.Text;
      using Microsoft.VisualStudio.Text.Classification;
      using Microsoft.VisualStudio.Utilities;
      using System.Collections.Generic;
     
      [ContentType("text")]
      partial class DummyClassifier
      {
        public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
        {
          var l = new List<ClassificationSpan>();
          l.Add(new ClassificationSpan(span, classificationTypeRegistry.GetClassificationType("html server-side script")));
          return l;
        }
      }
    }
    
  2. Namespace’lerin background’unu yellow yap
    namespace Ciper
    {
      using Microsoft.VisualStudio.Text;
      using Microsoft.VisualStudio.Text.Classification;
      using Microsoft.VisualStudio.Utilities;
      using System.Collections.Generic;
      using System.Text.RegularExpressions;
     
      [ContentType("csharp")]
      partial class DummyClassifier
      {
        public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
        {
          var l = new List<ClassificationSpan>();
          var m = new Regex(@"^\s*namespace\s+(\S+)").Match(span.GetText());
          if (m.Success)
          {
            var s = new SnapshotSpan(span.Snapshot, span.Start + m.Groups[1].Index, m.Groups[1].Length);
            l.Add(new ClassificationSpan(s, classificationTypeRegistry.GetClassificationType("html server-side script")));
          }
          return l;
        }
      }
    }
    
  3. Namespace’leri string renginde goster
    namespace Ciper
    {
      using Microsoft.VisualStudio.Text;
      using Microsoft.VisualStudio.Text.Classification;
      using Microsoft.VisualStudio.Utilities;
      using System.Collections.Generic;
      using System.Text.RegularExpressions;
     
      [ContentType("csharp")]
      partial class DummyClassifier
      {
        public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
        {
          var l = new List<ClassificationSpan>();
          var m = new Regex(@"^\s*namespace\s+(\S+)").Match(span.GetText());
          if (m.Success)
          {
            var s = new SnapshotSpan(span.Snapshot, span.Start + m.Groups[1].Index, m.Groups[1].Length);
            l.Add(new ClassificationSpan(s, standardClassifications.StringLiteral));
          }
          return l;
        }
      }
    }
    
  4. Ceyhun ile baslayan comment’leri kirmizi olarak goster.
    namespace Ciper
    {
      using Microsoft.VisualStudio.Text;
      using Microsoft.VisualStudio.Text.Classification;
      using Microsoft.VisualStudio.Utilities;
      using System.Collections.Generic;
      using System.Text.RegularExpressions;
     
      [ContentType("csharp")]
      partial class DummyClassifier
      {
        public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
        {
          var l = new List<ClassificationSpan>();
          var m = new Regex(@"//\s*Ceyhun:?\s+(.+)"RegexOptions.IgnoreCase).Match(span.GetText());
          if (m.Success)
          {
            var s = new SnapshotSpan(span.Snapshot, span.Start + m.Groups[1].Index, m.Groups[1].Length);
            l.Add(new ClassificationSpan(s, standardClassifications.StringLiteral));
          }
          return l;
        }
      }
    }
    
  5. “// re 1.1.1.1 x@y.com data” gibi baslayan commentlerde ip’leri kirmizi, email’leri mavi, digerlerini disabled goster.
    namespace Ciper
    {
      using Microsoft.VisualStudio.Text;
      using Microsoft.VisualStudio.Text.Classification;
      using Microsoft.VisualStudio.Utilities;
      using System.Collections.Generic;
      using System.Text.RegularExpressions;
     
      [ContentType("csharp")]
      partial class DummyClassifier
      {
        public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
        {
          var l = new List<ClassificationSpan>();
          var m = new Regex(@"^\s*//\s*re:?\s+(.+)"RegexOptions.IgnoreCase).Match(span.GetText());
          if (m.Success)
          {
            var reGroup = m.Groups[1];
            int start = span.Start;
            var s = new SnapshotSpan(span.Snapshot, start + reGroup.Index, reGroup.Length);
            l.Add(new ClassificationSpan(s, standardClassifications.ExcludedCode));
     
            var ips = Regex.Matches(reGroup.Value, @"\d+\.\d+.\d+\.\d+");
            foreach (Match ip in ips)
            {
              s = new SnapshotSpan(span.Snapshot, start + ip.Index + reGroup.Index, ip.Length);
              l.Add(new ClassificationSpan(s, standardClassifications.StringLiteral));
            }
     
            var emails = Regex.Matches(reGroup.Value, @"\w+\@\w+\.\w+");
            foreach (Match email in emails)
            {
              s = new SnapshotSpan(span.Snapshot, start + email.Index + reGroup.Index, email.Length);
              l.Add(new ClassificationSpan(s, standardClassifications.Keyword));
            }
          }
          return l;
        }
      }
    }
    
  6. Output window’undaki tum span’leri comment renginde goster.
    namespace Ciper
    {
      using Microsoft.VisualStudio.Text;
      using Microsoft.VisualStudio.Text.Classification;
      using Microsoft.VisualStudio.Utilities;
      using System.Collections.Generic;
     
      [ContentType("output")]
      partial class DummyClassifier
      {
        public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
        {
          var l = new List<ClassificationSpan>();
          l.Add(new ClassificationSpan(span, standardClassifications.Comment));
          return l;
        }
      }
    }
    
  7. Output window’unu renklendir.
    namespace Ciper
    {
      using Microsoft.VisualStudio.Text;
      using Microsoft.VisualStudio.Text.Classification;
      using Microsoft.VisualStudio.Utilities;
      using System.Collections.Generic;
      using System.Text.RegularExpressions;
     
      [ContentType("output")]
      partial class DummyClassifier
      {
        public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
        {
          var l = new List<ClassificationSpan>();
          var text = span.GetText();
          Match m;
     
          m = Regex.Match(text, @"Project:\s([^,]+)");
          if (m.Success)
          {
            var projectSpan = new SnapshotSpan(span.Start + m.Groups[1].Index, m.Groups[1].Length);
            l.Add(new ClassificationSpan(projectSpan, standardClassifications.Keyword));
          }
     
          m = Regex.Match(text, @"error\s\w+:\s(.+)$");
          if (m.Success)
          {
            var projectSpan = new SnapshotSpan(span.Start + m.Groups[1].Index, m.Groups[1].Length);
            l.Add(new ClassificationSpan(projectSpan, standardClassifications.StringLiteral));
          }
     
          return l;
        }
      }
    }
    
  8. Show all standard classifications in the output window (with proper coloring).
    namespace Ciper
    {
      using Microsoft.VisualStudio.Language.StandardClassification;
      using Microsoft.VisualStudio.Text;
      using Microsoft.VisualStudio.Text.Classification;
      using Microsoft.VisualStudio.Utilities;
      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Reflection;
     
      [ContentType("output")]
      partial class DummyClassifier
      {
        bool initialized = false;
        IEnumerable<string> predefinedClassificationTypeNames
        {
          get
          {
            return
              typeof(PredefinedClassificationTypeNames)
              .GetFields(BindingFlags.Public | BindingFlags.Static)
              .Where(f => f.IsLiteral)
              .Select(f => f.GetValue(f).ToString());
          }
        }
     
        public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
        {
          if (!initialized)
          {
            outputWindowPane.OutputString(Environment.NewLine);
            outputWindowPane.OutputString("Predefined Classification Type Names:" + Environment.NewLine);
            outputWindowPane.OutputString("------------------------------------" + Environment.NewLine);
            foreach (var name in predefinedClassificationTypeNames)
            {
              outputWindowPane.OutputString(name + Environment.NewLine);
            }
            outputWindowPane.OutputString(Environment.NewLine);
            initialized = true;
          }
     
          var l = new List<ClassificationSpan>();
          var classificationTypeName = span.GetText().Trim();
          if (predefinedClassificationTypeNames.Any(n => n == classificationTypeName))
          {
            var classificationType = classificationTypeRegistry.GetClassificationType(classificationTypeName);
            if (classificationType != null)
              l.Add(new ClassificationSpan(span, classificationType));
          }
          return l;
        }
      }
    }
    
  9. Batch file’larinda REM ile baslayan satirlari comment olarak goster.
    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("bat")]
      partial class DummyClassifier
      {
        [ExportName("bat"), BaseDefinition("text")]
        ContentTypeDefinition batContentTypeDefinition;
     
        [ExportFileExtension(".bat"), ContentType("bat")]
        FileExtensionToContentTypeDefinition batFileExtensionDefinition;
     
        public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
        {
          var l = new List<ClassificationSpan>();
          var text = span.GetText();
          if (Regex.IsMatch(text, @"^\s*rem\s+"RegexOptions.IgnoreCase))
          {
            l.Add(new ClassificationSpan(span, classificationTypeRegistry.GetClassificationType("comment")));
          }
          return l;
        }
      }
    }
    
  10. Bash .sh file’larinda sha-bang satirlarini gri goster.
  11. Regex file’learinda (.re) ip ve email’leri degisik renkte goster.
  12. Passwords.txt file’ini renklendir.
Older Posts »

Blog at WordPress.com.