Skip to content

Commit 7edae94

Browse files
Remove usage of WaitAndGetResult from debugger paths (#76483)
2 parents 2f0aa43 + 554e3d9 commit 7edae94

File tree

5 files changed

+141
-165
lines changed

5 files changed

+141
-165
lines changed

src/VisualStudio/Core/Def/Implementation/AbstractOleCommandTarget.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Diagnostics.CodeAnalysis;
66
using System.Runtime.InteropServices;
77
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
8+
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
89
using Microsoft.VisualStudio.ComponentModelHost;
910
using Microsoft.VisualStudio.Editor;
1011
using Microsoft.VisualStudio.OLE.Interop;
@@ -35,6 +36,7 @@ public AbstractOleCommandTarget(
3536
}
3637

3738
public IComponentModel ComponentModel { get; }
39+
protected IThreadingContext ThreadingContext => ComponentModel.GetService<IThreadingContext>();
3840

3941
public IVsEditorAdaptersFactoryService EditorAdaptersFactory
4042
{

src/VisualStudio/Core/Def/Implementation/AbstractVsTextViewFilter.cs

Lines changed: 72 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System;
88
using System.Linq;
99
using System.Threading;
10+
using System.Threading.Tasks;
1011
using Microsoft.CodeAnalysis.BraceMatching;
1112
using Microsoft.CodeAnalysis.Debugging;
1213
using Microsoft.CodeAnalysis.Editor;
@@ -29,156 +30,141 @@
2930

3031
namespace Microsoft.VisualStudio.LanguageServices.Implementation;
3132

32-
internal abstract class AbstractVsTextViewFilter : AbstractOleCommandTarget, IVsTextViewFilter
33+
internal abstract class AbstractVsTextViewFilter(
34+
IWpfTextView wpfTextView,
35+
IComponentModel componentModel) : AbstractOleCommandTarget(wpfTextView, componentModel), IVsTextViewFilter
3336
{
34-
public AbstractVsTextViewFilter(
35-
IWpfTextView wpfTextView,
36-
IComponentModel componentModel)
37-
: base(wpfTextView, componentModel)
37+
int IVsTextViewFilter.GetDataTipText(TextSpan[] pSpan, out string pbstrText)
3838
{
39+
(pbstrText, var result) = this.ThreadingContext.JoinableTaskFactory.Run(() => GetDataTipTextAsync(pSpan));
40+
return result;
3941
}
4042

41-
int IVsTextViewFilter.GetDataTipText(TextSpan[] pSpan, out string pbstrText)
43+
private async Task<(string pbstrText, int result)> GetDataTipTextAsync(TextSpan[] pSpan)
4244
{
4345
try
4446
{
4547
if (pSpan == null || pSpan.Length != 1)
46-
{
47-
pbstrText = null;
48-
return VSConstants.E_INVALIDARG;
49-
}
48+
return (null, VSConstants.E_INVALIDARG);
5049

51-
return GetDataTipTextImpl(pSpan, out pbstrText);
50+
return await GetDataTipTextImplAsync(pSpan).ConfigureAwait(true);
5251
}
5352
catch (Exception e) when (FatalError.ReportAndCatch(e) && false)
5453
{
5554
throw ExceptionUtilities.Unreachable();
5655
}
5756
}
5857

59-
protected virtual int GetDataTipTextImpl(TextSpan[] pSpan, out string pbstrText)
58+
protected virtual async Task<(string pbstrText, int result)> GetDataTipTextImplAsync(TextSpan[] pSpan)
6059
{
6160
var subjectBuffer = WpfTextView.GetBufferContainingCaret();
6261
if (subjectBuffer == null)
63-
{
64-
pbstrText = null;
65-
return VSConstants.E_FAIL;
66-
}
62+
return (null, VSConstants.E_FAIL);
6763

68-
return GetDataTipTextImpl(subjectBuffer, pSpan, out pbstrText);
64+
return await GetDataTipTextImplAsync(subjectBuffer, pSpan).ConfigureAwait(true);
6965
}
7066

71-
protected int GetDataTipTextImpl(ITextBuffer subjectBuffer, TextSpan[] pSpan, out string pbstrText)
67+
protected async Task<(string pbstrText, int result)> GetDataTipTextImplAsync(ITextBuffer subjectBuffer, TextSpan[] pSpan)
7268
{
73-
pbstrText = null;
74-
7569
var vsBuffer = EditorAdaptersFactory.GetBufferAdapter(subjectBuffer);
7670

7771
// TODO: broken in REPL
7872
if (vsBuffer == null)
79-
{
80-
return VSConstants.E_FAIL;
81-
}
73+
return (null, VSConstants.E_FAIL);
8274

8375
using (Logger.LogBlock(FunctionId.Debugging_VsLanguageDebugInfo_GetDataTipText, CancellationToken.None))
8476
{
85-
pbstrText = null;
8677
if (pSpan == null || pSpan.Length != 1)
87-
{
88-
return VSConstants.E_INVALIDARG;
89-
}
78+
return (null, VSConstants.E_INVALIDARG);
9079

9180
var result = VSConstants.E_FAIL;
92-
string pbstrTextInternal = null;
81+
string pbstrText = null;
9382

9483
var uiThreadOperationExecutor = ComponentModel.GetService<IUIThreadOperationExecutor>();
95-
uiThreadOperationExecutor.Execute(
84+
using var context = uiThreadOperationExecutor.BeginExecute(
9685
title: ServicesVSResources.Debugger,
9786
defaultDescription: ServicesVSResources.Getting_DataTip_text,
9887
allowCancellation: true,
99-
showProgress: false,
100-
action: context =>
101-
{
102-
IServiceProvider serviceProvider = ComponentModel.GetService<SVsServiceProvider>();
103-
var debugger = (IVsDebugger)serviceProvider.GetService(typeof(SVsShellDebugger));
104-
var debugMode = new DBGMODE[1];
88+
showProgress: false);
10589

106-
var cancellationToken = context.UserCancellationToken;
107-
if (ErrorHandler.Succeeded(debugger.GetMode(debugMode)) && debugMode[0] != DBGMODE.DBGMODE_Design)
108-
{
109-
var textSpan = pSpan[0];
90+
IServiceProvider serviceProvider = ComponentModel.GetService<SVsServiceProvider>();
91+
var debugger = (IVsDebugger)serviceProvider.GetService(typeof(SVsShellDebugger));
92+
var debugMode = new DBGMODE[1];
93+
94+
var cancellationToken = context.UserCancellationToken;
95+
if (ErrorHandler.Succeeded(debugger.GetMode(debugMode)) && debugMode[0] != DBGMODE.DBGMODE_Design)
96+
{
97+
var textSpan = pSpan[0];
11098

111-
var textSnapshot = subjectBuffer.CurrentSnapshot;
112-
var document = textSnapshot.GetOpenDocumentInCurrentContextWithChanges();
99+
var textSnapshot = subjectBuffer.CurrentSnapshot;
100+
var document = textSnapshot.GetOpenDocumentInCurrentContextWithChanges();
113101

114-
if (document != null)
102+
if (document != null)
103+
{
104+
var languageDebugInfo = document.Project.Services.GetService<ILanguageDebugInfoService>();
105+
if (languageDebugInfo != null)
115106
{
116-
var languageDebugInfo = document.Project.Services.GetService<ILanguageDebugInfoService>();
117-
if (languageDebugInfo != null)
107+
var spanOpt = textSnapshot.TryGetSpan(textSpan);
108+
if (spanOpt.HasValue)
118109
{
119-
var spanOpt = textSnapshot.TryGetSpan(textSpan);
120-
if (spanOpt.HasValue)
110+
// 'kind' is an lsp-only concept, so we don't want/need to include it here (especially
111+
// as it can be expensive to compute, and we don't want to block the UI thread).
112+
var dataTipInfo = await languageDebugInfo.GetDataTipInfoAsync(
113+
document, spanOpt.Value.Start, includeKind: false, cancellationToken).ConfigureAwait(true);
114+
if (!dataTipInfo.IsDefault)
121115
{
122-
// 'kind' is an lsp-only concept, so we don't want/need to include it here (especially
123-
// as it can be expensive to compute, and we don't want to block the UI thread).
124-
var dataTipInfo = languageDebugInfo.GetDataTipInfoAsync(
125-
document, spanOpt.Value.Start, includeKind: false, cancellationToken).WaitAndGetResult(cancellationToken);
126-
if (!dataTipInfo.IsDefault)
127-
{
128-
var resultSpan = dataTipInfo.Span.ToSnapshotSpan(textSnapshot);
129-
var textOpt = dataTipInfo.Text;
116+
var resultSpan = dataTipInfo.Span.ToSnapshotSpan(textSnapshot);
117+
var textOpt = dataTipInfo.Text;
130118

131-
pSpan[0] = resultSpan.ToVsTextSpan();
132-
result = debugger.GetDataTipValue((IVsTextLines)vsBuffer, pSpan, textOpt, out pbstrTextInternal);
133-
}
119+
pSpan[0] = resultSpan.ToVsTextSpan();
120+
result = debugger.GetDataTipValue((IVsTextLines)vsBuffer, pSpan, textOpt, out pbstrText);
134121
}
135122
}
136123
}
137124
}
138-
});
125+
}
139126

140-
pbstrText = pbstrTextInternal;
141-
return result;
127+
return (pbstrText, result);
142128
}
143129
}
144130

145131
int IVsTextViewFilter.GetPairExtents(int iLine, int iIndex, TextSpan[] pSpan)
146132
{
147-
try
148-
{
149-
var result = VSConstants.S_OK;
150-
ComponentModel.GetService<IUIThreadOperationExecutor>().Execute(
151-
"Intellisense",
152-
defaultDescription: "",
153-
allowCancellation: true,
154-
showProgress: false,
155-
action: c => result = GetPairExtentsWorker(iLine, iIndex, pSpan, c.UserCancellationToken));
156-
157-
return result;
158-
}
159-
catch (Exception e) when (FatalError.ReportAndCatch(e) && false)
160-
{
161-
throw ExceptionUtilities.Unreachable();
162-
}
133+
return this.ThreadingContext.JoinableTaskFactory.Run(() => GetPairExtentsAsync(iLine, iIndex, pSpan));
163134
}
164135

165-
private int GetPairExtentsWorker(int iLine, int iIndex, TextSpan[] pSpan, CancellationToken cancellationToken)
136+
private async Task<int> GetPairExtentsAsync(int iLine, int iIndex, TextSpan[] pSpan)
166137
{
138+
using var waitContext = ComponentModel.GetService<IUIThreadOperationExecutor>().BeginExecute(
139+
"Intellisense",
140+
defaultDescription: "",
141+
allowCancellation: true,
142+
showProgress: false);
143+
167144
var braceMatcher = ComponentModel.GetService<IBraceMatchingService>();
168145
var globalOptions = ComponentModel.GetService<IGlobalOptionService>();
169-
return GetPairExtentsWorker(
146+
147+
return await GetPairExtentsAsync(
170148
WpfTextView,
171149
braceMatcher,
172150
globalOptions,
173151
iLine,
174152
iIndex,
175153
pSpan,
176154
(VSConstants.VSStd2KCmdID)this.CurrentlyExecutingCommand == VSConstants.VSStd2KCmdID.GOTOBRACE_EXT,
177-
cancellationToken);
155+
waitContext.UserCancellationToken).ConfigureAwait(true);
178156
}
179157

180158
// Internal for testing purposes
181-
internal static int GetPairExtentsWorker(ITextView textView, IBraceMatchingService braceMatcher, IGlobalOptionService globalOptions, int iLine, int iIndex, TextSpan[] pSpan, bool extendSelection, CancellationToken cancellationToken)
159+
internal static async Task<int> GetPairExtentsAsync(
160+
ITextView textView,
161+
IBraceMatchingService braceMatcher,
162+
IGlobalOptionService globalOptions,
163+
int iLine,
164+
int iIndex,
165+
TextSpan[] pSpan,
166+
bool extendSelection,
167+
CancellationToken cancellationToken)
182168
{
183169
pSpan[0].iStartLine = pSpan[0].iEndLine = iLine;
184170
pSpan[0].iStartIndex = pSpan[0].iEndIndex = iIndex;
@@ -202,7 +188,8 @@ internal static int GetPairExtentsWorker(ITextView textView, IBraceMatchingServi
202188
if (document != null)
203189
{
204190
var options = globalOptions.GetBraceMatchingOptions(document.Project.Language);
205-
var matchingSpan = braceMatcher.FindMatchingSpanAsync(document, position, options, cancellationToken).WaitAndGetResult(cancellationToken);
191+
var matchingSpan = await braceMatcher.FindMatchingSpanAsync(
192+
document, position, options, cancellationToken).ConfigureAwait(true);
206193

207194
if (matchingSpan.HasValue)
208195
{
@@ -236,7 +223,8 @@ internal static int GetPairExtentsWorker(ITextView textView, IBraceMatchingServi
236223
if (extendSelection)
237224
{
238225
// case a.
239-
var closingSpans = braceMatcher.FindMatchingSpanAsync(document, matchingSpan.Value.Start, options, cancellationToken).WaitAndGetResult(cancellationToken);
226+
var closingSpans = await braceMatcher.FindMatchingSpanAsync(
227+
document, matchingSpan.Value.Start, options, cancellationToken).ConfigureAwait(true);
240228
var vsClosingSpans = textView.GetSpanInView(closingSpans.Value.ToSnapshotSpan(subjectBuffer.CurrentSnapshot)).First().ToVsTextSpan();
241229
pSpan[0].iEndIndex = vsClosingSpans.iStartIndex;
242230
}
@@ -255,7 +243,8 @@ internal static int GetPairExtentsWorker(ITextView textView, IBraceMatchingServi
255243
pSpan[0].iEndIndex = vsTextSpan.iStartIndex;
256244

257245
// case b.
258-
var openingSpans = braceMatcher.FindMatchingSpanAsync(document, matchingSpan.Value.End, options, cancellationToken).WaitAndGetResult(cancellationToken);
246+
var openingSpans = await braceMatcher.FindMatchingSpanAsync(
247+
document, matchingSpan.Value.End, options, cancellationToken).ConfigureAwait(true);
259248
var vsOpeningSpans = textView.GetSpanInView(openingSpans.Value.ToSnapshotSpan(subjectBuffer.CurrentSnapshot)).First().ToVsTextSpan();
260249
pSpan[0].iStartIndex = vsOpeningSpans.iStartIndex;
261250
}

src/VisualStudio/Core/Def/Implementation/StandaloneCommandFilter.cs

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,5 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation;
1010
/// <summary>
1111
/// A CommandFilter used for "normal" files, as opposed to Venus files which are special.
1212
/// </summary>
13-
internal sealed class StandaloneCommandFilter : AbstractVsTextViewFilter
14-
{
15-
/// <summary>
16-
/// Creates a new command handler that is attached to an IVsTextView.
17-
/// </summary>
18-
/// <param name="wpfTextView">The IWpfTextView of the view.</param>
19-
internal StandaloneCommandFilter(
20-
IWpfTextView wpfTextView,
21-
IComponentModel componentModel)
22-
: base(wpfTextView, componentModel)
23-
{
24-
}
25-
}
13+
internal sealed class StandaloneCommandFilter(
14+
IWpfTextView wpfTextView, IComponentModel componentModel) : AbstractVsTextViewFilter(wpfTextView, componentModel);

src/VisualStudio/Core/Def/Venus/VenusCommandFilter.cs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
using System.Diagnostics;
88
using System.Linq;
9+
using System.Threading.Tasks;
910
using Microsoft.VisualStudio.ComponentModelHost;
1011
using Microsoft.VisualStudio.Editor;
1112
using Microsoft.VisualStudio.LanguageServices.Implementation.Extensions;
@@ -43,14 +44,13 @@ public VenusCommandFilter(
4344
protected override ITextBuffer GetSubjectBufferContainingCaret()
4445
=> _subjectBuffer;
4546

46-
protected override int GetDataTipTextImpl(TextSpan[] pSpan, out string pbstrText)
47+
protected override async Task<(string pbstrText, int result)> GetDataTipTextImplAsync(TextSpan[] pSpan)
4748
{
4849
var textViewModel = WpfTextView.TextViewModel;
4950
if (textViewModel == null)
5051
{
5152
Debug.Assert(WpfTextView.IsClosed);
52-
pbstrText = null;
53-
return VSConstants.E_FAIL;
53+
return (null, VSConstants.E_FAIL);
5454
}
5555

5656
// We need to map the TextSpan from the DataBuffer to our subject buffer.
@@ -72,7 +72,7 @@ protected override int GetDataTipTextImpl(TextSpan[] pSpan, out string pbstrText
7272
// Next, we'll check to see if there is actually a DataTip for this candidate.
7373
// If there is, we'll map this span back to the DataBuffer and return it.
7474
var subjectBufferSpanData = new TextSpan[] { candidateSpan.ToVsTextSpan() };
75-
var hr = GetDataTipTextImpl(_subjectBuffer, subjectBufferSpanData, out pbstrText);
75+
var (pbstrText, hr) = await GetDataTipTextImplAsync(_subjectBuffer, subjectBufferSpanData).ConfigureAwait(true);
7676
if (ErrorHandler.Succeeded(hr))
7777
{
7878
var subjectSpan = _subjectBuffer.CurrentSnapshot.GetSpan(subjectBufferSpanData[0]);
@@ -85,18 +85,14 @@ protected override int GetDataTipTextImpl(TextSpan[] pSpan, out string pbstrText
8585
.SingleOrDefault(x => x.IntersectsWith(span));
8686

8787
if (surfaceSpan == default)
88-
{
89-
pbstrText = null;
90-
return VSConstants.E_FAIL;
91-
}
88+
return (null, VSConstants.E_FAIL);
9289

9390
// pSpan is an in/out parameter
9491
pSpan[0] = surfaceSpan.ToVsTextSpan();
95-
return hr;
92+
return (pbstrText, hr);
9693
}
9794
}
9895

99-
pbstrText = null;
100-
return VSConstants.E_FAIL;
96+
return (null, VSConstants.E_FAIL);
10197
}
10298
}

0 commit comments

Comments
 (0)