99using Microsoft . CodeAnalysis . Editor . Shared . Extensions ;
1010using Microsoft . CodeAnalysis . Editor . Shared . Tagging ;
1111using Microsoft . CodeAnalysis . Editor . Tagging ;
12- using Microsoft . CodeAnalysis . ErrorReporting ;
1312using Microsoft . CodeAnalysis . Host ;
1413using Microsoft . CodeAnalysis . Internal . Log ;
15- using Microsoft . CodeAnalysis . Shared . TestHooks ;
1614using Microsoft . CodeAnalysis . Text ;
1715using Microsoft . CodeAnalysis . Text . Shared . Extensions ;
1816using Microsoft . CodeAnalysis . Threading ;
@@ -44,8 +42,8 @@ private sealed record CachedServices(
4442 private static readonly object s_uniqueKey = new ( ) ;
4543
4644 private readonly SyntacticClassificationTaggerProvider _taggerProvider ;
47- private readonly ITextBuffer2 _subjectBuffer ;
48- private readonly WorkspaceRegistration _workspaceRegistration ;
45+ private readonly ITextBuffer _subjectBuffer ;
46+ private readonly ITaggerEventSource _taggerEventSource ;
4947
5048 private readonly CancellationTokenSource _disposalCancellationSource = new ( ) ;
5149
@@ -61,10 +59,6 @@ private sealed record CachedServices(
6159 /// </summary>
6260 private readonly TimeSpan _diffTimeout ;
6361
64- private Workspace ? _workspace ;
65- private WorkspaceEventRegistration ? _workspaceChangedDisposer ;
66- private WorkspaceEventRegistration ? _workspaceDocumentActiveContextChangedDisposer ;
67-
6862 /// <summary>
6963 /// Cached values for the last services we computed for a particular <see cref="Workspace"/> and <see
7064 /// cref="IContentType"/>. These rarely change, and are expensive enough to show up in very hot scenarios (like
@@ -89,12 +83,11 @@ private sealed record CachedServices(
8983 /// again and again to find exactly same answer
9084 /// </summary>
9185 private readonly ClassifiedLineCache _lineCache ;
92-
9386 private int _taggerReferenceCount ;
9487
9588 public TagComputer (
9689 SyntacticClassificationTaggerProvider taggerProvider ,
97- ITextBuffer2 subjectBuffer ,
90+ ITextBuffer subjectBuffer ,
9891 TimeSpan diffTimeout )
9992 {
10093 _taggerProvider = taggerProvider ;
@@ -109,13 +102,19 @@ public TagComputer(
109102
110103 _lineCache = new ClassifiedLineCache ( taggerProvider . ThreadingContext ) ;
111104
112- _workspaceRegistration = Workspace . GetWorkspaceRegistration ( subjectBuffer . AsTextContainer ( ) ) ;
113- _workspaceRegistration . WorkspaceChanged += OnWorkspaceRegistrationChanged ;
105+ _taggerEventSource = TaggerEventSources . Compose (
106+ TaggerEventSources . OnWorkspaceChanged ( subjectBuffer , taggerProvider . _listener ) ,
107+ TaggerEventSources . OnWorkspaceRegistrationChanged ( subjectBuffer ) ,
108+ TaggerEventSources . OnDocumentActiveContextChanged ( subjectBuffer ) ,
109+ TaggerEventSources . OnTextChanged ( subjectBuffer ) ,
110+ TaggerEventSources . OnParseOptionChanged ( subjectBuffer ) ) ;
111+
112+ _taggerEventSource . Connect ( ) ;
114113
115- if ( _workspaceRegistration . Workspace != null )
116- ConnectToWorkspace ( _workspaceRegistration . Workspace ) ;
114+ _taggerEventSource . Changed += OnEventSourceChanged ;
117115
118- _subjectBuffer . ChangedOnBackground += this . OnSubjectBufferChanged ;
116+ // Kick off work to classify the current snapshot of the subject buffer.
117+ _workQueue . AddWork ( _subjectBuffer . CurrentSnapshot ) ;
119118 }
120119
121120 public static TagComputer GetOrCreate (
@@ -130,14 +129,13 @@ public static TagComputer GetOrCreate(
130129 return tagComputer ;
131130 }
132131
133- private void DisconnectTagComputer ( )
134- => _subjectBuffer . Properties . RemoveProperty ( s_uniqueKey ) ;
135-
136132 public event EventHandler < SnapshotSpanEventArgs > ? TagsChanged ;
137133
138134 private ( SolutionServices solutionServices , IClassificationService classificationService ) ? TryGetClassificationService ( ITextSnapshot snapshot )
139135 {
140- var workspace = _workspace ;
136+ var document = snapshot . GetOpenDocumentInCurrentContextWithChanges ( ) ;
137+ var workspace = document ? . Project . Solution . Workspace ;
138+
141139 var contentType = snapshot . ContentType ;
142140 var lastCachedServices = _lastCachedServices ;
143141
@@ -158,45 +156,6 @@ private void DisconnectTagComputer()
158156 return ( lastCachedServices . SolutionServices , lastCachedServices . ClassificationService ) ;
159157 }
160158
161- #region Workspace Hookup
162-
163- private void OnWorkspaceRegistrationChanged ( object ? sender , EventArgs e )
164- {
165- var token = _taggerProvider . _listener . BeginAsyncOperation ( nameof ( OnWorkspaceRegistrationChanged ) ) ;
166- var task = SwitchToMainThreadAndHookupWorkspaceAsync ( ) ;
167- task . CompletesAsyncOperation ( token ) ;
168- }
169-
170- private async Task SwitchToMainThreadAndHookupWorkspaceAsync ( )
171- {
172- try
173- {
174- await _taggerProvider . ThreadingContext . JoinableTaskFactory . SwitchToMainThreadAsync ( _disposalCancellationSource . Token ) ;
175-
176- // We both try to connect synchronously, and register for workspace registration events.
177- // It's possible (particularly in tests), to connect in the startup path, but then get a
178- // previously scheduled, but not yet delivered event. Don't bother connecting to the
179- // same workspace again in that case.
180- var newWorkspace = _workspaceRegistration . Workspace ;
181- if ( newWorkspace == _workspace )
182- return ;
183-
184- DisconnectFromWorkspace ( ) ;
185-
186- if ( newWorkspace != null )
187- ConnectToWorkspace ( newWorkspace ) ;
188- }
189- catch ( OperationCanceledException )
190- {
191- // can happen if we were disposed of.
192- }
193- catch ( Exception e ) when ( FatalError . ReportAndCatch ( e ) )
194- {
195- // We were in a fire and forget task. So just report the NFW and do not let
196- // this bleed out to an unhandled exception.
197- }
198- }
199-
200159 internal void IncrementReferenceCount ( )
201160 {
202161 _taggerProvider . ThreadingContext . ThrowIfNotOnUIThread ( ) ;
@@ -213,103 +172,16 @@ internal void DecrementReferenceCount()
213172 // stop any bg work we're doing.
214173 _disposalCancellationSource . Cancel ( ) ;
215174
216- _subjectBuffer . ChangedOnBackground -= this . OnSubjectBufferChanged ;
175+ _taggerEventSource . Changed -= OnEventSourceChanged ;
176+ _taggerEventSource . Disconnect ( ) ;
217177
218- DisconnectFromWorkspace ( ) ;
219- _workspaceRegistration . WorkspaceChanged -= OnWorkspaceRegistrationChanged ;
220- DisconnectTagComputer ( ) ;
178+ _subjectBuffer . Properties . RemoveProperty ( s_uniqueKey ) ;
179+ _lastCachedServices = null ;
221180 }
222181 }
223182
224- private void ConnectToWorkspace ( Workspace workspace )
225- {
226- _taggerProvider . ThreadingContext . ThrowIfNotOnUIThread ( ) ;
227-
228- _workspace = workspace ;
229- _workspaceChangedDisposer = _workspace . RegisterWorkspaceChangedHandler ( this . OnWorkspaceChanged ) ;
230- _workspaceDocumentActiveContextChangedDisposer = _workspace . RegisterDocumentActiveContextChangedHandler ( this . OnDocumentActiveContextChanged ) ;
231-
232- // Now that we've connected to the workspace, kick off work to reclassify this buffer.
233- _workQueue . AddWork ( _subjectBuffer . CurrentSnapshot ) ;
234- }
235-
236- public void DisconnectFromWorkspace ( )
237- {
238- _taggerProvider . ThreadingContext . ThrowIfNotOnUIThread ( ) ;
239- _lastCachedServices = null ;
240-
241- lock ( _gate )
242- {
243- _lastProcessedData = null ;
244- }
245-
246- if ( _workspace != null )
247- {
248- _workspaceChangedDisposer ? . Dispose ( ) ;
249- _workspaceChangedDisposer = null ;
250-
251- _workspaceDocumentActiveContextChangedDisposer ? . Dispose ( ) ;
252- _workspaceDocumentActiveContextChangedDisposer = null ;
253-
254- _workspace = null ;
255-
256- // Now that we've disconnected to the workspace, kick off work to reclassify this buffer.
257- _workQueue . AddWork ( _subjectBuffer . CurrentSnapshot ) ;
258- }
259- }
260-
261- #endregion
262-
263- #region Event Handling
264-
265- private void OnSubjectBufferChanged ( object ? sender , TextContentChangedEventArgs args )
266- {
267- // we know a change to our buffer is always affecting this document. So we can
268- // just enqueue the work to reclassify things unilaterally.
269- _workQueue . AddWork ( args . After ) ;
270- }
271-
272- private void OnDocumentActiveContextChanged ( DocumentActiveContextChangedEventArgs args )
273- {
274- if ( _workspace == null )
275- return ;
276-
277- var documentId = args . NewActiveContextDocumentId ;
278- var bufferDocumentId = _workspace . GetDocumentIdInCurrentContext ( _subjectBuffer . AsTextContainer ( ) ) ;
279- if ( bufferDocumentId != documentId )
280- return ;
281-
282- _workQueue . AddWork ( _subjectBuffer . CurrentSnapshot ) ;
283- }
284-
285- private void OnWorkspaceChanged ( WorkspaceChangeEventArgs args )
286- {
287- // We may be getting an event for a workspace we already disconnected from. If so,
288- // ignore them. We won't be able to find the Document corresponding to our text buffer,
289- // so we can't reasonably classify this anyways.
290- var workspace = _workspace ;
291- if ( args . NewSolution . Workspace != workspace )
292- return ;
293-
294- if ( args . Kind != WorkspaceChangeKind . ProjectChanged )
295- return ;
296-
297- var documentId = workspace . GetDocumentIdInCurrentContext ( _subjectBuffer . AsTextContainer ( ) ) ;
298- if ( args . ProjectId != documentId ? . ProjectId )
299- return ;
300-
301- var oldProject = args . OldSolution . GetProject ( args . ProjectId ) ;
302- var newProject = args . NewSolution . GetProject ( args . ProjectId ) ;
303-
304- // In case of parse options change reclassify the doc as it may have affected things
305- // like preprocessor directives.
306- if ( Equals ( oldProject ? . ParseOptions , newProject ? . ParseOptions ) )
307- return ;
308-
309- _workQueue . AddWork ( _subjectBuffer . CurrentSnapshot ) ;
310- }
311-
312- #endregion
183+ private void OnEventSourceChanged ( object ? sender , EventArgs e )
184+ => _workQueue . AddWork ( _subjectBuffer . CurrentSnapshot ) ;
313185
314186 /// <summary>
315187 /// Parses the document in the background and determines what has changed to report to
@@ -416,7 +288,7 @@ public void AddTags(NormalizedSnapshotSpanCollection spans, SegmentedList<TagSpa
416288 private void AddTagsWorker ( NormalizedSnapshotSpanCollection spans , SegmentedList < TagSpan < IClassificationTag > > tags )
417289 {
418290 _taggerProvider . ThreadingContext . ThrowIfNotOnUIThread ( ) ;
419- if ( spans . Count == 0 || _workspace == null )
291+ if ( spans . Count == 0 )
420292 return ;
421293
422294 var snapshot = spans [ 0 ] . Snapshot ;
0 commit comments