Skip to content

Commit

Permalink
Additional symbol info in completion
Browse files Browse the repository at this point in the history
  • Loading branch information
auduchinok committed Jul 7, 2021
1 parent 6049da2 commit 6d55412
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 115 deletions.
15 changes: 6 additions & 9 deletions src/fsharp/service/FSharpCheckerResults.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1087,7 +1087,7 @@ type internal TypeCheckInfo
|> Seq.tryFind (fun (_, _, _, m) -> equals m range)
|> Option.map (fun (ty, _, _, _) -> FSharpType (cenv, ty))
/// Get the auto-complete items at a location
member _.GetDeclarations (parseResultsOpt, line, lineStr, partialName, getAllEntities) =
member _.GetDeclarations (parseResultsOpt, line, lineStr, partialName, getAllEntities, unresolvedOnly) =
let isInterfaceFile = SourceFileImpl.IsInterfaceFile mainInputFileName
ErrorScope.Protect Range.range0
(fun () ->
Expand All @@ -1102,13 +1102,9 @@ type internal TypeCheckInfo
| None -> DeclarationListInfo.Empty
| Some (items, denv, ctx, m) ->
let items = if isInterfaceFile then items |> List.filter (fun x -> IsValidSignatureFileItem x.Item) else items
let getAccessibility item = FSharpSymbol.Create(cenv, item).Accessibility
let currentNamespaceOrModule =
parseResultsOpt
|> Option.map (fun x -> x.ParseTree)
|> Option.map (fun parsedInput -> ParsedInput.GetFullNameOfSmallestModuleOrNamespaceAtPoint(mkPos line 0, parsedInput))
let denv = { denv with shortTypeNames = true }
let isAttributeApplication = ctx = Some CompletionContext.AttributeApplication
DeclarationListInfo.Create(infoReader,m,denv,getAccessibility,items,currentNamespaceOrModule,isAttributeApplication))
DeclarationListInfo.Create(infoReader,m,denv,cenv, unresolvedOnly,items,isAttributeApplication))
(fun msg ->
Trace.TraceInformation(sprintf "FCS: recovering from error in GetDeclarations: '%s'" msg)
DeclarationListInfo.Error msg)
Expand Down Expand Up @@ -1954,10 +1950,11 @@ type FSharpCheckFileResults
(fun scope -> scope.TryGetExpressionType(range))

/// Intellisense autocompletions
member _.GetDeclarationListInfo(parsedFileResults, line, lineText, partialName, ?getAllEntities) =
member _.GetDeclarationListInfo(parsedFileResults, line, lineText, partialName, ?getAllEntities, ?unresolvedOnly) =
let getAllEntities = defaultArg getAllEntities (fun() -> [])
let unresolvedOnly = defaultArg unresolvedOnly false
threadSafeOp (fun () -> DeclarationListInfo.Empty) (fun scope ->
scope.GetDeclarations(parsedFileResults, line, lineText, partialName, getAllEntities))
scope.GetDeclarations(parsedFileResults, line, lineText, partialName, getAllEntities, unresolvedOnly))

member _.GetDeclarationListSymbols(parsedFileResults, line, lineText, partialName, ?getAllEntities) =
let getAllEntities = defaultArg getAllEntities (fun() -> [])
Expand Down
4 changes: 3 additions & 1 deletion src/fsharp/service/FSharpCheckerResults.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,9 @@ type public FSharpCheckFileResults =
/// <param name="getAllEntities">
/// Function that returns all entities from current and referenced assemblies.
/// </param>
member GetDeclarationListInfo: parsedFileResults:FSharpParseFileResults option * line: int * lineText:string * partialName: PartialLongName * ?getAllEntities: (unit -> AssemblySymbol list) -> DeclarationListInfo
/// /// <param name="unresolvedOnly">
/// </param>
member GetDeclarationListInfo: parsedFileResults:FSharpParseFileResults option * line: int * lineText:string * partialName: PartialLongName * ?getAllEntities: (unit -> AssemblySymbol list) * ?unresolvedOnly: bool -> DeclarationListInfo

/// <summary>Get the items for a declaration list in FSharpSymbol format</summary>
///
Expand Down
151 changes: 62 additions & 89 deletions src/fsharp/service/ServiceDeclarationLists.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

namespace FSharp.Compiler.EditorServices

open System
open System.Collections.Generic
open System.Collections.Immutable
open Internal.Utilities.Library
open Internal.Utilities.Library.Extras
Expand Down Expand Up @@ -915,50 +917,40 @@ module internal DescriptionListsImpl =

/// An intellisense declaration
[<Sealed>]
type DeclarationListItem(name: string, nameInCode: string, fullName: string, glyph: FSharpGlyph, info, accessibility: FSharpAccessibility,
kind: CompletionItemKind, isOwnMember: bool, priority: int, isResolved: bool, namespaceToOpen: string option) =
type DeclarationListItem(name: string, info, item: CompletionItem, symbol: FSharpSymbol, namespaceToOpen: string[]) =
member _.Name = name

member _.NameInCode = nameInCode

