Skip to content

pretty printer can break match expression into multiple lines, breaking edit round-trip #1744

@samgqroberts

Description

@samgqroberts

I have the following function in my codebase:

._rummikub.trunk.engine> view newGame

  newGame : Nat -> GameConfig -> Either GameCreationError GameState
  newGame seed config =
    match config with
      GameConfig numPlayers tileDuplicates startingPlayerTileCount initialPlayPointMinimum ->
        use Universal <
        match numPlayers with
          x | x < 1 -> Left ZeroPlayers
          _         ->
            match tileDuplicates with
              x | x < 1 -> Left ZeroTiles
              _         ->
                use Nat *
                use Universal >
                allTiles = generateAllTiles tileDuplicates
                numTilesTotal = List.size allTiles
                match numPlayers * startingPlayerTileCount with
                  x | x > numTilesTotal -> Left CannotAllocateEnoughTiles
                  _                     ->
                    shuffledTiles = shuffleList seed allTiles
                    unflippedAndPlayerHands =
                      List.foldl
                        (acc _ ->
                          (match acc with
                            (currUnflipped, currPlayerHands) ->
                              match takeTiles currUnflipped startingPlayerTileCount with
                                (newUnflipped, newPlayerHand) ->
                                  (newUnflipped, newPlayerHand +: currPlayerHands)))
                        (shuffledTiles, [])
                        (List.range 0 numPlayers)
                    match unflippedAndPlayerHands with
                      (unflipped, playerHands) ->
                        board = []
                        playerStates = List.map (hand -> newPlayer hand) playerHands
                        playerTurn = 0
                        Right (GameState unflipped board playerStates playerTurn config)

Note that the match takeTiles currUnflipped startingPlayerTileCount with does not have a line break in the match expression.

For some reason when I edit this term out, the following gets written:

newGame : Nat -> GameConfig -> Either GameCreationError GameState
newGame seed config =
  match config with
    GameConfig
      numPlayers tileDuplicates startingPlayerTileCount initialPlayPointMinimum ->
      use Universal <
      match numPlayers with
        x | x < 1 -> Left ZeroPlayers
        _         ->
          match tileDuplicates with
            x | x < 1 -> Left ZeroTiles
            _         ->
              use Nat *
              use Universal >
              allTiles = generateAllTiles tileDuplicates
              numTilesTotal = List.size allTiles
              match numPlayers * startingPlayerTileCount with
                x | x > numTilesTotal -> Left CannotAllocateEnoughTiles
                _                     ->
                  shuffledTiles = shuffleList seed allTiles
                  unflippedAndPlayerHands =
                    List.foldl
                      (acc _ ->
                        (match acc with
                          (currUnflipped, currPlayerHands) ->
                            match takeTiles
                              currUnflipped startingPlayerTileCount with
                              (newUnflipped, newPlayerHand) ->
                                (newUnflipped,
                                newPlayerHand +: currPlayerHands)))
                      (shuffledTiles, [])
                      (List.range 0 numPlayers)
                  match unflippedAndPlayerHands with
                    (unflipped, playerHands) ->
                      board = []
                      playerStates =
                        List.map (hand -> newPlayer hand) playerHands
                      playerTurn = 0
                      Right
                        (GameState
                          unflipped board playerStates playerTurn config)

Note that there is now a line break in that match expression mentioned before, leading to an immediate parsing error:

._rummikub.trunk.engine> 

  I was expecting an indented block following the `with` keyword
  but instead found this token:
     27 |                               currUnflipped startingPlayerTileCount with

No matter how I change the screen sizes, both of the terminal ucm is running in or of my open text file, the edit result in the text file always has that line break. However, If I change the terminal width to be small enough then view will show that line break.

I tried reproducing this with a smaller example:

longTermName1 b = b && true
longTermName2 b = b && true
longTermName3 b = b && true
longTermName4 = true

longMatchLine = match (longTermName1 (longTermName2 (longTermName3 longTermName4))) with
  true -> true
  false -> false

And with small enough terminal widths I can observe the line break behavior there:

.> view longMatchLine

  longMatchLine : Boolean
  longMatchLine =
    match longTermName1
      (longTermName1 (longTermName1 longTermName4)) with
      true  -> true
      false -> false

However, I cannot reproduce the linebreak in what edit writes, even with this contrived example:

longMatchLine : Boolean
longMatchLine =
  match longTermName1 (longTermName1 (longTermName1 longTermName4)) with
    true  -> true
    false ->
      match longTermName1 (longTermName1 (longTermName1 longTermName4)) with
        true  -> true
        false ->
          match longTermName1 (longTermName1 (longTermName1 longTermName4)) with
            true  -> true
            false ->
              match longTermName1 (longTermName1 (longTermName1 longTermName4)) with
                true  -> true
                false ->
                  match longTermName1 (longTermName1 (longTermName1 longTermName4)) with
                    true  -> true
                    false ->
                      match longTermName1 (longTermName1 (longTermName1 longTermName4)) with
                        true  -> true
                        false ->
                          match longTermName1 (longTermName1 (longTermName1 longTermName4)) with
                            true  -> true
                            false ->
                              match longTermName1 (longTermName1 (longTermName1 longTermName4)) with
                                true  -> true
                                false ->
                                  match longTermName1 (longTermName1 (longTermName1 longTermName4)) with
                                    true  -> true
                                    false ->
                                      match longTermName1 (longTermName1 (longTermName1 longTermName4)) with
                                        true  -> true
                                        false ->
                                          match longTermName1 (longTermName1 (longTermName1 longTermName4)) with
                                            true  -> true
                                            false ->
                                              match longTermName1 (longTermName1 (longTermName1 longTermName4)) with
                                                true  -> true
                                                false ->
                                                  match longTermName1 (longTermName1 (longTermName1 longTermName4)) with
                                                    true  -> true
                                                    false ->
                                                      match longTermName1 (longTermName1 (longTermName1 longTermName4)) with
                                                        true  -> true
                                                        false ->
                                                          match longTermName1 (longTermName1 (longTermName1 longTermName4)) with
                                                            true  -> true
                                                            false ->
                                                              match longTermName1 (longTermName1 (longTermName1 longTermName4)) with
                                                                true  -> true
                                                                false -> false

I'm not sure what about my newGame function above triggers this, so I hope you don't mind that I've provided it in its entirety.

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions