PageRenderTime 62ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/EditorExtensions/Classifications/CSS/CssHighlightWordTagger.cs

https://github.com/andymaster01/WebEssentials2012
C# | 225 lines | 174 code | 37 blank | 14 comment | 43 complexity | 5fe450406ffc99cf3fd062bdf8c3170d MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. using Microsoft.CSS.Core;
  2. using Microsoft.CSS.Editor;
  3. using Microsoft.VisualStudio.Text;
  4. using Microsoft.VisualStudio.Text.Editor;
  5. using Microsoft.VisualStudio.Text.Operations;
  6. using Microsoft.VisualStudio.Text.Tagging;
  7. using Microsoft.VisualStudio.Utilities;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.ComponentModel.Composition;
  11. using System.Linq;
  12. using System.Threading.Tasks;
  13. using System.Windows.Threading;
  14. namespace MadsKristensen.EditorExtensions
  15. {
  16. [Export(typeof(IViewTaggerProvider))]
  17. [ContentType("CSS")]
  18. [TagType(typeof(TextMarkerTag))]
  19. internal class CssHighlightWordTaggerProvider : IViewTaggerProvider
  20. {
  21. [Import]
  22. internal ITextSearchService TextSearchService { get; set; }
  23. [Import]
  24. internal ITextStructureNavigatorSelectorService TextStructureNavigatorSelector { get; set; }
  25. public ITagger<T> CreateTagger<T>(ITextView textView, ITextBuffer buffer) where T : ITag
  26. {
  27. if (textView.TextBuffer != buffer)
  28. return null;
  29. ITextStructureNavigator textStructureNavigator = TextStructureNavigatorSelector.GetTextStructureNavigator(buffer);
  30. //return new HighlightWordTagger(textView, buffer, TextSearchService, textStructureNavigator) as ITagger<T>;
  31. return buffer.Properties.GetOrCreateSingletonProperty(() => new CssHighlightWordTagger(textView, buffer, TextSearchService, textStructureNavigator)) as ITagger<T>;
  32. }
  33. }
  34. internal class HighlightWordTag : TextMarkerTag
  35. {
  36. public HighlightWordTag() : base("MarkerFormatDefinition/HighlightWordFormatDefinition") { }
  37. }
  38. internal class CssHighlightWordTagger : ITagger<HighlightWordTag>
  39. {
  40. ITextView _view { get; set; }
  41. ITextBuffer _buffer { get; set; }
  42. ITextSearchService _textSearchService { get; set; }
  43. ITextStructureNavigator _textStructureNavigator { get; set; }
  44. NormalizedSnapshotSpanCollection _wordSpans { get; set; }
  45. SnapshotSpan? _currentWord { get; set; }
  46. SnapshotPoint _requestedPoint { get; set; }
  47. readonly object _syncLock = new object();
  48. private CssTree _tree;
  49. public CssHighlightWordTagger(ITextView view, ITextBuffer sourceBuffer, ITextSearchService textSearchService, ITextStructureNavigator textStructureNavigator)
  50. {
  51. this._view = view;
  52. this._buffer = sourceBuffer;
  53. this._textSearchService = textSearchService;
  54. this._textStructureNavigator = textStructureNavigator;
  55. this._wordSpans = new NormalizedSnapshotSpanCollection();
  56. this._currentWord = null;
  57. this._view.Caret.PositionChanged += CaretPositionChanged;
  58. this._view.LayoutChanged += ViewLayoutChanged;
  59. }
  60. private void ViewLayoutChanged(object sender, TextViewLayoutChangedEventArgs e)
  61. {
  62. if (e.NewSnapshot != e.OldSnapshot)
  63. {
  64. Dispatcher.CurrentDispatcher.BeginInvoke(
  65. new Action(() => UpdateAtCaretPosition(_view.Caret.Position)), DispatcherPriority.ApplicationIdle, null);
  66. }
  67. }
  68. private void CaretPositionChanged(object sender, CaretPositionChangedEventArgs e)
  69. {
  70. Dispatcher.CurrentDispatcher.BeginInvoke(
  71. new Action(() => UpdateAtCaretPosition(e.NewPosition)), DispatcherPriority.ApplicationIdle, null);
  72. }
  73. public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
  74. private void UpdateAtCaretPosition(CaretPosition caretPosition)
  75. {
  76. if (!WESettings.GetBoolean(WESettings.Keys.EnableCssSelectorHighligting))
  77. return;
  78. SnapshotPoint? point = caretPosition.Point.GetPoint(_buffer, caretPosition.Affinity);
  79. if (!point.HasValue || !EnsureInitialized())
  80. return;
  81. ParseItem item = _tree.StyleSheet.ItemBeforePosition(point.Value.Position);
  82. if (item == null)
  83. return;
  84. ParseItem validItem = item.FindType<ItemName>();
  85. if (validItem == null)
  86. validItem = item.FindType<ClassSelector>();
  87. if (validItem == null)
  88. validItem = item.FindType<IdSelector>();
  89. // If the new caret position is still within the current word (and on the same snapshot), we don't need to check it
  90. if (_currentWord.HasValue
  91. && _currentWord.Value.Snapshot == _view.TextSnapshot
  92. && point.Value >= _currentWord.Value.Start
  93. && point.Value <= _currentWord.Value.End)
  94. {
  95. return;
  96. }
  97. _requestedPoint = point.Value;
  98. Task.Run(new Action(() => UpdateWordAdornments(validItem)));
  99. //UpdateWordAdornments(validItem);
  100. }
  101. void UpdateWordAdornments(ParseItem item)
  102. {
  103. SnapshotPoint currentRequest = _requestedPoint;
  104. List<SnapshotSpan> wordSpans = new List<SnapshotSpan>();
  105. SnapshotSpan currentWord;
  106. if (item != null)
  107. {
  108. currentWord = new SnapshotSpan(new SnapshotPoint(_buffer.CurrentSnapshot, item.Start), item.Length);// word.Span;
  109. //If this is the current word, and the caret moved within a word, we're done.
  110. if (_currentWord.HasValue && currentWord == _currentWord)
  111. return;
  112. //Find the new spans
  113. FindData findData = new FindData(item.Text, currentWord.Snapshot);
  114. findData.FindOptions = FindOptions.WholeWord | FindOptions.MatchCase;
  115. wordSpans.AddRange(_textSearchService.FindAll(findData));
  116. if (wordSpans.Count == 1)
  117. wordSpans.Clear();
  118. }
  119. else
  120. {
  121. TextExtent word = _textStructureNavigator.GetExtentOfWord(currentRequest);
  122. currentWord = word.Span;
  123. }
  124. //If another change hasn't happened, do a real update
  125. if (currentRequest == _requestedPoint)
  126. {
  127. //Task.Run(new Action(() => SynchronousUpdate(currentRequest, new NormalizedSnapshotSpanCollection(wordSpans), currentWord)));
  128. SynchronousUpdate(currentRequest, new NormalizedSnapshotSpanCollection(wordSpans), currentWord);
  129. }
  130. }
  131. private void SynchronousUpdate(SnapshotPoint currentRequest, NormalizedSnapshotSpanCollection newSpans, SnapshotSpan? newCurrentWord)
  132. {
  133. lock (_syncLock)
  134. {
  135. if (currentRequest != _requestedPoint)
  136. return;
  137. _wordSpans = newSpans;
  138. _currentWord = newCurrentWord;
  139. var tempEvent = TagsChanged;
  140. if (tempEvent != null)
  141. tempEvent(this, new SnapshotSpanEventArgs(new SnapshotSpan(_buffer.CurrentSnapshot, 0, _buffer.CurrentSnapshot.Length)));
  142. }
  143. }
  144. public IEnumerable<ITagSpan<HighlightWordTag>> GetTags(NormalizedSnapshotSpanCollection spans)
  145. {
  146. if (_currentWord == null)
  147. yield break;
  148. // Hold on to a "snapshot" of the word spans and current word, so that we maintain the same
  149. // collection throughout
  150. SnapshotSpan currentWord = _currentWord.Value;
  151. NormalizedSnapshotSpanCollection wordSpans = _wordSpans;
  152. if (spans.Count == 0 || _wordSpans.Count == 0)
  153. yield break;
  154. // If the requested snapshot isn't the same as the one our words are on, translate our spans to the expected snapshot
  155. if (spans[0].Snapshot != wordSpans[0].Snapshot)
  156. {
  157. wordSpans = new NormalizedSnapshotSpanCollection(
  158. wordSpans.Select(span => span.TranslateTo(spans[0].Snapshot, SpanTrackingMode.EdgeExclusive)));
  159. currentWord = currentWord.TranslateTo(spans[0].Snapshot, SpanTrackingMode.EdgeExclusive);
  160. }
  161. // First, yield back the word the cursor is under (if it overlaps)
  162. // Note that we'll yield back the same word again in the wordspans collection;
  163. // the duplication here is expected.
  164. if (spans.OverlapsWith(new NormalizedSnapshotSpanCollection(currentWord)))
  165. yield return new TagSpan<HighlightWordTag>(currentWord, new HighlightWordTag());
  166. // Second, yield all the other words in the file
  167. foreach (SnapshotSpan span in NormalizedSnapshotSpanCollection.Overlap(spans, wordSpans))
  168. {
  169. yield return new TagSpan<HighlightWordTag>(span, new HighlightWordTag());
  170. }
  171. }
  172. private bool EnsureInitialized()
  173. {
  174. if (_tree == null)
  175. {
  176. try
  177. {
  178. CssEditorDocument document = CssEditorDocument.FromTextBuffer(_buffer);
  179. _tree = document.Tree;
  180. }
  181. catch (ArgumentNullException)
  182. {
  183. }
  184. }
  185. return _tree != null;
  186. }
  187. }
  188. }