Skip to content

Commit c58230f

Browse files
committed
squash
uh
1 parent 45620bd commit c58230f

File tree

41 files changed

+994
-186
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+994
-186
lines changed

src/CSharpLanguageServer/CSharpLanguageServer.fsproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
<PackageReadmeFile>README.md</PackageReadmeFile>
1818
<ChangelogFile>CHANGELOG.md</ChangelogFile>
1919
<Nullable>enable</Nullable>
20-
<MSBuildTreatWarningsAsErrors>true</MSBuildTreatWarningsAsErrors>
2120
</PropertyGroup>
2221

2322
<ItemGroup>

src/CSharpLanguageServer/Conversions.fs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ module Location =
8888
|> Option.bind (fun filePath -> if File.Exists filePath then Some filePath else None)
8989
|> Option.map (fun filePath -> toLspLocation filePath (loc.GetLineSpan().Span))
9090

91+
//Console.Error.WriteLine("loc={0}; mapped={1}; source={2}", loc, mappedSourceLocation, sourceLocation)
92+
9193
mappedSourceLocation |> Option.orElse sourceLocation
9294

9395
| _ -> None

src/CSharpLanguageServer/Handlers/Completion.fs

Lines changed: 146 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,23 @@ namespace CSharpLanguageServer.Handlers
33
open System
44
open System.Reflection
55

6+
open Microsoft.CodeAnalysis
7+
open Microsoft.CodeAnalysis.Text
68
open Microsoft.Extensions.Caching.Memory
79
open Ionide.LanguageServerProtocol.Server
810
open Ionide.LanguageServerProtocol.Types
911
open Ionide.LanguageServerProtocol.JsonRpc
12+
open Microsoft.Extensions.Logging
1013

1114
open CSharpLanguageServer.State
1215
open CSharpLanguageServer.Util
1316
open CSharpLanguageServer.Conversions
1417
open CSharpLanguageServer.Logging
18+
open CSharpLanguageServer.RoslynHelpers
1519

1620
[<RequireQualifiedAccess>]
1721
module Completion =
18-
let private _logger = Logging.getLoggerByName "Completion"
22+
let private logger = Logging.getLoggerByName "Completion"
1923

2024
let private completionItemMemoryCache = new MemoryCache(new MemoryCacheOptions())
2125

@@ -180,13 +184,121 @@ module Completion =
180184
synopsis, documentationText
181185
| _, _ -> None, None
182186

