diff --git a/fcs/samples/EditorService/Program.fs b/fcs/samples/EditorService/Program.fs index 5a2cb624f0c..c7d7d7de6a7 100644 --- a/fcs/samples/EditorService/Program.fs +++ b/fcs/samples/EditorService/Program.fs @@ -40,9 +40,10 @@ printfn "%A" tip let partialName = GetPartialLongNameEx(inputLines.[4], 23) // Get declarations (autocomplete) for a location -let decls = - parsed.GetDeclarationListInfo(Some untyped, 5, inputLines.[4], partialName, (fun () -> [])) +let decls = + parsed.GetDeclarationListInfo(Some untyped, 5, inputLines.[4], partialName) |> Async.RunSynchronously -for item in decls.Items do - printfn " - %s" item.Name +match decls with +| FSharpDeclarationListInfo.Info (items, _, _) -> items |> Array.iter (fun item -> printfn " - %s" item.Name) +| _ -> () \ No newline at end of file diff --git a/src/fsharp/vs/ServiceDeclarationLists.fs b/src/fsharp/vs/ServiceDeclarationLists.fs index a36dc3ce69a..2033eefa283 100644 --- a/src/fsharp/vs/ServiceDeclarationLists.fs +++ b/src/fsharp/vs/ServiceDeclarationLists.fs @@ -484,7 +484,7 @@ module internal DescriptionListsImpl = /// An intellisense declaration [] -type FSharpDeclarationListItem(name: string, nameInCode: string, fullName: string, glyph: FSharpGlyph, info, accessibility: FSharpAccessibility option, +type FSharpDeclarationListItem(name: string, nameInCode: string, fullName: string, glyph: FSharpGlyph, info, symbol: FSharpSymbol, kind: CompletionItemKind, isOwnMember: bool, priority: int, isResolved: bool, namespaceToOpen: string option) = let mutable descriptionTextHolder: FSharpToolTipText<_> option = None @@ -544,8 +544,8 @@ type FSharpDeclarationListItem(name: string, nameInCode: string, fullName: strin ) (fun err -> FSharpToolTipText [FSharpStructuredToolTipElement.CompositionError err]) member decl.DescriptionText = decl.StructuredDescriptionText |> Tooltips.ToFSharpToolTipText - member __.Glyph = glyph - member __.Accessibility = accessibility + member __.Glyph = glyph + member __.Symbol = symbol member __.Kind = kind member __.IsOwnMember = isOwnMember member __.MinorPriority = priority @@ -554,18 +554,18 @@ type FSharpDeclarationListItem(name: string, nameInCode: string, fullName: strin member __.NamespaceToOpen = namespaceToOpen /// A table of declarations for Intellisense completion -[] -type FSharpDeclarationListInfo(declarations: FSharpDeclarationListItem[], isForType: bool, isError: bool) = - member __.Items = declarations - member __.IsForType = isForType - member __.IsError = isError +type FSharpDeclarationListInfo = + | Empty + | Info of Items: FSharpDeclarationListItem[] * IsForType: bool * DisplayContext: FSharpDisplayContext + | Error of FSharpToolTipText // Make a 'Declarations' object for a set of selected items - static member Create(infoReader:InfoReader, m, denv, getAccessibility, items: CompletionItem list, reactor, currentNamespaceOrModule: string[] option, isAttributeApplicationContext: bool, checkAlive) = + static member Create(infoReader:InfoReader, m, denv, createSymbol: (Item -> FSharpSymbol), items: CompletionItem list, reactor, currentNamespaceOrModule: string[] option, isAttributeApplicationContext: bool, checkAlive) = let g = infoReader.g let isForType = items |> List.exists (fun x -> x.Type.IsSome) let items = items |> SymbolHelpers.RemoveExplicitlySuppressedCompletionItems g - + let displayContext = FSharpDisplayContext(fun _ -> denv) + let tyconRefOptEq tref1 tref2 = match tref1 with | Some tref1 -> tyconRefEq g tref1 tref2 @@ -687,18 +687,10 @@ type FSharpDeclarationListInfo(declarations: FSharpDeclarationListItem[], isForT | ns -> Some (ns |> String.concat ".")) FSharpDeclarationListItem( - name, nameInCode, fullName, glyph, Choice1Of2 (items, infoReader, m, denv, reactor, checkAlive), getAccessibility item.Item, + name, nameInCode, fullName, glyph, Choice1Of2 (items, infoReader, m, denv, reactor, checkAlive), createSymbol item.Item, item.Kind, item.IsOwnMember, item.MinorPriority, item.Unresolved.IsNone, namespaceToOpen)) - new FSharpDeclarationListInfo(Array.ofList decls, isForType, false) - - static member Error msg = - new FSharpDeclarationListInfo( - [| FSharpDeclarationListItem("", "", "", FSharpGlyph.Error, Choice2Of2 (FSharpToolTipText [FSharpStructuredToolTipElement.CompositionError msg]), - None, CompletionItemKind.Other, false, 0, false, None) |], false, true) - - static member Empty = FSharpDeclarationListInfo([| |], false, false) - + FSharpDeclarationListInfo.Info(Array.ofList decls, isForType, displayContext) /// Represents one method (or other item) in a method group. The item may represent either a method or diff --git a/src/fsharp/vs/ServiceDeclarationLists.fsi b/src/fsharp/vs/ServiceDeclarationLists.fsi index 1ed6e5d4e11..599416c023f 100644 --- a/src/fsharp/vs/ServiceDeclarationLists.fsi +++ b/src/fsharp/vs/ServiceDeclarationLists.fsi @@ -46,7 +46,7 @@ type internal FSharpDeclarationListItem = member Glyph : FSharpGlyph - member Accessibility : FSharpAccessibility option + member Symbol : FSharpSymbol member Kind : CompletionItemKind @@ -60,8 +60,7 @@ type internal FSharpDeclarationListItem = member NamespaceToOpen : string option - -[] +[] /// Represents a set of declarations in F# source code, with information attached ready for display by an editor. /// Returned by GetDeclarations. // @@ -71,19 +70,11 @@ type FSharpDeclarationListInfo = #else type internal FSharpDeclarationListInfo = #endif - - member Items : FSharpDeclarationListItem[] - - member IsForType : bool - - member IsError : bool - - // Implementation details used by other code in the compiler - static member internal Create : infoReader:InfoReader * m:range * denv:DisplayEnv * getAccessibility:(Item -> FSharpAccessibility option) * items:CompletionItem list * reactor:IReactorOperations * currentNamespace:string[] option * isAttributeApplicationContex:bool * checkAlive:(unit -> bool) -> FSharpDeclarationListInfo - - static member internal Error : message:string -> FSharpDeclarationListInfo - - static member Empty : FSharpDeclarationListInfo + | Empty + | Info of Items: FSharpDeclarationListItem[] * IsForType: bool * DisplayContext: FSharpDisplayContext + | Error of FSharpToolTipText + // Implementation details used by other code in the compiler + static member internal Create: infoReader:InfoReader * m:range * denv:DisplayEnv * createSymbol: (Item -> FSharpSymbol) * items:CompletionItem list * reactor:IReactorOperations * currentNamespace:string[] option * isAttributeApplicationContex:bool * checkAlive:(unit -> bool) -> FSharpDeclarationListInfo /// Represents one parameter for one method (or other item) in a group. [] diff --git a/src/fsharp/vs/service.fs b/src/fsharp/vs/service.fs index a49e0cf0cdb..54a80f299d9 100644 --- a/src/fsharp/vs/service.fs +++ b/src/fsharp/vs/service.fs @@ -904,24 +904,25 @@ type TypeCheckInfo scope.IsRelativeNameResolvable(cursorPos, plid, symbol.Item) /// Get the auto-complete items at a location - member __.GetDeclarations (ctok, parseResultsOpt, line, lineStr, partialName, getAllSymbols, hasTextChangedSinceLastTypecheck) = + member __.GetDeclarations (ctok, parseResultsOpt, line, lineStr, partialName, getAllSymbols, shortTypeNames, hasTextChangedSinceLastTypecheck) = let isInterfaceFile = SourceFileImpl.IsInterfaceFile mainInputFileName ErrorScope.Protect Range.range0 (fun () -> match GetDeclItemsForNamesAtPosition(ctok, parseResultsOpt, Some partialName.QualifyingIdents, Some partialName.PartialIdent, partialName.LastDotPos, line, lineStr, partialName.EndColumn + 1, ResolveTypeNamesToCtors, ResolveOverloads.Yes, getAllSymbols, hasTextChangedSinceLastTypecheck) with | None -> FSharpDeclarationListInfo.Empty - | Some (items, denv, ctx, m) -> + | Some (items, denv, ctx, m) -> + let denv = if shortTypeNames then { denv with shortTypeNames = true } else denv + let createSymbol item = FSharpSymbol.Create(g, thisCcu, tcImports, item) let items = if isInterfaceFile then items |> List.filter (fun x -> IsValidSignatureFileItem x.Item) else items - let getAccessibility item = FSharpSymbol.GetAccessibility (FSharpSymbol.Create(g, thisCcu, tcImports, item)) let currentNamespaceOrModule = parseResultsOpt |> Option.bind (fun x -> x.ParseTree) |> Option.map (fun parsedInput -> UntypedParseImpl.GetFullNameOfSmallestModuleOrNamespaceAtPoint(parsedInput, mkPos line 0)) let isAttributeApplication = ctx = Some CompletionContext.AttributeApplication - FSharpDeclarationListInfo.Create(infoReader,m,denv,getAccessibility,items,reactorOps,currentNamespaceOrModule,isAttributeApplication,checkAlive)) + FSharpDeclarationListInfo.Create(infoReader,m,denv,createSymbol,items,reactorOps,currentNamespaceOrModule,isAttributeApplication,checkAlive)) (fun msg -> Trace.TraceInformation(sprintf "FCS: recovering from error in GetDeclarations: '%s'" msg) - FSharpDeclarationListInfo.Error msg) + FSharpDeclarationListInfo.Error(FSharpToolTipText [FSharpStructuredToolTipElement.CompositionError msg])) /// Get the symbols for auto-complete items at a location member __.GetDeclarationListSymbols (ctok, parseResultsOpt, line, lineStr, partialName, hasTextChangedSinceLastTypecheck) = @@ -1232,6 +1233,11 @@ type TypeCheckInfo member __.GetFormatSpecifierLocationsAndArity() = sSymbolUses.GetFormatSpecifierLocationsAndArity() + member __.GetAllResolvedSymbols() = + sResolutions.CapturedNameResolutions + |> Seq.map (fun (cnr: CapturedNameResolution) -> cnr.Range, FSharpSymbol.Create(g, thisCcu, tcImports, cnr.Item)) + |> Array.ofSeq + member __.GetSemanticClassification(range: range option) : (range * SemanticClassificationType) [] = ErrorScope.Protect Range.range0 (fun () -> @@ -1891,13 +1897,15 @@ type FSharpCheckFileResults(filename: string, errors: FSharpErrorInfo[], scopeOp member info.Errors = errors member info.HasFullTypeCheckInfo = details.IsSome - + /// Intellisense autocompletions - member info.GetDeclarationListInfo(parseResultsOpt, line, lineStr, partialName, getAllEntities, ?hasTextChangedSinceLastTypecheck, ?userOpName: string) = + member info.GetDeclarationListInfo(parseResultsOpt, line, lineStr, partialName, ?getAllEntities, ?shortTypeNames, ?hasTextChangedSinceLastTypecheck, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" + let getAllEntities = defaultArg getAllEntities (fun _ -> []) + let shortTypeNames = defaultArg shortTypeNames false let hasTextChangedSinceLastTypecheck = defaultArg hasTextChangedSinceLastTypecheck (fun _ -> false) - reactorOp userOpName "GetDeclarations" FSharpDeclarationListInfo.Empty (fun ctok scope -> - scope.GetDeclarations(ctok, parseResultsOpt, line, lineStr, partialName, getAllEntities, hasTextChangedSinceLastTypecheck)) + reactorOp userOpName "GetDeclarations" FSharpDeclarationListInfo.Empty (fun ctok scope -> + scope.GetDeclarations(ctok, parseResultsOpt, line, lineStr, partialName, getAllEntities, shortTypeNames, hasTextChangedSinceLastTypecheck)) member info.GetDeclarationListSymbols(parseResultsOpt, line, lineStr, partialName, ?hasTextChangedSinceLastTypecheck, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" @@ -2000,6 +2008,12 @@ type FSharpCheckFileResults(filename: string, errors: FSharpErrorInfo[], scopeOp yield FSharpSymbolUse(scope.TcGlobals, denv, symbol, itemOcc, m) |]) |> async.Return + member info.GetAllResolvedSymbols() = + threadSafeOp + (fun () -> [| |]) + (fun scope -> scope.GetAllResolvedSymbols()) + |> async.Return + member info.GetUsesOfSymbolInFile(symbol:FSharpSymbol) = threadSafeOp (fun () -> [| |]) diff --git a/src/fsharp/vs/service.fsi b/src/fsharp/vs/service.fsi index 1e7005bff08..98306d3efda 100755 --- a/src/fsharp/vs/service.fsi +++ b/src/fsharp/vs/service.fsi @@ -142,13 +142,21 @@ type internal FSharpCheckFileResults = /// /// Function that returns all symbols from current and referenced assemblies. /// + /// + /// + /// Function that returns additional info about a symbol in completion context. + /// + /// + /// + /// Hide non-opened namespaces in type names in item descriptions and additional info. + /// /// /// If text has been used from a captured name resolution from the typecheck, then /// callback to the client to check if the text has changed. If it has, then give up /// and assume that we're going to repeat the operation later on. /// /// An optional string used for tracing compiler operations associated with this request. - member GetDeclarationListInfo : ParsedFileResultsOpt:FSharpParseFileResults option * line: int * lineText:string * partialName: PartialLongName * getAllSymbols: (unit -> AssemblySymbol list) * ?hasTextChangedSinceLastTypecheck: (obj * range -> bool) * ?userOpName: string -> Async + member GetDeclarationListInfo : ParsedFileResultsOpt:FSharpParseFileResults option * line: int * lineText:string * partialName: PartialLongName * ?getAllSymbols: (unit -> AssemblySymbol list) * ?shortTypeNames: bool * ?hasTextChangedSinceLastTypecheck: (obj * range -> bool) * ?userOpName: string -> Async /// Get the items for a declaration list in FSharpSymbol format /// @@ -257,6 +265,8 @@ type internal FSharpCheckFileResults = /// Get all textual usages of all symbols throughout the file member GetAllUsesOfAllSymbolsInFile : unit -> Async + member GetAllResolvedSymbols: unit -> Async<(range * FSharpSymbol)[]> + /// Get the textual usages that resolved to the given symbol throughout the file member GetUsesOfSymbolInFile : symbol:FSharpSymbol -> Async diff --git a/tests/service/EditorTests.fs b/tests/service/EditorTests.fs index 2e050abe327..a320c62dbb2 100644 --- a/tests/service/EditorTests.fs +++ b/tests/service/EditorTests.fs @@ -87,7 +87,10 @@ let ``Intro test`` () = // (sprintf "%A" tip).Replace("\n","") |> shouldEqual """FSharpToolTipText [Single ("val foo : unit -> unitFull name: Test.foo",None)]""" // Get declarations (autocomplete) for a location let partialName = { QualifyingIdents = []; PartialIdent = "msg"; EndColumn = 22; LastDotPos = None } - let decls = typeCheckResults.GetDeclarationListInfo(Some parseResult, 7, inputLines.[6], partialName, (fun _ -> []), fun _ -> false)|> Async.RunSynchronously + let decls = + typeCheckResults.GetDeclarationListInfo(Some parseResult, 7, inputLines.[6], partialName, + hasTextChangedSinceLastTypecheck = (fun _ -> false)) + |> Async.RunSynchronously CollectionAssert.AreEquivalent(stringMethods,[ for item in decls.Items -> item.Name ]) // Get overloads of the String.Concat method let methods = typeCheckResults.GetMethods(5, 27, inputLines.[4], Some ["String"; "Concat"]) |> Async.RunSynchronously @@ -286,7 +289,10 @@ let ``Expression typing test`` () = // gives the results for the string type. // for col in 42..43 do - let decls = typeCheckResults.GetDeclarationListInfo(Some parseResult, 2, inputLines.[1], PartialLongName.Empty(col), (fun _ -> []), fun _ -> false)|> Async.RunSynchronously + let decls = + typeCheckResults.GetDeclarationListInfo(Some parseResult, 2, inputLines.[1], PartialLongName.Empty(col), + hasTextChangedSinceLastTypecheck = (fun _ -> false)) + |> Async.RunSynchronously let autoCompleteSet = set [ for item in decls.Items -> item.Name ] autoCompleteSet |> shouldEqual (set stringMethods) @@ -307,7 +313,10 @@ type Test() = let file = "/home/user/Test.fsx" let parseResult, typeCheckResults = parseAndCheckScript(file, input) - let decls = typeCheckResults.GetDeclarationListInfo(Some parseResult, 4, inputLines.[3], PartialLongName.Empty(20), (fun _ -> []), fun _ -> false)|> Async.RunSynchronously + let decls = + typeCheckResults.GetDeclarationListInfo(Some parseResult, 4, inputLines.[3], PartialLongName.Empty(20), + hasTextChangedSinceLastTypecheck = (fun _ -> false)) + |> Async.RunSynchronously let item = decls.Items |> Array.tryFind (fun d -> d.Name = "abc") decls.Items |> Seq.exists (fun d -> d.Name = "abc") |> shouldEqual true @@ -324,7 +333,10 @@ type Test() = let file = "/home/user/Test.fsx" let parseResult, typeCheckResults = parseAndCheckScript(file, input) - let decls = typeCheckResults.GetDeclarationListInfo(Some parseResult, 4, inputLines.[3], PartialLongName.Empty(21), (fun _ -> []), fun _ -> false)|> Async.RunSynchronously + let decls = + typeCheckResults.GetDeclarationListInfo(Some parseResult, 4, inputLines.[3], PartialLongName.Empty(21), + hasTextChangedSinceLastTypecheck = (fun _ -> false)) + |> Async.RunSynchronously let item = decls.Items |> Array.tryFind (fun d -> d.Name = "abc") decls.Items |> Seq.exists (fun d -> d.Name = "abc") |> shouldEqual true @@ -341,7 +353,10 @@ type Test() = let file = "/home/user/Test.fsx" let parseResult, typeCheckResults = parseAndCheckScript(file, input) - let decls = typeCheckResults.GetDeclarationListInfo(Some parseResult, 4, inputLines.[3], PartialLongName.Empty(14), (fun _ -> []), fun _ -> false)|> Async.RunSynchronously + let decls = + typeCheckResults.GetDeclarationListInfo(Some parseResult, 4, inputLines.[3], PartialLongName.Empty(14), + hasTextChangedSinceLastTypecheck = (fun _ -> false)) + |> Async.RunSynchronously decls.Items |> Seq.exists (fun d -> d.Name = "abc") |> shouldEqual true [] diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index 7825ccc1bda..ec42b67fbcf 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -34,7 +34,7 @@ type internal FSharpCompletionProvider inherit CompletionProvider() static let userOpName = "CompletionProvider" - static let declarationItemsCache = ConditionalWeakTable() + static let declarationItemsCache = ConditionalWeakTable>() static let [] NameInCodePropName = "NameInCode" static let [] FullNamePropName = "FullName" static let [] IsExtensionMemberPropName = "IsExtensionMember" @@ -111,13 +111,15 @@ 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() |> List.filter (fun entity -> entity.FullName.Contains "." && not (PrettyNaming.IsOperatorName entity.Symbol.DisplayName)) + let getAdditionalInfo = fun (symbol, _) -> FSharpSymbol.GetAccessibility symbol + let! declarations = checkFileResults.GetDeclarationListInfo(Some(parseResults), fcsCaretLineNumber, caretLine.ToString(), - partialName, getAllSymbols, userOpName=userOpName) |> liftAsync + partialName, getAllSymbols, getAdditionalInfo, userOpName=userOpName) |> liftAsync let results = List() let getKindPriority = function @@ -145,7 +147,7 @@ type internal FSharpCompletionProvider let maxHints = if mruItems.Values.Count = 0 then 0 else Seq.max mruItems.Values sortedDeclItems |> Array.iteri (fun number declItem -> - let glyph = Tokenizer.FSharpGlyphToRoslynGlyph (declItem.Glyph, declItem.Accessibility) + let glyph = Tokenizer.FSharpGlyphToRoslynGlyph (declItem.Glyph, declItem.AdditionalInfo) let name = match declItem.NamespaceToOpen with | Some namespaceToOpen -> sprintf "%s (open %s)" declItem.Name namespaceToOpen diff --git a/vsintegration/src/FSharp.LanguageService/Intellisense.fs b/vsintegration/src/FSharp.LanguageService/Intellisense.fs index 2a5fa9e6ebf..5338bda9b8d 100644 --- a/vsintegration/src/FSharp.LanguageService/Intellisense.fs +++ b/vsintegration/src/FSharp.LanguageService/Intellisense.fs @@ -116,7 +116,7 @@ type internal ObsoleteGlyph = // functionality and thus have considerable value, they should ony be deleted if we are sure this // is not the case. // -type internal FSharpDeclarations_DEPRECATED(documentationBuilder, declarations: FSharpDeclarationListItem[], reason: BackgroundRequestReason) = +type internal FSharpDeclarations_DEPRECATED(documentationBuilder, declarations: FSharpDeclarationListItem<_>[], reason: BackgroundRequestReason) = inherit Declarations_DEPRECATED() @@ -125,7 +125,7 @@ type internal FSharpDeclarations_DEPRECATED(documentationBuilder, declarations: let mutable lastBestMatch = "" let isEmpty = (declarations.Length = 0) - let tab = Dictionary() + let tab = Dictionary[]>() // Given a prefix, narrow the items to the include the ones containing that prefix, and store in a lookaside table // attached to this declaration set. @@ -535,7 +535,7 @@ type internal FSharpIntellisenseInfo_DEPRECATED let oldTextSnapshot = oldTextSnapshotInfo :?> ITextSnapshot hasTextChangedSinceLastTypecheck (textSnapshot, oldTextSnapshot, Range.Range.toZ range) - let! decls = typedResults.GetDeclarationListInfo(untypedParseInfoOpt, Range.Line.fromZ line, lineText, pname, (fun() -> []), detectTextChange) + let! decls = typedResults.GetDeclarationListInfo(untypedParseInfoOpt, Range.Line.fromZ line, lineText, pname, hasTextChangedSinceLastTypecheck = detectTextChange) return (new FSharpDeclarations_DEPRECATED(documentationBuilder, decls.Items, reason) :> Declarations_DEPRECATED) else // no TypeCheckInfo in ParseResult.