I'm having trouble finding out how to do this and the Visual Studio SDK Reference is not very helpful either.
I'm trying to figure out how to get a NormalizedSnapshotSpanCollection
of XML comments. I want to place an icon next to them... I don't want an icon next to each line, but only next to the first line of each group...
///<summary>SomeXML Comment</summary> [ICON]
///<remarks>some remarks</remarks>
public void Foo()
{
///Some false XML comment line that does not get an icon.
}
Here's what I could get, I think it's pretty similar to what you need. I'm going to update this with more details, if you have questions.
I started with this sample from VS 2010 SDK web site. It is already pretty close to what you need, but requires several more steps.
Download the C# version, unpack to a folder, compile. To run it and test you need to go to Project > Properties > Debug
You need to choose "Start External Program" option and set path to your VS 2010 app, for example C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe
In the command line arguments set: /rootsuffix Exp
Now you should be able to run it, create some sample project in the opened VS, and if you type anywhere a six-digit number like 00AA00
it will be shown as a rectangle with the corresponding color. Close the debug VS instance.
Now let's edit some code. In ColorAdornmentTagger.cs
comment the define #define HIDING_TEXT
. This will show the adornment next to the text, not instead of it.
In the same file you need to find where SnapshotSpan adornmentSpan
is initialized and change the line to:
SnapshotSpan adornmentSpan = new SnapshotSpan(colorTagSpans[0].End, 0);
This will place adornment after the text span, not before it.
In the ColorTagger.cs
. Change the regex in constructor, so the constructor now looks like
internal ColorTagger(ITextBuffer buffer)
: base(
buffer,
new[] { new Regex(@"/// <summary>.*", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase) }
)
{
}
This will set the regex to recognize the method commentary line.
Other methods in this class we won't use, you can comment them or return some random color.
In the 'ColorAdornment.cs'. This is the adornment WPF control itself. First change the base class from Button
to ContentControl
. Change the constructor of the class to
internal ColorAdornment(ColorTag colorTag)
{
BitmapImage image = new BitmapImage();
using (FileStream stream = File.OpenRead("c:\\temp\\sologo.png"))
{
image.BeginInit();
image.StreamSource = stream;
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit();
}
this.Content = new Image() { Margin = new Thickness(20,0,0,0), Width = 100, Height = 30, Source = image };
}
You can change the image path to the image path you need. I just downloaded SO logo from Wikipedia and put into my temp folder.
Compile and run. You should be able to see the SO logo next to the comments in the debug VS instance.
Some extra remarks.
First, in this way you just get a working prototype to start with, you should rename the classes and clean-up the code for your needs.
Second, when I was debugging it my debug VS was freezing from time to time. I think this might be related to locks in the IntraTextAdornmentTagger.cs
If you also see freezing, try to update the following method in this way:
protected void InvalidateSpans(IList<SnapshotSpan> spans)
{
if (spans.Count == 0)
return;
bool wasEmpty = false;
lock (this.invalidatedSpans)
{
wasEmpty = this.invalidatedSpans.Count == 0;
this.invalidatedSpans.AddRange(spans);
}
if (wasEmpty)
this.view.VisualElement.Dispatcher.BeginInvoke(new Action(AsyncUpdate));
}
and the AsyncUpdate in this way:
private void AsyncUpdate()
{
// Store the snapshot that we're now current with and send an event
// for the text that has changed.
if (this.snapshot != this.view.TextBuffer.CurrentSnapshot)
{
this.snapshot = this.view.TextBuffer.CurrentSnapshot;
Dictionary<SnapshotSpan, TAdornment> translatedAdornmentCache = new Dictionary<SnapshotSpan, TAdornment>();
foreach (var keyValuePair in this.adornmentCache)
translatedAdornmentCache.Add(keyValuePair.Key.TranslateTo(this.snapshot, SpanTrackingMode.EdgeExclusive), keyValuePair.Value);
this.adornmentCache = translatedAdornmentCache;
}
List<SnapshotSpan> spansCopy;
lock (this.invalidatedSpans)
{
spansCopy = this.invalidatedSpans.ToList();
this.invalidatedSpans.Clear();
}
List<SnapshotSpan> translatedSpans = spansCopy.Select(s => s.TranslateTo(this.snapshot, SpanTrackingMode.EdgeInclusive)).ToList();
if (translatedSpans.Count == 0)
return;
var start = translatedSpans.Select(span => span.Start).Min();
var end = translatedSpans.Select(span => span.End).Max();
RaiseTagsChanged(new SnapshotSpan(start, end));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With