|
13 | 13 | using Microsoft.CodeAnalysis.Internal.Log; |
14 | 14 | using Microsoft.CodeAnalysis.Text; |
15 | 15 | using Microsoft.CodeAnalysis.Text.Shared.Extensions; |
| 16 | +using Microsoft.Internal.VisualStudio.Shell.Interop; |
16 | 17 | using Microsoft.VisualStudio.LanguageServices.Implementation.Extensions; |
17 | 18 | using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; |
18 | 19 | using Microsoft.VisualStudio.Utilities; |
@@ -70,63 +71,52 @@ public int GetLocationOfName(string pszName, out string? pbstrMkDoc, out VsTextS |
70 | 71 |
|
71 | 72 | public int GetNameOfLocation(IVsTextBuffer pBuffer, int iLine, int iCol, out string? pbstrName, out int piLineOffset) |
72 | 73 | { |
73 | | - using (Logger.LogBlock(FunctionId.Debugging_VsLanguageDebugInfo_GetNameOfLocation, CancellationToken.None)) |
74 | | - { |
75 | | - string? name = null; |
76 | | - var lineOffset = 0; |
| 74 | + (pbstrName, piLineOffset) = GetNameOfLocationWorker(); |
77 | 75 |
|
78 | | - if (_languageDebugInfo != null) |
79 | | - { |
80 | | - _uiThreadOperationExecutor.Execute( |
81 | | - title: ServicesVSResources.Debugger, |
82 | | - defaultDescription: ServicesVSResources.Determining_breakpoint_location, |
83 | | - allowCancellation: true, |
84 | | - showProgress: false, |
85 | | - action: waitContext => |
86 | | - { |
87 | | - var cancellationToken = waitContext.UserCancellationToken; |
88 | | - var textBuffer = _languageService.EditorAdaptersFactoryService.GetDataBuffer(pBuffer); |
89 | | - if (textBuffer != null) |
90 | | - { |
91 | | - var nullablePoint = textBuffer.CurrentSnapshot.TryGetPoint(iLine, iCol); |
92 | | - if (nullablePoint.HasValue) |
93 | | - { |
94 | | - var point = nullablePoint.Value; |
95 | | - var document = point.Snapshot.GetOpenDocumentInCurrentContextWithChanges(); |
96 | | - |
97 | | - if (document != null) |
98 | | - { |
99 | | - // NOTE(cyrusn): We have to wait here because the debuggers' |
100 | | - // GetNameOfLocation is a blocking call. In the future, it |
101 | | - // would be nice if they could make it async. |
102 | | - this.ThreadingContext.JoinableTaskFactory.Run(async () => |
103 | | - { |
104 | | - var debugLocationInfo = await _languageDebugInfo.GetLocationInfoAsync(document, point, cancellationToken).ConfigureAwait(false); |
105 | | - |
106 | | - if (!debugLocationInfo.IsDefault) |
107 | | - { |
108 | | - name = debugLocationInfo.Name; |
109 | | - lineOffset = debugLocationInfo.LineOffset; |
110 | | - } |
111 | | - }); |
112 | | - } |
113 | | - } |
114 | | - } |
115 | | - }); |
| 76 | + // Note(DustinCa): Docs say that GetNameOfLocation should return S_FALSE if a name could not be found. |
| 77 | + // Also, that's what the old native code does, so we should do it here. |
| 78 | + return pbstrName != null ? VSConstants.S_OK : VSConstants.S_FALSE; |
116 | 79 |
|
117 | | - if (name != null) |
| 80 | + (string name, int lineOffset) GetNameOfLocationWorker() |
| 81 | + { |
| 82 | + return this.ThreadingContext.JoinableTaskFactory.Run(async () => |
| 83 | + { |
| 84 | + using (Logger.LogBlock(FunctionId.Debugging_VsLanguageDebugInfo_GetNameOfLocation, CancellationToken.None)) |
118 | 85 | { |
119 | | - pbstrName = name; |
120 | | - piLineOffset = lineOffset; |
121 | | - return VSConstants.S_OK; |
122 | | - } |
123 | | - } |
| 86 | + if (_languageDebugInfo == null) |
| 87 | + return default; |
124 | 88 |
|
125 | | - // Note(DustinCa): Docs say that GetNameOfLocation should return S_FALSE if a name could not be found. |
126 | | - // Also, that's what the old native code does, so we should do it here. |
127 | | - pbstrName = null; |
128 | | - piLineOffset = 0; |
129 | | - return VSConstants.S_FALSE; |
| 89 | + using var waitContext = _uiThreadOperationExecutor.BeginExecute( |
| 90 | + title: ServicesVSResources.Debugger, |
| 91 | + defaultDescription: ServicesVSResources.Determining_breakpoint_location, |
| 92 | + allowCancellation: true, |
| 93 | + showProgress: false); |
| 94 | + |
| 95 | + var cancellationToken = waitContext.UserCancellationToken; |
| 96 | + var textBuffer = _languageService.EditorAdaptersFactoryService.GetDataBuffer(pBuffer); |
| 97 | + if (textBuffer == null) |
| 98 | + return default; |
| 99 | + |
| 100 | + var nullablePoint = textBuffer.CurrentSnapshot.TryGetPoint(iLine, iCol); |
| 101 | + if (!nullablePoint.HasValue) |
| 102 | + return default; |
| 103 | + |
| 104 | + var point = nullablePoint.Value; |
| 105 | + var document = point.Snapshot.GetOpenDocumentInCurrentContextWithChanges(); |
| 106 | + if (document == null) |
| 107 | + return default; |
| 108 | + |
| 109 | + // NOTE(cyrusn): We have to wait here because the debuggers' |
| 110 | + // GetNameOfLocation is a blocking call. In the future, it |
| 111 | + // would be nice if they could make it async. |
| 112 | + var debugLocationInfo = await _languageDebugInfo.GetLocationInfoAsync(document, point, cancellationToken).ConfigureAwait(false); |
| 113 | + |
| 114 | + if (debugLocationInfo.IsDefault) |
| 115 | + return default; |
| 116 | + |
| 117 | + return (debugLocationInfo.Name, debugLocationInfo.LineOffset); |
| 118 | + } |
| 119 | + }); |
130 | 120 | } |
131 | 121 | } |
132 | 122 |
|
@@ -178,51 +168,49 @@ public int IsMappedLocation(IVsTextBuffer pBuffer, int iLine, int iCol) |
178 | 168 |
|
179 | 169 | public int ResolveName(string pszName, uint dwFlags, out IVsEnumDebugName? ppNames) |
180 | 170 | { |
181 | | - using (Logger.LogBlock(FunctionId.Debugging_VsLanguageDebugInfo_ResolveName, CancellationToken.None)) |
| 171 | + // In VS, this method frequently get's called with an empty string to test if the language service |
| 172 | + // supports this method (some language services, like F#, implement IVsLanguageDebugInfo but don't |
| 173 | + // implement this method). In that scenario, there's no sense doing work, so we'll just return |
| 174 | + // S_FALSE (as the old VB language service did). |
| 175 | + if (string.IsNullOrEmpty(pszName)) |
182 | 176 | { |
183 | | - // In VS, this method frequently get's called with an empty string to test if the language service |
184 | | - // supports this method (some language services, like F#, implement IVsLanguageDebugInfo but don't |
185 | | - // implement this method). In that scenario, there's no sense doing work, so we'll just return |
186 | | - // S_FALSE (as the old VB language service did). |
187 | | - if (string.IsNullOrEmpty(pszName)) |
188 | | - { |
189 | | - ppNames = null; |
190 | | - return VSConstants.S_FALSE; |
191 | | - } |
| 177 | + ppNames = null; |
| 178 | + return VSConstants.S_FALSE; |
| 179 | + } |
192 | 180 |
|
193 | | - VsEnumDebugName? enumName = null; |
194 | | - _uiThreadOperationExecutor.Execute( |
195 | | - title: ServicesVSResources.Debugger, |
196 | | - defaultDescription: ServicesVSResources.Resolving_breakpoint_location, |
197 | | - allowCancellation: true, |
198 | | - showProgress: false, |
199 | | - action: waitContext => |
| 181 | + ppNames = this.ThreadingContext.JoinableTaskFactory.Run(async () => |
| 182 | + { |
| 183 | + using (Logger.LogBlock(FunctionId.Debugging_VsLanguageDebugInfo_ResolveName, CancellationToken.None)) |
200 | 184 | { |
201 | | - this.ThreadingContext.JoinableTaskFactory.Run(async () => |
| 185 | + using var waitContext = _uiThreadOperationExecutor.BeginExecute( |
| 186 | + title: ServicesVSResources.Debugger, |
| 187 | + defaultDescription: ServicesVSResources.Resolving_breakpoint_location, |
| 188 | + allowCancellation: true, |
| 189 | + showProgress: false); |
| 190 | + |
| 191 | + var cancellationToken = waitContext.UserCancellationToken; |
| 192 | + if (dwFlags == (uint)RESOLVENAMEFLAGS.RNF_BREAKPOINT) |
202 | 193 | { |
203 | | - var cancellationToken = waitContext.UserCancellationToken; |
204 | | - if (dwFlags == (uint)RESOLVENAMEFLAGS.RNF_BREAKPOINT) |
205 | | - { |
206 | | - var solution = _languageService.Workspace.CurrentSolution; |
| 194 | + var solution = _languageService.Workspace.CurrentSolution; |
207 | 195 |
|
208 | | - // NOTE(cyrusn): We have to wait here because the debuggers' ResolveName |
209 | | - // call is synchronous. In the future it would be nice to make it async. |
210 | | - if (_breakpointService != null) |
211 | | - { |
212 | | - var breakpoints = await _breakpointService.ResolveBreakpointsAsync( |
213 | | - solution, pszName, cancellationToken).ConfigureAwait(false); |
214 | | - var debugNames = await breakpoints.SelectAsArrayAsync( |
215 | | - bp => CreateDebugNameAsync(bp, cancellationToken)).ConfigureAwait(false); |
| 196 | + // NOTE(cyrusn): We have to wait here because the debuggers' ResolveName |
| 197 | + // call is synchronous. In the future it would be nice to make it async. |
| 198 | + if (_breakpointService != null) |
| 199 | + { |
| 200 | + var breakpoints = await _breakpointService.ResolveBreakpointsAsync( |
| 201 | + solution, pszName, cancellationToken).ConfigureAwait(false); |
| 202 | + var debugNames = await breakpoints.SelectAsArrayAsync( |
| 203 | + bp => CreateDebugNameAsync(bp, cancellationToken)).ConfigureAwait(false); |
216 | 204 |
|
217 | | - enumName = new VsEnumDebugName(debugNames); |
218 | | - } |
| 205 | + return new VsEnumDebugName(debugNames); |
219 | 206 | } |
220 | | - }); |
221 | | - }); |
| 207 | + } |
| 208 | + } |
222 | 209 |
|
223 | | - ppNames = enumName; |
224 | | - return ppNames != null ? VSConstants.S_OK : VSConstants.E_NOTIMPL; |
225 | | - } |
| 210 | + return null; |
| 211 | + }); |
| 212 | + |
| 213 | + return ppNames != null ? VSConstants.S_OK : VSConstants.E_NOTIMPL; |
226 | 214 | } |
227 | 215 |
|
228 | 216 | private async ValueTask<IVsDebugName> CreateDebugNameAsync( |
|
0 commit comments