Skip to content

Commit

Permalink
Optimize completion (dotnet#5066)
Browse files Browse the repository at this point in the history
* optimize completion

* cache UnresolvedSymbol

do not generalize suppressed types twice

* more cancellable completion provider

* don't call isAttribute twice

remove cancellation from CompletionProvider

* optimize IsExplicitlySuppressed, traverseMemberFunctionAndValues

* cache ILTypeDef.CustomAttrs

* optimize IsExplicitlySuppressed

* avoid using Lazy to store custom attributes in ILTypeDef

* CompletionProvider item's cache: replace dictionary with array

* provide fast generic comparer for bool values

* make getKindPriority inline

* more defensive unresolvedSymbol

* empty array singleton table

* faster IsOperatorName

* optimize CompletionProvider

* Revert "empty array singleton table"

This reverts commit c4ef4ad.
  • Loading branch information
vasily-kirichenko authored and TIHan committed Jun 5, 2018
1 parent 29dde1b commit 68ecc88
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 34 deletions.
60 changes: 27 additions & 33 deletions Completion/CompletionProvider.fs
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,13 @@ type internal FSharpCompletionProvider
static let userOpName = "CompletionProvider"
// Save the backing data in a cache, we need to save for at least the length of the completion session
// See https://github.com/Microsoft/visualfsharp/issues/4714
static let declarationItemsData = new ConcurrentDictionary<string, FSharpDeclarationListItem>()
static let mutable declarationItems: FSharpDeclarationListItem[] = [||]
static let [<Literal>] NameInCodePropName = "NameInCode"
static let [<Literal>] FullNamePropName = "FullName"
static let [<Literal>] IsExtensionMemberPropName = "IsExtensionMember"
static let [<Literal>] NamespaceToOpenPropName = "NamespaceToOpen"
static let [<Literal>] IsKeywordPropName = "IsKeyword"
static let [<Literal>] IndexPropName = "Index"

static let keywordCompletionItems =
Lexhelp.Keywords.keywordsWithDescription
Expand Down Expand Up @@ -111,40 +112,31 @@ type internal FSharpCompletionProvider
let caretLineColumn = caretLinePos.Character
let partialName = QuickParse.GetPartialLongNameEx(caretLine.ToString(), caretLineColumn - 1)

let getAllSymbols() =
getAllSymbols checkFileResults
|> List.filter (fun entity -> entity.FullName.Contains "." && not (PrettyNaming.IsOperatorName entity.Symbol.DisplayName))
let getAllSymbols() =
getAllSymbols checkFileResults
|> List.filter (fun assemblySymbol ->
assemblySymbol.FullName.Contains "." && not (PrettyNaming.IsOperatorName assemblySymbol.Symbol.DisplayName))

let! declarations = checkFileResults.GetDeclarationListInfo(Some(parseResults), fcsCaretLineNumber, caretLine.ToString(),
partialName, getAllSymbols, userOpName=userOpName) |> liftAsync
let results = List<Completion.CompletionItem>()

let getKindPriority = function
| CompletionItemKind.Property -> 0
| CompletionItemKind.Field -> 1
| CompletionItemKind.Method (isExtension = false) -> 2
| CompletionItemKind.Event -> 3
| CompletionItemKind.Argument -> 4
| CompletionItemKind.Other -> 5
| CompletionItemKind.Method (isExtension = true) -> 6

let sortedDeclItems =
declarationItems <-
declarations.Items
|> Array.sortWith (fun x y ->
let mutable n = (not x.IsResolved).CompareTo(not y.IsResolved)
if n <> 0 then n else
n <- (getKindPriority x.Kind).CompareTo(getKindPriority y.Kind)
n <- (CompletionUtils.getKindPriority x.Kind).CompareTo(CompletionUtils.getKindPriority y.Kind)
if n <> 0 then n else
n <- (not x.IsOwnMember).CompareTo(not y.IsOwnMember)
if n <> 0 then n else
n <- StringComparer.OrdinalIgnoreCase.Compare(x.Name, y.Name)
n <- String.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase)
if n <> 0 then n else
x.MinorPriority.CompareTo(y.MinorPriority))

let maxHints = if mruItems.Values.Count = 0 then 0 else Seq.max mruItems.Values

