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