Skip to content

Commit 9f769d8

Browse files
authored
Capture index without dot inside a chain. (#2764)
* Capture index without dot inside a chain. * Add changelog entry.
1 parent 18f3154 commit 9f769d8

File tree

5 files changed

+129
-17
lines changed

5 files changed

+129
-17
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
# Changelog
22

3-
## [Unreleased]
3+
## [5.2.1] - 2023-02-04
44

55
### Fixed
66
* Conditional defines around selfIdentifier in implicit type constructor. [#2733](https://github.com/fsprojects/fantomas/issues/2733)
7+
* Insert extra spaces around index between method calling and member variable accessing. [#2760](https://github.com/fsprojects/fantomas/issues/2760)
8+
* Exception caused by long line over 80 characters including method calling and member indexing. [#2761](https://github.com/fsprojects/fantomas/issues/2761)
79

810
### Changed
911
* Update FCS to 'Add SynMemberDefnImplicitCtorTrivia', commit 924a64e8e40c840f05fbe7113796f267dd603282

src/Fantomas.Core.Tests/ChainTests.fs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,3 +387,54 @@ Fooooooooooo.Baaaaaaaaaaaaaaaaar
387387
.Moooooooooooooooo.Booooooooooooooooooooh
388388
.Yooooooooooooooou.Meeeeeeh.Meh2
389389
"""
390+
391+
[<Test>]
392+
let ``dot get with index without dot expression , 2761`` () =
393+
formatSourceString
394+
false
395+
"""
396+
x().y[0].zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
397+
"""
398+
config
399+
|> prepend newline
400+
|> should
401+
equal
402+
"""
403+
x().y[0].zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
404+
"""
405+
406+
[<Test>]
407+
let ``don't add extra space in index without dot expression, 2760`` () =
408+
formatSourceString
409+
false
410+
"""
411+
x().y[0].z // spaces inserted around index
412+
x().y.[0].z // no spaces inserted
413+
x().y[0] // no spaces inserted
414+
x.y[0].z // no spaces inserted
415+
"""
416+
config
417+
|> prepend newline
418+
|> should
419+
equal
420+
"""
421+
x().y[0].z // spaces inserted around index
422+
x().y.[0].z // no spaces inserted
423+
x().y[0] // no spaces inserted
424+
x.y[0].z // no spaces inserted
425+
"""
426+
427+
[<Test>]
428+
let ``multiple idents in dotget with index without dot`` () =
429+
formatSourceString
430+
false
431+
"""
432+
v().w.x.y.z['a'].b
433+
"""
434+
config
435+
|> prepend newline
436+
|> should
437+
equal
438+
"""
439+
v().w.x.y.z['a'].b
440+
"""

src/Fantomas.Core/ASTTransformer.fs

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,18 @@ let (|InfixApp|_|) synExpr =
389389
argExpr = e2) -> Some(e1, stn operator operatorIdent.idRange, e2)
390390
| _ -> None
391391

392+
let (|IndexWithoutDot|_|) expr =
393+
match expr with
394+
| SynExpr.App(ExprAtomicFlag.Atomic, false, identifierExpr, SynExpr.ArrayOrListComputed(false, indexExpr, _), _) ->
395+
Some(identifierExpr, indexExpr)
396+
| SynExpr.App(ExprAtomicFlag.NonAtomic,
397+
false,
398+
identifierExpr,
399+
(SynExpr.ArrayOrListComputed(isArray = false; expr = indexExpr) as argExpr),
400+
_) when (RangeHelpers.isAdjacentTo identifierExpr.Range argExpr.Range) ->
401+
Some(identifierExpr, indexExpr)
402+
| _ -> None
403+
392404
let (|MultipleConsInfixApps|_|) expr =
393405
let rec visit expr (headAndLastOperator: (SynExpr * SingleTextNode) option) (xs: Queue<SingleTextNode * SynExpr>) =
394406
match expr with
@@ -636,8 +648,7 @@ let mkLinksFromFunctionName (mkLinkFromExpr: SynExpr -> LinkExpr) (functionName:
636648
m
637649
)
638650

639-
[ yield! List.take (leftLinks.Length - 1) leftLinks
640-
yield mkLinkFromExpr typeAppExpr ]
651+
[ yield! List.cutOffLast leftLinks; yield mkLinkFromExpr typeAppExpr ]
641652

642653
| SynExpr.LongIdent(longDotId = sli) ->
643654
match sli.IdentsWithTrivia with
@@ -647,7 +658,7 @@ let mkLinksFromFunctionName (mkLinkFromExpr: SynExpr -> LinkExpr) (functionName:
647658
let leftLinks = mkLinksFromSynLongIdent sli
648659
let lastSynIdent = List.last synIdents
649660

650-
[ yield! List.take (leftLinks.Length - 1) leftLinks
661+
[ yield! List.cutOffLast leftLinks
651662
yield (mkLongIdentExprFromSynIdent lastSynIdent |> mkLinkFromExpr) ]
652663
| e -> [ mkLinkFromExpr e ]
653664

@@ -685,7 +696,7 @@ let (|ChainExpr|_|) (e: SynExpr) : LinkExpr list option =
685696
| _ -> []
686697
| _ -> []
687698

688-
let leftLinks = List.take (leftLinks.Length - 1) leftLinks
699+
let leftLinks = List.cutOffLast leftLinks
689700

690701
continuation [ yield! leftLinks; yield! lastLink ])
691702

@@ -712,15 +723,53 @@ let (|ChainExpr|_|) (e: SynExpr) : LinkExpr list option =
712723
|> LinkExpr.Identifier ]
713724
| _ -> []
714725

715-
let leftLinks = List.take (leftLinks.Length - 1) leftLinks
726+
let leftLinks = List.cutOffLast leftLinks
716727
continuation [ yield! leftLinks; yield! lastLink ])
728+
729+
// Transform `x().y[0]` into `x()` , `dot`, `y[0]`
730+
| IndexWithoutDot(SynExpr.DotGet(expr, mDot, sli, _), indexExpr) ->
731+
visit expr (fun leftLinks ->
732+
let middleLinks, lastExpr =
733+
match List.tryLast sli.IdentsWithTrivia with
734+
| None -> [], indexExpr
735+
| Some lastMiddleLink ->
736+
let middleLinks = mkLinksFromSynLongIdent sli |> List.cutOffLast
737+
738+
let indexWithDotExpr =
739+
let identifierExpr = mkLongIdentExprFromSynIdent lastMiddleLink
740+
741+
// Create an adjacent range for the `[`,`]` in the index expression.
742+
let adjacentRange =
743+
mkRange
744+
indexExpr.Range.FileName
745+
(Position.mkPos
746+
identifierExpr.Range.StartLine
747+
(identifierExpr.Range.StartColumn + 1))
748+
(Position.mkPos indexExpr.Range.EndLine (indexExpr.Range.EndColumn - 1))
749+
750+
SynExpr.App(
751+
ExprAtomicFlag.Atomic,
752+
false,
753+
identifierExpr,
754+
SynExpr.ArrayOrListComputed(false, indexExpr, adjacentRange),
755+
unionRanges identifierExpr.Range indexExpr.Range
756+
)
757+
758+
middleLinks, indexWithDotExpr
759+
760+
continuation
761+
[ yield! leftLinks
762+
yield LinkExpr.Dot mDot
763+
yield! middleLinks
764+
yield LinkExpr.Expr lastExpr ])
765+
717766
| SynExpr.App(isInfix = false; funcExpr = SynExpr.DotGet _ as funcExpr; argExpr = argExpr) ->
718767
visit funcExpr (fun leftLinks ->
719768
match List.tryLast leftLinks with
720769
| Some(LinkExpr.Identifier(identifierExpr)) ->
721770
match argExpr with
722771
| UnitExpr mUnit ->
723-
let leftLinks = List.take (leftLinks.Length - 1) leftLinks
772+
let leftLinks = List.cutOffLast leftLinks
724773

725774
// Compose a function application by taking the last identifier of the SynExpr.DotGet
726775
// and the following argument expression.
@@ -730,7 +779,7 @@ let (|ChainExpr|_|) (e: SynExpr) : LinkExpr list option =
730779
continuation [ yield! leftLinks; yield rightLink ]
731780

732781
| ParenExpr(lpr, e, rpr, pr) ->
733-
let leftLinks = List.take (leftLinks.Length - 1) leftLinks
782+
let leftLinks = List.cutOffLast leftLinks
734783
// Example: A().B(fun b -> b)
735784
let rightLink = LinkExpr.AppParen(identifierExpr, lpr, e, rpr, pr)
736785
continuation [ yield! leftLinks; yield rightLink ]
@@ -772,7 +821,7 @@ let (|ChainExpr|_|) (e: SynExpr) : LinkExpr list option =
772821
|> LinkExpr.Expr ]
773822
| _ -> []
774823

775-
let leftLinks = List.take (leftLinks.Length - 1) leftLinks
824+
let leftLinks = List.cutOffLast leftLinks
776825
continuation [ yield! leftLinks; yield! app ])
777826

778827
| SynExpr.TypeApp _ as typeApp -> mkLinksFromFunctionName LinkExpr.Identifier typeApp |> continuation
@@ -1104,14 +1153,7 @@ let mkExpr (creationAide: CreationAide) (e: SynExpr) : Expr =
11041153
ExprInfixAppNode(mkExpr creationAide e1, operator, mkExpr creationAide e2, exprRange)
11051154
|> Expr.InfixApp
11061155

1107-
| SynExpr.App(ExprAtomicFlag.Atomic, false, identifierExpr, SynExpr.ArrayOrListComputed(false, indexExpr, _), _) ->
1108-
ExprIndexWithoutDotNode(mkExpr creationAide identifierExpr, mkExpr creationAide indexExpr, exprRange)
1109-
|> Expr.IndexWithoutDot
1110-
| SynExpr.App(ExprAtomicFlag.NonAtomic,
1111-
false,
1112-
identifierExpr,
1113-
(SynExpr.ArrayOrListComputed(isArray = false; expr = indexExpr) as argExpr),
1114-
_) when (RangeHelpers.isAdjacentTo identifierExpr.Range argExpr.Range) ->
1156+
| IndexWithoutDot(identifierExpr, indexExpr) ->
11151157
ExprIndexWithoutDotNode(mkExpr creationAide identifierExpr, mkExpr creationAide indexExpr, exprRange)
11161158
|> Expr.IndexWithoutDot
11171159

src/Fantomas.Core/Utils.fs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ namespace Fantomas.Core
22

33
open System
44
open System.Text.RegularExpressions
5+
open Microsoft.FSharp.Core.CompilerServices
56

67
[<RequireQualifiedAccess>]
78
module String =
@@ -102,6 +103,20 @@ module List =
102103

103104
visit xs id
104105

106+
let cutOffLast list =
107+
let mutable headList = ListCollector<'a>()
108+
109+
let rec visit list =
110+
match list with
111+
| []
112+
| [ _ ] -> ()
113+
| head :: tail ->
114+
headList.Add(head)
115+
visit tail
116+
117+
visit list
118+
headList.Close()
119+
105120
module Async =
106121
let map f computation =
107122
async.Bind(computation, f >> async.Return)

src/Fantomas.Core/Utils.fsi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ module List =
1515
val moreThanOne: ('a list -> bool)
1616
val partitionWhile: f: (int -> 'a -> bool) -> xs: 'a list -> 'a list * 'a list
1717
val mapWithLast: f: ('a -> 'b) -> g: ('a -> 'b) -> xs: 'a list -> 'b list
18+
/// Removes the last element of a list
19+
val cutOffLast: 'a list -> 'a list
1820

1921
module Async =
2022
val map: f: ('a -> 'b) -> computation: Async<'a> -> Async<'b>

0 commit comments

Comments
 (0)