Skip to content

Commit 82ec3b5

Browse files
Merge branch 'main' into useSimpleLambdas
2 parents f6b496e + a7fa681 commit 82ec3b5

19 files changed

+80
-1684
lines changed

eng/targets/Settings.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<NoWarn>$(NoWarn);VSIXCompatibility1001</NoWarn>
1616

1717
<!-- TODO: https://github.com/dotnet/roslyn/issues/71667 -->
18-
<NoWarn>$(NoWarn);NU1507</NoWarn>
18+
<NoWarn>$(NoWarn);NU1507;NU1510</NoWarn>
1919

2020
<CommonExtensionInstallationRoot>CommonExtensions</CommonExtensionInstallationRoot>
2121
<LanguageServicesExtensionInstallationFolder>Microsoft\VBCSharp\LanguageServices</LanguageServicesExtensionInstallationFolder>

src/EditorFeatures/Core/Classification/Semantic/EmbeddedLanguageClassificationViewTaggerProvider.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,4 @@ namespace Microsoft.CodeAnalysis.Classification;
1414
/// </summary>
1515
internal sealed class EmbeddedLanguageClassificationViewTaggerProvider(
1616
TaggerHost taggerHost, ClassificationTypeMap typeMap)
17-
: AbstractSemanticOrEmbeddedClassificationViewTaggerProvider(taggerHost, typeMap, ClassificationType.EmbeddedLanguage)
18-
{
19-
}
17+
: AbstractSemanticOrEmbeddedClassificationViewTaggerProvider(taggerHost, typeMap, ClassificationType.EmbeddedLanguage);

src/EditorFeatures/Core/Classification/Semantic/SemanticClassificationViewTaggerProvider.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,4 @@ namespace Microsoft.CodeAnalysis.Classification;
1313
/// exported. It is consumed by the <see cref="TotalClassificationTaggerProvider"/> instead.
1414
/// </summary>
1515
internal sealed class SemanticClassificationViewTaggerProvider(TaggerHost taggerHost, ClassificationTypeMap typeMap)
16-
: AbstractSemanticOrEmbeddedClassificationViewTaggerProvider(taggerHost, typeMap, ClassificationType.Semantic)
17-
{
18-
}
16+
: AbstractSemanticOrEmbeddedClassificationViewTaggerProvider(taggerHost, typeMap, ClassificationType.Semantic);

src/EditorFeatures/Core/Classification/Syntactic/SyntacticClassificationTaggerProvider.TagComputer.cs

Lines changed: 24 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@
99
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
1010
using Microsoft.CodeAnalysis.Editor.Shared.Tagging;
1111
using Microsoft.CodeAnalysis.Editor.Tagging;
12-
using Microsoft.CodeAnalysis.ErrorReporting;
1312
using Microsoft.CodeAnalysis.Host;
1413
using Microsoft.CodeAnalysis.Internal.Log;
15-
using Microsoft.CodeAnalysis.Shared.TestHooks;
1614
using Microsoft.CodeAnalysis.Text;
1715
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
1816
using 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;

src/EditorFeatures/Core/IntelliSense/ModelComputation.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,10 @@ public void ChainTaskAndNotifyControllerWhenFinished(
146146

147147
async Task<TModel> TransformModelAsync(Task<TModel> lastTask)
148148
{
149-
// Ensure we're on the BG before doing any model transformation work.
150-
await TaskScheduler.Default;
149+
// Ensure we're on the BG before doing any model transformation work. Also, ensure we yield so that we don't
150+
// end up with an enormously long chain of tasks unwinding. That can otherwise cause a stack overflow if
151+
// this chain gets too long.
152+
await Task.Yield().ConfigureAwait(false);
151153
var model = await lastTask.ConfigureAwait(false);
152154
return await transformModelAsync(model, _stopCancellationToken).ConfigureAwait(false);
153155
}

src/EditorFeatures/Core/LineSeparators/LineSeparatorTaggerProvider.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,10 @@ private void OnFormatMappingChanged(object sender, FormatItemsEventArgs e)
6565
}
6666
}
6767

68-
protected override ITaggerEventSource CreateEventSource(
69-
ITextView? textView, ITextBuffer subjectBuffer)
70-
{
71-
return TaggerEventSources.Compose(
68+
protected override ITaggerEventSource CreateEventSource(ITextView? textView, ITextBuffer subjectBuffer)
69+
=> TaggerEventSources.Compose(
7270
new EditorFormatMapChangedEventSource(_editorFormatMap),
7371
TaggerEventSources.OnTextChanged(subjectBuffer));
74-
}
7572

7673
protected override async Task ProduceTagsAsync(
7774
TaggerContext<LineSeparatorTag> context, DocumentSnapshotSpan documentSnapshotSpan, int? caretPosition, CancellationToken cancellationToken)

src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.TextChangedEventSource.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,20 @@ public TextChangedEventSource(ITextBuffer subjectBuffer)
1919
}
2020