member _.Description =
match info with
| Choice1Of2 (items: CompletionItem list, infoReader, m, denv) ->
ToolTipText(items |> List.map (fun x -> FormatStructuredDescriptionOfItem true infoReader m denv x.ItemWithInst))
| Choice2Of2 result ->
result

member _.Glyph = glyph

member _.Accessibility = accessibility

member _.Kind = kind
member _.FSharpSymbol = symbol

member _.IsOwnMember = isOwnMember
member _.Kind = item.Kind

member _.MinorPriority = priority
member _.IsOwnMember = item.IsOwnMember

member _.FullName = fullName
member _.MinorPriority = item.MinorPriority

member _.IsResolved = isResolved
member _.IsResolved = item.Unresolved.IsNone

member _.NamespaceToOpen = namespaceToOpen

/// A table of declarations for Intellisense completion
[<Sealed>]
type DeclarationListInfo(declarations: DeclarationListItem[], isForType: bool, isError: bool) =
type DeclarationListInfo(declarations: DeclarationListItem seq, isError: bool, displayContext: FSharpDisplayContext) =
static let fsharpNamespace = [|"Microsoft"; "FSharp"|]

member _.Items = declarations

member _.IsForType = isForType

member _.IsError = isError
member _.DisplayContext = displayContext

// Make a 'Declarations' object for a set of selected items
static member Create(infoReader:InfoReader, m: range, denv, getAccessibility: (Item -> FSharpAccessibility), items: CompletionItem list, currentNamespace: string[] option, isAttributeApplicationContext: bool) =
static member Create(infoReader:InfoReader, m: range, denv, cenv: SymbolEnv, unresolvedOnly: bool, items: CompletionItem list, isAttributeApplicationContext: bool) =
let g = infoReader.g
let isForType = items |> List.exists (fun x -> x.Type.IsSome)
let items = items |> RemoveExplicitlySuppressedCompletionItems g

let tyconRefOptEq tref1 tref2 =
Expand Down Expand Up @@ -1017,9 +1009,11 @@ type DeclarationListInfo(declarations: DeclarationListItem[], isForType: bool, i
| None -> item.Item.DisplayName
name, items)

// Filter out operators, active patterns (as values) and the empty list
let items =
// Check whether this item looks like an operator.
let result = List()

for displayName, itemsWithSameName in items do
if displayName = "[]" then () else

let isOperatorItem name (items: CompletionItem list) =
match items with
| [item] ->
Expand All @@ -1036,81 +1030,60 @@ type DeclarationListInfo(declarations: DeclarationListItem[], isForType: bool, i
| _ -> false
| _ -> false

items |> List.filter (fun (displayName, items) ->
not (isOperatorItem displayName items) &&
not (displayName = "[]") && // list shows up as a Type and a UnionCase, only such entity with a symbolic name, but want to filter out of intellisense
not (isActivePatternItem items))
if isOperatorItem displayName itemsWithSameName || isActivePatternItem itemsWithSameName then () else

let decls =
items
|> List.map (fun (displayName, itemsWithSameFullName) ->
match itemsWithSameFullName with
| [] -> failwith "Unexpected empty bag"
| _ ->
let items =
match itemsWithSameFullName |> List.partition (fun x -> x.Unresolved.IsNone) with
| [], unresolved -> unresolved
// if there are resolvable items, throw out unresolved to prevent duplicates like `Set` and `FSharp.Collections.Set`.
| resolved, _ -> resolved

let item = items.Head
let glyph = GlyphOfItem(denv, item.Item)

let name, nameInCode =
if displayName.StartsWithOrdinal("( ") && displayName.EndsWithOrdinal(" )") then
let cleanName = displayName.[2..displayName.Length - 3]
cleanName,
if IsOperatorName displayName then cleanName else "``" + cleanName + "``"
else
displayName,
match item.Unresolved with
| Some _ -> displayName
| None -> Lexhelp.Keywords.QuoteIdentifierIfNeeded displayName
match itemsWithSameName with
| [] -> failwith "Unexpected empty bag"
| _ ->

let isAttributeItem = lazy (SymbolHelpers.IsAttribute infoReader item.Item)
let partitionedItems =
match itemsWithSameName |> List.partition (fun x -> x.Unresolved.IsNone) with
| [], unresolved -> unresolved
// if there are resolvable items, throw out unresolved to prevent duplicates like `Set` and `FSharp.Collections.Set`.
| resolved, _ -> resolved

let cutAttributeSuffix (name: string) =
if isAttributeApplicationContext && name <> "Attribute" && name.EndsWithOrdinal("Attribute") && isAttributeItem.Value then
name.[0..name.Length - "Attribute".Length - 1]
else name
let item = partitionedItems.Head
if unresolvedOnly && item.Unresolved.IsNone then () else
if IsExplicitlySuppressed g item.Item then () else

let name = cutAttributeSuffix name
let nameInCode = cutAttributeSuffix nameInCode

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 fsharpNamespace then None
else Some ns)
|> Option.map (fun ns ->
match currentNamespace with
| Some currentNs ->
if ns |> Array.startsWith currentNs then
ns.[currentNs.Length..]
else ns
| None -> ns)
|> Option.bind (function
| [||] -> None
| ns -> Some (System.String.Join(".", ns)))

DeclarationListItem(
name, nameInCode, fullName, glyph, Choice1Of2 (items, infoReader, m, denv), getAccessibility item.Item,
item.Kind, item.IsOwnMember, item.MinorPriority, item.Unresolved.IsNone, namespaceToOpen))

