diff --git a/CHANGELOG.md b/CHANGELOG.md index 813d8f145..ce7984ecd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +* Correctly format edge cases where fully collapsing string gaps changes the + string represented by a string literal. [Issue + 1160](https://github.com/tweag/ormolu/issues/1160). + ## Ormolu 0.8.0.0 * Format multiple files in parallel. [Issue diff --git a/data/examples/declaration/value/function/multiline-strings-0-out.hs b/data/examples/declaration/value/function/multiline-strings-0-out.hs index c8d25a693..8b412f09f 100644 --- a/data/examples/declaration/value/function/multiline-strings-0-out.hs +++ b/data/examples/declaration/value/function/multiline-strings-0-out.hs @@ -7,7 +7,7 @@ s = """ s_2 = - """Line 1 + """\ \Line 1 Line 2 Line 3 """ diff --git a/data/examples/declaration/value/function/multiline-strings-1-out.hs b/data/examples/declaration/value/function/multiline-strings-1-out.hs index e8ba128b3..7aedbc792 100644 --- a/data/examples/declaration/value/function/multiline-strings-1-out.hs +++ b/data/examples/declaration/value/function/multiline-strings-1-out.hs @@ -2,9 +2,11 @@ s = """ - a b c d e + a b\ \ c d e f g """ -- equivalent to s' = "a b c d e\nf g" + +weirdGap = """\65\ \0""" diff --git a/data/examples/declaration/value/function/multiline-strings-1.hs b/data/examples/declaration/value/function/multiline-strings-1.hs index 113f6e8d9..0ef6bd592 100644 --- a/data/examples/declaration/value/function/multiline-strings-1.hs +++ b/data/examples/declaration/value/function/multiline-strings-1.hs @@ -9,3 +9,5 @@ s = -- equivalent to s' = "a b c d e\nf g" + +weirdGap = """\65\ \0""" diff --git a/data/examples/declaration/value/function/strings-out.hs b/data/examples/declaration/value/function/strings-out.hs index 738602bdf..68b4ef2f4 100644 --- a/data/examples/declaration/value/function/strings-out.hs +++ b/data/examples/declaration/value/function/strings-out.hs @@ -2,9 +2,11 @@ foo = "foobar" -bar = "foo\&barbaz" +bar = "foo\&bar\ \baz" baz = "foo\ \bar\ \baz" + +weirdGap = "\65\ \0" diff --git a/data/examples/declaration/value/function/strings.hs b/data/examples/declaration/value/function/strings.hs index 88c7e2e91..0cb4a7e42 100644 --- a/data/examples/declaration/value/function/strings.hs +++ b/data/examples/declaration/value/function/strings.hs @@ -5,3 +5,5 @@ bar = "foo\&bar\ \baz" baz = "foo\ \bar\ \baz" + +weirdGap = "\65\ \0" diff --git a/src/Ormolu/Printer/Meat/Declaration/StringLiteral.hs b/src/Ormolu/Printer/Meat/Declaration/StringLiteral.hs index 2c3474d0a..e6047f70f 100644 --- a/src/Ormolu/Printer/Meat/Declaration/StringLiteral.hs +++ b/src/Ormolu/Printer/Meat/Declaration/StringLiteral.hs @@ -28,7 +28,7 @@ p_stringLit src = case parseStringLiteral $ T.pack $ unpackFS src of case stringLiteralKind of RegularStringLiteral -> do let singleLine = - txt $ T.concat segments + txt $ intercalateMinimalStringGaps segments multiLine = sep breakpoint f (attachRelativePos segments) where @@ -105,9 +105,9 @@ parseStringLiteral = \s -> do splitMultilineString :: Text -> [Text] splitMultilineString = splitGaps - -- There is no reason to use gaps with multiline string literals, so - -- we collapse them. - >>> T.concat + -- There is no reason to use gaps with multiline string literals just to + -- emulate multi-line strings, so we replace them with "\\ \\". + >>> intercalateMinimalStringGaps >>> splitNewlines >>> fmap expandLeadingTabs >>> rmCommonWhitespacePrefixAndBlank @@ -150,3 +150,12 @@ parseStringLiteral = \s -> do | otherwise = (Just $ Min leadingSpace, T.drop commonWs l) where leadingSpace = T.length $ T.takeWhile is_space l + +-- | Add minimal string gaps between string literal chunks. Such string gaps +-- /can/ be semantically meaningful (so we preserve them for simplicity); for +-- example: +-- +-- >>> "\65\ \0" == "\650" +-- False +intercalateMinimalStringGaps :: [Text] -> Text +intercalateMinimalStringGaps = T.intercalate "\\ \\"