2121
public override void Connect()
22-
=> _subjectBuffer.Changed += OnTextBufferChanged;
22+
{
23+
if (_subjectBuffer is ITextBuffer2 buffer2)
24+
buffer2.ChangedOnBackground += OnTextBufferChanged;
25+
else
26+
_subjectBuffer.Changed += OnTextBufferChanged;
27+
}
2328

2429
public override void Disconnect()
25-
=> _subjectBuffer.Changed -= OnTextBufferChanged;
30+
{
31+
if (_subjectBuffer is ITextBuffer2 buffer2)
32+
buffer2.ChangedOnBackground -= OnTextBufferChanged;
33+
else
34+
_subjectBuffer.Changed -= OnTextBufferChanged;
35+
}
2636

2737
private void OnTextBufferChanged(object? sender, TextContentChangedEventArgs e)
2838
{

src/LanguageServer/Protocol/Protocol/Internal/Converters/ImageElementConverter.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,7 @@ public override ImageElement Read(ref Utf8JsonReader reader, Type typeToConvert,
3333

3434
if (reader.TokenType == JsonTokenType.PropertyName)
3535
{
36-
var valueLength = reader.HasValueSequence ? reader.ValueSequence.Length : reader.ValueSpan.Length;
37-
38-
var propertyNameLength = valueLength <= scratchChars.Length ? reader.CopyString(scratchChars) : -1;
39-
var propertyName = propertyNameLength >= 0 ? scratchChars[..propertyNameLength] : reader.GetString().AsSpan();
36+
var propertyName = reader.GetStringSpan(scratchChars);
4037

4138
reader.Read();
4239
switch (propertyName)
@@ -48,10 +45,9 @@ public override ImageElement Read(ref Utf8JsonReader reader, Type typeToConvert,
4845
automationName = reader.GetString();
4946
break;
5047
case ObjectContentConverter.TypeProperty:
51-
var typePropertyLength = valueLength <= scratchChars.Length ? reader.CopyString(scratchChars) : -1;
52-
var typeProperty = typePropertyLength >= 0 ? scratchChars[..typePropertyLength] : reader.GetString().AsSpan();
48+
var typePropertyValue = reader.GetStringSpan(scratchChars);
5349

54-
if (!typeProperty.SequenceEqual(nameof(ImageElement).AsSpan()))
50+
if (!typePropertyValue.SequenceEqual(nameof(ImageElement).AsSpan()))
5551
throw new JsonException($"Expected {ObjectContentConverter.TypeProperty} property value {nameof(ImageElement)}");
5652
break;
5753
default:

src/LanguageServer/Protocol/Protocol/Internal/Converters/ImageIdConverter.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,7 @@ public override ImageId Read(ref Utf8JsonReader reader, Type objectType, JsonSer
3434

3535
if (reader.TokenType == JsonTokenType.PropertyName)
3636
{
37-
var valueLength = reader.HasValueSequence ? reader.ValueSequence.Length : reader.ValueSpan.Length;
38-
39-
var propertyNameLength = valueLength <= scratchChars.Length ? reader.CopyString(scratchChars) : -1;
40-
var propertyName = propertyNameLength >= 0 ? scratchChars[..propertyNameLength] : reader.GetString().AsSpan();
37+
var propertyName = reader.GetStringSpan(scratchChars);
4138

4239
reader.Read();
4340
switch (propertyName)
@@ -49,10 +46,9 @@ public override ImageId Read(ref Utf8JsonReader reader, Type objectType, JsonSer
4946
id = reader.GetInt32();
5047
break;
5148
case ObjectContentConverter.TypeProperty:
52-
var typePropertyLength = valueLength <= scratchChars.Length ? reader.CopyString(scratchChars) : -1;
53-
var typeProperty = typePropertyLength >= 0 ? scratchChars[..typePropertyLength] : reader.GetString().AsSpan();
49+
var typePropertyValue = reader.GetStringSpan(scratchChars);
5450

55-
if (!typeProperty.SequenceEqual(nameof(ImageId).AsSpan()))
51+
if (!typePropertyValue.SequenceEqual(nameof(ImageId).AsSpan()))
5652
throw new JsonException($"Expected {ObjectContentConverter.TypeProperty} property value {nameof(ImageId)}");
5753
break;
5854
default:

0 commit comments

Comments
 (0)