Skip to content

Commit

Permalink
Optimize completion (#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 ec46b97 commit eb0be3f
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 83 deletions.
12 changes: 11 additions & 1 deletion src/absil/il.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1990,6 +1990,8 @@ type ILTypeDef(name: string, attributes: TypeAttributes, layout: ILTypeDefLayout
extends: ILType option, methods: ILMethodDefs, nestedTypes: ILTypeDefs, fields: ILFieldDefs, methodImpls: ILMethodImplDefs,
events: ILEventDefs, properties: ILPropertyDefs, securityDeclsStored: ILSecurityDeclsStored, customAttrsStored: ILAttributesStored, metadataIndex: int32) =

let mutable customAttrsStored = customAttrsStored

new (name, attributes, layout, implements, genericParams, extends, methods, nestedTypes, fields, methodImpls, events, properties, securityDecls, customAttrs) =
ILTypeDef (name, attributes, layout, implements, genericParams, extends, methods, nestedTypes, fields, methodImpls, events, properties, storeILSecurityDecls securityDecls, storeILCustomAttrs customAttrs, NoMetadataIdx)

Expand Down Expand Up @@ -2025,7 +2027,15 @@ type ILTypeDef(name: string, attributes: TypeAttributes, layout: ILTypeDefLayout
properties = defaultArg properties x.Properties,
customAttrs = defaultArg customAttrs x.CustomAttrs)

member x.CustomAttrs = customAttrsStored.GetCustomAttrs x.MetadataIndex
member x.CustomAttrs =
match customAttrsStored with
| ILAttributesStored.Reader f ->
let res = ILAttributes(f x.MetadataIndex)
customAttrsStored <- ILAttributesStored.Given res
res
| ILAttributesStored.Given res ->
res

member x.SecurityDecls = x.SecurityDeclsStored.GetSecurityDecls x.MetadataIndex

member x.IsClass = (typeKindOfFlags x.Name x.Methods x.Fields x.Extends (int x.Attributes)) = ILTypeDefKind.Class
Expand Down
2 changes: 1 addition & 1 deletion src/fsharp/PrettyNaming.fs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ module public Microsoft.FSharp.Compiler.PrettyNaming
let IsOperatorName (name: string) =
let name = if name.StartsWith "( " && name.EndsWith " )" then name.[2..name.Length - 3] else name
// there is single operator containing a space - range operator with step: `.. ..`
let res = name = ".. .." || name |> Seq.forall (fun c -> opCharSet.Contains c && c <> ' ')
let res = name = ".. .." || name |> Seq.forall (fun c -> c <> ' ' && opCharSet.Contains c)
res

let IsMangledOpName (n:string) =
Expand Down
45 changes: 35 additions & 10 deletions src/fsharp/service/ServiceAssemblyContent.fs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ type AssemblySymbol =
TopRequireQualifiedAccessParent: Idents option
AutoOpenParent: Idents option
Symbol: FSharpSymbol
Kind: LookupType -> EntityKind }
Kind: LookupType -> EntityKind
UnresolvedSymbol: UnresolvedSymbol }
override x.ToString() = sprintf "%A" x

type AssemblyPath = string
Expand Down Expand Up @@ -186,14 +187,33 @@ type IAssemblyContentCache =
module AssemblyContentProvider =
open System.IO

let private createEntity ns (parent: Parent) (entity: FSharpEntity) =
let unresolvedSymbol (topRequireQualifiedAccessParent: Idents option) (cleanedIdents: Idents) (fullName: string) =
let getNamespace (idents: Idents) =
if idents.Length > 1 then Some idents.[..idents.Length - 2] else None

let ns =
topRequireQualifiedAccessParent
|> Option.bind getNamespace
|> Option.orElseWith (fun () -> getNamespace cleanedIdents)
|> Option.defaultValue [||]

let displayName =
let nameIdents = if cleanedIdents.Length > ns.Length then cleanedIdents |> Array.skip ns.Length else cleanedIdents
nameIdents |> String.concat "."

{ FullName = fullName
DisplayName = displayName
Namespace = ns }

let createEntity ns (parent: Parent) (entity: FSharpEntity) =
parent.FormatEntityFullName entity
|> Option.map (fun (fullName, cleanIdents) ->
let topRequireQualifiedAccessParent = parent.TopRequiresQualifiedAccess false |> Option.map parent.FixParentModuleSuffix
{ FullName = fullName
CleanedIdents = cleanIdents
Namespace = ns
NearestRequireQualifiedAccessParent = parent.ThisRequiresQualifiedAccess false |> Option.map parent.FixParentModuleSuffix
TopRequireQualifiedAccessParent = parent.TopRequiresQualifiedAccess false |> Option.map parent.FixParentModuleSuffix
TopRequireQualifiedAccessParent = topRequireQualifiedAccessParent
AutoOpenParent = parent.AutoOpen |> Option.map parent.FixParentModuleSuffix
Symbol = entity
Kind = fun lookupType ->
Expand All @@ -208,21 +228,26 @@ module AssemblyContentProvider =
match entity with
| Symbol.Attribute -> EntityKind.Attribute
| _ -> EntityKind.Type
UnresolvedSymbol = unresolvedSymbol topRequireQualifiedAccessParent cleanIdents fullName
})

