11namespace CSharpLanguageServer.Handlers
22
33open System
4+ open System.Reflection
45
56open FSharpPlus
67open Ionide.LanguageServerProtocol .Server
@@ -12,9 +13,57 @@ open CSharpLanguageServer.State
1213open CSharpLanguageServer.Util
1314open CSharpLanguageServer.Conversions
1415open CSharpLanguageServer.Types
16+ open CSharpLanguageServer.Logging
1517
1618[<RequireQualifiedAccess>]
1719module Completion =
20+ let private logger = LogProvider.getLoggerByName " Completion"
21+
22+ let emptyRoslynOptionSet : Microsoft.CodeAnalysis.Options.OptionSet =
23+ let osType = typeof< Microsoft.CodeAnalysis.Options.OptionSet>
24+ let osEmptyOptionSetField = osType.GetField( " Empty" , BindingFlags.Static||| BindingFlags.NonPublic)
25+ osEmptyOptionSetField.GetValue( null ) :?> Microsoft.CodeAnalysis.Options.OptionSet
26+
27+ /// the type reflects on internal class Microsoft.CodeAnalysis.Completion.CompletionOptions
28+ /// see https://github.com/dotnet/roslyn/blob/main/src/Features/Core/Portable/Completion/CompletionOptions.cs
29+ type RoslynCompletionOptions =
30+ {
31+ Object: obj
32+ CompletionOptionsType: Type
33+ }
34+ with
35+ member rco.WithBool ( optionName : string , optionValue : bool ) =
36+ let cloneCompletionOptionsMI = rco.CompletionOptionsType.GetMethod( " <Clone>$" )
37+ let updatedCompletionOptions = cloneCompletionOptionsMI.Invoke( rco.Object, null )
38+ let newCo = rco.CompletionOptionsType.GetProperty( optionName)
39+ newCo.SetValue( updatedCompletionOptions, optionValue)
40+ { rco with Object = updatedCompletionOptions }
41+
42+ static member Default () =
43+ let featuresAssembly = Assembly.Load( " Microsoft.CodeAnalysis.Features" )
44+ let coType = featuresAssembly.GetType( " Microsoft.CodeAnalysis.Completion.CompletionOptions" )
45+ let defaultCo : obj = coType.GetField( " Default" ) .GetValue()
46+ { Object = defaultCo; CompletionOptionsType = coType }
47+
48+ type RoslynCompletionServiceWrapper ( service : Microsoft.CodeAnalysis.Completion.CompletionService ) =
49+ member __.GetCompletionsAsync ( doc , position , completionOptions , completionTrigger , ct ) : Async < Microsoft.CodeAnalysis.Completion.CompletionList > =
50+ let completionServiceType = service.GetType()
51+
52+ let getCompletionsAsync7MI =
53+ completionServiceType.GetMethods( BindingFlags.Instance||| BindingFlags.NonPublic)
54+ |> Seq.filter ( fun mi -> mi.Name = " GetCompletionsAsync" && mi.GetParameters() .Length = 7 )
55+ |> Seq.head
56+
57+ let parameters : obj array = [| doc; position; completionOptions.Object; emptyRoslynOptionSet; completionTrigger; null ; ct |]
58+
59+ let result = getCompletionsAsync7MI.Invoke( service, parameters)
60+
61+ ( result :?> System.Threading.Tasks.Task< Microsoft.CodeAnalysis.Completion.CompletionList>)
62+ |> Async.AwaitTask
63+
64+ member __.ShouldTriggerCompletion ( sourceText , position , completionTrigger ) =
65+ service.ShouldTriggerCompletion( sourceText, position, completionTrigger)
66+
1867 let private dynamicRegistration ( clientCapabilities : ClientCapabilities option ) =
1968 clientCapabilities
2069 |> Option.bind ( fun x -> x.TextDocument)
@@ -76,7 +125,7 @@ module Completion =
76125 let private makeLspCompletionItem
77126 ( item : Microsoft.CodeAnalysis.Completion.CompletionItem )
78127 ( cacheKey : uint64 ) =
79- { CompletionItem.Create( item.DisplayText) with
128+ { Ionide.LanguageServerProtocol.Types. CompletionItem.Create( item.DisplayText) with
80129 Kind = item.Tags |> Seq.tryHead |> Option.map roslynTagToLspCompletion
81130 SortText = item.SortText |> Option.ofString
82131 FilterText = item.FilterText |> Option.ofString
@@ -87,7 +136,7 @@ module Completion =
87136
88137 let private cache = new LruCache<( Microsoft.CodeAnalysis.Document * Microsoft.CodeAnalysis.Completion.CompletionList)>( 5 )
89138
90- let handle ( context : ServerRequestContext ) ( p : CompletionParams ) : AsyncLspResult < CompletionList option > = async {
139+ let handle ( context : ServerRequestContext ) ( p : CompletionParams ) : AsyncLspResult < Ionide.LanguageServerProtocol.Types. CompletionList option > = async {
91140 match context.GetDocument p.TextDocument.Uri with
92141 | None -> return None |> success
93142 | Some doc ->
@@ -96,20 +145,29 @@ module Completion =
96145
97146 let position = Position.toRoslynPosition sourceText.Lines p.Position
98147
99- let completionService = Microsoft.CodeAnalysis.Completion.CompletionService.GetService( doc)
148+ let completionService =
149+ Microsoft.CodeAnalysis.Completion.CompletionService.GetService( doc)
150+ |> RoslynCompletionServiceWrapper
151+
152+ let completionOptions =
153+ RoslynCompletionOptions.Default()
154+ |> _. WithBool( " ShowItemsFromUnimportedNamespaces" , false )
155+ |> _. WithBool( " ShowNameSuggestions" , false )
156+
100157 let completionTrigger = CompletionContext.toCompletionTrigger p.Context
101158 let shouldTriggerCompletion =
102159 p.Context |> Option.exists ( fun x -> x.TriggerKind = CompletionTriggerKind.TriggerForIncompleteCompletions) ||
103160 completionService.ShouldTriggerCompletion( sourceText, position, completionTrigger)
161+
104162 let! completions =
105163 if shouldTriggerCompletion then
106- completionService.GetCompletionsAsync( doc, position, completionTrigger, cancellationToken= ct) |> Async.AwaitTask
164+ completionService.GetCompletionsAsync( doc, position, completionOptions, completionTrigger, ct)
165+ |> Async.map Option.ofObj
107166 else
108- async.Return null
167+ async.Return Option.None
109168
110169 return
111170 completions
112- |> Option.ofObj
113171 |> Option.map ( fun completions ->
114172 let key = cache.add(( doc, completions))
115173 let items =
0 commit comments