Add GoToDefinition for XAML LSP#55296
Conversation
|
@mgoertz-msft @dibarbet Could you please help to take a look? In reply to: 890167800 |
| { | ||
| var locations = ArrayBuilder<LSP.Location>.GetInstance(); | ||
|
|
||
| if (definition is XamlSourceDefinition sourceDefinition) |
There was a problem hiding this comment.
would recommend breaking up this method a little bit so it's easier to read, e.g. have methods to handle each kind of definition (HandleSourceDefinition, HandleSymbolDefinition
|
|
||
| public override async Task<LSP.Location[]> HandleRequestAsync(TextDocumentPositionParams request, RequestContext context, CancellationToken cancellationToken) | ||
| { | ||
| var locations = ArrayBuilder<LSP.Location>.GetInstance(); |
There was a problem hiding this comment.
can use arrabuilder like this - https://sourceroslyn.io/#Microsoft.CodeAnalysis.LanguageServer.Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs,64 and then just to ToArray() on it instead of ToArrayAndFree()
|
|
||
| private async Task<LSP.Location[]> GetLocationsAsync(XamlDefinition definition, RequestContext context, CancellationToken cancellationToken) | ||
| { | ||
| var locations = ArrayBuilder<LSP.Location>.GetInstance(); |
There was a problem hiding this comment.
same comment as above on ArrayBuilder usage
| } | ||
|
|
||
| var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); | ||
| var xamlGoToDefinitionService = document.Project.LanguageServices.GetService<IXamlGoToDefinitionService>(); |
There was a problem hiding this comment.
Looks like it could move down to before line 65 #Closed
|
|
||
| var definitions = await xamlGoToDefinitionService.GetDefinitionsAsync(document, position, cancellationToken).ConfigureAwait(false); | ||
| foreach (var definition in definitions) | ||
| { |
There was a problem hiding this comment.
Optimize with Parallel.ForEach or Task.WhenAll? #Closed
There was a problem hiding this comment.
I switched to use Task.WhenAll, and I also changed the locations to ConcurrentBag type so it is thread safe
| // Cannot find the file in solution. This is probably a file lives outside of the solution like generic.xaml | ||
| // which lives in the Windows SDK folder. Try getting the SourceText from the file path. | ||
| using var fileStream = new FileStream(sourceDefinition.FilePath, FileMode.Open, FileAccess.Read); | ||
| var sourceText = SourceText.From(fileStream); |
There was a problem hiding this comment.
What's the best way to handle error conditions here? For example if the file doesn't exist or cannot be opened for some reason.
There was a problem hiding this comment.
Just tried out giving it an atbitrary file path and let it throw. Don't see any error diaglog/indicator from the UI. I am thinking about let it throw if anything wrong with opening the document so we can get the exception from Telemetry late, but yeah the downside of it would be the remaining code will not execute either (if there are more definitions). @dibarbet how does csharp handle partial success for go to definition?
There was a problem hiding this comment.
wouldn't this be a full failure - you have no location to go to? Depending on the kind of failure we either throw or just return no definitions.
If you do not expect to hit an exception, I would leave it as throwing so you can capture telemetry on it. If an exception here is normal and not unexpected, then it should probably be handled. Throwing here is OK, the request queue should handle the exception and report a nfw since it isn't a mutating request.
| internal sealed class XamlSourceDefinition : XamlDefinition | ||
| { | ||
| private readonly TextSpan? _span; | ||
| private readonly int _line, _character; |
| Contract.ThrowIfNull(symbolDefinition.Symbol); | ||
| var symbol = symbolDefinition.Symbol; | ||
|
|
||
| if (symbol.Locations.First().IsInMetadata && _metadataAsSourceFileService != null) |
There was a problem hiding this comment.
@dibarbet IS the first location always the definition or is there a better way to get the location for a symbol definition?
There was a problem hiding this comment.
Yeah we usually use first. that being said this is what we do in non-LSP and we probably need to update our handler to do something similar
https://sourceroslyn.io/#Microsoft.VisualStudio.LanguageServices/Implementation/Workspace/VisualStudioSymbolNavigationService.cs,67
| Contract.ThrowIfNull(symbolDefinition.Symbol); | ||
| var symbol = symbolDefinition.Symbol; | ||
|
|
||
| if (symbol.Locations.First().IsInMetadata && _metadataAsSourceFileService != null) |
| _span = span; | ||
| } | ||
|
|
||
| public XamlSourceDefinition(string filePath, int line, int character) |
| // Convert the line column to TextSpan | ||
| if (_line < text.Lines.Count) | ||
| { | ||
| var character = Math.Min(_column, text.Lines[_line].Span.Length); |
| { | ||
| var task = Task.Run(async () => | ||
| { | ||
| foreach(var location in await this.GetLocationsAsync(definition, context, cancellationToken).ConfigureAwait(false)) |
| } | ||
| else | ||
| { | ||
| throw new InvalidOperationException("Unexpected XamlDefinition Type"); |

This PR enables GoToDefinition for LSP XAML. The GoToDefinitionHandler uses IXamlGoToDefinitionService, which will be implemented in XAML language service to get the definitions of the given document and position. There are two types of XamlDefinition we are getting from the XAML language service: