public GotoDefinitionResponse GetGotoDefinitionResponse(GotoDefinitionRequest request) { var res = _bufferParser.ParsedContent(request.Buffer, request.FileName); var loc = new TextLocation(request.Line, request.Column); ResolveResult resolveResult = ResolveAtLocation.Resolve(res.Compilation, res.UnresolvedFile, res.SyntaxTree, loc); var response = new GotoDefinitionResponse(); if (resolveResult != null) { var region = resolveResult.GetDefinitionRegion(); response.FileName = region.FileName == null ? null : region.FileName.LowerCaseDriveLetter().ApplyPathReplacementsForClient(); response.Line = region.BeginLine; response.Column = region.BeginColumn; } return response; }
public async Task ReturnsDefinitionInMetadata_FromMetadata_WhenSymbolIsType(string filename) { var testFile = new TestFile(filename, @" using System; class Bar { public void Baz() { var number = in$$t.MaxValue; } }"); using (var host = CreateOmniSharpHost(testFile)) { var point = testFile.Content.GetPointFromPosition(); // 1. start by asking for definition of "int" var gotoDefinitionRequest = new GotoDefinitionRequest { FileName = testFile.FileName, Line = point.Line, Column = point.Offset, WantMetadata = true, Timeout = 60000 }; var gotoDefinitionRequestHandler = GetRequestHandler(host); var gotoDefinitionResponse = await gotoDefinitionRequestHandler.Handle(gotoDefinitionRequest); // 2. now, based on the response information // go to the metadata endpoint, and ask for "int" specific metadata var metadataRequest = new MetadataRequest { AssemblyName = gotoDefinitionResponse.MetadataSource.AssemblyName, TypeName = gotoDefinitionResponse.MetadataSource.TypeName, ProjectName = gotoDefinitionResponse.MetadataSource.ProjectName, Language = gotoDefinitionResponse.MetadataSource.Language }; var metadataRequestHandler = host.GetRequestHandler <MetadataService>(OmniSharpEndpoints.Metadata); var metadataResponse = await metadataRequestHandler.Handle(metadataRequest); // 3. the metadata response contains SourceName (metadata "file") and SourceText (syntax tree) // use the source to locate "IComparable" which is an interface implemented by Int32 struct var metadataTree = CSharpSyntaxTree.ParseText(metadataResponse.Source); var iComparable = metadataTree.GetCompilationUnitRoot(). DescendantNodesAndSelf(). OfType <BaseTypeDeclarationSyntax>().First(). BaseList.Types.FirstOrDefault(x => x.Type.ToString() == "IComparable"); var relevantLineSpan = iComparable.GetLocation().GetLineSpan(); // 4. now ask for the definition of "IComparable" // pass in the SourceName (metadata "file") as FileName - since it's not a regular file in our workspace var metadataNavigationRequest = new GotoDefinitionRequest { FileName = metadataResponse.SourceName, Line = relevantLineSpan.StartLinePosition.Line, Column = relevantLineSpan.StartLinePosition.Character, WantMetadata = true }; var metadataNavigationResponse = await gotoDefinitionRequestHandler.Handle(metadataNavigationRequest); // 5. validate the response to be matching the expected IComparable meta info Assert.NotNull(metadataNavigationResponse.MetadataSource); Assert.Equal(AssemblyHelpers.CorLibName, metadataNavigationResponse.MetadataSource.AssemblyName); Assert.Equal("System.IComparable", metadataNavigationResponse.MetadataSource.TypeName); Assert.NotEqual(0, metadataNavigationResponse.Line); Assert.NotEqual(0, metadataNavigationResponse.Column); } }
public async Task ReturnsDecompiledDefinition_FromMetadata_WhenSymbolIsType(string filename) { var testFile = new TestFile(filename, @" using System; class Bar { public void Baz() { var number = in$$t.MaxValue; } }"); using var host = CreateOmniSharpHost(new[] { testFile }, new Dictionary <string, string> { ["RoslynExtensionsOptions:EnableDecompilationSupport"] = "true" }); var point = testFile.Content.GetPointFromPosition(); // 1. start by asking for definition of "int" var gotoDefinitionRequest = new GotoDefinitionRequest { FileName = testFile.FileName, Line = point.Line, Column = point.Offset, WantMetadata = true, Timeout = 60000 }; var gotoDefinitionRequestHandler = GetRequestHandler(host); var gotoDefinitionResponse = await gotoDefinitionRequestHandler.Handle(gotoDefinitionRequest); // 2. now, based on the response information // go to the metadata endpoint, and ask for "int" specific decompiled source var metadataRequest = new MetadataRequest { AssemblyName = gotoDefinitionResponse.MetadataSource.AssemblyName, TypeName = gotoDefinitionResponse.MetadataSource.TypeName, ProjectName = gotoDefinitionResponse.MetadataSource.ProjectName, Language = gotoDefinitionResponse.MetadataSource.Language, Timeout = 60000 }; var metadataRequestHandler = host.GetRequestHandler <MetadataService>(OmniSharpEndpoints.Metadata); var metadataResponse = await metadataRequestHandler.Handle(metadataRequest); // 3. the response contains SourceName ("file") and SourceText (syntax tree) // use the source to locate "IComparable" which is an interface implemented by Int32 struct var decompiledTree = CSharpSyntaxTree.ParseText(metadataResponse.Source); var compilationUnit = decompiledTree.GetCompilationUnitRoot(); // second comment should indicate we have decompiled var comments = compilationUnit.DescendantTrivia().Where(t => t.Kind() == SyntaxKind.SingleLineCommentTrivia).ToArray(); Assert.NotNull(comments); Assert.Equal("// Decompiled with ICSharpCode.Decompiler 5.0.2.5153", comments[1].ToString()); // contrary to regular metadata, we should have methods with full bodies // this condition would fail if decompilation wouldn't work var methods = compilationUnit. DescendantNodesAndSelf(). OfType <MethodDeclarationSyntax>(). Where(m => m.Body != null); Assert.NotEmpty(methods); var iComparable = compilationUnit. DescendantNodesAndSelf(). OfType <BaseTypeDeclarationSyntax>().First(). BaseList.Types.FirstOrDefault(x => x.Type.ToString() == "IComparable"); var relevantLineSpan = iComparable.GetLocation().GetLineSpan(); // 4. now ask for the definition of "IComparable" // pass in the SourceName (metadata "file") as FileName - since it's not a regular file in our workspace var metadataNavigationRequest = new GotoDefinitionRequest { FileName = metadataResponse.SourceName, Line = relevantLineSpan.StartLinePosition.Line, Column = relevantLineSpan.StartLinePosition.Character, WantMetadata = true }; var metadataNavigationResponse = await gotoDefinitionRequestHandler.Handle(metadataNavigationRequest); // 5. validate the response to be matching the expected IComparable meta info Assert.NotNull(metadataNavigationResponse.MetadataSource); Assert.Equal(AssemblyHelpers.CorLibName, metadataNavigationResponse.MetadataSource.AssemblyName); Assert.Equal("System.IComparable", metadataNavigationResponse.MetadataSource.TypeName); Assert.NotEqual(0, metadataNavigationResponse.Line); Assert.NotEqual(0, metadataNavigationResponse.Column); }
public async Task UpdateReturnsChanges() { const string Code = @" _ = GeneratedCode.$$S; _ = ""Hello world!"""; const string Path = @"Test.cs"; var reference = new TestGeneratorReference(context => { // NOTE: Don't actually do this in a real generator. This is just for test // code. Do not use this as an example of what to do in production. var syntax = context.Compilation.SyntaxTrees.Single().GetRoot().DescendantNodes().OfType <LiteralExpressionSyntax>().SingleOrDefault(); if (syntax != null) { context.AddSource("GeneratedSource", @" class GeneratedCode { public static string S = " + syntax.ToString() + @"; }"); } }); TestFile testFile = new TestFile(Path, Code); TestHelpers.AddProjectToWorkspace(SharedOmniSharpTestHost.Workspace, "project.csproj", new[] { "netcoreapp3.1" }, new[] { testFile }, ImmutableArray.Create <AnalyzerReference>(reference)); var point = testFile.Content.GetPointFromPosition(); var gotoDefRequest = new GotoDefinitionRequest { FileName = Path, Line = point.Line, Column = point.Offset }; var gotoDefResponse = (await SharedOmniSharpTestHost .GetRequestHandler <GotoDefinitionServiceV2>(OmniSharpEndpoints.V2.GotoDefinition) .Handle(gotoDefRequest)).Definitions.Single(); var initialContent = await SharedOmniSharpTestHost .GetRequestHandler <SourceGeneratedFileService>(OmniSharpEndpoints.SourceGeneratedFile) .Handle(new SourceGeneratedFileRequest() { ProjectGuid = gotoDefResponse.SourceGeneratedFileInfo.ProjectGuid, DocumentGuid = gotoDefResponse.SourceGeneratedFileInfo.DocumentGuid }); Assert.Contains("Hello world!", initialContent.Source); var updateRequest = new UpdateSourceGeneratedFileRequest { DocumentGuid = gotoDefResponse.SourceGeneratedFileInfo.DocumentGuid, ProjectGuid = gotoDefResponse.SourceGeneratedFileInfo.ProjectGuid }; var updateHandler = SharedOmniSharpTestHost.GetRequestHandler <SourceGeneratedFileService>(OmniSharpEndpoints.UpdateSourceGeneratedFile); var updatedResponse = await updateHandler.Handle(updateRequest); Assert.Null(updatedResponse.Source); Assert.Equal(UpdateType.Unchanged, updatedResponse.UpdateType); var updateBufferHandler = SharedOmniSharpTestHost.GetRequestHandler <UpdateBufferService>(OmniSharpEndpoints.UpdateBuffer); _ = await updateBufferHandler.Handle(new() { FileName = Path, Buffer = Code.Replace("Hello world!", "Goodbye!") }); updatedResponse = await updateHandler.Handle(updateRequest); Assert.Equal(UpdateType.Modified, updatedResponse.UpdateType); Assert.Contains("Goodbye!", updatedResponse.Source); Assert.DoesNotContain("Hello world!", updatedResponse.Source); _ = await updateBufferHandler.Handle(new() { FileName = Path, Buffer = @"_ = GeneratedCode.S;" }); updatedResponse = await updateHandler.Handle(updateRequest); Assert.Equal(UpdateType.Deleted, updatedResponse.UpdateType); Assert.Null(updatedResponse.Source); }