From bd0ab430a0d64317ae339f253ec6f92d32fe87eb Mon Sep 17 00:00:00 2001 From: majocha Date: Tue, 28 Mar 2023 20:49:35 +0200 Subject: [PATCH 1/7] first shot --- src/Compiler/Service/FSharpCheckerResults.fs | 12 +- .../Service/ServiceDeclarationLists.fs | 1596 +++++++++-------- .../Service/ServiceDeclarationLists.fsi | 8 +- .../src/FSharp.Editor/Common/RoslynHelpers.fs | 13 + .../DocComments/XMLDocumentation.fs | 75 +- .../Navigation/GoToDefinition.fs | 188 -- .../QuickInfo/QuickInfoProvider.fs | 238 +-- .../src/FSharp.Editor/QuickInfo/Views.fs | 24 +- .../FSharp.Editor/QuickInfo/WpfFactories.fs | 16 +- 9 files changed, 1056 insertions(+), 1114 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index b29d26f994..c07d81fd86 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -1726,7 +1726,7 @@ type internal TypeCheckInfo } let toolTipElement = - FormatStructuredDescriptionOfItem displayFullName infoReader accessorDomain m denv itemWithInst None + FormatStructuredDescriptionOfItem displayFullName infoReader accessorDomain m denv itemWithInst (Some symbol) None ToolTipText [ toolTipElement ] @@ -1757,7 +1757,8 @@ type internal TypeCheckInfo ToolTipText( items |> List.map (fun x -> - FormatStructuredDescriptionOfItem false infoReader tcAccessRights m denv x.ItemWithInst width) + let symbol = Some(FSharpSymbol.Create(cenv, x.Item)) + FormatStructuredDescriptionOfItem false infoReader tcAccessRights m denv x.ItemWithInst symbol width) )) (fun err -> @@ -2716,12 +2717,9 @@ type FSharpCheckFileResults | None -> () | Some kwDescription -> let kwText = kw |> TaggedText.tagKeyword |> wordL |> LayoutRender.toArray - let kwTip = ToolTipElementData.Create(kwText, FSharpXmlDoc.None) + let _descText = kwDescription |> TaggedText.tagText |> wordL |> LayoutRender.toArray - let descText = kwDescription |> TaggedText.tagText |> wordL |> LayoutRender.toArray - let descTip = ToolTipElementData.Create(descText, FSharpXmlDoc.None) - - yield ToolTipElement.Group [ kwTip; descTip ] + yield ToolTipElement.Single(kwText, FSharpXmlDoc.FromXmlText(Xml.XmlDoc([| kwDescription |], range.Zero))) ] /// Resolve the names at the given location to give a data tip diff --git a/src/Compiler/Service/ServiceDeclarationLists.fs b/src/Compiler/Service/ServiceDeclarationLists.fs index c062ecec29..04ed4cb5f6 100644 --- a/src/Compiler/Service/ServiceDeclarationLists.fs +++ b/src/Compiler/Service/ServiceDeclarationLists.fs @@ -34,14 +34,16 @@ open FSharp.Compiler.TypedTreeOps /// A single data tip display element [] type ToolTipElementData = - { MainDescription: TaggedText[] + { + Symbol: FSharpSymbol option + MainDescription: TaggedText[] XmlDoc: FSharpXmlDoc TypeMapping: TaggedText[] list Remarks: TaggedText[] option ParamName : string option } - static member internal Create(layout, xml, ?typeMapping, ?paramName, ?remarks) = - { MainDescription=layout; XmlDoc=xml; TypeMapping=defaultArg typeMapping []; ParamName=paramName; Remarks=remarks } + static member internal Create(layout, xml, ?typeMapping, ?paramName, ?remarks, ?symbol) = + { MainDescription=layout; XmlDoc=xml; TypeMapping=defaultArg typeMapping []; ParamName=paramName; Remarks=remarks; Symbol = symbol } /// A single data tip display element [] @@ -54,8 +56,8 @@ type ToolTipElement = /// An error occurred formatting this element | CompositionError of errorText: string - static member Single(layout, xml, ?typeMapping, ?paramName, ?remarks) = - Group [ ToolTipElementData.Create(layout, xml, ?typeMapping=typeMapping, ?paramName=paramName, ?remarks=remarks) ] + static member Single(layout, xml, ?typeMapping, ?paramName, ?remarks, ?symbol) = + Group [ ToolTipElementData.Create(layout, xml, ?typeMapping=typeMapping, ?paramName=paramName, ?remarks=remarks, ?symbol = symbol) ] /// Information for building a data tip box. type ToolTipText = @@ -86,793 +88,351 @@ type CompletionItem = Unresolved: UnresolvedSymbol option } member x.Item = x.ItemWithInst.Item -[] -module DeclarationListHelpers = - let mutable ToolTipFault = None +/// Represents one parameter for one method (or other item) in a group. +[] +type MethodGroupItemParameter(name: string, canonicalTypeTextForSorting: string, display: TaggedText[], isOptional: bool) = - let emptyToolTip = ToolTipText [] + /// The name of the parameter. + member _.ParameterName = name - /// Generate the structured tooltip for a method info - let FormatOverloadsToList (infoReader: InfoReader) m denv (item: ItemWithInst) minfos (width: int option) : ToolTipElement = - ToolTipFault |> Option.iter (fun msg -> - let exn = Error((0, msg), range.Zero) - let ph = PhasedDiagnostic.Create(exn, BuildPhase.TypeCheck) - simulateError ph) - - let layouts = - [ for minfo in minfos -> - let prettyTyparInst, layout = NicePrint.prettyLayoutOfMethInfoFreeStyle infoReader m denv item.TyparInstantiation minfo - let xml = GetXmlCommentForMethInfoItem infoReader m item.Item minfo - let tpsL = FormatTyparMapping denv prettyTyparInst - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - let tpsL = List.map toArray tpsL - ToolTipElementData.Create(layout, xml, tpsL) ] - - ToolTipElement.Group layouts - - let CompletionItemDisplayPartialEquality g = - let itemComparer = ItemDisplayPartialEquality g - - { new IPartialEqualityComparer with - member x.InEqualityRelation item = itemComparer.InEqualityRelation item.Item - member x.Equals(item1, item2) = itemComparer.Equals(item1.Item, item2.Item) - member x.GetHashCode item = itemComparer.GetHashCode(item.Item) } + /// A key that can be used for sorting the parameters, used to help sort overloads. + member _.CanonicalTypeTextForSorting = canonicalTypeTextForSorting - /// Remove all duplicate items - let RemoveDuplicateCompletionItems g items = - if isNil items then items else - items |> IPartialEqualityComparer.partialDistinctBy (CompletionItemDisplayPartialEquality g) + /// The text to display for the parameter including its name, its type and visual indicators of other + /// information such as whether it is optional. + member _.Display = display - /// Filter types that are explicitly suppressed from the IntelliSense (such as uppercase "FSharpList", "Option", etc.) - let RemoveExplicitlySuppressedCompletionItems (g: TcGlobals) (items: CompletionItem list) = - items |> List.filter (fun item -> not (IsExplicitlySuppressed g item.Item)) + /// Is the parameter optional + member _.IsOptional = isOptional - // Remove items containing the same module references - let RemoveDuplicateModuleRefs modrefs = - modrefs |> IPartialEqualityComparer.partialDistinctBy - { new IPartialEqualityComparer with - member x.InEqualityRelation _ = true - member x.Equals(item1, item2) = (fullDisplayTextOfModRef item1 = fullDisplayTextOfModRef item2) - member x.GetHashCode item = hash item.Stamp } +[] +module internal DescriptionListsImpl = - let OutputFullName displayFullName ppF fnF r = - // Only display full names in quick info, not declaration lists or method lists - if not displayFullName then - match ppF r with - | None -> emptyL - | Some _ -> wordL (tagText (FSComp.SR.typeInfoFullName())) ^^ RightL.colon ^^ (fnF r) - else emptyL + let isFunction g ty = + let _, tauTy = tryDestForallTy g ty + isFunTy g tauTy + + let printCanonicalizedTypeName g (denv:DisplayEnv) tauTy = + // get rid of F# abbreviations and such + let strippedTy = stripTyEqnsWrtErasure EraseAll g tauTy + // pretend no namespaces are open + let denv = denv.SetOpenPaths([]) + // now printing will see a .NET-like canonical representation, that is good for sorting overloads into a reasonable order (see bug 94520) + NicePrint.stringOfTy denv strippedTy - let pubpathOfValRef (v: ValRef) = v.PublicPath + let PrettyParamOfRecdField g denv (f: RecdField) = + let display = NicePrint.prettyLayoutOfType denv f.FormalType + let display = toArray display + MethodGroupItemParameter( + name = f.DisplayNameCore, + canonicalTypeTextForSorting = printCanonicalizedTypeName g denv f.FormalType, + display = display, + isOptional=false) + + let PrettyParamOfUnionCaseField g denv isGenerated (i: int) (f: RecdField) = + let initial = PrettyParamOfRecdField g denv f + let display = + if isGenerated i f then + initial.Display + else + let display = NicePrint.layoutOfParamData denv (ParamData(false, false, false, NotOptional, NoCallerInfo, Some f.Id, ReflectedArgInfo.None, f.FormalType)) + toArray display - let pubpathOfTyconRef (x: TyconRef) = x.PublicPath + MethodGroupItemParameter( + name=initial.ParameterName, + canonicalTypeTextForSorting=initial.CanonicalTypeTextForSorting, + display=display, + isOptional=false) - /// Output the quick info information of a language item - let rec FormatItemDescriptionToToolTipElement displayFullName (infoReader: InfoReader) ad m denv (item: ItemWithInst) (width: int option) = - let g = infoReader.g - let amap = infoReader.amap - let denv = SimplerDisplayEnv denv - let xml = GetXmlCommentForItem infoReader m item.Item - match item.Item with - | Item.ImplicitOp(_, { contents = Some(TraitConstraintSln.FSMethSln(vref=vref)) }) -> - // operator with solution - FormatItemDescriptionToToolTipElement displayFullName infoReader ad m denv { item with Item = Item.Value vref } width + let ParamOfParamData g denv (ParamData(_isParamArrayArg, _isInArg, _isOutArg, optArgInfo, _callerInfo, nmOpt, _reflArgInfo, pty) as paramData) = + let display = NicePrint.layoutOfParamData denv paramData + let display = toArray display + MethodGroupItemParameter( + name = (match nmOpt with None -> "" | Some pn -> pn.idText), + canonicalTypeTextForSorting = printCanonicalizedTypeName g denv pty, + display = display, + isOptional=optArgInfo.IsOptional) - | Item.Value vref | Item.CustomBuilder (_, vref) -> - let prettyTyparInst, resL = NicePrint.layoutQualifiedValOrMember denv infoReader item.TyparInstantiation vref - let remarks = OutputFullName displayFullName pubpathOfValRef fullDisplayTextOfValRefAsLayout vref - let tpsL = FormatTyparMapping denv prettyTyparInst - let tpsL = List.map toArray tpsL - let resL = PrintUtilities.squashToWidth width resL - let resL = toArray resL - let remarks = toArray remarks - ToolTipElement.Single(resL, xml, tpsL, remarks=remarks) + // TODO this code is similar to NicePrint.fs:formatParamDataToBuffer, refactor or figure out why different? + let PrettyParamsOfParamDatas g denv typarInst (paramDatas:ParamData list) paramTy = + let paramInfo, paramTypes = + paramDatas + |> List.map (fun (ParamData(isParamArrayArg, _isInArg, _isOutArg, optArgInfo, _callerInfo, nmOpt, _reflArgInfo, pty)) -> + let isOptArg = optArgInfo.IsOptional + match nmOpt, isOptArg, tryDestOptionTy denv.g pty with + // Layout an optional argument + | Some id, true, ptyOpt -> + let nm = id.idText + // detect parameter type, if ptyOpt is None - this is .NET style optional argument + let pty = match ptyOpt with ValueSome x -> x | _ -> pty + (nm, isOptArg, SepL.questionMark ^^ (wordL (tagParameter nm))), pty + // Layout an unnamed argument + | None, _, _ -> + ("", isOptArg, emptyL), pty + // Layout a named argument + | Some id, _, _ -> + let nm = id.idText + let prefix = + if isParamArrayArg then + NicePrint.PrintUtilities.layoutBuiltinAttribute denv denv.g.attrib_ParamArrayAttribute ^^ + wordL (tagParameter nm) ^^ + RightL.colon + //sprintf "%s %s: " (NicePrint.PrintUtilities.layoutBuiltinAttribute denv denv.g.attrib_ParamArrayAttribute |> showL) nm + else + wordL (tagParameter nm) ^^ + RightL.colon + //sprintf "%s: " nm + (nm, isOptArg, prefix), pty) + |> List.unzip - // Union tags (constructors) - | Item.UnionCase(ucinfo, _) -> - let uc = ucinfo.UnionCase - let unionTy = generalizedTyconRef g ucinfo.TyconRef - let recd = uc.RecdFields - let layout = - wordL (tagText (FSComp.SR.typeInfoUnionCase())) ^^ - NicePrint.layoutTyconRef denv ucinfo.TyconRef ^^ - sepL (tagPunctuation ".") ^^ - wordL (tagUnionCase (ConvertValLogicalNameToDisplayNameCore uc.Id.idText) |> mkNav uc.DefinitionRange) ^^ - RightL.colon ^^ - (if List.isEmpty recd then emptyL else NicePrint.layoutUnionCases denv infoReader ucinfo.TyconRef recd ^^ WordL.arrow) ^^ - NicePrint.layoutType denv unionTy - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml) + // Prettify everything + let prettyTyparInst, (prettyParamTys, _prettyRetTy), (prettyParamTysL, prettyRetTyL), prettyConstraintsL = + NicePrint.prettyLayoutOfInstAndSig denv (typarInst, paramTypes, paramTy) - // Active pattern tag inside the declaration (result) - | Item.ActivePatternResult(apinfo, ty, idx, _) -> - let items = apinfo.ActiveTags - let layout = - wordL (tagText (FSComp.SR.typeInfoActivePatternResult())) ^^ - wordL (tagActivePatternResult (List.item idx items) |> mkNav apinfo.Range) ^^ - RightL.colon ^^ - NicePrint.layoutType denv ty - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml) + // Remake the params using the prettified versions + let prettyParams = + (paramInfo, prettyParamTys, prettyParamTysL) |||> List.map3 (fun (nm, isOptArg, paramPrefix) tauTy tyL -> + let display = paramPrefix ^^ tyL + let display = toArray display + MethodGroupItemParameter( + name = nm, + canonicalTypeTextForSorting = printCanonicalizedTypeName g denv tauTy, + display = display, + isOptional=isOptArg + )) - // Active pattern tags - | Item.ActivePatternCase apref -> - let v = apref.ActivePatternVal - let vTauTy = v.TauType - // REVIEW: use _cxs here - let (prettyTyparInst, prettyTy), _cxs = PrettyTypes.PrettifyInstAndType denv.g (item.TyparInstantiation, vTauTy) - let remarks = OutputFullName displayFullName pubpathOfValRef fullDisplayTextOfValRefAsLayout v - let layout = - wordL (tagText (FSComp.SR.typeInfoActiveRecognizer())) ^^ - wordL (tagActivePatternCase apref.DisplayName |> mkNav v.DefinitionRange) ^^ - RightL.colon ^^ - NicePrint.layoutType denv prettyTy - let layout = PrintUtilities.squashToWidth width layout + prettyTyparInst, prettyParams, prettyRetTyL, prettyConstraintsL - let tpsL = FormatTyparMapping denv prettyTyparInst + let PrettyParamsOfTypes g denv typarInst paramTys retTy = - let layout = toArray layout - let tpsL = List.map toArray tpsL - let remarks = toArray remarks - ToolTipElement.Single (layout, xml, tpsL, remarks=remarks) + // Prettify everything + let prettyTyparInst, (prettyParamTys, _prettyRetTy), (prettyParamTysL, prettyRetTyL), prettyConstraintsL = + NicePrint.prettyLayoutOfInstAndSig denv (typarInst, paramTys, retTy) - // F# exception names - | Item.ExnCase ecref -> - let layout = NicePrint.layoutExnDef denv infoReader ecref - let layout = PrintUtilities.squashToWidth width layout - let remarks = OutputFullName displayFullName pubpathOfTyconRef fullDisplayTextOfExnRefAsLayout ecref - let layout = toArray layout - let remarks = toArray remarks - ToolTipElement.Single (layout, xml, remarks=remarks) + // Remake the params using the prettified versions + let parameters = + (prettyParamTys, prettyParamTysL) + ||> List.map2 (fun paramTy tyL -> + let display = toArray tyL + MethodGroupItemParameter( + name = "", + canonicalTypeTextForSorting = printCanonicalizedTypeName g denv paramTy, + display = display, + isOptional=false + )) - | Item.RecdField rfinfo when rfinfo.TyconRef.IsFSharpException -> - let ty, _ = PrettyTypes.PrettifyType g rfinfo.FieldType - let id = rfinfo.DisplayName - let layout = - wordL (tagText (FSComp.SR.typeInfoArgument())) ^^ - wordL (tagParameter id) ^^ - RightL.colon ^^ - NicePrint.layoutType denv ty - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml, paramName = id) + // Return the results + prettyTyparInst, parameters, prettyRetTyL, prettyConstraintsL + - // F# record field names - | Item.RecdField rfinfo -> - let rfield = rfinfo.RecdField - let ty, _cxs = PrettyTypes.PrettifyType g rfinfo.FieldType - let layout = - NicePrint.layoutTyconRef denv rfinfo.TyconRef ^^ - SepL.dot ^^ - wordL (tagRecordField rfield.DisplayName |> mkNav rfield.DefinitionRange) ^^ - RightL.colon ^^ - NicePrint.layoutType denv ty ^^ - ( - match rfinfo.LiteralValue with - | None -> emptyL - | Some lit -> try WordL.equals ^^ NicePrint.layoutConst denv.g ty lit with _ -> emptyL - ) - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml) +#if !NO_TYPEPROVIDERS - | Item.UnionCaseField (ucinfo, fieldIndex) -> - let rfield = ucinfo.UnionCase.GetFieldByIndex(fieldIndex) - let fieldTy, _ = PrettyTypes.PrettifyType g rfield.rfield_type - let id = rfield.Id - let layout = - wordL (tagText (FSComp.SR.typeInfoArgument())) ^^ - wordL (tagParameter id.idText) ^^ - RightL.colon ^^ - NicePrint.layoutType denv fieldTy - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml, paramName = id.idText) + /// Get the set of static parameters associated with an item + let StaticParamsOfItem (infoReader:InfoReader) m denv item = + let amap = infoReader.amap + let g = infoReader.g + match item with + | ItemIsWithStaticArguments m g staticParameters -> + staticParameters + |> Array.map (fun sp -> + let ty = Import.ImportProvidedType amap m (sp.PApply((fun x -> x.ParameterType), m)) + let spKind = NicePrint.prettyLayoutOfType denv ty + let spName = sp.PUntaint((fun sp -> sp.Name), m) + let spOpt = sp.PUntaint((fun sp -> sp.IsOptional), m) + let display = (if spOpt then SepL.questionMark else emptyL) ^^ wordL (tagParameter spName) ^^ RightL.colon ^^ spKind + let display = toArray display + MethodGroupItemParameter( + name = spName, + canonicalTypeTextForSorting = showL spKind, + display = display, + //display = sprintf "%s%s: %s" (if spOpt then "?" else "") spName spKind, + isOptional=spOpt)) + | _ -> [| |] +#endif - // Not used - | Item.NewDef id -> - let layout = - wordL (tagText (FSComp.SR.typeInfoPatternVariable())) ^^ - wordL (tagUnknownEntity id.idText) - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml) + /// Get all the information about parameters and "prettify" the types by choosing nice type variable + /// names. This is similar to the other variations on "show me an item" code. This version is + /// is used when presenting groups of methods (see MethodGroup). It is possible these different + /// versions could be better unified. + let rec PrettyParamsAndReturnTypeOfItem (infoReader:InfoReader) m denv (item: ItemWithInst) = + let amap = infoReader.amap + let g = infoReader.g + let denv = { SimplerDisplayEnv denv with useColonForReturnType=true} + match item.Item with + | Item.Value vref -> - // .NET fields - | Item.ILField finfo -> - let layout = - wordL (tagText (FSComp.SR.typeInfoField())) ^^ - NicePrint.layoutType denv finfo.ApparentEnclosingAppType ^^ - SepL.dot ^^ - wordL (tagField finfo.FieldName) ^^ - RightL.colon ^^ - NicePrint.layoutType denv (finfo.FieldType(amap, m)) ^^ - ( - match finfo.LiteralValue with - | None -> emptyL - | Some v -> - WordL.equals ^^ - try NicePrint.layoutConst denv.g (finfo.FieldType(infoReader.amap, m)) (CheckExpressions.TcFieldInit m v) with _ -> emptyL - ) - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml) + let getPrettyParamsOfTypes() = + let vTauTy = vref.TauType + match tryDestFunTy denv.g vTauTy with + | ValueSome(arg, retTy) -> + let args = tryDestRefTupleTy denv.g arg + let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfTypes g denv item.TyparInstantiation args retTy + // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned + // for display as part of the method group + prettyParams, prettyRetTyL + | _ -> + let _prettyTyparInst, prettyTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] vTauTy + [], prettyTyL - // .NET events - | Item.Event einfo -> - let eventTy = PropTypeOfEventInfo infoReader m AccessibleFromSomewhere einfo - let eventTy, _cxs = PrettyTypes.PrettifyType g eventTy - let layout = - wordL (tagText (FSComp.SR.typeInfoEvent())) ^^ - NicePrint.layoutTyconRef denv einfo.ApparentEnclosingTyconRef ^^ - SepL.dot ^^ - wordL (tagEvent einfo.EventName) ^^ - RightL.colon ^^ - NicePrint.layoutType denv eventTy - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml) + match vref.ValReprInfo with + | None -> + // ValReprInfo = None i.e. in let bindings defined in types or in local functions + // in this case use old approach and return only information about types + getPrettyParamsOfTypes () - // F# and .NET properties - | Item.Property(_, pinfo :: _) -> - let layout = NicePrint.prettyLayoutOfPropInfoFreeStyle g amap m denv pinfo - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml) + | Some valReprInfo -> + // ValReprInfo will exist for top-level syntactic functions + // per spec: binding is considered to define a syntactic function if it is either a function or its immediate right-hand-side is a anonymous function + let _, argInfos, lastRetTy, _ = GetValReprTypeInFSharpForm g valReprInfo vref.Type m + match argInfos with + | [] -> + // handles cases like 'let foo = List.map' + getPrettyParamsOfTypes() + | firstCurriedArgInfo :: _ -> + // result 'paramDatas' collection corresponds to the first argument of curried function + // i.e. let func (a : int) (b : int) = a + b + // paramDatas will contain information about a and retTy will be: int -> int + // This is good enough as we don't provide ways to display info for the second curried argument + let firstCurriedParamDatas = + firstCurriedArgInfo + |> List.map ParamNameAndType.FromArgInfo + |> List.map (fun (ParamNameAndType(nmOpt, pty)) -> ParamData(false, false, false, NotOptional, NoCallerInfo, nmOpt, ReflectedArgInfo.None, pty)) - // Custom operations in queries - | Item.CustomOperation (customOpName, usageText, Some minfo) -> + // Adjust the return type so it only strips the first argument + let curriedRetTy = + match tryDestFunTy denv.g vref.TauType with + | ValueSome(_, retTy) -> retTy + | _ -> lastRetTy - // Build 'custom operation: where (bool) - // - // Calls QueryBuilder.Where' - let layout = - wordL (tagText (FSComp.SR.typeInfoCustomOperation())) ^^ - RightL.colon ^^ - ( - match usageText() with - | Some t -> wordL (tagText t) - | None -> - let argTys = ParamNameAndTypesOfUnaryCustomOperation g minfo |> List.map (fun (ParamNameAndType(_, ty)) -> ty) - let argTys, _ = PrettyTypes.PrettifyTypes g argTys - wordL (tagMethod customOpName) ^^ sepListL SepL.space (List.map (fun ty -> LeftL.leftParen ^^ NicePrint.layoutType denv ty ^^ SepL.rightParen) argTys) - ) ^^ - SepL.lineBreak ^^ SepL.lineBreak ^^ - wordL (tagText (FSComp.SR.typeInfoCallsWord())) ^^ - NicePrint.layoutTyconRef denv minfo.ApparentEnclosingTyconRef ^^ - SepL.dot ^^ - wordL (tagMethod minfo.DisplayName) + let _prettyTyparInst, prettyFirstCurriedParams, prettyCurriedRetTyL, prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation firstCurriedParamDatas curriedRetTy + + let prettyCurriedRetTyL = prettyCurriedRetTyL ^^ SepL.space ^^ prettyConstraintsL - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml) + prettyFirstCurriedParams, prettyCurriedRetTyL - // F# constructors and methods - | Item.CtorGroup(_, minfos) - | Item.MethodGroup(_, minfos, _) -> - FormatOverloadsToList infoReader m denv item minfos width - - // The 'fake' zero-argument constructors of .NET interfaces. - // This ideally should never appear in intellisense, but we do get here in repros like: - // type IFoo = abstract F : int - // type II = IFoo // remove 'type II = ' and quickly hover over IFoo before it gets squiggled for 'invalid use of interface type' - // and in that case we'll just show the interface type name. - | Item.FakeInterfaceCtor ty -> - let ty, _ = PrettyTypes.PrettifyType g ty - let layout = NicePrint.layoutTyconRef denv (tcrefOfAppTy g ty) - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single(layout, xml) - - // The 'fake' representation of constructors of .NET delegate types - | Item.DelegateCtor delTy -> - let delTy, _cxs = PrettyTypes.PrettifyType g delTy - let (SigOfFunctionForDelegate(_, _, _, delFuncTy)) = GetSigOfFunctionForDelegate infoReader delTy m AccessibleFromSomewhere - let layout = - NicePrint.layoutTyconRef denv (tcrefOfAppTy g delTy) ^^ - LeftL.leftParen ^^ - NicePrint.layoutType denv delFuncTy ^^ - RightL.rightParen - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single(layout, xml) + | Item.UnionCase(ucinfo, _) -> + let prettyParams = + match ucinfo.UnionCase.RecdFields with + | [f] -> [PrettyParamOfUnionCaseField g denv NicePrint.isGeneratedUnionCaseField -1 f] + | fs -> fs |> List.mapi (PrettyParamOfUnionCaseField g denv NicePrint.isGeneratedUnionCaseField) + let unionTy = generalizedTyconRef g ucinfo.TyconRef + let rtyL = NicePrint.layoutType denv unionTy + prettyParams, rtyL - // Types. - | Item.Types(_, TType_app(tcref, _, _) :: _) - | Item.UnqualifiedType (tcref :: _) -> - let denv = { denv with - // tooltips are space-constrained, so use shorter names - shortTypeNames = true - // tooltips are space-constrained, so don't include xml doc comments - // on types/members. The doc comments for the actual member will still - // be shown in the tip. - showDocumentation = false } - let layout = NicePrint.layoutTyconDefn denv infoReader ad m (* width *) tcref.Deref - let layout = PrintUtilities.squashToWidth width layout - let remarks = OutputFullName displayFullName pubpathOfTyconRef fullDisplayTextOfTyconRefAsLayout tcref - let layout = toArray layout - let remarks = toArray remarks - ToolTipElement.Single (layout, xml, remarks=remarks) + | Item.ActivePatternCase(apref) -> + let v = apref.ActivePatternVal + let vTauTy = v.TauType + let args, resTy = stripFunTy denv.g vTauTy - // Type variables - | Item.TypeVar (_, typar) -> - let layout = NicePrint.prettyLayoutOfTypar denv typar - let layout = PrintUtilities.squashToWidth width layout - ToolTipElement.Single (toArray layout, xml) + let apinfo = Option.get (TryGetActivePatternInfo v) + let aparity = apinfo.ActiveTags.Length + + let caseTy = if aparity <= 1 then resTy else (argsOfAppTy g resTy)[apref.CaseIndex] - // Traits - | Item.Trait traitInfo -> - let denv = { denv with shortConstraints = false} - let layout = NicePrint.prettyLayoutOfTrait denv traitInfo - let layout = PrintUtilities.squashToWidth width layout - ToolTipElement.Single (toArray layout, xml) + let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfTypes g denv item.TyparInstantiation args caseTy + // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned + // for display as part of the method group + prettyParams, prettyRetTyL - // F# Modules and namespaces - | Item.ModuleOrNamespaces(modref :: _ as modrefs) -> - //let os = StringBuilder() - let modrefs = modrefs |> RemoveDuplicateModuleRefs - let definiteNamespace = modrefs |> List.forall (fun modref -> modref.IsNamespace) - let kind = - if definiteNamespace then FSComp.SR.typeInfoNamespace() - elif modrefs |> List.forall (fun modref -> modref.IsModule) then FSComp.SR.typeInfoModule() - else FSComp.SR.typeInfoNamespaceOrModule() - - let layout = - wordL (tagKeyword kind) ^^ - (if definiteNamespace then tagNamespace (fullDisplayTextOfModRef modref) else (tagModule modref.DemangledModuleOrNamespaceName) - |> mkNav modref.DefinitionRange - |> wordL) - if not definiteNamespace then - let namesToAdd = - ([], modrefs) - ||> Seq.fold (fun st modref -> - match fullDisplayTextOfParentOfModRef modref with - | ValueSome txt -> txt :: st - | _ -> st) - |> Seq.mapi (fun i x -> i, x) - |> Seq.toList - let layout = - layout ^^ - ( - if not (List.isEmpty namesToAdd) then - SepL.lineBreak ^^ - List.fold ( fun s (i, txt) -> - s ^^ - SepL.lineBreak ^^ - wordL (tagText ((if i = 0 then FSComp.SR.typeInfoFromFirst else FSComp.SR.typeInfoFromNext) txt)) - ) emptyL namesToAdd - else - emptyL - ) - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml) - else - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml) - - | Item.AnonRecdField(anon, argTys, i, _) -> - let argTy = argTys[i] - let nm = anon.DisplayNameByIdx i - let argTy, _ = PrettyTypes.PrettifyType g argTy - let layout = - wordL (tagText (FSComp.SR.typeInfoAnonRecdField())) ^^ - wordL (tagRecordField nm) ^^ - RightL.colon ^^ - NicePrint.layoutType denv argTy - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, FSharpXmlDoc.None) - - // Named parameters - | Item.ArgName (Some id, argTy, _, _) -> - let argTy, _ = PrettyTypes.PrettifyType g argTy - let layout = - wordL (tagText (FSComp.SR.typeInfoArgument())) ^^ - wordL (tagParameter id.idText) ^^ - RightL.colon ^^ - NicePrint.layoutType denv argTy - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml, paramName = id.idText) - - | Item.SetterArg (_, item) -> - FormatItemDescriptionToToolTipElement displayFullName infoReader ad m denv (ItemWithNoInst item) width + | Item.ExnCase ecref -> + let prettyParams = ecref |> recdFieldsOfExnDefRef |> List.mapi (PrettyParamOfUnionCaseField g denv NicePrint.isGeneratedExceptionField) + let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] g.exn_ty + prettyParams, prettyRetTyL - | Item.ArgName (None, _, _, _) - - // TODO: give a decent tooltip for implicit operators that include the resolution of the operator - // - //type C() = - // static member (++++++) (x: C, y: C) = C() - // - //let f (x: C) = - // x ++++++ x - // - // Here hovering over "++++++" in "f" could give a tooltip saying what the thing is and what it has resolved to. - // - // - | Item.ImplicitOp _ + | Item.RecdField rfinfo -> + let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] rfinfo.FieldType + [], prettyRetTyL - // TODO: consider why we aren't getting Item.Types for generic type parameters - // let F<'T>() = new System.Collections.Generic.List<'T>() - | Item.Types (_, [TType_var _]) + | Item.AnonRecdField(_anonInfo, tys, i, _) -> + let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] tys[i] + [], prettyRetTyL - // TODO: consider why we aren't getting Item.Types for units of measure - | Item.Types (_, [TType_measure _]) + | Item.ILField finfo -> + let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] (finfo.FieldType(amap, m)) + [], prettyRetTyL - // TODO: consider whether we ever get Item.Types with more than one element - | Item.Types (_, _ :: _ :: _) + | Item.Event einfo -> + let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] (PropTypeOfEventInfo infoReader m AccessibleFromSomewhere einfo) + [], prettyRetTyL - // We don't expect Item.Types with an anonymous record type, function types etc. - | Item.Types (_, [TType_anon _]) - | Item.Types (_, [TType_fun _]) - | Item.Types (_, [TType_forall _]) - | Item.Types (_, [TType_tuple _]) - | Item.Types (_, [TType_ucase _]) + | Item.Property(_, pinfo :: _) -> + let paramDatas = pinfo.GetParamDatas(amap, m) + let propTy = pinfo.GetPropertyType(amap, m) - // We don't expect these cases - | Item.Types (_, []) - | Item.Property (_, []) - | Item.UnqualifiedType [] - | Item.ModuleOrNamespaces [] - | Item.CustomOperation (_, _, None) -> ToolTipElement.None + let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation paramDatas propTy + // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned + // for display as part of the method group + prettyParams, prettyRetTyL - /// Format the structured version of a tooltip for an item - let FormatStructuredDescriptionOfItem isDecl infoReader ad m denv item width = - DiagnosticsScope.Protect m - (fun () -> FormatItemDescriptionToToolTipElement isDecl infoReader ad m denv item width) - (fun err -> ToolTipElement.CompositionError err) + | Item.CtorGroup(_, minfo :: _) + | Item.MethodGroup(_, minfo :: _, _) -> + let paramDatas = minfo.GetParamDatas(amap, m, minfo.FormalMethodInst) |> List.head + let retTy = minfo.GetFSharpReturnType(amap, m, minfo.FormalMethodInst) + let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation paramDatas retTy + // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned + // for display as part of the method group + prettyParams, prettyRetTyL -/// Represents one parameter for one method (or other item) in a group. -[] -type MethodGroupItemParameter(name: string, canonicalTypeTextForSorting: string, display: TaggedText[], isOptional: bool) = + | Item.Trait traitInfo -> + let paramDatas = + [ for pty in traitInfo.GetLogicalArgumentTypes(g) do + ParamData(false, false, false, OptionalArgInfo.NotOptional, CallerInfo.NoCallerInfo, None, ReflectedArgInfo.None, pty) ] + let retTy = traitInfo.GetReturnType(g) + let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation paramDatas retTy + prettyParams, prettyRetTyL - /// The name of the parameter. - member _.ParameterName = name + | Item.CustomBuilder (_, vref) -> + PrettyParamsAndReturnTypeOfItem infoReader m denv { item with Item = Item.Value vref } - /// A key that can be used for sorting the parameters, used to help sort overloads. - member _.CanonicalTypeTextForSorting = canonicalTypeTextForSorting + | Item.TypeVar _ -> + [], emptyL - /// The text to display for the parameter including its name, its type and visual indicators of other - /// information such as whether it is optional. - member _.Display = display + | Item.CustomOperation (_, usageText, Some minfo) -> + match usageText() with + | None -> + let argNamesAndTys = ParamNameAndTypesOfUnaryCustomOperation g minfo + let argTys, _ = PrettyTypes.PrettifyTypes g (argNamesAndTys |> List.map (fun (ParamNameAndType(_, ty)) -> ty)) + let paramDatas = (argNamesAndTys, argTys) ||> List.map2 (fun (ParamNameAndType(nmOpt, _)) argTy -> ParamData(false, false, false, NotOptional, NoCallerInfo, nmOpt, ReflectedArgInfo.None, argTy)) + let retTy = minfo.GetFSharpReturnType(amap, m, minfo.FormalMethodInst) + let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation paramDatas retTy - /// Is the parameter optional - member _.IsOptional = isOptional + // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned + // for display as part of the method group + prettyParams, prettyRetTyL -[] -module internal DescriptionListsImpl = + | Some _ -> + let retTy = minfo.GetFSharpReturnType(amap, m, minfo.FormalMethodInst) + let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] retTy + [], prettyRetTyL // no parameter data available for binary operators like 'zip', 'join' and 'groupJoin' since they use bespoke syntax - let isFunction g ty = - let _, tauTy = tryDestForallTy g ty - isFunTy g tauTy - - let printCanonicalizedTypeName g (denv:DisplayEnv) tauTy = - // get rid of F# abbreviations and such - let strippedTy = stripTyEqnsWrtErasure EraseAll g tauTy - // pretend no namespaces are open - let denv = denv.SetOpenPaths([]) - // now printing will see a .NET-like canonical representation, that is good for sorting overloads into a reasonable order (see bug 94520) - NicePrint.stringOfTy denv strippedTy + | Item.FakeInterfaceCtor ty -> + let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] ty + [], prettyRetTyL - let PrettyParamOfRecdField g denv (f: RecdField) = - let display = NicePrint.prettyLayoutOfType denv f.FormalType - let display = toArray display - MethodGroupItemParameter( - name = f.DisplayNameCore, - canonicalTypeTextForSorting = printCanonicalizedTypeName g denv f.FormalType, - display = display, - isOptional=false) - - let PrettyParamOfUnionCaseField g denv isGenerated (i: int) (f: RecdField) = - let initial = PrettyParamOfRecdField g denv f - let display = - if isGenerated i f then - initial.Display - else - let display = NicePrint.layoutOfParamData denv (ParamData(false, false, false, NotOptional, NoCallerInfo, Some f.Id, ReflectedArgInfo.None, f.FormalType)) - toArray display + | Item.DelegateCtor delTy -> + let (SigOfFunctionForDelegate(_, _, _, delFuncTy)) = GetSigOfFunctionForDelegate infoReader delTy m AccessibleFromSomewhere - MethodGroupItemParameter( - name=initial.ParameterName, - canonicalTypeTextForSorting=initial.CanonicalTypeTextForSorting, - display=display, - isOptional=false) + // No need to pass more generic type information in here since the instanitations have already been applied + let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation [ParamData(false, false, false, NotOptional, NoCallerInfo, None, ReflectedArgInfo.None, delFuncTy)] delTy - let ParamOfParamData g denv (ParamData(_isParamArrayArg, _isInArg, _isOutArg, optArgInfo, _callerInfo, nmOpt, _reflArgInfo, pty) as paramData) = - let display = NicePrint.layoutOfParamData denv paramData - let display = toArray display - MethodGroupItemParameter( - name = (match nmOpt with None -> "" | Some pn -> pn.idText), - canonicalTypeTextForSorting = printCanonicalizedTypeName g denv pty, - display = display, - isOptional=optArgInfo.IsOptional) + // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned + // for display as part of the method group + prettyParams, prettyRetTyL - // TODO this code is similar to NicePrint.fs:formatParamDataToBuffer, refactor or figure out why different? - let PrettyParamsOfParamDatas g denv typarInst (paramDatas:ParamData list) paramTy = - let paramInfo, paramTypes = - paramDatas - |> List.map (fun (ParamData(isParamArrayArg, _isInArg, _isOutArg, optArgInfo, _callerInfo, nmOpt, _reflArgInfo, pty)) -> - let isOptArg = optArgInfo.IsOptional - match nmOpt, isOptArg, tryDestOptionTy denv.g pty with - // Layout an optional argument - | Some id, true, ptyOpt -> - let nm = id.idText - // detect parameter type, if ptyOpt is None - this is .NET style optional argument - let pty = match ptyOpt with ValueSome x -> x | _ -> pty - (nm, isOptArg, SepL.questionMark ^^ (wordL (tagParameter nm))), pty - // Layout an unnamed argument - | None, _, _ -> - ("", isOptArg, emptyL), pty - // Layout a named argument - | Some id, _, _ -> - let nm = id.idText - let prefix = - if isParamArrayArg then - NicePrint.PrintUtilities.layoutBuiltinAttribute denv denv.g.attrib_ParamArrayAttribute ^^ - wordL (tagParameter nm) ^^ - RightL.colon - //sprintf "%s %s: " (NicePrint.PrintUtilities.layoutBuiltinAttribute denv denv.g.attrib_ParamArrayAttribute |> showL) nm - else - wordL (tagParameter nm) ^^ - RightL.colon - //sprintf "%s: " nm - (nm, isOptArg, prefix), pty) - |> List.unzip - - // Prettify everything - let prettyTyparInst, (prettyParamTys, _prettyRetTy), (prettyParamTysL, prettyRetTyL), prettyConstraintsL = - NicePrint.prettyLayoutOfInstAndSig denv (typarInst, paramTypes, paramTy) - - // Remake the params using the prettified versions - let prettyParams = - (paramInfo, prettyParamTys, prettyParamTysL) |||> List.map3 (fun (nm, isOptArg, paramPrefix) tauTy tyL -> - let display = paramPrefix ^^ tyL - let display = toArray display - MethodGroupItemParameter( - name = nm, - canonicalTypeTextForSorting = printCanonicalizedTypeName g denv tauTy, - display = display, - isOptional=isOptArg - )) - - prettyTyparInst, prettyParams, prettyRetTyL, prettyConstraintsL - - let PrettyParamsOfTypes g denv typarInst paramTys retTy = - - // Prettify everything - let prettyTyparInst, (prettyParamTys, _prettyRetTy), (prettyParamTysL, prettyRetTyL), prettyConstraintsL = - NicePrint.prettyLayoutOfInstAndSig denv (typarInst, paramTys, retTy) - - // Remake the params using the prettified versions - let parameters = - (prettyParamTys, prettyParamTysL) - ||> List.map2 (fun paramTy tyL -> - let display = toArray tyL - MethodGroupItemParameter( - name = "", - canonicalTypeTextForSorting = printCanonicalizedTypeName g denv paramTy, - display = display, - isOptional=false - )) - - // Return the results - prettyTyparInst, parameters, prettyRetTyL, prettyConstraintsL - - -#if !NO_TYPEPROVIDERS - - /// Get the set of static parameters associated with an item - let StaticParamsOfItem (infoReader:InfoReader) m denv item = - let amap = infoReader.amap - let g = infoReader.g - match item with - | ItemIsWithStaticArguments m g staticParameters -> - staticParameters - |> Array.map (fun sp -> - let ty = Import.ImportProvidedType amap m (sp.PApply((fun x -> x.ParameterType), m)) - let spKind = NicePrint.prettyLayoutOfType denv ty - let spName = sp.PUntaint((fun sp -> sp.Name), m) - let spOpt = sp.PUntaint((fun sp -> sp.IsOptional), m) - let display = (if spOpt then SepL.questionMark else emptyL) ^^ wordL (tagParameter spName) ^^ RightL.colon ^^ spKind - let display = toArray display - MethodGroupItemParameter( - name = spName, - canonicalTypeTextForSorting = showL spKind, - display = display, - //display = sprintf "%s%s: %s" (if spOpt then "?" else "") spName spKind, - isOptional=spOpt)) - | _ -> [| |] -#endif - - /// Get all the information about parameters and "prettify" the types by choosing nice type variable - /// names. This is similar to the other variations on "show me an item" code. This version is - /// is used when presenting groups of methods (see MethodGroup). It is possible these different - /// versions could be better unified. - let rec PrettyParamsAndReturnTypeOfItem (infoReader:InfoReader) m denv (item: ItemWithInst) = - let amap = infoReader.amap - let g = infoReader.g - let denv = { SimplerDisplayEnv denv with useColonForReturnType=true} - match item.Item with - | Item.Value vref -> - - let getPrettyParamsOfTypes() = - let vTauTy = vref.TauType - match tryDestFunTy denv.g vTauTy with - | ValueSome(arg, retTy) -> - let args = tryDestRefTupleTy denv.g arg - let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfTypes g denv item.TyparInstantiation args retTy - // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned - // for display as part of the method group - prettyParams, prettyRetTyL - | _ -> - let _prettyTyparInst, prettyTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] vTauTy - [], prettyTyL - - match vref.ValReprInfo with - | None -> - // ValReprInfo = None i.e. in let bindings defined in types or in local functions - // in this case use old approach and return only information about types - getPrettyParamsOfTypes () - - | Some valReprInfo -> - // ValReprInfo will exist for top-level syntactic functions - // per spec: binding is considered to define a syntactic function if it is either a function or its immediate right-hand-side is a anonymous function - let _, argInfos, lastRetTy, _ = GetValReprTypeInFSharpForm g valReprInfo vref.Type m - match argInfos with - | [] -> - // handles cases like 'let foo = List.map' - getPrettyParamsOfTypes() - | firstCurriedArgInfo :: _ -> - // result 'paramDatas' collection corresponds to the first argument of curried function - // i.e. let func (a : int) (b : int) = a + b - // paramDatas will contain information about a and retTy will be: int -> int - // This is good enough as we don't provide ways to display info for the second curried argument - let firstCurriedParamDatas = - firstCurriedArgInfo - |> List.map ParamNameAndType.FromArgInfo - |> List.map (fun (ParamNameAndType(nmOpt, pty)) -> ParamData(false, false, false, NotOptional, NoCallerInfo, nmOpt, ReflectedArgInfo.None, pty)) - - // Adjust the return type so it only strips the first argument - let curriedRetTy = - match tryDestFunTy denv.g vref.TauType with - | ValueSome(_, retTy) -> retTy - | _ -> lastRetTy - - let _prettyTyparInst, prettyFirstCurriedParams, prettyCurriedRetTyL, prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation firstCurriedParamDatas curriedRetTy - - let prettyCurriedRetTyL = prettyCurriedRetTyL ^^ SepL.space ^^ prettyConstraintsL - - prettyFirstCurriedParams, prettyCurriedRetTyL - - | Item.UnionCase(ucinfo, _) -> - let prettyParams = - match ucinfo.UnionCase.RecdFields with - | [f] -> [PrettyParamOfUnionCaseField g denv NicePrint.isGeneratedUnionCaseField -1 f] - | fs -> fs |> List.mapi (PrettyParamOfUnionCaseField g denv NicePrint.isGeneratedUnionCaseField) - let unionTy = generalizedTyconRef g ucinfo.TyconRef - let rtyL = NicePrint.layoutType denv unionTy - prettyParams, rtyL - - | Item.ActivePatternCase(apref) -> - let v = apref.ActivePatternVal - let vTauTy = v.TauType - let args, resTy = stripFunTy denv.g vTauTy - - let apinfo = Option.get (TryGetActivePatternInfo v) - let aparity = apinfo.ActiveTags.Length - - let caseTy = if aparity <= 1 then resTy else (argsOfAppTy g resTy)[apref.CaseIndex] - - let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfTypes g denv item.TyparInstantiation args caseTy - // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned - // for display as part of the method group - prettyParams, prettyRetTyL - - | Item.ExnCase ecref -> - let prettyParams = ecref |> recdFieldsOfExnDefRef |> List.mapi (PrettyParamOfUnionCaseField g denv NicePrint.isGeneratedExceptionField) - let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] g.exn_ty - prettyParams, prettyRetTyL - - | Item.RecdField rfinfo -> - let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] rfinfo.FieldType - [], prettyRetTyL - - | Item.AnonRecdField(_anonInfo, tys, i, _) -> - let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] tys[i] - [], prettyRetTyL - - | Item.ILField finfo -> - let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] (finfo.FieldType(amap, m)) - [], prettyRetTyL - - | Item.Event einfo -> - let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] (PropTypeOfEventInfo infoReader m AccessibleFromSomewhere einfo) - [], prettyRetTyL - - | Item.Property(_, pinfo :: _) -> - let paramDatas = pinfo.GetParamDatas(amap, m) - let propTy = pinfo.GetPropertyType(amap, m) - - let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation paramDatas propTy - // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned - // for display as part of the method group - prettyParams, prettyRetTyL - - | Item.CtorGroup(_, minfo :: _) - | Item.MethodGroup(_, minfo :: _, _) -> - let paramDatas = minfo.GetParamDatas(amap, m, minfo.FormalMethodInst) |> List.head - let retTy = minfo.GetFSharpReturnType(amap, m, minfo.FormalMethodInst) - let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation paramDatas retTy - // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned - // for display as part of the method group - prettyParams, prettyRetTyL - - | Item.Trait traitInfo -> - let paramDatas = - [ for pty in traitInfo.GetLogicalArgumentTypes(g) do - ParamData(false, false, false, OptionalArgInfo.NotOptional, CallerInfo.NoCallerInfo, None, ReflectedArgInfo.None, pty) ] - let retTy = traitInfo.GetReturnType(g) - let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation paramDatas retTy - prettyParams, prettyRetTyL - - | Item.CustomBuilder (_, vref) -> - PrettyParamsAndReturnTypeOfItem infoReader m denv { item with Item = Item.Value vref } - - | Item.TypeVar _ -> - [], emptyL - - | Item.CustomOperation (_, usageText, Some minfo) -> - match usageText() with - | None -> - let argNamesAndTys = ParamNameAndTypesOfUnaryCustomOperation g minfo - let argTys, _ = PrettyTypes.PrettifyTypes g (argNamesAndTys |> List.map (fun (ParamNameAndType(_, ty)) -> ty)) - let paramDatas = (argNamesAndTys, argTys) ||> List.map2 (fun (ParamNameAndType(nmOpt, _)) argTy -> ParamData(false, false, false, NotOptional, NoCallerInfo, nmOpt, ReflectedArgInfo.None, argTy)) - let retTy = minfo.GetFSharpReturnType(amap, m, minfo.FormalMethodInst) - let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation paramDatas retTy - - // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned - // for display as part of the method group - prettyParams, prettyRetTyL - - | Some _ -> - let retTy = minfo.GetFSharpReturnType(amap, m, minfo.FormalMethodInst) - let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] retTy - [], prettyRetTyL // no parameter data available for binary operators like 'zip', 'join' and 'groupJoin' since they use bespoke syntax - - | Item.FakeInterfaceCtor ty -> - let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] ty - [], prettyRetTyL - - | Item.DelegateCtor delTy -> - let (SigOfFunctionForDelegate(_, _, _, delFuncTy)) = GetSigOfFunctionForDelegate infoReader delTy m AccessibleFromSomewhere - - // No need to pass more generic type information in here since the instanitations have already been applied - let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation [ParamData(false, false, false, NotOptional, NoCallerInfo, None, ReflectedArgInfo.None, delFuncTy)] delTy - - // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned - // for display as part of the method group - prettyParams, prettyRetTyL - - | Item.CustomOperation _ // TODO: consider whether this should report parameter help - | Item.ActivePatternResult _ // TODO: consider whether this should report parameter help - | Item.UnqualifiedType _ - | Item.UnionCaseField _ - | Item.Types _ - | Item.SetterArg _ - | Item.NewDef _ - | Item.ModuleOrNamespaces _ - | Item.ImplicitOp _ - | Item.ArgName _ - | Item.MethodGroup(_, [], _) - | Item.CtorGroup(_,[]) - | Item.Property(_,[]) -> - [], emptyL + | Item.CustomOperation _ // TODO: consider whether this should report parameter help + | Item.ActivePatternResult _ // TODO: consider whether this should report parameter help + | Item.UnqualifiedType _ + | Item.UnionCaseField _ + | Item.Types _ + | Item.SetterArg _ + | Item.NewDef _ + | Item.ModuleOrNamespaces _ + | Item.ImplicitOp _ + | Item.ArgName _ + | Item.MethodGroup(_, [], _) + | Item.CtorGroup(_,[]) + | Item.Property(_,[]) -> + [], emptyL /// Compute the index of the VS glyph shown with an item in the Intellisense menu @@ -1020,6 +580,552 @@ module internal DescriptionListsImpl = | Item.UnqualifiedType _ | Item.ActivePatternResult _ -> [] + +[] +module DeclarationListHelpers = + let mutable ToolTipFault = None + + let emptyToolTip = ToolTipText [] + + /// Generate the structured tooltip for a method info + let FormatOverloadsToList (infoReader: InfoReader) m denv (item: ItemWithInst) minfos symbol (width: int option) : ToolTipElement = + ToolTipFault |> Option.iter (fun msg -> + let exn = Error((0, msg), range.Zero) + let ph = PhasedDiagnostic.Create(exn, BuildPhase.TypeCheck) + simulateError ph) + + let layouts = + [ for minfo in minfos -> + let prettyTyparInst, layout = NicePrint.prettyLayoutOfMethInfoFreeStyle infoReader m denv item.TyparInstantiation minfo + let xml = GetXmlCommentForMethInfoItem infoReader m item.Item minfo + let tpsL = FormatTyparMapping denv prettyTyparInst + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + let tpsL = List.map toArray tpsL + ToolTipElementData.Create(layout, xml, tpsL, ?symbol = symbol) ] + + ToolTipElement.Group layouts + + let CompletionItemDisplayPartialEquality g = + let itemComparer = ItemDisplayPartialEquality g + + { new IPartialEqualityComparer with + member x.InEqualityRelation item = itemComparer.InEqualityRelation item.Item + member x.Equals(item1, item2) = itemComparer.Equals(item1.Item, item2.Item) + member x.GetHashCode item = itemComparer.GetHashCode(item.Item) } + + /// Remove all duplicate items + let RemoveDuplicateCompletionItems g items = + if isNil items then items else + items |> IPartialEqualityComparer.partialDistinctBy (CompletionItemDisplayPartialEquality g) + + /// Filter types that are explicitly suppressed from the IntelliSense (such as uppercase "FSharpList", "Option", etc.) + let RemoveExplicitlySuppressedCompletionItems (g: TcGlobals) (items: CompletionItem list) = + items |> List.filter (fun item -> not (IsExplicitlySuppressed g item.Item)) + + // Remove items containing the same module references + let RemoveDuplicateModuleRefs modrefs = + modrefs |> IPartialEqualityComparer.partialDistinctBy + { new IPartialEqualityComparer with + member x.InEqualityRelation _ = true + member x.Equals(item1, item2) = (fullDisplayTextOfModRef item1 = fullDisplayTextOfModRef item2) + member x.GetHashCode item = hash item.Stamp } + + let OutputFullName displayFullName ppF fnF r = + // Only display full names in quick info, not declaration lists or method lists + if not displayFullName then + match ppF r with + | None -> emptyL + | Some _ -> wordL (tagText (FSComp.SR.typeInfoFullName())) ^^ RightL.colon ^^ (fnF r) + else emptyL + + let pubpathOfValRef (v: ValRef) = v.PublicPath + + let pubpathOfTyconRef (x: TyconRef) = x.PublicPath + + let isFunction g ty = + let _, tauTy = tryDestForallTy g ty + isFunTy g tauTy + + /// Compute the index of the VS glyph shown with an item in the Intellisense menu + let GlyphOfItem(denv, item) : FSharpGlyph = + /// Find the glyph for the given representation. + let reprToGlyph repr = + match repr with + | TFSharpObjectRepr om -> + match om.fsobjmodel_kind with + | TFSharpClass -> FSharpGlyph.Class + | TFSharpInterface -> FSharpGlyph.Interface + | TFSharpStruct -> FSharpGlyph.Struct + | TFSharpDelegate _ -> FSharpGlyph.Delegate + | TFSharpEnum -> FSharpGlyph.Enum + | TFSharpRecdRepr _ -> FSharpGlyph.Type + | TFSharpUnionRepr _ -> FSharpGlyph.Union + | TILObjectRepr (TILObjectReprData (_, _, td)) -> + if td.IsClass then FSharpGlyph.Class + elif td.IsStruct then FSharpGlyph.Struct + elif td.IsInterface then FSharpGlyph.Interface + elif td.IsEnum then FSharpGlyph.Enum + else FSharpGlyph.Delegate + | TAsmRepr _ -> FSharpGlyph.Typedef + | TMeasureableRepr _-> FSharpGlyph.Typedef +#if !NO_TYPEPROVIDERS + | TProvidedTypeRepr _-> FSharpGlyph.Typedef + | TProvidedNamespaceRepr _-> FSharpGlyph.Typedef +#endif + | TNoRepr -> FSharpGlyph.Class + + /// Find the glyph for the given type representation. + let typeToGlyph ty = + match tryTcrefOfAppTy denv.g ty with + | ValueSome tcref -> tcref.TypeReprInfo |> reprToGlyph + | _ -> + if isStructTupleTy denv.g ty then FSharpGlyph.Struct + elif isRefTupleTy denv.g ty then FSharpGlyph.Class + elif isFunction denv.g ty then FSharpGlyph.Delegate + elif isTyparTy denv.g ty then FSharpGlyph.Struct + else FSharpGlyph.Typedef + + // This may explore assemblies that are not in the reference set, + // e.g. for type abbreviations to types not in the reference set. + // In this case just use GlyphMajor.Class. + protectAssemblyExploration FSharpGlyph.Class (fun () -> + match item with + | Item.Value(vref) | Item.CustomBuilder (_, vref) -> + if isFunction denv.g vref.Type then FSharpGlyph.Method + elif vref.LiteralValue.IsSome then FSharpGlyph.Constant + else FSharpGlyph.Variable + | Item.Types(_, ty :: _) -> typeToGlyph (stripTyEqns denv.g ty) + | Item.UnionCase _ + | Item.ActivePatternResult _ + | Item.ImplicitOp _ + | Item.ActivePatternCase _ -> FSharpGlyph.EnumMember + | Item.ExnCase _ -> FSharpGlyph.Exception + | Item.AnonRecdField _ -> FSharpGlyph.Field + | Item.RecdField _ -> FSharpGlyph.Field + | Item.UnionCaseField _ -> FSharpGlyph.Field + | Item.ILField _ -> FSharpGlyph.Field + | Item.Event _ -> FSharpGlyph.Event + | Item.Property _ -> FSharpGlyph.Property + | Item.CtorGroup _ + | Item.DelegateCtor _ + | Item.FakeInterfaceCtor _ + | Item.CustomOperation _ -> FSharpGlyph.Method + | Item.MethodGroup (_, minfos, _) when minfos |> List.forall (fun minfo -> minfo.IsExtensionMember) -> FSharpGlyph.ExtensionMethod + | Item.MethodGroup _ -> FSharpGlyph.Method + | Item.Trait _ -> FSharpGlyph.Method + | Item.TypeVar _ -> FSharpGlyph.TypeParameter + | Item.Types _ -> FSharpGlyph.Class + | Item.UnqualifiedType (tcref :: _) -> + if tcref.IsEnumTycon || tcref.IsILEnumTycon then FSharpGlyph.Enum + elif tcref.IsFSharpException then FSharpGlyph.Exception + elif tcref.IsFSharpDelegateTycon then FSharpGlyph.Delegate + elif tcref.IsFSharpInterfaceTycon then FSharpGlyph.Interface + elif tcref.IsFSharpStructOrEnumTycon then FSharpGlyph.Struct + elif tcref.IsModule then FSharpGlyph.Module + elif tcref.IsNamespace then FSharpGlyph.NameSpace + elif tcref.IsUnionTycon then FSharpGlyph.Union + elif tcref.IsILTycon then + let (TILObjectReprData (_, _, tydef)) = tcref.ILTyconInfo + if tydef.IsInterface then FSharpGlyph.Interface + elif tydef.IsDelegate then FSharpGlyph.Delegate + elif tydef.IsEnum then FSharpGlyph.Enum + elif tydef.IsStruct then FSharpGlyph.Struct + else FSharpGlyph.Class + else FSharpGlyph.Class + | Item.ModuleOrNamespaces(modref :: _) -> + if modref.IsNamespace then FSharpGlyph.NameSpace else FSharpGlyph.Module + | Item.NewDef _ + | Item.ArgName _ + | Item.SetterArg _ -> FSharpGlyph.Variable + + // These empty lists are not expected to occur + | Item.ModuleOrNamespaces [] + | Item.UnqualifiedType [] -> + FSharpGlyph.Error + ) + + /// Output the quick info information of a language item + let rec FormatItemDescriptionToToolTipElement displayFullName (infoReader: InfoReader) ad m denv (item: ItemWithInst) symbol (width: int option) = + let g = infoReader.g + let amap = infoReader.amap + let denv = SimplerDisplayEnv denv + let xml = GetXmlCommentForItem infoReader m item.Item + + match item.Item with + | Item.ImplicitOp(_, { contents = Some(TraitConstraintSln.FSMethSln(vref=vref)) }) -> + // operator with solution + FormatItemDescriptionToToolTipElement displayFullName infoReader ad m denv { item with Item = Item.Value vref } symbol width + + | Item.Value vref | Item.CustomBuilder (_, vref) -> + let prettyTyparInst, resL = NicePrint.layoutQualifiedValOrMember denv infoReader item.TyparInstantiation vref + let remarks = OutputFullName displayFullName pubpathOfValRef fullDisplayTextOfValRefAsLayout vref + let tpsL = FormatTyparMapping denv prettyTyparInst + let tpsL = List.map toArray tpsL + let resL = PrintUtilities.squashToWidth width resL + let resL = toArray resL + let remarks = toArray remarks + ToolTipElement.Single(resL, xml, tpsL, remarks=remarks, ?symbol = symbol) + + // Union tags (constructors) + | Item.UnionCase(ucinfo, _) -> + let uc = ucinfo.UnionCase + let unionTy = generalizedTyconRef g ucinfo.TyconRef + let recd = uc.RecdFields + let layout = + wordL (tagText (FSComp.SR.typeInfoUnionCase())) ^^ + NicePrint.layoutTyconRef denv ucinfo.TyconRef ^^ + sepL (tagPunctuation ".") ^^ + wordL (tagUnionCase (ConvertValLogicalNameToDisplayNameCore uc.Id.idText) |> mkNav uc.DefinitionRange) ^^ + RightL.colon ^^ + (if List.isEmpty recd then emptyL else NicePrint.layoutUnionCases denv infoReader ucinfo.TyconRef recd ^^ WordL.arrow) ^^ + NicePrint.layoutType denv unionTy + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, ?symbol = symbol) + + // Active pattern tag inside the declaration (result) + | Item.ActivePatternResult(apinfo, ty, idx, _) -> + let items = apinfo.ActiveTags + let layout = + wordL (tagText (FSComp.SR.typeInfoActivePatternResult())) ^^ + wordL (tagActivePatternResult (List.item idx items) |> mkNav apinfo.Range) ^^ + RightL.colon ^^ + NicePrint.layoutType denv ty + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, ?symbol = symbol) + + // Active pattern tags + | Item.ActivePatternCase apref -> + let v = apref.ActivePatternVal + let vTauTy = v.TauType + // REVIEW: use _cxs here + let (prettyTyparInst, prettyTy), _cxs = PrettyTypes.PrettifyInstAndType denv.g (item.TyparInstantiation, vTauTy) + let remarks = OutputFullName displayFullName pubpathOfValRef fullDisplayTextOfValRefAsLayout v + let layout = + wordL (tagText (FSComp.SR.typeInfoActiveRecognizer())) ^^ + wordL (tagActivePatternCase apref.DisplayName |> mkNav v.DefinitionRange) ^^ + RightL.colon ^^ + NicePrint.layoutType denv prettyTy + let layout = PrintUtilities.squashToWidth width layout + + let tpsL = FormatTyparMapping denv prettyTyparInst + + let layout = toArray layout + let tpsL = List.map toArray tpsL + let remarks = toArray remarks + ToolTipElement.Single (layout, xml, tpsL, remarks=remarks, ?symbol = symbol) + + // F# exception names + | Item.ExnCase ecref -> + let layout = NicePrint.layoutExnDef denv infoReader ecref + let layout = PrintUtilities.squashToWidth width layout + let remarks = OutputFullName displayFullName pubpathOfTyconRef fullDisplayTextOfExnRefAsLayout ecref + let layout = toArray layout + let remarks = toArray remarks + ToolTipElement.Single (layout, xml, remarks=remarks, ?symbol = symbol) + + | Item.RecdField rfinfo when rfinfo.TyconRef.IsFSharpException -> + let ty, _ = PrettyTypes.PrettifyType g rfinfo.FieldType + let id = rfinfo.DisplayName + let layout = + wordL (tagText (FSComp.SR.typeInfoArgument())) ^^ + wordL (tagParameter id) ^^ + RightL.colon ^^ + NicePrint.layoutType denv ty + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, paramName = id, ?symbol = symbol) + + // F# record field names + | Item.RecdField rfinfo -> + let rfield = rfinfo.RecdField + let ty, _cxs = PrettyTypes.PrettifyType g rfinfo.FieldType + let layout = + NicePrint.layoutTyconRef denv rfinfo.TyconRef ^^ + SepL.dot ^^ + wordL (tagRecordField rfield.DisplayName |> mkNav rfield.DefinitionRange) ^^ + RightL.colon ^^ + NicePrint.layoutType denv ty ^^ + ( + match rfinfo.LiteralValue with + | None -> emptyL + | Some lit -> try WordL.equals ^^ NicePrint.layoutConst denv.g ty lit with _ -> emptyL + ) + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, ?symbol = symbol) + + | Item.UnionCaseField (ucinfo, fieldIndex) -> + let rfield = ucinfo.UnionCase.GetFieldByIndex(fieldIndex) + let fieldTy, _ = PrettyTypes.PrettifyType g rfield.rfield_type + let id = rfield.Id + let layout = + wordL (tagText (FSComp.SR.typeInfoArgument())) ^^ + wordL (tagParameter id.idText) ^^ + RightL.colon ^^ + NicePrint.layoutType denv fieldTy + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, paramName = id.idText, ?symbol = symbol) + + // Not used + | Item.NewDef id -> + let layout = + wordL (tagText (FSComp.SR.typeInfoPatternVariable())) ^^ + wordL (tagUnknownEntity id.idText) + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, ?symbol = symbol) + + // .NET fields + | Item.ILField finfo -> + let layout = + wordL (tagText (FSComp.SR.typeInfoField())) ^^ + NicePrint.layoutType denv finfo.ApparentEnclosingAppType ^^ + SepL.dot ^^ + wordL (tagField finfo.FieldName) ^^ + RightL.colon ^^ + NicePrint.layoutType denv (finfo.FieldType(amap, m)) ^^ + ( + match finfo.LiteralValue with + | None -> emptyL + | Some v -> + WordL.equals ^^ + try NicePrint.layoutConst denv.g (finfo.FieldType(infoReader.amap, m)) (CheckExpressions.TcFieldInit m v) with _ -> emptyL + ) + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, ?symbol = symbol) + + // .NET events + | Item.Event einfo -> + let eventTy = PropTypeOfEventInfo infoReader m AccessibleFromSomewhere einfo + let eventTy, _cxs = PrettyTypes.PrettifyType g eventTy + let layout = + wordL (tagText (FSComp.SR.typeInfoEvent())) ^^ + NicePrint.layoutTyconRef denv einfo.ApparentEnclosingTyconRef ^^ + SepL.dot ^^ + wordL (tagEvent einfo.EventName) ^^ + RightL.colon ^^ + NicePrint.layoutType denv eventTy + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, ?symbol = symbol) + + // F# and .NET properties + | Item.Property(_, pinfo :: _) -> + let layout = NicePrint.prettyLayoutOfPropInfoFreeStyle g amap m denv pinfo + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, ?symbol = symbol) + + // Custom operations in queries + | Item.CustomOperation (customOpName, usageText, Some minfo) -> + + // Build 'custom operation: where (bool) + // + // Calls QueryBuilder.Where' + let layout = + wordL (tagText (FSComp.SR.typeInfoCustomOperation())) ^^ + RightL.colon ^^ + ( + match usageText() with + | Some t -> wordL (tagText t) + | None -> + let argTys = ParamNameAndTypesOfUnaryCustomOperation g minfo |> List.map (fun (ParamNameAndType(_, ty)) -> ty) + let argTys, _ = PrettyTypes.PrettifyTypes g argTys + wordL (tagMethod customOpName) ^^ sepListL SepL.space (List.map (fun ty -> LeftL.leftParen ^^ NicePrint.layoutType denv ty ^^ SepL.rightParen) argTys) + ) ^^ + SepL.lineBreak ^^ SepL.lineBreak ^^ + wordL (tagText (FSComp.SR.typeInfoCallsWord())) ^^ + NicePrint.layoutTyconRef denv minfo.ApparentEnclosingTyconRef ^^ + SepL.dot ^^ + wordL (tagMethod minfo.DisplayName) + + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, ?symbol = symbol) + + // F# constructors and methods + | Item.CtorGroup(_, minfos) + | Item.MethodGroup(_, minfos, _) -> + FormatOverloadsToList infoReader m denv item minfos symbol width + + // The 'fake' zero-argument constructors of .NET interfaces. + // This ideally should never appear in intellisense, but we do get here in repros like: + // type IFoo = abstract F : int + // type II = IFoo // remove 'type II = ' and quickly hover over IFoo before it gets squiggled for 'invalid use of interface type' + // and in that case we'll just show the interface type name. + | Item.FakeInterfaceCtor ty -> + let ty, _ = PrettyTypes.PrettifyType g ty + let layout = NicePrint.layoutTyconRef denv (tcrefOfAppTy g ty) + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single(layout, xml, ?symbol = symbol) + + // The 'fake' representation of constructors of .NET delegate types + | Item.DelegateCtor delTy -> + let delTy, _cxs = PrettyTypes.PrettifyType g delTy + let (SigOfFunctionForDelegate(_, _, _, delFuncTy)) = GetSigOfFunctionForDelegate infoReader delTy m AccessibleFromSomewhere + let layout = + NicePrint.layoutTyconRef denv (tcrefOfAppTy g delTy) ^^ + LeftL.leftParen ^^ + NicePrint.layoutType denv delFuncTy ^^ + RightL.rightParen + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single(layout, xml, ?symbol = symbol) + + // Types. + | Item.Types(_, TType_app(tcref, _, _) :: _) + | Item.UnqualifiedType (tcref :: _) -> + let denv = { denv with + // tooltips are space-constrained, so use shorter names + shortTypeNames = true + // tooltips are space-constrained, so don't include xml doc comments + // on types/members. The doc comments for the actual member will still + // be shown in the tip. + showDocumentation = false } + let layout = NicePrint.layoutTyconDefn denv infoReader ad m (* width *) tcref.Deref + let layout = PrintUtilities.squashToWidth width layout + let remarks = OutputFullName displayFullName pubpathOfTyconRef fullDisplayTextOfTyconRefAsLayout tcref + let layout = toArray layout + let remarks = toArray remarks + ToolTipElement.Single (layout, xml, remarks=remarks, ?symbol = symbol) + + // Type variables + | Item.TypeVar (_, typar) -> + let layout = NicePrint.prettyLayoutOfTypar denv typar + let layout = PrintUtilities.squashToWidth width layout + ToolTipElement.Single (toArray layout, xml, ?symbol = symbol) + + // Traits + | Item.Trait traitInfo -> + let denv = { denv with shortConstraints = false} + let layout = NicePrint.prettyLayoutOfTrait denv traitInfo + let layout = PrintUtilities.squashToWidth width layout + ToolTipElement.Single (toArray layout, xml, ?symbol = symbol) + + // F# Modules and namespaces + | Item.ModuleOrNamespaces(modref :: _ as modrefs) -> + //let os = StringBuilder() + let modrefs = modrefs |> RemoveDuplicateModuleRefs + let definiteNamespace = modrefs |> List.forall (fun modref -> modref.IsNamespace) + let kind = + if definiteNamespace then FSComp.SR.typeInfoNamespace() + elif modrefs |> List.forall (fun modref -> modref.IsModule) then FSComp.SR.typeInfoModule() + else FSComp.SR.typeInfoNamespaceOrModule() + + let layout = + wordL (tagKeyword kind) ^^ + (if definiteNamespace then tagNamespace (fullDisplayTextOfModRef modref) else (tagModule modref.DemangledModuleOrNamespaceName) + |> mkNav modref.DefinitionRange + |> wordL) + if not definiteNamespace then + let namesToAdd = + ([], modrefs) + ||> Seq.fold (fun st modref -> + match fullDisplayTextOfParentOfModRef modref with + | ValueSome txt -> txt :: st + | _ -> st) + |> Seq.mapi (fun i x -> i, x) + |> Seq.toList + let layout = + layout ^^ + ( + if not (List.isEmpty namesToAdd) then + SepL.lineBreak ^^ + List.fold ( fun s (i, txt) -> + s ^^ + SepL.lineBreak ^^ + wordL (tagText ((if i = 0 then FSComp.SR.typeInfoFromFirst else FSComp.SR.typeInfoFromNext) txt)) + ) emptyL namesToAdd + else + emptyL + ) + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, ?symbol = symbol) + else + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, ?symbol = symbol) + + | Item.AnonRecdField(anon, argTys, i, _) -> + let argTy = argTys[i] + let nm = anon.DisplayNameByIdx i + let argTy, _ = PrettyTypes.PrettifyType g argTy + let layout = + wordL (tagText (FSComp.SR.typeInfoAnonRecdField())) ^^ + wordL (tagRecordField nm) ^^ + RightL.colon ^^ + NicePrint.layoutType denv argTy + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, FSharpXmlDoc.None, ?symbol = symbol) + + // Named parameters + | Item.ArgName (Some id, argTy, _, _) -> + let argTy, _ = PrettyTypes.PrettifyType g argTy + let layout = + wordL (tagText (FSComp.SR.typeInfoArgument())) ^^ + wordL (tagParameter id.idText) ^^ + RightL.colon ^^ + NicePrint.layoutType denv argTy + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, paramName = id.idText, ?symbol = symbol) + + | Item.SetterArg (_, item) -> + FormatItemDescriptionToToolTipElement displayFullName infoReader ad m denv (ItemWithNoInst item) symbol width + + | Item.ArgName (None, _, _, _) + + // TODO: give a decent tooltip for implicit operators that include the resolution of the operator + // + //type C() = + // static member (++++++) (x: C, y: C) = C() + // + //let f (x: C) = + // x ++++++ x + // + // Here hovering over "++++++" in "f" could give a tooltip saying what the thing is and what it has resolved to. + // + // + | Item.ImplicitOp _ + + // TODO: consider why we aren't getting Item.Types for generic type parameters + // let F<'T>() = new System.Collections.Generic.List<'T>() + | Item.Types (_, [TType_var _]) + + // TODO: consider why we aren't getting Item.Types for units of measure + | Item.Types (_, [TType_measure _]) + + // TODO: consider whether we ever get Item.Types with more than one element + | Item.Types (_, _ :: _ :: _) + + // We don't expect Item.Types with an anonymous record type, function types etc. + | Item.Types (_, [TType_anon _]) + | Item.Types (_, [TType_fun _]) + | Item.Types (_, [TType_forall _]) + | Item.Types (_, [TType_tuple _]) + | Item.Types (_, [TType_ucase _]) + + // We don't expect these cases + | Item.Types (_, []) + | Item.Property (_, []) + | Item.UnqualifiedType [] + | Item.ModuleOrNamespaces [] + | Item.CustomOperation (_, _, None) -> ToolTipElement.None + + /// Format the structured version of a tooltip for an item + let FormatStructuredDescriptionOfItem isDecl infoReader ad m denv item symbol width = + DiagnosticsScope.Protect m + (fun () -> FormatItemDescriptionToToolTipElement isDecl infoReader ad m denv item symbol width) + (fun err -> ToolTipElement.CompositionError err) + /// An intellisense declaration [] type DeclarationListItem(textInDeclList: string, textInCode: string, fullName: string, glyph: FSharpGlyph, info, accessibility: FSharpAccessibility, @@ -1034,7 +1140,7 @@ type DeclarationListItem(textInDeclList: string, textInCode: string, fullName: s member _.Description = match info with | Choice1Of2 (items: CompletionItem list, infoReader, ad, m, denv) -> - ToolTipText(items |> List.map (fun x -> FormatStructuredDescriptionOfItem true infoReader ad m denv x.ItemWithInst None)) + ToolTipText(items |> List.map (fun x -> FormatStructuredDescriptionOfItem true infoReader ad m denv x.ItemWithInst None None)) | Choice2Of2 result -> result @@ -1297,7 +1403,7 @@ type MethodGroup( name: string, unsortedMethods: MethodGroupItem[] ) = (fun () -> PrettyParamsAndReturnTypeOfItem infoReader m denv { item with Item = flatItem }) (fun err -> [], wordL (tagText err)) - let description = ToolTipText [FormatStructuredDescriptionOfItem true infoReader ad m denv { item with Item = flatItem } None] + let description = ToolTipText [FormatStructuredDescriptionOfItem true infoReader ad m denv { item with Item = flatItem } None None] let hasParamArrayArg = match flatItem with diff --git a/src/Compiler/Service/ServiceDeclarationLists.fsi b/src/Compiler/Service/ServiceDeclarationLists.fsi index a3987d6684..c0de77f525 100644 --- a/src/Compiler/Service/ServiceDeclarationLists.fsi +++ b/src/Compiler/Service/ServiceDeclarationLists.fsi @@ -17,6 +17,8 @@ open FSharp.Compiler.AccessibilityLogic [] type public ToolTipElementData = { + Symbol: FSharpSymbol option + MainDescription: TaggedText[] XmlDoc: FSharpXmlDoc @@ -31,7 +33,7 @@ type public ToolTipElementData = ParamName: string option } - static member internal Create: layout: TaggedText[] * xml: FSharpXmlDoc * ?typeMapping: TaggedText[] list * ?paramName: string * ?remarks: TaggedText[] -> ToolTipElementData + static member internal Create: layout: TaggedText[] * xml: FSharpXmlDoc * ?typeMapping: TaggedText[] list * ?paramName: string * ?remarks: TaggedText[] * ?symbol: FSharpSymbol -> ToolTipElementData /// A single tool tip display element // @@ -46,7 +48,7 @@ type public ToolTipElement = /// An error occurred formatting this element | CompositionError of errorText: string - static member Single: layout: TaggedText[] * xml: FSharpXmlDoc * ?typeMapping: TaggedText[] list * ?paramName: string * ?remarks: TaggedText[] -> ToolTipElement + static member Single: layout: TaggedText[] * xml: FSharpXmlDoc * ?typeMapping: TaggedText[] list * ?paramName: string * ?remarks: TaggedText[] * ?symbol: FSharpSymbol -> ToolTipElement /// Information for building a tool tip box. // @@ -226,7 +228,7 @@ type public MethodGroup = static member internal Empty: MethodGroup module internal DeclarationListHelpers = - val FormatStructuredDescriptionOfItem: isDecl:bool -> InfoReader -> AccessorDomain -> range -> DisplayEnv -> ItemWithInst -> int option -> ToolTipElement + val FormatStructuredDescriptionOfItem: isDecl:bool -> InfoReader -> AccessorDomain -> range -> DisplayEnv -> ItemWithInst -> FSharpSymbol option -> int option -> ToolTipElement val RemoveDuplicateCompletionItems: TcGlobals -> CompletionItem list -> CompletionItem list diff --git a/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs b/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs index 76aea5388b..41dee649d8 100644 --- a/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs +++ b/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs @@ -298,3 +298,16 @@ module internal OpenDeclarationHelper = module internal TaggedText = let toString (tts: TaggedText[]) = tts |> Array.map (fun tt -> tt.Text) |> String.concat "" + +// http://www.fssnip.net/7S3/title/Intersperse-a-list +module List = + /// The intersperse function takes an element and a list and + /// 'intersperses' that element between the elements of the list. + let intersperse sep ls = + List.foldBack + (fun x -> + function + | [] -> [ x ] + | xs -> x :: sep :: xs) + ls + [] diff --git a/vsintegration/src/FSharp.Editor/DocComments/XMLDocumentation.fs b/vsintegration/src/FSharp.Editor/DocComments/XMLDocumentation.fs index b28e9d20a0..77ef4567b0 100644 --- a/vsintegration/src/FSharp.Editor/DocComments/XMLDocumentation.fs +++ b/vsintegration/src/FSharp.Editor/DocComments/XMLDocumentation.fs @@ -324,7 +324,7 @@ module internal XmlDocumentation = /// Append Xml documentation contents into the StringBuilder override this.AppendDocumentation - ( /// ITaggedTextCollector to add to + ( // ITaggedTextCollector to add to xmlCollector: ITaggedTextCollector, exnCollector: ITaggedTextCollector, fileName: string, @@ -392,12 +392,83 @@ module internal XmlDocumentation = paramName ) + [] + let separatorText = "-------------" + let private AddSeparator (collector: ITaggedTextCollector) = if not collector.IsEmpty then EnsureHardLine collector - collector.Add(tagText "-------------") + collector.Add(tagText separatorText) AppendHardLine collector + let BuildSingleTipText (documentationProvider: IDocumentationBuilder, dataTipElement: ToolTipElement) = + let mainDescription, documentation, typeParameterMap, exceptions, usage = + ResizeArray(), ResizeArray(), ResizeArray(), ResizeArray(), ResizeArray() + + let textCollector: ITaggedTextCollector = + TextSanitizingCollector(mainDescription.Add, lineLimit = 45) + + let xmlCollector: ITaggedTextCollector = + TextSanitizingCollector(documentation.Add, lineLimit = 45) + + let typeParameterMapCollector: ITaggedTextCollector = + TextSanitizingCollector(typeParameterMap.Add, lineLimit = 6) + + let exnCollector: ITaggedTextCollector = + TextSanitizingCollector(exceptions.Add, lineLimit = 45) + + let usageCollector: ITaggedTextCollector = + TextSanitizingCollector(usage.Add, lineLimit = 45) + + let ProcessGenericParameters (tps: TaggedText[] list) = + if not tps.IsEmpty then + AppendHardLine typeParameterMapCollector + AppendOnNewLine typeParameterMapCollector (SR.GenericParametersHeader()) + + for tp in tps do + AppendHardLine typeParameterMapCollector + typeParameterMapCollector.Add(tagSpace " ") + tp |> Array.iter typeParameterMapCollector.Add + + let collectDocumentation () = + [ documentation; typeParameterMap; exceptions; usage ] + |> List.filter (Seq.isEmpty >> not) + |> List.map List.ofSeq + |> List.intersperse [ lineBreak ] + |> Seq.concat + |> List.ofSeq + + match dataTipElement with + | ToolTipElement.Group overloads when not overloads.IsEmpty -> + overloads[..4] + |> List.map (fun item -> item.MainDescription) + |> List.intersperse [| lineBreak |] + |> Seq.concat + |> Seq.iter textCollector.Add + + if not overloads[5..].IsEmpty then + AppendOnNewLine textCollector $"({(PrettyNaming.FormatAndOtherOverloadsString overloads[5..].Length)})" + + let item0 = overloads.Head + + item0.Remarks + |> Option.iter (fun r -> + if TaggedText.toString r <> "" then + AppendHardLine usageCollector + r |> Seq.iter usageCollector.Add) + + AppendXmlComment(documentationProvider, xmlCollector, exnCollector, item0.XmlDoc, true, true, item0.ParamName) + + ProcessGenericParameters item0.TypeMapping + + item0.Symbol, mainDescription |> List.ofSeq, collectDocumentation () + + | ToolTipElement.CompositionError (errText) -> + textCollector.Add(tagText errText) + None, mainDescription |> List.ofSeq, collectDocumentation () + + | _ -> None, [], [] + /// Build a data tip text string with xml comments injected. let BuildTipText ( diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs index 4901de036c..bce993643e 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs @@ -556,194 +556,6 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = else statusBar.TempMessage(SR.CannotNavigateUnknown()) -type internal FSharpQuickInfo = - { - StructuredText: ToolTipText - Span: TextSpan - Symbol: FSharpSymbol option - SymbolKind: LexerSymbolKind - } - -module internal FSharpQuickInfo = - - let userOpName = "QuickInfo" - - // when a construct has been declared in a signature file the documentation comments that are - // written in that file are the ones that go into the generated xml when the project is compiled - // therefore we should include these doccoms in our design time quick info - let getQuickInfoFromRange - ( - document: Document, - declRange: range, - width: int option, - cancellationToken: CancellationToken - ) : Async = - - asyncMaybe { - let userOpName = "getQuickInfoFromRange" - let solution = document.Project.Solution - // ascertain the location of the target declaration in the signature file - let! extDocId = solution.GetDocumentIdsWithFilePath declRange.FileName |> Seq.tryHead - let extDocument = solution.GetProject(extDocId.ProjectId).GetDocument extDocId - let! extSourceText = extDocument.GetTextAsync cancellationToken - let! extSpan = RoslynHelpers.TryFSharpRangeToTextSpan(extSourceText, declRange) - let extLineText = (extSourceText.Lines.GetLineFromPosition extSpan.Start).ToString() - - // project options need to be retrieved because the signature file could be in another project - let! extLexerSymbol = extDocument.TryFindFSharpLexerSymbolAsync(extSpan.Start, SymbolLookupKind.Greedy, true, true, userOpName) - let! _, extCheckFileResults = extDocument.GetFSharpParseAndCheckResultsAsync(userOpName) |> liftAsync - - let extQuickInfoText = - extCheckFileResults.GetToolTip( - declRange.StartLine, - extLexerSymbol.Ident.idRange.EndColumn, - extLineText, - extLexerSymbol.FullIsland, - FSharpTokenTag.IDENT, - ?width = width - ) - - match extQuickInfoText with - | ToolTipText [] - | ToolTipText [ ToolTipElement.None ] -> return! None - | extQuickInfoText -> - let! extSymbolUse = - extCheckFileResults.GetSymbolUseAtLocation( - declRange.StartLine, - extLexerSymbol.Ident.idRange.EndColumn, - extLineText, - extLexerSymbol.FullIsland - ) - - let! span = RoslynHelpers.TryFSharpRangeToTextSpan(extSourceText, extLexerSymbol.Range) - - return - { - StructuredText = extQuickInfoText - Span = span - Symbol = Some extSymbolUse.Symbol - SymbolKind = extLexerSymbol.Kind - } - } - - /// Get QuickInfo combined from doccom of Signature and definition - let getQuickInfo - ( - document: Document, - position: int, - width: int option, - cancellationToken: CancellationToken - ) : Async<(range * FSharpQuickInfo option * FSharpQuickInfo option) option> = - - asyncMaybe { - let userOpName = "getQuickInfo" - let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, true, true, userOpName) - let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(userOpName) |> liftAsync - let! sourceText = document.GetTextAsync cancellationToken - let idRange = lexerSymbol.Ident.idRange - let textLinePos = sourceText.Lines.GetLinePosition position - let fcsTextLineNumber = Line.fromZ textLinePos.Line - let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() - - /// Gets the QuickInfo information for the orignal target - let getTargetSymbolQuickInfo (symbol, tag) = - asyncMaybe { - let targetQuickInfo = - match lexerSymbol.Kind with - | LexerSymbolKind.Keyword -> checkFileResults.GetKeywordTooltip(lexerSymbol.FullIsland) - | _ -> - checkFileResults.GetToolTip( - fcsTextLineNumber, - idRange.EndColumn, - lineText, - lexerSymbol.FullIsland, - tag, - ?width = width - ) - - match targetQuickInfo with - | ToolTipText [] - | ToolTipText [ ToolTipElement.None ] -> return! None - | _ -> - let! targetTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, lexerSymbol.Range) - - return - { - StructuredText = targetQuickInfo - Span = targetTextSpan - Symbol = symbol - SymbolKind = lexerSymbol.Kind - } - } - - match lexerSymbol.Kind with - | LexerSymbolKind.Keyword - | LexerSymbolKind.String -> - let! targetQuickInfo = getTargetSymbolQuickInfo (None, FSharpTokenTag.STRING) - return lexerSymbol.Range, None, Some targetQuickInfo - - | _ -> - let! symbolUse = - checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland) - - // if the target is in a signature file, adjusting the quick info is unnecessary - if isSignatureFile document.FilePath then - let! targetQuickInfo = getTargetSymbolQuickInfo (Some symbolUse.Symbol, FSharpTokenTag.IDENT) - return symbolUse.Range, None, Some targetQuickInfo - else - // find the declaration location of the target symbol, with a preference for signature files - let findSigDeclarationResult = - checkFileResults.GetDeclarationLocation( - idRange.StartLine, - idRange.EndColumn, - lineText, - lexerSymbol.FullIsland, - preferFlag = true - ) - - // it is necessary to retrieve the backup quick info because this acquires - // the textSpan designating where we want the quick info to appear. - let! targetQuickInfo = getTargetSymbolQuickInfo (Some symbolUse.Symbol, FSharpTokenTag.IDENT) - - let! result = - match findSigDeclarationResult with - | FindDeclResult.DeclFound declRange when isSignatureFile declRange.FileName -> - asyncMaybe { - let! sigQuickInfo = getQuickInfoFromRange (document, declRange, width, cancellationToken) - - // if the target was declared in a signature file, and the current file - // is not the corresponding module implementation file for that signature, - // the doccoms from the signature will overwrite any doccoms that might be - // present on the definition/implementation - let findImplDefinitionResult = - checkFileResults.GetDeclarationLocation( - idRange.StartLine, - idRange.EndColumn, - lineText, - lexerSymbol.FullIsland, - preferFlag = false - ) - - match findImplDefinitionResult with - | FindDeclResult.DeclNotFound _ - | FindDeclResult.ExternalDecl _ -> return symbolUse.Range, Some sigQuickInfo, None - | FindDeclResult.DeclFound declRange -> - let! implQuickInfo = getQuickInfoFromRange (document, declRange, width, cancellationToken) - - return - symbolUse.Range, - Some sigQuickInfo, - Some - { implQuickInfo with - Span = targetQuickInfo.Span - } - } - | _ -> async.Return None - |> liftAsync - - return result |> Option.defaultValue (symbolUse.Range, None, Some targetQuickInfo) - } - type internal FSharpNavigation ( statusBar: StatusBar, diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs index 45d24fb9d5..727211e9a1 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs @@ -3,11 +3,9 @@ namespace Microsoft.VisualStudio.FSharp.Editor.QuickInfo open System -open System.IO open System.Threading open System.Threading.Tasks open System.ComponentModel.Composition -open System.Text open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text @@ -21,6 +19,7 @@ open Microsoft.VisualStudio.FSharp.Editor open FSharp.Compiler.Text open Microsoft.IO +open FSharp.Compiler.EditorServices type internal FSharpAsyncQuickInfoSource ( @@ -31,165 +30,101 @@ type internal FSharpAsyncQuickInfoSource editorOptions: EditorOptions ) = - // test helper - static member ProvideQuickInfo(document: Document, position: int, ?width: int) = + let tryGetQuickInfoItem (session: IAsyncQuickInfoSession) = asyncMaybe { - let! _, sigQuickInfo, targetQuickInfo = FSharpQuickInfo.getQuickInfo (document, position, width, CancellationToken.None) - return! sigQuickInfo |> Option.orElse targetQuickInfo - } + let userOpName = "getQuickInfo" + + let! triggerPoint = session.GetTriggerPoint(textBuffer.CurrentSnapshot) |> Option.ofNullable + let position = triggerPoint.Position + + let document = + textBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges() + + let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, true, true, userOpName) + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(userOpName) |> liftAsync + let! cancellationToken = Async.CancellationToken |> liftAsync + let! sourceText = document.GetTextAsync cancellationToken + let idRange = lexerSymbol.Ident.idRange + let textLinePos = sourceText.Lines.GetLinePosition position + let fcsTextLineNumber = Line.fromZ textLinePos.Line + let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() + + let getLinkTooltip filePath = + let solutionDir = Path.GetDirectoryName(document.Project.Solution.FilePath) + let projectDir = Path.GetDirectoryName(document.Project.FilePath) + + [ + Path.GetRelativePath(projectDir, filePath) + Path.GetRelativePath(solutionDir, filePath) + ] + |> List.minBy String.length + + let symbolUseRange = + maybe { + let! symbolUse = + checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland) + + return symbolUse.Range + } - static member BuildSingleQuickInfoItem (documentationBuilder: IDocumentationBuilder) (quickInfo: FSharpQuickInfo) = - let mainDescription, documentation, typeParameterMap, usage, exceptions = - ResizeArray(), ResizeArray(), ResizeArray(), ResizeArray(), ResizeArray() + let tryGetSingleContent (data: ToolTipElement) = + maybe { + let documentationBuilder = + XmlDocumentation.CreateDocumentationBuilder(xmlMemberIndexService) - XmlDocumentation.BuildDataTipText( - documentationBuilder, - mainDescription.Add, - documentation.Add, - typeParameterMap.Add, - usage.Add, - exceptions.Add, - quickInfo.StructuredText - ) + let symbol, description, documentation = + XmlDocumentation.BuildSingleTipText(documentationBuilder, data) - let docs = - RoslynHelpers.joinWithLineBreaks [ documentation; typeParameterMap; usage; exceptions ] + return + QuickInfoViewProvider.provideContent ( + Tokenizer.GetImageIdForSymbol(symbol, lexerSymbol.Kind), + description, + documentation, + FSharpNavigation(statusBar, metadataAsSource, document, defaultArg symbolUseRange range.Zero), + getLinkTooltip + ) + } - (mainDescription, docs) + let (ToolTipText elements) = + match lexerSymbol.Kind with + | LexerSymbolKind.Keyword -> checkFileResults.GetKeywordTooltip(lexerSymbol.FullIsland) + | LexerSymbolKind.String -> + checkFileResults.GetToolTip( + fcsTextLineNumber, + idRange.EndColumn, + lineText, + lexerSymbol.FullIsland, + FSharp.Compiler.Tokenization.FSharpTokenTag.String, + ?width = editorOptions.QuickInfo.DescriptionWidth + ) + | _ -> + checkFileResults.GetToolTip( + fcsTextLineNumber, + idRange.EndColumn, + lineText, + lexerSymbol.FullIsland, + FSharp.Compiler.Tokenization.FSharpTokenTag.IDENT, + ?width = editorOptions.QuickInfo.DescriptionWidth + ) + + let content = elements |> List.choose tryGetSingleContent + do! Option.guard (not content.IsEmpty) + + let! textSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, lexerSymbol.Range) + + let trackingSpan = + textBuffer.CurrentSnapshot.CreateTrackingSpan(textSpan.Start, textSpan.Length, SpanTrackingMode.EdgeInclusive) + + return QuickInfoItem(trackingSpan, QuickInfoViewProvider.stackWithSeparators content) + } interface IAsyncQuickInfoSource with override _.Dispose() = () // no cleanup necessary - // This method can be called from the background thread. - // Do not call IServiceProvider.GetService here. override _.GetQuickInfoItemAsync(session: IAsyncQuickInfoSession, cancellationToken: CancellationToken) : Task = - let triggerPoint = session.GetTriggerPoint(textBuffer.CurrentSnapshot) - - match triggerPoint.HasValue with - | false -> Task.FromResult(null) - | true -> - let triggerPoint = triggerPoint.GetValueOrDefault() - - let width = editorOptions.QuickInfo.DescriptionWidth - - asyncMaybe { - let document = - textBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges() - - let! symbolUseRange, sigQuickInfo, targetQuickInfo = - FSharpQuickInfo.getQuickInfo (document, triggerPoint.Position, width, cancellationToken) - - let getTooltip filePath = - let solutionDir = Path.GetDirectoryName(document.Project.Solution.FilePath) - let projectDir = Path.GetDirectoryName(document.Project.FilePath) - - [ - Path.GetRelativePath(projectDir, filePath) - Path.GetRelativePath(solutionDir, filePath) - ] - |> List.minBy String.length - - let getTrackingSpan (span: TextSpan) = - textBuffer.CurrentSnapshot.CreateTrackingSpan(span.Start, span.Length, SpanTrackingMode.EdgeInclusive) - - let documentationBuilder = - XmlDocumentation.CreateDocumentationBuilder(xmlMemberIndexService) - - match sigQuickInfo, targetQuickInfo with - | None, None -> return null - | Some quickInfo, None - | None, Some quickInfo -> - let mainDescription, docs = - FSharpAsyncQuickInfoSource.BuildSingleQuickInfoItem documentationBuilder quickInfo - - let imageId = Tokenizer.GetImageIdForSymbol(quickInfo.Symbol, quickInfo.SymbolKind) - - let navigation = - FSharpNavigation(statusBar, metadataAsSource, document, symbolUseRange) - - let content = - QuickInfoViewProvider.provideContent ( - imageId, - mainDescription |> List.ofSeq, - [ docs |> List.ofSeq ], - navigation, - getTooltip - ) - - let span = getTrackingSpan quickInfo.Span - return QuickInfoItem(span, content) - - | Some sigQuickInfo, Some targetQuickInfo -> - let mainDescription, targetDocumentation, sigDocumentation, typeParameterMap, exceptions, usage = - ResizeArray(), ResizeArray(), ResizeArray(), ResizeArray(), ResizeArray(), ResizeArray() - - XmlDocumentation.BuildDataTipText( - documentationBuilder, - ignore, - sigDocumentation.Add, - ignore, - ignore, - ignore, - sigQuickInfo.StructuredText - ) - - XmlDocumentation.BuildDataTipText( - documentationBuilder, - mainDescription.Add, - targetDocumentation.Add, - typeParameterMap.Add, - exceptions.Add, - usage.Add, - targetQuickInfo.StructuredText - ) - // get whitespace nomalized documentation text - let getText (tts: seq) = - let text = - (StringBuilder(), tts) - ||> Seq.fold (fun sb tt -> - if String.IsNullOrWhiteSpace tt.Text then - sb - else - sb.Append tt.Text) - |> string - - if String.IsNullOrWhiteSpace text then None else Some text - - let documentationParts: TaggedText list list = - [ - match getText targetDocumentation, getText sigDocumentation with - | None, None -> () - | None, Some _ -> sigDocumentation |> List.ofSeq - | Some _, None -> targetDocumentation |> List.ofSeq - | Some implText, Some sigText when implText.Equals(sigText, StringComparison.OrdinalIgnoreCase) -> - sigDocumentation |> List.ofSeq - | Some _, Some _ -> - sigDocumentation |> List.ofSeq - targetDocumentation |> List.ofSeq - RoslynHelpers.joinWithLineBreaks [ typeParameterMap; usage; exceptions ] - |> List.ofSeq - ] - - let imageId = - Tokenizer.GetImageIdForSymbol(targetQuickInfo.Symbol, targetQuickInfo.SymbolKind) - - let navigation = - FSharpNavigation(statusBar, metadataAsSource, document, symbolUseRange) - - let content = - QuickInfoViewProvider.provideContent ( - imageId, - mainDescription |> List.ofSeq, - documentationParts, - navigation, - getTooltip - ) - - let span = getTrackingSpan targetQuickInfo.Span - return QuickInfoItem(span, content) - } - |> Async.map Option.toObj - |> RoslynHelpers.StartAsyncAsTask cancellationToken + tryGetQuickInfoItem session + |> Async.map Option.toObj + |> RoslynHelpers.StartAsyncAsTask cancellationToken [)>] [] @@ -210,4 +145,3 @@ type internal FSharpAsyncQuickInfoSourceProvider [] let xmlMemberIndexService = serviceProvider.XMLMemberIndexService new FSharpAsyncQuickInfoSource(statusBar, xmlMemberIndexService, metadataAsSource, textBuffer, editorOptions) - :> IAsyncQuickInfoSource diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/Views.fs b/vsintegration/src/FSharp.Editor/QuickInfo/Views.fs index f5830104ed..e3309470c5 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/Views.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/Views.fs @@ -11,7 +11,6 @@ open Microsoft.VisualStudio.Text.Adornments open Microsoft.VisualStudio.FSharp.Editor module internal QuickInfoViewProvider = - let layoutTagToClassificationTag (layoutTag: TextTag) = match layoutTag with | TextTag.ActivePatternCase @@ -56,11 +55,6 @@ module internal QuickInfoViewProvider = | TaggedText (TextTag.LineBreak, _) -> Some() | _ -> None - let (|DocSeparator|_|) = - function - | LineBreak :: TaggedText (TextTag.Text, "-------------") :: LineBreak :: rest -> Some rest - | _ -> None - let wrapContent (elements: obj list) = ContainerElement(ContainerElementStyle.Wrapped, elements |> Seq.map box) @@ -69,14 +63,11 @@ module internal QuickInfoViewProvider = let encloseRuns runs = wrapContent (runs |> List.rev) |> box - let emptyLine = - wrapContent [ ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, "") |> box ] - let provideContent ( imageId: ImageId option, description: TaggedText list, - documentation: TaggedText list list, + documentation: TaggedText list, navigation: FSharpNavigation, getTooltip ) = @@ -85,9 +76,8 @@ module internal QuickInfoViewProvider = let rec loop text runs stack = match (text: TaggedText list) with | [] -> stackContent (encloseRuns runs :: stack |> List.rev) - | DocSeparator (LineBreak :: rest) - | DocSeparator rest -> loop rest [] (box Separator :: encloseRuns runs :: stack) - | LineBreak :: rest when runs |> List.isEmpty -> loop rest [] (emptyLine :: stack) + // smaller gap instead of huge double line break + | LineBreak :: rest when runs |> List.isEmpty -> loop rest [] (box (Separator false) :: stack) | LineBreak :: rest -> loop rest [] (encloseRuns runs :: stack) | :? NavigableTaggedText as item :: rest when navigation.IsTargetValid item.Range -> let classificationTag = layoutTagToClassificationTag item.Tag @@ -108,6 +98,10 @@ module internal QuickInfoViewProvider = | Some imageId -> wrapContent [ stackContent [ ImageElement(imageId) ]; encloseText description ] | None -> ContainerElement(ContainerElementStyle.Wrapped, encloseText description) - let separated = stackContent (documentation |> List.map encloseText) + wrapContent [ stackContent [ innerElement; encloseText documentation ] ] - wrapContent [ stackContent [ innerElement; separated ] ] + let stackWithSeparators elements = + elements + |> List.map box + |> List.intersperse (box (Separator true)) + |> stackContent diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/WpfFactories.fs b/vsintegration/src/FSharp.Editor/QuickInfo/WpfFactories.fs index 607b1e2f3d..e6beb9f5d2 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/WpfFactories.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/WpfFactories.fs @@ -12,7 +12,13 @@ open Microsoft.VisualStudio.Utilities open Microsoft.VisualStudio.FSharp.Editor -type Separator = Separator +type Separator = + | Separator of visible: bool + // preserve old behavior on mac + override this.ToString() = + match this with + | Separator true -> XmlDocumentation.separatorText + | _ -> System.Environment.NewLine [)>] [] @@ -56,5 +62,11 @@ type WpfSeparatorFactory() = interface IViewElementFactory with member _.CreateViewElement(_, model: obj) = match model with - | :? Separator -> Controls.Separator(Opacity = 0.4, Margin = Thickness(0, 10, 0, 10)) |> box :?> _ + | :? Separator as Separator visible -> + if visible then + Controls.Separator(Opacity = 0.3, Margin = Thickness(0, 8, 0, 8)) + else + Controls.Separator(Opacity = 0) + |> box + :?> _ | _ -> failwith $"Invalid type conversion. Supported conversion is {typeof.Name} to {typeof.Name}." From 2057cfdbff5220e04d49e8cc846a05d1bcd6505e Mon Sep 17 00:00:00 2001 From: majocha Date: Tue, 28 Mar 2023 21:01:11 +0200 Subject: [PATCH 2/7] wip --- .../Service/ServiceDeclarationLists.fs | 1639 ++++++++--------- .../FSharp.Editor/xlf/FSharp.Editor.cs.xlf | 6 +- .../FSharp.Editor/xlf/FSharp.Editor.de.xlf | 6 +- .../FSharp.Editor/xlf/FSharp.Editor.es.xlf | 6 +- .../FSharp.Editor/xlf/FSharp.Editor.fr.xlf | 6 +- .../FSharp.Editor/xlf/FSharp.Editor.it.xlf | 6 +- .../FSharp.Editor/xlf/FSharp.Editor.ja.xlf | 6 +- .../FSharp.Editor/xlf/FSharp.Editor.ko.xlf | 6 +- .../FSharp.Editor/xlf/FSharp.Editor.pl.xlf | 6 +- .../FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf | 6 +- .../FSharp.Editor/xlf/FSharp.Editor.ru.xlf | 6 +- .../FSharp.Editor/xlf/FSharp.Editor.tr.xlf | 6 +- .../xlf/FSharp.Editor.zh-Hans.xlf | 6 +- .../xlf/FSharp.Editor.zh-Hant.xlf | 6 +- 14 files changed, 820 insertions(+), 897 deletions(-) diff --git a/src/Compiler/Service/ServiceDeclarationLists.fs b/src/Compiler/Service/ServiceDeclarationLists.fs index 04ed4cb5f6..c6546c4f02 100644 --- a/src/Compiler/Service/ServiceDeclarationLists.fs +++ b/src/Compiler/Service/ServiceDeclarationLists.fs @@ -88,564 +88,795 @@ type CompletionItem = Unresolved: UnresolvedSymbol option } member x.Item = x.ItemWithInst.Item -/// Represents one parameter for one method (or other item) in a group. -[] -type MethodGroupItemParameter(name: string, canonicalTypeTextForSorting: string, display: TaggedText[], isOptional: bool) = +[] +module DeclarationListHelpers = + let mutable ToolTipFault = None - /// The name of the parameter. - member _.ParameterName = name + let emptyToolTip = ToolTipText [] - /// A key that can be used for sorting the parameters, used to help sort overloads. - member _.CanonicalTypeTextForSorting = canonicalTypeTextForSorting + /// Generate the structured tooltip for a method info + let FormatOverloadsToList (infoReader: InfoReader) m denv (item: ItemWithInst) minfos symbol (width: int option) : ToolTipElement = + ToolTipFault |> Option.iter (fun msg -> + let exn = Error((0, msg), range.Zero) + let ph = PhasedDiagnostic.Create(exn, BuildPhase.TypeCheck) + simulateError ph) + + let layouts = + [ for minfo in minfos -> + let prettyTyparInst, layout = NicePrint.prettyLayoutOfMethInfoFreeStyle infoReader m denv item.TyparInstantiation minfo + let xml = GetXmlCommentForMethInfoItem infoReader m item.Item minfo + let tpsL = FormatTyparMapping denv prettyTyparInst + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + let tpsL = List.map toArray tpsL + ToolTipElementData.Create(layout, xml, tpsL, ?symbol = symbol) ] + + ToolTipElement.Group layouts + + let CompletionItemDisplayPartialEquality g = + let itemComparer = ItemDisplayPartialEquality g + + { new IPartialEqualityComparer with + member x.InEqualityRelation item = itemComparer.InEqualityRelation item.Item + member x.Equals(item1, item2) = itemComparer.Equals(item1.Item, item2.Item) + member x.GetHashCode item = itemComparer.GetHashCode(item.Item) } - /// The text to display for the parameter including its name, its type and visual indicators of other - /// information such as whether it is optional. - member _.Display = display + /// Remove all duplicate items + let RemoveDuplicateCompletionItems g items = + if isNil items then items else + items |> IPartialEqualityComparer.partialDistinctBy (CompletionItemDisplayPartialEquality g) - /// Is the parameter optional - member _.IsOptional = isOptional + /// Filter types that are explicitly suppressed from the IntelliSense (such as uppercase "FSharpList", "Option", etc.) + let RemoveExplicitlySuppressedCompletionItems (g: TcGlobals) (items: CompletionItem list) = + items |> List.filter (fun item -> not (IsExplicitlySuppressed g item.Item)) -[] -module internal DescriptionListsImpl = + // Remove items containing the same module references + let RemoveDuplicateModuleRefs modrefs = + modrefs |> IPartialEqualityComparer.partialDistinctBy + { new IPartialEqualityComparer with + member x.InEqualityRelation _ = true + member x.Equals(item1, item2) = (fullDisplayTextOfModRef item1 = fullDisplayTextOfModRef item2) + member x.GetHashCode item = hash item.Stamp } - let isFunction g ty = - let _, tauTy = tryDestForallTy g ty - isFunTy g tauTy - - let printCanonicalizedTypeName g (denv:DisplayEnv) tauTy = - // get rid of F# abbreviations and such - let strippedTy = stripTyEqnsWrtErasure EraseAll g tauTy - // pretend no namespaces are open - let denv = denv.SetOpenPaths([]) - // now printing will see a .NET-like canonical representation, that is good for sorting overloads into a reasonable order (see bug 94520) - NicePrint.stringOfTy denv strippedTy + let OutputFullName displayFullName ppF fnF r = + // Only display full names in quick info, not declaration lists or method lists + if not displayFullName then + match ppF r with + | None -> emptyL + | Some _ -> wordL (tagText (FSComp.SR.typeInfoFullName())) ^^ RightL.colon ^^ (fnF r) + else emptyL - let PrettyParamOfRecdField g denv (f: RecdField) = - let display = NicePrint.prettyLayoutOfType denv f.FormalType - let display = toArray display - MethodGroupItemParameter( - name = f.DisplayNameCore, - canonicalTypeTextForSorting = printCanonicalizedTypeName g denv f.FormalType, - display = display, - isOptional=false) - - let PrettyParamOfUnionCaseField g denv isGenerated (i: int) (f: RecdField) = - let initial = PrettyParamOfRecdField g denv f - let display = - if isGenerated i f then - initial.Display - else - let display = NicePrint.layoutOfParamData denv (ParamData(false, false, false, NotOptional, NoCallerInfo, Some f.Id, ReflectedArgInfo.None, f.FormalType)) - toArray display + let pubpathOfValRef (v: ValRef) = v.PublicPath - MethodGroupItemParameter( - name=initial.ParameterName, - canonicalTypeTextForSorting=initial.CanonicalTypeTextForSorting, - display=display, - isOptional=false) + let pubpathOfTyconRef (x: TyconRef) = x.PublicPath - let ParamOfParamData g denv (ParamData(_isParamArrayArg, _isInArg, _isOutArg, optArgInfo, _callerInfo, nmOpt, _reflArgInfo, pty) as paramData) = - let display = NicePrint.layoutOfParamData denv paramData - let display = toArray display - MethodGroupItemParameter( - name = (match nmOpt with None -> "" | Some pn -> pn.idText), - canonicalTypeTextForSorting = printCanonicalizedTypeName g denv pty, - display = display, - isOptional=optArgInfo.IsOptional) + /// Output the quick info information of a language item + let rec FormatItemDescriptionToToolTipElement displayFullName (infoReader: InfoReader) ad m denv (item: ItemWithInst) symbol (width: int option) = + let g = infoReader.g + let amap = infoReader.amap + let denv = SimplerDisplayEnv denv + let xml = GetXmlCommentForItem infoReader m item.Item - // TODO this code is similar to NicePrint.fs:formatParamDataToBuffer, refactor or figure out why different? - let PrettyParamsOfParamDatas g denv typarInst (paramDatas:ParamData list) paramTy = - let paramInfo, paramTypes = - paramDatas - |> List.map (fun (ParamData(isParamArrayArg, _isInArg, _isOutArg, optArgInfo, _callerInfo, nmOpt, _reflArgInfo, pty)) -> - let isOptArg = optArgInfo.IsOptional - match nmOpt, isOptArg, tryDestOptionTy denv.g pty with - // Layout an optional argument - | Some id, true, ptyOpt -> - let nm = id.idText - // detect parameter type, if ptyOpt is None - this is .NET style optional argument - let pty = match ptyOpt with ValueSome x -> x | _ -> pty - (nm, isOptArg, SepL.questionMark ^^ (wordL (tagParameter nm))), pty - // Layout an unnamed argument - | None, _, _ -> - ("", isOptArg, emptyL), pty - // Layout a named argument - | Some id, _, _ -> - let nm = id.idText - let prefix = - if isParamArrayArg then - NicePrint.PrintUtilities.layoutBuiltinAttribute denv denv.g.attrib_ParamArrayAttribute ^^ - wordL (tagParameter nm) ^^ - RightL.colon - //sprintf "%s %s: " (NicePrint.PrintUtilities.layoutBuiltinAttribute denv denv.g.attrib_ParamArrayAttribute |> showL) nm - else - wordL (tagParameter nm) ^^ - RightL.colon - //sprintf "%s: " nm - (nm, isOptArg, prefix), pty) - |> List.unzip + match item.Item with + | Item.ImplicitOp(_, { contents = Some(TraitConstraintSln.FSMethSln(vref=vref)) }) -> + // operator with solution + FormatItemDescriptionToToolTipElement displayFullName infoReader ad m denv { item with Item = Item.Value vref } symbol width - // Prettify everything - let prettyTyparInst, (prettyParamTys, _prettyRetTy), (prettyParamTysL, prettyRetTyL), prettyConstraintsL = - NicePrint.prettyLayoutOfInstAndSig denv (typarInst, paramTypes, paramTy) + | Item.Value vref | Item.CustomBuilder (_, vref) -> + let prettyTyparInst, resL = NicePrint.layoutQualifiedValOrMember denv infoReader item.TyparInstantiation vref + let remarks = OutputFullName displayFullName pubpathOfValRef fullDisplayTextOfValRefAsLayout vref + let tpsL = FormatTyparMapping denv prettyTyparInst + let tpsL = List.map toArray tpsL + let resL = PrintUtilities.squashToWidth width resL + let resL = toArray resL + let remarks = toArray remarks + ToolTipElement.Single(resL, xml, tpsL, remarks=remarks, ?symbol = symbol) - // Remake the params using the prettified versions - let prettyParams = - (paramInfo, prettyParamTys, prettyParamTysL) |||> List.map3 (fun (nm, isOptArg, paramPrefix) tauTy tyL -> - let display = paramPrefix ^^ tyL - let display = toArray display - MethodGroupItemParameter( - name = nm, - canonicalTypeTextForSorting = printCanonicalizedTypeName g denv tauTy, - display = display, - isOptional=isOptArg - )) + // Union tags (constructors) + | Item.UnionCase(ucinfo, _) -> + let uc = ucinfo.UnionCase + let unionTy = generalizedTyconRef g ucinfo.TyconRef + let recd = uc.RecdFields + let layout = + wordL (tagText (FSComp.SR.typeInfoUnionCase())) ^^ + NicePrint.layoutTyconRef denv ucinfo.TyconRef ^^ + sepL (tagPunctuation ".") ^^ + wordL (tagUnionCase (ConvertValLogicalNameToDisplayNameCore uc.Id.idText) |> mkNav uc.DefinitionRange) ^^ + RightL.colon ^^ + (if List.isEmpty recd then emptyL else NicePrint.layoutUnionCases denv infoReader ucinfo.TyconRef recd ^^ WordL.arrow) ^^ + NicePrint.layoutType denv unionTy + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, ?symbol = symbol) - prettyTyparInst, prettyParams, prettyRetTyL, prettyConstraintsL + // Active pattern tag inside the declaration (result) + | Item.ActivePatternResult(apinfo, ty, idx, _) -> + let items = apinfo.ActiveTags + let layout = + wordL (tagText (FSComp.SR.typeInfoActivePatternResult())) ^^ + wordL (tagActivePatternResult (List.item idx items) |> mkNav apinfo.Range) ^^ + RightL.colon ^^ + NicePrint.layoutType denv ty + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, ?symbol = symbol) - let PrettyParamsOfTypes g denv typarInst paramTys retTy = + // Active pattern tags + | Item.ActivePatternCase apref -> + let v = apref.ActivePatternVal + let vTauTy = v.TauType + // REVIEW: use _cxs here + let (prettyTyparInst, prettyTy), _cxs = PrettyTypes.PrettifyInstAndType denv.g (item.TyparInstantiation, vTauTy) + let remarks = OutputFullName displayFullName pubpathOfValRef fullDisplayTextOfValRefAsLayout v + let layout = + wordL (tagText (FSComp.SR.typeInfoActiveRecognizer())) ^^ + wordL (tagActivePatternCase apref.DisplayName |> mkNav v.DefinitionRange) ^^ + RightL.colon ^^ + NicePrint.layoutType denv prettyTy + let layout = PrintUtilities.squashToWidth width layout - // Prettify everything - let prettyTyparInst, (prettyParamTys, _prettyRetTy), (prettyParamTysL, prettyRetTyL), prettyConstraintsL = - NicePrint.prettyLayoutOfInstAndSig denv (typarInst, paramTys, retTy) + let tpsL = FormatTyparMapping denv prettyTyparInst - // Remake the params using the prettified versions - let parameters = - (prettyParamTys, prettyParamTysL) - ||> List.map2 (fun paramTy tyL -> - let display = toArray tyL - MethodGroupItemParameter( - name = "", - canonicalTypeTextForSorting = printCanonicalizedTypeName g denv paramTy, - display = display, - isOptional=false - )) + let layout = toArray layout + let tpsL = List.map toArray tpsL + let remarks = toArray remarks + ToolTipElement.Single (layout, xml, tpsL, remarks=remarks, ?symbol = symbol) - // Return the results - prettyTyparInst, parameters, prettyRetTyL, prettyConstraintsL - + // F# exception names + | Item.ExnCase ecref -> + let layout = NicePrint.layoutExnDef denv infoReader ecref + let layout = PrintUtilities.squashToWidth width layout + let remarks = OutputFullName displayFullName pubpathOfTyconRef fullDisplayTextOfExnRefAsLayout ecref + let layout = toArray layout + let remarks = toArray remarks + ToolTipElement.Single (layout, xml, remarks=remarks, ?symbol = symbol) -#if !NO_TYPEPROVIDERS + | Item.RecdField rfinfo when rfinfo.TyconRef.IsFSharpException -> + let ty, _ = PrettyTypes.PrettifyType g rfinfo.FieldType + let id = rfinfo.DisplayName + let layout = + wordL (tagText (FSComp.SR.typeInfoArgument())) ^^ + wordL (tagParameter id) ^^ + RightL.colon ^^ + NicePrint.layoutType denv ty + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, paramName = id, ?symbol = symbol) - /// Get the set of static parameters associated with an item - let StaticParamsOfItem (infoReader:InfoReader) m denv item = - let amap = infoReader.amap - let g = infoReader.g - match item with - | ItemIsWithStaticArguments m g staticParameters -> - staticParameters - |> Array.map (fun sp -> - let ty = Import.ImportProvidedType amap m (sp.PApply((fun x -> x.ParameterType), m)) - let spKind = NicePrint.prettyLayoutOfType denv ty - let spName = sp.PUntaint((fun sp -> sp.Name), m) - let spOpt = sp.PUntaint((fun sp -> sp.IsOptional), m) - let display = (if spOpt then SepL.questionMark else emptyL) ^^ wordL (tagParameter spName) ^^ RightL.colon ^^ spKind - let display = toArray display - MethodGroupItemParameter( - name = spName, - canonicalTypeTextForSorting = showL spKind, - display = display, - //display = sprintf "%s%s: %s" (if spOpt then "?" else "") spName spKind, - isOptional=spOpt)) - | _ -> [| |] -#endif + // F# record field names + | Item.RecdField rfinfo -> + let rfield = rfinfo.RecdField + let ty, _cxs = PrettyTypes.PrettifyType g rfinfo.FieldType + let layout = + NicePrint.layoutTyconRef denv rfinfo.TyconRef ^^ + SepL.dot ^^ + wordL (tagRecordField rfield.DisplayName |> mkNav rfield.DefinitionRange) ^^ + RightL.colon ^^ + NicePrint.layoutType denv ty ^^ + ( + match rfinfo.LiteralValue with + | None -> emptyL + | Some lit -> try WordL.equals ^^ NicePrint.layoutConst denv.g ty lit with _ -> emptyL + ) + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, ?symbol = symbol) - /// Get all the information about parameters and "prettify" the types by choosing nice type variable - /// names. This is similar to the other variations on "show me an item" code. This version is - /// is used when presenting groups of methods (see MethodGroup). It is possible these different - /// versions could be better unified. - let rec PrettyParamsAndReturnTypeOfItem (infoReader:InfoReader) m denv (item: ItemWithInst) = - let amap = infoReader.amap - let g = infoReader.g - let denv = { SimplerDisplayEnv denv with useColonForReturnType=true} - match item.Item with - | Item.Value vref -> + | Item.UnionCaseField (ucinfo, fieldIndex) -> + let rfield = ucinfo.UnionCase.GetFieldByIndex(fieldIndex) + let fieldTy, _ = PrettyTypes.PrettifyType g rfield.rfield_type + let id = rfield.Id + let layout = + wordL (tagText (FSComp.SR.typeInfoArgument())) ^^ + wordL (tagParameter id.idText) ^^ + RightL.colon ^^ + NicePrint.layoutType denv fieldTy + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, paramName = id.idText, ?symbol = symbol) - let getPrettyParamsOfTypes() = - let vTauTy = vref.TauType - match tryDestFunTy denv.g vTauTy with - | ValueSome(arg, retTy) -> - let args = tryDestRefTupleTy denv.g arg - let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfTypes g denv item.TyparInstantiation args retTy - // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned - // for display as part of the method group - prettyParams, prettyRetTyL - | _ -> - let _prettyTyparInst, prettyTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] vTauTy - [], prettyTyL + // Not used + | Item.NewDef id -> + let layout = + wordL (tagText (FSComp.SR.typeInfoPatternVariable())) ^^ + wordL (tagUnknownEntity id.idText) + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, ?symbol = symbol) - match vref.ValReprInfo with - | None -> - // ValReprInfo = None i.e. in let bindings defined in types or in local functions - // in this case use old approach and return only information about types - getPrettyParamsOfTypes () + // .NET fields + | Item.ILField finfo -> + let layout = + wordL (tagText (FSComp.SR.typeInfoField())) ^^ + NicePrint.layoutType denv finfo.ApparentEnclosingAppType ^^ + SepL.dot ^^ + wordL (tagField finfo.FieldName) ^^ + RightL.colon ^^ + NicePrint.layoutType denv (finfo.FieldType(amap, m)) ^^ + ( + match finfo.LiteralValue with + | None -> emptyL + | Some v -> + WordL.equals ^^ + try NicePrint.layoutConst denv.g (finfo.FieldType(infoReader.amap, m)) (CheckExpressions.TcFieldInit m v) with _ -> emptyL + ) + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, ?symbol = symbol) - | Some valReprInfo -> - // ValReprInfo will exist for top-level syntactic functions - // per spec: binding is considered to define a syntactic function if it is either a function or its immediate right-hand-side is a anonymous function - let _, argInfos, lastRetTy, _ = GetValReprTypeInFSharpForm g valReprInfo vref.Type m - match argInfos with - | [] -> - // handles cases like 'let foo = List.map' - getPrettyParamsOfTypes() - | firstCurriedArgInfo :: _ -> - // result 'paramDatas' collection corresponds to the first argument of curried function - // i.e. let func (a : int) (b : int) = a + b - // paramDatas will contain information about a and retTy will be: int -> int - // This is good enough as we don't provide ways to display info for the second curried argument - let firstCurriedParamDatas = - firstCurriedArgInfo - |> List.map ParamNameAndType.FromArgInfo - |> List.map (fun (ParamNameAndType(nmOpt, pty)) -> ParamData(false, false, false, NotOptional, NoCallerInfo, nmOpt, ReflectedArgInfo.None, pty)) + // .NET events + | Item.Event einfo -> + let eventTy = PropTypeOfEventInfo infoReader m AccessibleFromSomewhere einfo + let eventTy, _cxs = PrettyTypes.PrettifyType g eventTy + let layout = + wordL (tagText (FSComp.SR.typeInfoEvent())) ^^ + NicePrint.layoutTyconRef denv einfo.ApparentEnclosingTyconRef ^^ + SepL.dot ^^ + wordL (tagEvent einfo.EventName) ^^ + RightL.colon ^^ + NicePrint.layoutType denv eventTy + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, ?symbol = symbol) - // Adjust the return type so it only strips the first argument - let curriedRetTy = - match tryDestFunTy denv.g vref.TauType with - | ValueSome(_, retTy) -> retTy - | _ -> lastRetTy + // F# and .NET properties + | Item.Property(_, pinfo :: _) -> + let layout = NicePrint.prettyLayoutOfPropInfoFreeStyle g amap m denv pinfo + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, ?symbol = symbol) - let _prettyTyparInst, prettyFirstCurriedParams, prettyCurriedRetTyL, prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation firstCurriedParamDatas curriedRetTy - - let prettyCurriedRetTyL = prettyCurriedRetTyL ^^ SepL.space ^^ prettyConstraintsL + // Custom operations in queries + | Item.CustomOperation (customOpName, usageText, Some minfo) -> - prettyFirstCurriedParams, prettyCurriedRetTyL + // Build 'custom operation: where (bool) + // + // Calls QueryBuilder.Where' + let layout = + wordL (tagText (FSComp.SR.typeInfoCustomOperation())) ^^ + RightL.colon ^^ + ( + match usageText() with + | Some t -> wordL (tagText t) + | None -> + let argTys = ParamNameAndTypesOfUnaryCustomOperation g minfo |> List.map (fun (ParamNameAndType(_, ty)) -> ty) + let argTys, _ = PrettyTypes.PrettifyTypes g argTys + wordL (tagMethod customOpName) ^^ sepListL SepL.space (List.map (fun ty -> LeftL.leftParen ^^ NicePrint.layoutType denv ty ^^ SepL.rightParen) argTys) + ) ^^ + SepL.lineBreak ^^ SepL.lineBreak ^^ + wordL (tagText (FSComp.SR.typeInfoCallsWord())) ^^ + NicePrint.layoutTyconRef denv minfo.ApparentEnclosingTyconRef ^^ + SepL.dot ^^ + wordL (tagMethod minfo.DisplayName) - | Item.UnionCase(ucinfo, _) -> - let prettyParams = - match ucinfo.UnionCase.RecdFields with - | [f] -> [PrettyParamOfUnionCaseField g denv NicePrint.isGeneratedUnionCaseField -1 f] - | fs -> fs |> List.mapi (PrettyParamOfUnionCaseField g denv NicePrint.isGeneratedUnionCaseField) - let unionTy = generalizedTyconRef g ucinfo.TyconRef - let rtyL = NicePrint.layoutType denv unionTy - prettyParams, rtyL + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, ?symbol = symbol) - | Item.ActivePatternCase(apref) -> - let v = apref.ActivePatternVal - let vTauTy = v.TauType - let args, resTy = stripFunTy denv.g vTauTy + // F# constructors and methods + | Item.CtorGroup(_, minfos) + | Item.MethodGroup(_, minfos, _) -> + FormatOverloadsToList infoReader m denv item minfos symbol width + + // The 'fake' zero-argument constructors of .NET interfaces. + // This ideally should never appear in intellisense, but we do get here in repros like: + // type IFoo = abstract F : int + // type II = IFoo // remove 'type II = ' and quickly hover over IFoo before it gets squiggled for 'invalid use of interface type' + // and in that case we'll just show the interface type name. + | Item.FakeInterfaceCtor ty -> + let ty, _ = PrettyTypes.PrettifyType g ty + let layout = NicePrint.layoutTyconRef denv (tcrefOfAppTy g ty) + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single(layout, xml, ?symbol = symbol) + + // The 'fake' representation of constructors of .NET delegate types + | Item.DelegateCtor delTy -> + let delTy, _cxs = PrettyTypes.PrettifyType g delTy + let (SigOfFunctionForDelegate(_, _, _, delFuncTy)) = GetSigOfFunctionForDelegate infoReader delTy m AccessibleFromSomewhere + let layout = + NicePrint.layoutTyconRef denv (tcrefOfAppTy g delTy) ^^ + LeftL.leftParen ^^ + NicePrint.layoutType denv delFuncTy ^^ + RightL.rightParen + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single(layout, xml, ?symbol = symbol) - let apinfo = Option.get (TryGetActivePatternInfo v) - let aparity = apinfo.ActiveTags.Length - - let caseTy = if aparity <= 1 then resTy else (argsOfAppTy g resTy)[apref.CaseIndex] + // Types. + | Item.Types(_, TType_app(tcref, _, _) :: _) + | Item.UnqualifiedType (tcref :: _) -> + let denv = { denv with + // tooltips are space-constrained, so use shorter names + shortTypeNames = true + // tooltips are space-constrained, so don't include xml doc comments + // on types/members. The doc comments for the actual member will still + // be shown in the tip. + showDocumentation = false } + let layout = NicePrint.layoutTyconDefn denv infoReader ad m (* width *) tcref.Deref + let layout = PrintUtilities.squashToWidth width layout + let remarks = OutputFullName displayFullName pubpathOfTyconRef fullDisplayTextOfTyconRefAsLayout tcref + let layout = toArray layout + let remarks = toArray remarks + ToolTipElement.Single (layout, xml, remarks=remarks, ?symbol = symbol) - let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfTypes g denv item.TyparInstantiation args caseTy - // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned - // for display as part of the method group - prettyParams, prettyRetTyL + // Type variables + | Item.TypeVar (_, typar) -> + let layout = NicePrint.prettyLayoutOfTypar denv typar + let layout = PrintUtilities.squashToWidth width layout + ToolTipElement.Single (toArray layout, xml, ?symbol = symbol) - | Item.ExnCase ecref -> - let prettyParams = ecref |> recdFieldsOfExnDefRef |> List.mapi (PrettyParamOfUnionCaseField g denv NicePrint.isGeneratedExceptionField) - let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] g.exn_ty - prettyParams, prettyRetTyL + // Traits + | Item.Trait traitInfo -> + let denv = { denv with shortConstraints = false} + let layout = NicePrint.prettyLayoutOfTrait denv traitInfo + let layout = PrintUtilities.squashToWidth width layout + ToolTipElement.Single (toArray layout, xml, ?symbol = symbol) - | Item.RecdField rfinfo -> - let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] rfinfo.FieldType - [], prettyRetTyL + // F# Modules and namespaces + | Item.ModuleOrNamespaces(modref :: _ as modrefs) -> + //let os = StringBuilder() + let modrefs = modrefs |> RemoveDuplicateModuleRefs + let definiteNamespace = modrefs |> List.forall (fun modref -> modref.IsNamespace) + let kind = + if definiteNamespace then FSComp.SR.typeInfoNamespace() + elif modrefs |> List.forall (fun modref -> modref.IsModule) then FSComp.SR.typeInfoModule() + else FSComp.SR.typeInfoNamespaceOrModule() + + let layout = + wordL (tagKeyword kind) ^^ + (if definiteNamespace then tagNamespace (fullDisplayTextOfModRef modref) else (tagModule modref.DemangledModuleOrNamespaceName) + |> mkNav modref.DefinitionRange + |> wordL) + if not definiteNamespace then + let namesToAdd = + ([], modrefs) + ||> Seq.fold (fun st modref -> + match fullDisplayTextOfParentOfModRef modref with + | ValueSome txt -> txt :: st + | _ -> st) + |> Seq.mapi (fun i x -> i, x) + |> Seq.toList + let layout = + layout ^^ + ( + if not (List.isEmpty namesToAdd) then + SepL.lineBreak ^^ + List.fold ( fun s (i, txt) -> + s ^^ + SepL.lineBreak ^^ + wordL (tagText ((if i = 0 then FSComp.SR.typeInfoFromFirst else FSComp.SR.typeInfoFromNext) txt)) + ) emptyL namesToAdd + else + emptyL + ) + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, ?symbol = symbol) + else + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, ?symbol = symbol) - | Item.AnonRecdField(_anonInfo, tys, i, _) -> - let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] tys[i] - [], prettyRetTyL + | Item.AnonRecdField(anon, argTys, i, _) -> + let argTy = argTys[i] + let nm = anon.DisplayNameByIdx i + let argTy, _ = PrettyTypes.PrettifyType g argTy + let layout = + wordL (tagText (FSComp.SR.typeInfoAnonRecdField())) ^^ + wordL (tagRecordField nm) ^^ + RightL.colon ^^ + NicePrint.layoutType denv argTy + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, FSharpXmlDoc.None, ?symbol = symbol) + + // Named parameters + | Item.ArgName (Some id, argTy, _, _) -> + let argTy, _ = PrettyTypes.PrettifyType g argTy + let layout = + wordL (tagText (FSComp.SR.typeInfoArgument())) ^^ + wordL (tagParameter id.idText) ^^ + RightL.colon ^^ + NicePrint.layoutType denv argTy + let layout = PrintUtilities.squashToWidth width layout + let layout = toArray layout + ToolTipElement.Single (layout, xml, paramName = id.idText, ?symbol = symbol) + + | Item.SetterArg (_, item) -> + FormatItemDescriptionToToolTipElement displayFullName infoReader ad m denv (ItemWithNoInst item) symbol width - | Item.ILField finfo -> - let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] (finfo.FieldType(amap, m)) - [], prettyRetTyL + | Item.ArgName (None, _, _, _) + + // TODO: give a decent tooltip for implicit operators that include the resolution of the operator + // + //type C() = + // static member (++++++) (x: C, y: C) = C() + // + //let f (x: C) = + // x ++++++ x + // + // Here hovering over "++++++" in "f" could give a tooltip saying what the thing is and what it has resolved to. + // + // + | Item.ImplicitOp _ - | Item.Event einfo -> - let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] (PropTypeOfEventInfo infoReader m AccessibleFromSomewhere einfo) - [], prettyRetTyL + // TODO: consider why we aren't getting Item.Types for generic type parameters + // let F<'T>() = new System.Collections.Generic.List<'T>() + | Item.Types (_, [TType_var _]) - | Item.Property(_, pinfo :: _) -> - let paramDatas = pinfo.GetParamDatas(amap, m) - let propTy = pinfo.GetPropertyType(amap, m) + // TODO: consider why we aren't getting Item.Types for units of measure + | Item.Types (_, [TType_measure _]) - let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation paramDatas propTy - // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned - // for display as part of the method group - prettyParams, prettyRetTyL + // TODO: consider whether we ever get Item.Types with more than one element + | Item.Types (_, _ :: _ :: _) - | Item.CtorGroup(_, minfo :: _) - | Item.MethodGroup(_, minfo :: _, _) -> - let paramDatas = minfo.GetParamDatas(amap, m, minfo.FormalMethodInst) |> List.head - let retTy = minfo.GetFSharpReturnType(amap, m, minfo.FormalMethodInst) - let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation paramDatas retTy - // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned - // for display as part of the method group - prettyParams, prettyRetTyL + // We don't expect Item.Types with an anonymous record type, function types etc. + | Item.Types (_, [TType_anon _]) + | Item.Types (_, [TType_fun _]) + | Item.Types (_, [TType_forall _]) + | Item.Types (_, [TType_tuple _]) + | Item.Types (_, [TType_ucase _]) - | Item.Trait traitInfo -> - let paramDatas = - [ for pty in traitInfo.GetLogicalArgumentTypes(g) do - ParamData(false, false, false, OptionalArgInfo.NotOptional, CallerInfo.NoCallerInfo, None, ReflectedArgInfo.None, pty) ] - let retTy = traitInfo.GetReturnType(g) - let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation paramDatas retTy - prettyParams, prettyRetTyL + // We don't expect these cases + | Item.Types (_, []) + | Item.Property (_, []) + | Item.UnqualifiedType [] + | Item.ModuleOrNamespaces [] + | Item.CustomOperation (_, _, None) -> ToolTipElement.None - | Item.CustomBuilder (_, vref) -> - PrettyParamsAndReturnTypeOfItem infoReader m denv { item with Item = Item.Value vref } + /// Format the structured version of a tooltip for an item + let FormatStructuredDescriptionOfItem isDecl infoReader ad m denv item symbol width = + DiagnosticsScope.Protect m + (fun () -> FormatItemDescriptionToToolTipElement isDecl infoReader ad m denv item symbol width) + (fun err -> ToolTipElement.CompositionError err) - | Item.TypeVar _ -> - [], emptyL +/// Represents one parameter for one method (or other item) in a group. +[] +type MethodGroupItemParameter(name: string, canonicalTypeTextForSorting: string, display: TaggedText[], isOptional: bool) = - | Item.CustomOperation (_, usageText, Some minfo) -> - match usageText() with - | None -> - let argNamesAndTys = ParamNameAndTypesOfUnaryCustomOperation g minfo - let argTys, _ = PrettyTypes.PrettifyTypes g (argNamesAndTys |> List.map (fun (ParamNameAndType(_, ty)) -> ty)) - let paramDatas = (argNamesAndTys, argTys) ||> List.map2 (fun (ParamNameAndType(nmOpt, _)) argTy -> ParamData(false, false, false, NotOptional, NoCallerInfo, nmOpt, ReflectedArgInfo.None, argTy)) - let retTy = minfo.GetFSharpReturnType(amap, m, minfo.FormalMethodInst) - let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation paramDatas retTy + /// The name of the parameter. + member _.ParameterName = name - // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned - // for display as part of the method group - prettyParams, prettyRetTyL + /// A key that can be used for sorting the parameters, used to help sort overloads. + member _.CanonicalTypeTextForSorting = canonicalTypeTextForSorting - | Some _ -> - let retTy = minfo.GetFSharpReturnType(amap, m, minfo.FormalMethodInst) - let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] retTy - [], prettyRetTyL // no parameter data available for binary operators like 'zip', 'join' and 'groupJoin' since they use bespoke syntax + /// The text to display for the parameter including its name, its type and visual indicators of other + /// information such as whether it is optional. + member _.Display = display - | Item.FakeInterfaceCtor ty -> - let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] ty - [], prettyRetTyL + /// Is the parameter optional + member _.IsOptional = isOptional - | Item.DelegateCtor delTy -> - let (SigOfFunctionForDelegate(_, _, _, delFuncTy)) = GetSigOfFunctionForDelegate infoReader delTy m AccessibleFromSomewhere +[] +module internal DescriptionListsImpl = - // No need to pass more generic type information in here since the instanitations have already been applied - let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation [ParamData(false, false, false, NotOptional, NoCallerInfo, None, ReflectedArgInfo.None, delFuncTy)] delTy + let isFunction g ty = + let _, tauTy = tryDestForallTy g ty + isFunTy g tauTy + + let printCanonicalizedTypeName g (denv:DisplayEnv) tauTy = + // get rid of F# abbreviations and such + let strippedTy = stripTyEqnsWrtErasure EraseAll g tauTy + // pretend no namespaces are open + let denv = denv.SetOpenPaths([]) + // now printing will see a .NET-like canonical representation, that is good for sorting overloads into a reasonable order (see bug 94520) + NicePrint.stringOfTy denv strippedTy - // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned - // for display as part of the method group - prettyParams, prettyRetTyL + let PrettyParamOfRecdField g denv (f: RecdField) = + let display = NicePrint.prettyLayoutOfType denv f.FormalType + let display = toArray display + MethodGroupItemParameter( + name = f.DisplayNameCore, + canonicalTypeTextForSorting = printCanonicalizedTypeName g denv f.FormalType, + display = display, + isOptional=false) + + let PrettyParamOfUnionCaseField g denv isGenerated (i: int) (f: RecdField) = + let initial = PrettyParamOfRecdField g denv f + let display = + if isGenerated i f then + initial.Display + else + let display = NicePrint.layoutOfParamData denv (ParamData(false, false, false, NotOptional, NoCallerInfo, Some f.Id, ReflectedArgInfo.None, f.FormalType)) + toArray display - | Item.CustomOperation _ // TODO: consider whether this should report parameter help - | Item.ActivePatternResult _ // TODO: consider whether this should report parameter help - | Item.UnqualifiedType _ - | Item.UnionCaseField _ - | Item.Types _ - | Item.SetterArg _ - | Item.NewDef _ - | Item.ModuleOrNamespaces _ - | Item.ImplicitOp _ - | Item.ArgName _ - | Item.MethodGroup(_, [], _) - | Item.CtorGroup(_,[]) - | Item.Property(_,[]) -> - [], emptyL + MethodGroupItemParameter( + name=initial.ParameterName, + canonicalTypeTextForSorting=initial.CanonicalTypeTextForSorting, + display=display, + isOptional=false) + let ParamOfParamData g denv (ParamData(_isParamArrayArg, _isInArg, _isOutArg, optArgInfo, _callerInfo, nmOpt, _reflArgInfo, pty) as paramData) = + let display = NicePrint.layoutOfParamData denv paramData + let display = toArray display + MethodGroupItemParameter( + name = (match nmOpt with None -> "" | Some pn -> pn.idText), + canonicalTypeTextForSorting = printCanonicalizedTypeName g denv pty, + display = display, + isOptional=optArgInfo.IsOptional) - /// Compute the index of the VS glyph shown with an item in the Intellisense menu - let GlyphOfItem(denv, item) : FSharpGlyph = - /// Find the glyph for the given representation. - let reprToGlyph repr = - match repr with - | TFSharpObjectRepr om -> - match om.fsobjmodel_kind with - | TFSharpClass -> FSharpGlyph.Class - | TFSharpInterface -> FSharpGlyph.Interface - | TFSharpStruct -> FSharpGlyph.Struct - | TFSharpDelegate _ -> FSharpGlyph.Delegate - | TFSharpEnum -> FSharpGlyph.Enum - | TFSharpRecdRepr _ -> FSharpGlyph.Type - | TFSharpUnionRepr _ -> FSharpGlyph.Union - | TILObjectRepr (TILObjectReprData (_, _, td)) -> - if td.IsClass then FSharpGlyph.Class - elif td.IsStruct then FSharpGlyph.Struct - elif td.IsInterface then FSharpGlyph.Interface - elif td.IsEnum then FSharpGlyph.Enum - else FSharpGlyph.Delegate - | TAsmRepr _ -> FSharpGlyph.Typedef - | TMeasureableRepr _-> FSharpGlyph.Typedef -#if !NO_TYPEPROVIDERS - | TProvidedTypeRepr _-> FSharpGlyph.Typedef - | TProvidedNamespaceRepr _-> FSharpGlyph.Typedef -#endif - | TNoRepr -> FSharpGlyph.Class - - /// Find the glyph for the given type representation. - let typeToGlyph ty = - match tryTcrefOfAppTy denv.g ty with - | ValueSome tcref -> tcref.TypeReprInfo |> reprToGlyph - | _ -> - if isStructTupleTy denv.g ty then FSharpGlyph.Struct - elif isRefTupleTy denv.g ty then FSharpGlyph.Class - elif isFunction denv.g ty then FSharpGlyph.Delegate - elif isTyparTy denv.g ty then FSharpGlyph.Struct - else FSharpGlyph.Typedef - - // This may explore assemblies that are not in the reference set, - // e.g. for type abbreviations to types not in the reference set. - // In this case just use GlyphMajor.Class. - protectAssemblyExploration FSharpGlyph.Class (fun () -> - match item with - | Item.Value(vref) | Item.CustomBuilder (_, vref) -> - if isFunction denv.g vref.Type then FSharpGlyph.Method - elif vref.LiteralValue.IsSome then FSharpGlyph.Constant - else FSharpGlyph.Variable - | Item.Types(_, ty :: _) -> typeToGlyph (stripTyEqns denv.g ty) - | Item.UnionCase _ - | Item.ActivePatternResult _ - | Item.ImplicitOp _ - | Item.ActivePatternCase _ -> FSharpGlyph.EnumMember - | Item.ExnCase _ -> FSharpGlyph.Exception - | Item.AnonRecdField _ -> FSharpGlyph.Field - | Item.RecdField _ -> FSharpGlyph.Field - | Item.UnionCaseField _ -> FSharpGlyph.Field - | Item.ILField _ -> FSharpGlyph.Field - | Item.Event _ -> FSharpGlyph.Event - | Item.Property _ -> FSharpGlyph.Property - | Item.CtorGroup _ - | Item.DelegateCtor _ - | Item.FakeInterfaceCtor _ - | Item.CustomOperation _ -> FSharpGlyph.Method - | Item.MethodGroup (_, minfos, _) when minfos |> List.forall (fun minfo -> minfo.IsExtensionMember) -> FSharpGlyph.ExtensionMethod - | Item.MethodGroup _ -> FSharpGlyph.Method - | Item.Trait _ -> FSharpGlyph.Method - | Item.TypeVar _ -> FSharpGlyph.TypeParameter - | Item.Types _ -> FSharpGlyph.Class - | Item.UnqualifiedType (tcref :: _) -> - if tcref.IsEnumTycon || tcref.IsILEnumTycon then FSharpGlyph.Enum - elif tcref.IsFSharpException then FSharpGlyph.Exception - elif tcref.IsFSharpDelegateTycon then FSharpGlyph.Delegate - elif tcref.IsFSharpInterfaceTycon then FSharpGlyph.Interface - elif tcref.IsFSharpStructOrEnumTycon then FSharpGlyph.Struct - elif tcref.IsModule then FSharpGlyph.Module - elif tcref.IsNamespace then FSharpGlyph.NameSpace - elif tcref.IsUnionTycon then FSharpGlyph.Union - elif tcref.IsILTycon then - let (TILObjectReprData (_, _, tydef)) = tcref.ILTyconInfo - if tydef.IsInterface then FSharpGlyph.Interface - elif tydef.IsDelegate then FSharpGlyph.Delegate - elif tydef.IsEnum then FSharpGlyph.Enum - elif tydef.IsStruct then FSharpGlyph.Struct - else FSharpGlyph.Class - else FSharpGlyph.Class - | Item.ModuleOrNamespaces(modref :: _) -> - if modref.IsNamespace then FSharpGlyph.NameSpace else FSharpGlyph.Module - | Item.NewDef _ - | Item.ArgName _ - | Item.SetterArg _ -> FSharpGlyph.Variable + // TODO this code is similar to NicePrint.fs:formatParamDataToBuffer, refactor or figure out why different? + let PrettyParamsOfParamDatas g denv typarInst (paramDatas:ParamData list) paramTy = + let paramInfo, paramTypes = + paramDatas + |> List.map (fun (ParamData(isParamArrayArg, _isInArg, _isOutArg, optArgInfo, _callerInfo, nmOpt, _reflArgInfo, pty)) -> + let isOptArg = optArgInfo.IsOptional + match nmOpt, isOptArg, tryDestOptionTy denv.g pty with + // Layout an optional argument + | Some id, true, ptyOpt -> + let nm = id.idText + // detect parameter type, if ptyOpt is None - this is .NET style optional argument + let pty = match ptyOpt with ValueSome x -> x | _ -> pty + (nm, isOptArg, SepL.questionMark ^^ (wordL (tagParameter nm))), pty + // Layout an unnamed argument + | None, _, _ -> + ("", isOptArg, emptyL), pty + // Layout a named argument + | Some id, _, _ -> + let nm = id.idText + let prefix = + if isParamArrayArg then + NicePrint.PrintUtilities.layoutBuiltinAttribute denv denv.g.attrib_ParamArrayAttribute ^^ + wordL (tagParameter nm) ^^ + RightL.colon + //sprintf "%s %s: " (NicePrint.PrintUtilities.layoutBuiltinAttribute denv denv.g.attrib_ParamArrayAttribute |> showL) nm + else + wordL (tagParameter nm) ^^ + RightL.colon + //sprintf "%s: " nm + (nm, isOptArg, prefix), pty) + |> List.unzip - // These empty lists are not expected to occur - | Item.ModuleOrNamespaces [] - | Item.UnqualifiedType [] -> - FSharpGlyph.Error - ) + // Prettify everything + let prettyTyparInst, (prettyParamTys, _prettyRetTy), (prettyParamTysL, prettyRetTyL), prettyConstraintsL = + NicePrint.prettyLayoutOfInstAndSig denv (typarInst, paramTypes, paramTy) + // Remake the params using the prettified versions + let prettyParams = + (paramInfo, prettyParamTys, prettyParamTysL) |||> List.map3 (fun (nm, isOptArg, paramPrefix) tauTy tyL -> + let display = paramPrefix ^^ tyL + let display = toArray display + MethodGroupItemParameter( + name = nm, + canonicalTypeTextForSorting = printCanonicalizedTypeName g denv tauTy, + display = display, + isOptional=isOptArg + )) - /// Select the items that participate in a MethodGroup. - let SelectMethodGroupItems g m item = -#if NO_TYPEPROVIDERS - ignore m -#endif - match item with - | Item.CtorGroup(nm, cinfos) -> List.map (fun minfo -> Item.CtorGroup(nm, [minfo])) cinfos - | Item.Trait traitInfo -> - if traitInfo.GetLogicalArgumentTypes(g).IsEmpty then [] else [item] - | Item.FakeInterfaceCtor _ - | Item.DelegateCtor _ -> [item] - | Item.NewDef _ - | Item.ILField _ -> [] - | Item.Event _ -> [] - | Item.RecdField(rfinfo) -> - if isFunction g rfinfo.FieldType then [item] else [] - | Item.Value v -> - if isFunction g v.Type then [item] else [] - | Item.UnionCase(ucr, _) -> - if not ucr.UnionCase.IsNullary then [item] else [] - | Item.ExnCase(ecr) -> - if isNil (recdFieldsOfExnDefRef ecr) then [] else [item] - | Item.Property(_, pinfos) -> - let pinfo = List.head pinfos - if pinfo.IsIndexer then [item] else [] -#if !NO_TYPEPROVIDERS - | ItemIsWithStaticArguments m g _ -> - // we pretend that provided-types-with-static-args are method-like in order to get ParamInfo for them - [item] -#endif - | Item.MethodGroup(nm, minfos, orig) -> minfos |> List.map (fun minfo -> Item.MethodGroup(nm, [minfo], orig)) - | Item.CustomOperation _ -> [item] - // These are not items that can participate in a method group - | Item.TypeVar _ - | Item.CustomBuilder _ - | Item.ActivePatternCase _ - | Item.AnonRecdField _ - | Item.ArgName _ - | Item.ImplicitOp _ - | Item.ModuleOrNamespaces _ - | Item.SetterArg _ - | Item.Types _ - | Item.UnionCaseField _ - | Item.UnqualifiedType _ - | Item.ActivePatternResult _ -> [] + prettyTyparInst, prettyParams, prettyRetTyL, prettyConstraintsL + let PrettyParamsOfTypes g denv typarInst paramTys retTy = -[] -module DeclarationListHelpers = - let mutable ToolTipFault = None + // Prettify everything + let prettyTyparInst, (prettyParamTys, _prettyRetTy), (prettyParamTysL, prettyRetTyL), prettyConstraintsL = + NicePrint.prettyLayoutOfInstAndSig denv (typarInst, paramTys, retTy) - let emptyToolTip = ToolTipText [] + // Remake the params using the prettified versions + let parameters = + (prettyParamTys, prettyParamTysL) + ||> List.map2 (fun paramTy tyL -> + let display = toArray tyL + MethodGroupItemParameter( + name = "", + canonicalTypeTextForSorting = printCanonicalizedTypeName g denv paramTy, + display = display, + isOptional=false + )) - /// Generate the structured tooltip for a method info - let FormatOverloadsToList (infoReader: InfoReader) m denv (item: ItemWithInst) minfos symbol (width: int option) : ToolTipElement = - ToolTipFault |> Option.iter (fun msg -> - let exn = Error((0, msg), range.Zero) - let ph = PhasedDiagnostic.Create(exn, BuildPhase.TypeCheck) - simulateError ph) - - let layouts = - [ for minfo in minfos -> - let prettyTyparInst, layout = NicePrint.prettyLayoutOfMethInfoFreeStyle infoReader m denv item.TyparInstantiation minfo - let xml = GetXmlCommentForMethInfoItem infoReader m item.Item minfo - let tpsL = FormatTyparMapping denv prettyTyparInst - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - let tpsL = List.map toArray tpsL - ToolTipElementData.Create(layout, xml, tpsL, ?symbol = symbol) ] - - ToolTipElement.Group layouts - - let CompletionItemDisplayPartialEquality g = - let itemComparer = ItemDisplayPartialEquality g - - { new IPartialEqualityComparer with - member x.InEqualityRelation item = itemComparer.InEqualityRelation item.Item - member x.Equals(item1, item2) = itemComparer.Equals(item1.Item, item2.Item) - member x.GetHashCode item = itemComparer.GetHashCode(item.Item) } + // Return the results + prettyTyparInst, parameters, prettyRetTyL, prettyConstraintsL + - /// Remove all duplicate items - let RemoveDuplicateCompletionItems g items = - if isNil items then items else - items |> IPartialEqualityComparer.partialDistinctBy (CompletionItemDisplayPartialEquality g) +#if !NO_TYPEPROVIDERS - /// Filter types that are explicitly suppressed from the IntelliSense (such as uppercase "FSharpList", "Option", etc.) - let RemoveExplicitlySuppressedCompletionItems (g: TcGlobals) (items: CompletionItem list) = - items |> List.filter (fun item -> not (IsExplicitlySuppressed g item.Item)) + /// Get the set of static parameters associated with an item + let StaticParamsOfItem (infoReader:InfoReader) m denv item = + let amap = infoReader.amap + let g = infoReader.g + match item with + | ItemIsWithStaticArguments m g staticParameters -> + staticParameters + |> Array.map (fun sp -> + let ty = Import.ImportProvidedType amap m (sp.PApply((fun x -> x.ParameterType), m)) + let spKind = NicePrint.prettyLayoutOfType denv ty + let spName = sp.PUntaint((fun sp -> sp.Name), m) + let spOpt = sp.PUntaint((fun sp -> sp.IsOptional), m) + let display = (if spOpt then SepL.questionMark else emptyL) ^^ wordL (tagParameter spName) ^^ RightL.colon ^^ spKind + let display = toArray display + MethodGroupItemParameter( + name = spName, + canonicalTypeTextForSorting = showL spKind, + display = display, + //display = sprintf "%s%s: %s" (if spOpt then "?" else "") spName spKind, + isOptional=spOpt)) + | _ -> [| |] +#endif - // Remove items containing the same module references - let RemoveDuplicateModuleRefs modrefs = - modrefs |> IPartialEqualityComparer.partialDistinctBy - { new IPartialEqualityComparer with - member x.InEqualityRelation _ = true - member x.Equals(item1, item2) = (fullDisplayTextOfModRef item1 = fullDisplayTextOfModRef item2) - member x.GetHashCode item = hash item.Stamp } + /// Get all the information about parameters and "prettify" the types by choosing nice type variable + /// names. This is similar to the other variations on "show me an item" code. This version is + /// is used when presenting groups of methods (see MethodGroup). It is possible these different + /// versions could be better unified. + let rec PrettyParamsAndReturnTypeOfItem (infoReader:InfoReader) m denv (item: ItemWithInst) = + let amap = infoReader.amap + let g = infoReader.g + let denv = { SimplerDisplayEnv denv with useColonForReturnType=true} + match item.Item with + | Item.Value vref -> - let OutputFullName displayFullName ppF fnF r = - // Only display full names in quick info, not declaration lists or method lists - if not displayFullName then - match ppF r with - | None -> emptyL - | Some _ -> wordL (tagText (FSComp.SR.typeInfoFullName())) ^^ RightL.colon ^^ (fnF r) - else emptyL + let getPrettyParamsOfTypes() = + let vTauTy = vref.TauType + match tryDestFunTy denv.g vTauTy with + | ValueSome(arg, retTy) -> + let args = tryDestRefTupleTy denv.g arg + let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfTypes g denv item.TyparInstantiation args retTy + // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned + // for display as part of the method group + prettyParams, prettyRetTyL + | _ -> + let _prettyTyparInst, prettyTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] vTauTy + [], prettyTyL - let pubpathOfValRef (v: ValRef) = v.PublicPath + match vref.ValReprInfo with + | None -> + // ValReprInfo = None i.e. in let bindings defined in types or in local functions + // in this case use old approach and return only information about types + getPrettyParamsOfTypes () - let pubpathOfTyconRef (x: TyconRef) = x.PublicPath + | Some valReprInfo -> + // ValReprInfo will exist for top-level syntactic functions + // per spec: binding is considered to define a syntactic function if it is either a function or its immediate right-hand-side is a anonymous function + let _, argInfos, lastRetTy, _ = GetValReprTypeInFSharpForm g valReprInfo vref.Type m + match argInfos with + | [] -> + // handles cases like 'let foo = List.map' + getPrettyParamsOfTypes() + | firstCurriedArgInfo :: _ -> + // result 'paramDatas' collection corresponds to the first argument of curried function + // i.e. let func (a : int) (b : int) = a + b + // paramDatas will contain information about a and retTy will be: int -> int + // This is good enough as we don't provide ways to display info for the second curried argument + let firstCurriedParamDatas = + firstCurriedArgInfo + |> List.map ParamNameAndType.FromArgInfo + |> List.map (fun (ParamNameAndType(nmOpt, pty)) -> ParamData(false, false, false, NotOptional, NoCallerInfo, nmOpt, ReflectedArgInfo.None, pty)) + + // Adjust the return type so it only strips the first argument + let curriedRetTy = + match tryDestFunTy denv.g vref.TauType with + | ValueSome(_, retTy) -> retTy + | _ -> lastRetTy + + let _prettyTyparInst, prettyFirstCurriedParams, prettyCurriedRetTyL, prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation firstCurriedParamDatas curriedRetTy + + let prettyCurriedRetTyL = prettyCurriedRetTyL ^^ SepL.space ^^ prettyConstraintsL + + prettyFirstCurriedParams, prettyCurriedRetTyL + + | Item.UnionCase(ucinfo, _) -> + let prettyParams = + match ucinfo.UnionCase.RecdFields with + | [f] -> [PrettyParamOfUnionCaseField g denv NicePrint.isGeneratedUnionCaseField -1 f] + | fs -> fs |> List.mapi (PrettyParamOfUnionCaseField g denv NicePrint.isGeneratedUnionCaseField) + let unionTy = generalizedTyconRef g ucinfo.TyconRef + let rtyL = NicePrint.layoutType denv unionTy + prettyParams, rtyL + + | Item.ActivePatternCase(apref) -> + let v = apref.ActivePatternVal + let vTauTy = v.TauType + let args, resTy = stripFunTy denv.g vTauTy + + let apinfo = Option.get (TryGetActivePatternInfo v) + let aparity = apinfo.ActiveTags.Length + + let caseTy = if aparity <= 1 then resTy else (argsOfAppTy g resTy)[apref.CaseIndex] + + let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfTypes g denv item.TyparInstantiation args caseTy + // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned + // for display as part of the method group + prettyParams, prettyRetTyL + + | Item.ExnCase ecref -> + let prettyParams = ecref |> recdFieldsOfExnDefRef |> List.mapi (PrettyParamOfUnionCaseField g denv NicePrint.isGeneratedExceptionField) + let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] g.exn_ty + prettyParams, prettyRetTyL + + | Item.RecdField rfinfo -> + let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] rfinfo.FieldType + [], prettyRetTyL + + | Item.AnonRecdField(_anonInfo, tys, i, _) -> + let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] tys[i] + [], prettyRetTyL + + | Item.ILField finfo -> + let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] (finfo.FieldType(amap, m)) + [], prettyRetTyL + + | Item.Event einfo -> + let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] (PropTypeOfEventInfo infoReader m AccessibleFromSomewhere einfo) + [], prettyRetTyL + + | Item.Property(_, pinfo :: _) -> + let paramDatas = pinfo.GetParamDatas(amap, m) + let propTy = pinfo.GetPropertyType(amap, m) + + let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation paramDatas propTy + // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned + // for display as part of the method group + prettyParams, prettyRetTyL + + | Item.CtorGroup(_, minfo :: _) + | Item.MethodGroup(_, minfo :: _, _) -> + let paramDatas = minfo.GetParamDatas(amap, m, minfo.FormalMethodInst) |> List.head + let retTy = minfo.GetFSharpReturnType(amap, m, minfo.FormalMethodInst) + let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation paramDatas retTy + // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned + // for display as part of the method group + prettyParams, prettyRetTyL + + | Item.Trait traitInfo -> + let paramDatas = + [ for pty in traitInfo.GetLogicalArgumentTypes(g) do + ParamData(false, false, false, OptionalArgInfo.NotOptional, CallerInfo.NoCallerInfo, None, ReflectedArgInfo.None, pty) ] + let retTy = traitInfo.GetReturnType(g) + let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation paramDatas retTy + prettyParams, prettyRetTyL + + | Item.CustomBuilder (_, vref) -> + PrettyParamsAndReturnTypeOfItem infoReader m denv { item with Item = Item.Value vref } + + | Item.TypeVar _ -> + [], emptyL + + | Item.CustomOperation (_, usageText, Some minfo) -> + match usageText() with + | None -> + let argNamesAndTys = ParamNameAndTypesOfUnaryCustomOperation g minfo + let argTys, _ = PrettyTypes.PrettifyTypes g (argNamesAndTys |> List.map (fun (ParamNameAndType(_, ty)) -> ty)) + let paramDatas = (argNamesAndTys, argTys) ||> List.map2 (fun (ParamNameAndType(nmOpt, _)) argTy -> ParamData(false, false, false, NotOptional, NoCallerInfo, nmOpt, ReflectedArgInfo.None, argTy)) + let retTy = minfo.GetFSharpReturnType(amap, m, minfo.FormalMethodInst) + let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation paramDatas retTy + + // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned + // for display as part of the method group + prettyParams, prettyRetTyL + + | Some _ -> + let retTy = minfo.GetFSharpReturnType(amap, m, minfo.FormalMethodInst) + let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] retTy + [], prettyRetTyL // no parameter data available for binary operators like 'zip', 'join' and 'groupJoin' since they use bespoke syntax + + | Item.FakeInterfaceCtor ty -> + let _prettyTyparInst, prettyRetTyL = NicePrint.prettyLayoutOfUncurriedSig denv item.TyparInstantiation [] ty + [], prettyRetTyL + + | Item.DelegateCtor delTy -> + let (SigOfFunctionForDelegate(_, _, _, delFuncTy)) = GetSigOfFunctionForDelegate infoReader delTy m AccessibleFromSomewhere + + // No need to pass more generic type information in here since the instanitations have already been applied + let _prettyTyparInst, prettyParams, prettyRetTyL, _prettyConstraintsL = PrettyParamsOfParamDatas g denv item.TyparInstantiation [ParamData(false, false, false, NotOptional, NoCallerInfo, None, ReflectedArgInfo.None, delFuncTy)] delTy + + // FUTURE: prettyTyparInst is the pretty version of the known instantiations of type parameters in the output. It could be returned + // for display as part of the method group + prettyParams, prettyRetTyL + + | Item.CustomOperation _ // TODO: consider whether this should report parameter help + | Item.ActivePatternResult _ // TODO: consider whether this should report parameter help + | Item.UnqualifiedType _ + | Item.UnionCaseField _ + | Item.Types _ + | Item.SetterArg _ + | Item.NewDef _ + | Item.ModuleOrNamespaces _ + | Item.ImplicitOp _ + | Item.ArgName _ + | Item.MethodGroup(_, [], _) + | Item.CtorGroup(_,[]) + | Item.Property(_,[]) -> + [], emptyL - let isFunction g ty = - let _, tauTy = tryDestForallTy g ty - isFunTy g tauTy /// Compute the index of the VS glyph shown with an item in the Intellisense menu let GlyphOfItem(denv, item) : FSharpGlyph = @@ -745,386 +976,52 @@ module DeclarationListHelpers = FSharpGlyph.Error ) - /// Output the quick info information of a language item - let rec FormatItemDescriptionToToolTipElement displayFullName (infoReader: InfoReader) ad m denv (item: ItemWithInst) symbol (width: int option) = - let g = infoReader.g - let amap = infoReader.amap - let denv = SimplerDisplayEnv denv - let xml = GetXmlCommentForItem infoReader m item.Item - - match item.Item with - | Item.ImplicitOp(_, { contents = Some(TraitConstraintSln.FSMethSln(vref=vref)) }) -> - // operator with solution - FormatItemDescriptionToToolTipElement displayFullName infoReader ad m denv { item with Item = Item.Value vref } symbol width - - | Item.Value vref | Item.CustomBuilder (_, vref) -> - let prettyTyparInst, resL = NicePrint.layoutQualifiedValOrMember denv infoReader item.TyparInstantiation vref - let remarks = OutputFullName displayFullName pubpathOfValRef fullDisplayTextOfValRefAsLayout vref - let tpsL = FormatTyparMapping denv prettyTyparInst - let tpsL = List.map toArray tpsL - let resL = PrintUtilities.squashToWidth width resL - let resL = toArray resL - let remarks = toArray remarks - ToolTipElement.Single(resL, xml, tpsL, remarks=remarks, ?symbol = symbol) - - // Union tags (constructors) - | Item.UnionCase(ucinfo, _) -> - let uc = ucinfo.UnionCase - let unionTy = generalizedTyconRef g ucinfo.TyconRef - let recd = uc.RecdFields - let layout = - wordL (tagText (FSComp.SR.typeInfoUnionCase())) ^^ - NicePrint.layoutTyconRef denv ucinfo.TyconRef ^^ - sepL (tagPunctuation ".") ^^ - wordL (tagUnionCase (ConvertValLogicalNameToDisplayNameCore uc.Id.idText) |> mkNav uc.DefinitionRange) ^^ - RightL.colon ^^ - (if List.isEmpty recd then emptyL else NicePrint.layoutUnionCases denv infoReader ucinfo.TyconRef recd ^^ WordL.arrow) ^^ - NicePrint.layoutType denv unionTy - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml, ?symbol = symbol) - - // Active pattern tag inside the declaration (result) - | Item.ActivePatternResult(apinfo, ty, idx, _) -> - let items = apinfo.ActiveTags - let layout = - wordL (tagText (FSComp.SR.typeInfoActivePatternResult())) ^^ - wordL (tagActivePatternResult (List.item idx items) |> mkNav apinfo.Range) ^^ - RightL.colon ^^ - NicePrint.layoutType denv ty - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml, ?symbol = symbol) - // Active pattern tags - | Item.ActivePatternCase apref -> - let v = apref.ActivePatternVal - let vTauTy = v.TauType - // REVIEW: use _cxs here - let (prettyTyparInst, prettyTy), _cxs = PrettyTypes.PrettifyInstAndType denv.g (item.TyparInstantiation, vTauTy) - let remarks = OutputFullName displayFullName pubpathOfValRef fullDisplayTextOfValRefAsLayout v - let layout = - wordL (tagText (FSComp.SR.typeInfoActiveRecognizer())) ^^ - wordL (tagActivePatternCase apref.DisplayName |> mkNav v.DefinitionRange) ^^ - RightL.colon ^^ - NicePrint.layoutType denv prettyTy - let layout = PrintUtilities.squashToWidth width layout - - let tpsL = FormatTyparMapping denv prettyTyparInst - - let layout = toArray layout - let tpsL = List.map toArray tpsL - let remarks = toArray remarks - ToolTipElement.Single (layout, xml, tpsL, remarks=remarks, ?symbol = symbol) - - // F# exception names - | Item.ExnCase ecref -> - let layout = NicePrint.layoutExnDef denv infoReader ecref - let layout = PrintUtilities.squashToWidth width layout - let remarks = OutputFullName displayFullName pubpathOfTyconRef fullDisplayTextOfExnRefAsLayout ecref - let layout = toArray layout - let remarks = toArray remarks - ToolTipElement.Single (layout, xml, remarks=remarks, ?symbol = symbol) - - | Item.RecdField rfinfo when rfinfo.TyconRef.IsFSharpException -> - let ty, _ = PrettyTypes.PrettifyType g rfinfo.FieldType - let id = rfinfo.DisplayName - let layout = - wordL (tagText (FSComp.SR.typeInfoArgument())) ^^ - wordL (tagParameter id) ^^ - RightL.colon ^^ - NicePrint.layoutType denv ty - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml, paramName = id, ?symbol = symbol) - - // F# record field names - | Item.RecdField rfinfo -> - let rfield = rfinfo.RecdField - let ty, _cxs = PrettyTypes.PrettifyType g rfinfo.FieldType - let layout = - NicePrint.layoutTyconRef denv rfinfo.TyconRef ^^ - SepL.dot ^^ - wordL (tagRecordField rfield.DisplayName |> mkNav rfield.DefinitionRange) ^^ - RightL.colon ^^ - NicePrint.layoutType denv ty ^^ - ( - match rfinfo.LiteralValue with - | None -> emptyL - | Some lit -> try WordL.equals ^^ NicePrint.layoutConst denv.g ty lit with _ -> emptyL - ) - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml, ?symbol = symbol) - - | Item.UnionCaseField (ucinfo, fieldIndex) -> - let rfield = ucinfo.UnionCase.GetFieldByIndex(fieldIndex) - let fieldTy, _ = PrettyTypes.PrettifyType g rfield.rfield_type - let id = rfield.Id - let layout = - wordL (tagText (FSComp.SR.typeInfoArgument())) ^^ - wordL (tagParameter id.idText) ^^ - RightL.colon ^^ - NicePrint.layoutType denv fieldTy - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml, paramName = id.idText, ?symbol = symbol) - - // Not used - | Item.NewDef id -> - let layout = - wordL (tagText (FSComp.SR.typeInfoPatternVariable())) ^^ - wordL (tagUnknownEntity id.idText) - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml, ?symbol = symbol) - - // .NET fields - | Item.ILField finfo -> - let layout = - wordL (tagText (FSComp.SR.typeInfoField())) ^^ - NicePrint.layoutType denv finfo.ApparentEnclosingAppType ^^ - SepL.dot ^^ - wordL (tagField finfo.FieldName) ^^ - RightL.colon ^^ - NicePrint.layoutType denv (finfo.FieldType(amap, m)) ^^ - ( - match finfo.LiteralValue with - | None -> emptyL - | Some v -> - WordL.equals ^^ - try NicePrint.layoutConst denv.g (finfo.FieldType(infoReader.amap, m)) (CheckExpressions.TcFieldInit m v) with _ -> emptyL - ) - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml, ?symbol = symbol) - - // .NET events - | Item.Event einfo -> - let eventTy = PropTypeOfEventInfo infoReader m AccessibleFromSomewhere einfo - let eventTy, _cxs = PrettyTypes.PrettifyType g eventTy - let layout = - wordL (tagText (FSComp.SR.typeInfoEvent())) ^^ - NicePrint.layoutTyconRef denv einfo.ApparentEnclosingTyconRef ^^ - SepL.dot ^^ - wordL (tagEvent einfo.EventName) ^^ - RightL.colon ^^ - NicePrint.layoutType denv eventTy - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml, ?symbol = symbol) - - // F# and .NET properties - | Item.Property(_, pinfo :: _) -> - let layout = NicePrint.prettyLayoutOfPropInfoFreeStyle g amap m denv pinfo - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml, ?symbol = symbol) - - // Custom operations in queries - | Item.CustomOperation (customOpName, usageText, Some minfo) -> - - // Build 'custom operation: where (bool) - // - // Calls QueryBuilder.Where' - let layout = - wordL (tagText (FSComp.SR.typeInfoCustomOperation())) ^^ - RightL.colon ^^ - ( - match usageText() with - | Some t -> wordL (tagText t) - | None -> - let argTys = ParamNameAndTypesOfUnaryCustomOperation g minfo |> List.map (fun (ParamNameAndType(_, ty)) -> ty) - let argTys, _ = PrettyTypes.PrettifyTypes g argTys - wordL (tagMethod customOpName) ^^ sepListL SepL.space (List.map (fun ty -> LeftL.leftParen ^^ NicePrint.layoutType denv ty ^^ SepL.rightParen) argTys) - ) ^^ - SepL.lineBreak ^^ SepL.lineBreak ^^ - wordL (tagText (FSComp.SR.typeInfoCallsWord())) ^^ - NicePrint.layoutTyconRef denv minfo.ApparentEnclosingTyconRef ^^ - SepL.dot ^^ - wordL (tagMethod minfo.DisplayName) - - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml, ?symbol = symbol) - - // F# constructors and methods - | Item.CtorGroup(_, minfos) - | Item.MethodGroup(_, minfos, _) -> - FormatOverloadsToList infoReader m denv item minfos symbol width - - // The 'fake' zero-argument constructors of .NET interfaces. - // This ideally should never appear in intellisense, but we do get here in repros like: - // type IFoo = abstract F : int - // type II = IFoo // remove 'type II = ' and quickly hover over IFoo before it gets squiggled for 'invalid use of interface type' - // and in that case we'll just show the interface type name. - | Item.FakeInterfaceCtor ty -> - let ty, _ = PrettyTypes.PrettifyType g ty - let layout = NicePrint.layoutTyconRef denv (tcrefOfAppTy g ty) - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single(layout, xml, ?symbol = symbol) - - // The 'fake' representation of constructors of .NET delegate types - | Item.DelegateCtor delTy -> - let delTy, _cxs = PrettyTypes.PrettifyType g delTy - let (SigOfFunctionForDelegate(_, _, _, delFuncTy)) = GetSigOfFunctionForDelegate infoReader delTy m AccessibleFromSomewhere - let layout = - NicePrint.layoutTyconRef denv (tcrefOfAppTy g delTy) ^^ - LeftL.leftParen ^^ - NicePrint.layoutType denv delFuncTy ^^ - RightL.rightParen - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single(layout, xml, ?symbol = symbol) - - // Types. - | Item.Types(_, TType_app(tcref, _, _) :: _) - | Item.UnqualifiedType (tcref :: _) -> - let denv = { denv with - // tooltips are space-constrained, so use shorter names - shortTypeNames = true - // tooltips are space-constrained, so don't include xml doc comments - // on types/members. The doc comments for the actual member will still - // be shown in the tip. - showDocumentation = false } - let layout = NicePrint.layoutTyconDefn denv infoReader ad m (* width *) tcref.Deref - let layout = PrintUtilities.squashToWidth width layout - let remarks = OutputFullName displayFullName pubpathOfTyconRef fullDisplayTextOfTyconRefAsLayout tcref - let layout = toArray layout - let remarks = toArray remarks - ToolTipElement.Single (layout, xml, remarks=remarks, ?symbol = symbol) - - // Type variables - | Item.TypeVar (_, typar) -> - let layout = NicePrint.prettyLayoutOfTypar denv typar - let layout = PrintUtilities.squashToWidth width layout - ToolTipElement.Single (toArray layout, xml, ?symbol = symbol) - - // Traits + /// Select the items that participate in a MethodGroup. + let SelectMethodGroupItems g m item = +#if NO_TYPEPROVIDERS + ignore m +#endif + match item with + | Item.CtorGroup(nm, cinfos) -> List.map (fun minfo -> Item.CtorGroup(nm, [minfo])) cinfos | Item.Trait traitInfo -> - let denv = { denv with shortConstraints = false} - let layout = NicePrint.prettyLayoutOfTrait denv traitInfo - let layout = PrintUtilities.squashToWidth width layout - ToolTipElement.Single (toArray layout, xml, ?symbol = symbol) - - // F# Modules and namespaces - | Item.ModuleOrNamespaces(modref :: _ as modrefs) -> - //let os = StringBuilder() - let modrefs = modrefs |> RemoveDuplicateModuleRefs - let definiteNamespace = modrefs |> List.forall (fun modref -> modref.IsNamespace) - let kind = - if definiteNamespace then FSComp.SR.typeInfoNamespace() - elif modrefs |> List.forall (fun modref -> modref.IsModule) then FSComp.SR.typeInfoModule() - else FSComp.SR.typeInfoNamespaceOrModule() - - let layout = - wordL (tagKeyword kind) ^^ - (if definiteNamespace then tagNamespace (fullDisplayTextOfModRef modref) else (tagModule modref.DemangledModuleOrNamespaceName) - |> mkNav modref.DefinitionRange - |> wordL) - if not definiteNamespace then - let namesToAdd = - ([], modrefs) - ||> Seq.fold (fun st modref -> - match fullDisplayTextOfParentOfModRef modref with - | ValueSome txt -> txt :: st - | _ -> st) - |> Seq.mapi (fun i x -> i, x) - |> Seq.toList - let layout = - layout ^^ - ( - if not (List.isEmpty namesToAdd) then - SepL.lineBreak ^^ - List.fold ( fun s (i, txt) -> - s ^^ - SepL.lineBreak ^^ - wordL (tagText ((if i = 0 then FSComp.SR.typeInfoFromFirst else FSComp.SR.typeInfoFromNext) txt)) - ) emptyL namesToAdd - else - emptyL - ) - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml, ?symbol = symbol) - else - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml, ?symbol = symbol) - - | Item.AnonRecdField(anon, argTys, i, _) -> - let argTy = argTys[i] - let nm = anon.DisplayNameByIdx i - let argTy, _ = PrettyTypes.PrettifyType g argTy - let layout = - wordL (tagText (FSComp.SR.typeInfoAnonRecdField())) ^^ - wordL (tagRecordField nm) ^^ - RightL.colon ^^ - NicePrint.layoutType denv argTy - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, FSharpXmlDoc.None, ?symbol = symbol) - - // Named parameters - | Item.ArgName (Some id, argTy, _, _) -> - let argTy, _ = PrettyTypes.PrettifyType g argTy - let layout = - wordL (tagText (FSComp.SR.typeInfoArgument())) ^^ - wordL (tagParameter id.idText) ^^ - RightL.colon ^^ - NicePrint.layoutType denv argTy - let layout = PrintUtilities.squashToWidth width layout - let layout = toArray layout - ToolTipElement.Single (layout, xml, paramName = id.idText, ?symbol = symbol) - - | Item.SetterArg (_, item) -> - FormatItemDescriptionToToolTipElement displayFullName infoReader ad m denv (ItemWithNoInst item) symbol width - - | Item.ArgName (None, _, _, _) - - // TODO: give a decent tooltip for implicit operators that include the resolution of the operator - // - //type C() = - // static member (++++++) (x: C, y: C) = C() - // - //let f (x: C) = - // x ++++++ x - // - // Here hovering over "++++++" in "f" could give a tooltip saying what the thing is and what it has resolved to. - // - // - | Item.ImplicitOp _ - - // TODO: consider why we aren't getting Item.Types for generic type parameters - // let F<'T>() = new System.Collections.Generic.List<'T>() - | Item.Types (_, [TType_var _]) - - // TODO: consider why we aren't getting Item.Types for units of measure - | Item.Types (_, [TType_measure _]) - - // TODO: consider whether we ever get Item.Types with more than one element - | Item.Types (_, _ :: _ :: _) - - // We don't expect Item.Types with an anonymous record type, function types etc. - | Item.Types (_, [TType_anon _]) - | Item.Types (_, [TType_fun _]) - | Item.Types (_, [TType_forall _]) - | Item.Types (_, [TType_tuple _]) - | Item.Types (_, [TType_ucase _]) - - // We don't expect these cases - | Item.Types (_, []) - | Item.Property (_, []) - | Item.UnqualifiedType [] - | Item.ModuleOrNamespaces [] - | Item.CustomOperation (_, _, None) -> ToolTipElement.None - - /// Format the structured version of a tooltip for an item - let FormatStructuredDescriptionOfItem isDecl infoReader ad m denv item symbol width = - DiagnosticsScope.Protect m - (fun () -> FormatItemDescriptionToToolTipElement isDecl infoReader ad m denv item symbol width) - (fun err -> ToolTipElement.CompositionError err) + if traitInfo.GetLogicalArgumentTypes(g).IsEmpty then [] else [item] + | Item.FakeInterfaceCtor _ + | Item.DelegateCtor _ -> [item] + | Item.NewDef _ + | Item.ILField _ -> [] + | Item.Event _ -> [] + | Item.RecdField(rfinfo) -> + if isFunction g rfinfo.FieldType then [item] else [] + | Item.Value v -> + if isFunction g v.Type then [item] else [] + | Item.UnionCase(ucr, _) -> + if not ucr.UnionCase.IsNullary then [item] else [] + | Item.ExnCase(ecr) -> + if isNil (recdFieldsOfExnDefRef ecr) then [] else [item] + | Item.Property(_, pinfos) -> + let pinfo = List.head pinfos + if pinfo.IsIndexer then [item] else [] +#if !NO_TYPEPROVIDERS + | ItemIsWithStaticArguments m g _ -> + // we pretend that provided-types-with-static-args are method-like in order to get ParamInfo for them + [item] +#endif + | Item.MethodGroup(nm, minfos, orig) -> minfos |> List.map (fun minfo -> Item.MethodGroup(nm, [minfo], orig)) + | Item.CustomOperation _ -> [item] + // These are not items that can participate in a method group + | Item.TypeVar _ + | Item.CustomBuilder _ + | Item.ActivePatternCase _ + | Item.AnonRecdField _ + | Item.ArgName _ + | Item.ImplicitOp _ + | Item.ModuleOrNamespaces _ + | Item.SetterArg _ + | Item.Types _ + | Item.UnionCaseField _ + | Item.UnqualifiedType _ + | Item.ActivePatternResult _ -> [] /// An intellisense declaration [] diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.cs.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.cs.xlf index 0135d1c95a..a831bef879 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.cs.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.cs.xlf @@ -182,14 +182,16 @@ Cache parsing results (experimental) Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; Dot underline; Dash underline; Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.de.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.de.xlf index d12cfa3497..f93b21f57a 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.de.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.de.xlf @@ -182,14 +182,16 @@ Cache parsing results (experimental) Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; Dot underline; Dash underline; Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.es.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.es.xlf index 759070b96e..433fe60541 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.es.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.es.xlf @@ -182,14 +182,16 @@ Cache parsing results (experimental) Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; Dot underline; Dash underline; Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.fr.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.fr.xlf index 1094b4529c..bd0c4c6988 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.fr.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.fr.xlf @@ -182,14 +182,16 @@ Cache parsing results (experimental) Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; Dot underline; Dash underline; Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.it.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.it.xlf index 0081e0ab57..ece82b76af 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.it.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.it.xlf @@ -182,14 +182,16 @@ Cache parsing results (experimental) Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; Dot underline; Dash underline; Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ja.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ja.xlf index 4dbe03a075..688ad3123f 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ja.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ja.xlf @@ -182,14 +182,16 @@ Cache parsing results (experimental) Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; Dot underline; Dash underline; Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ko.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ko.xlf index e2d8d93319..a01fcb2e77 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ko.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ko.xlf @@ -182,14 +182,16 @@ Cache parsing results (experimental) Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; Dot underline; Dash underline; Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pl.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pl.xlf index 355dbae447..39618f2694 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pl.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pl.xlf @@ -182,14 +182,16 @@ Cache parsing results (experimental) Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; Dot underline; Dash underline; Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf index 794f33d932..940c53b43a 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf @@ -182,14 +182,16 @@ Cache parsing results (experimental) Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; Dot underline; Dash underline; Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ru.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ru.xlf index 2759af321c..0a5b668279 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ru.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ru.xlf @@ -182,14 +182,16 @@ Cache parsing results (experimental) Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; Dot underline; Dash underline; Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.tr.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.tr.xlf index 69dec9a44f..eb8a524241 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.tr.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.tr.xlf @@ -182,14 +182,16 @@ Cache parsing results (experimental) Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; Dot underline; Dash underline; Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hans.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hans.xlf index 75fce885f8..aa70f9bf92 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hans.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hans.xlf @@ -182,14 +182,16 @@ Cache parsing results (experimental) Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; Dot underline; Dash underline; Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hant.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hant.xlf index 3a689f3b68..0002b466f3 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hant.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hant.xlf @@ -182,14 +182,16 @@ Cache parsing results (experimental) Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; Dot underline; Dash underline; Formatting; -Maximum description width in characters; +Preferred description width in characters; +Format signature to the given width by adding line breaks conforming with F# syntax rules; Navigation links; Show navigation links as; Solid underline; From 82919f02eb6fa47e61e39e0b0aaec4166e9cc6b5 Mon Sep 17 00:00:00 2001 From: majocha Date: Thu, 6 Apr 2023 08:51:39 +0200 Subject: [PATCH 3/7] reduce diff --- ...vice.SurfaceArea.netstandard20.release.bsl | 6 +- .../Navigation/FindDefinitionService.fs | 9 +- .../Navigation/GoToDefinition.fs | 51 ++--- .../Navigation/GoToDefinitionService.fs | 14 +- .../Navigation/NavigableSymbolsService.fs | 21 +- .../QuickInfo/QuickInfoProvider.fs | 131 +++++------ .../QuickInfoProviderTests.fs | 213 ++++++++++-------- .../FSharp.Editor.Tests/QuickInfoTests.fs | 12 +- 8 files changed, 223 insertions(+), 234 deletions(-) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index c9de754446..001af598b9 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -3970,7 +3970,7 @@ FSharp.Compiler.EditorServices.ToolTipElement: Boolean get_IsNone() FSharp.Compiler.EditorServices.ToolTipElement: FSharp.Compiler.EditorServices.ToolTipElement NewCompositionError(System.String) FSharp.Compiler.EditorServices.ToolTipElement: FSharp.Compiler.EditorServices.ToolTipElement NewGroup(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.EditorServices.ToolTipElementData]) FSharp.Compiler.EditorServices.ToolTipElement: FSharp.Compiler.EditorServices.ToolTipElement None -FSharp.Compiler.EditorServices.ToolTipElement: FSharp.Compiler.EditorServices.ToolTipElement Single(FSharp.Compiler.Text.TaggedText[], FSharp.Compiler.Symbols.FSharpXmlDoc, Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Text.TaggedText[]]], Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.TaggedText[]]) +FSharp.Compiler.EditorServices.ToolTipElement: FSharp.Compiler.EditorServices.ToolTipElement Single(FSharp.Compiler.Text.TaggedText[], FSharp.Compiler.Symbols.FSharpXmlDoc, Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Text.TaggedText[]]], Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.TaggedText[]], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Symbols.FSharpSymbol]) FSharp.Compiler.EditorServices.ToolTipElement: FSharp.Compiler.EditorServices.ToolTipElement get_None() FSharp.Compiler.EditorServices.ToolTipElement: FSharp.Compiler.EditorServices.ToolTipElement+CompositionError FSharp.Compiler.EditorServices.ToolTipElement: FSharp.Compiler.EditorServices.ToolTipElement+Group @@ -3991,12 +3991,14 @@ FSharp.Compiler.EditorServices.ToolTipElementData: Int32 GetHashCode() FSharp.Compiler.EditorServices.ToolTipElementData: Int32 GetHashCode(System.Collections.IEqualityComparer) FSharp.Compiler.EditorServices.ToolTipElementData: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Text.TaggedText[]] TypeMapping FSharp.Compiler.EditorServices.ToolTipElementData: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Text.TaggedText[]] get_TypeMapping() +FSharp.Compiler.EditorServices.ToolTipElementData: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Symbols.FSharpSymbol] Symbol +FSharp.Compiler.EditorServices.ToolTipElementData: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Symbols.FSharpSymbol] get_Symbol() FSharp.Compiler.EditorServices.ToolTipElementData: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.TaggedText[]] Remarks FSharp.Compiler.EditorServices.ToolTipElementData: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.TaggedText[]] get_Remarks() FSharp.Compiler.EditorServices.ToolTipElementData: Microsoft.FSharp.Core.FSharpOption`1[System.String] ParamName FSharp.Compiler.EditorServices.ToolTipElementData: Microsoft.FSharp.Core.FSharpOption`1[System.String] get_ParamName() FSharp.Compiler.EditorServices.ToolTipElementData: System.String ToString() -FSharp.Compiler.EditorServices.ToolTipElementData: Void .ctor(FSharp.Compiler.Text.TaggedText[], FSharp.Compiler.Symbols.FSharpXmlDoc, Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Text.TaggedText[]], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.TaggedText[]], Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.EditorServices.ToolTipElementData: Void .ctor(Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Symbols.FSharpSymbol], FSharp.Compiler.Text.TaggedText[], FSharp.Compiler.Symbols.FSharpXmlDoc, Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Text.TaggedText[]], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.TaggedText[]], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.EditorServices.ToolTipText: Boolean Equals(FSharp.Compiler.EditorServices.ToolTipText) FSharp.Compiler.EditorServices.ToolTipText: Boolean Equals(System.Object) FSharp.Compiler.EditorServices.ToolTipText: Boolean Equals(System.Object, System.Collections.IEqualityComparer) diff --git a/vsintegration/src/FSharp.Editor/Navigation/FindDefinitionService.fs b/vsintegration/src/FSharp.Editor/Navigation/FindDefinitionService.fs index cd938c41b8..884ea193da 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/FindDefinitionService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/FindDefinitionService.fs @@ -10,22 +10,15 @@ open FSharp.Compiler.Text.Range open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.ExternalAccess.FSharp.GoToDefinition -open Microsoft.VisualStudio.Shell -open Microsoft.VisualStudio.Shell.Interop open System.Collections.Immutable open System.Threading.Tasks [)>] [)>] type internal FSharpFindDefinitionService [] (metadataAsSource: FSharpMetadataAsSourceService) = - - let statusBar = - StatusBar(ServiceProvider.GlobalProvider.GetService()) - interface IFSharpFindDefinitionService with member _.FindDefinitionsAsync(document: Document, position: int, cancellationToken: CancellationToken) = - let navigation = - FSharpNavigation(statusBar, metadataAsSource, document, rangeStartup) + let navigation = FSharpNavigation(metadataAsSource, document, rangeStartup) let definitions = navigation.FindDefinitions(position, cancellationToken) ImmutableArray.CreateRange(definitions) |> Task.FromResult diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs index bce993643e..e2e4d71690 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs @@ -109,7 +109,10 @@ module private ExternalSymbol = | _ -> [] // TODO: Uncomment code when VS has a fix for updating the status bar. -type StatusBar(statusBar: IVsStatusbar) = +type StatusBar() = + let statusBar = + ServiceProvider.GlobalProvider.GetService() + let mutable _searchIcon = int16 Microsoft.VisualStudio.Shell.Interop.Constants.SBAI_Find :> obj @@ -394,7 +397,6 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = ( document: Document, textSpan: Microsoft.CodeAnalysis.Text.TextSpan, - statusBar: StatusBar, cancellationToken: CancellationToken ) = let navigableItem = FSharpGoToDefinitionNavigableItem(document, textSpan) @@ -407,9 +409,10 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = navigationService.TryNavigateToSpan(workspace, navigableItem.Document.Id, navigableItem.SourceSpan, cancellationToken) if not navigationSucceeded then - statusBar.TempMessage(SR.CannotNavigateUnknown()) + StatusBar().TempMessage(SR.CannotNavigateUnknown()) - member _.NavigateToItem(navigableItem: FSharpNavigableItem, statusBar: StatusBar, cancellationToken: CancellationToken) = + member _.NavigateToItem(navigableItem: FSharpNavigableItem, cancellationToken: CancellationToken) = + let statusBar = StatusBar() use __ = statusBar.Animate() statusBar.Message(SR.NavigatingTo()) @@ -434,12 +437,11 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = targetDocument: Document, targetSourceText: SourceText, symbolRange: range, - statusBar: StatusBar, cancellationToken: CancellationToken ) = asyncMaybe { let! item = this.FindDeclarationOfSymbolAtRange(targetDocument, symbolRange, targetSourceText) - return this.NavigateToItem(item, statusBar, cancellationToken) + return this.NavigateToItem(item, cancellationToken) } /// Find the definition location (implementation file/.fs) of the target symbol @@ -448,12 +450,11 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = targetDocument: Document, targetSourceText: SourceText, symbolRange: range, - statusBar: StatusBar, cancellationToken: CancellationToken ) = asyncMaybe { let! item = this.FindDefinitionOfSymbolAtRange(targetDocument, symbolRange, targetSourceText) - return this.NavigateToItem(item, statusBar, cancellationToken) + return this.NavigateToItem(item, cancellationToken) } member this.NavigateToExternalDeclaration @@ -546,7 +547,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = | _ -> TextSpan() let navItem = FSharpGoToDefinitionNavigableItem(tmpShownDoc, span) - this.NavigateToItem(navItem, statusBar, cancellationToken) + this.NavigateToItem(navItem, cancellationToken) true | _ -> false | _ -> false @@ -556,13 +557,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = else statusBar.TempMessage(SR.CannotNavigateUnknown()) -type internal FSharpNavigation - ( - statusBar: StatusBar, - metadataAsSource: FSharpMetadataAsSourceService, - initialDoc: Document, - thisSymbolUseRange: range - ) = +type internal FSharpNavigation(metadataAsSource: FSharpMetadataAsSourceService, initialDoc: Document, thisSymbolUseRange: range) = let workspace = initialDoc.Project.Solution.Workspace let solution = workspace.CurrentSolution @@ -603,15 +598,13 @@ type internal FSharpNavigation match initialDoc.FilePath, targetPath with | Signature, Signature - | Implementation, Implementation -> return gtd.TryNavigateToTextSpan(targetDoc, targetTextSpan, statusBar, cancellationToken) + | Implementation, Implementation -> return gtd.TryNavigateToTextSpan(targetDoc, targetTextSpan, cancellationToken) // Adjust the target from signature to implementation. - | Implementation, Signature -> - return! gtd.NavigateToSymbolDefinitionAsync(targetDoc, targetSource, range, statusBar, cancellationToken) + | Implementation, Signature -> return! gtd.NavigateToSymbolDefinitionAsync(targetDoc, targetSource, range, cancellationToken) // Adjust the target from implmentation to signature. - | Signature, Implementation -> - return! gtd.NavigateToSymbolDeclarationAsync(targetDoc, targetSource, range, statusBar, cancellationToken) + | Signature, Implementation -> return! gtd.NavigateToSymbolDeclarationAsync(targetDoc, targetSource, range, cancellationToken) } |> Async.Ignore |> Async.StartImmediate @@ -630,6 +623,7 @@ type internal FSharpNavigation member _.TryGoToDefinition(position, cancellationToken) = let gtd = GoToDefinition(metadataAsSource) + let statusBar = StatusBar() let gtdTask = gtd.FindDefinitionTask(initialDoc, position, cancellationToken) // Wrap this in a try/with as if the user clicks "Cancel" on the thread dialog, we'll be cancelled. @@ -641,7 +635,7 @@ type internal FSharpNavigation if gtdTask.Status = TaskStatus.RanToCompletion && gtdTask.Result.IsSome then match gtdTask.Result.Value with | FSharpGoToDefinitionResult.NavigableItem (navItem), _ -> - gtd.NavigateToItem(navItem, statusBar, cancellationToken) + gtd.NavigateToItem(navItem, cancellationToken) // 'true' means do it, like Sheev Palpatine would want us to. true | FSharpGoToDefinitionResult.ExternalAssembly (targetSymbolUse, metadataReferences), _ -> @@ -688,7 +682,7 @@ type internal DocCommentId = | Type of EntityPath: string list | None -type FSharpNavigableLocation(statusBar: StatusBar, metadataAsSource: FSharpMetadataAsSourceService, symbolRange: range, project: Project) = +type FSharpNavigableLocation(metadataAsSource: FSharpMetadataAsSourceService, symbolRange: range, project: Project) = interface IFSharpNavigableLocation with member _.NavigateToAsync(_options: FSharpNavigationOptions2, cancellationToken: CancellationToken) : Task = asyncMaybe { @@ -704,10 +698,8 @@ type FSharpNavigableLocation(statusBar: StatusBar, metadataAsSource: FSharpMetad Implementation match targetPath with - | Signature -> - return! gtd.NavigateToSymbolDefinitionAsync(targetDoc, targetSource, symbolRange, statusBar, cancellationToken) - | Implementation -> - return! gtd.NavigateToSymbolDeclarationAsync(targetDoc, targetSource, symbolRange, statusBar, cancellationToken) + | Signature -> return! gtd.NavigateToSymbolDefinitionAsync(targetDoc, targetSource, symbolRange, cancellationToken) + | Implementation -> return! gtd.NavigateToSymbolDeclarationAsync(targetDoc, targetSource, symbolRange, cancellationToken) } |> Async.map (fun a -> a.IsSome) |> RoslynHelpers.StartAsyncAsTask cancellationToken @@ -720,9 +712,6 @@ type FSharpCrossLanguageSymbolNavigationService() = let workspace = componentModel.GetService() - let statusBar = - StatusBar(ServiceProvider.GlobalProvider.GetService()) - let metadataAsSource = componentModel .DefaultExportProvider @@ -953,7 +942,7 @@ type FSharpCrossLanguageSymbolNavigationService() = // More results can theoretically be returned in case of method overloads, or when we have both signature and implementation files. if locations.Count() >= 1 then let (location, project) = locations.First() - return FSharpNavigableLocation(statusBar, metadataAsSource, location, project) :> IFSharpNavigableLocation + return FSharpNavigableLocation(metadataAsSource, location, project) :> IFSharpNavigableLocation else return Unchecked.defaultof<_> // returning null here, so Roslyn can fallback to default source-as-metadata implementation. } diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs index 9588fc3a7b..a3a800c9ef 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs @@ -2,7 +2,6 @@ namespace Microsoft.VisualStudio.FSharp.Editor -open System open System.Composition open System.Threading open System.Threading.Tasks @@ -12,31 +11,24 @@ open FSharp.Compiler.Text.Range open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor -open Microsoft.VisualStudio.Shell -open Microsoft.VisualStudio.Shell.Interop - [)>] [)>] type internal FSharpGoToDefinitionService [] (metadataAsSource: FSharpMetadataAsSourceService) = - let statusBar = - StatusBar(ServiceProvider.GlobalProvider.GetService()) - interface IFSharpGoToDefinitionService with /// Invoked with Peek Definition. member _.FindDefinitionsAsync(document: Document, position: int, cancellationToken: CancellationToken) = - let navigation = - FSharpNavigation(statusBar, metadataAsSource, document, rangeStartup) + let navigation = FSharpNavigation(metadataAsSource, document, rangeStartup) navigation.FindDefinitions(position, cancellationToken) |> Task.FromResult /// Invoked with Go to Definition. /// Try to navigate to the definiton of the symbol at the symbolRange in the originDocument member _.TryGoToDefinition(document: Document, position: int, cancellationToken: CancellationToken) = + let statusBar = StatusBar() statusBar.Message(SR.LocatingSymbol()) use __ = statusBar.Animate() - let navigation = - FSharpNavigation(statusBar, metadataAsSource, document, rangeStartup) + let navigation = FSharpNavigation(metadataAsSource, document, rangeStartup) navigation.TryGoToDefinition(position, cancellationToken) diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs index 5a87b2fc47..99a07f8fd1 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs @@ -8,31 +8,28 @@ open System.Threading.Tasks open System.ComponentModel.Composition open Microsoft.CodeAnalysis.Text -open Microsoft.CodeAnalysis.Navigation open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Navigation open Microsoft.VisualStudio.Language.Intellisense open Microsoft.VisualStudio.Text open Microsoft.VisualStudio.Text.Editor -open Microsoft.VisualStudio.Shell.Interop open Microsoft.VisualStudio.Utilities -open Microsoft.VisualStudio.Shell [] -type internal FSharpNavigableSymbol(item: FSharpNavigableItem, span: SnapshotSpan, gtd: GoToDefinition, statusBar: StatusBar) = +type internal FSharpNavigableSymbol(item: FSharpNavigableItem, span: SnapshotSpan, gtd: GoToDefinition) = interface INavigableSymbol with member _.Navigate(_: INavigableRelationship) = - gtd.NavigateToItem(item, statusBar, CancellationToken.None) + gtd.NavigateToItem(item, CancellationToken.None) member _.Relationships = seq { yield PredefinedNavigableRelationships.Definition } member _.SymbolSpan = span -type internal FSharpNavigableSymbolSource(metadataAsSource, serviceProvider: IServiceProvider) = +type internal FSharpNavigableSymbolSource(metadataAsSource) = let mutable disposed = false let gtd = GoToDefinition(metadataAsSource) - let statusBar = StatusBar(serviceProvider.GetService()) + let statusBar = StatusBar() interface INavigableSymbolSource with member _.GetNavigableSymbolAsync(triggerSpan: SnapshotSpan, cancellationToken: CancellationToken) = @@ -67,7 +64,7 @@ type internal FSharpNavigableSymbolSource(metadataAsSource, serviceProvider: ISe match result with | FSharpGoToDefinitionResult.NavigableItem (navItem) -> - return FSharpNavigableSymbol(navItem, symbolSpan, gtd, statusBar) :> INavigableSymbol + return FSharpNavigableSymbol(navItem, symbolSpan, gtd) :> INavigableSymbol | FSharpGoToDefinitionResult.ExternalAssembly (targetSymbolUse, metadataReferences) -> let nav = @@ -105,12 +102,8 @@ type internal FSharpNavigableSymbolSource(metadataAsSource, serviceProvider: ISe [] [] [] -type internal FSharpNavigableSymbolService [] - ( - [)>] serviceProvider: IServiceProvider, - metadataAsSource: FSharpMetadataAsSourceService - ) = +type internal FSharpNavigableSymbolService [] (metadataAsSource: FSharpMetadataAsSourceService) = interface INavigableSymbolSourceProvider with member _.TryCreateNavigableSymbolSource(_: ITextView, _: ITextBuffer) = - new FSharpNavigableSymbolSource(metadataAsSource, serviceProvider) :> INavigableSymbolSource + new FSharpNavigableSymbolSource(metadataAsSource) diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs index 727211e9a1..70017f2de1 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs @@ -2,7 +2,6 @@ namespace Microsoft.VisualStudio.FSharp.Editor.QuickInfo -open System open System.Threading open System.Threading.Tasks open System.ComponentModel.Composition @@ -11,9 +10,8 @@ open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text open Microsoft.VisualStudio.Language.Intellisense -open Microsoft.VisualStudio.Shell -open Microsoft.VisualStudio.Shell.Interop open Microsoft.VisualStudio.Text +open Microsoft.VisualStudio.Shell open Microsoft.VisualStudio.Utilities open Microsoft.VisualStudio.FSharp.Editor @@ -23,106 +21,105 @@ open FSharp.Compiler.EditorServices type internal FSharpAsyncQuickInfoSource ( - statusBar: StatusBar, - xmlMemberIndexService: IVsXMLMemberIndexService, + xmlMemberIndexService, metadataAsSource: FSharpMetadataAsSourceService, textBuffer: ITextBuffer, editorOptions: EditorOptions ) = - let tryGetQuickInfoItem (session: IAsyncQuickInfoSession) = + let getQuickInfoItem (sourceText, (document: Document), (lexerSymbol: LexerSymbol), (ToolTipText elements)) = asyncMaybe { - let userOpName = "getQuickInfo" + let documentationBuilder = + XmlDocumentation.CreateDocumentationBuilder(xmlMemberIndexService) + + let getSingleContent (data: ToolTipElement) = + + let symbol, description, documentation = + XmlDocumentation.BuildSingleTipText(documentationBuilder, data) + + let getLinkTooltip filePath = + let solutionDir = Path.GetDirectoryName(document.Project.Solution.FilePath) + let projectDir = Path.GetDirectoryName(document.Project.FilePath) + + [ + Path.GetRelativePath(projectDir, filePath) + Path.GetRelativePath(solutionDir, filePath) + ] + |> List.minBy String.length + + QuickInfoViewProvider.provideContent ( + Tokenizer.GetImageIdForSymbol(symbol, lexerSymbol.Kind), + description, + documentation, + FSharpNavigation(metadataAsSource, document, lexerSymbol.Range), + getLinkTooltip + ) - let! triggerPoint = session.GetTriggerPoint(textBuffer.CurrentSnapshot) |> Option.ofNullable - let position = triggerPoint.Position + let content = elements |> List.map getSingleContent + do! Option.guard (not content.IsEmpty) + + let! textSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, lexerSymbol.Range) + + let trackingSpan = + textBuffer.CurrentSnapshot.CreateTrackingSpan(textSpan.Start, textSpan.Length, SpanTrackingMode.EdgeInclusive) - let document = - textBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges() + return QuickInfoItem(trackingSpan, QuickInfoViewProvider.stackWithSeparators content) + } + + static member TryGetToolTip(document: Document, position, ?width) = + asyncMaybe { + let userOpName = "getQuickInfo" let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, true, true, userOpName) let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(userOpName) |> liftAsync let! cancellationToken = Async.CancellationToken |> liftAsync let! sourceText = document.GetTextAsync cancellationToken - let idRange = lexerSymbol.Ident.idRange + let range = lexerSymbol.Range let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() - let getLinkTooltip filePath = - let solutionDir = Path.GetDirectoryName(document.Project.Solution.FilePath) - let projectDir = Path.GetDirectoryName(document.Project.FilePath) - - [ - Path.GetRelativePath(projectDir, filePath) - Path.GetRelativePath(solutionDir, filePath) - ] - |> List.minBy String.length - - let symbolUseRange = - maybe { - let! symbolUse = - checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland) - - return symbolUse.Range - } - - let tryGetSingleContent (data: ToolTipElement) = - maybe { - let documentationBuilder = - XmlDocumentation.CreateDocumentationBuilder(xmlMemberIndexService) - - let symbol, description, documentation = - XmlDocumentation.BuildSingleTipText(documentationBuilder, data) - - return - QuickInfoViewProvider.provideContent ( - Tokenizer.GetImageIdForSymbol(symbol, lexerSymbol.Kind), - description, - documentation, - FSharpNavigation(statusBar, metadataAsSource, document, defaultArg symbolUseRange range.Zero), - getLinkTooltip - ) - } - - let (ToolTipText elements) = + let tooltip = match lexerSymbol.Kind with | LexerSymbolKind.Keyword -> checkFileResults.GetKeywordTooltip(lexerSymbol.FullIsland) | LexerSymbolKind.String -> checkFileResults.GetToolTip( fcsTextLineNumber, - idRange.EndColumn, + range.EndColumn, lineText, lexerSymbol.FullIsland, FSharp.Compiler.Tokenization.FSharpTokenTag.String, - ?width = editorOptions.QuickInfo.DescriptionWidth + ?width = width ) | _ -> checkFileResults.GetToolTip( fcsTextLineNumber, - idRange.EndColumn, + range.EndColumn, lineText, lexerSymbol.FullIsland, FSharp.Compiler.Tokenization.FSharpTokenTag.IDENT, - ?width = editorOptions.QuickInfo.DescriptionWidth + ?width = width ) - let content = elements |> List.choose tryGetSingleContent - do! Option.guard (not content.IsEmpty) - - let! textSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, lexerSymbol.Range) - - let trackingSpan = - textBuffer.CurrentSnapshot.CreateTrackingSpan(textSpan.Start, textSpan.Length, SpanTrackingMode.EdgeInclusive) - - return QuickInfoItem(trackingSpan, QuickInfoViewProvider.stackWithSeparators content) + return sourceText, document, lexerSymbol, tooltip } interface IAsyncQuickInfoSource with override _.Dispose() = () // no cleanup necessary override _.GetQuickInfoItemAsync(session: IAsyncQuickInfoSession, cancellationToken: CancellationToken) : Task = - tryGetQuickInfoItem session + asyncMaybe { + let document = + textBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges() + + let! triggerPoint = session.GetTriggerPoint(textBuffer.CurrentSnapshot) |> Option.ofNullable + let position = triggerPoint.Position + + let! tipdata = + FSharpAsyncQuickInfoSource.TryGetToolTip(document, position, ?width = editorOptions.QuickInfo.DescriptionWidth) + + return! getQuickInfoItem tipdata + } |> Async.map Option.toObj |> RoslynHelpers.StartAsyncAsTask cancellationToken @@ -132,16 +129,12 @@ type internal FSharpAsyncQuickInfoSource [] type internal FSharpAsyncQuickInfoSourceProvider [] ( - [)>] serviceProvider: IServiceProvider, + [)>] serviceProvider: System.IServiceProvider, metadataAsSource: FSharpMetadataAsSourceService, editorOptions: EditorOptions ) = interface IAsyncQuickInfoSourceProvider with override _.TryCreateQuickInfoSource(textBuffer: ITextBuffer) : IAsyncQuickInfoSource = - // GetService calls must be made on the UI thread - // It is safe to do it here (see #4713) - let statusBar = StatusBar(serviceProvider.GetService()) let xmlMemberIndexService = serviceProvider.XMLMemberIndexService - - new FSharpAsyncQuickInfoSource(statusBar, xmlMemberIndexService, metadataAsSource, textBuffer, editorOptions) + new FSharpAsyncQuickInfoSource(xmlMemberIndexService, metadataAsSource, textBuffer, editorOptions) diff --git a/vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs index 26402791e7..91e504577f 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs @@ -16,71 +16,93 @@ type public AssemblyResolverTestFixture() = member public __.Init() = AssemblyResolver.addResolver () module QuickInfoProviderTests = + type Expected = + | QuickInfo of description: string * docs: string + | Desc of string + | Empty + | Error - let filePath = "C:\\test.fs" + type TestCase = TestCase of prompt: string * Expected let private normalizeLineEnds (s: string) = s.Replace("\r\n", "\n").Replace("\n\n", "\n") - let private tooltipTextToRawString (ToolTipText elements) : string = - let rec parseElement = - function - | ToolTipElement.None -> "" - | ToolTipElement.Group (xs) -> - let descriptions = xs |> List.map (fun item -> item.MainDescription) + let mkFull prompt desc docs = + TestCase(prompt, QuickInfo(normalizeLineEnds desc, normalizeLineEnds docs)) + + let mkDesc prompt desc = + TestCase(prompt, Desc(normalizeLineEnds desc)) + + let mkNone prompt = TestCase(prompt, Empty) + + let filePath = "C:\\test.fs" + + let private tooltipElementToExpected expected = + function + | ToolTipElement.None -> Empty + | ToolTipElement.Group (xs) -> + let descriptions = xs |> List.map (fun item -> item.MainDescription) - let descriptionTexts = - descriptions - |> List.map (fun taggedTexts -> taggedTexts |> Array.map (fun taggedText -> taggedText.Text)) + let descriptionTexts = + descriptions + |> List.map (fun taggedTexts -> taggedTexts |> Array.map (fun taggedText -> taggedText.Text)) - let descriptionText = descriptionTexts |> Array.concat |> String.concat "" + let descriptionText = descriptionTexts |> Array.concat |> String.concat "" - let remarks = xs |> List.choose (fun item -> item.Remarks) + let remarks = xs |> List.choose (fun item -> item.Remarks) - let remarkTexts = - remarks |> Array.concat |> Array.map (fun taggedText -> taggedText.Text) + let remarkTexts = + remarks |> Array.concat |> Array.map (fun taggedText -> taggedText.Text) - let remarkText = - (match remarks with - | [] -> "" - | _ -> "\n" + String.concat "" remarkTexts) + let remarkText = + (match remarks with + | [] -> "" + | _ -> "\n" + String.concat "" remarkTexts) - let tps = xs |> List.collect (fun item -> item.TypeMapping) + let tps = xs |> List.collect (fun item -> item.TypeMapping) - let tpTexts = - tps |> List.map (fun x -> x |> Array.map (fun y -> y.Text) |> String.concat "") + let tpTexts = + tps |> List.map (fun x -> x |> Array.map (fun y -> y.Text) |> String.concat "") - let tpText = - (match tps with - | [] -> "" - | _ -> "\n" + String.concat "\n" tpTexts) + let tpText = + (match tps with + | [] -> "" + | _ -> "\n" + String.concat "\n" tpTexts) - descriptionText + remarkText + tpText - | ToolTipElement.CompositionError (error) -> error + let collectDocs (element: ToolTipElementData) = + match element.XmlDoc with + | FSharp.Compiler.Symbols.FSharpXmlDoc.FromXmlText xmlDoc -> xmlDoc.UnprocessedLines |> String.concat "\n" + | _ -> "" - elements |> List.map parseElement |> String.concat "\n" |> normalizeLineEnds + let desc = + [ descriptionText; remarkText; tpText ] |> String.concat "" |> normalizeLineEnds + + let docs = xs |> List.map collectDocs |> String.concat "" |> normalizeLineEnds + + match expected with + | QuickInfo _ -> QuickInfo(desc, docs) + | _ -> Desc desc + + | ToolTipElement.CompositionError (error) -> Error let executeQuickInfoTest (programText: string) testCases = let document = RoslynTestHelpers.CreateSolution(programText) |> RoslynTestHelpers.GetSingleDocument - for (symbol: string, expected: string option) in testCases do - let expected = - expected - |> Option.map normalizeLineEnds - |> Option.map (fun s -> s.Replace("___", "")) - + for TestCase (symbol, expected) in testCases do let caretPosition = programText.IndexOf(symbol) + symbol.Length - 1 let quickInfo = - FSharpAsyncQuickInfoSource.ProvideQuickInfo(document, caretPosition) + FSharpAsyncQuickInfoSource.TryGetToolTip(document, caretPosition) |> Async.RunSynchronously let actual = - quickInfo |> Option.map (fun qi -> tooltipTextToRawString qi.StructuredText) + quickInfo + |> Option.map (fun (_, _, _, ToolTipText elements) -> elements |> List.map (tooltipElementToExpected expected)) + |> Option.defaultValue [ Empty ] - actual |> Assert.shouldBeEqualWith expected $"Symbol: {symbol}" + actual.Head |> Assert.shouldBeEqualWith expected $"Symbol: {symbol}" [] let ShouldShowQuickInfoAtCorrectPositions () = @@ -93,20 +115,20 @@ System.Console.WriteLine(x + y) let testCases = [ - "let", Some "let___Used to associate, or bind, a name to a value or function." - "x", Some "val x: int\nFull name: Test.x" - "y", Some "val y: int\nFull name: Test.y" - "1", None - "2", None - "x +", - Some + mkFull "let" "let" "Used to associate, or bind, a name to a value or function." + mkDesc "x" "val x: int\nFull name: Test.x" + mkDesc "y" "val y: int\nFull name: Test.y" + mkNone "1" + mkNone "2" + mkDesc + "x +" """val (+) : x: 'T1 -> y: 'T2 -> 'T3 (requires member (+)) Full name: Microsoft.FSharp.Core.Operators.(+) 'T1 is int 'T2 is int 'T3 is int""" - "System", Some "namespace System" - "WriteLine", Some "System.Console.WriteLine(value: int) : unit" + mkDesc "System" "namespace System" + mkDesc "WriteLine" "System.Console.WriteLine(value: int) : unit" ] executeQuickInfoTest fileContents testCases @@ -122,17 +144,20 @@ module internal MyModule = let testCases = [ - "namespace", - Some - "namespace___Used to associate a name with a group of related types and modules, to logically separate it from other code." - "module", - Some - "module___Used to associate a name with a group of related types, values, and functions, to logically separate it from other code." - "internal", Some "internal___Used to specify that a member is visible inside an assembly but not outside it." - "val", Some "val___Used in a signature to indicate a value, or in a type to declare a member, in limited situations." - "->", - Some - "->___In function types, delimits arguments and return values. Yields an expression (in sequence expressions); equivalent to the yield keyword. Used in match expressions" + mkFull + "namespace" + "namespace" + "Used to associate a name with a group of related types and modules, to logically separate it from other code." + mkFull + "module" + "module" + "Used to associate a name with a group of related types, values, and functions, to logically separate it from other code." + mkFull "internal" "internal" "Used to specify that a member is visible inside an assembly but not outside it." + mkFull "val" "val" "Used in a signature to indicate a value, or in a type to declare a member, in limited situations." + mkFull + "->" + "->" + "In function types, delimits arguments and return values. Yields an expression (in sequence expressions); equivalent to the yield keyword. Used in match expressions" ] executeQuickInfoTest fileContents testCases @@ -157,8 +182,8 @@ let x = let testCases = [ - "let!", Some "let!___Used in computation expressions to bind a name to the result of another computation expression." - "return", Some "return___Used to provide a value for the result of the containing computation expression." + mkFull "let!" "let!" "Used in computation expressions to bind a name to the result of another computation expression." + mkFull "return" "return" "Used to provide a value for the result of the containing computation expression." ] executeQuickInfoTest fileContents testCases @@ -167,7 +192,7 @@ let x = let ShouldShowQuickInfoForGenericParameters () = let fileContents = """ - + type C() = member x.FSharpGenericMethodExplitTypeParams<'T>(a:'T, y:'T) = (a,y) @@ -194,94 +219,94 @@ let res8 = abs 5.0 let testCases = [ - "GroupBy", - Some + mkDesc + "GroupBy" "(extension) System.Collections.Generic.IEnumerable.GroupBy<'TSource,'TKey>(keySelector: System.Func<'TSource,'TKey>) : System.Collections.Generic.IEnumerable> 'TSource is int * string 'TKey is int" - "Sort", - Some + mkDesc + "Sort" "System.Array.Sort<'T>(array: 'T array) : unit 'T is int" - "let test4 x = C().FSharpGenericMethodExplitTypeParams", - Some + mkDesc + "let test4 x = C().FSharpGenericMethodExplitTypeParams" "member C.FSharpGenericMethodExplitTypeParams: a: 'T0 * y: 'T0 -> 'T0 * 'T0 'T is 'a list" - "let test5<'U> (x: 'U) = C().FSharpGenericMethodExplitTypeParams", - Some + mkDesc + "let test5<'U> (x: 'U) = C().FSharpGenericMethodExplitTypeParams" "member C.FSharpGenericMethodExplitTypeParams: a: 'T0 * y: 'T0 -> 'T0 * 'T0 'T is 'U list" - "let test6 = C().FSharpGenericMethodExplitTypeParams", - Some + mkDesc + "let test6 = C().FSharpGenericMethodExplitTypeParams" "member C.FSharpGenericMethodExplitTypeParams: a: 'T0 * y: 'T0 -> 'T0 * 'T0 'T is int" - "let test7 x = C().FSharpGenericMethodInferredTypeParams", - Some + mkDesc + "let test7 x = C().FSharpGenericMethodInferredTypeParams" "member C.FSharpGenericMethodInferredTypeParams: a: 'a1 * y: 'b2 -> 'a1 * 'b2 'a is 'a0 list 'b is 'a0 list" - "let test8 = C().FSharpGenericMethodInferredTypeParams", - Some + mkDesc + "let test8 = C().FSharpGenericMethodInferredTypeParams" "member C.FSharpGenericMethodInferredTypeParams: a: 'a0 * y: 'b1 -> 'a0 * 'b1 'a is int 'b is int" - "let test9<'U> (x: 'U) = C().FSharpGenericMethodInferredTypeParams", - Some + mkDesc + "let test9<'U> (x: 'U) = C().FSharpGenericMethodInferredTypeParams" "member C.FSharpGenericMethodInferredTypeParams: a: 'a0 * y: 'b1 -> 'a0 * 'b1 'a is 'U list 'b is 'U list" - "let res3 = [1] |>", - Some + mkDesc + "let res3 = [1] |>" "val (|>) : arg: 'T1 -> func: ('T1 -> 'U) -> 'U Full name: Microsoft.FSharp.Core.Operators.(|>) 'T1 is int list 'U is int list" - "let res3 = [1] |> List.map id", - Some + mkDesc + "let res3 = [1] |> List.map id" "val id: x: 'T -> 'T Full name: Microsoft.FSharp.Core.Operators.id 'T is int" - "let res4 = (1.0,[1]) ||>", - Some + mkDesc + "let res4 = (1.0,[1]) ||>" "val (||>) : arg1: 'T1 * arg2: 'T2 -> func: ('T1 -> 'T2 -> 'U) -> 'U Full name: Microsoft.FSharp.Core.Operators.(||>) 'T1 is float 'T2 is int list 'U is float" - "let res4 = (1.0,[1]) ||> List.fold", - Some + mkDesc + "let res4 = (1.0,[1]) ||> List.fold" "val fold: folder: ('State -> 'T -> 'State) -> state: 'State -> list: 'T list -> 'State Full name: Microsoft.FSharp.Collections.List.fold 'T is int 'State is float" - "let res4 = (1.0,[1]) ||> List.fold (fun s x -> string s +", - Some + mkDesc + "let res4 = (1.0,[1]) ||> List.fold (fun s x -> string s +" "val (+) : x: 'T1 -> y: 'T2 -> 'T3 (requires member (+)) Full name: Microsoft.FSharp.Core.Operators.(+) 'T1 is string 'T2 is string 'T3 is float" - "let res5 = 1 +", - Some + mkDesc + "let res5 = 1 +" "val (+) : x: 'T1 -> y: 'T2 -> 'T3 (requires member (+)) Full name: Microsoft.FSharp.Core.Operators.(+) 'T1 is int 'T2 is int 'T3 is int" - "let res6 = System.DateTime.Now +", - Some + mkDesc + "let res6 = System.DateTime.Now +" "val (+) : x: 'T1 -> y: 'T2 -> 'T3 (requires member (+)) Full name: Microsoft.FSharp.Core.Operators.(+) 'T1 is System.DateTime 'T2 is System.TimeSpan 'T3 is System.DateTime" - "let res7 = sin", - Some + mkDesc + "let res7 = sin" "val sin: value: 'T -> 'T (requires member Sin) Full name: Microsoft.FSharp.Core.Operators.sin 'T is float" - "let res8 = abs", - Some + mkDesc + "let res8 = abs" "val abs: value: 'T -> 'T (requires member Abs) Full name: Microsoft.FSharp.Core.Operators.abs 'T is int" diff --git a/vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs index 4d4730f82c..20d6f9826f 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs @@ -8,6 +8,7 @@ open Xunit open FSharp.Editor.Tests.Helpers module QuickInfo = + open FSharp.Compiler.EditorServices let private GetCaretPosition (codeWithCaret: string) = let caretSentinel = "$$" @@ -26,11 +27,12 @@ module QuickInfo = cursorInfo let internal GetQuickInfo (code: string) caretPosition = - async { + asyncMaybe { let document = RoslynTestHelpers.CreateSolution(code) |> RoslynTestHelpers.GetSingleDocument - return! FSharpAsyncQuickInfoSource.ProvideQuickInfo(document, caretPosition) + let! _, _, _, tooltip = FSharpAsyncQuickInfoSource.TryGetToolTip(document, caretPosition) + return tooltip } |> Async.RunSynchronously @@ -39,15 +41,15 @@ module QuickInfo = let sigHelp = GetQuickInfo code caretPosition match sigHelp with - | Some (quickInfo) -> + | Some (ToolTipText elements) when not elements.IsEmpty -> let documentationBuilder = { new IDocumentationBuilder with override _.AppendDocumentationFromProcessedXML(_, _, _, _, _, _) = () override _.AppendDocumentation(_, _, _, _, _, _, _) = () } - let mainDescription, docs = - FSharpAsyncQuickInfoSource.BuildSingleQuickInfoItem documentationBuilder quickInfo + let _, mainDescription, docs = + XmlDocumentation.BuildSingleTipText(documentationBuilder, elements.Head) let mainTextItems = mainDescription |> Seq.map (fun x -> x.Text) let docTextItems = docs |> Seq.map (fun x -> x.Text) From 3a2f3a5c780589bc9b4143e0974b82d493489637 Mon Sep 17 00:00:00 2001 From: majocha Date: Thu, 6 Apr 2023 09:40:40 +0200 Subject: [PATCH 4/7] hide params --- vsintegration/src/FSharp.Editor/DocComments/XMLDocumentation.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/src/FSharp.Editor/DocComments/XMLDocumentation.fs b/vsintegration/src/FSharp.Editor/DocComments/XMLDocumentation.fs index 77ef4567b0..689b9c0a18 100644 --- a/vsintegration/src/FSharp.Editor/DocComments/XMLDocumentation.fs +++ b/vsintegration/src/FSharp.Editor/DocComments/XMLDocumentation.fs @@ -457,7 +457,7 @@ module internal XmlDocumentation = AppendHardLine usageCollector r |> Seq.iter usageCollector.Add) - AppendXmlComment(documentationProvider, xmlCollector, exnCollector, item0.XmlDoc, true, true, item0.ParamName) + AppendXmlComment(documentationProvider, xmlCollector, exnCollector, item0.XmlDoc, true, false, item0.ParamName) ProcessGenericParameters item0.TypeMapping From fdcbe04af5cdee8487af23bc0b841c8df4b0af7c Mon Sep 17 00:00:00 2001 From: majocha Date: Thu, 6 Apr 2023 11:03:48 +0200 Subject: [PATCH 5/7] fix ugly hack --- .../src/FSharp.Editor/QuickInfo/Views.fs | 4 +- .../FSharp.Editor/QuickInfo/WpfFactories.fs | 59 ++++++++++++------- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/Views.fs b/vsintegration/src/FSharp.Editor/QuickInfo/Views.fs index e3309470c5..415b38798b 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/Views.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/Views.fs @@ -61,7 +61,8 @@ module internal QuickInfoViewProvider = let stackContent (elements: obj list) = ContainerElement(ContainerElementStyle.Stacked, elements |> Seq.map box) - let encloseRuns runs = wrapContent (runs |> List.rev) |> box + let encloseRuns runs = + ClassifiedTextElement(runs |> List.rev) |> box let provideContent ( @@ -75,6 +76,7 @@ module internal QuickInfoViewProvider = let encloseText text = let rec loop text runs stack = match (text: TaggedText list) with + | [] when runs |> List.isEmpty -> stackContent (stack |> List.rev) | [] -> stackContent (encloseRuns runs :: stack |> List.rev) // smaller gap instead of huge double line break | LineBreak :: rest when runs |> List.isEmpty -> loop rest [] (box (Separator false) :: stack) diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/WpfFactories.fs b/vsintegration/src/FSharp.Editor/QuickInfo/WpfFactories.fs index e6beb9f5d2..1e13f13a34 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/WpfFactories.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/WpfFactories.fs @@ -11,6 +11,7 @@ open Microsoft.VisualStudio.Text.Editor open Microsoft.VisualStudio.Utilities open Microsoft.VisualStudio.FSharp.Editor +open Microsoft.VisualStudio.Text.Classification type Separator = | Separator of visible: bool @@ -21,39 +22,55 @@ type Separator = | _ -> System.Environment.NewLine [)>] -[] -[, typeof)>] -type WpfNavigableTextRunFactory [] (viewElementFactoryService: IViewElementFactoryService, settings: EditorOptions) = +[] +[, typeof)>] +type WpfClassifiedTextElementFactory [] + ( + classificationformatMapService: IClassificationFormatMapService, + classificationTypeRegistry: IClassificationTypeRegistryService, + settings: EditorOptions + ) = let resources = Microsoft.VisualStudio.FSharp.UIResources.NavStyles().Resources + let formatMap = classificationformatMapService.GetClassificationFormatMap("tooltip") interface IViewElementFactory with - member _.CreateViewElement(textView: ITextView, model: obj) = + member _.CreateViewElement(_textView: ITextView, model: obj) = match model with - | :? ClassifiedTextRun as classifiedTextRun -> - // use the default converters to get a UIElement - let classifiedTextElement = ClassifiedTextElement([ classifiedTextRun ]) + | :? ClassifiedTextElement as text -> + let tb = TextBlock() + tb.FontSize <- formatMap.DefaultTextProperties.FontRenderingEmSize + tb.FontFamily <- formatMap.DefaultTextProperties.Typeface.FontFamily + tb.TextWrapping <- TextWrapping.Wrap + + for run in text.Runs do + let ctype = + classificationTypeRegistry.GetClassificationType(run.ClassificationTypeName) + + let props = formatMap.GetTextProperties(ctype) + let inl = Documents.Run(run.Text, Foreground = props.ForegroundBrush) + + match run.NavigationAction |> Option.ofObj with + | Some action -> + let link = + { new Documents.Hyperlink(inl) with + override _.OnClick() = action.Invoke() + } - let convertedElement = - viewElementFactoryService.CreateViewElement(textView, classifiedTextElement) - // Apply custom underline. - match convertedElement with - | :? TextBlock as tb when classifiedTextRun.NavigationAction <> null && settings.QuickInfo.DisplayLinks -> - match tb.Inlines.FirstInline with - | :? Documents.Hyperlink as hyperlink -> let key = match settings.QuickInfo.UnderlineStyle with | QuickInfoUnderlineStyle.Solid -> "solid_underline" | QuickInfoUnderlineStyle.Dash -> "dash_underline" | QuickInfoUnderlineStyle.Dot -> "dot_underline" - // Fix color and apply styles. - hyperlink.Foreground <- hyperlink.Inlines.FirstInline.Foreground - hyperlink.Style <- downcast resources[key] - | _ -> () - | _ -> () - box convertedElement :?> _ + link.Style <- downcast resources[key] + link.Foreground <- props.ForegroundBrush + tb.Inlines.Add(link) + | _ -> tb.Inlines.Add(inl) + + box tb :?> _ | _ -> - failwith $"Invalid type conversion. Supported conversion is {typeof.Name} to {typeof.Name}." + failwith + $"Invalid type conversion. Supported conversion is {typeof.Name} to {typeof.Name}." [)>] [] From 9d7b16e30331b1dc521512701a41d344fea20c51 Mon Sep 17 00:00:00 2001 From: majocha Date: Thu, 6 Apr 2023 17:05:57 +0200 Subject: [PATCH 6/7] feedback --- src/Compiler/Service/FSharpCheckerResults.fs | 2 - .../DocComments/XMLDocumentation.fs | 40 ++++++++++++++----- .../QuickInfo/QuickInfoProvider.fs | 2 +- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index c07d81fd86..7582e10984 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -2717,8 +2717,6 @@ type FSharpCheckFileResults | None -> () | Some kwDescription -> let kwText = kw |> TaggedText.tagKeyword |> wordL |> LayoutRender.toArray - let _descText = kwDescription |> TaggedText.tagText |> wordL |> LayoutRender.toArray - yield ToolTipElement.Single(kwText, FSharpXmlDoc.FromXmlText(Xml.XmlDoc([| kwDescription |], range.Zero))) ] diff --git a/vsintegration/src/FSharp.Editor/DocComments/XMLDocumentation.fs b/vsintegration/src/FSharp.Editor/DocComments/XMLDocumentation.fs index 689b9c0a18..b48991fb35 100644 --- a/vsintegration/src/FSharp.Editor/DocComments/XMLDocumentation.fs +++ b/vsintegration/src/FSharp.Editor/DocComments/XMLDocumentation.fs @@ -401,24 +401,46 @@ module internal XmlDocumentation = collector.Add(tagText separatorText) AppendHardLine collector - let BuildSingleTipText (documentationProvider: IDocumentationBuilder, dataTipElement: ToolTipElement) = + type LineLimits = + { + LineLimit: int + TypeParameterLimit: int + OverLoadsLimit: int + } + + let DefaultLineLimits = + { + LineLimit = 45 + TypeParameterLimit = 6 + OverLoadsLimit = 5 + } + + let BuildSingleTipText (documentationProvider: IDocumentationBuilder, dataTipElement: ToolTipElement, limits: LineLimits) = + + let { + LineLimit = lineLimit + TypeParameterLimit = typeParameterLineLimit + OverLoadsLimit = overLoadsLimit + } = + limits + let mainDescription, documentation, typeParameterMap, exceptions, usage = ResizeArray(), ResizeArray(), ResizeArray(), ResizeArray(), ResizeArray() let textCollector: ITaggedTextCollector = - TextSanitizingCollector(mainDescription.Add, lineLimit = 45) + TextSanitizingCollector(mainDescription.Add, lineLimit = lineLimit) let xmlCollector: ITaggedTextCollector = - TextSanitizingCollector(documentation.Add, lineLimit = 45) + TextSanitizingCollector(documentation.Add, lineLimit = lineLimit) let typeParameterMapCollector: ITaggedTextCollector = - TextSanitizingCollector(typeParameterMap.Add, lineLimit = 6) + TextSanitizingCollector(typeParameterMap.Add, lineLimit = typeParameterLineLimit) let exnCollector: ITaggedTextCollector = - TextSanitizingCollector(exceptions.Add, lineLimit = 45) + TextSanitizingCollector(exceptions.Add, lineLimit = lineLimit) let usageCollector: ITaggedTextCollector = - TextSanitizingCollector(usage.Add, lineLimit = 45) + TextSanitizingCollector(usage.Add, lineLimit = lineLimit) let ProcessGenericParameters (tps: TaggedText[] list) = if not tps.IsEmpty then @@ -440,14 +462,14 @@ module internal XmlDocumentation = match dataTipElement with | ToolTipElement.Group overloads when not overloads.IsEmpty -> - overloads[..4] + overloads[.. overLoadsLimit - 1] |> List.map (fun item -> item.MainDescription) |> List.intersperse [| lineBreak |] |> Seq.concat |> Seq.iter textCollector.Add - if not overloads[5..].IsEmpty then - AppendOnNewLine textCollector $"({(PrettyNaming.FormatAndOtherOverloadsString overloads[5..].Length)})" + if not overloads[overLoadsLimit..].IsEmpty then + AppendOnNewLine textCollector $"({(PrettyNaming.FormatAndOtherOverloadsString overloads[overLoadsLimit..].Length)})" let item0 = overloads.Head diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs index 70017f2de1..c8e893070a 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs @@ -35,7 +35,7 @@ type internal FSharpAsyncQuickInfoSource let getSingleContent (data: ToolTipElement) = let symbol, description, documentation = - XmlDocumentation.BuildSingleTipText(documentationBuilder, data) + XmlDocumentation.BuildSingleTipText(documentationBuilder, data, XmlDocumentation.DefaultLineLimits) let getLinkTooltip filePath = let solutionDir = Path.GetDirectoryName(document.Project.Solution.FilePath) From fdf5e21becf6cfec68ca3a2aa975764f0c09b5e7 Mon Sep 17 00:00:00 2001 From: majocha Date: Thu, 6 Apr 2023 17:40:25 +0200 Subject: [PATCH 7/7] fix --- vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs index 20d6f9826f..706d79c59b 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs @@ -49,7 +49,7 @@ module QuickInfo = } let _, mainDescription, docs = - XmlDocumentation.BuildSingleTipText(documentationBuilder, elements.Head) + XmlDocumentation.BuildSingleTipText(documentationBuilder, elements.Head, XmlDocumentation.DefaultLineLimits) let mainTextItems = mainDescription |> Seq.map (fun x -> x.Text) let docTextItems = docs |> Seq.map (fun x -> x.Text)