3
3
4
4
using System ;
5
5
using System . Collections . Generic ;
6
+ using System . Diagnostics ;
6
7
using System . Linq ;
7
8
using System . Threading ;
8
9
using System . Threading . Tasks ;
18
19
using Microsoft . CodeAnalysis . Razor . Tooltip ;
19
20
using Microsoft . CodeAnalysis . Testing ;
20
21
using Microsoft . CodeAnalysis . Text ;
22
+ using Roslyn . Test . Utilities ;
21
23
using Xunit ;
22
24
using Xunit . Abstractions ;
23
25
using Xunit . Sdk ;
@@ -214,6 +216,68 @@ async Task FooAsync()
214
216
Assert . True ( expectedSourceText . ContentEquals ( actualSourceText ) ) ;
215
217
}
216
218
219
+ [ Fact ]
220
+ public async Task ResolveAsync_CSharp_RemapAndFormatsTextEdit_UsingDirective ( )
221
+ {
222
+ // Arrange
223
+ TestCode input =
224
+ """
225
+ @{
226
+ Task FooAsync()
227
+ {
228
+ String$$
229
+ }
230
+ }
231
+ """ ;
232
+
233
+ // Admittedly the result here is not perfect, but only because our tests don't implement the full LSP editor logic. The key thing
234
+ // is the addition of the using directive.
235
+ var expectedSourceText = SourceText . From (
236
+ """
237
+ @using System.Text
238
+ @{
239
+ Task FooAsync()
240
+ {
241
+ String
242
+ }
243
+ }
244
+ """ ) ;
245
+
246
+ var codeDocument = CreateCodeDocument ( input . Text , filePath : "C:/path/to/file.razor" ) ;
247
+ // Roslyn won't send unimported types if SupportsVisualStudioExtensions is true
248
+ await using var csharpServer = await CreateCSharpServerAsync ( codeDocument , supportsVisualStudioExtensions : false ) ;
249
+
250
+ var clientConnection = CreateClientConnectionForResolve ( csharpServer ) ;
251
+ var documentContextFactory = new TestDocumentContextFactory ( "C:/path/to/file.razor" , codeDocument ) ;
252
+ var optionsMonitor = TestRazorLSPOptionsMonitor . Create ( ) ;
253
+ var formattingService = await _lazyFormattingService . GetValueAsync ( DisposalToken ) ;
254
+ var resolver = new DelegatedCompletionItemResolver ( documentContextFactory , formattingService , DocumentMappingService , optionsMonitor , clientConnection , LoggerFactory ) ;
255
+ var containingCompletionList = await GetCompletionListAndOriginalParamsAsync ( input . Position , codeDocument , csharpServer ) ;
256
+
257
+ var sw = Stopwatch . StartNew ( ) ;
258
+ VSInternalCompletionItem item ;
259
+ while ( ( item = containingCompletionList . Items . FirstOrDefault ( item => item . Label == "StringBuilder" ) ) == null )
260
+ {
261
+ Assert . True ( sw . Elapsed < TimeSpan . FromSeconds ( 5 ) , "Failed to resolve unimported completion item after 5 second." ) ;
262
+
263
+ // Roslyn only computes unimported types in the background, and we have no access to its internal workings to wait for it to be
264
+ // finished, so we just have to delay and ask for completion items again.
265
+ await Task . Delay ( 100 , DisposalToken ) ;
266
+ containingCompletionList = await GetCompletionListAndOriginalParamsAsync ( input . Position , codeDocument , csharpServer ) ;
267
+ }
268
+
269
+ Assert . NotNull ( item ) ;
270
+
271
+ var originalRequestContext = new DelegatedCompletionResolutionContext ( _csharpCompletionParams . Identifier , _csharpCompletionParams . ProjectedKind , containingCompletionList . Data ) ;
272
+ var resolvedItem = await resolver . ResolveAsync (
273
+ item , containingCompletionList , originalRequestContext , s_clientCapabilities , _componentAvailabilityService , DisposalToken ) ;
274
+
275
+ var originalSourceText = SourceText . From ( input . Text ) ;
276
+ var textChange = originalSourceText . GetTextChange ( resolvedItem . AdditionalTextEdits . Single ( ) ) ;
277
+ var actualSourceText = originalSourceText . WithChanges ( textChange ) ;
278
+ AssertEx . EqualOrDiff ( expectedSourceText . ToString ( ) , actualSourceText . ToString ( ) ) ;
279
+ }
280
+
217
281
[ Fact ]
218
282
public async Task ResolveAsync_Html_Resolves ( )
219
283
{
@@ -246,15 +310,14 @@ private async Task<VSInternalCompletionItem> ResolveCompletionItemAsync(string c
246
310
{
247
311
TestFileMarkupParser . GetPosition ( content , out var documentContent , out var cursorPosition ) ;
248
312
var codeDocument = CreateCodeDocument ( documentContent , filePath : "C:/path/to/file.razor" ) ;
249
- await using var csharpServer = await CreateCSharpServerAsync ( codeDocument ) ;
313
+ await using var csharpServer = await CreateCSharpServerAsync ( codeDocument , supportsVisualStudioExtensions : true ) ;
250
314
251
315
var clientConnection = CreateClientConnectionForResolve ( csharpServer ) ;
252
316
var documentContextFactory = new TestDocumentContextFactory ( "C:/path/to/file.razor" , codeDocument ) ;
253
317
var optionsMonitor = TestRazorLSPOptionsMonitor . Create ( ) ;
254
318
var formattingService = await _lazyFormattingService . GetValueAsync ( DisposalToken ) ;
255
319
var resolver = new DelegatedCompletionItemResolver ( documentContextFactory , formattingService , DocumentMappingService , optionsMonitor , clientConnection , LoggerFactory ) ;
256
- var ( containingCompletionList , csharpCompletionParams ) = await GetCompletionListAndOriginalParamsAsync (
257
- cursorPosition , codeDocument , csharpServer ) ;
320
+ var containingCompletionList = await GetCompletionListAndOriginalParamsAsync ( cursorPosition , codeDocument , csharpServer ) ;
258
321
259
322
var originalRequestContext = new DelegatedCompletionResolutionContext ( _csharpCompletionParams . Identifier , _csharpCompletionParams . ProjectedKind , containingCompletionList . Data ) ;
260
323
var item = containingCompletionList . Items . FirstOrDefault ( item => item . Label == itemToResolve ) ;
@@ -270,7 +333,7 @@ private async Task<VSInternalCompletionItem> ResolveCompletionItemAsync(string c
270
333
return resolvedItem ;
271
334
}
272
335
273
- private async Task < CSharpTestLspServer > CreateCSharpServerAsync ( RazorCodeDocument codeDocument )
336
+ private async Task < CSharpTestLspServer > CreateCSharpServerAsync ( RazorCodeDocument codeDocument , bool supportsVisualStudioExtensions )
274
337
{
275
338
var csharpSourceText = codeDocument . GetCSharpSourceText ( ) ;
276
339
var csharpDocumentUri = new Uri ( "C:/path/to/file.razor__virtual.g.cs" ) ;
@@ -283,32 +346,33 @@ private async Task<CSharpTestLspServer> CreateCSharpServerAsync(RazorCodeDocumen
283
346
}
284
347
} ;
285
348
349
+ var capabilitiesUpdater = ( VSInternalClientCapabilities c ) =>
350
+ {
351
+ c . SupportsVisualStudioExtensions = supportsVisualStudioExtensions ;
352
+ } ;
353
+
286
354
// Don't declare this with an 'await using'. The caller owns the lifetime of this C# LSP server.
287
355
var csharpServer = await CSharpTestLspServerHelpers . CreateCSharpLspServerAsync (
288
- csharpSourceText , csharpDocumentUri , serverCapabilities , DisposalToken ) ;
356
+ csharpSourceText , csharpDocumentUri , serverCapabilities , capabilitiesUpdater , DisposalToken ) ;
289
357
290
358
await csharpServer . OpenDocumentAsync ( csharpDocumentUri , csharpSourceText . ToString ( ) , DisposalToken ) ;
291
359
292
360
return csharpServer ;
293
361
}
294
362
295
- private async Task < ( RazorVSInternalCompletionList , DelegatedCompletionParams ) > GetCompletionListAndOriginalParamsAsync (
363
+ private async Task < RazorVSInternalCompletionList > GetCompletionListAndOriginalParamsAsync (
296
364
int cursorPosition ,
297
365
RazorCodeDocument codeDocument ,
298
366
CSharpTestLspServer csharpServer )
299
367
{
300
368
var completionContext = new VSInternalCompletionContext ( ) { TriggerKind = CompletionTriggerKind . Invoked } ;
301
369
var documentContext = TestDocumentContext . Create ( "C:/path/to/file.razor" , codeDocument ) ;
302
370
303
- DelegatedCompletionParams ? delegatedParams = null ;
304
- var clientConnection = CreateClientConnectionForCompletion ( csharpServer , processParams : @params =>
305
- {
306
- delegatedParams = @params ;
307
- } ) ;
371
+ var clientConnection = CreateClientConnectionForCompletion ( csharpServer ) ;
308
372
309
373
var provider = CreateDelegatedCompletionListProvider ( clientConnection ) ;
310
374
311
- var completionList = await provider . GetCompletionListAsync (
375
+ return await provider . GetCompletionListAsync (
312
376
codeDocument ,
313
377
cursorPosition ,
314
378
completionContext ,
@@ -317,7 +381,5 @@ private async Task<CSharpTestLspServer> CreateCSharpServerAsync(RazorCodeDocumen
317
381
s_defaultRazorCompletionOptions ,
318
382
correlationId : Guid . Empty ,
319
383
cancellationToken : DisposalToken ) ;
320
-
321
- return ( completionList , delegatedParams ) ;
322
384
}
323
385
}
0 commit comments