let private traverseMemberFunctionAndValues ns (parent: Parent) (membersFunctionsAndValues: seq<FSharpMemberOrFunctionOrValue>) =
let traverseMemberFunctionAndValues ns (parent: Parent) (membersFunctionsAndValues: seq<FSharpMemberOrFunctionOrValue>) =
let topRequireQualifiedAccessParent = parent.TopRequiresQualifiedAccess true |> Option.map parent.FixParentModuleSuffix
let autoOpenParent = parent.AutoOpen |> Option.map parent.FixParentModuleSuffix
membersFunctionsAndValues
|> Seq.filter (fun x -> not x.IsInstanceMember && not x.IsPropertyGetterMethod && not x.IsPropertySetterMethod)
|> Seq.collect (fun func ->
let processIdents fullName idents =
let cleanedIdentes = parent.FixParentModuleSuffix idents
{ FullName = fullName
CleanedIdents = parent.FixParentModuleSuffix idents
CleanedIdents = cleanedIdentes
Namespace = ns
NearestRequireQualifiedAccessParent = parent.ThisRequiresQualifiedAccess true |> Option.map parent.FixParentModuleSuffix
TopRequireQualifiedAccessParent = parent.TopRequiresQualifiedAccess true |> Option.map parent.FixParentModuleSuffix
AutoOpenParent = parent.AutoOpen |> Option.map parent.FixParentModuleSuffix
TopRequireQualifiedAccessParent = topRequireQualifiedAccessParent
AutoOpenParent = autoOpenParent
Symbol = func
Kind = fun _ -> EntityKind.FunctionOrValue func.IsActivePattern }
Kind = fun _ -> EntityKind.FunctionOrValue func.IsActivePattern
UnresolvedSymbol = unresolvedSymbol topRequireQualifiedAccessParent cleanedIdentes fullName }

[ yield! func.TryGetFullDisplayName()
|> Option.map (fun fullDisplayName -> processIdents func.FullName (fullDisplayName.Split '.'))
Expand All @@ -241,7 +266,7 @@ module AssemblyContentProvider =
processIdents (fullCompiledIdents |> String.concat ".") fullCompiledIdents)
|> Option.toList ])

let rec private traverseEntity contentType (parent: Parent) (entity: FSharpEntity) =
let rec traverseEntity contentType (parent: Parent) (entity: FSharpEntity) =

seq {
#if !NO_EXTENSIONTYPING
Expand Down Expand Up @@ -308,7 +333,7 @@ module AssemblyContentProvider =
|> Seq.distinctBy (fun {FullName = fullName; CleanedIdents = cleanIdents} -> (fullName, cleanIdents))
|> Seq.toList

let private getAssemblySignaturesContent contentType (assemblies: FSharpAssembly list) =
let getAssemblySignaturesContent contentType (assemblies: FSharpAssembly list) =
assemblies |> List.collect (fun asm -> getAssemblySignatureContent contentType asm.Contents)

let getAssemblyContent (withCache: (IAssemblyContentCache -> _) -> _) contentType (fileName: string option) (assemblies: FSharpAssembly list) =
Expand Down
4 changes: 3 additions & 1 deletion src/fsharp/service/ServiceAssemblyContent.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ type public AssemblySymbol =
AutoOpenParent: Idents option
Symbol: FSharpSymbol
/// Function that returns `EntityKind` based of given `LookupKind`.
Kind: LookupType -> EntityKind }
Kind: LookupType -> EntityKind
/// Cache display name and namespace, used for completion.
UnresolvedSymbol: UnresolvedSymbol }

