diff --git a/src/fsharp/vs/ServiceParseTreeWalk.fs b/src/fsharp/vs/ServiceParseTreeWalk.fs index 992570be68..9dddedb627 100755 --- a/src/fsharp/vs/ServiceParseTreeWalk.fs +++ b/src/fsharp/vs/ServiceParseTreeWalk.fs @@ -90,9 +90,15 @@ module internal AstTraversal = /// VisitLetOrUse allows overriding behavior when visiting module or local let or use bindings abstract VisitLetOrUse : SynBinding list * range -> 'T option default this.VisitLetOrUse (_, _) = None - + /// VisitType allows overriding behavior when visiting simple pats abstract VisitSimplePats : SynSimplePat list -> 'T option default this.VisitSimplePats (_) = None + /// VisitPat allows overriding behavior when visiting patterns + abstract VisitPat : (SynPat -> 'T option) * SynPat -> 'T option + default this.VisitPat (defaultTraverse, pat) = defaultTraverse pat + /// VisitType allows overriding behavior when visiting type hints (x: ..., etc.) + abstract VisitType : (SynType -> 'T option) * SynType -> 'T option + default this.VisitType (defaultTraverse, ty) = defaultTraverse ty let dive node range project = range,(fun() -> project node) @@ -189,12 +195,12 @@ module internal AstTraversal = dive synExpr2 synExpr2.Range traverseSynExpr] |> pick expr | SynExpr.Const(_synConst, _range) -> None - | SynExpr.Typed(synExpr, _synType, _range) -> traverseSynExpr synExpr + | SynExpr.Typed(synExpr, synType, _range) -> [ traverseSynExpr synExpr; traverseSynType synType ] |> List.tryPick id | SynExpr.Tuple(synExprList, _, _range) | SynExpr.StructTuple(synExprList, _, _range) -> synExprList |> List.map (fun x -> dive x x.Range traverseSynExpr) |> pick expr | SynExpr.ArrayOrList(_, synExprList, _range) -> synExprList |> List.map (fun x -> dive x x.Range traverseSynExpr) |> pick expr | SynExpr.Record(inheritOpt,copyOpt,fields, _range) -> - [ + [ let diveIntoSeparator offsideColumn scPosOpt copyOpt = match scPosOpt with | Some scPos -> @@ -448,6 +454,50 @@ module internal AstTraversal = visitor.VisitExpr(path, traverseSynExpr path, defaultTraverse, expr) + and traversePat (pat: SynPat) = + let defaultTraverse p = + match p with + | SynPat.Paren (p, _) -> traversePat p + | SynPat.Or (p1, p2, _) -> [ p1; p2] |> List.tryPick traversePat + | SynPat.Ands (ps, _) + | SynPat.Tuple (ps, _) + | SynPat.StructTuple (ps, _) + | SynPat.ArrayOrList (_, ps, _) -> ps |> List.tryPick traversePat + | SynPat.Attrib (p, _, _) -> traversePat p + | SynPat.LongIdent(_, _, _, args, _, _) -> + match args with + | SynConstructorArgs.Pats ps -> ps |> List.tryPick traversePat + | SynConstructorArgs.NamePatPairs (ps, _) -> + ps |> List.map snd |> List.tryPick traversePat + | SynPat.Typed (p, ty, _) -> + [ traversePat p; traverseSynType ty ] |> List.tryPick id + | _ -> None + + visitor.VisitPat (defaultTraverse, pat) + + and traverseSynType (ty: SynType) = + let defaultTraverse ty = + match ty with + | SynType.App (typeName, _, typeArgs, _, _, _, _) + | SynType.LongIdentApp (typeName, _, _, typeArgs, _, _, _) -> + [ yield typeName + yield! typeArgs ] + |> List.tryPick traverseSynType + | SynType.Fun (ty1, ty2, _) -> [ty1; ty2] |> List.tryPick traverseSynType + | SynType.MeasurePower (ty, _, _) + | SynType.HashConstraint (ty, _) + | SynType.WithGlobalConstraints (ty, _, _) + | SynType.Array (_, ty, _) -> traverseSynType ty + | SynType.StaticConstantNamed (ty1, ty2, _) + | SynType.MeasureDivide (ty1, ty2, _) -> [ty1; ty2] |> List.tryPick traverseSynType + | SynType.Tuple (tys, _) + | SynType.StructTuple (tys, _) -> tys |> List.map snd |> List.tryPick traverseSynType + | SynType.StaticConstantExpr (expr, _) -> traverseSynExpr [] expr + | SynType.Anon _ -> None + | _ -> None + + visitor.VisitType (defaultTraverse, ty) + and normalizeMembersToDealWithPeculiaritiesOfGettersAndSetters path traverseInherit (synMemberDefns:SynMemberDefns) = synMemberDefns // property getters are setters are two members that can have the same range, so do some somersaults to deal with this @@ -566,8 +616,10 @@ module internal AstTraversal = let defaultTraverse b = let path = TraverseStep.Binding b :: path match b with - | (SynBinding.Binding(_synAccessOption, _synBindingKind, _, _, _synAttributes, _preXmlDoc, _synValData, _synPat, _synBindingReturnInfoOption, synExpr, _range, _sequencePointInfoForBinding)) -> - traverseSynExpr path synExpr + | (SynBinding.Binding(_synAccessOption, _synBindingKind, _, _, _synAttributes, _preXmlDoc, _synValData, synPat, _synBindingReturnInfoOption, synExpr, _range, _sequencePointInfoForBinding)) -> + [ traversePat synPat + traverseSynExpr path synExpr ] + |> List.tryPick id visitor.VisitBinding(defaultTraverse,b) match parseTree with diff --git a/src/fsharp/vs/ServiceUntypedParse.fs b/src/fsharp/vs/ServiceUntypedParse.fs index c5e30bf3df..dff684fe52 100755 --- a/src/fsharp/vs/ServiceUntypedParse.fs +++ b/src/fsharp/vs/ServiceUntypedParse.fs @@ -79,6 +79,8 @@ type CompletionContext = | ParameterList of pos * HashSet | AttributeApplication | OpenDeclaration + /// completing pattern type (e.g. foo (x: |)) + | PatternType //---------------------------------------------------------------------------- // FSharpParseFileResults @@ -1272,6 +1274,12 @@ module UntypedParseImpl = else None | _ -> defaultTraverse decl + + member __.VisitType(defaultTraverse, ty) = + match ty with + | SynType.LongIdent _ when rangeContainsPos ty.Range pos -> + Some CompletionContext.PatternType + | _ -> defaultTraverse ty } AstTraversal.Traverse(pos, pt, walker) diff --git a/src/fsharp/vs/ServiceUntypedParse.fsi b/src/fsharp/vs/ServiceUntypedParse.fsi index 9a2c8379d5..62485fdefa 100755 --- a/src/fsharp/vs/ServiceUntypedParse.fsi +++ b/src/fsharp/vs/ServiceUntypedParse.fsi @@ -104,6 +104,8 @@ type internal CompletionContext = | ParameterList of pos * HashSet | AttributeApplication | OpenDeclaration + /// completing pattern type (e.g. foo (x: |)) + | PatternType #if COMPILER_PUBLIC_API type ModuleKind = { IsAutoOpen: bool; HasModuleSuffix: bool } diff --git a/src/fsharp/vs/service.fs b/src/fsharp/vs/service.fs index 10f2c61926..f40cc61dc4 100644 --- a/src/fsharp/vs/service.fs +++ b/src/fsharp/vs/service.fs @@ -870,6 +870,19 @@ type TypeCheckInfo |> Option.map (fun (items, denv, m) -> items |> List.filter (fun x -> match x.Item with Item.ModuleOrNamespaces _ -> true | _ -> false), denv, m) + // Completion at '(x: ...)" + | Some (CompletionContext.PatternType) -> + GetDeclaredItems (parseResultsOpt, lineStr, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, lastDotPos, line, loc, filterCtors, resolveOverloads, hasTextChangedSinceLastTypecheck, false, getAllSymbols) + |> Option.map (fun (items, denv, m) -> + items + |> List.filter (fun cItem -> + match cItem.Item with + | Item.ModuleOrNamespaces _ + | Item.Types _ + | Item.UnqualifiedType _ + | Item.ExnCase _ -> true + | _ -> false), denv, m) + // Other completions | cc -> match residueOpt |> Option.bind Seq.tryHead with diff --git a/vsintegration/tests/unittests/Tests.LanguageService.Completion.fs b/vsintegration/tests/unittests/Tests.LanguageService.Completion.fs index 88d22ecb29..6c5a8596ff 100644 --- a/vsintegration/tests/unittests/Tests.LanguageService.Completion.fs +++ b/vsintegration/tests/unittests/Tests.LanguageService.Completion.fs @@ -6064,7 +6064,7 @@ let rec f l = let f (x:MyNamespace1.MyModule(*Maftervariable4*)) = 10 let y = int System.IO(*Maftervariable5*)""", marker = "(*Maftervariable4*)", - list = ["DuType";"Tag"]) + list = ["DuType"]) [] member this.``VariableIdentifier.SystemNamespace``() =