183-
let handle
187+
let getCompletionsForRazorDocument
188+
(solution: Solution)
189+
(p: CompletionParams)
190+
: Async<option<Microsoft.CodeAnalysis.Completion.CompletionList * Document>> =
191+
async {
192+
match! getRazorDocumentForUri solution p.TextDocument.Uri with
193+
| None -> return None
194+
| Some(project, compilation, cshtmlPath, cshtmlTree) ->
195+
let! ct = Async.CancellationToken
196+
let! sourceText = cshtmlTree.GetTextAsync() |> Async.AwaitTask
197+
198+
let razorTextDocument =
199+
solution.Projects
200+
|> Seq.collect (fun p -> p.AdditionalDocuments)
201+
|> Seq.filter (fun d -> Uri(d.FilePath, UriKind.Absolute) = Uri p.TextDocument.Uri)
202+
|> Seq.head
203+
204+
let! razorSourceText = razorTextDocument.GetTextAsync() |> Async.AwaitTask
205+
206+
//logger.LogInformation("razorSourceText={0}", razorSourceText)
207+
208+
//logger.LogInformation("doc={0}", sourceText)
209+
210+
let posInCshtml = Position.toRoslynPosition sourceText.Lines p.Position
211+
//logger.LogInformation("posInCshtml={posInCshtml=}", posInCshtml)
212+
let pos = p.Position
213+
214+
let root = cshtmlTree.GetRoot()
215+
216+
let mutable position: int option = None
217+
let mutable tokenForPosition: SyntaxToken option = None
218+
let mutable debug: string option = None
219+
220+
for t in root.DescendantTokens() do
221+
let cshtmlSpan = cshtmlTree.GetMappedLineSpan(t.Span)
222+
223+
if
224+
cshtmlSpan.StartLinePosition.Line = (int pos.Line)
225+
&& cshtmlSpan.EndLinePosition.Line = (int pos.Line)
226+
&& cshtmlSpan.StartLinePosition.Character <= (int pos.Character)
227+
then
228+
let tokenStartCharacterOffset =
229+
(int pos.Character - cshtmlSpan.StartLinePosition.Character)
230+
231+
position <- Some(t.Span.Start + tokenStartCharacterOffset)
232+
233+
debug <-
234+
Some(
235+
String.Format(
236+
"token={0}; pos.Character={1}; cshtmlSpan.StartLinePosition.Character={2}; offset={3}",
237+
t,
238+
pos.Character,
239+
cshtmlSpan.StartLinePosition.Character,
240+
tokenStartCharacterOffset
241+
)
242+
)
243+
244+
tokenForPosition <- Some(t)
245+
246+
//logger.LogInformation(debug |> Option.defaultValue "")
247+
248+
//let position = Position.toRoslynPosition sourceText.Lines translatedPosition
249+
//logger.LogInformation("position in .cs={position}", position)
250+
251+
let posInCS = sourceText.Lines.GetLinePosition(position.Value)
252+
//logger.LogInformation("lineposition={x}", posInCS)
253+
254+
// a hack to make <span>@Model.|</span> autocompletion to work:
255+
// - force a dot if present on .cscshtml but missing on .cs
256+
let newSourceText =
257+
// TODO: check if the text in cshtml is '.', though!
258+
let cshtmlPosition = Position.toRoslynPosition razorSourceText.Lines p.Position
259+
let charInCshtml: char = razorSourceText[cshtmlPosition - 1]
260+
261+
//logger.LogInformation("charInCshtml={0}", charInCshtml)
262+
263+
if charInCshtml = '.' && string tokenForPosition <> "." then
264+
sourceText.WithChanges(new TextChange(new TextSpan(position.Value - 1, 0), "."))
265+
else
266+
sourceText
267+
268+
//logger.LogInformation("newSourceText={0}", newSourceText)
269+
270+
let! doc = tryAddDocument logger (cshtmlPath + ".cs") (newSourceText.ToString()) solution
271+
272+
let doc = doc.Value
273+
274+
//logger.LogError("handle: doc={doc}", doc)
275+
276+
let completionService =
277+
Microsoft.CodeAnalysis.Completion.CompletionService.GetService(doc)
278+
|> RoslynCompletionServiceWrapper
279+
280+
let completionOptions =
281+
RoslynCompletionOptions.Default()
282+
|> _.WithBool("ShowItemsFromUnimportedNamespaces", false)
283+
|> _.WithBool("ShowNameSuggestions", false)
284+
285+
let completionTrigger = CompletionContext.toCompletionTrigger p.Context
286+
287+
let! roslynCompletions =
288+
completionService.GetCompletionsAsync(doc, position.Value, completionOptions, completionTrigger, ct)
289+
|> Async.map Option.ofObj
290+
291+
return roslynCompletions |> Option.map (fun rcl -> rcl, doc)
292+
}
293+
294+
let getCompletionsForCSharpDocument
184295
(context: ServerRequestContext)
185296
(p: CompletionParams)
186-
: Async<LspResult<U2<CompletionItem array, CompletionList> option>> =
297+
: Async<option<Microsoft.CodeAnalysis.Completion.CompletionList * Document>> =
187298
async {
188299
match context.GetDocument p.TextDocument.Uri with
189-
| None -> return None |> LspResult.success
300+
| None -> return None
301+
190302
| Some doc ->
191303
let! ct = Async.CancellationToken
192304
let! sourceText = doc.GetTextAsync(ct) |> Async.AwaitTask
@@ -216,6 +328,23 @@ module Completion =
216328
else
217329
async.Return None
218330

331+
return roslynCompletions |> Option.map (fun rcl -> rcl, doc)
332+
}
333+
334+
let handle
335+
(context: ServerRequestContext)
336+
(p: CompletionParams)
337+
: Async<LspResult<U2<CompletionItem array, CompletionList> option>> =
338+
async {
339+
let! roslynCompletionsAndDoc =
340+
if p.TextDocument.Uri.EndsWith(".cshtml") then
341+
getCompletionsForRazorDocument context.Solution p
342+
else
343+
getCompletionsForCSharpDocument context p
344+
345+
match roslynCompletionsAndDoc with
346+
| None -> return None |> LspResult.success
347+
| Some(roslynCompletions, doc) ->
219348
let toLspCompletionItemsWithCacheInfo (completions: Microsoft.CodeAnalysis.Completion.CompletionList) =
220349
completions.ItemsList
221350
|> Seq.map (fun item -> (item, Guid.NewGuid() |> string))
@@ -232,33 +361,35 @@ module Completion =
232361
|> Array.ofSeq
233362

234363
let lspCompletionItemsWithCacheInfo =
235-
roslynCompletions |> Option.map toLspCompletionItemsWithCacheInfo
364+
roslynCompletions |> toLspCompletionItemsWithCacheInfo
236365

237366
// cache roslyn completion items
238-
for (_, cacheItemId, roslynDoc, roslynItem) in
239-
(lspCompletionItemsWithCacheInfo |> Option.defaultValue Array.empty) do
367+
for (_, cacheItemId, roslynDoc, roslynItem) in lspCompletionItemsWithCacheInfo do
240368
completionItemMemoryCacheSet cacheItemId roslynDoc roslynItem
241369

370+
let items =
371+
lspCompletionItemsWithCacheInfo |> Array.map (fun (item, _, _, _) -> item)
372+
242373
return
243-
lspCompletionItemsWithCacheInfo
244-
|> Option.map (fun itemsWithCacheInfo ->
245-
itemsWithCacheInfo |> Array.map (fun (item, _, _, _) -> item))
246-
|> Option.map (fun items ->
247-
{ IsIncomplete = true
248-
Items = items
249-
ItemDefaults = None })
250-
|> Option.map U2.C2
374+
{ IsIncomplete = true
375+
Items = items
376+
ItemDefaults = None }
377+
|> U2.C2
378+
|> Some
251379
|> LspResult.success
252380
}
253381

254382
let resolve (_context: ServerRequestContext) (item: CompletionItem) : AsyncLspResult<CompletionItem> = async {
383+
255384
let roslynDocAndItemMaybe =
256385
item.Data
257386
|> Option.bind deserialize<string option>
258387
|> Option.bind completionItemMemoryCacheGet
259388

260389
match roslynDocAndItemMaybe with
261390
| Some(doc, roslynCompletionItem) ->
391+
logger.LogInformation("resolve, doc={0}, item={1}", doc, roslynCompletionItem)
392+
262393
let completionService =
263394
Microsoft.CodeAnalysis.Completion.CompletionService.GetService(doc)
264395
|> nonNull "Microsoft.CodeAnalysis.Completion.CompletionService.GetService(doc)"

0 commit comments

Comments
 (0)