/// `RawEntity` list retrieved from an assembly.
type internal AssemblyContentCacheEntry =
Expand Down
18 changes: 12 additions & 6 deletions src/fsharp/service/ServiceDeclarationLists.fs
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,8 @@ type FSharpDeclarationListItem(name: string, nameInCode: string, fullName: strin
/// A table of declarations for Intellisense completion
[<Sealed>]
type FSharpDeclarationListInfo(declarations: FSharpDeclarationListItem[], isForType: bool, isError: bool) =
static let fsharpNamespace = [|"Microsoft"; "FSharp"|]

member __.Items = declarations
member __.IsForType = isForType
member __.IsError = isError
Expand Down Expand Up @@ -650,22 +652,26 @@ type FSharpDeclarationListInfo(declarations: FSharpDeclarationListItem[], isForT
| Some _ -> displayName
| None -> Lexhelp.Keywords.QuoteIdentifierIfNeeded displayName

let isAttribute = SymbolHelpers.IsAttribute infoReader item.Item
let isAttributeItem = lazy (SymbolHelpers.IsAttribute infoReader item.Item)

let cutAttributeSuffix (name: string) =
if isAttributeApplicationContext && isAttribute && name <> "Attribute" && name.EndsWith "Attribute" then
if isAttributeApplicationContext && name <> "Attribute" && name.EndsWith "Attribute" && isAttributeItem.Value then
name.[0..name.Length - "Attribute".Length - 1]
else name

let name = cutAttributeSuffix name
let nameInCode = cutAttributeSuffix nameInCode
let fullName = SymbolHelpers.FullNameOfItem g item.Item

let fullName =
match item.Unresolved with
| Some x -> x.FullName
| None -> SymbolHelpers.FullNameOfItem g item.Item

let namespaceToOpen =
item.Unresolved
|> Option.map (fun x -> x.Namespace)
|> Option.bind (fun ns ->
if ns |> Array.startsWith [|"Microsoft"; "FSharp"|] then None
if ns |> Array.startsWith fsharpNamespace then None
else Some ns)
|> Option.map (fun ns ->
match currentNamespaceOrModule with
Expand All @@ -676,7 +682,7 @@ type FSharpDeclarationListInfo(declarations: FSharpDeclarationListItem[], isForT
| None -> ns)
|> Option.bind (function
| [||] -> None
| ns -> Some (ns |> String.concat "."))
| ns -> Some (System.String.Join(".", ns)))

FSharpDeclarationListItem(
name, nameInCode, fullName, glyph, Choice1Of2 (items, infoReader, m, denv, reactor, checkAlive), getAccessibility item.Item,
Expand Down
21 changes: 2 additions & 19 deletions src/fsharp/service/service.fs
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,7 @@ type TypeCheckInfo
p <- p - 1
if p >= 0 then Some p else None

let CompletionItem (ty: TyconRef option) (unresolvedEntity: AssemblySymbol option) (item: ItemWithInst) =
let CompletionItem (ty: TyconRef option) (assemblySymbol: AssemblySymbol option) (item: ItemWithInst) =
let kind =
match item.Item with
| Item.MethodGroup (_, minfo :: _, _) -> CompletionItemKind.Method minfo.IsExtensionMember
Expand All @@ -579,29 +579,12 @@ type TypeCheckInfo
| Item.Value _ -> CompletionItemKind.Field
| _ -> CompletionItemKind.Other

let getNamespace (idents: Idents) =
if idents.Length > 1 then Some idents.[..idents.Length - 2] else None

let unresolved =
unresolvedEntity
|> Option.map (fun x ->
let ns =
x.TopRequireQualifiedAccessParent
|> Option.bind getNamespace
|> Option.orElseWith (fun () -> getNamespace x.CleanedIdents)
|> Option.defaultValue [||]

let displayName = x.CleanedIdents |> Array.skip ns.Length |> String.concat "."

{ DisplayName = displayName
Namespace = ns })

{ ItemWithInst = item
MinorPriority = 0
Kind = kind
IsOwnMember = false
Type = ty
Unresolved = unresolved }
Unresolved = assemblySymbol |> Option.map (fun x -> x.UnresolvedSymbol) }

let DefaultCompletionItem item = CompletionItem None None item

Expand Down
19 changes: 10 additions & 9 deletions src/fsharp/symbols/SymbolHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,8 @@ type CompletionItemKind =
| Other

type UnresolvedSymbol =
{ DisplayName: string
{ FullName: string
DisplayName: string
Namespace: string[] }

type CompletionItem =
Expand Down Expand Up @@ -825,16 +826,16 @@ module internal SymbolHelpers =
protectAssemblyExploration true (fun () ->
match item with
| Item.Types(it, [ty]) ->
isAppTy g ty &&
g.suppressed_types
|> List.exists (fun supp ->
if isAppTy g ty && isAppTy g (generalizedTyconRef supp) then
// check if they are the same logical type (after removing all abbreviations)
let tcr1 = tcrefOfAppTy g ty
let tcr2 = tcrefOfAppTy g (generalizedTyconRef supp)
tyconRefEq g tcr1 tcr2 &&
// check the display name is precisely the one we're suppressing
it = supp.DisplayName
else false)
let generalizedSupp = generalizedTyconRef supp
// check the display name is precisely the one we're suppressing
isAppTy g generalizedSupp && it = supp.DisplayName &&
// check if they are the same logical type (after removing all abbreviations)
let tcr1 = tcrefOfAppTy g ty
let tcr2 = tcrefOfAppTy g generalizedSupp
tyconRefEq g tcr1 tcr2)
| _ -> false)

/// Filter types that are explicitly suppressed from the IntelliSense (such as uppercase "FSharpList", "Option", etc.)
Expand Down
5 changes: 3 additions & 2 deletions src/fsharp/symbols/SymbolHelpers.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,9 @@ type public CompletionItemKind =
| Argument
| Other

type internal UnresolvedSymbol =
{ DisplayName: string
type UnresolvedSymbol =
{ FullName: string
DisplayName: string
Namespace: string[] }

type internal CompletionItem =
Expand Down
Loading

0 comments on commit eb0be3f

Please sign in to comment.