diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index a75c52cf451e850247bf30f3bfddaa176d40bb1e..33840d6c4343207db9f8cdb81186916151e922dd 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -800,6 +800,28 @@ type internal TypeCheckInfo | Item.Types (_, ty :: _) when isInterfaceTy g ty -> true | _ -> false + /// Is the item suitable for completion in a pattern + let IsPatternCandidate (item: CompletionItem) = + match item.Item with + | Item.Value v -> v.LiteralValue.IsSome + | Item.ILField field -> field.LiteralValue.IsSome + | Item.ActivePatternCase _ + | Item.ExnCase _ + | Item.ModuleOrNamespaces _ + | Item.Types _ + | Item.UnionCase _ -> true + | _ -> false + + /// Is the item suitable for completion in a type application or type annotation + let IsTypeCandidate (item: CompletionItem) = + match item.Item with + | Item.ModuleOrNamespaces _ + | Item.Types _ + | Item.TypeVar _ + | Item.UnqualifiedType _ + | Item.ExnCase _ -> true + | _ -> false + /// Return only items with the specified name, modulo "Attribute" for type completions let FilterDeclItemsByResidue (getItem: 'a -> Item) residue (items: 'a list) = let attributedResidue = residue + "Attribute" @@ -939,6 +961,10 @@ type internal TypeCheckInfo Unresolved = None } + let getItem (x: ItemWithInst) = x.Item + + let getItem2 (x: CompletionItem) = x.Item + /// Checks whether the suggested name is unused. /// In the future we could use an increasing numeric suffix for conflict resolution let CreateCompletionItemForSuggestedPatternName (pos: pos) name = @@ -1044,7 +1070,7 @@ type internal TypeCheckInfo |> List.map (fun (name, overloads) -> Item.MethodGroup(name, overloads, None) |> ItemWithNoInst - |> CompletionItem ValueNone ValueNone) + |> DefaultCompletionItem) Some(overridableMethods, nenv.DisplayEnv, m) | _ -> None) @@ -1063,12 +1089,77 @@ type internal TypeCheckInfo else Item.UnionCaseField(uci, index) |> ItemWithNoInst - |> CompletionItem ValueNone ValueNone + |> DefaultCompletionItem |> Some) |> Some | _ -> None) - let getItem (x: ItemWithInst) = x.Item + let GetCompletionsForUnionCaseField pos indexOrName caseIdRange isTheOnlyField declaredItems = + let declaredItems = + declaredItems + |> Option.bind (FilterRelevantItemsBy getItem2 None IsPatternCandidate) + + // When the user types `fun (Case (x| )) ->`, we do not yet know whether the intention is to use positional or named arguments, + // so let's show options for both. + let fields indexOrName isTheOnlyField (uci: UnionCaseInfo) = + match indexOrName, isTheOnlyField with + | Choice1Of2 (Some 0), true -> + uci.UnionCase.RecdFields + |> List.mapi (fun index _ -> Item.UnionCaseField(uci, index) |> ItemWithNoInst |> DefaultCompletionItem) + | _ -> [] + + sResolutions.CapturedNameResolutions + |> ResizeArray.tryPick (fun r -> + match r.Item with + | Item.UnionCase (uci, _) when equals r.Range caseIdRange -> + let list = + declaredItems + |> Option.map p13 + |> Option.defaultValue [] + |> List.append (fields indexOrName isTheOnlyField uci) + + Some(SuggestNameForUnionCaseFieldPattern g caseIdRange.End pos uci indexOrName list, r.DisplayEnv, r.Range) + | _ -> None) + |> Option.orElse declaredItems + + let GetCompletionsForRecordField pos referencedFields declaredItems = + declaredItems + |> Option.map (fun (items: CompletionItem list, denv, range) -> + let fields = + // Try to find a name resolution for any of the referenced fields, and through it access all available fields of the record + referencedFields + |> List.tryPick (fun (_, fieldRange) -> + sResolutions.CapturedNameResolutions + |> ResizeArray.tryPick (fun cnr -> + match cnr.Item with + | Item.RecdField info when equals cnr.Range fieldRange -> + info.TyconRef.AllFieldAsRefList + |> List.choose (fun field -> + if + referencedFields + |> List.exists (fun (fieldName, _) -> fieldName = field.DisplayName) + then + None + else + FreshenRecdFieldRef ncenv field.Range field |> Item.RecdField |> Some) + |> Some + | _ -> None)) + |> Option.defaultWith (fun () -> + // Fall back to showing all record field names in scope + let (nenv, _), _ = GetBestEnvForPos pos + getRecordFieldsInScope nenv) + |> List.map (ItemWithNoInst >> DefaultCompletionItem) + + let items = + items + |> List.filter (fun item -> + match item.Item with + | Item.ModuleOrNamespaces _ -> true + | Item.Types (_, ty :: _) -> isRecdTy g ty + | _ -> false) + |> List.append fields + + items, denv, range) let GetDeclaredItems ( @@ -1316,6 +1407,22 @@ type internal TypeCheckInfo | atStart when atStart = 0 -> 0 | otherwise -> otherwise - 1 + let getDeclaredItemsNotInRangeOpWithAllSymbols () = + GetDeclaredItems( + parseResultsOpt, + lineStr, + origLongIdentOpt, + colAtEndOfNamesAndResidue, + residueOpt, + lastDotPos, + line, + loc, + filterCtors, + resolveOverloads, + false, + getAllSymbols + ) + let pos = mkPos line colAtEndOfNamesAndResidue // Look for a "special" completion context @@ -1445,21 +1552,7 @@ type internal TypeCheckInfo | Some (CompletionContext.ParameterList (endPos, fields)) -> let results = GetNamedParametersAndSettableFields endPos - let declaredItems = - GetDeclaredItems( - parseResultsOpt, - lineStr, - origLongIdentOpt, - colAtEndOfNamesAndResidue, - residueOpt, - lastDotPos, - line, - loc, - filterCtors, - resolveOverloads, - false, - getAllSymbols - ) + let declaredItems = getDeclaredItemsNotInRangeOpWithAllSymbols () match results with | NameResResult.Members (items, denv, m) -> @@ -1484,20 +1577,7 @@ type internal TypeCheckInfo | _ -> declaredItems | Some (CompletionContext.AttributeApplication) -> - GetDeclaredItems( - parseResultsOpt, - lineStr, - origLongIdentOpt, - colAtEndOfNamesAndResidue, - residueOpt, - lastDotPos, - line, - loc, - filterCtors, - resolveOverloads, - false, - getAllSymbols - ) + getDeclaredItemsNotInRangeOpWithAllSymbols () |> Option.map (fun (items, denv, m) -> items |> List.filter (fun cItem -> @@ -1509,20 +1589,7 @@ type internal TypeCheckInfo m) | Some (CompletionContext.OpenDeclaration isOpenType) -> - GetDeclaredItems( - parseResultsOpt, - lineStr, - origLongIdentOpt, - colAtEndOfNamesAndResidue, - residueOpt, - lastDotPos, - line, - loc, - filterCtors, - resolveOverloads, - false, - getAllSymbols - ) + getDeclaredItemsNotInRangeOpWithAllSymbols () |> Option.map (fun (items, denv, m) -> items |> List.filter (fun x -> @@ -1541,108 +1608,28 @@ type internal TypeCheckInfo | Some CompletionContext.TypeAbbreviationOrSingleCaseUnion // Completion at 'Field1: ...' | Some (CompletionContext.RecordField (RecordContext.Declaration false)) -> - GetDeclaredItems( - parseResultsOpt, - lineStr, - origLongIdentOpt, - colAtEndOfNamesAndResidue, - residueOpt, - lastDotPos, - line, - loc, - filterCtors, - resolveOverloads, - false, - getAllSymbols - ) - |> Option.map (fun (items, denv, m) -> - items - |> List.filter (fun cItem -> - match cItem.Item with - | Item.ModuleOrNamespaces _ - | Item.Types _ - | Item.TypeVar _ - | Item.UnqualifiedType _ - | Item.ExnCase _ -> true - | _ -> false), - denv, - m) - - | Some (CompletionContext.Pattern (PatternContext.UnionCaseFieldIdentifier (referencedFields, caseIdRange))) -> - GetUnionCaseFields caseIdRange referencedFields - |> Option.map (fun completions -> - let (nenv, _ad), m = GetBestEnvForPos pos - completions, nenv.DisplayEnv, m) + getDeclaredItemsNotInRangeOpWithAllSymbols () + |> Option.bind (FilterRelevantItemsBy getItem2 None IsTypeCandidate) | Some (CompletionContext.Pattern patternContext) -> - let declaredItems = - GetDeclaredItems( - parseResultsOpt, - lineStr, - origLongIdentOpt, - colAtEndOfNamesAndResidue, - residueOpt, - lastDotPos, - line, - loc, - filterCtors, - resolveOverloads, - false, - getAllSymbols - ) - |> Option.map (fun (items, denv, range) -> - let filtered = - items - |> List.filter (fun item -> - match item.Item with - | Item.Value v -> v.LiteralValue.IsSome - | Item.ILField field -> field.LiteralValue.IsSome - | Item.ActivePatternCase _ - | Item.ExnCase _ - | Item.ModuleOrNamespaces _ - | Item.NewDef _ - | Item.Types _ - | Item.UnionCase _ -> true - | _ -> false) - - filtered, denv, range) - - let indexOrName, caseIdRange = - match patternContext with - | PatternContext.PositionalUnionCaseField (index, _, m) -> Choice1Of2 index, m - | PatternContext.NamedUnionCaseField (name, m) -> Choice2Of2 name, m - | PatternContext.UnionCaseFieldIdentifier _ - | PatternContext.Other -> Choice1Of2 None, range0 - - // No special handling other than filtering out items that may not appear in a pattern - if equals caseIdRange range0 then - declaredItems - else - // When the user types `fun (Case (x| )) ->`, we do not yet know whether the intention is to use positional or named arguments, - // so let's show options for both. - let fields patternContext (uci: UnionCaseInfo) = - match patternContext with - | PatternContext.PositionalUnionCaseField (Some 0, true, _) -> - uci.UnionCase.RecdFields - |> List.mapi (fun index _ -> - Item.UnionCaseField(uci, index) - |> ItemWithNoInst - |> CompletionItem ValueNone ValueNone) - | _ -> [] - - sResolutions.CapturedNameResolutions - |> ResizeArray.tryPick (fun r -> - match r.Item with - | Item.UnionCase (uci, _) when equals r.Range caseIdRange -> - let list = - declaredItems - |> Option.map p13 - |> Option.defaultValue [] - |> List.append (fields patternContext uci) - - Some(SuggestNameForUnionCaseFieldPattern g caseIdRange.End pos uci indexOrName list, r.DisplayEnv, r.Range) - | _ -> None) - |> Option.orElse declaredItems + match patternContext with + | PatternContext.UnionCaseFieldIdentifier (referencedFields, caseIdRange) -> + GetUnionCaseFields caseIdRange referencedFields + |> Option.map (fun completions -> + let (nenv, _ad), m = GetBestEnvForPos pos + completions, nenv.DisplayEnv, m) + | PatternContext.PositionalUnionCaseField (fieldIndex, isTheOnlyField, caseIdRange) -> + getDeclaredItemsNotInRangeOpWithAllSymbols () + |> GetCompletionsForUnionCaseField pos (Choice1Of2 fieldIndex) caseIdRange isTheOnlyField + | PatternContext.NamedUnionCaseField (fieldName, caseIdRange) -> + getDeclaredItemsNotInRangeOpWithAllSymbols () + |> GetCompletionsForUnionCaseField pos (Choice2Of2 fieldName) caseIdRange false + | PatternContext.RecordFieldIdentifier referencedFields -> + getDeclaredItemsNotInRangeOpWithAllSymbols () + |> GetCompletionsForRecordField pos referencedFields + | PatternContext.Other -> + getDeclaredItemsNotInRangeOpWithAllSymbols () + |> Option.bind (FilterRelevantItemsBy getItem2 None IsPatternCandidate) | Some (CompletionContext.MethodOverride enclosingTypeNameRange) -> GetOverridableMethods pos enclosingTypeNameRange diff --git a/src/Compiler/Service/ServiceParsedInputOps.fs b/src/Compiler/Service/ServiceParsedInputOps.fs index 2a292e97639828fe8384165b831d887a1d4177f0..c1d2d6e0c4577ae0523dad95f888b9aab8f42795 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fs +++ b/src/Compiler/Service/ServiceParsedInputOps.fs @@ -64,6 +64,9 @@ type PatternContext = /// Completing union case field identifier in a pattern (e.g. fun (Case (field1 = a; fie| )) -> ) | UnionCaseFieldIdentifier of referencedFields: string list * caseIdRange: range + /// Completing a record field identifier in a pattern (e.g. fun { Field1 = a; Fie| } -> ) + | RecordFieldIdentifier of referencedFields: (string * range) list + /// Any other position in a pattern that does not need special handling | Other @@ -1310,10 +1313,28 @@ module ParsedInput = | _ -> pats |> List.tryPick (fun pat -> TryGetCompletionContextInPattern false pat None pos) + | SynPat.Record (fieldPats = pats) -> + pats + |> List.tryPick (fun ((_, fieldId), _, pat) -> + if rangeContainsPos fieldId.idRange pos then + let referencedFields = pats |> List.map (fun ((_, x), _, _) -> x.idText, x.idRange) + Some(CompletionContext.Pattern(PatternContext.RecordFieldIdentifier referencedFields)) + elif rangeContainsPos pat.Range pos then + TryGetCompletionContextInPattern false pat None pos + else + None) + |> Option.orElseWith (fun () -> + // Last resort - check for fun { Field1 = a; F| } -> + // That is, pos is after the last field and still within braces + if pats |> List.forall (fun (_, m, _) -> rangeBeforePos m pos) then + let referencedFields = pats |> List.map (fun ((_, x), _, _) -> x.idText, x.idRange) + Some(CompletionContext.Pattern(PatternContext.RecordFieldIdentifier referencedFields)) + else + None) | SynPat.Ands (pats = pats) | SynPat.ArrayOrList (elementPats = pats) -> pats - |> List.tryPick (fun pat -> TryGetCompletionContextInPattern suppressIdentifierCompletions pat None pos) + |> List.tryPick (fun pat -> TryGetCompletionContextInPattern false pat None pos) | SynPat.Tuple (elementPats = pats; commaRanges = commas; range = m) -> pats |> List.indexed diff --git a/src/Compiler/Service/ServiceParsedInputOps.fsi b/src/Compiler/Service/ServiceParsedInputOps.fsi index 877ca64a7f30538b59d23c5f216c8e22512d7a46..5b8d877a80eac37a9f2c8bc673f2000595d7214e 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fsi +++ b/src/Compiler/Service/ServiceParsedInputOps.fsi @@ -36,6 +36,9 @@ type public PatternContext = /// Completing union case field identifier in a pattern (e.g. fun (Case (field1 = a; fie| )) -> ) | UnionCaseFieldIdentifier of referencedFields: string list * caseIdRange: range + /// Completing a record field identifier in a pattern (e.g. fun { Field1 = a; Fie| } -> ) + | RecordFieldIdentifier of referencedFields: (string * range) list + /// Any other position in a pattern that does not need special handling | Other diff --git a/src/Compiler/TypedTree/TypedTree.fs b/src/Compiler/TypedTree/TypedTree.fs index a47b9db9228b476caaa7cb657368c76d59cd7e89..b82490c520972836092bb0e16dcff462689a0de6 100644 --- a/src/Compiler/TypedTree/TypedTree.fs +++ b/src/Compiler/TypedTree/TypedTree.fs @@ -4142,7 +4142,7 @@ type UnionCaseRef = type RecdFieldRef = | RecdFieldRef of tyconRef: TyconRef * fieldName: string - /// Get a reference to the type containing this union case + /// Get a reference to the type containing this record field member x.TyconRef = let (RecdFieldRef(tcref, _)) = x in tcref /// Get the name of the field @@ -4151,7 +4151,7 @@ type RecdFieldRef = /// Get the name of the field, with backticks added for non-identifier names member x.DisplayName = x.FieldName |> ConvertLogicalNameToDisplayName - /// Get the Entity for the type containing this union case + /// Get the Entity for the type containing this record field member x.Tycon = x.TyconRef.Deref /// Dereference the reference diff --git a/src/Compiler/TypedTree/TypedTree.fsi b/src/Compiler/TypedTree/TypedTree.fsi index 72b7efb1f6bf4fffdc09dffb06d5e6e6d1fd69e0..55af38d9b9e8a51cc72e10488989877b02fef5db 100644 --- a/src/Compiler/TypedTree/TypedTree.fsi +++ b/src/Compiler/TypedTree/TypedTree.fsi @@ -3001,10 +3001,10 @@ type RecdFieldRef = /// Try to dereference the reference member TryRecdField: RecdField voption - /// Get the Entity for the type containing this union case + /// Get the Entity for the type containing this record field member Tycon: Entity - /// Get a reference to the type containing this union case + /// Get a reference to the type containing this record field member TyconRef: TyconRef /// Represents a type in the typed abstract syntax diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index f1c1166d11b5baf08bd31e7a69d7153c03c17428..dd4fec52d42afd7b7f049dff3ba4e94faab2708c 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -3583,9 +3583,12 @@ FSharp.Compiler.EditorServices.PatternContext+PositionalUnionCaseField: FSharp.C FSharp.Compiler.EditorServices.PatternContext+PositionalUnionCaseField: FSharp.Compiler.Text.Range get_caseIdRange() FSharp.Compiler.EditorServices.PatternContext+PositionalUnionCaseField: Microsoft.FSharp.Core.FSharpOption`1[System.Int32] fieldIndex FSharp.Compiler.EditorServices.PatternContext+PositionalUnionCaseField: Microsoft.FSharp.Core.FSharpOption`1[System.Int32] get_fieldIndex() +FSharp.Compiler.EditorServices.PatternContext+RecordFieldIdentifier: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[System.String,FSharp.Compiler.Text.Range]] get_referencedFields() +FSharp.Compiler.EditorServices.PatternContext+RecordFieldIdentifier: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[System.String,FSharp.Compiler.Text.Range]] referencedFields FSharp.Compiler.EditorServices.PatternContext+Tags: Int32 NamedUnionCaseField FSharp.Compiler.EditorServices.PatternContext+Tags: Int32 Other FSharp.Compiler.EditorServices.PatternContext+Tags: Int32 PositionalUnionCaseField +FSharp.Compiler.EditorServices.PatternContext+Tags: Int32 RecordFieldIdentifier FSharp.Compiler.EditorServices.PatternContext+Tags: Int32 UnionCaseFieldIdentifier FSharp.Compiler.EditorServices.PatternContext+UnionCaseFieldIdentifier: FSharp.Compiler.Text.Range caseIdRange FSharp.Compiler.EditorServices.PatternContext+UnionCaseFieldIdentifier: FSharp.Compiler.Text.Range get_caseIdRange() @@ -3597,18 +3600,22 @@ FSharp.Compiler.EditorServices.PatternContext: Boolean Equals(System.Object, Sys FSharp.Compiler.EditorServices.PatternContext: Boolean IsNamedUnionCaseField FSharp.Compiler.EditorServices.PatternContext: Boolean IsOther FSharp.Compiler.EditorServices.PatternContext: Boolean IsPositionalUnionCaseField +FSharp.Compiler.EditorServices.PatternContext: Boolean IsRecordFieldIdentifier FSharp.Compiler.EditorServices.PatternContext: Boolean IsUnionCaseFieldIdentifier FSharp.Compiler.EditorServices.PatternContext: Boolean get_IsNamedUnionCaseField() FSharp.Compiler.EditorServices.PatternContext: Boolean get_IsOther() FSharp.Compiler.EditorServices.PatternContext: Boolean get_IsPositionalUnionCaseField() +FSharp.Compiler.EditorServices.PatternContext: Boolean get_IsRecordFieldIdentifier() FSharp.Compiler.EditorServices.PatternContext: Boolean get_IsUnionCaseFieldIdentifier() FSharp.Compiler.EditorServices.PatternContext: FSharp.Compiler.EditorServices.PatternContext NewNamedUnionCaseField(System.String, FSharp.Compiler.Text.Range) FSharp.Compiler.EditorServices.PatternContext: FSharp.Compiler.EditorServices.PatternContext NewPositionalUnionCaseField(Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Boolean, FSharp.Compiler.Text.Range) +FSharp.Compiler.EditorServices.PatternContext: FSharp.Compiler.EditorServices.PatternContext NewRecordFieldIdentifier(Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[System.String,FSharp.Compiler.Text.Range]]) FSharp.Compiler.EditorServices.PatternContext: FSharp.Compiler.EditorServices.PatternContext NewUnionCaseFieldIdentifier(Microsoft.FSharp.Collections.FSharpList`1[System.String], FSharp.Compiler.Text.Range) FSharp.Compiler.EditorServices.PatternContext: FSharp.Compiler.EditorServices.PatternContext Other FSharp.Compiler.EditorServices.PatternContext: FSharp.Compiler.EditorServices.PatternContext get_Other() FSharp.Compiler.EditorServices.PatternContext: FSharp.Compiler.EditorServices.PatternContext+NamedUnionCaseField FSharp.Compiler.EditorServices.PatternContext: FSharp.Compiler.EditorServices.PatternContext+PositionalUnionCaseField +FSharp.Compiler.EditorServices.PatternContext: FSharp.Compiler.EditorServices.PatternContext+RecordFieldIdentifier FSharp.Compiler.EditorServices.PatternContext: FSharp.Compiler.EditorServices.PatternContext+Tags FSharp.Compiler.EditorServices.PatternContext: FSharp.Compiler.EditorServices.PatternContext+UnionCaseFieldIdentifier FSharp.Compiler.EditorServices.PatternContext: Int32 GetHashCode() diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index f1c1166d11b5baf08bd31e7a69d7153c03c17428..dd4fec52d42afd7b7f049dff3ba4e94faab2708c 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -3583,9 +3583,12 @@ FSharp.Compiler.EditorServices.PatternContext+PositionalUnionCaseField: FSharp.C FSharp.Compiler.EditorServices.PatternContext+PositionalUnionCaseField: FSharp.Compiler.Text.Range get_caseIdRange() FSharp.Compiler.EditorServices.PatternContext+PositionalUnionCaseField: Microsoft.FSharp.Core.FSharpOption`1[System.Int32] fieldIndex FSharp.Compiler.EditorServices.PatternContext+PositionalUnionCaseField: Microsoft.FSharp.Core.FSharpOption`1[System.Int32] get_fieldIndex() +FSharp.Compiler.EditorServices.PatternContext+RecordFieldIdentifier: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[System.String,FSharp.Compiler.Text.Range]] get_referencedFields() +FSharp.Compiler.EditorServices.PatternContext+RecordFieldIdentifier: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[System.String,FSharp.Compiler.Text.Range]] referencedFields FSharp.Compiler.EditorServices.PatternContext+Tags: Int32 NamedUnionCaseField FSharp.Compiler.EditorServices.PatternContext+Tags: Int32 Other FSharp.Compiler.EditorServices.PatternContext+Tags: Int32 PositionalUnionCaseField +FSharp.Compiler.EditorServices.PatternContext+Tags: Int32 RecordFieldIdentifier FSharp.Compiler.EditorServices.PatternContext+Tags: Int32 UnionCaseFieldIdentifier FSharp.Compiler.EditorServices.PatternContext+UnionCaseFieldIdentifier: FSharp.Compiler.Text.Range caseIdRange FSharp.Compiler.EditorServices.PatternContext+UnionCaseFieldIdentifier: FSharp.Compiler.Text.Range get_caseIdRange() @@ -3597,18 +3600,22 @@ FSharp.Compiler.EditorServices.PatternContext: Boolean Equals(System.Object, Sys FSharp.Compiler.EditorServices.PatternContext: Boolean IsNamedUnionCaseField FSharp.Compiler.EditorServices.PatternContext: Boolean IsOther FSharp.Compiler.EditorServices.PatternContext: Boolean IsPositionalUnionCaseField +FSharp.Compiler.EditorServices.PatternContext: Boolean IsRecordFieldIdentifier FSharp.Compiler.EditorServices.PatternContext: Boolean IsUnionCaseFieldIdentifier FSharp.Compiler.EditorServices.PatternContext: Boolean get_IsNamedUnionCaseField() FSharp.Compiler.EditorServices.PatternContext: Boolean get_IsOther() FSharp.Compiler.EditorServices.PatternContext: Boolean get_IsPositionalUnionCaseField() +FSharp.Compiler.EditorServices.PatternContext: Boolean get_IsRecordFieldIdentifier() FSharp.Compiler.EditorServices.PatternContext: Boolean get_IsUnionCaseFieldIdentifier() FSharp.Compiler.EditorServices.PatternContext: FSharp.Compiler.EditorServices.PatternContext NewNamedUnionCaseField(System.String, FSharp.Compiler.Text.Range) FSharp.Compiler.EditorServices.PatternContext: FSharp.Compiler.EditorServices.PatternContext NewPositionalUnionCaseField(Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Boolean, FSharp.Compiler.Text.Range) +FSharp.Compiler.EditorServices.PatternContext: FSharp.Compiler.EditorServices.PatternContext NewRecordFieldIdentifier(Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[System.String,FSharp.Compiler.Text.Range]]) FSharp.Compiler.EditorServices.PatternContext: FSharp.Compiler.EditorServices.PatternContext NewUnionCaseFieldIdentifier(Microsoft.FSharp.Collections.FSharpList`1[System.String], FSharp.Compiler.Text.Range) FSharp.Compiler.EditorServices.PatternContext: FSharp.Compiler.EditorServices.PatternContext Other FSharp.Compiler.EditorServices.PatternContext: FSharp.Compiler.EditorServices.PatternContext get_Other() FSharp.Compiler.EditorServices.PatternContext: FSharp.Compiler.EditorServices.PatternContext+NamedUnionCaseField FSharp.Compiler.EditorServices.PatternContext: FSharp.Compiler.EditorServices.PatternContext+PositionalUnionCaseField +FSharp.Compiler.EditorServices.PatternContext: FSharp.Compiler.EditorServices.PatternContext+RecordFieldIdentifier FSharp.Compiler.EditorServices.PatternContext: FSharp.Compiler.EditorServices.PatternContext+Tags FSharp.Compiler.EditorServices.PatternContext: FSharp.Compiler.EditorServices.PatternContext+UnionCaseFieldIdentifier FSharp.Compiler.EditorServices.PatternContext: Int32 GetHashCode() diff --git a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs index e8294caff85ca6fdac9c3ed0ade38d31945f443d..487bb0d3323cf0249625b5b2ec4f9b787a45366c 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs @@ -1763,7 +1763,7 @@ match U1 (1, A) with VerifyCompletionList(fileContents, "| U1 (x, y", [ "yyy"; "tab" ], [ "xxx"; "num"; "fff" ]) [] - let ``Completion list for union case field identifier contains available fields`` () = + let ``Completion list for union case field identifier in a pattern contains available fields`` () = let fileContents = """ type PatternContext = @@ -1921,3 +1921,41 @@ type C () = VerifyNoCompletionList(fileContents, "override a") VerifyNoCompletionList(fileContents, "override _") VerifyNoCompletionList(fileContents, "override c") + + [] + let ``Completion list for record field identifier in a pattern contains available fields, modules, namespaces and record types`` () = + let fileContents = + """ +open System + +type DU = + | X + +type R1 = { A: int; B: int } +type R2 = { C: int; D: int } + +match [] with +| [ { A = 2; l = 2 } ] +""" + + VerifyCompletionList( + fileContents, + "| [ { A = 2; l", + [ "B"; "R1"; "R2"; "System"; "LanguagePrimitives" ], + [ "A"; "C"; "D"; "DU"; "X"; "log"; "let"; "Lazy" ] + ) + + [] + let ``Completion list for record field identifier in a pattern contains fields of all records in scope when the record type is not known yet`` + () + = + let fileContents = + """ +type R1 = { A: int; B: int } +type R2 = { C: int; D: int } + +match { A = 1; B = 2 } with +| { f = () } +""" + + VerifyCompletionList(fileContents, "| { f = ()", [ "A"; "B"; "C"; "D" ], [])