Skip to content

Commit 8eac7bf

Browse files
authored
Merge pull request #168 from razzmatazz/speed-up-completion
Speed up completion
2 parents 6cf6ae9 + 76f019e commit 8eac7bf

File tree

2 files changed

+66
-6
lines changed

2 files changed

+66
-6
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
55

66
## [Unreleased]
7+
* Speed up completion by not showing name suggestions or items from unimported namespaces
8+
- https://github.com/razzmatazz/csharp-language-server/pull/168
79
* Bump Ionide.LanguageServerProtocol to 0.6.0, fix some of the types used for dynamic registration
810
* Reduce noise when loading >10 project files, include currently loading project in progress bar.
911
- By @BrianCArnold in https://github.com/razzmatazz/csharp-language-server/pull/162

src/CSharpLanguageServer/Handlers/Completion.fs

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
namespace CSharpLanguageServer.Handlers
22

33
open System
4+
open System.Reflection
45

56
open FSharpPlus
67
open Ionide.LanguageServerProtocol.Server
@@ -12,9 +13,57 @@ open CSharpLanguageServer.State
1213
open CSharpLanguageServer.Util
1314
open CSharpLanguageServer.Conversions
1415
open CSharpLanguageServer.Types
16+
open CSharpLanguageServer.Logging
1517

1618
[<RequireQualifiedAccess>]
1719
module 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

Comments
 (0)