11namespace CSharpLanguageServer.Handlers
22
33open System
4+ open System.Text
5+ open System.IO
46
7+ open Microsoft.Extensions .Logging
58open Microsoft.CodeAnalysis .Text
69open Ionide.LanguageServerProtocol .Types
710open Ionide.LanguageServerProtocol .JsonRpc
@@ -21,7 +24,6 @@ module TextDocumentSync =
2124 ( changes : TextDocumentContentChangeEvent [])
2225 ( initialSourceText : SourceText )
2326 =
24-
2527 let applyLspContentChangeOnRoslynSourceText ( sourceText : SourceText ) ( change : TextDocumentContentChangeEvent ) =
2628 match change with
2729 | U2.C1 change ->
@@ -42,74 +44,160 @@ module TextDocumentSync =
4244 Change = Some TextDocumentSyncKind.Incremental }
4345 |> Some
4446
45-
4647 let didOpen ( context : ServerRequestContext ) ( openParams : DidOpenTextDocumentParams ) : Async < LspResult < unit >> =
47- match context.GetDocumentForUriOfType AnyDocument openParams.TextDocument.Uri with
48- | Some( doc, docType) ->
49- match docType with
50- | UserDocument ->
51- // we want to load the document in case it has been changed since we have the solution loaded
52- // also, as a bonus we can recover from corrupted document view in case document in roslyn solution
53- // went out of sync with editor
54- let updatedDoc = SourceText.From( openParams.TextDocument.Text) |> doc.WithText
48+ if openParams.TextDocument.Uri.EndsWith( " .cshtml" ) then
49+ let u = openParams.TextDocument.Uri |> string
50+ let uri = Uri( u.Replace( " %3A " , " :" , true , null ))
51+
52+ let matchingAdditionalDoc =
53+ context.Solution.Projects
54+ |> Seq.collect ( fun p -> p.AdditionalDocuments)
55+ |> Seq.filter ( fun d -> Uri( d.FilePath, UriKind.Absolute) = uri)
56+ |> List.ofSeq
57+
58+ let doc =
59+ if matchingAdditionalDoc.Length = 1 then
60+ matchingAdditionalDoc |> Seq.head |> Some
61+ else
62+ None
63+
64+ let newSourceText = SourceText.From( openParams.TextDocument.Text, Encoding.UTF8)
65+
66+ match doc with
67+ | Some doc ->
68+ let updatedDoc =
69+ doc.Project
70+ |> _. RemoveAdditionalDocument( doc.Id)
71+ |> _. AddAdditionalDocument( doc.Name, newSourceText, doc.Folders, doc.FilePath)
5572
5673 context.Emit( OpenDocAdd( openParams.TextDocument.Uri, openParams.TextDocument.Version, DateTime.Now))
5774 context.Emit( SolutionChange updatedDoc.Project.Solution)
5875
59- Ok() |> async.Return
76+ | None ->
77+ let cshtmlPath = Uri.toPath openParams.TextDocument.Uri
78+ let project = getProjectForPathOnSolution context.Solution cshtmlPath
6079
61- | _ -> Ok() |> async.Return
80+ match project with
81+ | Some project ->
82+ let projectBaseDir = Path.GetDirectoryName( project.FilePath)
83+ let relativePath = Path.GetRelativePath( projectBaseDir, cshtmlPath)
6284
63- | None ->
64- let docFilePathMaybe = Util.tryParseFileUri openParams.TextDocument.Uri
85+ let folders = relativePath.Split( Path.DirectorySeparatorChar)
6586
66- match docFilePathMaybe with
67- | Some docFilePath -> async {
68- // ok, this document is not in solution, register a new document
69- let! newDocMaybe = tryAddDocument logger docFilePath openParams.TextDocument.Text context.Solution
87+ let folders = folders |> Seq.take ( folders.Length - 1 )
88+
89+ let newDoc =
90+ project.AddAdditionalDocument ( Path.GetFileName ( cshtmlPath ), newSourceText , folders , cshtmlPath )
7091
71- match newDocMaybe with
72- | Some newDoc ->
7392 context.Emit( OpenDocAdd( openParams.TextDocument.Uri, openParams.TextDocument.Version, DateTime.Now))
7493 context.Emit( SolutionChange newDoc.Project.Solution)
94+ ()
7595
7696 | None -> ()
7797
78- return Ok()
79- }
98+ Ok() |> async.Return
99+ else
100+ match context.GetDocumentForUriOfType AnyDocument openParams.TextDocument.Uri with
101+ | Some( doc, docType) ->
102+ match docType with
103+ | UserDocument ->
104+ // we want to load the document in case it has been changed since we have the solution loaded
105+ // also, as a bonus we can recover from corrupted document view in case document in roslyn solution
106+ // went out of sync with editor
107+ let updatedDoc = SourceText.From( openParams.TextDocument.Text) |> doc.WithText
108+
109+ context.Emit( OpenDocAdd( openParams.TextDocument.Uri, openParams.TextDocument.Version, DateTime.Now))
110+ context.Emit( SolutionChange updatedDoc.Project.Solution)
111+
112+ Ok() |> async.Return
113+
114+ | _ -> Ok() |> async.Return
115+
116+ | None ->
117+ let docFilePathMaybe = Util.tryParseFileUri openParams.TextDocument.Uri
118+
119+ match docFilePathMaybe with
120+ | Some docFilePath -> async {
121+ // ok, this document is not in solution, register a new document
122+ let! newDocMaybe = tryAddDocument logger docFilePath openParams.TextDocument.Text context.Solution
123+
124+ match newDocMaybe with
125+ | Some newDoc ->
126+ context.Emit(
127+ OpenDocAdd( openParams.TextDocument.Uri, openParams.TextDocument.Version, DateTime.Now)
128+ )
129+
130+ context.Emit( SolutionChange newDoc.Project.Solution)
131+
132+ | None -> ()
80133
81- | None -> Ok() |> async.Return
134+ return Ok()
135+ }
136+ | None -> Ok() |> async.Return
82137
83138 let didChange ( context : ServerRequestContext ) ( changeParams : DidChangeTextDocumentParams ) : Async < LspResult < unit >> = async {
84- let docMaybe = context.GetUserDocument changeParams.TextDocument.Uri
139+ if changeParams.TextDocument.Uri.EndsWith( " .cshtml" ) then
140+ let u = changeParams.TextDocument.Uri |> string
141+ let uri = Uri( u.Replace( " %3A " , " :" , true , null ))
142+
143+ let matchingAdditionalDoc =
144+ context.Solution.Projects
145+ |> Seq.collect ( fun p -> p.AdditionalDocuments)
146+ |> Seq.filter ( fun d -> Uri( d.FilePath, UriKind.Absolute) = uri)
147+ |> List.ofSeq
148+
149+ let doc =
150+ if matchingAdditionalDoc.Length = 1 then
151+ matchingAdditionalDoc |> Seq.head |> Some
152+ else
153+ None
154+
155+ match doc with
156+ | None -> ()
157+ | Some doc ->
158+ let! ct = Async.CancellationToken
159+ let! sourceText = doc.GetTextAsync( ct) |> Async.AwaitTask
160+
161+ let updatedSourceText =
162+ sourceText
163+ |> applyLspContentChangesOnRoslynSourceText changeParams.ContentChanges
85164
86- match docMaybe with
87- | None -> ()
88- | Some doc ->
89- let! ct = Async.CancellationToken
90- let! sourceText = doc.GetTextAsync ( ct ) |> Async.AwaitTask
91- //logMessage (sprintf "TextDocumentDidChange: changeParams: %s" (string changeParams))
92- //logMessage (sprintf "TextDocumentDidChange: sourceText: %s" (string sourceText) )
165+ let updatedDoc =
166+ doc.Project
167+ |> _. RemoveAdditionalDocument ( doc.Id )
168+ |> _. AddAdditionalDocument ( doc.Name , updatedSourceText , doc.Folders , doc.FilePath )
169+
170+ context.Emit ( OpenDocAdd ( changeParams.TextDocument.Uri , changeParams.TextDocument.Version , DateTime.Now ))
171+ context.Emit ( SolutionChange updatedDoc.Project.Solution )
93172
94- let updatedSourceText =
95- sourceText
96- |> applyLspContentChangesOnRoslynSourceText changeParams.ContentChanges
173+ return Ok ()
174+ else
175+ let docMaybe = context.GetUserDocument changeParams.TextDocument.Uri
97176
98- let updatedDoc = doc.WithText( updatedSourceText)
177+ match docMaybe with
178+ | None -> ()
179+ | Some doc ->
180+ let! ct = Async.CancellationToken
181+ let! sourceText = doc.GetTextAsync( ct) |> Async.AwaitTask
99182
100- //logMessage (sprintf "TextDocumentDidChange: newSourceText: %s" (string updatedSourceText))
183+ let updatedSourceText =
184+ sourceText
185+ |> applyLspContentChangesOnRoslynSourceText changeParams.ContentChanges
101186
102- let updatedSolution = updatedDoc .Project.Solution
187+ let updatedSolution = doc.WithText ( updatedSourceText ) . Project.Solution
103188
104- context.Emit( SolutionChange updatedSolution)
105- context.Emit( OpenDocAdd( changeParams.TextDocument.Uri, changeParams.TextDocument.Version, DateTime.Now))
189+ context.Emit( SolutionChange updatedSolution)
190+ context.Emit( OpenDocAdd( changeParams.TextDocument.Uri, changeParams.TextDocument.Version, DateTime.Now))
106191
107- return Ok()
192+ return Ok()
108193 }
109194
110195 let didClose ( context : ServerRequestContext ) ( closeParams : DidCloseTextDocumentParams ) : Async < LspResult < unit >> =
111- context.Emit( OpenDocRemove closeParams.TextDocument.Uri)
112- Ok() |> async.Return
196+ if closeParams.TextDocument.Uri.EndsWith( " .cshtml" ) then
197+ Ok() |> async.Return
198+ else
199+ context.Emit( OpenDocRemove closeParams.TextDocument.Uri)
200+ Ok() |> async.Return
113201
114202 let willSave ( _context : ServerRequestContext ) ( _p : WillSaveTextDocumentParams ) : Async < LspResult < unit >> = async {
115203 return Ok()
@@ -122,22 +210,25 @@ module TextDocumentSync =
122210 async { return LspResult.notImplemented< TextEdit[] option> }
123211
124212 let didSave ( context : ServerRequestContext ) ( saveParams : DidSaveTextDocumentParams ) : Async < LspResult < unit >> =
125- // we need to add this file to solution if not already
126- let doc = context.GetDocument saveParams.TextDocument.Uri
213+ if saveParams.TextDocument.Uri.EndsWith( " .cshtml" ) then
214+ Ok() |> async.Return
215+ else
216+ // we need to add this file to solution if not already
217+ let doc = context.GetDocument saveParams.TextDocument.Uri
127218
128- match doc with
129- | Some _ -> Ok() |> async.Return
219+ match doc with
220+ | Some _ -> Ok() |> async.Return
130221
131- | None -> async {
132- let docFilePath = Util.parseFileUri saveParams.TextDocument.Uri
133- let! newDocMaybe = tryAddDocument logger docFilePath saveParams.Text.Value context.Solution
222+ | None -> async {
223+ let docFilePath = Util.parseFileUri saveParams.TextDocument.Uri
224+ let! newDocMaybe = tryAddDocument logger docFilePath saveParams.Text.Value context.Solution
134225
135- match newDocMaybe with
136- | Some newDoc ->
137- context.Emit( OpenDocTouch( saveParams.TextDocument.Uri, DateTime.Now))
138- context.Emit( SolutionChange newDoc.Project.Solution)
226+ match newDocMaybe with
227+ | Some newDoc ->
228+ context.Emit( OpenDocTouch( saveParams.TextDocument.Uri, DateTime.Now))
229+ context.Emit( SolutionChange newDoc.Project.Solution)
139230
140- | None -> ()
231+ | None -> ()
141232
142- return Ok()
143- }
233+ return Ok()
234+ }
0 commit comments