зеркало из https://github.com/microsoft/ivy.git
218 строки
8.1 KiB
C#
218 строки
8.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel.Composition;
|
|
using System.Linq;
|
|
using System.Windows.Media;
|
|
using Microsoft.VisualStudio.Text;
|
|
using Microsoft.VisualStudio.Text.Classification;
|
|
using Microsoft.VisualStudio.Text.Editor;
|
|
using Microsoft.VisualStudio.Text.Operations;
|
|
using Microsoft.VisualStudio.Text.Tagging;
|
|
using Microsoft.VisualStudio.Utilities;
|
|
|
|
|
|
namespace IvyLanguage
|
|
{
|
|
|
|
#if LATER_MAYBE
|
|
|
|
#region (the current annoying) word highligher
|
|
|
|
internal class HighlightWordTagger : ITagger<HighlightWordTag>
|
|
{
|
|
ITextView View { get; set; }
|
|
ITextBuffer SourceBuffer { get; set; }
|
|
ITextSearchService TextSearchService { get; set; }
|
|
ITextStructureNavigator TextStructureNavigator { get; set; }
|
|
NormalizedSnapshotSpanCollection WordSpans { get; set; }
|
|
SnapshotSpan? CurrentWord { get; set; }
|
|
SnapshotPoint RequestedPoint { get; set; }
|
|
object updateLock = new object();
|
|
|
|
public HighlightWordTagger(ITextView view, ITextBuffer sourceBuffer, ITextSearchService textSearchService,
|
|
ITextStructureNavigator textStructureNavigator) {
|
|
this.View = view;
|
|
this.SourceBuffer = sourceBuffer;
|
|
this.TextSearchService = textSearchService;
|
|
this.TextStructureNavigator = textStructureNavigator;
|
|
this.WordSpans = new NormalizedSnapshotSpanCollection();
|
|
this.CurrentWord = null;
|
|
this.View.Caret.PositionChanged += CaretPositionChanged;
|
|
this.View.LayoutChanged += ViewLayoutChanged;
|
|
}
|
|
|
|
void ViewLayoutChanged(object sender, TextViewLayoutChangedEventArgs e) {
|
|
// If a new snapshot wasn't generated, then skip this layout
|
|
if (e.NewSnapshot != e.OldSnapshot) {
|
|
UpdateAtCaretPosition(View.Caret.Position);
|
|
}
|
|
}
|
|
|
|
void CaretPositionChanged(object sender, CaretPositionChangedEventArgs e) {
|
|
UpdateAtCaretPosition(e.NewPosition);
|
|
}
|
|
|
|
public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
|
|
|
|
void UpdateAtCaretPosition(CaretPosition caretPosition) {
|
|
SnapshotPoint? point = caretPosition.Point.GetPoint(SourceBuffer, caretPosition.Affinity);
|
|
|
|
if (!point.HasValue)
|
|
return;
|
|
|
|
// If the new caret position is still within the current word (and on the same snapshot), we don't need to check it
|
|
if (CurrentWord.HasValue
|
|
&& CurrentWord.Value.Snapshot == View.TextSnapshot
|
|
&& CurrentWord.Value.Start <= point.Value && point.Value <= CurrentWord.Value.End) {
|
|
return;
|
|
}
|
|
|
|
RequestedPoint = point.Value;
|
|
UpdateWordAdornments();
|
|
}
|
|
|
|
void UpdateWordAdornments() {
|
|
SnapshotPoint currentRequest = RequestedPoint;
|
|
List<SnapshotSpan> wordSpans = new List<SnapshotSpan>();
|
|
//Find all words in the buffer like the one the caret is on
|
|
TextExtent word = TextStructureNavigator.GetExtentOfWord(currentRequest);
|
|
bool foundWord = true;
|
|
//If we've selected something not worth highlighting, we might have missed a "word" by a little bit
|
|
if (!WordExtentIsValid(currentRequest, word)) {
|
|
//Before we retry, make sure it is worthwhile
|
|
if (word.Span.Start != currentRequest
|
|
|| currentRequest == currentRequest.GetContainingLine().Start
|
|
|| char.IsWhiteSpace((currentRequest - 1).GetChar())) {
|
|
foundWord = false;
|
|
} else {
|
|
// Try again, one character previous.
|
|
//If the caret is at the end of a word, pick up the word.
|
|
word = TextStructureNavigator.GetExtentOfWord(currentRequest - 1);
|
|
|
|
//If the word still isn't valid, we're done
|
|
if (!WordExtentIsValid(currentRequest, word))
|
|
foundWord = false;
|
|
}
|
|
}
|
|
|
|
if (!foundWord) {
|
|
//If we couldn't find a word, clear out the existing markers
|
|
SynchronousUpdate(currentRequest, new NormalizedSnapshotSpanCollection(), null);
|
|
return;
|
|
}
|
|
|
|
SnapshotSpan currentWord = word.Span;
|
|
//If this is the current word, and the caret moved within a word, we're done.
|
|
if (CurrentWord.HasValue && currentWord == CurrentWord)
|
|
return;
|
|
|
|
//Find the new spans
|
|
FindData findData = new FindData(currentWord.GetText(), currentWord.Snapshot);
|
|
findData.FindOptions = FindOptions.WholeWord | FindOptions.MatchCase;
|
|
|
|
wordSpans.AddRange(TextSearchService.FindAll(findData));
|
|
|
|
//If another change hasn't happened, do a real update
|
|
if (currentRequest == RequestedPoint)
|
|
SynchronousUpdate(currentRequest, new NormalizedSnapshotSpanCollection(wordSpans), currentWord);
|
|
}
|
|
|
|
static bool WordExtentIsValid(SnapshotPoint currentRequest, TextExtent word) {
|
|
return word.IsSignificant
|
|
&& currentRequest.Snapshot.GetText(word.Span).Any(c => char.IsLetter(c));
|
|
}
|
|
|
|
void SynchronousUpdate(SnapshotPoint currentRequest, NormalizedSnapshotSpanCollection newSpans, SnapshotSpan? newCurrentWord) {
|
|
lock (updateLock) {
|
|
if (currentRequest != RequestedPoint)
|
|
return;
|
|
|
|
WordSpans = newSpans;
|
|
CurrentWord = newCurrentWord;
|
|
|
|
var chngd = TagsChanged;
|
|
if (chngd != null)
|
|
chngd(this, new SnapshotSpanEventArgs(new SnapshotSpan(SourceBuffer.CurrentSnapshot, 0, SourceBuffer.CurrentSnapshot.Length)));
|
|
}
|
|
}
|
|
|
|
public IEnumerable<ITagSpan<HighlightWordTag>> GetTags(NormalizedSnapshotSpanCollection spans) {
|
|
if (CurrentWord == null)
|
|
yield break;
|
|
|
|
// Hold on to a "snapshot" of the word spans and current word, so that we maintain the same
|
|
// collection throughout
|
|
SnapshotSpan currentWord = CurrentWord.Value;
|
|
NormalizedSnapshotSpanCollection wordSpans = WordSpans;
|
|
|
|
if (spans.Count == 0 || WordSpans.Count == 0)
|
|
yield break;
|
|
|
|
// If the requested snapshot isn't the same as the one our words are on, translate our spans to the expected snapshot
|
|
if (spans[0].Snapshot != wordSpans[0].Snapshot) {
|
|
wordSpans = new NormalizedSnapshotSpanCollection(
|
|
wordSpans.Select(span => span.TranslateTo(spans[0].Snapshot, SpanTrackingMode.EdgeExclusive)));
|
|
|
|
currentWord = currentWord.TranslateTo(spans[0].Snapshot, SpanTrackingMode.EdgeExclusive);
|
|
}
|
|
|
|
// First, yield back the word the cursor is under (if it overlaps)
|
|
// Note that we'll yield back the same word again in the wordspans collection;
|
|
// the duplication here is expected.
|
|
if (spans.OverlapsWith(new NormalizedSnapshotSpanCollection(currentWord)))
|
|
yield return new TagSpan<HighlightWordTag>(currentWord, new HighlightWordTag());
|
|
|
|
// Second, yield all the other words in the file
|
|
foreach (SnapshotSpan span in NormalizedSnapshotSpanCollection.Overlap(spans, wordSpans)) {
|
|
yield return new TagSpan<HighlightWordTag>(span, new HighlightWordTag());
|
|
}
|
|
}
|
|
}
|
|
|
|
internal class HighlightWordTag : TextMarkerTag
|
|
{
|
|
public HighlightWordTag() : base("MarkerFormatDefinition/HighlightWordFormatDefinition") { }
|
|
}
|
|
|
|
[Export(typeof(EditorFormatDefinition))]
|
|
[Name("MarkerFormatDefinition/HighlightWordFormatDefinition")]
|
|
[UserVisible(true)]
|
|
internal class HighlightWordFormatDefinition : MarkerFormatDefinition
|
|
{
|
|
public HighlightWordFormatDefinition() {
|
|
this.BackgroundColor = Colors.LightBlue;
|
|
this.ForegroundColor = Colors.DarkBlue;
|
|
this.DisplayName = "Highlight Word";
|
|
this.ZOrder = 5;
|
|
}
|
|
}
|
|
|
|
[Export(typeof(IViewTaggerProvider))]
|
|
[ContentType("text")]
|
|
[TagType(typeof(TextMarkerTag))]
|
|
internal class HighlightWordTaggerProvider : IViewTaggerProvider
|
|
{
|
|
[Import]
|
|
internal ITextSearchService TextSearchService { get; set; }
|
|
|
|
[Import]
|
|
internal ITextStructureNavigatorSelectorService TextStructureNavigatorSelector { get; set; }
|
|
|
|
public ITagger<T> CreateTagger<T>(ITextView textView, ITextBuffer buffer) where T : ITag {
|
|
//provide highlighting only on the top buffer
|
|
if (textView.TextBuffer != buffer)
|
|
return null;
|
|
|
|
ITextStructureNavigator textStructureNavigator =
|
|
TextStructureNavigatorSelector.GetTextStructureNavigator(buffer);
|
|
|
|
return new HighlightWordTagger(textView, buffer, TextSearchService, textStructureNavigator) as ITagger<T>;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endif
|
|
|
|
}
|