diff --git a/src/fsharp/FSharp.Compiler/FSharp.Compiler.fsproj b/src/fsharp/FSharp.Compiler/FSharp.Compiler.fsproj index 1ef7b86406..c5d0c2f435 100644 --- a/src/fsharp/FSharp.Compiler/FSharp.Compiler.fsproj +++ b/src/fsharp/FSharp.Compiler/FSharp.Compiler.fsproj @@ -516,17 +516,6 @@ ..\..\..\packages\System.Reflection.Metadata.1.4.1-beta-24227-04\lib\portable-net45+win8\System.Reflection.Metadata.dll ..\..\..\packages\System.Collections.Immutable.1.2.0\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll ..\..\..\packages\System.ValueTuple.4.3.0\lib\netstandard1.0\System.ValueTuple.dlltrue - - - - - - - - - - - diff --git a/src/fsharp/infos.fs b/src/fsharp/infos.fs index 167e71c824..87a7bf07d7 100755 --- a/src/fsharp/infos.fs +++ b/src/fsharp/infos.fs @@ -1459,6 +1459,10 @@ type MethInfo = (paramAttribs,paramNamesAndTypes) ||> List.map2 (List.map2 (fun (isParamArrayArg,isOutArg,optArgInfo,callerInfoInfo,reflArgInfo) (ParamNameAndType(nmOpt,pty)) -> ParamData(isParamArrayArg,isOutArg,optArgInfo,callerInfoInfo,nmOpt,reflArgInfo,pty))) + /// Get the ParamData objects for the parameters of a MethInfo + member x.HasParamArrayArg(amap, m, minst) = + x.GetParamDatas(amap, m, minst) |> List.existsSquared (fun (ParamData(isParamArrayArg,_,_,_,_,_,_)) -> isParamArrayArg) + /// Select all the type parameters of the declaring type of a method. /// diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 490e7fe88d..365823feee 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -4520,12 +4520,11 @@ typeArgsActual: | LESS typeArgActual GREATER { (rhs parseState 1), Some(rhs parseState 3), true, [$2], [], lhs parseState } - | LESS typeArgActual recover - { if not $3 then - reportParseErrorAt (rhs parseState 3) (FSComp.SR.parsUnexpectedEndOfFileTypeArgs()) - else - reportParseErrorAt (rhs parseState 3) (FSComp.SR.parsMissingGreaterThan()) - (rhs parseState 1), None, false, [$2], [], (rhs2 parseState 1 2) } + | LESS typeArgActual ends_coming_soon_or_recover + { let nextToken = rhs parseState 3 + if not $3 then reportParseErrorAt nextToken (FSComp.SR.parsMissingTypeArgs()) + let zeroWidthAtStartOfNextToken = nextToken.StartRange + (rhs parseState 1), None, false, [$2], [], unionRanges (rhs parseState 1) zeroWidthAtStartOfNextToken } | LESS GREATER { (rhs parseState 1), Some(rhs parseState 2), true, [], [], lhs parseState } diff --git a/src/fsharp/vs/ServiceDeclarations.fs b/src/fsharp/vs/ServiceDeclarations.fs index 77103a50c8..53a4702c24 100644 --- a/src/fsharp/vs/ServiceDeclarations.fs +++ b/src/fsharp/vs/ServiceDeclarations.fs @@ -375,7 +375,7 @@ module internal ItemDescriptionsImpl = | _ -> None /// This function gets the signature to pass to Visual Studio to use its lookup functions for .NET stuff. - let rec GetXmlDocHelpSigOfItemForLookup (infoReader:InfoReader) m d = + let GetXmlDocHelpSigOfItemForLookup (infoReader:InfoReader) m d = let g = infoReader.g match d with @@ -404,30 +404,33 @@ module internal ItemDescriptionsImpl = | ArgumentContainer.UnionCase ucinfo -> mkXmlComment (GetXmlDocSigOfUnionCaseInfo ucinfo) | _ -> FSharpXmlDoc.None - /// Produce an XmlComment with a signature or raw text. - let GetXmlComment (xmlDoc:XmlDoc) (infoReader:InfoReader) m d = + /// Produce an XmlComment with a signature or raw text, given the F# comment and the item + let GetXmlCommentForItemAux (xmlDoc:XmlDoc option) (infoReader:InfoReader) m d = let result = match xmlDoc with - | XmlDoc [| |] -> "" - | XmlDoc l -> + | None | Some (XmlDoc [| |]) -> "" + | Some (XmlDoc l) -> bufs (fun os -> bprintf os "\n"; l |> Array.iter (fun (s:string) -> // Note: this code runs for local/within-project xmldoc tooltips, but not for cross-project or .XML bprintf os "\n%s" s)) - let xml = if String.IsNullOrEmpty result then FSharpXmlDoc.None else FSharpXmlDoc.Text result - match xml with - | FSharpXmlDoc.None -> GetXmlDocHelpSigOfItemForLookup infoReader m d - | _ -> xml + if String.IsNullOrEmpty result then + GetXmlDocHelpSigOfItemForLookup infoReader m d + else + FSharpXmlDoc.Text result let mutable ToolTipFault = None + let GetXmlCommentForMethInfoItem infoReader m d (minfo: MethInfo) = + GetXmlCommentForItemAux (if minfo.HasDirectXmlComment then Some minfo.XmlDoc else None) infoReader m d + /// Output a method info let FormatOverloadsToList (infoReader:InfoReader) m denv d minfos : FSharpToolTipElement = let formatOne minfo = let text = bufs (fun os -> NicePrint.formatMethInfoToBufferFreeStyle infoReader.amap m denv os minfo) - let xml = GetXmlComment (if minfo.HasDirectXmlComment then minfo.XmlDoc else XmlDoc [||]) infoReader m d + let xml = GetXmlCommentForMethInfoItem infoReader m d minfo text,xml ToolTipFault |> Option.iter (fun msg -> @@ -634,11 +637,80 @@ module internal ItemDescriptionsImpl = | Item.ModuleOrNamespaces [] | Item.Property(_,[]) -> "" + /// Output a the description of a language item + let rec GetXmlCommentForItem (infoReader:InfoReader) m d = + let g = infoReader.g + match d with + | Item.ImplicitOp(_, { contents = Some(TraitConstraintSln.FSMethSln(_, vref, _)) }) -> + GetXmlCommentForItem infoReader m (Item.Value vref) + + | Item.Value vref | Item.CustomBuilder (_,vref) -> + GetXmlCommentForItemAux (if valRefInThisAssembly g.compilingFslib vref then Some vref.XmlDoc else None) infoReader m d + + | Item.UnionCase(ucinfo,_) -> + GetXmlCommentForItemAux (if tyconRefUsesLocalXmlDoc g.compilingFslib ucinfo.TyconRef then Some ucinfo.UnionCase .XmlDoc else None) infoReader m d + + | Item.ActivePatternCase apref -> + GetXmlCommentForItemAux (Some apref.ActivePatternVal.XmlDoc) infoReader m d + + | Item.ExnCase ecref -> + GetXmlCommentForItemAux (if tyconRefUsesLocalXmlDoc g.compilingFslib ecref then Some ecref.XmlDoc else None) infoReader m d + + | Item.RecdField rfinfo -> + GetXmlCommentForItemAux (if tyconRefUsesLocalXmlDoc g.compilingFslib rfinfo.TyconRef then Some rfinfo.RecdField.XmlDoc else None) infoReader m d + + | Item.Event einfo -> + GetXmlCommentForItemAux (if einfo.HasDirectXmlComment then Some einfo.XmlDoc else None) infoReader m d + + | Item.Property(_,pinfos) -> + let pinfo = pinfos.Head + GetXmlCommentForItemAux (if pinfo.HasDirectXmlComment then Some pinfo.XmlDoc else None) infoReader m d + + | Item.CustomOperation (_,_,Some minfo) + | Item.CtorGroup(_,minfo :: _) + | Item.MethodGroup(_,minfo :: _,_) -> + GetXmlCommentForMethInfoItem infoReader m d minfo + + | Item.Types(_,((TType_app(tcref,_)):: _)) -> + GetXmlCommentForItemAux (if tyconRefUsesLocalXmlDoc g.compilingFslib tcref then Some tcref.XmlDoc else None) infoReader m d + + | Item.ModuleOrNamespaces((modref :: _) as modrefs) -> + let definiteNamespace = modrefs |> List.forall (fun modref -> modref.IsNamespace) + if not definiteNamespace then + GetXmlCommentForItemAux (if entityRefInThisAssembly g.compilingFslib modref then Some modref.XmlDoc else None) infoReader m d + else + GetXmlCommentForItemAux None infoReader m d + + | Item.ArgName (_, _, argContainer) -> + let xmldoc = + match argContainer with + | Some(ArgumentContainer.Method (minfo)) -> + if minfo.HasDirectXmlComment then Some minfo.XmlDoc else None + | Some(ArgumentContainer.Type(tcref)) -> + if (tyconRefUsesLocalXmlDoc g.compilingFslib tcref) then Some tcref.XmlDoc else None + | Some(ArgumentContainer.UnionCase(ucinfo)) -> + if (tyconRefUsesLocalXmlDoc g.compilingFslib ucinfo.TyconRef) then Some ucinfo.UnionCase.XmlDoc else None + | _ -> None + GetXmlCommentForItemAux xmldoc infoReader m d + + | Item.SetterArg (_, item) -> + GetXmlCommentForItem infoReader m item + + // In all these cases, there is no direct XML documentation from F# comments + | Item.ActivePatternResult _ + | Item.NewDef _ + | Item.ILField _ + | Item.FakeInterfaceCtor _ + | Item.DelegateCtor _ + | _ -> + GetXmlCommentForItemAux None infoReader m d + /// Output a the description of a language item let rec FormatItemDescriptionToToolTipElement isDecl (infoReader:InfoReader) m denv d = let g = infoReader.g let amap = infoReader.amap let denv = SimplerDisplayEnv denv isDecl + let xml = GetXmlCommentForItem infoReader m d match d with | Item.ImplicitOp(_, { contents = Some(TraitConstraintSln.FSMethSln(_, vref, _)) }) -> // operator with solution @@ -649,7 +721,6 @@ module internal ItemDescriptionsImpl = NicePrint.outputQualifiedValOrMember denv os vref.Deref OutputFullName isDecl pubpath_of_vref fullDisplayTextOfValRef os vref) - let xml = GetXmlComment (if (valRefInThisAssembly g.compilingFslib vref) then vref.XmlDoc else XmlDoc [||]) infoReader m d FSharpToolTipElement.Single(text, xml) // Union tags (constructors) @@ -668,8 +739,6 @@ module internal ItemDescriptionsImpl = os.Append (" -> ") |> ignore NicePrint.outputTy denv os rty ) - - let xml = GetXmlComment (if (tyconRefUsesLocalXmlDoc g.compilingFslib ucinfo.TyconRef) then uc.XmlDoc else XmlDoc [||]) infoReader m d FSharpToolTipElement.Single(text, xml) // Active pattern tag inside the declaration (result) @@ -678,11 +747,9 @@ module internal ItemDescriptionsImpl = let text = bufs (fun os -> bprintf os "%s %s: " (FSComp.SR.typeInfoActivePatternResult()) (List.item idx items) NicePrint.outputTy denv os ty) - let xml = GetXmlComment (XmlDoc [||]) infoReader m d FSharpToolTipElement.Single(text, xml) // Active pattern tags - // XmlDoc is never emitted to xml doc files for these | Item.ActivePatternCase apref -> let v = apref.ActivePatternVal // Format the type parameters to get e.g. ('a -> 'a) rather than ('?1234 -> '?1234) @@ -695,8 +762,6 @@ module internal ItemDescriptionsImpl = apref.Name NicePrint.outputTy denv os ptau OutputFullName isDecl pubpath_of_vref fullDisplayTextOfValRef os v) - - let xml = GetXmlComment v.XmlDoc infoReader m d FSharpToolTipElement.Single(text, xml) // F# exception names @@ -704,7 +769,6 @@ module internal ItemDescriptionsImpl = let text = bufs (fun os -> NicePrint.outputExnDef denv os ecref.Deref OutputFullName isDecl pubpath_of_tcref fullDisplayTextOfExnRef os ecref) - let xml = GetXmlComment (if (tyconRefUsesLocalXmlDoc g.compilingFslib ecref) then ecref.XmlDoc else XmlDoc [||]) infoReader m d FSharpToolTipElement.Single(text, xml) // F# record field names @@ -721,14 +785,12 @@ module internal ItemDescriptionsImpl = | None -> () | Some lit -> try bprintf os " = %s" (Layout.showL ( NicePrint.layoutConst denv.g ty lit )) with _ -> ()) - - let xml = GetXmlComment (if (tyconRefUsesLocalXmlDoc g.compilingFslib rfinfo.TyconRef) then rfield.XmlDoc else XmlDoc [||]) infoReader m d FSharpToolTipElement.Single(text, xml) // Not used | Item.NewDef id -> let dataTip = bufs (fun os -> bprintf os "%s %s" (FSComp.SR.typeInfoPatternVariable()) id.idText) - FSharpToolTipElement.Single(dataTip, GetXmlComment (XmlDoc [||]) infoReader m d) + FSharpToolTipElement.Single(dataTip, xml) // .NET fields | Item.ILField finfo -> @@ -741,7 +803,7 @@ module internal ItemDescriptionsImpl = | Some v -> try bprintf os " = %s" (Layout.showL ( NicePrint.layoutConst denv.g (finfo.FieldType(infoReader.amap, m)) (TypeChecker.TcFieldInit m v) )) with _ -> ()) - FSharpToolTipElement.Single(dataTip, GetXmlComment (XmlDoc [||]) infoReader m d) + FSharpToolTipElement.Single(dataTip, xml) // .NET events | Item.Event einfo -> @@ -755,8 +817,6 @@ module internal ItemDescriptionsImpl = bprintf os ".%s: " einfo.EventName NicePrint.outputTy denv os rty) - let xml = GetXmlComment (if einfo.HasDirectXmlComment then einfo.XmlDoc else XmlDoc [||]) infoReader m d - FSharpToolTipElement.Single(text, xml) // F# and .NET properties @@ -772,8 +832,6 @@ module internal ItemDescriptionsImpl = bprintf os ".%s: " pinfo.PropertyName NicePrint.outputTy denv os rty) - let xml = GetXmlComment (if pinfo.HasDirectXmlComment then pinfo.XmlDoc else XmlDoc [||]) infoReader m d - FSharpToolTipElement.Single(text, xml) // Custom operations in queries @@ -803,8 +861,6 @@ module internal ItemDescriptionsImpl = bprintf os ".%s " minfo.DisplayName) - let xml = GetXmlComment (if minfo.HasDirectXmlComment then minfo.XmlDoc else XmlDoc [||]) infoReader m d - FSharpToolTipElement.Single(text, xml) // F# constructors and methods @@ -820,7 +876,7 @@ module internal ItemDescriptionsImpl = | Item.FakeInterfaceCtor typ -> let _, typ, _ = PrettyTypes.PrettifyTypes1 g typ let text = bufs (fun os -> NicePrint.outputTyconRef denv os (tcrefOfAppTy g typ)) - FSharpToolTipElement.Single(text, GetXmlComment (XmlDoc [||]) infoReader m d) + FSharpToolTipElement.Single(text, xml) // The 'fake' representation of constructors of .NET delegate types | Item.DelegateCtor delty -> @@ -831,7 +887,6 @@ module internal ItemDescriptionsImpl = bprintf os "(" NicePrint.outputTy denv os fty bprintf os ")") - let xml = GetXmlComment (XmlDoc [||]) infoReader m d FSharpToolTipElement.Single(text, xml) // Types. @@ -841,8 +896,6 @@ module internal ItemDescriptionsImpl = let denv = { denv with shortTypeNames = true } NicePrint.outputTycon denv infoReader AccessibleFromSomewhere m (* width *) os tcref.Deref OutputFullName isDecl pubpath_of_tcref fullDisplayTextOfTyconRef os tcref) - - let xml = GetXmlComment (if (tyconRefUsesLocalXmlDoc g.compilingFslib tcref) then tcref.XmlDoc else XmlDoc [||]) infoReader m d FSharpToolTipElement.Single(text, xml) // F# Modules and namespaces @@ -868,27 +921,16 @@ module internal ItemDescriptionsImpl = bprintf os "\n" for i, txt in namesToAdd do bprintf os "\n%s" ((if i = 0 then FSComp.SR.typeInfoFromFirst else FSComp.SR.typeInfoFromNext) txt) - let xml = GetXmlComment (if (entityRefInThisAssembly g.compilingFslib modref) then modref.XmlDoc else XmlDoc [||]) infoReader m d FSharpToolTipElement.Single(os.ToString(), xml) else - FSharpToolTipElement.Single(os.ToString(), GetXmlComment (XmlDoc [||]) infoReader m d) + FSharpToolTipElement.Single(os.ToString(), xml) // Named parameters - | Item.ArgName (id, argTy, argContainer) -> + | Item.ArgName (id, argTy, _) -> let _, argTy, _ = PrettyTypes.PrettifyTypes1 g argTy let text = bufs (fun os -> bprintf os "%s %s : " (FSComp.SR.typeInfoArgument()) id.idText NicePrint.outputTy denv os argTy) - - let xmldoc = match argContainer with - | Some(ArgumentContainer.Method (minfo)) -> - if minfo.HasDirectXmlComment then minfo.XmlDoc else XmlDoc [||] - | Some(ArgumentContainer.Type(tcref)) -> - if (tyconRefUsesLocalXmlDoc g.compilingFslib tcref) then tcref.XmlDoc else XmlDoc [||] - | Some(ArgumentContainer.UnionCase(ucinfo)) -> - if (tyconRefUsesLocalXmlDoc g.compilingFslib ucinfo.TyconRef) then ucinfo.UnionCase.XmlDoc else XmlDoc [||] - | _ -> XmlDoc [||] - let xml = GetXmlComment xmldoc infoReader m d FSharpToolTipElement.SingleParameter(text, xml, id.idText) | Item.SetterArg (_, item) -> diff --git a/src/fsharp/vs/ServiceDeclarations.fsi b/src/fsharp/vs/ServiceDeclarations.fsi index 028b9935f7..b1f6b156c2 100755 --- a/src/fsharp/vs/ServiceDeclarations.fsi +++ b/src/fsharp/vs/ServiceDeclarations.fsi @@ -98,6 +98,7 @@ module internal ItemDescriptionsImpl = val GetXmlDocSigOfValRef : TcGlobals -> ValRef -> (string option * string) option val GetXmlDocSigOfProp : InfoReader -> range -> PropInfo -> (string option * string) option val GetXmlDocSigOfEvent : InfoReader -> range -> EventInfo -> (string option * string) option + val GetXmlCommentForItem : InfoReader -> range -> Item -> FSharpXmlDoc val FormatDescriptionOfItem : bool -> InfoReader -> range -> DisplayEnv -> Item -> FSharpToolTipElement val FormatReturnTypeOfItem : InfoReader -> range -> DisplayEnv -> Item -> string val RemoveDuplicateItems : TcGlobals -> Item list -> Item list diff --git a/src/fsharp/vs/ServiceParamInfoLocations.fs b/src/fsharp/vs/ServiceParamInfoLocations.fs index 7f29854c4b..f72c1f4a2a 100755 --- a/src/fsharp/vs/ServiceParamInfoLocations.fs +++ b/src/fsharp/vs/ServiceParamInfoLocations.fs @@ -6,7 +6,7 @@ open Microsoft.FSharp.Compiler.Range open Microsoft.FSharp.Compiler.Ast [] -type FSharpNoteworthyParamInfoLocations(longId: string list, longIdRange: range, openParenLocation: pos, tupleEndLocations: pos list, isThereACloseParen: bool, namedParamNames: string list) = +type FSharpNoteworthyParamInfoLocations(longId: string list, longIdRange: range, openParenLocation: pos, tupleEndLocations: pos list, isThereACloseParen: bool, namedParamNames: string option list) = let tupleEndLocations = Array.ofList tupleEndLocations let namedParamNames = Array.ofList namedParamNames @@ -19,7 +19,7 @@ type FSharpNoteworthyParamInfoLocations(longId: string list, longIdRange: range, // so just fill in a blank named param to represent the final missing param // (compare to f( or f(42, where the parser injects a fake "AbrExpr" to represent the missing argument) assert(tupleEndLocations.Length = namedParamNames.Length + 1) - [| yield! namedParamNames; yield null |] // "null" is representation of a non-named param + [| yield! namedParamNames; yield None |] // None is representation of a non-named param member this.LongId = longId member this.LongIdStartLocation = longIdRange.Start member this.LongIdEndLocation = longIdRange.End @@ -48,14 +48,14 @@ module internal NoteworthyParamInfoLocationsImpl = | _ -> None type FindResult = - | Found of openParen: pos * commasAndCloseParen: (pos * string) list * hasClosedParen: bool + | Found of openParen: pos * commasAndCloseParen: (pos * string option) list * hasClosedParen: bool | NotFound let digOutIdentFromStaticArg synType = match synType with - | SynType.StaticConstantNamed(SynType.LongIdent(LongIdentWithDots([id],_)),_,_) -> id.idText - | SynType.LongIdent(LongIdentWithDots([id],_)) -> id.idText // NOTE: again, not a static constant, but may be a prefix of a Named in incomplete code - | _ -> null + | SynType.StaticConstantNamed(SynType.LongIdent(LongIdentWithDots([id],_)),_,_) -> Some id.idText + | SynType.LongIdent(LongIdentWithDots([id],_)) -> Some id.idText // NOTE: again, not a static constant, but may be a prefix of a Named in incomplete code + | _ -> None let getNamedParamName e = match e with @@ -65,14 +65,14 @@ module internal NoteworthyParamInfoLocationsImpl = SynExpr.Ident op, SynExpr.Ident n, _range), - _, _) when op.idText="op_Equality" -> n.idText + _, _) when op.idText="op_Equality" -> Some n.idText // f(?x=4) | SynExpr.App(ExprAtomicFlag.NonAtomic, _, SynExpr.App(ExprAtomicFlag.NonAtomic, true, SynExpr.Ident op, SynExpr.LongIdent(true(*isOptional*),LongIdentWithDots([n],_),_ref,_lidrange), _range), - _, _) when op.idText="op_Equality" -> n.idText - | _ -> null + _, _) when op.idText="op_Equality" -> Some n.idText + | _ -> None let getTypeName(synType) = match synType with @@ -121,14 +121,14 @@ module internal NoteworthyParamInfoLocationsImpl = | SynExpr.ArbitraryAfterError(_debugStr, range) -> // single argument when e.g. after open paren you hit EOF if AstTraversal.rangeContainsPosEdgesExclusive range pos then - let r = Found (range.Start, [range.End, null], false) + let r = Found (range.Start, [(range.End, None)], false) r, None else NotFound, None | SynExpr.Const(SynConst.Unit, unitRange) -> if AstTraversal.rangeContainsPosEdgesExclusive unitRange pos then - let r = Found (unitRange.Start, [unitRange.End, null], true) + let r = Found (unitRange.Start, [(unitRange.End, None)], true) r, None else NotFound, None @@ -139,7 +139,7 @@ module internal NoteworthyParamInfoLocationsImpl = | None -> if AstTraversal.rangeContainsPosEdgesExclusive e.Range pos then // any other expression doesn't start with parens, so if it was the target of an App, then it must be a single argument e.g. "f x" - Found (e.Range.Start, [e.Range.End, null], false), Some inner + Found (e.Range.Start, [ (e.Range.End, None) ], false), Some inner else NotFound, Some inner | _ -> NotFound, Some inner diff --git a/src/fsharp/vs/ServiceParamInfoLocations.fsi b/src/fsharp/vs/ServiceParamInfoLocations.fsi index 5b6b19f177..b4cdef33f9 100755 --- a/src/fsharp/vs/ServiceParamInfoLocations.fsi +++ b/src/fsharp/vs/ServiceParamInfoLocations.fsi @@ -20,8 +20,8 @@ type internal FSharpNoteworthyParamInfoLocations = member TupleEndLocations : pos[] /// false if either this is a call without parens "f x" or the parser recovered as in "f(x,y" member IsThereACloseParen : bool - /// empty or a name if an actual named parameter; f(0,a=4,?b=None) would be [|null;"a";"b"|] - member NamedParamNames : string[] + /// empty or a name if an actual named parameter; f(0,a=4,?b=None) would be [|None; Some "a"; Some "b"|] + member NamedParamNames : string option [] static member Find : pos * Ast.ParsedInput -> FSharpNoteworthyParamInfoLocations option diff --git a/src/fsharp/vs/service.fs b/src/fsharp/vs/service.fs index fa47e06e95..e31c120fc7 100755 --- a/src/fsharp/vs/service.fs +++ b/src/fsharp/vs/service.fs @@ -63,10 +63,11 @@ module EnvMisc = //-------------------------------------------------------------------------- [] -type FSharpMethodGroupItemParameter(name: string, canonicalTypeTextForSorting: string, display: string) = +type FSharpMethodGroupItemParameter(name: string, canonicalTypeTextForSorting: string, display: string, isOptional: bool) = member __.ParameterName = name member __.CanonicalTypeTextForSorting = canonicalTypeTextForSorting member __.Display = display + member __.IsOptional = isOptional /// Format parameters for Intellisense completion module internal Params = @@ -82,7 +83,8 @@ module internal Params = FSharpMethodGroupItemParameter( name = f.rfield_id.idText, canonicalTypeTextForSorting = printCanonicalizedTypeName g denv f.rfield_type, - display = NicePrint.prettyStringOfTy denv f.rfield_type) + display = NicePrint.prettyStringOfTy denv f.rfield_type, + isOptional=false) let ParamOfUnionCaseField g denv isGenerated (i : int) f = let initial = ParamOfRecdField g denv f @@ -90,17 +92,19 @@ module internal Params = FSharpMethodGroupItemParameter( name=initial.ParameterName, canonicalTypeTextForSorting=initial.CanonicalTypeTextForSorting, - display=display) + display=display, + isOptional=false) - let ParamOfParamData g denv (ParamData(_isParamArrayArg, _isOutArg, _optArgInfo, _callerInfoInfo, nmOpt, _reflArgInfo, pty) as paramData) = + let ParamOfParamData g denv (ParamData(_isParamArrayArg, _isOutArg, optArgInfo, _callerInfoInfo, nmOpt, _reflArgInfo, pty) as paramData) = FSharpMethodGroupItemParameter( name = (match nmOpt with None -> "" | Some pn -> pn.idText), canonicalTypeTextForSorting = printCanonicalizedTypeName g denv pty, - display = NicePrint.stringOfParamData denv paramData) + display = NicePrint.stringOfParamData denv paramData, + isOptional=optArgInfo.IsOptional) // TODO this code is similar to NicePrint.fs:formatParamDataToBuffer, refactor or figure out why different? let ParamsOfParamDatas g denv (paramDatas:ParamData list) rty = - let paramNames,paramPrefixes,paramTypes = + let paramInfo,paramTypes = paramDatas |> List.map (fun (ParamData(isParamArrayArg, _isOutArg, optArgInfo, _callerInfoInfo, nmOpt, _reflArgInfo, pty)) -> let isOptArg = optArgInfo.IsOptional @@ -110,10 +114,10 @@ module internal Params = let nm = id.idText // detect parameter type, if ptyOpt is None - this is .NET style optional argument let pty = defaultArg ptyOpt pty - nm, (sprintf "?%s:" nm), pty + (nm, isOptArg, sprintf "?%s:" nm), pty // Layout an unnamed argument | None, _,_ -> - "", "", pty + ("", isOptArg, ""), pty // Layout a named argument | Some id,_,_ -> let nm = id.idText @@ -122,15 +126,16 @@ module internal Params = sprintf "%s %s: " (NicePrint.PrintUtilities.layoutBuiltinAttribute denv denv.g.attrib_ParamArrayAttribute |> showL) nm else sprintf "%s: " nm - nm, prefix,pty) - |> List.unzip3 + (nm,isOptArg, prefix),pty) + |> List.unzip let paramTypeAndRetLs,_ = NicePrint.layoutPrettifiedTypes denv (paramTypes@[rty]) let paramTypeLs,_ = List.frontAndBack paramTypeAndRetLs - (paramNames,paramPrefixes,(paramTypes,paramTypeLs)||>List.zip) |||> List.map3 (fun nm paramPrefix (tau,tyL) -> + (paramInfo,paramTypes,paramTypeLs) |||> List.map3 (fun (nm,isOptArg,paramPrefix) tau tyL -> FSharpMethodGroupItemParameter( name = nm, canonicalTypeTextForSorting = printCanonicalizedTypeName g denv tau, - display = paramPrefix+(showL tyL) + display = paramPrefix+(showL tyL), + isOptional=isOptArg )) let ParamsOfTypes g denv args rtau = @@ -140,7 +145,8 @@ module internal Params = FSharpMethodGroupItemParameter( name = "", canonicalTypeTextForSorting = printCanonicalizedTypeName g denv tau, - display = Layout.showL tyL + display = Layout.showL tyL, + isOptional=false ) (args,argsL) ||> List.zip |> List.map mkParam @@ -212,7 +218,8 @@ module internal Params = FSharpMethodGroupItemParameter( name = spName, canonicalTypeTextForSorting = spKind, - display = sprintf "%s%s: %s" (if spOpt then "?" else "") spName spKind)) + display = sprintf "%s%s: %s" (if spOpt then "?" else "") spName spKind, + isOptional=spOpt)) #endif | _ -> [| |] @@ -295,11 +302,13 @@ module internal Params = /// A single method for Intellisense completion [] // Note: instances of this type do not hold any references to any compiler resources. -type FSharpMethodGroupItem(description: FSharpToolTipText, typeText: string, parameters: FSharpMethodGroupItemParameter[], hasParameters: bool, staticParameters: FSharpMethodGroupItemParameter[]) = +type FSharpMethodGroupItem(description: FSharpToolTipText, xmlDoc: FSharpXmlDoc, typeText: string, parameters: FSharpMethodGroupItemParameter[], hasParameters: bool, hasParamArrayArg: bool, staticParameters: FSharpMethodGroupItemParameter[]) = member __.Description = description + member __.XmlDoc = xmlDoc member __.TypeText = typeText member __.Parameters = parameters member __.HasParameters = hasParameters + member __.HasParamArrayArg = hasParamArrayArg // Does the type name or method support a static arguments list, like TP<42,"foo"> or conn.CreateCommand<42, "foo">(arg1, arg2)? member __.StaticParameters = staticParameters @@ -322,7 +331,7 @@ type FSharpMethodGroup( name: string, unsortedMethods: FSharpMethodGroupItem[] ) |> Array.map (fun meth -> let parms = meth.Parameters if parms.Length = 1 && parms.[0].CanonicalTypeTextForSorting="Microsoft.FSharp.Core.Unit" then - FSharpMethodGroupItem(meth.Description,meth.TypeText,[||],true,meth.StaticParameters) + FSharpMethodGroupItem(meth.Description, meth.XmlDoc, meth.TypeText, [||], true, meth.HasParamArrayArg, meth.StaticParameters) else meth) // Fix the order of methods, to be stable for unit testing. @@ -375,8 +384,10 @@ type FSharpMethodGroup( name: string, unsortedMethods: FSharpMethodGroupItem[] ) FSharpMethodGroupItem( description = FSharpToolTipText [FormatDescriptionOfItem true infoReader m denv item], typeText = FormatReturnTypeOfItem infoReader m denv item, + xmlDoc = GetXmlCommentForItem infoReader m item, parameters = (Params.ParamsOfItem infoReader m denv item |> Array.ofList), hasParameters = (match item with Params.ItemIsProvidedTypeWithStaticArguments m g _ -> false | _ -> true), + hasParamArrayArg = (match item with Item.CtorGroup(_,[meth]) | Item.MethodGroup(_,[meth],_) -> meth.HasParamArrayArg(infoReader.amap, m, meth.FormalMethodInst) | _ -> false), staticParameters = Params.StaticParamsOfItem infoReader m denv item )) #if !FX_NO_WEAKTABLE diff --git a/src/fsharp/vs/service.fsi b/src/fsharp/vs/service.fsi index b74805915f..d978cafc41 100755 --- a/src/fsharp/vs/service.fsi +++ b/src/fsharp/vs/service.fsi @@ -29,11 +29,17 @@ type internal FSharpMethodGroupItemParameter = /// information such as whether it is optional. member Display: string + /// Is the parameter optional + member IsOptional: bool + /// Represents one method (or other item) in a method group. The item may represent either a method or /// a single, non-overloaded item such as union case or a named function value. [] type internal FSharpMethodGroupItem = + /// The documentation for the item + member XmlDoc : FSharpXmlDoc + /// The formatted description text for the method (or other item) member Description : FSharpToolTipText @@ -46,6 +52,9 @@ type internal FSharpMethodGroupItem = /// Does the method support an arguments list? This is always true except for static type instantiations like TP<42,"foo">. member HasParameters: bool + /// Does the method support a params list arg? + member HasParamArrayArg: bool + /// Does the type name or method support a static arguments list, like TP<42,"foo"> or conn.CreateCommand<42, "foo">(arg1, arg2)? member StaticParameters: FSharpMethodGroupItemParameter[] diff --git a/tests/fsharp/tests.fs b/tests/fsharp/tests.fs index 82b3a2d2ba..d626d307c5 100644 --- a/tests/fsharp/tests.fs +++ b/tests/fsharp/tests.fs @@ -1707,30 +1707,76 @@ module TypecheckTests = for n in negs do singleNegTest cfg n [] - let ``sigs neg group1`` () = negGroup ["neg97"; "neg96"; "neg95"; "neg94"; "neg93"; "neg92"; "neg91" ] + let ``sigs neg group1`` () = negGroup ["neg97"] [] - let ``sigs neg group2`` () = negGroup ["neg90"; "neg89"; "neg88"; "neg35" ] + let ``sigs neg group1a`` () = negGroup ["neg96"; ] [] - let ``sigs neg group3`` () = negGroup ["neg87"; "neg86"; "neg85"; "neg84"; "neg83"; "neg82"; "neg81"; "neg80"; "neg79"; "neg78"; "neg77"; "neg76"; "neg75"; ] + let ``sigs neg group1b`` () = negGroup ["neg93"; ] [] - let ``sigs neg group4`` () = negGroup ["neg74"; "neg73"; "neg72"; "neg71"; "neg70"; "neg69"; "neg68"; "neg67"; "neg66"; "neg65"; "neg64"; "neg61"; "neg63"; ] + let ``sigs neg group1c`` () = negGroup [ "neg91" ] [] - let ``sigs neg group5`` () = negGroup ["neg62"; "neg20"; "neg24"; "neg32"; "neg37"; "neg37_a"; "neg60"; "neg59"; "neg58"; "neg57"; "neg56"; "neg56_a"; "neg56_b" ] + let ``sigs neg group1d`` () = negGroup ["neg92" ] + + [] + let ``sigs neg group1e`` () = negGroup ["neg94"; ] + + [] + let ``sigs neg group1f`` () = negGroup ["neg95"; ] + + [] + let ``sigs neg group2`` () = negGroup ["neg90"; "neg89"; ] + + [] + let ``sigs neg group2a`` () = negGroup ["neg88"; "neg35" ] + [] - let ``sigs neg group6`` () = negGroup ["neg55"; "neg54"; "neg53"; "neg52"; "neg51"; "neg50"; "neg49"; "neg48"; "neg47"; "neg46"; "neg10"; "neg10_a"; "neg45"; ] + let ``sigs neg group3`` () = negGroup ["neg87"; "neg86"; "neg85"; "neg84"; "neg83"; "neg82"; ] + + [] + let ``sigs neg group3a`` () = negGroup [ "neg81"; "neg80"; "neg79"; "neg78"; "neg77"; "neg76"; "neg75"; ] + + [] + let ``sigs neg group4`` () = negGroup ["neg74"; "neg73"; "neg72"; "neg71"; "neg70"; "neg68"; ] + + [] + let ``sigs neg group4a`` () = negGroup ["neg69"; ] + + [] + let ``sigs neg group4b`` () = negGroup [ "neg64"; "neg61"; "neg63"; ] + + [] + let ``sigs neg group4c`` () = negGroup [ "neg67"; "neg66"; "neg65" ] + + [] + let ``sigs neg group5`` () = negGroup ["neg60"; "neg59"; "neg58"; "neg57"; "neg56"; "neg56_a"; "neg56_b" ] + + [] + let ``sigs neg group5a`` () = negGroup ["neg62"; "neg20"; "neg24"; "neg32"; "neg37"; "neg37_a"; ] + + [] + let ``sigs neg group6`` () = negGroup ["neg49"; "neg48"; "neg47"; "neg46"; "neg10"; "neg10_a"; "neg45"; ] + + [] + let ``sigs neg group6a`` () = negGroup ["neg55"; "neg54"; "neg53"; "neg52"; "neg51"; "neg50"; ] [] let ``sigs neg group7`` () = negGroup ["neg44"; "neg43"; "neg38"; "neg39"; "neg40"; "neg41"; "neg42"] [] - let ``sigs neg group8`` () = negGroup ["neg34"; "neg33"; "neg30"; "neg31"; "neg29"; "neg28"; "neg07"; "neg_byref_20"; ] + let ``sigs neg group8`` () = negGroup ["neg34"; "neg33"; "neg30"; "neg31" ] [] - let ``sigs neg group9`` () = negGroup [ "neg_byref_1"; "neg_byref_2"; "neg_byref_3"; "neg_byref_4"; "neg_byref_5"; "neg_byref_6"; "neg_byref_7"; "neg_byref_8"; ] + let ``sigs neg group8a`` () = negGroup ["neg29"; "neg28"; "neg07"; "neg_byref_20"; ] + + [] + let ``sigs neg group9`` () = negGroup [ "neg_byref_1"; "neg_byref_2"; "neg_byref_3"; "neg_byref_4"; ] + + [] + let ``sigs neg group9a`` () = negGroup [ "neg_byref_5"; "neg_byref_6"; "neg_byref_7"; "neg_byref_8"; ] [] let ``sigs neg group10`` () = negGroup ["neg_byref_10"; "neg_byref_11"; "neg_byref_12"; "neg_byref_13"; "neg_byref_14"; "neg_byref_15"; "neg_byref_16"; ] @@ -1739,10 +1785,16 @@ module TypecheckTests = let ``sigs neg group11`` () = negGroup [ "neg_byref_17"; "neg_byref_18"; "neg_byref_19"; "neg_byref_21"; "neg_byref_22"; "neg_byref_23"; "neg36"; "neg17"; "neg26"; ] [] - let ``sigs neg group12`` () = negGroup [ "neg27"; "neg25"; "neg03"; "neg23"; "neg22"; "neg21"; "neg04"; "neg05"; "neg06"; "neg06_a"; "neg06_b"; "neg08"; "neg09"; ] + let ``sigs neg group12`` () = negGroup [ "neg27"; "neg25"; "neg03"; "neg23"; "neg22"; "neg21" ] + + [] + let ``sigs neg group12a`` () = negGroup [ "neg04"; "neg05"; "neg06"; "neg06_a"; "neg06_b"; "neg08"; "neg09"; ] + + [] + let ``sigs neg group13`` () = negGroup [ "neg11"; "neg12"; "neg13"; "neg14"; "neg16" ] [] - let ``sigs neg group13`` () = negGroup [ "neg11"; "neg12"; "neg13"; "neg14"; "neg16"; "neg18"; "neg19"; "neg01"; "neg02"; "neg15" ] + let ``sigs neg group13a`` () = negGroup [ "neg18"; "neg19"; "neg01"; "neg02"; "neg15" ] module TypeProviders = diff --git a/tests/fsharp/typecheck/sigs/neg69.bsl b/tests/fsharp/typecheck/sigs/neg69.bsl index 72e8704f76..fb2c0bcdfc 100644 --- a/tests/fsharp/typecheck/sigs/neg69.bsl +++ b/tests/fsharp/typecheck/sigs/neg69.bsl @@ -64,5 +64,3 @@ neg69.fsx(94,5,94,8): parse error FS0010: Unexpected keyword 'let' or 'use' in i neg69.fsx(88,44,88,45): parse error FS0010: Unexpected symbol '>' in type definition. Expected '=' or other token. neg69.fsx(88,43,88,44): parse error FS1241: Expected type argument or static argument - -neg69.fsx(88,43,88,44): parse error FS0010: Unexpected symbol ')' in type arguments. Expected ',' or other token. diff --git a/tests/fsharp/typecheck/sigs/neg69.vsbsl b/tests/fsharp/typecheck/sigs/neg69.vsbsl index eac235551b..7b3b296bc6 100644 --- a/tests/fsharp/typecheck/sigs/neg69.vsbsl +++ b/tests/fsharp/typecheck/sigs/neg69.vsbsl @@ -65,10 +65,6 @@ neg69.fsx(88,44,88,45): parse error FS0010: Unexpected symbol '>' in type defini neg69.fsx(88,43,88,44): parse error FS1241: Expected type argument or static argument -neg69.fsx(88,43,88,44): parse error FS0010: Unexpected symbol ')' in type arguments. Expected ',' or other token. - -neg69.fsx(88,43,88,44): parse error FS0010: Unexpected symbol ')' in type arguments. Expected ',' or other token. - neg69.fsx(88,43,88,44): parse error FS1241: Expected type argument or static argument neg69.fsx(88,44,88,45): parse error FS0010: Unexpected symbol '>' in type definition. Expected '=' or other token. diff --git a/tests/service/EditorTests.fs b/tests/service/EditorTests.fs index 4a01ffcfc5..f71cd9de17 100644 --- a/tests/service/EditorTests.fs +++ b/tests/service/EditorTests.fs @@ -11,8 +11,8 @@ // and capturing large amounts of structured output. (* cd Debug\net40\bin - .\fsc.exe --define:EXE -r:.\Microsoft.Build.Utilities.Core.dll -o SomeTests.exe -g --optimize- -r .\FSharp.LanguageService.Compiler.dll -r nunit.framework.dll ..\..\..\tests\service\FsUnit.fs ..\..\..\tests\service\Common.fs /delaysign /keyfile:..\..\..\src\fsharp\msft.pubkey ..\..\..\tests\service\EditorTests.fs - .\SomeTests.exe + .\fsc.exe --define:EXE -r:.\Microsoft.Build.Utilities.Core.dll -o VisualFSharp.Unittests.exe -g --optimize- -r .\FSharp.LanguageService.Compiler.dll -r nunit.framework.dll ..\..\..\tests\service\FsUnit.fs ..\..\..\tests\service\Common.fs /delaysign /keyfile:..\..\..\src\fsharp\msft.pubkey ..\..\..\tests\service\EditorTests.fs + .\VisualFSharp.Unittests.exe *) // Technique 3: // diff --git a/vsintegration/src/FSharp.Editor/BreakpointResolutionService.fs b/vsintegration/src/FSharp.Editor/BreakpointResolutionService.fs index 9c7d6b7bee..db7e405d79 100644 --- a/vsintegration/src/FSharp.Editor/BreakpointResolutionService.fs +++ b/vsintegration/src/FSharp.Editor/BreakpointResolutionService.fs @@ -40,7 +40,7 @@ type internal FSharpBreakpointResolutionService() = let! parseResults = FSharpLanguageService.Checker.ParseFileInProject(fileName, sourceText.ToString(), options) let textLinePos = sourceText.Lines.GetLinePosition(textSpan.Start) let textLineColumn = textLinePos.Character - let fcsTextLineNumber = textLinePos.Line + 1 // Roslyn line numbers are zero-based, FSharp.Compiler.Service line numbers are 1-based + let fcsTextLineNumber = Line.fromZ textLinePos.Line // Roslyn line numbers are zero-based, FSharp.Compiler.Service line numbers are 1-based return parseResults.ValidateBreakpointLocation(mkPos fcsTextLineNumber textLineColumn) } diff --git a/vsintegration/src/FSharp.Editor/ColorizationService.fs b/vsintegration/src/FSharp.Editor/ColorizationService.fs index 82506d4ef2..9060eb0b24 100644 --- a/vsintegration/src/FSharp.Editor/ColorizationService.fs +++ b/vsintegration/src/FSharp.Editor/ColorizationService.fs @@ -50,13 +50,12 @@ type internal FSharpColorizationService() = match FSharpLanguageService.GetOptions(document.Project.Id) with | Some(options) -> let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask - let! parseResults = FSharpLanguageService.Checker.ParseFileInProject(document.Name, sourceText.ToString(), options) let! textVersion = document.GetTextVersionAsync(cancellationToken) |> Async.AwaitTask - let! checkResultsAnswer = FSharpLanguageService.Checker.CheckFileInProject(parseResults, document.FilePath, textVersion.GetHashCode(), textSpan.ToString(), options) + let! _parseResults, checkResultsAnswer = FSharpLanguageService.Checker.ParseAndCheckFileInProject(document.FilePath, textVersion.GetHashCode(), textSpan.ToString(), options) let extraColorizationData = match checkResultsAnswer with - | FSharpCheckFileAnswer.Aborted -> failwith "Compilation isn't complete yet" + | FSharpCheckFileAnswer.Aborted -> [| |] | FSharpCheckFileAnswer.Succeeded(results) -> results.GetExtraColorizationsAlternate() |> Seq.map(fun (range, tokenColorKind) -> ClassifiedSpan(CommonRoslynHelpers.FSharpRangeToTextSpan(sourceText, range), diff --git a/vsintegration/src/FSharp.Editor/CommonHelpers.fs b/vsintegration/src/FSharp.Editor/CommonHelpers.fs index 8ebd6a41fb..c177ff13e4 100644 --- a/vsintegration/src/FSharp.Editor/CommonHelpers.fs +++ b/vsintegration/src/FSharp.Editor/CommonHelpers.fs @@ -155,7 +155,7 @@ module CommonHelpers = result with ex -> Assert.Exception(ex) - reraise() + List() let tryClassifyAtPosition (documentKey, sourceText: SourceText, filePath, defines, position: int, cancellationToken) = let textLine = sourceText.Lines.GetLineFromPosition(position) diff --git a/vsintegration/src/FSharp.Editor/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/CompletionProvider.fs index 6d5c547a49..819eec560b 100644 --- a/vsintegration/src/FSharp.Editor/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/CompletionProvider.fs @@ -19,6 +19,7 @@ open Microsoft.CodeAnalysis.Editor open Microsoft.CodeAnalysis.Editor.Implementation.Debugging open Microsoft.CodeAnalysis.Editor.Shared.Utilities open Microsoft.CodeAnalysis.Formatting +open Microsoft.CodeAnalysis.Host open Microsoft.CodeAnalysis.Host.Mef open Microsoft.CodeAnalysis.Options open Microsoft.CodeAnalysis.Text @@ -54,17 +55,18 @@ type internal FSharpCompletionProvider(workspace: Workspace, serviceProvider: SV // Skip if we are not on a completion trigger else - let c = sourceText.[caretPosition - 1] + let triggerPosition = caretPosition - 1 + let c = sourceText.[triggerPosition] if not (completionTriggers |> Array.contains c) then false // Trigger completion if we are on a valid classification type else let documentId, filePath, defines = getInfo() - let triggerPosition = caretPosition - 1 - let textLine = sourceText.Lines.GetLineFromPosition(triggerPosition) + let textLines = sourceText.Lines + let triggerLine = textLines.GetLineFromPosition(triggerPosition) let classifiedSpanOption = - CommonHelpers.getColorizationData(documentId, sourceText, textLine.Span, Some(filePath), defines, CancellationToken.None) + CommonHelpers.getColorizationData(documentId, sourceText, triggerLine.Span, Some(filePath), defines, CancellationToken.None) |> Seq.tryFind(fun classifiedSpan -> classifiedSpan.TextSpan.Contains(triggerPosition)) match classifiedSpanOption with @@ -77,20 +79,19 @@ type internal FSharpCompletionProvider(workspace: Workspace, serviceProvider: SV | _ -> true // anything else is a valid classification type static member ProvideCompletionsAsyncAux(sourceText: SourceText, caretPosition: int, options: FSharpProjectOptions, filePath: string, textVersionHash: int) = async { - let! parseResults = FSharpLanguageService.Checker.ParseFileInProject(filePath, sourceText.ToString(), options) - let! checkFileAnswer = FSharpLanguageService.Checker.CheckFileInProject(parseResults, filePath, textVersionHash, sourceText.ToString(), options) - let checkFileResults = - match checkFileAnswer with - | FSharpCheckFileAnswer.Aborted -> failwith "Compilation isn't complete yet or was cancelled" - | FSharpCheckFileAnswer.Succeeded(results) -> results + let! parseResults, checkFileAnswer = FSharpLanguageService.Checker.ParseAndCheckFileInProject(filePath, textVersionHash, sourceText.ToString(), options) + match checkFileAnswer with + | FSharpCheckFileAnswer.Aborted -> return List() + | FSharpCheckFileAnswer.Succeeded(checkFileResults) -> - let textLine = sourceText.Lines.GetLineFromPosition(caretPosition) - let textLinePos = sourceText.Lines.GetLinePosition(caretPosition) - let fcsTextLineNumber = textLinePos.Line + 1 // Roslyn line numbers are zero-based, FSharp.Compiler.Service line numbers are 1-based - let textLineColumn = textLinePos.Character + let textLines = sourceText.Lines + let caretLine = textLines.GetLineFromPosition(caretPosition) + let caretLinePos = textLines.GetLinePosition(caretPosition) + let fcsCaretLineNumber = Line.fromZ caretLinePos.Line // Roslyn line numbers are zero-based, FSharp.Compiler.Service line numbers are 1-based + let caretLineColumn = caretLinePos.Character - let qualifyingNames, partialName = QuickParse.GetPartialLongNameEx(textLine.ToString(), textLineColumn - 1) - let! declarations = checkFileResults.GetDeclarationListInfo(Some(parseResults), fcsTextLineNumber, textLineColumn, textLine.ToString(), qualifyingNames, partialName) + let qualifyingNames, partialName = QuickParse.GetPartialLongNameEx(caretLine.ToString(), caretLineColumn - 1) + let! declarations = checkFileResults.GetDeclarationListInfo(Some(parseResults), fcsCaretLineNumber, caretLineColumn, caretLine.ToString(), qualifyingNames, partialName) let results = List() @@ -166,3 +167,24 @@ type internal FSharpCompletionProvider(workspace: Workspace, serviceProvider: SV else return CompletionDescription.Empty } |> CommonRoslynHelpers.StartAsyncAsTask cancellationToken + +type internal FSharpCompletionService(workspace: Workspace, serviceProvider: SVsServiceProvider) = + inherit CompletionServiceWithProviders(workspace) + + let builtInProviders = ImmutableArray.Create(FSharpCompletionProvider(workspace, serviceProvider)) + let completionRules = CompletionRules.Default.WithDismissIfEmpty(true).WithDismissIfLastCharacterDeleted(true).WithDefaultEnterKeyRule(EnterKeyRule.Never) + + override this.Language = FSharpCommonConstants.FSharpLanguageName + override this.GetBuiltInProviders() = builtInProviders + override this.GetRules() = completionRules + + + +[] +[, FSharpCommonConstants.FSharpLanguageName)>] +type internal FSharpCompletionServiceFactory [] (serviceProvider: SVsServiceProvider) = + interface ILanguageServiceFactory with + member this.CreateLanguageService(hostLanguageServices: HostLanguageServices) : ILanguageService = + upcast new FSharpCompletionService(hostLanguageServices.WorkspaceServices.Workspace, serviceProvider) + + diff --git a/vsintegration/src/FSharp.Editor/CompletionService.fs b/vsintegration/src/FSharp.Editor/CompletionService.fs deleted file mode 100644 index de2406cd1a..0000000000 --- a/vsintegration/src/FSharp.Editor/CompletionService.fs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.VisualStudio.FSharp.Editor - -open System -open System.Composition -open System.Collections.Concurrent -open System.Collections.Generic -open System.Collections.Immutable -open System.Threading -open System.Threading.Tasks -open System.Linq - -open Microsoft.CodeAnalysis -open Microsoft.CodeAnalysis.Completion -open Microsoft.CodeAnalysis.Editor -open Microsoft.CodeAnalysis.Editor.Implementation.Debugging -open Microsoft.CodeAnalysis.Editor.Shared.Utilities -open Microsoft.CodeAnalysis.Formatting -open Microsoft.CodeAnalysis.Host -open Microsoft.CodeAnalysis.Host.Mef -open Microsoft.CodeAnalysis.Text - -open Microsoft.VisualStudio.FSharp.LanguageService -open Microsoft.VisualStudio.Text -open Microsoft.VisualStudio.Text.Tagging -open Microsoft.VisualStudio.Shell - -open Microsoft.FSharp.Compiler.Parser -open Microsoft.FSharp.Compiler.SourceCodeServices -open Microsoft.FSharp.Compiler.Range - -type internal FSharpCompletionService(workspace: Workspace, serviceProvider: SVsServiceProvider) = - inherit CompletionServiceWithProviders(workspace) - - let builtInProviders = ImmutableArray.Create(FSharpCompletionProvider(workspace, serviceProvider)) - let completionRules = CompletionRules.Default.WithDismissIfEmpty(true).WithDismissIfLastCharacterDeleted(true).WithDefaultEnterKeyRule(EnterKeyRule.Never) - - override this.Language = FSharpCommonConstants.FSharpLanguageName - override this.GetBuiltInProviders() = builtInProviders - override this.GetRules() = completionRules diff --git a/vsintegration/src/FSharp.Editor/CompletionServiceFactory.fs b/vsintegration/src/FSharp.Editor/CompletionServiceFactory.fs deleted file mode 100644 index f6bffa2ece..0000000000 --- a/vsintegration/src/FSharp.Editor/CompletionServiceFactory.fs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.VisualStudio.FSharp.Editor - -open System -open System.Composition -open System.Collections.Concurrent -open System.Collections.Generic -open System.Collections.Immutable -open System.Threading -open System.Threading.Tasks -open System.Linq - -open Microsoft.CodeAnalysis -open Microsoft.CodeAnalysis.Completion -open Microsoft.CodeAnalysis.Editor -open Microsoft.CodeAnalysis.Editor.Implementation.Debugging -open Microsoft.CodeAnalysis.Editor.Shared.Utilities -open Microsoft.CodeAnalysis.Formatting -open Microsoft.CodeAnalysis.Host -open Microsoft.CodeAnalysis.Host.Mef -open Microsoft.CodeAnalysis.Text - -open Microsoft.VisualStudio.FSharp.LanguageService -open Microsoft.VisualStudio.Text -open Microsoft.VisualStudio.Text.Tagging -open Microsoft.VisualStudio.Shell - -open Microsoft.FSharp.Compiler.Parser -open Microsoft.FSharp.Compiler.SourceCodeServices -open Microsoft.FSharp.Compiler.Range - -[] -[, FSharpCommonConstants.FSharpLanguageName)>] -type internal FSharpCompletionServiceFactory [] (serviceProvider: SVsServiceProvider) = - interface ILanguageServiceFactory with - member this.CreateLanguageService(hostLanguageServices: HostLanguageServices) : ILanguageService = - upcast new FSharpCompletionService(hostLanguageServices.WorkspaceServices.Workspace, serviceProvider) diff --git a/vsintegration/src/FSharp.Editor/DocumentDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/DocumentDiagnosticAnalyzer.fs index 1499050150..3de82d3f62 100644 --- a/vsintegration/src/FSharp.Editor/DocumentDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/DocumentDiagnosticAnalyzer.fs @@ -30,7 +30,7 @@ type internal FSharpDocumentDiagnosticAnalyzer() = if addSemanticErrors then let! checkResultsAnswer = FSharpLanguageService.Checker.CheckFileInProject(parseResults, filePath, textVersionHash, sourceText.ToString(), options) match checkResultsAnswer with - | FSharpCheckFileAnswer.Aborted -> return! failwith "Compilation isn't complete yet" + | FSharpCheckFileAnswer.Aborted -> return [| |] | FSharpCheckFileAnswer.Succeeded(results) -> return results.Errors else return parseResults.Errors diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index 9d206f94cb..b4b3cb163a 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -57,11 +57,8 @@ Completion\CompletionProvider.fs - - Completion\CompletionService.fs - - - Completion\CompletionServiceFactory.fs + + Completion\SignatureHelp.fs GoToDefinition\GoToDefinitionService.fs diff --git a/vsintegration/src/FSharp.Editor/GoToDefinitionService.fs b/vsintegration/src/FSharp.Editor/GoToDefinitionService.fs index 0006b179ea..d6611c0445 100644 --- a/vsintegration/src/FSharp.Editor/GoToDefinitionService.fs +++ b/vsintegration/src/FSharp.Editor/GoToDefinitionService.fs @@ -35,7 +35,7 @@ type internal FSharpNavigableItem(document: Document, textSpan: TextSpan) = member this.IsImplicitlyDeclared = false member this.Document = document member this.SourceSpan = textSpan - member this.DisplayTaggedParts = Unchecked.defaultof> + member this.DisplayTaggedParts = ImmutableArray.Empty member this.ChildItems = ImmutableArray.Empty [] @@ -48,28 +48,28 @@ type internal FSharpGoToDefinitionService [] ([ 0 -> tryClassifyAtPosition (position - 1) - | res -> res - - match quickParseInfo with - | Some (islandColumn, qualifiers, _) -> - let! parseResults = FSharpLanguageService.Checker.ParseFileInProject(filePath, sourceText.ToString(), options) - let! checkFileAnswer = FSharpLanguageService.Checker.CheckFileInProject(parseResults, filePath, textVersionHash, sourceText.ToString(), options) - let checkFileResults = + let tryGotoAtPosition position = + async { + match CommonHelpers.tryClassifyAtPosition(documentKey, sourceText, filePath, defines, position, cancellationToken) with + | Some (islandColumn, qualifiers, _) -> + let! _parseResults, checkFileAnswer = FSharpLanguageService.Checker.ParseAndCheckFileInProject(filePath, textVersionHash, sourceText.ToString(), options) match checkFileAnswer with - | FSharpCheckFileAnswer.Aborted -> failwith "Compilation isn't complete yet" - | FSharpCheckFileAnswer.Succeeded(results) -> results + | FSharpCheckFileAnswer.Aborted -> return None + | FSharpCheckFileAnswer.Succeeded(checkFileResults) -> - let! declarations = checkFileResults.GetDeclarationLocationAlternate (fcsTextLineNumber, islandColumn, textLine.ToString(), qualifiers, false) + let! declarations = checkFileResults.GetDeclarationLocationAlternate (fcsTextLineNumber, islandColumn, textLine.ToString(), qualifiers, false) - match declarations with - | FSharpFindDeclResult.DeclFound(range) -> return Some(range) - | _ -> return None - | None -> return None + match declarations with + | FSharpFindDeclResult.DeclFound(range) -> return Some(range) + | _ -> return None + | None -> return None + } + + // Tolerate being on the right of the identifier + let! attempt1 = tryGotoAtPosition position + match attempt1 with + | None when textLineColumn > 0 -> return! tryGotoAtPosition (position - 1) + | res -> return res } // FSROSLYNTODO: Since we are not integrated with the Roslyn project system yet, the below call diff --git a/vsintegration/src/FSharp.Editor/ProjectDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/ProjectDiagnosticAnalyzer.fs index 493ed4fc7f..8fe4f65157 100644 --- a/vsintegration/src/FSharp.Editor/ProjectDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/ProjectDiagnosticAnalyzer.fs @@ -21,6 +21,11 @@ open Microsoft.FSharp.Compiler.Range open Microsoft.VisualStudio.FSharp.LanguageService +#if PROJECT_ANALYSIS +// Project-wide error analysis. We don't enable this because ParseAndCheckProject checks projects against the versions of the files +// saves to the file system. This is different to the versions of the files active in the editor. This results in out-of-sync error +// messages while files are being edited + [] type internal FSharpProjectDiagnosticAnalyzer() = inherit ProjectDiagnosticAnalyzer() @@ -38,7 +43,7 @@ type internal FSharpProjectDiagnosticAnalyzer() = return results } - override this.SupportedDiagnostics with get() = CommonRoslynHelpers.SupportedDiagnostics() + override this.SupportedDiagnostics = CommonRoslynHelpers.SupportedDiagnostics() override this.AnalyzeProjectAsync(project: Project, cancellationToken: CancellationToken): Task> = async { @@ -46,3 +51,4 @@ type internal FSharpProjectDiagnosticAnalyzer() = | Some(options) -> return! FSharpProjectDiagnosticAnalyzer.GetDiagnostics(options) | None -> return ImmutableArray.Empty } |> CommonRoslynHelpers.StartAsyncAsTask cancellationToken +#endif diff --git a/vsintegration/src/FSharp.Editor/SignatureHelp.fs b/vsintegration/src/FSharp.Editor/SignatureHelp.fs new file mode 100644 index 0000000000..0fb66c3f76 --- /dev/null +++ b/vsintegration/src/FSharp.Editor/SignatureHelp.fs @@ -0,0 +1,220 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.VisualStudio.FSharp.Editor + +open System +open System.Composition +open System.Collections.Concurrent +open System.Collections.Generic +open System.Collections.Immutable +open System.Threading +open System.Threading.Tasks +open System.Runtime.CompilerServices + +open Microsoft.CodeAnalysis +open Microsoft.CodeAnalysis.Classification +open Microsoft.CodeAnalysis.Editor +open Microsoft.CodeAnalysis.Editor.Implementation.Debugging +open Microsoft.CodeAnalysis.Editor.Shared.Utilities +open Microsoft.CodeAnalysis.Formatting +open Microsoft.CodeAnalysis.Host +open Microsoft.CodeAnalysis.Host.Mef +open Microsoft.CodeAnalysis.Options +open Microsoft.CodeAnalysis.SignatureHelp +open Microsoft.CodeAnalysis.Text + +open Microsoft.VisualStudio.FSharp.LanguageService +open Microsoft.VisualStudio.Text +open Microsoft.VisualStudio.Text.Tagging +open Microsoft.VisualStudio.Shell +open Microsoft.VisualStudio.Shell.Interop + +open Microsoft.FSharp.Compiler.Parser +open Microsoft.FSharp.Compiler.Range +open Microsoft.FSharp.Compiler.SourceCodeServices +open Microsoft.FSharp.Compiler.SourceCodeServices.ItemDescriptionIcons + +[] +[] +type FSharpSignatureHelpProvider [] (serviceProvider: SVsServiceProvider) = + + let xmlMemberIndexService = serviceProvider.GetService(typeof) :?> IVsXMLMemberIndexService + let documentationBuilder = XmlDocumentation.CreateDocumentationBuilder(xmlMemberIndexService, serviceProvider.DTE) + + static let oneColAfter (lp: LinePosition) = LinePosition(lp.Line,lp.Character+1) + static let oneColBefore (lp: LinePosition) = LinePosition(lp.Line,max 0 (lp.Character-1)) + + // Unit-testable core rutine + static member internal ProvideMethodsAsyncAux(documentationBuilder: IDocumentationBuilder, sourceText: SourceText, caretPosition: int, options: FSharpProjectOptions, triggerIsTypedChar: char option, filePath: string, textVersionHash: int) = async { + let! parseResults, checkFileAnswer = FSharpLanguageService.Checker.ParseAndCheckFileInProject(filePath, textVersionHash, sourceText.ToString(), options) + match checkFileAnswer with + | FSharpCheckFileAnswer.Aborted -> return None + | FSharpCheckFileAnswer.Succeeded(checkFileResults) -> + + let textLines = sourceText.Lines + let caretLinePos = textLines.GetLinePosition(caretPosition) + let caretLineColumn = caretLinePos.Character + + // Get the parameter locations + let paramLocations = parseResults.FindNoteworthyParamInfoLocations(Pos.fromZ caretLinePos.Line caretLineColumn) + + match paramLocations with + | None -> return None // no locations = no help + | Some nwpl -> + let names = nwpl.LongId + let lidEnd = nwpl.LongIdEndLocation + + // Get the methods + let! methodGroup = checkFileResults.GetMethodsAlternate(lidEnd.Line, lidEnd.Column, "", Some names) + + let methods = methodGroup.Methods + + if (methods.Length = 0 || methodGroup.MethodName.EndsWith("> )")) then return None else + + let isStaticArgTip = + let parenLine, parenCol = Pos.toZ nwpl.OpenParenLocation + assert (parenLine < textLines.Count) + let parenLineText = textLines.[parenLine].ToString() + parenCol < parenLineText.Length && parenLineText.[parenCol] = '<' + + let filteredMethods = + [| for m in methods do + if (isStaticArgTip && m.StaticParameters.Length > 0) || + (not isStaticArgTip && m.HasParameters) then // need to distinguish TP<...>(...) angle brackets tip from parens tip + yield m |] + + if filteredMethods.Length = 0 then return None else + + let posToLinePosition pos = + let (l,c) = Pos.toZ pos + // FSROSLYNTODO: FCS gives back line counts that are too large. Really, this shouldn't happen + let result =LinePosition(l,c) + let lastPosInDocument = textLines.GetLinePosition(textLines.[textLines.Count-1].End) + if lastPosInDocument.CompareTo(result) > 0 then result else lastPosInDocument + + // Compute the start position + let startPos = nwpl.LongIdStartLocation |> posToLinePosition + + // Compute the end position + let endPos = + let last = nwpl.TupleEndLocations.[nwpl.TupleEndLocations.Length-1] |> posToLinePosition + (if nwpl.IsThereACloseParen then oneColBefore last else last) + + assert (startPos.CompareTo(endPos) <= 0) + + // Compute the applicable span between the parentheses + let applicableSpan = + textLines.GetTextSpan(LinePositionSpan(startPos, endPos)) + + let startOfArgs = nwpl.OpenParenLocation |> posToLinePosition |> oneColAfter + + let tupleEnds = + [| yield startOfArgs + for i in 0..nwpl.TupleEndLocations.Length-2 do + yield nwpl.TupleEndLocations.[i] |> posToLinePosition + yield endPos |] + + // If we are pressing "(" or "<" or ",", then only pop up the info if this is one of the actual, real detected positions in the detected promptable call + // + // For example the last "(" in + // List.map (fun a -> ( + // should not result in a prompt. + // + // Likewise the last "," in + // Console.WriteLine( [(1, + // should not result in a prompt, whereas this one will: + // Console.WriteLine( [(1,2)], + + match triggerIsTypedChar with + | Some ('<' | '(' | ',') when not (tupleEnds |> Array.exists (fun lp -> lp.Character = caretLineColumn)) -> + return None // comma or paren at wrong location = remove help display + | _ -> + + // Compute the argument index by working out where the caret is between the various commas + let argumentIndex = + tupleEnds + |> Array.pairwise + |> Array.tryFindIndex (fun (lp1,lp2) -> textLines.GetTextSpan(LinePositionSpan(lp1, lp2)).Contains(caretPosition)) + |> (function None -> 0 | Some n -> n) + + // Compute the overall argument count + let argumentCount = + match nwpl.TupleEndLocations.Length with + | 1 when caretLinePos.Character = startOfArgs.Character -> 0 // count "WriteLine(" as zero arguments + | n -> n + + // Compute the current argument name, if any + let argumentName = + if argumentIndex < nwpl.NamedParamNames.Length then + nwpl.NamedParamNames.[argumentIndex] + else + None // not a named argument + + // Prepare the results + let results = List<_>() + + for method in methods do + // Create the documentation. Note, do this on the background thread, since doing it in the documentationBuild fails to build the XML index + let methodDocs = XmlDocumentation.BuildMethodOverloadTipText(documentationBuilder, method.Description, true) + + let parameters = + let parameters = if isStaticArgTip then method.StaticParameters else method.Parameters + [| for p in parameters do + // FSROSLYNTODO: compute the proper help text for parameters, c.f. AppendParameter in XmlDocumentation.fs + let paramDoc = XmlDocumentation.BuildMethodParamText(documentationBuilder, method.XmlDoc, p.ParameterName) + let doc = [| TaggedText(TextTags.Text, paramDoc); |] + yield (p.ParameterName,p.IsOptional,doc,[| TaggedText(TextTags.Text,p.Display) |]) |] + + let doc = [| TaggedText(TextTags.Text, methodDocs + "\n") |] + + // Prepare the text to display + let descriptionParts = [| TaggedText(TextTags.Text, method.TypeText) |] + let prefixParts = [| TaggedText(TextTags.Text, methodGroup.MethodName); TaggedText(TextTags.Punctuation, (if isStaticArgTip then "<" else "(")) |] + let separatorParts = [| TaggedText(TextTags.Punctuation, ", ") |] + let suffixParts = [| TaggedText(TextTags.Text, (if isStaticArgTip then ">" else ")")) |] + let completionItem = (method.HasParamArrayArg ,doc,prefixParts,separatorParts,suffixParts,parameters,descriptionParts) + // FSROSLYNTODO: Do we need a cache like for completion? + //declarationItemsCache.Remove(completionItem.DisplayText) |> ignore // clear out stale entries if they exist + //declarationItemsCache.Add(completionItem.DisplayText, declarationItem) + results.Add(completionItem) + + + let items = (results.ToArray(),applicableSpan,argumentIndex,argumentCount,argumentName) + return Some items + } + + interface ISignatureHelpProvider with + member this.IsTriggerCharacter(c) = c ='(' || c = '<' || c = ',' + member this.IsRetriggerCharacter(c) = c = ')' || c = '>' || c = '=' + + member this.GetItemsAsync(document, position, triggerInfo, cancellationToken) = + async { + try + match FSharpLanguageService.GetOptions(document.Project.Id) with + | Some(options) -> + let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask + let! textVersion = document.GetTextVersionAsync(cancellationToken) |> Async.AwaitTask + + let triggerTypedChar = + if triggerInfo.TriggerCharacter.HasValue && triggerInfo.TriggerReason = SignatureHelpTriggerReason.TypeCharCommand then + Some triggerInfo.TriggerCharacter.Value + else None + + let! methods = FSharpSignatureHelpProvider.ProvideMethodsAsyncAux(documentationBuilder, sourceText, position, options, triggerTypedChar, document.FilePath, textVersion.GetHashCode()) + match methods with + | None -> return null + | Some (results,applicableSpan,argumentIndex,argumentCount,argumentName) -> + + let items = + results |> Array.map (fun (hasParamArrayArg,doc,prefixParts,separatorParts,suffixParts,parameters,descriptionParts) -> + let parameters = parameters |> Array.map (fun (paramName, isOptional, paramDoc, displayParts) -> SignatureHelpParameter(paramName,isOptional,documentationFactory=(fun _ -> paramDoc :> seq<_>),displayParts=displayParts)) + SignatureHelpItem(isVariadic=hasParamArrayArg ,documentationFactory=(fun _ -> doc :> seq<_>),prefixParts=prefixParts,separatorParts=separatorParts,suffixParts=suffixParts,parameters=parameters,descriptionParts=descriptionParts)) + + return SignatureHelpItems(items,applicableSpan,argumentIndex,argumentCount,Option.toObj argumentName) + | None -> + return null + with ex -> + Assert.Exception(ex) + return null + } |> CommonRoslynHelpers.StartAsyncAsTask cancellationToken + diff --git a/vsintegration/src/FSharp.LanguageService/Intellisense.fs b/vsintegration/src/FSharp.LanguageService/Intellisense.fs index 87c6b4ffc3..a8d5d99d2b 100644 --- a/vsintegration/src/FSharp.LanguageService/Intellisense.fs +++ b/vsintegration/src/FSharp.LanguageService/Intellisense.fs @@ -56,13 +56,13 @@ type internal FSharpMethodListForAMethodTip(documentationBuilder: IDocumentation override x.GetNoteworthyParamInfoLocations() = tupleEnds - override x.GetParameterNames() = nwpl.NamedParamNames + override x.GetParameterNames() = nwpl.NamedParamNames |> Array.map Option.toObj override x.GetParameterRanges() = parameterRanges override x.GetCount() = methods.Length - override x.GetDescription(methodIndex) = safe methodIndex "" (fun m -> XmlDocumentation.BuildMethodOverloadTipText(documentationBuilder, m.Description)) + override x.GetDescription(methodIndex) = safe methodIndex "" (fun m -> XmlDocumentation.BuildMethodOverloadTipText(documentationBuilder, m.Description, true)) override x.GetType(methodIndex) = safe methodIndex "" (fun m -> m.TypeText) diff --git a/vsintegration/src/FSharp.LanguageService/XmlDocumentation.fs b/vsintegration/src/FSharp.LanguageService/XmlDocumentation.fs index 0bb9e24e48..9b3f8ccff5 100644 --- a/vsintegration/src/FSharp.LanguageService/XmlDocumentation.fs +++ b/vsintegration/src/FSharp.LanguageService/XmlDocumentation.fs @@ -158,18 +158,7 @@ module internal XmlDocumentation = interface IDocumentationBuilder with /// Append the given processed XML formatted into the string builder - override this.AppendDocumentationFromProcessedXML - ( /// StringBuilder to append to - appendTo:StringBuilder, - /// The processed XML text. - processedXml:string, - /// Whether to show exceptions - showExceptions:bool, - /// Whether to show parameters and return - showParameters:bool, - /// Name of parameter - paramName:string option - ) = + override this.AppendDocumentationFromProcessedXML(appendTo, processedXml, showExceptions, showParameters, paramName) = let ok,xml = xmlIndexService.GetMemberDataFromXML(processedXml) if Com.Succeeded(ok) then if paramName.IsSome then @@ -285,7 +274,14 @@ module internal XmlDocumentation = let BuildDataTipText(documentationProvider, FSharpToolTipText(dataTipText)) = BuildTipText(documentationProvider,dataTipText,true, true, false, true) - let BuildMethodOverloadTipText(documentationProvider, FSharpToolTipText(dataTipText)) = - BuildTipText(documentationProvider,dataTipText,false, false, true, false) + let BuildMethodOverloadTipText(documentationProvider, FSharpToolTipText(dataTipText), showParams) = + BuildTipText(documentationProvider,dataTipText,false, false, showParams, false) + + let BuildMethodParamText(documentationProvider, xml, paramName) = + let sb = StringBuilder() + AppendXmlComment(documentationProvider, sb, xml, false, true, Some paramName) + sb.ToString() - let CreateDocumentationBuilder(xmlIndexService, dte) = Provider(xmlIndexService, dte) :> IDocumentationBuilder \ No newline at end of file + let documentationBuilderCache = System.Runtime.CompilerServices.ConditionalWeakTable() + let CreateDocumentationBuilder(xmlIndexService: IVsXMLMemberIndexService, dte: DTE) = + documentationBuilderCache.GetValue(xmlIndexService,(fun _ -> Provider(xmlIndexService, dte) :> IDocumentationBuilder)) \ No newline at end of file diff --git a/vsintegration/tests/unittests/CompletionProviderTests.fs b/vsintegration/tests/unittests/CompletionProviderTests.fs index 88dce96d2c..b80a738f7c 100644 --- a/vsintegration/tests/unittests/CompletionProviderTests.fs +++ b/vsintegration/tests/unittests/CompletionProviderTests.fs @@ -1,5 +1,25 @@ + +// To run the tests in this file: +// +// Technique 1: Compile VisualFSharp.Unittests.dll and run it as a set of unit tests +// +// Technique 2: +// +// Enable some tests in the #if EXE section at the end of the file, +// then compile this file as an EXE that has InternalsVisibleTo access into the +// appropriate DLLs. This can be the quickest way to get turnaround on updating the tests +// and capturing large amounts of structured output. +(* + cd Debug\net40\bin + .\fsc.exe --define:EXE -r:.\Microsoft.Build.Utilities.Core.dll -o VisualFSharp.Unittests.exe -g --optimize- -r .\FSharp.LanguageService.Compiler.dll -r .\FSharp.Editor.dll -r nunit.framework.dll ..\..\..\tests\service\FsUnit.fs ..\..\..\tests\service\Common.fs /delaysign /keyfile:..\..\..\src\fsharp\msft.pubkey ..\..\..\vsintegration\tests\unittests\CompletionProviderTests.fs + .\VisualFSharp.Unittests.exe +*) +// Technique 3: +// +// Use F# Interactive. This only works for FSharp.Compiler.Service.dll which has a public API + // Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -namespace Microsoft.VisualStudio.FSharp.Editor.Tests.Roslyn +module Microsoft.VisualStudio.FSharp.Editor.Tests.Roslyn.CompletionProviderTests open System open System.Threading @@ -19,103 +39,105 @@ open Microsoft.VisualStudio.FSharp.LanguageService open Microsoft.FSharp.Compiler.SourceCodeServices open Microsoft.FSharp.Compiler.Range -[] -type CompletionProviderTests() = - let filePath = "C:\\test.fs" - let options: FSharpProjectOptions = { - ProjectFileName = "C:\\test.fsproj" - ProjectFileNames = [| filePath |] - ReferencedProjects = [| |] - OtherOptions = [| |] - IsIncompleteTypeCheckEnvironment = true - UseScriptResolutionRules = false - LoadTime = DateTime.MaxValue - UnresolvedReferences = None - } - - member private this.VerifyCompletionList(fileContents: string, marker: string, expected: string list, unexpected: string list) = - let caretPosition = fileContents.IndexOf(marker) + marker.Length - let results = FSharpCompletionProvider.ProvideCompletionsAsyncAux(SourceText.From(fileContents), caretPosition, options, filePath, 0) |> - Async.RunSynchronously |> - Seq.map(fun result -> result.DisplayText) - - for item in expected do - Assert.IsTrue(results.Contains(item), "Completions should contain '{0}'. Got '{1}'.", item, String.Join(", ", results)) - - for item in unexpected do - Assert.IsFalse(results.Contains(item), "Completions should not contain '{0}'. Got '{1}'", item, String.Join(", ", results)) +let filePath = "C:\\test.fs" +let internal options = { + ProjectFileName = "C:\\test.fsproj" + ProjectFileNames = [| filePath |] + ReferencedProjects = [| |] + OtherOptions = [| |] + IsIncompleteTypeCheckEnvironment = true + UseScriptResolutionRules = false + LoadTime = DateTime.MaxValue + UnresolvedReferences = None +} + +let VerifyCompletionList(fileContents: string, marker: string, expected: string list, unexpected: string list) = + let caretPosition = fileContents.IndexOf(marker) + marker.Length + let results = + FSharpCompletionProvider.ProvideCompletionsAsyncAux(SourceText.From(fileContents), caretPosition, options, filePath, 0) + |> Async.RunSynchronously + |> Seq.map(fun result -> result.DisplayText) + + for item in expected do + Assert.IsTrue(results.Contains(item), "Completions should contain '{0}'. Got '{1}'.", item, String.Join(", ", results)) + + for item in unexpected do + Assert.IsFalse(results.Contains(item), "Completions should not contain '{0}'. Got '{1}'", item, String.Join(", ", results)) - [] - [] - [] - [] - [] - [] - [] - [] - member this.ShouldTriggerCompletionAtCorrectMarkers(marker: string, shouldBeTriggered: bool) = - let fileContents = """ +[] +let ShouldTriggerCompletionAtCorrectMarkers() = + let testCases = + [("x", false) + ("y", false) + ("1", false) + ("2", false) + ("x +", false) + ("Console.Write", false) + ("System.", true) + ("Console.", true) ] + + for (marker: string, shouldBeTriggered: bool) in testCases do + let fileContents = """ let x = 1 let y = 2 System.Console.WriteLine(x + y) """ - let caretPosition = fileContents.IndexOf(marker) + marker.Length - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let getInfo() = documentId, filePath, [] - let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo) - Assert.AreEqual(shouldBeTriggered, triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should compute the correct result") - - [] - [] - [] - member this.ShouldNotTriggerCompletionAfterAnyTriggerOtherThanInsertion(triggerKind: CompletionTriggerKind) = - let fileContents = "System.Console.WriteLine(123)" - let caretPosition = fileContents.IndexOf("System.") - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let getInfo() = documentId, filePath, [] - let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, triggerKind, getInfo) - Assert.IsFalse(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger") + let caretPosition = fileContents.IndexOf(marker) + marker.Length + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let getInfo() = documentId, filePath, [] + let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo) + Assert.AreEqual(shouldBeTriggered, triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should compute the correct result") + +[] +let ShouldNotTriggerCompletionAfterAnyTriggerOtherThanInsertion() = + for triggerKind in [CompletionTriggerKind.Deletion; CompletionTriggerKind.Other; CompletionTriggerKind.Snippets ] do + let fileContents = "System.Console.WriteLine(123)" + let caretPosition = fileContents.IndexOf("System.") + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let getInfo() = documentId, filePath, [] + let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, triggerKind, getInfo) + Assert.IsFalse(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger") - [] - member this.ShouldNotTriggerCompletionInStringLiterals() = - let fileContents = "let literal = \"System.Console.WriteLine()\"" - let caretPosition = fileContents.IndexOf("System.") - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let getInfo() = documentId, filePath, [] - let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo) - Assert.IsFalse(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger") +[] +let ShouldNotTriggerCompletionInStringLiterals() = + let fileContents = "let literal = \"System.Console.WriteLine()\"" + let caretPosition = fileContents.IndexOf("System.") + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let getInfo() = documentId, filePath, [] + let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo) + Assert.IsFalse(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger") - [] - member this.ShouldNotTriggerCompletionInComments() = - let fileContents = """ +[] +let ShouldNotTriggerCompletionInComments() = + let fileContents = """ (* This is a comment System.Console.WriteLine() *) """ - let caretPosition = fileContents.IndexOf("System.") - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let getInfo() = documentId, filePath, [] - let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo) - Assert.IsFalse(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger") + let caretPosition = fileContents.IndexOf("System.") + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let getInfo() = documentId, filePath, [] + let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo) + Assert.IsFalse(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger") - [] - member this.ShouldNotTriggerCompletionInExcludedCode() = - let fileContents = """ +[] +let ShouldNotTriggerCompletionInExcludedCode() = + let fileContents = """ #if UNDEFINED System.Console.WriteLine() #endif """ - let caretPosition = fileContents.IndexOf("System.") - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let getInfo() = documentId, filePath, [] - let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo) - Assert.IsFalse(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger") - - [] - member this.ShouldDisplayTypeMembers() = - let fileContents = """ + let caretPosition = fileContents.IndexOf("System.") + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let getInfo() = documentId, filePath, [] + let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo) + Assert.IsFalse(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger") + +[] +let ShouldDisplayTypeMembers() = + let fileContents = """ type T1() = member this.M1 = 5 member this.M2 = "literal" @@ -125,14 +147,19 @@ let main argv = let obj = T1() obj. """ - this.VerifyCompletionList(fileContents, "obj.", ["M1"; "M2"], ["System"]) + VerifyCompletionList(fileContents, "obj.", ["M1"; "M2"], ["System"]) - [] - member this.ShouldDisplaySystemNamespace() = - let fileContents = """ +[] +let ShouldDisplaySystemNamespace() = + let fileContents = """ type T1 = member this.M1 = 5 member this.M2 = "literal" System.Console.WriteLine() """ - this.VerifyCompletionList(fileContents, "System.", ["Console"; "Array"; "String"], ["T1"; "M1"; "M2"]) + VerifyCompletionList(fileContents, "System.", ["Console"; "Array"; "String"], ["T1"; "M1"; "M2"]) + +#if EXE + +ShouldDisplaySystemNamespace() +#endif diff --git a/vsintegration/tests/unittests/GoToDefinitionServiceTests.fs b/vsintegration/tests/unittests/GoToDefinitionServiceTests.fs index 4ae043eb81..be5e844440 100644 --- a/vsintegration/tests/unittests/GoToDefinitionServiceTests.fs +++ b/vsintegration/tests/unittests/GoToDefinitionServiceTests.fs @@ -1,4 +1,24 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// +// To run the tests in this file: +// +// Technique 1: Compile VisualFSharp.Unittests.dll and run it as a set of unit tests +// +// Technique 2: +// +// Enable some tests in the #if EXE section at the end of the file, +// then compile this file as an EXE that has InternalsVisibleTo access into the +// appropriate DLLs. This can be the quickest way to get turnaround on updating the tests +// and capturing large amounts of structured output. +(* + cd Debug\net40\bin + .\fsc.exe --define:EXE -r:.\Microsoft.Build.Utilities.Core.dll -o VisualFSharp.Unittests.exe -g --optimize- -r .\FSharp.LanguageService.Compiler.dll -r .\FSharp.Editor.dll -r nunit.framework.dll ..\..\..\tests\service\FsUnit.fs ..\..\..\tests\service\Common.fs /delaysign /keyfile:..\..\..\src\fsharp\msft.pubkey ..\..\..\vsintegration\tests\unittests\GoToDefinitionServiceTests.fs + .\VisualFSharp.Unittests.exe +*) +// Technique 3: +// +// Use F# Interactive. This only works for FSharp.Compiler.Service.dll which has a public API + namespace Microsoft.VisualStudio.FSharp.Editor.Tests.Roslyn open System @@ -21,16 +41,15 @@ open Microsoft.FSharp.Compiler.SourceCodeServices open Microsoft.FSharp.Compiler.Range [] -type GoToDefinitionServiceTests() = - - [] - [] - [] - [] - [] - [] - member this.VerifyDefinition(caretMarker: string, definitionLine: int, definitionStartColumn: int, definitionEndColumn: int) = - let fileContents = """ +module GoToDefinitionServiceTests = + + [] + let VerifyDefinition() = + + let manyTestCases = + [ +// Test1 + (""" type TestType() = member this.Member1(par1: int) = printf "%d" par1 @@ -41,8 +60,27 @@ type TestType() = let main argv = let obj = TestType() obj.Member1(5) - obj.Member2("test")""" + obj.Member2("test")""", + [ ("printf \"%d\" par1", Some(3, 3, 24, 28)); + ("printf \"%s\" par2", Some(5, 5, 24, 28)); + ("let obj = TestType", Some(2, 2, 5, 13)); + ("let obj", Some(10, 10, 8, 11)); + ("obj.Member1", Some(3, 3, 16, 23)); + ("obj.Member2", Some(5, 5, 16, 23)); ]); +// Test2 + (""" +module Module1 = + let foo x = x + +let _ = Module1.foo 1 +""", + [ ("let _ = Module", Some (2, 2, 7, 14)) ]) + ] + + for fileContents, testCases in manyTestCases do + for caretMarker, expected in testCases do + printfn "Test case: caretMarker=<<<%s>>>" caretMarker let filePath = Path.GetTempFileName() + ".fs" let options: FSharpProjectOptions = { ProjectFileName = "C:\\test.fsproj" @@ -59,12 +97,17 @@ let main argv = let caretPosition = fileContents.IndexOf(caretMarker) + caretMarker.Length - 1 // inside the marker let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let definitionOption = FSharpGoToDefinitionService.FindDefinition(documentId, SourceText.From(fileContents), filePath, caretPosition, [], options, 0, CancellationToken.None) |> Async.RunSynchronously - - match definitionOption with - | None -> Assert.Fail("No definition found") - | Some(range) -> - Assert.AreEqual(range.StartLine, range.EndLine, "Range must be on the same line") - Assert.AreEqual(definitionLine, range.StartLine, "Range line should match") - Assert.AreEqual(definitionStartColumn, range.StartColumn, "Range start column should match") - Assert.AreEqual(definitionEndColumn, range.EndColumn, "Range end column should match") + let actual = + FSharpGoToDefinitionService.FindDefinition(documentId, SourceText.From(fileContents), filePath, caretPosition, [], options, 0, CancellationToken.None) |> Async.RunSynchronously + |> Option.map (fun range -> (range.StartLine, range.EndLine, range.StartColumn, range.EndColumn)) + + if actual <> expected then + Assert.Fail(sprintf "Incorrect information returned for fileContents=<<<%s>>>, caretMarker=<<<%s>>>, expected =<<<%A>>>, actual = <<<%A>>>" fileContents caretMarker expected actual) + + + + + +#if EXE + VerifyDefinition() +#endif diff --git a/vsintegration/tests/unittests/ProjectDiagnosticAnalyzerTests.fs b/vsintegration/tests/unittests/ProjectDiagnosticAnalyzerTests.fs index 183ae2e778..11d18bb315 100644 --- a/vsintegration/tests/unittests/ProjectDiagnosticAnalyzerTests.fs +++ b/vsintegration/tests/unittests/ProjectDiagnosticAnalyzerTests.fs @@ -33,6 +33,7 @@ type ProjectDiagnosticAnalyzerTests() = let args = mkProjectCommandLineArgs (dllName, [fileName]) checker.GetProjectOptionsFromCommandLineArgs (projectName, args) +#if PROJECT_ANALYSIS [] member public this.ProjectDiagnosticsDontReportJustProjectErrors_Bug1596() = // https://github.com/Microsoft/visualfsharp/issues/1596 @@ -61,3 +62,4 @@ printf "%d" x let errors = FSharpProjectDiagnosticAnalyzer.GetDiagnostics(options) |> Async.RunSynchronously Assert.AreEqual(0, errors.Length, "No semantic errors should have been reported") +#endif diff --git a/vsintegration/tests/unittests/SignatureHelpProviderTests.fs b/vsintegration/tests/unittests/SignatureHelpProviderTests.fs new file mode 100644 index 0000000000..954926b5e5 --- /dev/null +++ b/vsintegration/tests/unittests/SignatureHelpProviderTests.fs @@ -0,0 +1,178 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// +// To run the tests in this file: +// +// Technique 1: Compile VisualFSharp.Unittests.dll and run it as a set of unit tests +// +// Technique 2: +// +// Enable some tests in the #if EXE section at the end of the file, +// then compile this file as an EXE that has InternalsVisibleTo access into the +// appropriate DLLs. This can be the quickest way to get turnaround on updating the tests +// and capturing large amounts of structured output. +(* + cd Debug\net40\bin + .\fsc.exe --define:EXE -r:.\Microsoft.Build.Utilities.Core.dll -o VisualFSharp.Unittests.exe -g --optimize- -r .\FSharp.LanguageService.Compiler.dll -r .\FSharp.Editor.dll -r nunit.framework.dll ..\..\..\tests\service\FsUnit.fs ..\..\..\tests\service\Common.fs /delaysign /keyfile:..\..\..\src\fsharp\msft.pubkey ..\..\..\vsintegration\tests\unittests\SignatureHelpProviderTests.fs + .\VisualFSharp.Unittests.exe +*) +// Technique 3: +// +// Use F# Interactive. This only works for FSharp.Compiler.Service.dll which has a public API + +module Microsoft.VisualStudio.FSharp.Editor.Tests.Roslyn.SignatureHelpProvider + +open System +open System.IO +open System.Threading +open System.Text + +open NUnit.Framework + +open Microsoft.CodeAnalysis +open Microsoft.CodeAnalysis.Classification +open Microsoft.CodeAnalysis.Editor +open Microsoft.CodeAnalysis.Editor.Implementation.Debugging +open Microsoft.CodeAnalysis.Editor.Shared.Utilities +open Microsoft.CodeAnalysis.Formatting +open Microsoft.CodeAnalysis.Host +open Microsoft.CodeAnalysis.Host.Mef +open Microsoft.CodeAnalysis.Options +open Microsoft.CodeAnalysis.SignatureHelp +open Microsoft.CodeAnalysis.Text + +open Microsoft.VisualStudio.FSharp.Editor +open Microsoft.VisualStudio.FSharp.LanguageService + +open Microsoft.FSharp.Compiler.SourceCodeServices +open Microsoft.FSharp.Compiler.Range + +let filePath = "C:\\test.fs" + +let PathRelativeToTestAssembly p = Path.Combine(Path.GetDirectoryName(Uri( System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath), p) + +let internal options = { + ProjectFileName = "C:\\test.fsproj" + ProjectFileNames = [| filePath |] + ReferencedProjects = [| |] + OtherOptions = [| "-r:" + PathRelativeToTestAssembly(@"UnitTestsResources\MockTypeProviders\DummyProviderForLanguageServiceTesting.dll") |] + IsIncompleteTypeCheckEnvironment = true + UseScriptResolutionRules = false + LoadTime = DateTime.MaxValue + UnresolvedReferences = None +} + +[] +let ShouldGiveSignatureHelpAtCorrectMarkers() = + let manyTestCases = + [ (""" +//1 +System.Console.WriteLine(1,arg1=2) + +""", + [(".", None); + ("System", None); + ("WriteLine", None); + ("(", Some ("[7..40)", 0, 2, None)); + (",", Some ("[7..40)", 1, 2, Some "arg1")); + ("arg", Some ("[7..40)", 1, 2, Some "arg1")); + ("arg1", Some ("[7..40)", 1, 2, Some "arg1")); + ("=", Some ("[7..40)", 1, 2, Some "arg1")); + ("2", Some ("[7..40)", 0, 2, None)); + (")", None)]); + ( """ +//2 +open System +Console.WriteLine([(1,2)]) +""", + [ + ("WriteLine(", Some ("[20..45)", 0, 0, None)); + (",", None); + ("[(", Some ("[20..45)", 0, 1, None)) + ]); + ( """ +//3 +type foo = N1.T< +type foo2 = N1.T +type foo2 = N1.T +type foo3 = N1.T +type foo4 = N1.T +type foo5 = N1.T +""", + [("type foo = N1.T<", Some ("[18..24)", 0, 0, None)); + ("type foo2 = N1.T<", Some ("[40..53)", 0, 0, Some "Param1")); + ("type foo2 = N1.T Async.RunSynchronously + FSharpLanguageService.Checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() + let actual = + match triggered with + | None -> None + | Some (results,applicableSpan,argumentIndex,argumentCount,argumentName) -> Some (applicableSpan.ToString(),argumentIndex,argumentCount,argumentName) + + if expected <> actual then Assert.Fail(sprintf "FSharpCompletionProvider.ProvideMethodsAsyncAux() gave unexpected results, expected %A, got %A" expected actual) + + yield (marker, actual) ] + () + // Use this to print out data to update the test cases, after uncommenting the assert + //printfn "(\"\"\"%s\n\"\"\",\n%s)" fileContents ((sprintf "%A" actual).Replace("null","None")) + + + + +#if EXE +ShouldGiveSignatureHelpAtCorrectMarkers() +#endif diff --git a/vsintegration/tests/unittests/VisualFSharp.Unittests.fsproj b/vsintegration/tests/unittests/VisualFSharp.Unittests.fsproj index fc3e055a30..066282814b 100644 --- a/vsintegration/tests/unittests/VisualFSharp.Unittests.fsproj +++ b/vsintegration/tests/unittests/VisualFSharp.Unittests.fsproj @@ -82,31 +82,34 @@ ProjectOptionsTests.fs - Roslyn\Classification\ColorizationServiceTests.fs + Roslyn\ColorizationServiceTests.fs - Roslyn\Utilities\BraceMatchingServiceTests.fs + Roslyn\BraceMatchingServiceTests.fs - Roslyn\Utilities\IndentationServiceTests.fs + Roslyn\IndentationServiceTests.fs - Roslyn\Debugging\BreakpointResolutionService.fs + Roslyn\BreakpointResolutionService.fs - Roslyn\Debugging\LanguageDebugInfoServiceTests.fs + Roslyn\LanguageDebugInfoServiceTests.fs - Roslyn\Diagnostics\DocumentDiagnosticAnalyzerTests.fs + Roslyn\DocumentDiagnosticAnalyzerTests.fs - Roslyn\Diagnostics\ProjectDiagnosticAnalyzerTests.fs + Roslyn\ProjectDiagnosticAnalyzerTests.fs - Roslyn\Completion\CompletionProviderTests.fs + Roslyn\CompletionProviderTests.fs + + + Roslyn\SignatureHelpProviderTests.fs - Roslyn\GoToDefinition\GoToDefinitionServiceTests.fs + Roslyn\GoToDefinitionServiceTests.fs Roslyn\QuickInfoProvider\QuickInfoProviderTests.fs