new DeclarationListInfo(Array.ofList decls, isForType, false)
let name =
if displayName.StartsWithOrdinal("( ") && displayName.EndsWithOrdinal(" )") then
displayName.[2..displayName.Length - 3]
else
displayName

let cutAttributeSuffix isAttributeApplicationContext infoReader (item: CompletionItem) (name: string) =
if isAttributeApplicationContext &&
name.EndsWithOrdinal("Attribute") && name.Length > "Attribute".Length &&
SymbolHelpers.IsAttribute infoReader item.Item then
name.[0..name.Length - "Attribute".Length - 1]
else
name

let name = cutAttributeSuffix isAttributeApplicationContext infoReader item name

let namespaceToOpen =
item.Unresolved
|> Option.map (fun x ->
let ns = x.Namespace
if Array.startsWith fsharpNamespace ns then Array.Empty() else ns)
|> Option.defaultValue (System.Array.Empty())

let item =
DeclarationListItem(
name, Choice1Of2 (partitionedItems, infoReader, m, denv),
item, FSharpSymbol.Create(cenv, item.ItemWithInst.Item), namespaceToOpen)

result.Add(item)

new DeclarationListInfo(result, false, FSharpDisplayContext(fun _ -> denv))

static member Error message =
new DeclarationListInfo(
[| DeclarationListItem("<Note>", "<Note>", "<Note>", FSharpGlyph.Error, Choice2Of2 (ToolTipText [ToolTipElement.CompositionError message]),
FSharpAccessibility(taccessPublic), CompletionItemKind.Other, false, 0, false, None) |], false, true)
[| DeclarationListItem("<Note>", Choice2Of2 (ToolTipText [ToolTipElement.CompositionError message]),
Unchecked.defaultof<_>, Unchecked.defaultof<_>, System.Array.Empty()) |], true, Unchecked.defaultof<_>)

static member Empty = DeclarationListInfo([| |], false, false)
static member Empty = DeclarationListInfo([| |], false, Unchecked.defaultof<_>)



Expand Down
21 changes: 7 additions & 14 deletions src/fsharp/service/ServiceDeclarationLists.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -97,27 +97,20 @@ type public DeclarationListItem =
/// Get the display name for the declaration.
member Name: string

/// Get the name for the declaration as it's presented in source code.
member NameInCode: string

/// Get the description
member Description: ToolTipText

member Glyph: FSharpGlyph

member Accessibility: FSharpAccessibility
member FSharpSymbol : FSharpSymbol

member Kind: CompletionItemKind

member IsOwnMember: bool

member MinorPriority: int

member FullName: string

member IsResolved: bool

member NamespaceToOpen: string option
member NamespaceToOpen: string[]


[<Sealed>]
Expand All @@ -127,20 +120,20 @@ type public DeclarationListItem =
// Note: this type holds a weak reference to compiler resources.
type public DeclarationListInfo =

member Items: DeclarationListItem[]

member IsForType: bool
member Items : DeclarationListItem seq

member IsError: bool

member DisplayContext: FSharpDisplayContext

// Implementation details used by other code in the compiler
static member internal Create:
infoReader:InfoReader *
m:range *
denv:DisplayEnv *
getAccessibility:(Item -> FSharpAccessibility) *
cenv: SymbolEnv *
unresolvedOnly: bool *
items:CompletionItem list *
currentNamespace:string[] option *
isAttributeApplicationContext:bool
-> DeclarationListInfo

Expand Down
4 changes: 2 additions & 2 deletions tests/service/EditorTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ type Test() =
let parseResult, typeCheckResults = parseAndCheckScript(file, input)

let decls = typeCheckResults.GetDeclarationListInfo(Some parseResult, 4, inputLines.[3], PartialLongName.Empty(20), (fun _ -> []))
let item = decls.Items |> Array.tryFind (fun d -> d.Name = "abc")
let item = decls.Items |> Seq.tryFind (fun d -> d.Name = "abc")
decls.Items |> Seq.exists (fun d -> d.Name = "abc") |> shouldEqual true

[<Test>]
Expand All @@ -333,7 +333,7 @@ type Test() =
let parseResult, typeCheckResults = parseAndCheckScript(file, input)

let decls = typeCheckResults.GetDeclarationListInfo(Some parseResult, 4, inputLines.[3], PartialLongName.Empty(21), (fun _ -> []))
let item = decls.Items |> Array.tryFind (fun d -> d.Name = "abc")
let item = decls.Items |> Seq.tryFind (fun d -> d.Name = "abc")
decls.Items |> Seq.exists (fun d -> d.Name = "abc") |> shouldEqual true

[<Test>]
Expand Down

0 comments on commit 6d55412

Please sign in to comment.