Skip to content

Commit 04c7443

Browse files
authored
Merge branch 'main' into async2
2 parents 6f0fa7c + 694c512 commit 04c7443

File tree

3 files changed

+70
-21
lines changed

3 files changed

+70
-21
lines changed

docs/release-notes/.FSharp.Compiler.Service/10.0.100.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
* Caches: type subsumption cache key perf regression ([Issue #18925](https://github.com/dotnet/fsharp/issues/18925) [PR #18926](https://github.com/dotnet/fsharp/pull/18926))
3737
* Ensure that line directives are applied to source identifiers (issue [#18908](https://github.com/dotnet/fsharp/issues/18908), PR [#18918](https://github.com/dotnet/fsharp/pull/18918))
3838
* Fix expected and actual types in ErrorFromAddingTypeEquation message and extended diagnostic data. ([PR #18915](https://github.com/dotnet/fsharp/pull/18915))
39+
* Editor: Fix Record fields completion in update record with partial field name. ([PR #18946](https://github.com/dotnet/fsharp/pull/18946))
3940

4041
### Changed
4142
* Use `errorR` instead of `error` in `CheckDeclarations.fs` when possible. ([PR #18645](https://github.com/dotnet/fsharp/pull/18645))

src/Compiler/Service/ServiceParseTreeWalk.fs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,17 @@ module SyntaxTraversal =
528528
for SynExprRecordField(fieldName = (field, _); expr = e; blockSeparator = sepOpt) in fields do
529529
yield
530530
dive (path, copyOpt, Some field) field.Range (fun r ->
531-
if rangeContainsPos field.Range pos then
531+
// Treat the caret placed right after the field name (before '=' or a value) as "inside" the field,
532+
// but only if the field does not yet have a value.
533+
//
534+
// Examples (the '$' marks the caret):
535+
// { r with Field1$ }
536+
// { r with
537+
// Field1$
538+
// }
539+
let isCaretAfterFieldNameWithoutValue = (e.IsNone && posEq pos field.Range.End)
540+
541+
if rangeContainsPos field.Range pos || isCaretAfterFieldNameWithoutValue then
532542
visitor.VisitRecordField r
533543
else
534544
None)

tests/FSharp.Compiler.Service.Tests/EditorTests.fs

Lines changed: 58 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1937,6 +1937,15 @@ let hasRecordType (recordTypeName: string) (symbolUses: FSharpSymbolUse list) =
19371937
| _ -> false
19381938
)
19391939
|> fun exists -> Assert.True(exists, $"Record type {recordTypeName} not found.")
1940+
1941+
let private assertItemsWithNames contains names (completionInfo: DeclarationListInfo) =
1942+
let itemNames = completionInfo.Items |> Array.map _.NameInCode |> set
1943+
1944+
for name in names do
1945+
Assert.True(Set.contains name itemNames = contains)
1946+
1947+
let assertHasItemWithNames names (completionInfo: DeclarationListInfo) =
1948+
assertItemsWithNames true names completionInfo
19401949

19411950
[<Fact>]
19421951
let ``Record fields are completed via type name usage`` () =
@@ -2113,34 +2122,63 @@ let rUpdate = { r1 with }
21132122
hasRecordField "Field1" declarations
21142123
hasRecordField "Field2" declarations
21152124

2116-
[<Fact(Skip = "Current fails to suggest any record fields")>]
2125+
[<Fact>]
21172126
let ``Record fields are completed in update record with partial field name`` () =
2118-
let parseResults, checkResults =
2119-
getParseAndCheckResults """
2127+
let info = Checker.getCompletionInfo """
21202128
module Module
21212129
21222130
type R1 =
21232131
{ Field1: int; Field2: int }
21242132
21252133
let r1 = { Field1 = 1; Field2 = 2 }
21262134
2127-
let rUpdate = { r1 with Fi }
2135+
let rUpdate = { r1 with Fi{caret} }
21282136
"""
21292137

2130-
let declarations =
2131-
checkResults.GetDeclarationListSymbols(
2132-
Some parseResults,
2133-
9,
2134-
"let rUpdate = { r1 with Fi }",
2135-
{
2136-
EndColumn = 26
2137-
LastDotPos = None
2138-
PartialIdent = "Fi"
2139-
QualifyingIdents = []
2140-
},
2141-
fun _ -> List.empty
2142-
)
2143-
|> List.concat
2138+
assertHasItemWithNames ["Field1"; "Field2"] info
21442139

2145-
hasRecordField "Field1" declarations
2146-
hasRecordField "Field2" declarations
2140+
[<Fact>]
2141+
let ``Multiline record fields are completed in update record with partial field name`` () =
2142+
let info = Checker.getCompletionInfo """
2143+
module Module
2144+
2145+
type R1 =
2146+
{ Field1: int; Field2: int }
2147+
2148+
let r1 = { Field1 = 1; Field2 = 2 }
2149+
2150+
let rUpdate =
2151+
{ r1 with
2152+
Fi{caret}
2153+
}
2154+
"""
2155+
2156+
assertHasItemWithNames ["Field1"; "Field2"] info
2157+
2158+
[<Fact>]
2159+
let ``No record field completion after '=' with missing value in first binding (after =;)`` () =
2160+
let info = Checker.getCompletionInfo """
2161+
module M
2162+
2163+
type X =
2164+
val field1: int
2165+
val field2: string
2166+
2167+
let x = new X()
2168+
let _ = { field1 =; {caret} }
2169+
"""
2170+
assertItemsWithNames false ["field1"; "field2"] info
2171+
2172+
[<Fact>]
2173+
let ``No record field completion after '=' with missing value in first binding (after =; f)`` () =
2174+
let info = Checker.getCompletionInfo """
2175+
module M
2176+
2177+
type X =
2178+
val field1: int
2179+
val field2: string
2180+
2181+
let x = new X()
2182+
let _ = { field1 =; f{caret} }
2183+
"""
2184+
assertItemsWithNames false ["field1"; "field2"] info

0 commit comments

Comments
 (0)