提交 b9d2f3fb 编写于 作者: V Vladimir Matveev 提交者: latkin

Intellisense completion for named parameters

closes #220

commit 4c59b117cf64e3b85c885f32b7fe50560b44f028
Author: Vladimir Matveev <vladima@microsoft.com>
Date:   Fri Feb 13 15:42:51 2015 -0800

    do not drop error arguments entirely, but ignore them then validating consistency of positional\named arguments

commit c3a65310c6ac619c233913d1072efc8753f6d4a9
Author: Vladimir Matveev <vladima@microsoft.com>
Date:   Wed Feb 11 11:24:52 2015 -0800

    added unit tests for completion of named arguments\settable properties

commit 27a2b6307cfe55db24e2f3a5a5ae3727d0c9d182
Author: Vladimir Matveev <vladima@microsoft.com>
Date:   Wed Feb 11 11:23:57 2015 -0800

    do not abort overload resolution if parameter list contains errors

commit 2851370637c67afd634d9a05b667b0b4172fc590
Author: Vladimir Matveev <vladima@microsoft.com>
Date:   Sat Feb 7 01:11:09 2015 -0800

    initial version of completion for named parameters
上级 49b20b00
......@@ -3018,11 +3018,16 @@ let GetMethodArgs arg =
| SynExpr.Const (SynConst.Unit,_) -> []
| SynExprParen(SynExpr.Tuple (args,_,_),_,_,_) | SynExpr.Tuple (args,_,_) -> args
| SynExprParen(arg,_,_,_) | arg -> [arg]
let unnamedCallerArgs,namedCallerArgs = List.takeUntil IsNamedArg args
let unnamedCallerArgs,namedCallerArgs =
args |> List.takeUntil IsNamedArg
let namedCallerArgs =
namedCallerArgs |> List.choose (fun e ->
if not (IsNamedArg e) then
error(Error(FSComp.SR.tcNameArgumentsMustAppearLast(), e.Range))
if not (IsNamedArg e) then
// ignore errors to avoid confusing error messages in cases like foo(a = 1,)
// do not abort overload resolution in case if named arguments are mixed with errors
match e with
| SynExpr.ArbitraryAfterError _ -> ()
| _ -> error(Error(FSComp.SR.tcNameArgumentsMustAppearLast(), e.Range))
TryGetNamedArg e)
unnamedCallerArgs, namedCallerArgs
......
......@@ -70,7 +70,9 @@ type internal CompletionContext =
// completing records field
| RecordField of RecordContext
| RangeOperator
| NewObject of pos * HashSet<string>
// completing named parameters\setters in parameter list of constructor\method calls
// end of name ast node * list of properties\parameters that were already set
| ParameterList of pos * HashSet<string>
//----------------------------------------------------------------------------
// Untyped scope
......@@ -772,7 +774,7 @@ module internal UntypedParseInfoImpl =
| Some m -> m.End
| None -> id.idRange.End
let (|NewObject|_|) e =
let (|NewObjectOrMethodCall|_|) e =
match e with
| (SynExpr.New (_, SynType.LongIdent typeName, arg, _)) ->
// new A()
......@@ -808,9 +810,9 @@ module internal UntypedParseInfoImpl =
let (|PartOfParameterList|_|) precedingArgument path =
match path with
| TS.Expr(SynExpr.Paren _)::TS.Expr(NewObject(args))::_ ->
| TS.Expr(SynExpr.Paren _)::TS.Expr(NewObjectOrMethodCall(args))::_ ->
if Option.isSome precedingArgument then None else Some args
| TS.Expr(SynExpr.Tuple (elements, commas, _))::TS.Expr(SynExpr.Paren _)::TS.Expr(NewObject(args))::_ ->
| TS.Expr(SynExpr.Tuple (elements, commas, _))::TS.Expr(SynExpr.Paren _)::TS.Expr(NewObjectOrMethodCall(args))::_ ->
match precedingArgument with
| None -> Some args
| Some e ->
......@@ -836,20 +838,26 @@ module internal UntypedParseInfoImpl =
// new A($)
| SynExpr.Const(SynConst.Unit, m) when rangeContainsPos m pos ->
match path with
| TS.Expr(NewObject args)::_ -> Some (CompletionContext.NewObject args)
| _ -> defaultTraverse expr
| TS.Expr(NewObjectOrMethodCall args)::_ ->
Some (CompletionContext.ParameterList args)
| _ ->
defaultTraverse expr
// new (... A$)
| SynExpr.Ident id when id.idRange.End = pos ->
match path with
| PartOfParameterList None args -> Some (CompletionContext.NewObject args)
| _ -> defaultTraverse expr
| PartOfParameterList None args ->
Some (CompletionContext.ParameterList args)
| _ ->
defaultTraverse expr
// new (A$ = 1)
// new (A = 1,$)
| Setter id when id.idRange.End = pos || rangeBeforePos expr.Range pos ->
let precedingArgument = if id.idRange.End = pos then None else Some expr
match path with
| PartOfParameterList precedingArgument args-> Some (CompletionContext.NewObject args)
| _ -> defaultTraverse expr
| PartOfParameterList precedingArgument args->
Some (CompletionContext.ParameterList args)
| _ ->
defaultTraverse expr
| _ -> defaultTraverse expr
member this.VisitRecordField(path, copyOpt, field) =
......
......@@ -67,9 +67,9 @@ type internal CompletionContext =
// completing records field
| RecordField of RecordContext
| RangeOperator
// completing property setters in constructor call
// end of constructor ast node * list of properties that were already set
| NewObject of pos * HashSet<string>
// completing named parameters\setters in parameter list of constructor\method calls
// end of name ast node * list of properties\parameters that were already set
| ParameterList of pos * HashSet<string>
// implementation details used by other code in the compiler
module internal UntypedParseInfoImpl =
......
......@@ -598,13 +598,42 @@ type TypeCheckInfo
let GetPreciseItemsFromNameResolutionVS(line,colAtEndOfNames,membersByResidue,filterCtors, resolveOverloads, hasTextChangedSinceLastTypecheck) =
GetPreciseItemsFromNameResolution(Pos.fromVS line colAtEndOfNames, membersByResidue, filterCtors, resolveOverloads, hasTextChangedSinceLastTypecheck)
let GetSettableFields endOfExprPos hasTextChangedSinceLastTypecheck =
let CollectParameters (methods: MethInfo list) amap m: Item list =
methods
|> List.collect (fun meth ->
match meth.GetParamDatas(amap, m, meth.FormalMethodInst) with
| x::_ -> x |> List.choose(fun (ParamData(_isParamArray, _isOut, _optArgInfo, name, _, ty)) ->
match name with
| Some n -> Some (Item.ArgName(Ident(n, m), ty, Some (ArgumentContainer.Method meth)))
| None -> None
)
| _ -> []
)
let GetNamedParametersAndSettableFields endOfExprPos hasTextChangedSinceLastTypecheck =
let cnrs = GetCapturedNameResolutions endOfExprPos ResolveOverloads.No |> ResizeArray.toList |> List.rev
match cnrs with
| CNR(_, Item.CtorGroup(_, ctor::_), _, denv, nenv, ad, m)::_ ->
let result = ResolveCompletionsInType ncenv nenv ResolveCompletionTargets.SettablePropertiesAndFields m ad false ctor.EnclosingType
let result =
match cnrs with
| CNR(_, Item.CtorGroup(_, ((ctor::_) as ctors)), _, denv, nenv, ad, m)::_ ->
let props = ResolveCompletionsInType ncenv nenv ResolveCompletionTargets.SettablePropertiesAndFields m ad false ctor.EnclosingType
let parameters = CollectParameters ctors amap m
Some (denv, m, props @ parameters)
| CNR(_, Item.MethodGroup(_, methods), _, denv, nenv, ad, m)::_ ->
let props =
methods
|> List.collect (fun meth ->
let retTy = meth.GetFSharpReturnTy(amap, m, meth.FormalMethodInst)
ResolveCompletionsInType ncenv nenv ResolveCompletionTargets.SettablePropertiesAndFields m ad false retTy
)
let parameters = CollectParameters methods amap m
Some (denv, m, props @ parameters)
| _ ->
None
match result with
| None ->
NameResResult.Empty
| Some (denv, m, result) ->
ReturnItemsOfType result g denv m TypeNameResolutionFlag.ResolveTypeNamesToTypeRefs hasTextChangedSinceLastTypecheck NameResResult.Members
| _ -> NameResResult.Empty
/// finds captured typing for the given position
let GetExprTypingForPosition(endOfExprPos) =
......@@ -645,7 +674,6 @@ type TypeCheckInfo
Some (items, denv, m)
| _ -> None
/// Looks at the exact expression types at the position to the left of the
/// residue then the source when it was typechecked.
let GetPreciseCompletionListFromExprTypings(untypedParseInfo:UntypedParseInfo, line, colAtEndOfNames, filterCtors, hasTextChangedSinceLastTypecheck: (obj * Range -> bool)) =
......@@ -946,8 +974,8 @@ type TypeCheckInfo
| Some(CompletionContext.RecordField(RecordContext.Constructor(typeName))) ->
FindRecordFieldsInEnv([typeName], None)
|> Some
| Some(NewObject (endPos, fields)) ->
let results = GetSettableFields endPos hasTextChangedSinceLastTypecheck
| Some(CompletionContext.ParameterList (endPos, fields)) ->
let results = GetNamedParametersAndSettableFields endPos hasTextChangedSinceLastTypecheck
let declaredItems = getDeclaredItems false
......
......@@ -557,6 +557,65 @@ type AutoCompletionListTests() as this =
AssertCtrlSpaceCompleteContains (typeDef @ ["open Ext"; "A((**))"]) "A((**)" ["XYZ"] [] // positive
AssertCtrlSpaceCompleteContains (typeDef @ ["A((**))"]) "A((**)" [] ["XYZ"] // negative
[<Test>]
[<Category("Completion in object initializer")>]
member public this.``ObjectInitializer.CompletionForNamedParameters``() =
let typeDef1 =
[
"type A = "
" static member Run(xyz: int, zyx: string) = 1"
]
AssertCtrlSpaceCompleteContains (typeDef1 @ ["A.Run()"]) ".Run(" ["xyz"; "zyx"] []
AssertCtrlSpaceCompleteContains (typeDef1 @ ["A.Run(x = 1)"]) ".Run(x" ["xyz"] []
AssertCtrlSpaceCompleteContains (typeDef1 @ ["A.Run(x = 1,)"]) ".Run(x = 1," ["xyz"; "zyx"] []
let typeDef2 =
[
"type A = "
" static member Run<'T>(xyz: 'T, zyx: string) = 1"
]
AssertCtrlSpaceCompleteContains (typeDef2 @ ["A.Run()"]) ".Run(" ["xyz"; "zyx"] []
AssertCtrlSpaceCompleteContains (typeDef2 @ ["A.Run(x = 1)"]) ".Run(x" ["xyz"] []
AssertCtrlSpaceCompleteContains (typeDef2 @ ["A.Run(x = 1,)"]) ".Run(x = 1," ["xyz"; "zyx"] []
AssertCtrlSpaceCompleteContains (typeDef2 @ ["A.Run<_>()"]) ".Run<_>(" ["xyz"; "zyx"] []
AssertCtrlSpaceCompleteContains (typeDef2 @ ["A.Run<_>(x = 1)"]) ".Run<_>(x" ["xyz"] []
AssertCtrlSpaceCompleteContains (typeDef2 @ ["A.Run<_>(x = 1,)"]) ".Run<_>(x = 1," ["xyz"; "zyx"] []
[<Test>]
[<Category("Completion in object initializer")>]
member public this.``ObjectInitializer.CompletionForSettablePropertiesInReturnValue``() =
let typeDef1 =
[
"type A0() = member val Settable0 = 1 with get,set"
"type A() = "
" member val Settable = 1 with get,set"
" member val NonSettable = 1"
" static member Run(): A0 = Unchecked.defaultof<_>"
" static member Run(a: string): A = Unchecked.defaultof<_>"
]
AssertCtrlSpaceCompleteContains (typeDef1 @ ["A.Run()"]) ".Run(" ["Settable"; "Settable0"] ["NonSettable"]
AssertCtrlSpaceCompleteContains (typeDef1 @ ["A.Run(S = 1)"]) ".Run(S" ["Settable"; "Settable0"] ["NonSettable"]
AssertCtrlSpaceCompleteContains (typeDef1 @ ["A.Run(S = 1,)"]) ".Run(S = 1," ["Settable"; "Settable0"] ["NonSettable"]
AssertCtrlSpaceCompleteContains (typeDef1 @ ["A.Run(Settable = 1,)"]) ".Run(Settable = 1," ["Settable0"] ["NonSettable"]
let typeDef2 =
[
"type A0() = member val Settable0 = 1 with get,set"
"type A() = "
" member val Settable = 1 with get,set"
" member val NonSettable = 1"
" static member Run<'T>(): A0 = Unchecked.defaultof<_>"
" static member Run(a: int): A = Unchecked.defaultof<_>"
]
AssertCtrlSpaceCompleteContains (typeDef2 @ ["A.Run()"]) ".Run(" ["Settable"; "Settable0"] ["NonSettable"]
AssertCtrlSpaceCompleteContains (typeDef2 @ ["A.Run(S = 1)"]) ".Run(S" ["Settable"; "Settable0"] ["NonSettable"]
AssertCtrlSpaceCompleteContains (typeDef2 @ ["A.Run(S = 1,)"]) ".Run(S = 1," ["Settable"; "Settable0"] ["NonSettable"]
AssertCtrlSpaceCompleteContains (typeDef2 @ ["A.Run(Settable = 1,)"]) ".Run(Settable = 1," ["Settable0"] ["NonSettable"]
AssertCtrlSpaceCompleteContains (typeDef2 @ ["A.Run<_>()"]) ".Run<_>(" ["Settable"; "Settable0"] ["NonSettable"]
AssertCtrlSpaceCompleteContains (typeDef2 @ ["A.Run<_>(S = 1)"]) ".Run<_>(S" ["Settable"; "Settable0"] ["NonSettable"]
AssertCtrlSpaceCompleteContains (typeDef2 @ ["A.Run<_>(S = 1,)"]) ".Run<_>(S = 1," ["Settable"; "Settable0"] ["NonSettable"]
AssertCtrlSpaceCompleteContains (typeDef2 @ ["A.Run<_>(Settable = 1,)"]) ".Run<_>(Settable = 1," ["Settable0"] ["NonSettable"]
[<Test>]
[<Category("RangeOperator")>]
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册