1
+ using System ;
2
+ using System . Collections . Generic ;
3
+ using System . Threading . Tasks ;
4
+ using System . Windows . Threading ;
5
+ using Microsoft . VisualStudio . Text ;
6
+ using Microsoft . VisualStudio . Text . Classification ;
7
+ using Microsoft . VisualStudio . Text . Editor ;
8
+ using Microsoft . VisualStudio . Text . Projection ;
9
+
10
+ namespace TextTools
11
+ {
12
+ internal class TrailingClassifier : IClassifier
13
+ {
14
+ private readonly IClassificationType _whitespace ;
15
+ private IWpfTextView _view ;
16
+ private readonly ITextBuffer _buffer ;
17
+ private ITrackingSpan _span ;
18
+ private SnapshotPoint _caret , _lastCaret ;
19
+ private static readonly IList < ClassificationSpan > _empty = new List < ClassificationSpan > ( ) ;
20
+
21
+ public TrailingClassifier ( IClassificationTypeRegistryService registry , ITextBuffer buffer )
22
+ {
23
+ _whitespace = registry . GetClassificationType ( TrailingClassificationTypes . Whitespace ) ;
24
+ _buffer = buffer ;
25
+ _buffer . Changed += OnSomethingChanged ;
26
+ }
27
+
28
+ public IList < ClassificationSpan > GetClassificationSpans ( SnapshotSpan span )
29
+ {
30
+ if ( span . IsEmpty || _view == null )
31
+ return _empty ;
32
+
33
+ IList < ClassificationSpan > list = new List < ClassificationSpan > ( ) ;
34
+ ITextSnapshotLine line = span . Snapshot . GetLineFromPosition ( span . Start . Position ) ;
35
+
36
+ if ( _caret > 0 && ( line . Extent . Contains ( _caret . Position ) || line . Extent . End . Position == _caret . Position ) )
37
+ {
38
+ _span = span . Snapshot . CreateTrackingSpan ( line . Extent , SpanTrackingMode . EdgeExclusive ) ;
39
+ return _empty ;
40
+ }
41
+
42
+ if ( span . Snapshot . TextBuffer is IProjectionBuffer projection )
43
+ {
44
+ SnapshotPoint point = projection . CurrentSnapshot . MapToSourceSnapshot ( line . Start + line . Length ) ;
45
+ ITextSnapshotLine basePoint = point . Snapshot . GetLineFromPosition ( point . Position ) ;
46
+
47
+ if ( basePoint . Length > line . Length )
48
+ return _empty ;
49
+ }
50
+
51
+ string text = line . GetText ( ) ;
52
+ string trimmed = text . TrimEnd ( ) ;
53
+ int diff = text . Length - trimmed . Length ;
54
+
55
+ if ( diff > 0 )
56
+ {
57
+ var ss = new SnapshotSpan ( span . Snapshot , line . Start + line . Length - diff , diff ) ;
58
+ list . Add ( new ClassificationSpan ( ss , _whitespace ) ) ;
59
+ }
60
+
61
+ return list ;
62
+ }
63
+
64
+
65
+ public async Task SetTextViewAsync ( IWpfTextView view )
66
+ {
67
+ if ( _view != null )
68
+ return ;
69
+
70
+ // Delay to allow Add Any File extension to place the caret
71
+ await Task . Delay ( 100 ) ;
72
+ if ( _view == null )
73
+ {
74
+ _view = view ;
75
+ _view . Caret . PositionChanged += OnSomethingChanged ;
76
+ _view . Closed += OnViewClosed ;
77
+ UpdateCaret ( ) ;
78
+ }
79
+ }
80
+
81
+ private void OnViewClosed ( object sender , EventArgs e )
82
+ {
83
+ var view = ( ITextView ) sender ;
84
+ view . Closed -= OnViewClosed ;
85
+ view . Caret . PositionChanged -= OnSomethingChanged ;
86
+
87
+ if ( _buffer != null )
88
+ _buffer . Changed -= OnSomethingChanged ;
89
+ }
90
+
91
+ private void OnSomethingChanged ( object sender , EventArgs e )
92
+ {
93
+ Dispatcher . CurrentDispatcher . BeginInvoke ( new Action ( ( ) =>
94
+ {
95
+ UpdateCaret ( ) ;
96
+
97
+ if ( _span != null )
98
+ OnClassificationChanged ( _span ) ;
99
+
100
+ } ) , DispatcherPriority . ApplicationIdle , null ) ;
101
+ }
102
+
103
+ private void UpdateCaret ( )
104
+ {
105
+ if ( _view == null )
106
+ return ;
107
+
108
+ _lastCaret = _caret ;
109
+ SnapshotPoint position = _view . Caret . Position . BufferPosition ;
110
+
111
+ if ( position == 0 )
112
+ return ;
113
+
114
+ if ( _view . TextBuffer is IProjectionBuffer projection && position <= _view . TextBuffer . CurrentSnapshot . Length )
115
+ {
116
+ _caret = projection . CurrentSnapshot . MapToSourceSnapshot ( position . Position ) ;
117
+ return ;
118
+ }
119
+
120
+ _caret = position ;
121
+ return ;
122
+ }
123
+
124
+ private void OnClassificationChanged ( ITrackingSpan span )
125
+ {
126
+ if ( ( _caret == 0 || _caret != _lastCaret ) && ClassificationChanged != null )
127
+ {
128
+ ClassificationChanged ( this , new ClassificationChangedEventArgs ( span . GetSpan ( span . TextBuffer . CurrentSnapshot ) ) ) ;
129
+ _span = null ;
130
+ }
131
+ }
132
+
133
+ public event EventHandler < ClassificationChangedEventArgs > ClassificationChanged ;
134
+ }
135
+ }
0 commit comments