declarationItemsData.Clear()
sortedDeclItems |> Array.iteri (fun number declarationItem ->
declarationItems |> Array.iteri (fun number declarationItem ->
let glyph = Tokenizer.FSharpGlyphToRoslynGlyph (declarationItem.Glyph, declarationItem.Accessibility)
let name =
match declarationItem.NamespaceToOpen with
Expand All @@ -166,7 +158,7 @@ type internal FSharpCompletionProvider
let completionItem =
match declarationItem.Kind with
| CompletionItemKind.Method (isExtension = true) ->
completionItem.AddProperty(IsExtensionMemberPropName, "")
completionItem.AddProperty(IsExtensionMemberPropName, "")
| _ -> completionItem

let completionItem =
Expand All @@ -179,17 +171,15 @@ type internal FSharpCompletionProvider
| Some ns -> completionItem.AddProperty(NamespaceToOpenPropName, ns)
| None -> completionItem

let completionItem = completionItem.AddProperty(IndexPropName, string number)

let priority =
match mruItems.TryGetValue declarationItem.FullName with
| true, hints -> maxHints - hints
| _ -> number + maxHints + 1

let sortText = sprintf "%06d" priority

let sortText = priority.ToString("D6")
let completionItem = completionItem.WithSortText(sortText)

let key = completionItem.DisplayText
declarationItemsData.TryAdd(key, declarationItem) |> ignore
results.Add(completionItem))

if results.Count > 0 && not declarations.IsForType && not declarations.IsError && List.isEmpty partialName.QualifyingIdents then
Expand Down Expand Up @@ -242,15 +232,19 @@ type internal FSharpCompletionProvider
override this.GetDescriptionAsync(document: Document, completionItem: Completion.CompletionItem, cancellationToken: CancellationToken): Task<CompletionDescription> =
async {
use _logBlock = Logger.LogBlockMessage document.Name LogEditorFunctionId.Completion_GetDescriptionAsync

match declarationItemsData.TryGetValue(completionItem.DisplayText) with
| true, declarationItem ->
let! description = declarationItem.StructuredDescriptionTextAsync
let documentation = List()
let collector = RoslynHelpers.CollectTaggedText documentation
// mix main description and xmldoc by using one collector
XmlDocumentation.BuildDataTipText(documentationBuilder, collector, collector, collector, collector, collector, description)
return CompletionDescription.Create(documentation.ToImmutableArray())
match completionItem.Properties.TryGetValue IndexPropName with
| true, completionItemIndexStr ->
let completionItemIndex = int completionItemIndexStr
if completionItemIndex < declarationItems.Length then
let declarationItem = declarationItems.[completionItemIndex]
let! description = declarationItem.StructuredDescriptionTextAsync
let documentation = List()
let collector = RoslynHelpers.CollectTaggedText documentation
// mix main description and xmldoc by using one collector
XmlDocumentation.BuildDataTipText(documentationBuilder, collector, collector, collector, collector, collector, description)
return CompletionDescription.Create(documentation.ToImmutableArray())
else
return CompletionDescription.Empty
| _ ->
return CompletionDescription.Empty
} |> RoslynHelpers.StartAsyncAsTask cancellationToken
Expand Down
13 changes: 12 additions & 1 deletion Completion/CompletionUtils.fs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ open Microsoft.CodeAnalysis.Classification
open Microsoft.CodeAnalysis.Text
open Microsoft.CodeAnalysis.Completion
open System.Globalization
open Microsoft.FSharp.Compiler.SourceCodeServices

module internal CompletionUtils =

Expand Down Expand Up @@ -97,4 +98,14 @@ module internal CompletionUtils =
| ClassificationTypeNames.Operator
| ClassificationTypeNames.NumericLiteral -> false
| _ -> true // anything else is a valid classification type
))
))

let inline getKindPriority kind =
match kind with
| CompletionItemKind.Property -> 0
| CompletionItemKind.Field -> 1
| CompletionItemKind.Method (isExtension = false) -> 2
| CompletionItemKind.Event -> 3
| CompletionItemKind.Argument -> 4
| CompletionItemKind.Other -> 5
| CompletionItemKind.Method (isExtension = true) -> 6

0 comments on commit 68ecc88

Please sign in to comment.