@@ -4,14 +4,17 @@ open System
44open System.IO
55
66open Microsoft.CodeAnalysis
7+ open Microsoft.CodeAnalysis .Text
78open Microsoft.CodeAnalysis .FindSymbols
89open Ionide.LanguageServerProtocol .Types
910open Microsoft.Extensions .Logging
11+ open ICSharpCode.Decompiler
12+ open ICSharpCode.Decompiler .CSharp
13+ open ICSharpCode.Decompiler .CSharp .Transforms
1014
1115open CSharpLanguageServer.Util
1216open CSharpLanguageServer.Types
1317open CSharpLanguageServer.Logging
14- open CSharpLanguageServer.Roslyn .Document
1518open CSharpLanguageServer.Roslyn .Symbol
1619open CSharpLanguageServer.Roslyn .Conversions
1720
@@ -54,54 +57,101 @@ let workspaceFolderMetadataUri wf projectName containingAssemblyName symbolFullN
5457 containingAssemblyName
5558 symbolFullName
5659
57- let workspaceFolderResolveSymbolLocation
60+ let documentFromMetadata
61+ ( project : Microsoft.CodeAnalysis.Project )
62+ ( containingAssembly : Microsoft.CodeAnalysis.IAssemblySymbol )
63+ ( fullName : string )
64+ =
65+ async {
66+ let! ct = Async.CancellationToken
67+ let! compilation = project.GetCompilationAsync( ct) |> Async.AwaitTask
68+
69+ let reference =
70+ compilation.GetMetadataReference containingAssembly
71+ |> nonNull " compilation.GetMetadataReference(containingAssembly)"
72+
73+ let peReference = reference :?> PortableExecutableReference |> Option .ofObj
74+
75+ let assemblyLocation =
76+ peReference |> Option.map ( fun r -> r.FilePath) |> Option.defaultValue " ???"
77+
78+ let decompilerSettings = DecompilerSettings()
79+ decompilerSettings.ThrowOnAssemblyResolveErrors <- false // this shouldn't be a showstopper for us
80+
81+ let decompiler = CSharpDecompiler( assemblyLocation, decompilerSettings)
82+
83+ // Escape invalid identifiers to prevent Roslyn from failing to parse the generated code.
84+ // (This happens for example, when there is compiler-generated code that is not yet recognized/transformed by the decompiler.)
85+ decompiler.AstTransforms.Add( EscapeInvalidIdentifiers())
86+
87+ let fullTypeName = TypeSystem.FullTypeName fullName
88+
89+ let text = decompiler.DecompileTypeAsString fullTypeName
90+
91+ let mdDocumentFilename =
92+ $" $metadata$/projects/{project.Name}/assemblies/{containingAssembly.Name}/symbols/{fullName}.cs"
93+
94+ let mdDocumentEmpty = project.AddDocument( mdDocumentFilename, String.Empty)
95+
96+ let mdDocument = SourceText.From text |> mdDocumentEmpty.WithText
97+ return mdDocument, text
98+ }
99+
100+ let workspaceFolderWithDocumentFromMetadata
101+ wf
58102 ( project : Microsoft.CodeAnalysis.Project )
59103 ( symbol : Microsoft.CodeAnalysis.ISymbol )
60104 ( l : Microsoft.CodeAnalysis.Location )
61- ( wf : LspWorkspaceFolder )
62105 =
63106 async {
64- match l.IsInMetadata, l.IsInSource with
65- | true , _ ->
66- let! ct = Async.CancellationToken
67- let! compilation = project.GetCompilationAsync( ct) |> Async.AwaitTask
107+ let containingAssembly : IAssemblySymbol =
108+ l.MetadataModule |> nonNull " l.MetadataModule" |> _. ContainingAssembly
68109
69- let symbolGetContainingTypeOrThis ( symbol : ISymbol ) =
70- match symbol with
71- | : ? INamedTypeSymbol as namedType -> namedType
72- | _ -> symbol.ContainingType
110+ let symbolGetContainingTypeOrThis ( symbol : Microsoft.CodeAnalysis. ISymbol) =
111+ match symbol with
112+ | : ? INamedTypeSymbol as namedType -> namedType
113+ | _ -> symbol.ContainingType
73114
74- let symbolFullName =
75- symbol |> symbolGetContainingTypeOrThis |> symbolGetFullReflectionName
115+ let symbolFullName =
116+ symbol |> symbolGetContainingTypeOrThis |> symbolGetFullReflectionName
76117
77- let containingAssemblyName =
78- l.MetadataModule |> nonNull " l.MetadataModule " |> _. ContainingAssembly. Name
118+ let metadataUri =
119+ workspaceFolderMetadataUri wf project.Name containingAssembly. Name symbolFullName
79120
80- let metadataUri =
81- workspaceFolderMetadataUri wf project.Name containingAssemblyName symbolFullName
121+ match Map.tryFind metadataUri wf.DecompiledMetadata with
122+ | Some md -> return wf, metadataUri, md.Document
123+ | None ->
124+ let! documentFromMd , text = documentFromMetadata project containingAssembly symbolFullName
82125
83- let mdDocument , updatedWf =
84- match Map.tryFind metadataUri wf.DecompiledMetadata with
85- | Some value -> ( value.Document, wf)
86- | None ->
87- let ( documentFromMd , text ) =
88- documentFromMetadata compilation project l symbolFullName
126+ let csharpMetadata =
127+ { ProjectName = project.Name
128+ AssemblyName = containingAssembly.Name
129+ SymbolName = symbolFullName
130+ Source = text }
89131
90- let csharpMetadata =
91- { ProjectName = project.Name
92- AssemblyName = containingAssemblyName
93- SymbolName = symbolFullName
94- Source = text }
132+ let md =
133+ { Metadata = csharpMetadata
134+ Document = documentFromMd }
95135
96- let md =
97- { Metadata = csharpMetadata
98- Document = documentFromMd }
136+ let updatedFolder =
137+ { wf with
138+ DecompiledMetadata = Map.add metadataUri md wf.DecompiledMetadata }
99139
100- let updatedFolder =
101- { wf with
102- DecompiledMetadata = Map.add metadataUri md wf.DecompiledMetadata }
140+ return updatedFolder, metadataUri, documentFromMd
141+ }
142+
143+ let workspaceFolderResolveSymbolLocation
144+ ( project : Microsoft.CodeAnalysis.Project )
145+ ( symbol : Microsoft.CodeAnalysis.ISymbol )
146+ ( l : Microsoft.CodeAnalysis.Location )
147+ ( wf : LspWorkspaceFolder )
148+ =
149+ async {
150+ match l.IsInMetadata, l.IsInSource with
151+ | true , _ ->
152+ let! ct = Async.CancellationToken
103153
104- ( documentFromMd , updatedFolder )
154+ let! ( updatedWf , metadataUri , mdDocument ) = workspaceFolderWithDocumentFromMetadata wf project symbol l
105155
106156 // figure out location on the document (approx implementation)
107157 let! syntaxTree = mdDocument.GetSyntaxTreeAsync( ct) |> Async.AwaitTask
0 commit comments