Skip to content

Commit ecca531

Browse files
committed
Fix string literal gap parsing with control-with-character
1 parent 48c84b7 commit ecca531

File tree

4 files changed

+24
-4
lines changed

4 files changed

+24
-4
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010
* Updated to `Cabal-syntax-3.16`.
1111

12+
* Correctly format string literals containing the `\^\` escape sequence. [Issue
13+
1165](https://github.com/tweag/ormolu/issues/1165).
14+
1215
## Ormolu 0.8.0.1
1316

1417
* Correctly format edge cases where fully collapsing string gaps changes the

data/examples/declaration/value/function/strings-out.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ baz =
1010
\baz"
1111

1212
weirdGap = "\65\ \0"
13+
14+
weirdEscape = "\^\ "

data/examples/declaration/value/function/strings.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ baz = "foo\
77
\baz"
88

99
weirdGap = "\65\ \0"
10+
11+
weirdEscape = "\^\ "

src/Ormolu/Printer/Meat/Declaration/StringLiteral.hs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,26 @@ parseStringLiteral = \s -> do
9191
where
9292
go [] = [s]
9393
go ((pre, suf) : bs) = case T.uncons suf of
94-
Just ('\\', T.uncons -> Just (c, s'))
95-
| is_space c,
96-
let rest = T.drop 1 $ T.dropWhile (/= '\\') s' ->
94+
Just ('\\', suf')
95+
| (gap, T.uncons -> Just ('\\', rest)) <- T.span is_space suf',
96+
-- If there is a space after the backslash, this definitely is a
97+
-- string gap. Continue parsing gaps after the next backslash.
98+
not $ T.null gap ->
9799
pre : splitGaps rest
98-
| otherwise -> go $ (if c == '\\' then drop 1 else id) bs
100+
| otherwise ->
101+
-- Check whether @suf@ starts with an escape sequence involving
102+
-- another backslash. If so, it can not be the start of a string
103+
-- gap, so we skip it.
104+
let skipNextBackslash =
105+
any (`T.isPrefixOf` suf') escapesWithAnotherBackslash
106+
in go $ (if skipNextBackslash then drop 1 else id) bs
99107
_ -> go bs
100108

109+
-- All escape sequences (without the initial backslash) with another
110+
-- backslash. See
111+
-- https://www.haskell.org/onlinereport/haskell2010/haskellch2.html#x7-200002.6
112+
escapesWithAnotherBackslash = ["\\", "^\\"]
113+
101114
-- See the the MultilineStrings GHC proposal and 'lexMultilineString' from
102115
-- "GHC.Parser.String" for reference.
103116
--

0 commit comments

Comments
 (0)