diff --git a/src/fsharp/ast.fs b/src/fsharp/ast.fs index f285d511183..6d6e2b7a255 100644 --- a/src/fsharp/ast.fs +++ b/src/fsharp/ast.fs @@ -1063,12 +1063,12 @@ and SynBindingKind * mustInline:bool * isMutable:bool * - SynAttributes * + attrs:SynAttributes * xmlDoc:PreXmlDoc * SynValData * headPat:SynPat * SynBindingReturnInfo option * - SynExpr * + expr:SynExpr * range:range * SequencePointInfoForBinding // no member just named "Range", as that would be confusing: @@ -1178,7 +1178,7 @@ and [] SynEnumCase = /// The untyped, unchecked syntax tree for one case in an enum definition. - | EnumCase of SynAttributes * ident:Ident * SynConst * PreXmlDoc * range:range + | EnumCase of attrs:SynAttributes * ident:Ident * SynConst * PreXmlDoc * range:range member this.Range = match this with | EnumCase (range=m) -> m @@ -1234,7 +1234,7 @@ and [] /// The untyped, unchecked syntax tree for a field declaration in a record or class SynField = - | Field of SynAttributes * isStatic:bool * Ident option * SynType * bool * xmlDoc:PreXmlDoc * accessibility:SynAccess option * range:range + | Field of attrs:SynAttributes * isStatic:bool * Ident option * SynType * bool * xmlDoc:PreXmlDoc * accessibility:SynAccess option * range:range and @@ -1344,7 +1344,7 @@ and | Inherit of SynType * Ident option * range:range | ValField of SynField * range:range /// A feature that is not implemented - | NestedType of SynTypeDefn * accessibility:SynAccess option * range:range + | NestedType of typeDefn:SynTypeDefn * accessibility:SynAccess option * range:range /// SynMemberDefn.AutoProperty (attribs,isStatic,id,tyOpt,propKind,memberFlags,xmlDoc,access,synExpr,mGetSet,mWholeAutoProp). /// /// F# syntax: 'member val X = expr' diff --git a/src/fsharp/vs/ServiceAssemblyContent.fs b/src/fsharp/vs/ServiceAssemblyContent.fs index 08156324e27..926c78bec82 100644 --- a/src/fsharp/vs/ServiceAssemblyContent.fs +++ b/src/fsharp/vs/ServiceAssemblyContent.fs @@ -16,7 +16,6 @@ open Microsoft.FSharp.Compiler.Range type internal ShortIdent = string type Idents = ShortIdent[] type IsAutoOpen = bool -type ModuleKind = { IsAutoOpen: bool; HasModuleSuffix: bool } [] module internal Extensions = @@ -189,20 +188,13 @@ module internal Utils = res))) |> Option.isSome -type EntityKind = - | Attribute - | Type - | FunctionOrValue of isActivePattern:bool - | Module of ModuleKind - override x.ToString() = sprintf "%A" x - [] -type LookupType = +type internal LookupType = | Fuzzy | Precise [] -type RawEntity = +type internal RawEntity = { /// Full entity name as it's seen in compiled code (raw FSharpEntity.FullName, FSharpValueOrFunction.FullName). FullName: string /// Entity name parts with removed module suffixes (Ns.M1Module.M2Module.M3.entity -> Ns.M1.M2.M3.entity) @@ -1028,274 +1020,4 @@ module internal ParsedInput = match scope.Kind with | TopModule -> NestedModule | x -> x - { ScopeKind = scopeKind; Pos = Point.make (endLine + 1) startCol }) - - let getEntityKind (input: ParsedInput) (pos: Range.pos) : EntityKind option = - let (|ConstructorPats|) = function - | Pats ps -> ps - | NamePatPairs(xs, _) -> List.map snd xs - - let isPosInRange range = Range.rangeContainsPos range pos - - let ifPosInRange range f = - if isPosInRange range then f() - else None - - let rec walkImplFileInput (ParsedImplFileInput(_, _, _, _, _, moduleOrNamespaceList, _)) = - List.tryPick (walkSynModuleOrNamespace true) moduleOrNamespaceList - - and walkSynModuleOrNamespace isTopLevel (SynModuleOrNamespace(_, _, isModule, decls, _, attrs, _, r)) = - if isModule && isTopLevel then None else List.tryPick walkAttribute attrs - |> Option.orElse (ifPosInRange r (fun _ -> List.tryPick (walkSynModuleDecl isTopLevel) decls)) - - and walkAttribute (attr: SynAttribute) = - if isPosInRange attr.Range then Some EntityKind.Attribute else None - |> Option.orElse (walkExprWithKind (Some EntityKind.Type) attr.ArgExpr) - - and walkTypar (Typar (ident, _, _)) = ifPosInRange ident.idRange (fun _ -> Some EntityKind.Type) - - and walkTyparDecl (SynTyparDecl.TyparDecl (attrs, typar)) = - List.tryPick walkAttribute attrs - |> Option.orElse (walkTypar typar) - - and walkTypeConstraint = function - | SynTypeConstraint.WhereTyparDefaultsToType (t1, t2, _) -> walkTypar t1 |> Option.orElse (walkType t2) - | SynTypeConstraint.WhereTyparIsValueType(t, _) -> walkTypar t - | SynTypeConstraint.WhereTyparIsReferenceType(t, _) -> walkTypar t - | SynTypeConstraint.WhereTyparIsUnmanaged(t, _) -> walkTypar t - | SynTypeConstraint.WhereTyparSupportsNull (t, _) -> walkTypar t - | SynTypeConstraint.WhereTyparIsComparable(t, _) -> walkTypar t - | SynTypeConstraint.WhereTyparIsEquatable(t, _) -> walkTypar t - | SynTypeConstraint.WhereTyparSubtypeOfType(t, ty, _) -> walkTypar t |> Option.orElse (walkType ty) - | SynTypeConstraint.WhereTyparSupportsMember(ts, sign, _) -> - List.tryPick walkType ts |> Option.orElse (walkMemberSig sign) - | SynTypeConstraint.WhereTyparIsEnum(t, ts, _) -> walkTypar t |> Option.orElse (List.tryPick walkType ts) - | SynTypeConstraint.WhereTyparIsDelegate(t, ts, _) -> walkTypar t |> Option.orElse (List.tryPick walkType ts) - - and walkPatWithKind (kind: EntityKind option) = function - | SynPat.Ands (pats, _) -> List.tryPick walkPat pats - | SynPat.Named(SynPat.Wild nameRange as pat, _, _, _, _) -> - if isPosInRange nameRange then None - else walkPat pat - | SynPat.Typed(pat, t, _) -> walkPat pat |> Option.orElse (walkType t) - | SynPat.Attrib(pat, attrs, _) -> walkPat pat |> Option.orElse (List.tryPick walkAttribute attrs) - | SynPat.Or(pat1, pat2, _) -> List.tryPick walkPat [pat1; pat2] - | SynPat.LongIdent(_, _, typars, ConstructorPats pats, _, r) -> - ifPosInRange r (fun _ -> kind) - |> Option.orElse ( - typars - |> Option.bind (fun (SynValTyparDecls (typars, _, constraints)) -> - List.tryPick walkTyparDecl typars - |> Option.orElse (List.tryPick walkTypeConstraint constraints))) - |> Option.orElse (List.tryPick walkPat pats) - | SynPat.Tuple(pats, _) -> List.tryPick walkPat pats - | SynPat.Paren(pat, _) -> walkPat pat - | SynPat.ArrayOrList(_, pats, _) -> List.tryPick walkPat pats - | SynPat.IsInst(t, _) -> walkType t - | SynPat.QuoteExpr(e, _) -> walkExpr e - | _ -> None - - and walkPat = walkPatWithKind None - - and walkBinding (SynBinding.Binding(_, _, _, _, attrs, _, _, pat, returnInfo, e, _, _)) = - List.tryPick walkAttribute attrs - |> Option.orElse (walkPat pat) - |> Option.orElse (walkExpr e) - |> Option.orElse ( - match returnInfo with - | Some (SynBindingReturnInfo (t, _, _)) -> walkType t - | None -> None) - - and walkInterfaceImpl (InterfaceImpl(_, bindings, _)) = - List.tryPick walkBinding bindings - - and walkIndexerArg = function - | SynIndexerArg.One e -> walkExpr e - | SynIndexerArg.Two(e1, e2) -> List.tryPick walkExpr [e1; e2] - - and walkType = function - | SynType.LongIdent ident -> ifPosInRange ident.Range (fun _ -> Some EntityKind.Type) - | SynType.App(ty, _, types, _, _, _, _) -> - walkType ty |> Option.orElse (List.tryPick walkType types) - | SynType.LongIdentApp(_, _, _, types, _, _, _) -> List.tryPick walkType types - | SynType.Tuple(ts, _) -> ts |> List.tryPick (fun (_, t) -> walkType t) - | SynType.Array(_, t, _) -> walkType t - | SynType.Fun(t1, t2, _) -> walkType t1 |> Option.orElse (walkType t2) - | SynType.WithGlobalConstraints(t, _, _) -> walkType t - | SynType.HashConstraint(t, _) -> walkType t - | SynType.MeasureDivide(t1, t2, _) -> walkType t1 |> Option.orElse (walkType t2) - | SynType.MeasurePower(t, _, _) -> walkType t - | _ -> None - - and walkClause (Clause(pat, e1, e2, _, _)) = - walkPatWithKind (Some EntityKind.Type) pat - |> Option.orElse (walkExpr e2) - |> Option.orElse (Option.bind walkExpr e1) - - and walkExprWithKind (parentKind: EntityKind option) = function - | SynExpr.LongIdent (_, LongIdentWithDots(_, dotRanges), _, r) -> - match dotRanges with - | [] when isPosInRange r -> parentKind |> Option.orElse (Some (EntityKind.FunctionOrValue false)) - | firstDotRange :: _ -> - let firstPartRange = - Range.mkRange "" r.Start (Range.mkPos firstDotRange.StartLine (firstDotRange.StartColumn - 1)) - if isPosInRange firstPartRange then - parentKind |> Option.orElse (Some (EntityKind.FunctionOrValue false)) - else None - | _ -> None - | SynExpr.Paren (e, _, _, _) -> walkExprWithKind parentKind e - | SynExpr.Quote(_, _, e, _, _) -> walkExprWithKind parentKind e - | SynExpr.Typed(e, _, _) -> walkExprWithKind parentKind e - | SynExpr.Tuple(es, _, _) -> List.tryPick (walkExprWithKind parentKind) es - | SynExpr.ArrayOrList(_, es, _) -> List.tryPick (walkExprWithKind parentKind) es - | SynExpr.Record(_, _, fields, r) -> - ifPosInRange r (fun _ -> - fields |> List.tryPick (fun (_, e, _) -> e |> Option.bind (walkExprWithKind parentKind))) - | SynExpr.New(_, t, e, _) -> walkExprWithKind parentKind e |> Option.orElse (walkType t) - | SynExpr.ObjExpr(ty, _, bindings, ifaces, _, _) -> - walkType ty - |> Option.orElse (List.tryPick walkBinding bindings) - |> Option.orElse (List.tryPick walkInterfaceImpl ifaces) - | SynExpr.While(_, e1, e2, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2] - | SynExpr.For(_, _, e1, _, e2, e3, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2; e3] - | SynExpr.ForEach(_, _, _, _, e1, e2, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2] - | SynExpr.ArrayOrListOfSeqExpr(_, e, _) -> walkExprWithKind parentKind e - | SynExpr.CompExpr(_, _, e, _) -> walkExprWithKind parentKind e - | SynExpr.Lambda(_, _, _, e, _) -> walkExprWithKind parentKind e - | SynExpr.MatchLambda(_, _, synMatchClauseList, _, _) -> - List.tryPick walkClause synMatchClauseList - | SynExpr.Match(_, e, synMatchClauseList, _, _) -> - walkExprWithKind parentKind e |> Option.orElse (List.tryPick walkClause synMatchClauseList) - | SynExpr.Do(e, _) -> walkExprWithKind parentKind e - | SynExpr.Assert(e, _) -> walkExprWithKind parentKind e - | SynExpr.App(_, _, e1, e2, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2] - | SynExpr.TypeApp(e, _, tys, _, _, _, _) -> - walkExprWithKind (Some EntityKind.Type) e |> Option.orElse (List.tryPick walkType tys) - | SynExpr.LetOrUse(_, _, bindings, e, _) -> List.tryPick walkBinding bindings |> Option.orElse (walkExprWithKind parentKind e) - | SynExpr.TryWith(e, _, clauses, _, _, _, _) -> walkExprWithKind parentKind e |> Option.orElse (List.tryPick walkClause clauses) - | SynExpr.TryFinally(e1, e2, _, _, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2] - | SynExpr.Lazy(e, _) -> walkExprWithKind parentKind e - | Sequentials es -> List.tryPick (walkExprWithKind parentKind) es - | SynExpr.IfThenElse(e1, e2, e3, _, _, _, _) -> - List.tryPick (walkExprWithKind parentKind) [e1; e2] |> Option.orElse (match e3 with None -> None | Some e -> walkExprWithKind parentKind e) - | SynExpr.Ident ident -> ifPosInRange ident.idRange (fun _ -> Some (EntityKind.FunctionOrValue false)) - | SynExpr.LongIdentSet(_, e, _) -> walkExprWithKind parentKind e - | SynExpr.DotGet(e, _, _, _) -> walkExprWithKind parentKind e - | SynExpr.DotSet(e, _, _, _) -> walkExprWithKind parentKind e - | SynExpr.DotIndexedGet(e, args, _, _) -> walkExprWithKind parentKind e |> Option.orElse (List.tryPick walkIndexerArg args) - | SynExpr.DotIndexedSet(e, args, _, _, _, _) -> walkExprWithKind parentKind e |> Option.orElse (List.tryPick walkIndexerArg args) - | SynExpr.NamedIndexedPropertySet(_, e1, e2, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2] - | SynExpr.DotNamedIndexedPropertySet(e1, _, e2, e3, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2; e3] - | SynExpr.TypeTest(e, t, _) -> walkExprWithKind parentKind e |> Option.orElse (walkType t) - | SynExpr.Upcast(e, t, _) -> walkExprWithKind parentKind e |> Option.orElse (walkType t) - | SynExpr.Downcast(e, t, _) -> walkExprWithKind parentKind e |> Option.orElse (walkType t) - | SynExpr.InferredUpcast(e, _) -> walkExprWithKind parentKind e - | SynExpr.InferredDowncast(e, _) -> walkExprWithKind parentKind e - | SynExpr.AddressOf(_, e, _, _) -> walkExprWithKind parentKind e - | SynExpr.JoinIn(e1, _, e2, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2] - | SynExpr.YieldOrReturn(_, e, _) -> walkExprWithKind parentKind e - | SynExpr.YieldOrReturnFrom(_, e, _) -> walkExprWithKind parentKind e - | SynExpr.LetOrUseBang(_, _, _, _, e1, e2, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2] - | SynExpr.DoBang(e, _) -> walkExprWithKind parentKind e - | SynExpr.TraitCall (ts, sign, e, _) -> - List.tryPick walkTypar ts - |> Option.orElse (walkMemberSig sign) - |> Option.orElse (walkExprWithKind parentKind e) - | _ -> None - - and walkExpr = walkExprWithKind None - - and walkSimplePat = function - | SynSimplePat.Attrib (pat, attrs, _) -> - walkSimplePat pat |> Option.orElse (List.tryPick walkAttribute attrs) - | SynSimplePat.Typed(pat, t, _) -> walkSimplePat pat |> Option.orElse (walkType t) - | _ -> None - - and walkField (SynField.Field(attrs, _, _, t, _, _, _, _)) = - List.tryPick walkAttribute attrs |> Option.orElse (walkType t) - - and walkValSig (SynValSig.ValSpfn(attrs, _, _, t, _, _, _, _, _, _, _)) = - List.tryPick walkAttribute attrs |> Option.orElse (walkType t) - - and walkMemberSig = function - | SynMemberSig.Inherit (t, _) -> walkType t - | SynMemberSig.Member(vs, _, _) -> walkValSig vs - | SynMemberSig.Interface(t, _) -> walkType t - | SynMemberSig.ValField(f, _) -> walkField f - | SynMemberSig.NestedType(SynTypeDefnSig.TypeDefnSig (info, repr, memberSigs, _), _) -> - walkComponentInfo false info - |> Option.orElse (walkTypeDefnSigRepr repr) - |> Option.orElse (List.tryPick walkMemberSig memberSigs) - - and walkMember = function - | SynMemberDefn.AbstractSlot (valSig, _, _) -> walkValSig valSig - | SynMemberDefn.Member(binding, _) -> walkBinding binding - | SynMemberDefn.ImplicitCtor(_, attrs, pats, _, _) -> - List.tryPick walkAttribute attrs |> Option.orElse (List.tryPick walkSimplePat pats) - | SynMemberDefn.ImplicitInherit(t, e, _, _) -> walkType t |> Option.orElse (walkExpr e) - | SynMemberDefn.LetBindings(bindings, _, _, _) -> List.tryPick walkBinding bindings - | SynMemberDefn.Interface(t, members, _) -> - walkType t - |> Option.orElse (members |> Option.bind (List.tryPick walkMember)) - | SynMemberDefn.Inherit(t, _, _) -> walkType t - | SynMemberDefn.ValField(field, _) -> walkField field - | SynMemberDefn.NestedType(tdef, _, _) -> walkTypeDefn tdef - | SynMemberDefn.AutoProperty(attrs, _, _, t, _, _, _, _, e, _, _) -> - List.tryPick walkAttribute attrs - |> Option.orElse (Option.bind walkType t) - |> Option.orElse (walkExpr e) - | _ -> None - - and walkEnumCase (EnumCase(attrs, _, _, _, _)) = List.tryPick walkAttribute attrs - - and walkUnionCaseType = function - | SynUnionCaseType.UnionCaseFields fields -> List.tryPick walkField fields - | SynUnionCaseType.UnionCaseFullType(t, _) -> walkType t - - and walkUnionCase (UnionCase(attrs, _, t, _, _, _)) = - List.tryPick walkAttribute attrs |> Option.orElse (walkUnionCaseType t) - - and walkTypeDefnSimple = function - | SynTypeDefnSimpleRepr.Enum (cases, _) -> List.tryPick walkEnumCase cases - | SynTypeDefnSimpleRepr.Union(_, cases, _) -> List.tryPick walkUnionCase cases - | SynTypeDefnSimpleRepr.Record(_, fields, _) -> List.tryPick walkField fields - | SynTypeDefnSimpleRepr.TypeAbbrev(_, t, _) -> walkType t - | _ -> None - - and walkComponentInfo isModule (ComponentInfo(attrs, typars, constraints, _, _, _, _, r)) = - if isModule then None else ifPosInRange r (fun _ -> Some EntityKind.Type) - |> Option.orElse ( - List.tryPick walkAttribute attrs - |> Option.orElse (List.tryPick walkTyparDecl typars) - |> Option.orElse (List.tryPick walkTypeConstraint constraints)) - - and walkTypeDefnRepr = function - | SynTypeDefnRepr.ObjectModel (_, defns, _) -> List.tryPick walkMember defns - | SynTypeDefnRepr.Simple(defn, _) -> walkTypeDefnSimple defn - | SynTypeDefnRepr.Exception(_) -> None - - and walkTypeDefnSigRepr = function - | SynTypeDefnSigRepr.ObjectModel (_, defns, _) -> List.tryPick walkMemberSig defns - | SynTypeDefnSigRepr.Simple(defn, _) -> walkTypeDefnSimple defn - | SynTypeDefnSigRepr.Exception(_) -> None - - and walkTypeDefn (TypeDefn (info, repr, members, _)) = - walkComponentInfo false info - |> Option.orElse (walkTypeDefnRepr repr) - |> Option.orElse (List.tryPick walkMember members) - - and walkSynModuleDecl isTopLevel (decl: SynModuleDecl) = - match decl with - | SynModuleDecl.NamespaceFragment fragment -> walkSynModuleOrNamespace isTopLevel fragment - | SynModuleDecl.NestedModule(info, _, modules, _, range) -> - walkComponentInfo true info - |> Option.orElse (ifPosInRange range (fun _ -> List.tryPick (walkSynModuleDecl false) modules)) - | SynModuleDecl.Open _ -> None - | SynModuleDecl.Let (_, bindings, _) -> List.tryPick walkBinding bindings - | SynModuleDecl.DoExpr (_, expr, _) -> walkExpr expr - | SynModuleDecl.Types (types, _) -> List.tryPick walkTypeDefn types - | _ -> None - - match input with - | ParsedInput.SigFile _ -> None - | ParsedInput.ImplFile input -> walkImplFileInput input \ No newline at end of file + { ScopeKind = scopeKind; Pos = Point.make (endLine + 1) startCol }) \ No newline at end of file diff --git a/src/fsharp/vs/ServiceDeclarations.fs b/src/fsharp/vs/ServiceDeclarations.fs index 4f6b4bc9091..2b02dbe40a6 100644 --- a/src/fsharp/vs/ServiceDeclarations.fs +++ b/src/fsharp/vs/ServiceDeclarations.fs @@ -705,6 +705,17 @@ module internal ItemDescriptionsImpl = | _ -> GetXmlCommentForItemAux None infoReader m d + let IsAttribute (infoReader: InfoReader) d = + try + let g = infoReader.g + let amap = infoReader.amap + match d with + | Item.Types(_,((TType_app(tcref,_)):: _)) -> + let ty = generalizedTyconRef tcref + Infos.ExistsHeadTypeInEntireHierarchy g amap range0 ty g.tcref_System_Attribute + | _ -> false + with _ -> false + /// Output a the description of a language item let rec FormatItemDescriptionToToolTipElement isDecl (infoReader:InfoReader) m denv d = let g = infoReader.g @@ -1168,7 +1179,6 @@ module internal ItemDescriptionsImpl = // Compute the index of the VS glyph shown with an item in the Intellisense menu let GlyphOfItem(denv,d) = - /// Find the glyph for the given representation. let reprToGlyph repr = match repr with @@ -1255,7 +1265,7 @@ module internal ItemDescriptionsImpl = /// An intellisense declaration [] -type FSharpDeclarationListItem(name, glyphMajor:GlyphMajor, glyphMinor:GlyphMinor, info) = +type FSharpDeclarationListItem(name: string, glyphMajor: GlyphMajor, glyphMinor: GlyphMinor, info, isAttribute: bool) = let mutable descriptionTextHolder:FSharpToolTipText option = None let mutable task = null @@ -1302,18 +1312,17 @@ type FSharpDeclarationListItem(name, glyphMajor:GlyphMajor, glyphMinor:GlyphMino member decl.Glyph = 6 * int glyphMajor + int glyphMinor member decl.GlyphMajor = glyphMajor - member decl.GlyphMinor = glyphMinor + member decl.GlyphMinor = glyphMinor + member decl.IsAttribute = isAttribute /// A table of declarations for Intellisense completion [] type FSharpDeclarationListInfo(declarations: FSharpDeclarationListItem[]) = - member self.Items = declarations // Make a 'Declarations' object for a set of selected items static member Create(infoReader:InfoReader, m, denv, items, reactor, checkAlive) = let g = infoReader.g - let items = items |> RemoveExplicitlySuppressed g // Sort by name. For things with the same name, @@ -1363,11 +1372,11 @@ type FSharpDeclarationListInfo(declarations: FSharpDeclarationListItem[]) = | [] -> failwith "Unexpected empty bag" | items -> let glyphMajor, glyphMinor = GlyphOfItem(denv,items.Head) - new FSharpDeclarationListItem(nm, glyphMajor, glyphMinor, Choice1Of2 (items, infoReader, m, denv, reactor, checkAlive))) + new FSharpDeclarationListItem(nm, glyphMajor, glyphMinor, Choice1Of2 (items, infoReader, m, denv, reactor, checkAlive), IsAttribute infoReader items.Head)) new FSharpDeclarationListInfo(Array.ofList decls) - - static member Error msg = new FSharpDeclarationListInfo([| new FSharpDeclarationListItem("", GlyphMajor.Error, GlyphMinor.Normal, Choice2Of2 (FSharpToolTipText [FSharpToolTipElement.CompositionError msg])) |] ) - static member Empty = new FSharpDeclarationListInfo([| |]) - + static member Error msg = + new FSharpDeclarationListInfo( + [| new FSharpDeclarationListItem("", GlyphMajor.Error, GlyphMinor.Normal, Choice2Of2 (FSharpToolTipText [FSharpToolTipElement.CompositionError msg]), false) |] ) + static member Empty = FSharpDeclarationListInfo([| |]) \ No newline at end of file diff --git a/src/fsharp/vs/ServiceDeclarations.fsi b/src/fsharp/vs/ServiceDeclarations.fsi index b1f6b156c21..d32cad3d241 100755 --- a/src/fsharp/vs/ServiceDeclarations.fsi +++ b/src/fsharp/vs/ServiceDeclarations.fsi @@ -69,7 +69,8 @@ type internal FSharpDeclarationListItem = member Glyph : int member GlyphMajor : ItemDescriptionIcons.GlyphMajor member GlyphMinor : ItemDescriptionIcons.GlyphMinor - + member IsAttribute : bool + [] /// Represents a set of declarations in F# source code, with information attached ready for display by an editor. /// Returned by GetDeclarations. diff --git a/src/fsharp/vs/ServiceParseTreeWalk.fs b/src/fsharp/vs/ServiceParseTreeWalk.fs index 84c374f318e..15fe90fec2e 100755 --- a/src/fsharp/vs/ServiceParseTreeWalk.fs +++ b/src/fsharp/vs/ServiceParseTreeWalk.fs @@ -535,5 +535,4 @@ module internal AstTraversal = range0 // only used for asserting, does not matter in non-debug #endif l |> List.map (fun x -> dive x x.Range (traverseSynModuleOrNamespace [])) |> pick fileRange l - | ParsedInput.SigFile _sigFile -> None - + | ParsedInput.SigFile _sigFile -> None \ No newline at end of file diff --git a/src/fsharp/vs/ServiceUntypedParse.fs b/src/fsharp/vs/ServiceUntypedParse.fs index a6cd9b48622..07b14db3b0b 100755 --- a/src/fsharp/vs/ServiceUntypedParse.fs +++ b/src/fsharp/vs/ServiceUntypedParse.fs @@ -72,6 +72,7 @@ type CompletionContext = // 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 + | AttributeApplication //---------------------------------------------------------------------------- // FSharpParseFileResults @@ -360,6 +361,15 @@ type FSharpParseFileResults(errors : FSharpErrorInfo[], input : Ast.ParsedInput // This does not need to be run on the background thread scope.ValidateBreakpointLocationImpl(pos) +type ModuleKind = { IsAutoOpen: bool; HasModuleSuffix: bool } + +type EntityKind = + | Attribute + | Type + | FunctionOrValue of isActivePattern:bool + | Module of ModuleKind + override x.ToString() = sprintf "%A" x + module UntypedParseImpl = let emptyStringSet = HashSet() @@ -603,6 +613,286 @@ module UntypedParseImpl = | _ -> defaultTraverse(expr) } AstTraversal.Traverse(pos, parseTree, walker) + let GetEntityKind (pos: pos, input: ParsedInput) : EntityKind option = + let (|ConstructorPats|) = function + | Pats ps -> ps + | NamePatPairs(xs, _) -> List.map snd xs + + /// An recursive pattern that collect all sequential expressions to avoid StackOverflowException + let rec (|Sequentials|_|) = function + | SynExpr.Sequential(_, _, e, Sequentials es, _) -> Some(e::es) + | SynExpr.Sequential(_, _, e1, e2, _) -> Some [e1; e2] + | _ -> None + + let inline orElse x = Microsoft.FSharp.Core.Option.orElse x + + let inline isPosInRange range = Range.rangeContainsPos range pos + + let inline ifPosInRange range f = + if isPosInRange range then f() + else None + + let rec walkImplFileInput (ParsedImplFileInput(_, _, _, _, _, moduleOrNamespaceList, _)) = + List.tryPick (walkSynModuleOrNamespace true) moduleOrNamespaceList + + and walkSynModuleOrNamespace isTopLevel (SynModuleOrNamespace(_, _, _, decls, _, attrs, _, r)) = + List.tryPick walkAttribute attrs + |> orElse (ifPosInRange r (fun _ -> List.tryPick (walkSynModuleDecl isTopLevel) decls)) + + and walkAttribute (attr: SynAttribute) = + if isPosInRange attr.Range then Some EntityKind.Attribute else None + |> orElse (walkExprWithKind (Some EntityKind.Type) attr.ArgExpr) + + and walkTypar (Typar (ident, _, _)) = ifPosInRange ident.idRange (fun _ -> Some EntityKind.Type) + + and walkTyparDecl (SynTyparDecl.TyparDecl (attrs, typar)) = + List.tryPick walkAttribute attrs + |> orElse (walkTypar typar) + + and walkTypeConstraint = function + | SynTypeConstraint.WhereTyparDefaultsToType (t1, t2, _) -> walkTypar t1 |> orElse (walkType t2) + | SynTypeConstraint.WhereTyparIsValueType(t, _) -> walkTypar t + | SynTypeConstraint.WhereTyparIsReferenceType(t, _) -> walkTypar t + | SynTypeConstraint.WhereTyparIsUnmanaged(t, _) -> walkTypar t + | SynTypeConstraint.WhereTyparSupportsNull (t, _) -> walkTypar t + | SynTypeConstraint.WhereTyparIsComparable(t, _) -> walkTypar t + | SynTypeConstraint.WhereTyparIsEquatable(t, _) -> walkTypar t + | SynTypeConstraint.WhereTyparSubtypeOfType(t, ty, _) -> walkTypar t |> orElse (walkType ty) + | SynTypeConstraint.WhereTyparSupportsMember(ts, sign, _) -> + List.tryPick walkType ts |> orElse (walkMemberSig sign) + | SynTypeConstraint.WhereTyparIsEnum(t, ts, _) -> walkTypar t |> orElse (List.tryPick walkType ts) + | SynTypeConstraint.WhereTyparIsDelegate(t, ts, _) -> walkTypar t |> orElse (List.tryPick walkType ts) + + and walkPatWithKind (kind: EntityKind option) = function + | SynPat.Ands (pats, _) -> List.tryPick walkPat pats + | SynPat.Named(SynPat.Wild nameRange as pat, _, _, _, _) -> + if isPosInRange nameRange then None + else walkPat pat + | SynPat.Typed(pat, t, _) -> walkPat pat |> orElse (walkType t) + | SynPat.Attrib(pat, attrs, _) -> walkPat pat |> orElse (List.tryPick walkAttribute attrs) + | SynPat.Or(pat1, pat2, _) -> List.tryPick walkPat [pat1; pat2] + | SynPat.LongIdent(_, _, typars, ConstructorPats pats, _, r) -> + ifPosInRange r (fun _ -> kind) + |> orElse ( + typars + |> Option.bind (fun (SynValTyparDecls (typars, _, constraints)) -> + List.tryPick walkTyparDecl typars + |> orElse (List.tryPick walkTypeConstraint constraints))) + |> orElse (List.tryPick walkPat pats) + | SynPat.Tuple(pats, _) -> List.tryPick walkPat pats + | SynPat.Paren(pat, _) -> walkPat pat + | SynPat.ArrayOrList(_, pats, _) -> List.tryPick walkPat pats + | SynPat.IsInst(t, _) -> walkType t + | SynPat.QuoteExpr(e, _) -> walkExpr e + | _ -> None + + and walkPat = walkPatWithKind None + + and walkBinding (SynBinding.Binding(_, _, _, _, attrs, _, _, pat, returnInfo, e, _, _)) = + List.tryPick walkAttribute attrs + |> orElse (walkPat pat) + |> orElse (walkExpr e) + |> orElse ( + match returnInfo with + | Some (SynBindingReturnInfo (t, _, _)) -> walkType t + | None -> None) + + and walkInterfaceImpl (InterfaceImpl(_, bindings, _)) = + List.tryPick walkBinding bindings + + and walkIndexerArg = function + | SynIndexerArg.One e -> walkExpr e + | SynIndexerArg.Two(e1, e2) -> List.tryPick walkExpr [e1; e2] + + and walkType = function + | SynType.LongIdent ident -> + // we protect it with try..with because System.Exception : rangeOfLidwd may raise + // at Microsoft.FSharp.Compiler.Ast.LongIdentWithDots.get_Range() in D:\j\workspace\release_ci_pa---3f142ccc\src\fsharp\ast.fs:line 156 + try ifPosInRange ident.Range (fun _ -> Some EntityKind.Type) with _ -> None + | SynType.App(ty, _, types, _, _, _, _) -> + walkType ty |> orElse (List.tryPick walkType types) + | SynType.LongIdentApp(_, _, _, types, _, _, _) -> List.tryPick walkType types + | SynType.Tuple(ts, _) -> ts |> List.tryPick (fun (_, t) -> walkType t) + | SynType.Array(_, t, _) -> walkType t + | SynType.Fun(t1, t2, _) -> walkType t1 |> orElse (walkType t2) + | SynType.WithGlobalConstraints(t, _, _) -> walkType t + | SynType.HashConstraint(t, _) -> walkType t + | SynType.MeasureDivide(t1, t2, _) -> walkType t1 |> orElse (walkType t2) + | SynType.MeasurePower(t, _, _) -> walkType t + | _ -> None + + and walkClause (Clause(pat, e1, e2, _, _)) = + walkPatWithKind (Some EntityKind.Type) pat + |> orElse (walkExpr e2) + |> orElse (Option.bind walkExpr e1) + + and walkExprWithKind (parentKind: EntityKind option) = function + | SynExpr.LongIdent (_, LongIdentWithDots(_, dotRanges), _, r) -> + match dotRanges with + | [] when isPosInRange r -> parentKind |> orElse (Some (EntityKind.FunctionOrValue false)) + | firstDotRange :: _ -> + let firstPartRange = + Range.mkRange "" r.Start (Range.mkPos firstDotRange.StartLine (firstDotRange.StartColumn - 1)) + if isPosInRange firstPartRange then + parentKind |> orElse (Some (EntityKind.FunctionOrValue false)) + else None + | _ -> None + | SynExpr.Paren (e, _, _, _) -> walkExprWithKind parentKind e + | SynExpr.Quote(_, _, e, _, _) -> walkExprWithKind parentKind e + | SynExpr.Typed(e, _, _) -> walkExprWithKind parentKind e + | SynExpr.Tuple(es, _, _) -> List.tryPick (walkExprWithKind parentKind) es + | SynExpr.ArrayOrList(_, es, _) -> List.tryPick (walkExprWithKind parentKind) es + | SynExpr.Record(_, _, fields, r) -> + ifPosInRange r (fun _ -> + fields |> List.tryPick (fun (_, e, _) -> e |> Option.bind (walkExprWithKind parentKind))) + | SynExpr.New(_, t, e, _) -> walkExprWithKind parentKind e |> orElse (walkType t) + | SynExpr.ObjExpr(ty, _, bindings, ifaces, _, _) -> + walkType ty + |> orElse (List.tryPick walkBinding bindings) + |> orElse (List.tryPick walkInterfaceImpl ifaces) + | SynExpr.While(_, e1, e2, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2] + | SynExpr.For(_, _, e1, _, e2, e3, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2; e3] + | SynExpr.ForEach(_, _, _, _, e1, e2, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2] + | SynExpr.ArrayOrListOfSeqExpr(_, e, _) -> walkExprWithKind parentKind e + | SynExpr.CompExpr(_, _, e, _) -> walkExprWithKind parentKind e + | SynExpr.Lambda(_, _, _, e, _) -> walkExprWithKind parentKind e + | SynExpr.MatchLambda(_, _, synMatchClauseList, _, _) -> + List.tryPick walkClause synMatchClauseList + | SynExpr.Match(_, e, synMatchClauseList, _, _) -> + walkExprWithKind parentKind e |> orElse (List.tryPick walkClause synMatchClauseList) + | SynExpr.Do(e, _) -> walkExprWithKind parentKind e + | SynExpr.Assert(e, _) -> walkExprWithKind parentKind e + | SynExpr.App(_, _, e1, e2, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2] + | SynExpr.TypeApp(e, _, tys, _, _, _, _) -> + walkExprWithKind (Some EntityKind.Type) e |> orElse (List.tryPick walkType tys) + | SynExpr.LetOrUse(_, _, bindings, e, _) -> List.tryPick walkBinding bindings |> orElse (walkExprWithKind parentKind e) + | SynExpr.TryWith(e, _, clauses, _, _, _, _) -> walkExprWithKind parentKind e |> orElse (List.tryPick walkClause clauses) + | SynExpr.TryFinally(e1, e2, _, _, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2] + | SynExpr.Lazy(e, _) -> walkExprWithKind parentKind e + | Sequentials es -> List.tryPick (walkExprWithKind parentKind) es + | SynExpr.IfThenElse(e1, e2, e3, _, _, _, _) -> + List.tryPick (walkExprWithKind parentKind) [e1; e2] |> orElse (match e3 with None -> None | Some e -> walkExprWithKind parentKind e) + | SynExpr.Ident ident -> ifPosInRange ident.idRange (fun _ -> Some (EntityKind.FunctionOrValue false)) + | SynExpr.LongIdentSet(_, e, _) -> walkExprWithKind parentKind e + | SynExpr.DotGet(e, _, _, _) -> walkExprWithKind parentKind e + | SynExpr.DotSet(e, _, _, _) -> walkExprWithKind parentKind e + | SynExpr.DotIndexedGet(e, args, _, _) -> walkExprWithKind parentKind e |> orElse (List.tryPick walkIndexerArg args) + | SynExpr.DotIndexedSet(e, args, _, _, _, _) -> walkExprWithKind parentKind e |> orElse (List.tryPick walkIndexerArg args) + | SynExpr.NamedIndexedPropertySet(_, e1, e2, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2] + | SynExpr.DotNamedIndexedPropertySet(e1, _, e2, e3, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2; e3] + | SynExpr.TypeTest(e, t, _) -> walkExprWithKind parentKind e |> orElse (walkType t) + | SynExpr.Upcast(e, t, _) -> walkExprWithKind parentKind e |> orElse (walkType t) + | SynExpr.Downcast(e, t, _) -> walkExprWithKind parentKind e |> orElse (walkType t) + | SynExpr.InferredUpcast(e, _) -> walkExprWithKind parentKind e + | SynExpr.InferredDowncast(e, _) -> walkExprWithKind parentKind e + | SynExpr.AddressOf(_, e, _, _) -> walkExprWithKind parentKind e + | SynExpr.JoinIn(e1, _, e2, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2] + | SynExpr.YieldOrReturn(_, e, _) -> walkExprWithKind parentKind e + | SynExpr.YieldOrReturnFrom(_, e, _) -> walkExprWithKind parentKind e + | SynExpr.LetOrUseBang(_, _, _, _, e1, e2, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2] + | SynExpr.DoBang(e, _) -> walkExprWithKind parentKind e + | SynExpr.TraitCall (ts, sign, e, _) -> + List.tryPick walkTypar ts + |> orElse (walkMemberSig sign) + |> orElse (walkExprWithKind parentKind e) + | _ -> None + + and walkExpr = walkExprWithKind None + + and walkSimplePat = function + | SynSimplePat.Attrib (pat, attrs, _) -> + walkSimplePat pat |> orElse (List.tryPick walkAttribute attrs) + | SynSimplePat.Typed(pat, t, _) -> walkSimplePat pat |> orElse (walkType t) + | _ -> None + + and walkField (SynField.Field(attrs, _, _, t, _, _, _, _)) = + List.tryPick walkAttribute attrs |> orElse (walkType t) + + and walkValSig (SynValSig.ValSpfn(attrs, _, _, t, _, _, _, _, _, _, _)) = + List.tryPick walkAttribute attrs |> orElse (walkType t) + + and walkMemberSig = function + | SynMemberSig.Inherit (t, _) -> walkType t + | SynMemberSig.Member(vs, _, _) -> walkValSig vs + | SynMemberSig.Interface(t, _) -> walkType t + | SynMemberSig.ValField(f, _) -> walkField f + | SynMemberSig.NestedType(SynTypeDefnSig.TypeDefnSig (info, repr, memberSigs, _), _) -> + walkComponentInfo false info + |> orElse (walkTypeDefnSigRepr repr) + |> orElse (List.tryPick walkMemberSig memberSigs) + + and walkMember = function + | SynMemberDefn.AbstractSlot (valSig, _, _) -> walkValSig valSig + | SynMemberDefn.Member(binding, _) -> walkBinding binding + | SynMemberDefn.ImplicitCtor(_, attrs, pats, _, _) -> + List.tryPick walkAttribute attrs |> orElse (List.tryPick walkSimplePat pats) + | SynMemberDefn.ImplicitInherit(t, e, _, _) -> walkType t |> orElse (walkExpr e) + | SynMemberDefn.LetBindings(bindings, _, _, _) -> List.tryPick walkBinding bindings + | SynMemberDefn.Interface(t, members, _) -> + walkType t |> orElse (members |> Option.bind (List.tryPick walkMember)) + | SynMemberDefn.Inherit(t, _, _) -> walkType t + | SynMemberDefn.ValField(field, _) -> walkField field + | SynMemberDefn.NestedType(tdef, _, _) -> walkTypeDefn tdef + | SynMemberDefn.AutoProperty(attrs, _, _, t, _, _, _, _, e, _, _) -> + List.tryPick walkAttribute attrs + |> orElse (Option.bind walkType t) + |> orElse (walkExpr e) + | _ -> None + + and walkEnumCase (EnumCase(attrs, _, _, _, _)) = List.tryPick walkAttribute attrs + + and walkUnionCaseType = function + | SynUnionCaseType.UnionCaseFields fields -> List.tryPick walkField fields + | SynUnionCaseType.UnionCaseFullType(t, _) -> walkType t + + and walkUnionCase (UnionCase(attrs, _, t, _, _, _)) = + List.tryPick walkAttribute attrs |> orElse (walkUnionCaseType t) + + and walkTypeDefnSimple = function + | SynTypeDefnSimpleRepr.Enum (cases, _) -> List.tryPick walkEnumCase cases + | SynTypeDefnSimpleRepr.Union(_, cases, _) -> List.tryPick walkUnionCase cases + | SynTypeDefnSimpleRepr.Record(_, fields, _) -> List.tryPick walkField fields + | SynTypeDefnSimpleRepr.TypeAbbrev(_, t, _) -> walkType t + | _ -> None + + and walkComponentInfo isModule (ComponentInfo(attrs, typars, constraints, _, _, _, _, r)) = + if isModule then None else ifPosInRange r (fun _ -> Some EntityKind.Type) + |> orElse ( + List.tryPick walkAttribute attrs + |> orElse (List.tryPick walkTyparDecl typars) + |> orElse (List.tryPick walkTypeConstraint constraints)) + + and walkTypeDefnRepr = function + | SynTypeDefnRepr.ObjectModel (_, defns, _) -> List.tryPick walkMember defns + | SynTypeDefnRepr.Simple(defn, _) -> walkTypeDefnSimple defn + | SynTypeDefnRepr.Exception(_) -> None + + and walkTypeDefnSigRepr = function + | SynTypeDefnSigRepr.ObjectModel (_, defns, _) -> List.tryPick walkMemberSig defns + | SynTypeDefnSigRepr.Simple(defn, _) -> walkTypeDefnSimple defn + | SynTypeDefnSigRepr.Exception(_) -> None + + and walkTypeDefn (TypeDefn (info, repr, members, _)) = + walkComponentInfo false info + |> orElse (walkTypeDefnRepr repr) + |> orElse (List.tryPick walkMember members) + + and walkSynModuleDecl isTopLevel (decl: SynModuleDecl) = + match decl with + | SynModuleDecl.NamespaceFragment fragment -> walkSynModuleOrNamespace isTopLevel fragment + | SynModuleDecl.NestedModule(info, _, modules, _, range) -> + walkComponentInfo true info + |> orElse (ifPosInRange range (fun _ -> List.tryPick (walkSynModuleDecl false) modules)) + | SynModuleDecl.Open _ -> None + | SynModuleDecl.Let (_, bindings, _) -> List.tryPick walkBinding bindings + | SynModuleDecl.DoExpr (_, expr, _) -> walkExpr expr + | SynModuleDecl.Types (types, _) -> List.tryPick walkTypeDefn types + | _ -> None + + match input with + | ParsedInput.SigFile _ -> None + | ParsedInput.ImplFile input -> walkImplFileInput input + type internal TS = AstTraversal.TraverseStep /// Try to determine completion context for the given pair (row, columns) @@ -614,7 +904,10 @@ module UntypedParseImpl = match parsedInputOpt with | None -> None - | Some pt -> + | Some pt -> + match GetEntityKind(pos, pt) with + | Some EntityKind.Attribute -> Some CompletionContext.AttributeApplication + | _ -> let parseLid (LongIdentWithDots(lid, dots)) = let rec collect plid (parts : Ident list) (dots : range list) = @@ -861,6 +1154,7 @@ module UntypedParseImpl = match parseLid lidwd with | Some (completionPath) -> GetCompletionContextForInheritSynMember (componentInfo, typeDefnKind, completionPath) | None -> Some (CompletionContext.Invalid) // A $ .B -> no completion list - | _ -> None } - AstTraversal.Traverse(pos, pt, walker) - + | _ -> None + + member this.VisitBinding(defaultTraverse, synBinding) = defaultTraverse synBinding } + AstTraversal.Traverse(pos, pt, walker) \ No newline at end of file diff --git a/src/fsharp/vs/ServiceUntypedParse.fsi b/src/fsharp/vs/ServiceUntypedParse.fsi index 3133f6b5a22..4be910f598c 100755 --- a/src/fsharp/vs/ServiceUntypedParse.fsi +++ b/src/fsharp/vs/ServiceUntypedParse.fsi @@ -9,6 +9,7 @@ namespace Microsoft.FSharp.Compiler.SourceCodeServices open System.Collections.Generic open Microsoft.FSharp.Compiler +open Microsoft.FSharp.Compiler.Ast open Microsoft.FSharp.Compiler.Range open Microsoft.FSharp.Compiler.ErrorLogger @@ -74,14 +75,23 @@ type internal CompletionContext = // 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 + | AttributeApplication + +type internal ModuleKind = { IsAutoOpen: bool; HasModuleSuffix: bool } + +type internal EntityKind = + | Attribute + | Type + | FunctionOrValue of isActivePattern:bool + | Module of ModuleKind // implementation details used by other code in the compiler module internal UntypedParseImpl = - open Microsoft.FSharp.Compiler.Ast val TryFindExpressionASTLeftOfDotLeftOfCursor : pos * ParsedInput option -> (pos * bool) option val GetRangeOfExprLeftOfDot : pos * ParsedInput option -> range option val TryFindExpressionIslandInPosition : pos * ParsedInput option -> string option val TryGetCompletionContext : pos * FSharpParseFileResults option -> CompletionContext option + val GetEntityKind: pos * ParsedInput -> EntityKind option // implementation details used by other code in the compiler module internal SourceFileImpl = diff --git a/src/fsharp/vs/service.fs b/src/fsharp/vs/service.fs index 7bb1fbdff35..83fbc37a656 100755 --- a/src/fsharp/vs/service.fs +++ b/src/fsharp/vs/service.fs @@ -1015,8 +1015,7 @@ type TypeCheckInfo // Completion at ' { XXX = ... } " | Some(CompletionContext.RecordField(RecordContext.New(plid, residue))) -> - GetClassOrRecordFieldsEnvironmentLookupResolutions(mkPos line loc, plid, residue) - |> Some + Some(GetClassOrRecordFieldsEnvironmentLookupResolutions(mkPos line loc, plid, residue)) // Completion at ' { XXX = ... with ... } " | Some(CompletionContext.RecordField(RecordContext.CopyOnUpdate(r, (plid, residue)))) -> @@ -1028,8 +1027,7 @@ type TypeCheckInfo // Completion at ' { XXX = ... with ... } " | Some(CompletionContext.RecordField(RecordContext.Constructor(typeName))) -> - GetClassOrRecordFieldsEnvironmentLookupResolutions(mkPos line loc, [typeName], None) - |> Some + Some(GetClassOrRecordFieldsEnvironmentLookupResolutions(mkPos line loc, [typeName], None)) // Completion at ' SomeMethod( ... ) ' with named arguments | Some(CompletionContext.ParameterList (endPos, fields)) -> @@ -1049,11 +1047,19 @@ type TypeCheckInfo | Some (declItems, declaredDisplayEnv, declaredRange) -> Some (filtered @ declItems, declaredDisplayEnv, declaredRange) | _ -> declaredItems + | Some(CompletionContext.AttributeApplication) -> + GetDeclaredItems (parseResultsOpt, lineStr, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, line, loc, filterCtors, resolveOverloads, hasTextChangedSinceLastTypecheck, false) + |> Option.map (fun (items, denv, r) -> + items + |> List.filter (function + | Item.Types _ + | Item.ModuleOrNamespaces _ -> true + | _ -> false), denv, r) + // Other completions | cc -> let isInRangeOperator = (match cc with Some (CompletionContext.RangeOperator) -> true | _ -> false) - let declaredItems = GetDeclaredItems (parseResultsOpt, lineStr, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, line, loc, filterCtors,resolveOverloads, hasTextChangedSinceLastTypecheck, isInRangeOperator) - declaredItems + GetDeclaredItems (parseResultsOpt, lineStr, origLongIdentOpt, colAtEndOfNamesAndResidue, residueOpt, line, loc, filterCtors,resolveOverloads, hasTextChangedSinceLastTypecheck, isInRangeOperator) /// Return 'false' if this is not a completion item valid in an interface file. let IsValidSignatureFileItem item = @@ -1135,7 +1141,7 @@ type TypeCheckInfo (fun () -> match GetDeclItemsForNamesAtPosition(parseResultsOpt, Some qualifyingNames, Some partialName, line, lineStr, colAtEndOfNamesAndResidue, ResolveTypeNamesToCtors, ResolveOverloads.Yes, hasTextChangedSinceLastTypecheck) with | None -> FSharpDeclarationListInfo.Empty - | Some(items,denv,m) -> + | Some (items, denv, m) -> let items = items |> FilterAutoCompletesBasedOnParseContext parseResultsOpt (mkPos line colAtEndOfNamesAndResidue) let items = if isInterfaceFile then items |> List.filter IsValidSignatureFileItem else items FSharpDeclarationListInfo.Create(infoReader,m,denv,items,reactorOps,checkAlive)) @@ -1149,7 +1155,7 @@ type TypeCheckInfo (fun () -> match GetDeclItemsForNamesAtPosition(parseResultsOpt, Some qualifyingNames, Some partialName, line, lineStr, colAtEndOfNamesAndResidue, ResolveTypeNamesToCtors, ResolveOverloads.Yes, hasTextChangedSinceLastTypecheck) with | None -> List.Empty - | Some(items,_denv,_m) -> + | Some (items, _denv, _m) -> let items = items |> FilterAutoCompletesBasedOnParseContext parseResultsOpt (mkPos line colAtEndOfNamesAndResidue) let items = if isInterfaceFile then items |> List.filter IsValidSignatureFileItem else items @@ -1251,7 +1257,7 @@ type TypeCheckInfo (fun () -> match GetDeclItemsForNamesAtPosition(None,Some(names),None,line,lineStr,colAtEndOfNames,ResolveTypeNamesToCtors,ResolveOverloads.Yes,fun _ -> false) with | None -> FSharpToolTipText [] - | Some(items,denv,m) -> + | Some (items, denv, m) -> FSharpToolTipText(items |> List.map (FormatDescriptionOfItem false infoReader m denv ))) (fun err -> FSharpToolTipText [FSharpToolTipElement.CompositionError err]) @@ -1270,7 +1276,7 @@ type TypeCheckInfo (fun () -> match GetDeclItemsForNamesAtPosition(None, Some names, None, line, lineStr, colAtEndOfNames, ResolveTypeNamesToCtors, ResolveOverloads.No, fun _ -> false) with // F1 Keywords do not distiguish between overloads | None -> None - | Some(items,_,_) -> + | Some (items, _, _) -> match items with | [] -> None | [item] -> @@ -1301,7 +1307,7 @@ type TypeCheckInfo (fun () -> match GetDeclItemsForNamesAtPosition(None,namesOpt,None,line,lineStr,colAtEndOfNames,ResolveTypeNamesToCtors,ResolveOverloads.No, fun _ -> false) with | None -> FSharpMethodGroup("",[| |]) - | Some(items,denv,m) -> FSharpMethodGroup.Create(infoReader,m,denv,items)) + | Some (items, denv, m) -> FSharpMethodGroup.Create(infoReader,m,denv,items)) (fun msg -> FSharpMethodGroup(msg,[| |])) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs index ae41ef67574..57923da0763 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs @@ -184,7 +184,7 @@ type internal FSharpAddOpenCodeFixProvider match symbol with | Some symbol -> let pos = Pos.fromZ textLinePos.Line textLinePos.Character - let isAttribute = ParsedInput.getEntityKind parsedInput pos = Some EntityKind.Attribute + let isAttribute = UntypedParseImpl.GetEntityKind(pos, parsedInput) = Some EntityKind.Attribute let entities = assemblyContentProvider.GetAllEntitiesInProjectAndReferencedAssemblies checkFileResults |> List.map (fun e -> diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index 8df504a775d..5137226cafa 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -49,7 +49,8 @@ type internal FSharpCompletionProvider let xmlMemberIndexService = serviceProvider.GetService(typeof) :?> IVsXMLMemberIndexService let documentationBuilder = XmlDocumentation.CreateDocumentationBuilder(xmlMemberIndexService, serviceProvider.DTE) - + static let attributeSuffixLength = "Attribute".Length + static member ShouldTriggerCompletionAux(sourceText: SourceText, caretPosition: int, trigger: CompletionTriggerKind, getInfo: (unit -> DocumentId * string * string list)) = // Skip if we are at the start of a document if caretPosition = 0 then @@ -92,29 +93,36 @@ type internal FSharpCompletionProvider static member ProvideCompletionsAsyncAux(checker: FSharpChecker, sourceText: SourceText, caretPosition: int, options: FSharpProjectOptions, filePath: string, textVersionHash: int) = async { let! parseResults, checkFileAnswer = checker.ParseAndCheckFileInProject(filePath, textVersionHash, sourceText.ToString(), options) - match checkFileAnswer with - | FSharpCheckFileAnswer.Aborted -> return List() - | FSharpCheckFileAnswer.Succeeded(checkFileResults) -> - - 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(caretLine.ToString(), caretLineColumn - 1) - let! declarations = checkFileResults.GetDeclarationListInfo(Some(parseResults), fcsCaretLineNumber, caretLineColumn, caretLine.ToString(), qualifyingNames, partialName) - - let results = List() - - for declarationItem in declarations.Items do - let glyph = CommonRoslynHelpers.FSharpGlyphToRoslynGlyph declarationItem.GlyphMajor - let completionItem = CommonCompletionItem.Create(declarationItem.Name, glyph=Nullable(glyph)) - declarationItemsCache.Remove(completionItem.DisplayText) |> ignore // clear out stale entries if they exist - declarationItemsCache.Add(completionItem.DisplayText, declarationItem) - results.Add(completionItem) - - return results + match parseResults.ParseTree, checkFileAnswer with + | _, FSharpCheckFileAnswer.Aborted + | None, _ -> return List() + | Some parsedInput, FSharpCheckFileAnswer.Succeeded(checkFileResults) -> + let textLines = sourceText.Lines + let caretLinePos = textLines.GetLinePosition(caretPosition) + let entityKind = UntypedParseImpl.GetEntityKind(Pos.fromZ caretLinePos.Line caretLinePos.Character, parsedInput) + + let caretLine = textLines.GetLineFromPosition(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(caretLine.ToString(), caretLineColumn - 1) + let! declarations = checkFileResults.GetDeclarationListInfo(Some(parseResults), fcsCaretLineNumber, caretLineColumn, caretLine.ToString(), qualifyingNames, partialName) + + let results = List() + + for declarationItem in declarations.Items do + let glyph = CommonRoslynHelpers.FSharpGlyphToRoslynGlyph declarationItem.GlyphMajor + let name = + match entityKind with + | Some EntityKind.Attribute when declarationItem.IsAttribute && declarationItem.Name.EndsWith "Attribute" -> + declarationItem.Name.[0..declarationItem.Name.Length - attributeSuffixLength - 1] + | _ -> declarationItem.Name + let completionItem = CommonCompletionItem.Create(name, glyph = Nullable glyph) + declarationItemsCache.Remove(completionItem.DisplayText) |> ignore // clear out stale entries if they exist + declarationItemsCache.Add(completionItem.DisplayText, declarationItem) + results.Add(completionItem) + + return results } override this.ShouldTriggerCompletion(sourceText: SourceText, caretPosition: int, trigger: CompletionTrigger, _: OptionSet) = diff --git a/vsintegration/tests/Salsa/SalsaUtils.fs b/vsintegration/tests/Salsa/SalsaUtils.fs index 54c1dae5583..a776fb0dc9b 100644 --- a/vsintegration/tests/Salsa/SalsaUtils.fs +++ b/vsintegration/tests/Salsa/SalsaUtils.fs @@ -220,7 +220,7 @@ module internal VsOpsUtils = printfn "Failed to find expected value %s in " membername let MAX = 25 printfn "Completion list = %s" (if completions.Length > MAX then sprintf "%A ... and more" completions.[0..MAX] else sprintf "%A" completions) - Assert.Fail(sprintf "Couldn't find '%s' in completion list" membername) + Assert.Fail(sprintf "Couldn't find '%s' in completion list: %+A" membername (completions |> Array.map (fun (name,_,_,_) -> name))) /// Verify the completion list does not contain a member with the given name let AssertCompListDoesNotContain(completions : CompletionItem[], membername) = diff --git a/vsintegration/tests/unittests/Tests.LanguageService.Completion.fs b/vsintegration/tests/unittests/Tests.LanguageService.Completion.fs index db2a4d2211d..20d65e140d2 100644 --- a/vsintegration/tests/unittests/Tests.LanguageService.Completion.fs +++ b/vsintegration/tests/unittests/Tests.LanguageService.Completion.fs @@ -3476,7 +3476,7 @@ let x = query { for bbbb in abbbbc(*D0*) do member public this.``Attribute.WhenAttachedToLet.Bug70080``() = this.AutoCompleteBug70080Helper @" open System - []