Skip to content

Commit

Permalink
Capture index without dot inside a chain. (#2764)
Browse files Browse the repository at this point in the history
* Capture index without dot inside a chain.

* Add changelog entry.
  • Loading branch information
nojaf authored Feb 4, 2023
1 parent 18f3154 commit 9f769d8
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 17 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# Changelog

## [Unreleased]
## [5.2.1] - 2023-02-04

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

### Changed
* Update FCS to 'Add SynMemberDefnImplicitCtorTrivia', commit 924a64e8e40c840f05fbe7113796f267dd603282
Expand Down
51 changes: 51 additions & 0 deletions src/Fantomas.Core.Tests/ChainTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -387,3 +387,54 @@ Fooooooooooo.Baaaaaaaaaaaaaaaaar
.Moooooooooooooooo.Booooooooooooooooooooh
.Yooooooooooooooou.Meeeeeeh.Meh2
"""

[<Test>]
let ``dot get with index without dot expression , 2761`` () =
formatSourceString
false
"""
x().y[0].zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
"""
config
|> prepend newline
|> should
equal
"""
x().y[0].zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
"""

[<Test>]
let ``don't add extra space in index without dot expression, 2760`` () =
formatSourceString
false
"""
x().y[0].z // spaces inserted around index
x().y.[0].z // no spaces inserted
x().y[0] // no spaces inserted
x.y[0].z // no spaces inserted
"""
config
|> prepend newline
|> should
equal
"""
x().y[0].z // spaces inserted around index
x().y.[0].z // no spaces inserted
x().y[0] // no spaces inserted
x.y[0].z // no spaces inserted
"""

[<Test>]
let ``multiple idents in dotget with index without dot`` () =
formatSourceString
false
"""
v().w.x.y.z['a'].b
"""
config
|> prepend newline
|> should
equal
"""
v().w.x.y.z['a'].b
"""
74 changes: 58 additions & 16 deletions src/Fantomas.Core/ASTTransformer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,18 @@ let (|InfixApp|_|) synExpr =
argExpr = e2) -> Some(e1, stn operator operatorIdent.idRange, e2)
| _ -> None

let (|IndexWithoutDot|_|) expr =
match expr with
| SynExpr.App(ExprAtomicFlag.Atomic, false, identifierExpr, SynExpr.ArrayOrListComputed(false, indexExpr, _), _) ->
Some(identifierExpr, indexExpr)
| SynExpr.App(ExprAtomicFlag.NonAtomic,
false,
identifierExpr,
(SynExpr.ArrayOrListComputed(isArray = false; expr = indexExpr) as argExpr),
_) when (RangeHelpers.isAdjacentTo identifierExpr.Range argExpr.Range) ->
Some(identifierExpr, indexExpr)
| _ -> None

let (|MultipleConsInfixApps|_|) expr =
let rec visit expr (headAndLastOperator: (SynExpr * SingleTextNode) option) (xs: Queue<SingleTextNode * SynExpr>) =
match expr with
Expand Down Expand Up @@ -636,8 +648,7 @@ let mkLinksFromFunctionName (mkLinkFromExpr: SynExpr -> LinkExpr) (functionName:
m
)

[ yield! List.take (leftLinks.Length - 1) leftLinks
yield mkLinkFromExpr typeAppExpr ]
[ yield! List.cutOffLast leftLinks; yield mkLinkFromExpr typeAppExpr ]

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

[ yield! List.take (leftLinks.Length - 1) leftLinks
[ yield! List.cutOffLast leftLinks
yield (mkLongIdentExprFromSynIdent lastSynIdent |> mkLinkFromExpr) ]
| e -> [ mkLinkFromExpr e ]

Expand Down Expand Up @@ -685,7 +696,7 @@ let (|ChainExpr|_|) (e: SynExpr) : LinkExpr list option =
| _ -> []
| _ -> []

let leftLinks = List.take (leftLinks.Length - 1) leftLinks
let leftLinks = List.cutOffLast leftLinks

continuation [ yield! leftLinks; yield! lastLink ])

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

let leftLinks = List.take (leftLinks.Length - 1) leftLinks
let leftLinks = List.cutOffLast leftLinks
continuation [ yield! leftLinks; yield! lastLink ])

// Transform `x().y[0]` into `x()` , `dot`, `y[0]`
| IndexWithoutDot(SynExpr.DotGet(expr, mDot, sli, _), indexExpr) ->
visit expr (fun leftLinks ->
let middleLinks, lastExpr =
match List.tryLast sli.IdentsWithTrivia with
| None -> [], indexExpr
| Some lastMiddleLink ->
let middleLinks = mkLinksFromSynLongIdent sli |> List.cutOffLast

let indexWithDotExpr =
let identifierExpr = mkLongIdentExprFromSynIdent lastMiddleLink

// Create an adjacent range for the `[`,`]` in the index expression.
let adjacentRange =
mkRange
indexExpr.Range.FileName
(Position.mkPos
identifierExpr.Range.StartLine
(identifierExpr.Range.StartColumn + 1))
(Position.mkPos indexExpr.Range.EndLine (indexExpr.Range.EndColumn - 1))

SynExpr.App(
ExprAtomicFlag.Atomic,
false,
identifierExpr,
SynExpr.ArrayOrListComputed(false, indexExpr, adjacentRange),
unionRanges identifierExpr.Range indexExpr.Range
)

middleLinks, indexWithDotExpr

continuation
[ yield! leftLinks
yield LinkExpr.Dot mDot
yield! middleLinks
yield LinkExpr.Expr lastExpr ])

| SynExpr.App(isInfix = false; funcExpr = SynExpr.DotGet _ as funcExpr; argExpr = argExpr) ->
visit funcExpr (fun leftLinks ->
match List.tryLast leftLinks with
| Some(LinkExpr.Identifier(identifierExpr)) ->
match argExpr with
| UnitExpr mUnit ->
let leftLinks = List.take (leftLinks.Length - 1) leftLinks
let leftLinks = List.cutOffLast leftLinks

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

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

let leftLinks = List.take (leftLinks.Length - 1) leftLinks
let leftLinks = List.cutOffLast leftLinks
continuation [ yield! leftLinks; yield! app ])

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

| SynExpr.App(ExprAtomicFlag.Atomic, false, identifierExpr, SynExpr.ArrayOrListComputed(false, indexExpr, _), _) ->
ExprIndexWithoutDotNode(mkExpr creationAide identifierExpr, mkExpr creationAide indexExpr, exprRange)
|> Expr.IndexWithoutDot
| SynExpr.App(ExprAtomicFlag.NonAtomic,
false,
identifierExpr,
(SynExpr.ArrayOrListComputed(isArray = false; expr = indexExpr) as argExpr),
_) when (RangeHelpers.isAdjacentTo identifierExpr.Range argExpr.Range) ->
| IndexWithoutDot(identifierExpr, indexExpr) ->
ExprIndexWithoutDotNode(mkExpr creationAide identifierExpr, mkExpr creationAide indexExpr, exprRange)
|> Expr.IndexWithoutDot

Expand Down
15 changes: 15 additions & 0 deletions src/Fantomas.Core/Utils.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ namespace Fantomas.Core

open System
open System.Text.RegularExpressions
open Microsoft.FSharp.Core.CompilerServices

[<RequireQualifiedAccess>]
module String =
Expand Down Expand Up @@ -102,6 +103,20 @@ module List =

visit xs id

let cutOffLast list =
let mutable headList = ListCollector<'a>()

let rec visit list =
match list with
| []
| [ _ ] -> ()
| head :: tail ->
headList.Add(head)
visit tail

visit list
headList.Close()

module Async =
let map f computation =
async.Bind(computation, f >> async.Return)
Expand Down
2 changes: 2 additions & 0 deletions src/Fantomas.Core/Utils.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ module List =
val moreThanOne: ('a list -> bool)
val partitionWhile: f: (int -> 'a -> bool) -> xs: 'a list -> 'a list * 'a list
val mapWithLast: f: ('a -> 'b) -> g: ('a -> 'b) -> xs: 'a list -> 'b list
/// Removes the last element of a list
val cutOffLast: 'a list -> 'a list

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

0 comments on commit 9f769d8

Please sign in to comment.