From 823cb998a11b4379f51cbc5ecbce7d869bda7751 Mon Sep 17 00:00:00 2001 From: Vasily Kirichenko Date: Fri, 1 Jun 2018 15:55:21 +0300 Subject: [PATCH 01/16] optimize completion --- src/fsharp/service/ServiceDeclarationLists.fs | 16 ++++++++++------ src/fsharp/service/service.fs | 6 ++++-- src/fsharp/symbols/SymbolHelpers.fs | 3 ++- src/fsharp/symbols/SymbolHelpers.fsi | 3 ++- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/fsharp/service/ServiceDeclarationLists.fs b/src/fsharp/service/ServiceDeclarationLists.fs index 83f1bccba0f..36847359bf6 100644 --- a/src/fsharp/service/ServiceDeclarationLists.fs +++ b/src/fsharp/service/ServiceDeclarationLists.fs @@ -548,6 +548,8 @@ type FSharpDeclarationListItem(name: string, nameInCode: string, fullName: strin /// A table of declarations for Intellisense completion [] type FSharpDeclarationListInfo(declarations: FSharpDeclarationListItem[], isForType: bool, isError: bool) = + static let fsharpNamespace = [|"Microsoft"; "FSharp"|] + member __.Items = declarations member __.IsForType = isForType member __.IsError = isError @@ -650,22 +652,24 @@ type FSharpDeclarationListInfo(declarations: FSharpDeclarationListItem[], isForT | Some _ -> displayName | None -> Lexhelp.Keywords.QuoteIdentifierIfNeeded displayName - let isAttribute = 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" && SymbolHelpers.IsAttribute infoReader item.Item 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.FullName with + | Some x -> x + | 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 @@ -676,7 +680,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, diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index 1fefac46ad6..fa301e1ceae 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -596,7 +596,8 @@ type TypeCheckInfo { DisplayName = displayName Namespace = ns }) - { ItemWithInst = item + { FullName = unresolvedEntity |> Option.map (fun x -> x.FullName) + ItemWithInst = item MinorPriority = 0 Kind = kind IsOwnMember = false @@ -859,7 +860,8 @@ type TypeCheckInfo |> RemoveExplicitlySuppressed g |> List.filter (fun item -> not (fields.Contains item.Item.DisplayName)) |> List.map (fun item -> - { ItemWithInst = item + { FullName = None + ItemWithInst = item Kind = CompletionItemKind.Argument MinorPriority = 0 IsOwnMember = false diff --git a/src/fsharp/symbols/SymbolHelpers.fs b/src/fsharp/symbols/SymbolHelpers.fs index 5fd1090b008..300e252dcfa 100644 --- a/src/fsharp/symbols/SymbolHelpers.fs +++ b/src/fsharp/symbols/SymbolHelpers.fs @@ -282,7 +282,8 @@ type UnresolvedSymbol = Namespace: string[] } type CompletionItem = - { ItemWithInst: ItemWithInst + { FullName: string option + ItemWithInst: ItemWithInst Kind: CompletionItemKind IsOwnMember: bool MinorPriority: int diff --git a/src/fsharp/symbols/SymbolHelpers.fsi b/src/fsharp/symbols/SymbolHelpers.fsi index 065c28335d4..eadcf81275a 100755 --- a/src/fsharp/symbols/SymbolHelpers.fsi +++ b/src/fsharp/symbols/SymbolHelpers.fsi @@ -120,7 +120,8 @@ type internal UnresolvedSymbol = Namespace: string[] } type internal CompletionItem = - { ItemWithInst: ItemWithInst + { FullName: string option + ItemWithInst: ItemWithInst Kind: CompletionItemKind IsOwnMember: bool MinorPriority: int From c86ab75ee6e4faf58dbee0ec8a50f5ee51aef0aa Mon Sep 17 00:00:00 2001 From: Vasily Kirichenko Date: Fri, 1 Jun 2018 17:01:03 +0300 Subject: [PATCH 02/16] cache UnresolvedSymbol do not generalize suppressed types twice --- src/fsharp/service/ServiceAssemblyContent.fs | 39 ++++++++++++++----- src/fsharp/service/ServiceAssemblyContent.fsi | 4 +- src/fsharp/service/service.fs | 17 +------- src/fsharp/symbols/SymbolHelpers.fs | 5 ++- src/fsharp/symbols/SymbolHelpers.fsi | 2 +- 5 files changed, 38 insertions(+), 29 deletions(-) diff --git a/src/fsharp/service/ServiceAssemblyContent.fs b/src/fsharp/service/ServiceAssemblyContent.fs index f4a7ac416c3..87e757c97de 100644 --- a/src/fsharp/service/ServiceAssemblyContent.fs +++ b/src/fsharp/service/ServiceAssemblyContent.fs @@ -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 @@ -186,14 +187,30 @@ type IAssemblyContentCache = module AssemblyContentProvider = open System.IO - let private createEntity ns (parent: Parent) (entity: FSharpEntity) = + let unresolvedSymbol (topRequireQualifiedAccessParent: Idents option) (cleanedIdents: Idents) = + 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 = cleanedIdents |> Array.skip ns.Length |> String.concat "." + + { 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 -> @@ -208,21 +225,25 @@ module AssemblyContentProvider = match entity with | Symbol.Attribute -> EntityKind.Attribute | _ -> EntityKind.Type + UnresolvedSymbol = unresolvedSymbol topRequireQualifiedAccessParent cleanIdents }) - let private traverseMemberFunctionAndValues ns (parent: Parent) (membersFunctionsAndValues: seq) = + let traverseMemberFunctionAndValues ns (parent: Parent) (membersFunctionsAndValues: seq) = membersFunctionsAndValues |> Seq.filter (fun x -> not x.IsInstanceMember && not x.IsPropertyGetterMethod && not x.IsPropertySetterMethod) |> Seq.collect (fun func -> let processIdents fullName idents = + let topRequireQualifiedAccessParent = parent.TopRequiresQualifiedAccess true |> Option.map parent.FixParentModuleSuffix + 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 + TopRequireQualifiedAccessParent = topRequireQualifiedAccessParent AutoOpenParent = parent.AutoOpen |> Option.map parent.FixParentModuleSuffix Symbol = func - Kind = fun _ -> EntityKind.FunctionOrValue func.IsActivePattern } + Kind = fun _ -> EntityKind.FunctionOrValue func.IsActivePattern + UnresolvedSymbol = unresolvedSymbol topRequireQualifiedAccessParent cleanedIdentes } [ yield! func.TryGetFullDisplayName() |> Option.map (fun fullDisplayName -> processIdents func.FullName (fullDisplayName.Split '.')) @@ -241,7 +262,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 @@ -308,7 +329,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) = diff --git a/src/fsharp/service/ServiceAssemblyContent.fsi b/src/fsharp/service/ServiceAssemblyContent.fsi index 8780305161d..3ac40ae0066 100644 --- a/src/fsharp/service/ServiceAssemblyContent.fsi +++ b/src/fsharp/service/ServiceAssemblyContent.fsi @@ -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 = diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index fa301e1ceae..88dfd4cce67 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -579,22 +579,7 @@ 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 }) + { FullName = unresolvedEntity |> Option.map (fun x -> x.FullName) ItemWithInst = item diff --git a/src/fsharp/symbols/SymbolHelpers.fs b/src/fsharp/symbols/SymbolHelpers.fs index 300e252dcfa..13f884e2b4e 100644 --- a/src/fsharp/symbols/SymbolHelpers.fs +++ b/src/fsharp/symbols/SymbolHelpers.fs @@ -828,10 +828,11 @@ module internal SymbolHelpers = | Item.Types(it, [ty]) -> g.suppressed_types |> List.exists (fun supp -> - if isAppTy g ty && isAppTy g (generalizedTyconRef supp) then + let generalizedSupp = generalizedTyconRef supp + if isAppTy g ty && isAppTy g generalizedSupp then // check if they are the same logical type (after removing all abbreviations) let tcr1 = tcrefOfAppTy g ty - let tcr2 = tcrefOfAppTy g (generalizedTyconRef supp) + let tcr2 = tcrefOfAppTy g generalizedSupp tyconRefEq g tcr1 tcr2 && // check the display name is precisely the one we're suppressing it = supp.DisplayName diff --git a/src/fsharp/symbols/SymbolHelpers.fsi b/src/fsharp/symbols/SymbolHelpers.fsi index eadcf81275a..76a9206170f 100755 --- a/src/fsharp/symbols/SymbolHelpers.fsi +++ b/src/fsharp/symbols/SymbolHelpers.fsi @@ -115,7 +115,7 @@ type public CompletionItemKind = | Argument | Other -type internal UnresolvedSymbol = +type UnresolvedSymbol = { DisplayName: string Namespace: string[] } From 375abe7709122d64e03ad9cb38511289e9777aa7 Mon Sep 17 00:00:00 2001 From: Vasily Kirichenko Date: Fri, 1 Jun 2018 22:45:50 +0300 Subject: [PATCH 03/16] more cancellable completion provider --- src/fsharp/service/ServiceAssemblyContent.fs | 9 +- src/fsharp/service/ServiceDeclarationLists.fs | 4 +- src/fsharp/service/service.fs | 12 +-- src/fsharp/symbols/SymbolHelpers.fs | 6 +- src/fsharp/symbols/SymbolHelpers.fsi | 6 +- .../Completion/CompletionProvider.fs | 91 ++++++++++--------- 6 files changed, 65 insertions(+), 63 deletions(-) diff --git a/src/fsharp/service/ServiceAssemblyContent.fs b/src/fsharp/service/ServiceAssemblyContent.fs index 87e757c97de..e585d7dbf03 100644 --- a/src/fsharp/service/ServiceAssemblyContent.fs +++ b/src/fsharp/service/ServiceAssemblyContent.fs @@ -187,7 +187,7 @@ type IAssemblyContentCache = module AssemblyContentProvider = open System.IO - let unresolvedSymbol (topRequireQualifiedAccessParent: Idents option) (cleanedIdents: Idents) = + 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 @@ -199,7 +199,8 @@ module AssemblyContentProvider = let displayName = cleanedIdents |> Array.skip ns.Length |> String.concat "." - { DisplayName = displayName + { FullName = fullName + DisplayName = displayName Namespace = ns } let createEntity ns (parent: Parent) (entity: FSharpEntity) = @@ -225,7 +226,7 @@ module AssemblyContentProvider = match entity with | Symbol.Attribute -> EntityKind.Attribute | _ -> EntityKind.Type - UnresolvedSymbol = unresolvedSymbol topRequireQualifiedAccessParent cleanIdents + UnresolvedSymbol = unresolvedSymbol topRequireQualifiedAccessParent cleanIdents fullName }) let traverseMemberFunctionAndValues ns (parent: Parent) (membersFunctionsAndValues: seq) = @@ -243,7 +244,7 @@ module AssemblyContentProvider = AutoOpenParent = parent.AutoOpen |> Option.map parent.FixParentModuleSuffix Symbol = func Kind = fun _ -> EntityKind.FunctionOrValue func.IsActivePattern - UnresolvedSymbol = unresolvedSymbol topRequireQualifiedAccessParent cleanedIdentes } + UnresolvedSymbol = unresolvedSymbol topRequireQualifiedAccessParent cleanedIdentes fullName } [ yield! func.TryGetFullDisplayName() |> Option.map (fun fullDisplayName -> processIdents func.FullName (fullDisplayName.Split '.')) diff --git a/src/fsharp/service/ServiceDeclarationLists.fs b/src/fsharp/service/ServiceDeclarationLists.fs index 36847359bf6..5aa2d9f2687 100644 --- a/src/fsharp/service/ServiceDeclarationLists.fs +++ b/src/fsharp/service/ServiceDeclarationLists.fs @@ -661,8 +661,8 @@ type FSharpDeclarationListInfo(declarations: FSharpDeclarationListItem[], isForT let nameInCode = cutAttributeSuffix nameInCode let fullName = - match item.FullName with - | Some x -> x + match item.Unresolved with + | Some x -> x.FullName | None -> SymbolHelpers.FullNameOfItem g item.Item let namespaceToOpen = diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index 88dfd4cce67..ac5916995b7 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -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 @@ -579,15 +579,12 @@ type TypeCheckInfo | Item.Value _ -> CompletionItemKind.Field | _ -> CompletionItemKind.Other - - - { FullName = unresolvedEntity |> Option.map (fun x -> x.FullName) - ItemWithInst = item + { 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 @@ -845,8 +842,7 @@ type TypeCheckInfo |> RemoveExplicitlySuppressed g |> List.filter (fun item -> not (fields.Contains item.Item.DisplayName)) |> List.map (fun item -> - { FullName = None - ItemWithInst = item + { ItemWithInst = item Kind = CompletionItemKind.Argument MinorPriority = 0 IsOwnMember = false diff --git a/src/fsharp/symbols/SymbolHelpers.fs b/src/fsharp/symbols/SymbolHelpers.fs index 13f884e2b4e..d52aa9754c0 100644 --- a/src/fsharp/symbols/SymbolHelpers.fs +++ b/src/fsharp/symbols/SymbolHelpers.fs @@ -278,12 +278,12 @@ type CompletionItemKind = | Other type UnresolvedSymbol = - { DisplayName: string + { FullName: string + DisplayName: string Namespace: string[] } type CompletionItem = - { FullName: string option - ItemWithInst: ItemWithInst + { ItemWithInst: ItemWithInst Kind: CompletionItemKind IsOwnMember: bool MinorPriority: int diff --git a/src/fsharp/symbols/SymbolHelpers.fsi b/src/fsharp/symbols/SymbolHelpers.fsi index 76a9206170f..03d62d4aec7 100755 --- a/src/fsharp/symbols/SymbolHelpers.fsi +++ b/src/fsharp/symbols/SymbolHelpers.fsi @@ -116,12 +116,12 @@ type public CompletionItemKind = | Other type UnresolvedSymbol = - { DisplayName: string + { FullName: string + DisplayName: string Namespace: string[] } type internal CompletionItem = - { FullName: string option - ItemWithInst: ItemWithInst + { ItemWithInst: ItemWithInst Kind: CompletionItemKind IsOwnMember: bool MinorPriority: int diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index f45a585b4ee..4ad5306e411 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -110,13 +110,17 @@ type internal FSharpCompletionProvider let fcsCaretLineNumber = Line.fromZ caretLinePos.Line // Roslyn line numbers are zero-based, FSharp.Compiler.Service line numbers are 1-based 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! cancellationToken = liftAsync Async.CancellationToken + + let getAllSymbols() = + if cancellationToken.IsCancellationRequested then [] + else 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 + do! Option.guard cancellationToken.IsCancellationRequested let results = List() let getKindPriority = function @@ -145,54 +149,55 @@ type internal FSharpCompletionProvider declarationItemsData.Clear() sortedDeclItems |> Array.iteri (fun number declarationItem -> - let glyph = Tokenizer.FSharpGlyphToRoslynGlyph (declarationItem.Glyph, declarationItem.Accessibility) - let name = - match declarationItem.NamespaceToOpen with - | Some namespaceToOpen -> sprintf "%s (open %s)" declarationItem.Name namespaceToOpen - | _ -> declarationItem.Name + if not cancellationToken.IsCancellationRequested then + let glyph = Tokenizer.FSharpGlyphToRoslynGlyph (declarationItem.Glyph, declarationItem.Accessibility) + let name = + match declarationItem.NamespaceToOpen with + | Some namespaceToOpen -> sprintf "%s (open %s)" declarationItem.Name namespaceToOpen + | _ -> declarationItem.Name - let filterText = - match declarationItem.NamespaceToOpen, declarationItem.Name.Split '.' with - // There is no namespace to open and the item name does not contain dots, so we don't need to pass special FilterText to Roslyn. - | None, [|_|] -> null - // Either we have a namespace to open ("DateTime (open System)") or item name contains dots ("Array.map"), or both. - // We are passing last part of long ident as FilterText. - | _, idents -> Array.last idents - - let completionItem = - CommonCompletionItem.Create(name, glyph = Nullable glyph, rules = getRules(), filterText = filterText) - .AddProperty(FullNamePropName, declarationItem.FullName) + let filterText = + match declarationItem.NamespaceToOpen, declarationItem.Name.Split '.' with + // There is no namespace to open and the item name does not contain dots, so we don't need to pass special FilterText to Roslyn. + | None, [|_|] -> null + // Either we have a namespace to open ("DateTime (open System)") or item name contains dots ("Array.map"), or both. + // We are passing last part of long ident as FilterText. + | _, idents -> Array.last idents + + let completionItem = + CommonCompletionItem.Create(name, glyph = Nullable glyph, rules = getRules(), filterText = filterText) + .AddProperty(FullNamePropName, declarationItem.FullName) - let completionItem = - match declarationItem.Kind with - | CompletionItemKind.Method (isExtension = true) -> - completionItem.AddProperty(IsExtensionMemberPropName, "") - | _ -> completionItem + let completionItem = + match declarationItem.Kind with + | CompletionItemKind.Method (isExtension = true) -> + completionItem.AddProperty(IsExtensionMemberPropName, "") + | _ -> completionItem - let completionItem = - if name <> declarationItem.NameInCode then - completionItem.AddProperty(NameInCodePropName, declarationItem.NameInCode) - else completionItem + let completionItem = + if name <> declarationItem.NameInCode then + completionItem.AddProperty(NameInCodePropName, declarationItem.NameInCode) + else completionItem - let completionItem = - match declarationItem.NamespaceToOpen with - | Some ns -> completionItem.AddProperty(NamespaceToOpenPropName, ns) - | None -> completionItem + let completionItem = + match declarationItem.NamespaceToOpen with + | Some ns -> completionItem.AddProperty(NamespaceToOpenPropName, ns) + | None -> completionItem - let priority = - match mruItems.TryGetValue declarationItem.FullName with - | true, hints -> maxHints - hints - | _ -> number + maxHints + 1 + let priority = + match mruItems.TryGetValue declarationItem.FullName with + | true, hints -> maxHints - hints + | _ -> number + maxHints + 1 - let sortText = sprintf "%06d" priority + let sortText = sprintf "%06d" priority - let completionItem = completionItem.WithSortText(sortText) + let completionItem = completionItem.WithSortText(sortText) - let key = completionItem.DisplayText - declarationItemsData.TryAdd(key, declarationItem) |> ignore - results.Add(completionItem)) + 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 + if not cancellationToken.IsCancellationRequested && results.Count > 0 && not declarations.IsForType && not declarations.IsError && List.isEmpty partialName.QualifyingIdents then let lineStr = textLines.[caretLinePos.Line].ToString() let completionContext = From 15734b27453eb6f9d98b0485192cf83f330c5cfb Mon Sep 17 00:00:00 2001 From: Vasily Kirichenko Date: Sat, 2 Jun 2018 10:18:47 +0300 Subject: [PATCH 04/16] don't call isAttribute twice remove cancellation from CompletionProvider --- src/fsharp/service/ServiceDeclarationLists.fs | 4 +- .../Completion/CompletionProvider.fs | 88 +++++++++---------- 2 files changed, 45 insertions(+), 47 deletions(-) diff --git a/src/fsharp/service/ServiceDeclarationLists.fs b/src/fsharp/service/ServiceDeclarationLists.fs index 5aa2d9f2687..3cf70bcd2e8 100644 --- a/src/fsharp/service/ServiceDeclarationLists.fs +++ b/src/fsharp/service/ServiceDeclarationLists.fs @@ -652,8 +652,10 @@ type FSharpDeclarationListInfo(declarations: FSharpDeclarationListItem[], isForT | Some _ -> displayName | None -> Lexhelp.Keywords.QuoteIdentifierIfNeeded displayName + let isAttributeItem = lazy (SymbolHelpers.IsAttribute infoReader item.Item) + let cutAttributeSuffix (name: string) = - if isAttributeApplicationContext && name <> "Attribute" && name.EndsWith "Attribute" && SymbolHelpers.IsAttribute infoReader item.Item then + if isAttributeApplicationContext && name <> "Attribute" && name.EndsWith "Attribute" && isAttributeItem.Value then name.[0..name.Length - "Attribute".Length - 1] else name diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index cefdacf3f08..c8989daeecc 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -110,17 +110,14 @@ type internal FSharpCompletionProvider let fcsCaretLineNumber = Line.fromZ caretLinePos.Line // Roslyn line numbers are zero-based, FSharp.Compiler.Service line numbers are 1-based let caretLineColumn = caretLinePos.Character let partialName = QuickParse.GetPartialLongNameEx(caretLine.ToString(), caretLineColumn - 1) - let! cancellationToken = liftAsync Async.CancellationToken let getAllSymbols() = - if cancellationToken.IsCancellationRequested then [] - else getAllSymbols checkFileResults - |> List.filter (fun assemblySymbol -> - assemblySymbol.FullName.Contains "." && not (PrettyNaming.IsOperatorName assemblySymbol.Symbol.DisplayName)) + 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 - do! Option.guard cancellationToken.IsCancellationRequested let results = List() let getKindPriority = function @@ -149,55 +146,54 @@ type internal FSharpCompletionProvider declarationItemsData.Clear() sortedDeclItems |> Array.iteri (fun number declarationItem -> - if not cancellationToken.IsCancellationRequested then - let glyph = Tokenizer.FSharpGlyphToRoslynGlyph (declarationItem.Glyph, declarationItem.Accessibility) - let name = - match declarationItem.NamespaceToOpen with - | Some namespaceToOpen -> sprintf "%s (open %s)" declarationItem.Name namespaceToOpen - | _ -> declarationItem.Name + let glyph = Tokenizer.FSharpGlyphToRoslynGlyph (declarationItem.Glyph, declarationItem.Accessibility) + let name = + match declarationItem.NamespaceToOpen with + | Some namespaceToOpen -> sprintf "%s (open %s)" declarationItem.Name namespaceToOpen + | _ -> declarationItem.Name - let filterText = - match declarationItem.NamespaceToOpen, declarationItem.Name.Split '.' with - // There is no namespace to open and the item name does not contain dots, so we don't need to pass special FilterText to Roslyn. - | None, [|_|] -> null - // Either we have a namespace to open ("DateTime (open System)") or item name contains dots ("Array.map"), or both. - // We are passing last part of long ident as FilterText. - | _, idents -> Array.last idents - - let completionItem = - CommonCompletionItem.Create(name, glyph = Nullable glyph, rules = getRules(), filterText = filterText) - .AddProperty(FullNamePropName, declarationItem.FullName) + let filterText = + match declarationItem.NamespaceToOpen, declarationItem.Name.Split '.' with + // There is no namespace to open and the item name does not contain dots, so we don't need to pass special FilterText to Roslyn. + | None, [|_|] -> null + // Either we have a namespace to open ("DateTime (open System)") or item name contains dots ("Array.map"), or both. + // We are passing last part of long ident as FilterText. + | _, idents -> Array.last idents + + let completionItem = + CommonCompletionItem.Create(name, glyph = Nullable glyph, rules = getRules(), filterText = filterText) + .AddProperty(FullNamePropName, declarationItem.FullName) - let completionItem = - match declarationItem.Kind with - | CompletionItemKind.Method (isExtension = true) -> - completionItem.AddProperty(IsExtensionMemberPropName, "") - | _ -> completionItem + let completionItem = + match declarationItem.Kind with + | CompletionItemKind.Method (isExtension = true) -> + completionItem.AddProperty(IsExtensionMemberPropName, "") + | _ -> completionItem - let completionItem = - if name <> declarationItem.NameInCode then - completionItem.AddProperty(NameInCodePropName, declarationItem.NameInCode) - else completionItem + let completionItem = + if name <> declarationItem.NameInCode then + completionItem.AddProperty(NameInCodePropName, declarationItem.NameInCode) + else completionItem - let completionItem = - match declarationItem.NamespaceToOpen with - | Some ns -> completionItem.AddProperty(NamespaceToOpenPropName, ns) - | None -> completionItem + let completionItem = + match declarationItem.NamespaceToOpen with + | Some ns -> completionItem.AddProperty(NamespaceToOpenPropName, ns) + | None -> completionItem - let priority = - match mruItems.TryGetValue declarationItem.FullName with - | true, hints -> maxHints - hints - | _ -> number + maxHints + 1 + let priority = + match mruItems.TryGetValue declarationItem.FullName with + | true, hints -> maxHints - hints + | _ -> number + maxHints + 1 - let sortText = sprintf "%06d" priority + let sortText = sprintf "%06d" priority - let completionItem = completionItem.WithSortText(sortText) + let completionItem = completionItem.WithSortText(sortText) - let key = completionItem.DisplayText - declarationItemsData.TryAdd(key, declarationItem) |> ignore - results.Add(completionItem)) + let key = completionItem.DisplayText + declarationItemsData.TryAdd(key, declarationItem) |> ignore + results.Add(completionItem)) - if not cancellationToken.IsCancellationRequested && results.Count > 0 && not declarations.IsForType && not declarations.IsError && List.isEmpty partialName.QualifyingIdents then + if results.Count > 0 && not declarations.IsForType && not declarations.IsError && List.isEmpty partialName.QualifyingIdents then let lineStr = textLines.[caretLinePos.Line].ToString() let completionContext = From fc7b105f66e4eb672341260eba35e6fda6d29599 Mon Sep 17 00:00:00 2001 From: Vasily Kirichenko Date: Sat, 2 Jun 2018 11:45:54 +0300 Subject: [PATCH 05/16] optimize IsExplicitlySuppressed, traverseMemberFunctionAndValues --- src/fsharp/service/ServiceAssemblyContent.fs | 5 +++-- src/fsharp/symbols/SymbolHelpers.fs | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/fsharp/service/ServiceAssemblyContent.fs b/src/fsharp/service/ServiceAssemblyContent.fs index 63d156c7340..5d76ff08c59 100644 --- a/src/fsharp/service/ServiceAssemblyContent.fs +++ b/src/fsharp/service/ServiceAssemblyContent.fs @@ -230,18 +230,19 @@ module AssemblyContentProvider = }) let traverseMemberFunctionAndValues ns (parent: Parent) (membersFunctionsAndValues: seq) = + 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 topRequireQualifiedAccessParent = parent.TopRequiresQualifiedAccess true |> Option.map parent.FixParentModuleSuffix let cleanedIdentes = parent.FixParentModuleSuffix idents { FullName = fullName CleanedIdents = cleanedIdentes Namespace = ns NearestRequireQualifiedAccessParent = parent.ThisRequiresQualifiedAccess true |> Option.map parent.FixParentModuleSuffix TopRequireQualifiedAccessParent = topRequireQualifiedAccessParent - AutoOpenParent = parent.AutoOpen |> Option.map parent.FixParentModuleSuffix + AutoOpenParent = autoOpenParent Symbol = func Kind = fun _ -> EntityKind.FunctionOrValue func.IsActivePattern UnresolvedSymbol = unresolvedSymbol topRequireQualifiedAccessParent cleanedIdentes fullName } diff --git a/src/fsharp/symbols/SymbolHelpers.fs b/src/fsharp/symbols/SymbolHelpers.fs index d52aa9754c0..a271de71ce0 100644 --- a/src/fsharp/symbols/SymbolHelpers.fs +++ b/src/fsharp/symbols/SymbolHelpers.fs @@ -826,10 +826,11 @@ module internal SymbolHelpers = protectAssemblyExploration true (fun () -> match item with | Item.Types(it, [ty]) -> + isAppTy g ty && g.suppressed_types |> List.exists (fun supp -> let generalizedSupp = generalizedTyconRef supp - if isAppTy g ty && isAppTy g generalizedSupp then + if isAppTy g generalizedSupp then // check if they are the same logical type (after removing all abbreviations) let tcr1 = tcrefOfAppTy g ty let tcr2 = tcrefOfAppTy g generalizedSupp From cab4150d05f52c0762e02b11942d12bceb93dbab Mon Sep 17 00:00:00 2001 From: Vasily Kirichenko Date: Sat, 2 Jun 2018 13:38:27 +0300 Subject: [PATCH 06/16] cache ILTypeDef.CustomAttrs --- src/absil/il.fs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/absil/il.fs b/src/absil/il.fs index 2a5b7cc1fff..937a3853115 100644 --- a/src/absil/il.fs +++ b/src/absil/il.fs @@ -1933,6 +1933,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 customAttrs = lazy (customAttrsStored.GetCustomAttrs metadataIndex) + 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) @@ -1968,7 +1970,7 @@ 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 = customAttrs.Value 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 From 0fc55de4c8d4751383a3fb41cbc23cf08272e3da Mon Sep 17 00:00:00 2001 From: Vasily Kirichenko Date: Sat, 2 Jun 2018 13:38:56 +0300 Subject: [PATCH 07/16] optimize IsExplicitlySuppressed --- src/fsharp/symbols/SymbolHelpers.fs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/fsharp/symbols/SymbolHelpers.fs b/src/fsharp/symbols/SymbolHelpers.fs index a271de71ce0..68834b60e5e 100644 --- a/src/fsharp/symbols/SymbolHelpers.fs +++ b/src/fsharp/symbols/SymbolHelpers.fs @@ -830,14 +830,12 @@ module internal SymbolHelpers = g.suppressed_types |> List.exists (fun supp -> let generalizedSupp = generalizedTyconRef supp - if isAppTy g generalizedSupp then - // 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 && - // check the display name is precisely the one we're suppressing - it = supp.DisplayName - else false) + // 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.) From c63e49d5ee4b5aae2449c9768212541ee0b028bc Mon Sep 17 00:00:00 2001 From: Vasily Kirichenko Date: Sat, 2 Jun 2018 15:07:41 +0300 Subject: [PATCH 08/16] avoid using Lazy to store custom attributes in ILTypeDef --- src/absil/il.fs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/absil/il.fs b/src/absil/il.fs index 937a3853115..0df2c3d079f 100644 --- a/src/absil/il.fs +++ b/src/absil/il.fs @@ -1933,7 +1933,7 @@ 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 customAttrs = lazy (customAttrsStored.GetCustomAttrs metadataIndex) + 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) @@ -1970,7 +1970,15 @@ type ILTypeDef(name: string, attributes: TypeAttributes, layout: ILTypeDefLayout properties = defaultArg properties x.Properties, customAttrs = defaultArg customAttrs x.CustomAttrs) - member x.CustomAttrs = customAttrs.Value + 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 From 320f56c3e54ea1f5908b66e0535b58f868d0a80c Mon Sep 17 00:00:00 2001 From: Vasily Kirichenko Date: Sat, 2 Jun 2018 16:21:41 +0300 Subject: [PATCH 09/16] CompletionProvider item's cache: replace dictionary with array --- .../Completion/CompletionProvider.fs | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index c8989daeecc..36180303ffa 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -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() + static let mutable declarationItems: FSharpDeclarationListItem[] = [||] static let [] NameInCodePropName = "NameInCode" static let [] FullNamePropName = "FullName" static let [] IsExtensionMemberPropName = "IsExtensionMember" static let [] NamespaceToOpenPropName = "NamespaceToOpen" static let [] IsKeywordPropName = "IsKeyword" + static let [] IndexPropName = "Index" static let keywordCompletionItems = Lexhelp.Keywords.keywordsWithDescription @@ -129,7 +130,7 @@ type internal FSharpCompletionProvider | 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) @@ -144,8 +145,7 @@ type internal FSharpCompletionProvider 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 @@ -180,17 +180,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 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 @@ -243,15 +241,19 @@ type internal FSharpCompletionProvider override this.GetDescriptionAsync(document: Document, completionItem: Completion.CompletionItem, cancellationToken: CancellationToken): Task = 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 From 17328499eb297af7b5187961136b29163f23376f Mon Sep 17 00:00:00 2001 From: Vasily Kirichenko Date: Sat, 2 Jun 2018 18:20:12 +0300 Subject: [PATCH 10/16] provide fast generic comparer for bool values --- src/fsharp/FSharp.Core/prim-types.fs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index 560f1ded177..f97591b4abb 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -2162,6 +2162,7 @@ namespace Microsoft.FSharp.Core let FloatComparer = MakeGenericComparer() let Float32Comparer = MakeGenericComparer() let DecimalComparer = MakeGenericComparer() + let BoolComparer = MakeGenericComparer() /// Use a type-indexed table to ensure we only create a single FastStructuralComparison function /// for each type @@ -2199,6 +2200,7 @@ namespace Microsoft.FSharp.Core | ty when ty.Equals(typeof) -> null | ty when ty.Equals(typeof) -> null | ty when ty.Equals(typeof) -> unboxPrim (box StringComparer) + | ty when ty.Equals(typeof) -> null | _ -> MakeGenericComparer<'T>() static let f : System.Collections.Generic.IComparer<'T> = @@ -2218,6 +2220,7 @@ namespace Microsoft.FSharp.Core | ty when ty.Equals(typeof) -> unboxPrim (box Float32Comparer) | ty when ty.Equals(typeof) -> unboxPrim (box DecimalComparer) | ty when ty.Equals(typeof) -> unboxPrim (box StringComparer) + | ty when ty.Equals(typeof) -> unboxPrim (box BoolComparer) | _ -> // Review: There are situations where we should be able // to return System.Collections.Generic.Comparer<'T>.Default here. From 21f7fe606e79e1e4e5e0341b295f269ae5568d01 Mon Sep 17 00:00:00 2001 From: Vasily Kirichenko Date: Sat, 2 Jun 2018 20:58:08 +0300 Subject: [PATCH 11/16] make getKindPriority inline --- .../FSharp.Editor/Completion/CompletionProvider.fs | 11 +---------- .../src/FSharp.Editor/Completion/CompletionUtils.fs | 13 ++++++++++++- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index 36180303ffa..803c4ccaa9e 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -121,21 +121,12 @@ type internal FSharpCompletionProvider partialName, getAllSymbols, userOpName=userOpName) |> liftAsync let results = List() - 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 - 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 diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs index 0acd4db9325..2abba274acd 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs @@ -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 = @@ -97,4 +98,14 @@ module internal CompletionUtils = | ClassificationTypeNames.Operator | ClassificationTypeNames.NumericLiteral -> false | _ -> true // anything else is a valid classification type - )) \ No newline at end of file + )) + + 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 \ No newline at end of file From 613fe0fd3076d0e92f4422c20516d7da9f42d3b6 Mon Sep 17 00:00:00 2001 From: Vasily Kirichenko Date: Sat, 2 Jun 2018 22:02:10 +0300 Subject: [PATCH 12/16] more defensive unresolvedSymbol --- src/fsharp/service/ServiceAssemblyContent.fs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/fsharp/service/ServiceAssemblyContent.fs b/src/fsharp/service/ServiceAssemblyContent.fs index 5d76ff08c59..cfb82b24a21 100644 --- a/src/fsharp/service/ServiceAssemblyContent.fs +++ b/src/fsharp/service/ServiceAssemblyContent.fs @@ -197,7 +197,9 @@ module AssemblyContentProvider = |> Option.orElseWith (fun () -> getNamespace cleanedIdents) |> Option.defaultValue [||] - let displayName = cleanedIdents |> Array.skip ns.Length |> String.concat "." + let displayName = + let nameIdents = if cleanedIdents.Length > ns.Length then cleanedIdents |> Array.skip ns.Length else cleanedIdents + nameIdents |> String.concat "." { FullName = fullName DisplayName = displayName From c4ef4ade23235efc1b15eb4cc9dca49a99c953cf Mon Sep 17 00:00:00 2001 From: Vasily Kirichenko Date: Sat, 2 Jun 2018 22:53:48 +0300 Subject: [PATCH 13/16] empty array singleton table --- src/fsharp/FSharp.Core/array.fs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/fsharp/FSharp.Core/array.fs b/src/fsharp/FSharp.Core/array.fs index f9cced85d3b..202e198866f 100644 --- a/src/fsharp/FSharp.Core/array.fs +++ b/src/fsharp/FSharp.Core/array.fs @@ -74,8 +74,12 @@ namespace Microsoft.FSharp.Collections if array.Length = 0 then invalidArg "array" (SR.GetString(SR.notEnoughElements)) Microsoft.FSharp.Primitives.Basics.Array.subUnchecked 1 (array.Length - 1) array + type EmptyArray<'T>() = + static let empty = ([| |] : 'T []) + static member Empty = empty + [] - let empty<'T> : 'T [] = [| |] + let empty<'T> : 'T [] = EmptyArray.Empty [] [] From a8051fe6475bf15bcdeca088fd1c99fb09e64952 Mon Sep 17 00:00:00 2001 From: Vasily Kirichenko Date: Sun, 3 Jun 2018 09:19:34 +0300 Subject: [PATCH 14/16] faster IsOperatorName --- src/fsharp/PrettyNaming.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/PrettyNaming.fs b/src/fsharp/PrettyNaming.fs index 46ab87d59c9..bb784e0e007 100755 --- a/src/fsharp/PrettyNaming.fs +++ b/src/fsharp/PrettyNaming.fs @@ -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) = From a3c6fba65fd0570cc1eaab12342ae0b7de58cb9d Mon Sep 17 00:00:00 2001 From: Vasily Kirichenko Date: Sun, 3 Jun 2018 10:40:36 +0300 Subject: [PATCH 15/16] optimize CompletionProvider --- .../src/FSharp.Editor/Completion/CompletionProvider.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index 803c4ccaa9e..1ec9696d55d 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -111,7 +111,7 @@ type internal FSharpCompletionProvider let fcsCaretLineNumber = Line.fromZ caretLinePos.Line // Roslyn line numbers are zero-based, FSharp.Compiler.Service line numbers are 1-based let caretLineColumn = caretLinePos.Character let partialName = QuickParse.GetPartialLongNameEx(caretLine.ToString(), caretLineColumn - 1) - + let getAllSymbols() = getAllSymbols checkFileResults |> List.filter (fun assemblySymbol -> @@ -130,7 +130,7 @@ type internal FSharpCompletionProvider 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)) @@ -178,7 +178,7 @@ type internal FSharpCompletionProvider | true, hints -> maxHints - hints | _ -> number + maxHints + 1 - let sortText = sprintf "%06d" priority + let sortText = priority.ToString("D6") let completionItem = completionItem.WithSortText(sortText) results.Add(completionItem)) From 0148fe9dd30536a1498c8d738cb0fea9e7b8846e Mon Sep 17 00:00:00 2001 From: Vasily Kirichenko Date: Sun, 3 Jun 2018 17:25:05 +0300 Subject: [PATCH 16/16] Revert "empty array singleton table" This reverts commit c4ef4ade23235efc1b15eb4cc9dca49a99c953cf. --- src/fsharp/FSharp.Core/array.fs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/fsharp/FSharp.Core/array.fs b/src/fsharp/FSharp.Core/array.fs index 202e198866f..f9cced85d3b 100644 --- a/src/fsharp/FSharp.Core/array.fs +++ b/src/fsharp/FSharp.Core/array.fs @@ -74,12 +74,8 @@ namespace Microsoft.FSharp.Collections if array.Length = 0 then invalidArg "array" (SR.GetString(SR.notEnoughElements)) Microsoft.FSharp.Primitives.Basics.Array.subUnchecked 1 (array.Length - 1) array - type EmptyArray<'T>() = - static let empty = ([| |] : 'T []) - static member Empty = empty - [] - let empty<'T> : 'T [] = EmptyArray.Empty + let empty<'T> : 'T [] = [| |] [] []