@@ -1089,7 +1089,7 @@ prettyCharacterSet characterSet expression =
1089
1089
prettyChunks :: Pretty a => Chunks Src a -> Doc Ann
1090
1090
prettyChunks chunks@ (Chunks a b)
1091
1091
| anyText (== ' \n ' ) =
1092
- if anyText (/= ' \n ' )
1092
+ if not ( null a) || anyText (/= ' \n ' )
1093
1093
then long
1094
1094
else Pretty. flatAlt long short
1095
1095
| otherwise =
@@ -1139,24 +1139,80 @@ prettyCharacterSet characterSet expression =
1139
1139
prettyText t = literal (Pretty. pretty (escapeText_ t))
1140
1140
1141
1141
1142
- -- | Prepare 'Chunks' for multi-line formatting by interpolating characters that
1143
- -- may not appear in multi-line strings directly.
1142
+ -- | Prepare 'Chunks' for multi-line formatting by escaping problematic
1143
+ -- character sequences via string interpolations
1144
1144
--
1145
+ -- >>> multilineChunks (Chunks [] "\n \tx")
1146
+ -- Chunks [("\n",TextLit (Chunks [] " \t"))] "x"
1145
1147
-- >>> multilineChunks (Chunks [] "\n\NUL\b\f\t")
1146
1148
-- Chunks [("\n",TextLit (Chunks [] "\NUL\b\f"))] "\t"
1147
- multilineChunks :: Chunks Src a -> Chunks Src a
1148
- multilineChunks (Chunks as0 b0) = Chunks as1 b1
1149
+ multilineChunks :: Chunks s a -> Chunks s a
1150
+ multilineChunks = escapeControlCharacters . escapeLastLineLeadingWhitespace
1151
+
1152
+ -- | Escape leading whitespace on the last line by moving it into a string
1153
+ -- string interpolation
1154
+ --
1155
+ -- Unescaped leading whitespace on the last line would otherwise be removed
1156
+ -- by the parser's dedentation logic.
1157
+ --
1158
+ -- >>> escapeLastLineLeadingWhitespace (Chunks [] "\n \tx")
1159
+ -- Chunks [("\n",TextLit (Chunks [] " \t"))] "x"
1160
+ -- >>> escapeLastLineLeadingWhitespace (Chunks [("\n",Var (V "x" 0))] " ")
1161
+ -- Chunks [("\n",Var (V "x" 0))] " "
1162
+ -- >>> escapeLastLineLeadingWhitespace (Chunks [("\n ",Var (V "x" 0))] "")
1163
+ -- Chunks [("\n",TextLit (Chunks [] " ")),("",Var (V "x" 0))] ""
1164
+ -- >>> escapeLastLineLeadingWhitespace (Chunks [("\n ",Var (V "x" 0))] "\n")
1165
+ -- Chunks [("\n ",Var (V "x" 0))] "\n"
1166
+ --
1167
+ -- We assume that there's at least one newline and may therefore ignore leading
1168
+ -- whitespace on the first line:
1169
+ --
1170
+ -- >>> escapeLastLineLeadingWhitespace (Chunks [] " ")
1171
+ -- Chunks [] " "
1172
+ escapeLastLineLeadingWhitespace :: Chunks s a -> Chunks s a
1173
+ escapeLastLineLeadingWhitespace (Chunks as0 b0) =
1174
+ case escape1 b0 of
1175
+ Nothing -> Chunks (escapeChunks as0) b0
1176
+ Just (Chunks cs b1) -> Chunks (as0 ++ cs) b1
1177
+ where
1178
+ -- Nothing: No newline found
1179
+ -- Just chunks: Newline was found!
1180
+ escape1 :: Text -> Maybe (Chunks s a )
1181
+ escape1 t = case Text. breakOnEnd " \n " t of
1182
+ (" " , _) -> Nothing
1183
+ (a , b) -> Just $ case Text. span predicate b of
1184
+ (" " , _) -> Chunks [] t
1185
+ (c , d) -> Chunks [(a, TextLit (Chunks [] c))] d
1186
+
1187
+ predicate c = c == ' ' || c == ' \t '
1188
+
1189
+ escapeChunks = snd . foldr f (NotDone , [] )
1190
+
1191
+ f chunk (Done , chunks) = (Done , chunk : chunks)
1192
+ f (t, e) (NotDone , chunks) = case escape1 t of
1193
+ Nothing -> (NotDone , (t, e) : chunks)
1194
+ Just (Chunks as b) -> (Done , as ++ (b, e) : chunks)
1195
+
1196
+ data Done = NotDone | Done
1197
+
1198
+ -- | Escape control characters by moving them into string interpolations
1199
+ --
1200
+ -- >>> escapeControlCharacters (Chunks [] "\n\NUL\b\f\t")
1201
+ -- Chunks [("\n",TextLit (Chunks [] "\NUL\b\f"))] "\t"
1202
+ escapeControlCharacters :: Chunks s a -> Chunks s a
1203
+ escapeControlCharacters (Chunks as0 b0) = Chunks as1 b1
1149
1204
where
1150
- as1 = foldr f (map toPair bs) as0
1205
+ as1 = foldr f (map toChunk bs) as0
1151
1206
1152
1207
(bs, b1) = splitOnPredicate predicate b0
1153
1208
1154
- predicate c = Data.Char. isControl c && c /= ' ' && c /= ' \t ' && c /= ' \n '
1209
+ f (t0, e) chunks = map toChunk ts1 ++ (t1, e) : chunks
1210
+ where
1211
+ (ts1, t1) = splitOnPredicate predicate t0
1155
1212
1156
- f (t0, e) pairs = case splitOnPredicate predicate t0 of
1157
- (ts1, t1) -> map toPair ts1 ++ (t1, e) : pairs
1213
+ predicate c = Data.Char. isControl c && c /= ' ' && c /= ' \t ' && c /= ' \n '
1158
1214
1159
- toPair (t0, t1) = (t0, TextLit (Chunks [] t1))
1215
+ toChunk (t0, t1) = (t0, TextLit (Chunks [] t1))
1160
1216
1161
1217
-- | Split `Text` on a predicate, preserving all parts of the original string.
1162
1218
--
0 commit comments