Skip to content

Commit b9cbd94

Browse files
committed
chore(Lsp.Workspace): import `documentFromMetadata' from Roslyn.Document
1 parent b284204 commit b9cbd94

File tree

2 files changed

+85
-87
lines changed

2 files changed

+85
-87
lines changed

src/CSharpLanguageServer/Lsp/Workspace.fs

Lines changed: 85 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@ open System
44
open System.IO
55

66
open Microsoft.CodeAnalysis
7+
open Microsoft.CodeAnalysis.Text
78
open Microsoft.CodeAnalysis.FindSymbols
89
open Ionide.LanguageServerProtocol.Types
910
open Microsoft.Extensions.Logging
11+
open ICSharpCode.Decompiler
12+
open ICSharpCode.Decompiler.CSharp
13+
open ICSharpCode.Decompiler.CSharp.Transforms
1014

1115
open CSharpLanguageServer.Util
1216
open CSharpLanguageServer.Types
1317
open CSharpLanguageServer.Logging
14-
open CSharpLanguageServer.Roslyn.Document
1518
open CSharpLanguageServer.Roslyn.Symbol
1619
open 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

src/CSharpLanguageServer/Roslyn/Document.fs

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,11 @@
11
module CSharpLanguageServer.Roslyn.Document
22

3-
open System
4-
53
open Microsoft.CodeAnalysis
64
open Microsoft.CodeAnalysis.CSharp.Formatting
75
open Microsoft.CodeAnalysis.Options
86
open Microsoft.CodeAnalysis.Text
97
open Microsoft.CodeAnalysis.Formatting
108
open Ionide.LanguageServerProtocol.Types
11-
open ICSharpCode.Decompiler
12-
open ICSharpCode.Decompiler.CSharp
13-
open ICSharpCode.Decompiler.CSharp.Transforms
14-
15-
open CSharpLanguageServer.Types
16-
open CSharpLanguageServer.Util
179

1810

1911
let private processChange (oldText: SourceText) (change: TextChange) : TextEdit =
@@ -106,47 +98,3 @@ let getDocumentFormattingOptionSet (doc: Document) (lspFormattingOptions: Format
10698
_.WithChangedOption(CSharpFormattingOptions.NewLineForFinally, not trimFinalNewlines)
10799
| None -> id
108100
}
109-
110-
111-
let documentFromMetadata
112-
(compilation: Microsoft.CodeAnalysis.Compilation)
113-
(project: Microsoft.CodeAnalysis.Project)
114-
(l: Microsoft.CodeAnalysis.Location)
115-
(fullName: string)
116-
=
117-
let mdLocation = l
118-
119-
let containingAssembly =
120-
mdLocation.MetadataModule
121-
|> nonNull "mdLocation.MetadataModule"
122-
|> _.ContainingAssembly
123-
124-
let reference =
125-
compilation.GetMetadataReference containingAssembly
126-
|> nonNull "compilation.GetMetadataReference(containingAssembly)"
127-
128-
let peReference = reference :?> PortableExecutableReference |> Option.ofObj
129-
130-
let assemblyLocation =
131-
peReference |> Option.map (fun r -> r.FilePath) |> Option.defaultValue "???"
132-
133-
let decompilerSettings = DecompilerSettings()
134-
decompilerSettings.ThrowOnAssemblyResolveErrors <- false // this shouldn't be a showstopper for us
135-
136-
let decompiler = CSharpDecompiler(assemblyLocation, decompilerSettings)
137-
138-
// Escape invalid identifiers to prevent Roslyn from failing to parse the generated code.
139-
// (This happens for example, when there is compiler-generated code that is not yet recognized/transformed by the decompiler.)
140-
decompiler.AstTransforms.Add(EscapeInvalidIdentifiers())
141-
142-
let fullTypeName = TypeSystem.FullTypeName fullName
143-
144-
let text = decompiler.DecompileTypeAsString fullTypeName
145-
146-
let mdDocumentFilename =
147-
$"$metadata$/projects/{project.Name}/assemblies/{containingAssembly.Name}/symbols/{fullName}.cs"
148-
149-
let mdDocumentEmpty = project.AddDocument(mdDocumentFilename, String.Empty)
150-
151-
let mdDocument = SourceText.From text |> mdDocumentEmpty.WithText
152-
mdDocument, text

0 commit comments

Comments
 (0)