diagnose 1.7.2 → 1.8.0
raw patch · 9 files changed
+129/−104 lines, 9 filesdep +wcwidthdep ~aesondep ~bytestringdep ~data-defaultPVP ok
version bump matches the API change (PVP)
Dependencies added: wcwidth
Dependency ranges changed: aeson, bytestring, data-default, hashable, prettyprinter, prettyprinter-ansi-terminal, text
API changes (from Hackage documentation)
- Error.Diagnose.Diagnostic: printDiagnostic :: (MonadIO m, Pretty msg) => Handle -> Bool -> Bool -> Diagnostic msg -> m ()
+ Error.Diagnose.Diagnostic: printDiagnostic :: (MonadIO m, Pretty msg) => Handle -> Bool -> Bool -> Int -> Diagnostic msg -> m ()
Files
- README.md +4/−5
- diagnose.cabal +33/−29
- src/Error/Diagnose.hs +4/−2
- src/Error/Diagnose/Diagnostic/Internal.hs +9/−5
- src/Error/Diagnose/Report/Internal.hs +67/−53
- test/megaparsec/Spec.hs +2/−2
- test/parsec/Repro2.hs +2/−2
- test/parsec/Spec.hs +2/−2
- test/rendering/Spec.hs +6/−4
README.md view
@@ -25,6 +25,8 @@ - Diagnostics can be exported to JSON, if you don't quite like the rendering as it is, or if you need to transmit them to e.g. a website - Plug and play (mega)parsec integration and it magically works with your parsers! - Support for optional custom error codes, if you want to go the Rust way+- Variable width Unicode characters are handled in a crossplatform manner+- TAB characters have custom sizes specified when printing a diagnostic, so that *you* decide the width of a TAB, not your terminal emulator! ## Usage @@ -97,17 +99,14 @@ let diagnostic' = addReport diagnostic beautifulExample -- Print with unicode characters and colors-printDiagnostic stdout True True diagnostic'+printDiagnostic stdout True True 4 diagnostic' ``` More examples are given in the [`test/rendering`](./test/rendering) folder. ## TODO list -- Handle variable-width characters such as tabs or some unicode characters.-- For tabs, we may just resort to having the user fix a given number of- spaces in the `printDiagnostic` function.+<< empty, for now >> ## License
diagnose.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: diagnose-version: 1.7.2+version: 1.8.0 synopsis: Beautiful error reporting done easily description: This package provides a simple way of getting beautiful compiler/interpreter errors using a very simple interface for the programmer.@@ -65,17 +65,18 @@ ghc-options: -Wall -Wextra build-depends: base >=4.7 && <5- , bytestring ==0.10.*- , data-default ==0.7.*- , hashable ==1.3.*- , prettyprinter ==1.7.*- , prettyprinter-ansi-terminal ==1.1.*- , text ==1.2.*+ , data-default >=0.7 && <1+ , hashable >=1.3 && <2+ , prettyprinter >=1.7.0 && <2+ , prettyprinter-ansi-terminal >=1.1.0 && <2+ , text >=1.2 && <2 , unordered-containers ==0.2.*+ , wcwidth >=0.0.1 && <1 if flag(json) cpp-options: -DUSE_AESON build-depends:- aeson ==1.5.*+ aeson >=1.5 && <2+ , bytestring >=0.9 && <1 if flag(megaparsec-compat) build-depends: containers ==0.6.*@@ -105,18 +106,19 @@ ghc-options: -Wall -Wextra -threaded -rtsopts -with-rtsopts=-N build-depends: base >=4.7 && <5- , bytestring ==0.10.*- , data-default ==0.7.*+ , data-default >=0.7 && <1 , diagnose- , hashable ==1.3.*- , prettyprinter ==1.7.*- , prettyprinter-ansi-terminal ==1.1.*- , text ==1.2.*+ , hashable >=1.3 && <2+ , prettyprinter >=1.7.0 && <2+ , prettyprinter-ansi-terminal >=1.1.0 && <2+ , text >=1.2 && <2 , unordered-containers ==0.2.*+ , wcwidth >=0.0.1 && <1 if flag(json) cpp-options: -DUSE_AESON build-depends:- aeson ==1.5.*+ aeson >=1.5 && <2+ , bytestring >=0.9 && <1 if flag(megaparsec-compat) build-depends: containers ==0.6.*@@ -143,18 +145,19 @@ ghc-options: -Wall -Wextra -threaded -rtsopts -with-rtsopts=-N build-depends: base >=4.7 && <5- , bytestring ==0.10.*- , data-default ==0.7.*+ , data-default >=0.7 && <1 , diagnose- , hashable ==1.3.*- , prettyprinter ==1.7.*- , prettyprinter-ansi-terminal ==1.1.*- , text ==1.2.*+ , hashable >=1.3 && <2+ , prettyprinter >=1.7.0 && <2+ , prettyprinter-ansi-terminal >=1.1.0 && <2+ , text >=1.2 && <2 , unordered-containers ==0.2.*+ , wcwidth >=0.0.1 && <1 if flag(json) cpp-options: -DUSE_AESON build-depends:- aeson ==1.5.*+ aeson >=1.5 && <2+ , bytestring >=0.9 && <1 if flag(megaparsec-compat) build-depends: containers ==0.6.*@@ -180,18 +183,19 @@ ghc-options: -Wall -Wextra -threaded -rtsopts -with-rtsopts=-N build-depends: base >=4.7 && <5- , bytestring ==0.10.*- , data-default ==0.7.*+ , data-default >=0.7 && <1 , diagnose- , hashable ==1.3.*- , prettyprinter ==1.7.*- , prettyprinter-ansi-terminal ==1.1.*- , text ==1.2.*+ , hashable >=1.3 && <2+ , prettyprinter >=1.7.0 && <2+ , prettyprinter-ansi-terminal >=1.1.0 && <2+ , text >=1.2 && <2 , unordered-containers ==0.2.*+ , wcwidth >=0.0.1 && <1 if flag(json) cpp-options: -DUSE_AESON build-depends:- aeson ==1.5.*+ aeson >=1.5 && <2+ , bytestring >=0.9 && <1 if flag(megaparsec-compat) build-depends: containers ==0.6.*
src/Error/Diagnose.hs view
@@ -150,6 +150,8 @@ -- -- - A 'Bool' set to 'False' if you don't want colors in the end result. --+-- - A 'Int' describing the number of spaces with which to output a TAB character.+-- -- - And finally the 'Diagnostic' to output. -- $diagnostic_json@@ -218,7 +220,7 @@ -- > -- Creates a new diagnostic with no default hints from the bundle returned by megaparsec -- > diag' = addFile diag filename content -- > -- Add the file used when parsing with the same filename given to 'MP.runParser'--- > in printDiagnostic stderr True True diag'+-- > in printDiagnostic stderr True True 4 diag' -- > Right res -> print res -- -- This example will return the following error message (assuming default instances for @'Error.Diagnose.Compat.Megaparsec.HasHints' 'Data.Void.Void' msg@):@@ -259,7 +261,7 @@ -- > -- Creates a new diagnostic with no default hints from the bundle returned by megaparsec -- > diag' = addFile diag filename content -- > -- Add the file used when parsing with the same filename given to 'MP.runParser'--- > in printDiagnostic stderr True True diag'+-- > in printDiagnostic stderr True True 4 diag' -- > Right res -> print res -- -- This will output the following errr on @stderr@:
src/Error/Diagnose/Diagnostic/Internal.hs view
@@ -69,11 +69,13 @@ Pretty msg => -- | Should we use unicode when printing paths? Bool ->- -- | The diagnostic to print+ -- | The number of spaces each TAB character will span.+ Int ->+ -- | The diagnostic to print. Diagnostic msg -> Doc AnsiStyle-prettyDiagnostic withUnicode (Diagnostic reports file) =- fold . intersperse hardline $ prettyReport file withUnicode <$> reports+prettyDiagnostic withUnicode tabSize (Diagnostic reports file) =+ fold . intersperse hardline $ prettyReport file withUnicode tabSize <$> reports {-# INLINE prettyDiagnostic #-} -- | Prints a 'Diagnostic' onto a specific 'Handle'.@@ -85,11 +87,13 @@ Bool -> -- | 'False' to disable colors. Bool ->+ -- | The number of spaces each TAB character will span.+ Int -> -- | The diagnostic to output. Diagnostic msg -> m ()-printDiagnostic handle withUnicode withColors diag =- liftIO $ hPutDoc handle (unlessId withColors unAnnotate $ prettyDiagnostic withUnicode diag)+printDiagnostic handle withUnicode withColors tabSize diag =+ liftIO $ hPutDoc handle (unlessId withColors unAnnotate $ prettyDiagnostic withUnicode tabSize diag) where unlessId cond app = if cond then id else app {-# INLINE unlessId #-}
src/Error/Diagnose/Report/Internal.hs view
@@ -4,9 +4,8 @@ {-# LANGUAGE MultiWayIf #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-}- {-# OPTIONS -Wno-name-shadowing #-}+{-# LANGUAGE TypeApplications #-} -- | -- Module : Error.Diagnose.Report.Internal@@ -27,15 +26,16 @@ #endif import Control.Applicative ((<|>)) import Data.Bifunctor (bimap, first, second)+import Data.Char.WCWidth (wcwidth) import Data.Default (def) import Data.Foldable (fold) import Data.Function (on) import Data.Functor ((<&>)) import Data.HashMap.Lazy (HashMap) import qualified Data.HashMap.Lazy as HashMap+import qualified Data.HashMap.Lazy as IntMap import qualified Data.List as List import qualified Data.List.Safe as List-import Data.Ord (Down (..)) import Error.Diagnose.Position import Prettyprinter (Doc, Pretty (..), align, annotate, colon, hardline, lbracket, rbracket, space, width, (<+>)) import Prettyprinter.Internal (Doc (..))@@ -136,10 +136,12 @@ HashMap FilePath [String] -> -- | Should we print paths in unicode? Bool ->+ -- | The number of spaces each TAB character will span+ Int -> -- | The whole report to output Report msg -> Doc AnsiStyle-prettyReport fileContent withUnicode (Report isError code message markers hints) =+prettyReport fileContent withUnicode tabSize (Report isError code message markers hints) = let sortedMarkers = List.sortOn (fst . begin . fst) markers -- sort the markers so that the first lines of the reports are the first lines of the file @@ -150,7 +152,8 @@ -- if there are no markers, then default to 3, else get the maximum between 3 and the length of the last marker header =- annotate (bold <> color if isError then Red else Yellow) $+ annotate+ (bold <> color if isError then Red else Yellow) ( lbracket <> ( if isError then "error"@@ -163,7 +166,7 @@ in {- A report is of the form: (1) [error|warning]: <message>- (2) +-> <file>+ (2) +--> <file> (3) : (4) <line> | <line of code> : <marker lines>@@ -174,7 +177,7 @@ -} {- (1) -} header <> colon <+> align (pretty message)- <> {- (2), (3), (4) -} fold (uncurry (prettySubReport fileContent withUnicode isError maxLineNumberLength) <$> groupedMarkers)+ <> {- (2), (3), (4) -} fold (uncurry (prettySubReport fileContent withUnicode isError tabSize maxLineNumberLength) <$> groupedMarkers) <> {- (5) -} ( if | null hints && null markers -> mempty | null hints -> mempty@@ -280,6 +283,8 @@ Bool -> -- | Is the current report an error report? Bool ->+ -- | The number of spaces each TAB character will span+ Int -> -- | The size of the biggest line number Int -> -- | Is this sub-report the first one in the list?@@ -287,7 +292,7 @@ -- | The list of line-ordered markers appearing in a single file [(Position, Marker msg)] -> Doc AnsiStyle-prettySubReport fileContent withUnicode isError maxLineNumberLength isFirst markers =+prettySubReport fileContent withUnicode isError tabSize maxLineNumberLength isFirst markers = let (markersPerLine, multilineMarkers) = splitMarkersPerLine markers -- split the list on whether markers are multiline or not @@ -312,7 +317,7 @@ in {- (2) -} hardline <> fileMarker <> hardline <+> {- (3) -} pipePrefix maxLineNumberLength withUnicode- <> {- (4) -} prettyAllLines fileContent withUnicode isError maxLineNumberLength sortedMarkersPerLine multilineMarkers allLineNumbers+ <> {- (4) -} prettyAllLines fileContent withUnicode isError tabSize maxLineNumberLength sortedMarkersPerLine multilineMarkers allLineNumbers isThisMarker :: Marker msg -> Bool isThisMarker (This _) = True@@ -333,13 +338,15 @@ HashMap FilePath [String] -> Bool -> Bool ->+ -- | The number of spaces each TAB character will span Int ->+ Int -> [(Int, [(Position, Marker msg)])] -> [(Position, Marker msg)] -> [Int] -> Doc AnsiStyle-prettyAllLines _ _ _ _ _ [] [] = mempty-prettyAllLines _ withUnicode isError leftLen _ multiline [] =+prettyAllLines _ _ _ _ _ _ [] [] = mempty+prettyAllLines _ withUnicode isError _ leftLen _ multiline [] = let colorOfLastMultilineMarker = maybe mempty (markerColor isError . snd) (List.safeLast multiline) -- take the color of the last multiline marker in case we need to add additional bars @@ -358,7 +365,7 @@ showMultilineMarkerMessages [m] = [showMultilineMarkerMessage m True] showMultilineMarkerMessages (m : ms) = showMultilineMarkerMessage m False : showMultilineMarkerMessages ms in prefixWithBar colorOfLastMultilineMarker <> prefix <> fold (List.intersperse prefix $ showMultilineMarkerMessages $ reverse multiline)-prettyAllLines files withUnicode isError leftLen inline multiline (line : ls) =+prettyAllLines files withUnicode isError tabSize leftLen inline multiline (line : ls) = {- A line of code is composed of: (1) <line> | <source code>@@ -399,47 +406,50 @@ <> space allMarkersInLine = {- List.sortOn fst $ -} allInlineMarkersInLine <> allMultilineMarkersInLine++ (widths, renderedCode) = getLine_ files (allMarkersInLine <> allMultilineMarkersSpanningLine) line tabSize isError in hardline <> {- (1) -} linePrefix leftLen line withUnicode <+> additionalPrefix- <> getLine_ files (allMarkersInLine <> allMultilineMarkersSpanningLine) line isError- <> {- (2) -} showAllMarkersInLine (not $ null multiline) inSpanOfMultiline colorOfFirstMultilineMarker withUnicode isError leftLen allInlineMarkersInLine- <> {- (3) -} prettyAllLines files withUnicode isError leftLen inline multiline ls+ <> renderedCode+ <> {- (2) -} showAllMarkersInLine (not $ null multiline) inSpanOfMultiline colorOfFirstMultilineMarker withUnicode isError leftLen widths allInlineMarkersInLine+ <> {- (3) -} prettyAllLines files withUnicode isError tabSize leftLen inline multiline ls -- |-getLine_ :: HashMap FilePath [String] -> [(Position, Marker msg)] -> Int -> Bool -> Doc AnsiStyle-getLine_ files markers line isError = case List.safeIndex (line - 1) =<< (HashMap.!?) files . file . fst =<< List.safeHead markers of- Nothing -> annotate (bold <> colorDull Magenta) "<no line>"+getLine_ :: HashMap FilePath [String] -> [(Position, Marker msg)] -> Int -> Int -> Bool -> (IntMap.HashMap Int Int, Doc AnsiStyle)+getLine_ files markers line tabSize isError = case List.safeIndex (line - 1) =<< (HashMap.!?) files . file . fst =<< List.safeHead markers of+ Nothing -> (mempty, annotate (bold <> colorDull Magenta) "<no line>") Just code ->- fold $- indexed code <&> \(n, c) ->- let colorizingMarkers = flip- filter- markers- \(Position (bl, bc) (el, ec) _, _) ->- if bl == el- then n >= bc && n < ec- else (bl == line && n >= bc) || (el == line && n < ec)- in maybe id ((\m -> annotate (bold <> markerColor isError m)) . snd) (List.safeHead colorizingMarkers) (pretty c)+ let (tabs, code') = indexedWithTabsReplaced code+ in ( tabs,+ fold $+ code' <&> \(n, c) ->+ let colorizingMarkers = flip+ filter+ markers+ \(Position (bl, bc) (el, ec) _, _) ->+ if bl == el+ then n >= bc && n < ec+ else (bl == line && n >= bc) || (el == line && n < ec)+ in maybe id ((\m -> annotate (bold <> markerColor isError m)) . snd) (List.safeHead colorizingMarkers) (pretty c)+ ) where- indexed :: [a] -> [(Int, a)]- indexed = goIndexed 1+ indexedWithTabsReplaced :: String -> (IntMap.HashMap Int Int, [(Int, Char)])+ indexedWithTabsReplaced = goIndexed 1 - goIndexed :: Int -> [a] -> [(Int, a)]- goIndexed _ [] = []- goIndexed n (x : xs) = (n, x) : goIndexed (n + 1) xs+ goIndexed :: Int -> String -> (IntMap.HashMap Int Int, [(Int, Char)])+ goIndexed _ [] = (mempty, [])+ goIndexed n ('\t' : xs) = bimap (IntMap.insert n tabSize) (replicate tabSize (n, ' ') <>) (goIndexed (n + 1) xs)+ goIndexed n (x : xs) = bimap (IntMap.insert n (wcwidth x)) ((n, x) :) (goIndexed (n + 1) xs) -- |-showAllMarkersInLine :: Pretty msg => Bool -> Bool -> (Doc AnsiStyle -> Doc AnsiStyle) -> Bool -> Bool -> Int -> [(Position, Marker msg)] -> Doc AnsiStyle-showAllMarkersInLine _ _ _ _ _ _ [] = mempty-showAllMarkersInLine hasMultilines inSpanOfMultiline colorMultilinePrefix withUnicode isError leftLen ms =+showAllMarkersInLine :: Pretty msg => Bool -> Bool -> (Doc AnsiStyle -> Doc AnsiStyle) -> Bool -> Bool -> Int -> IntMap.HashMap Int Int -> [(Position, Marker msg)] -> Doc AnsiStyle+showAllMarkersInLine _ _ _ _ _ _ _ [] = mempty+showAllMarkersInLine hasMultilines inSpanOfMultiline colorMultilinePrefix withUnicode isError leftLen widths ms = let maxMarkerColumn = snd $ end $ fst $ List.last $ List.sortOn (snd . end . fst) ms- specialPrefix =- if inSpanOfMultiline- then colorMultilinePrefix (if withUnicode then "│ " else "| ") <> space- else- if hasMultilines- then colorMultilinePrefix " " <> space- else mempty+ specialPrefix+ | inSpanOfMultiline = colorMultilinePrefix (if withUnicode then "│ " else "| ") <> space+ | hasMultilines = colorMultilinePrefix " " <> space+ | otherwise = mempty in -- get the maximum end column, so that we know when to stop looking for other markers on the same line hardline <+> dotPrefix leftLen withUnicode <+> (if List.null ms then mempty else specialPrefix <> showMarkers 1 maxMarkerColumn <> showMessages specialPrefix ms maxMarkerColumn) where@@ -449,12 +459,15 @@ let allMarkers = flip filter ms \(Position (_, bc) (_, ec) _, _) -> n >= bc && n < ec in -- only consider markers which span onto the current column case allMarkers of- [] -> space <> showMarkers (n + 1) lineLen+ [] -> fold (replicate (IntMap.lookupDefault 0 n widths) space) <> showMarkers (n + 1) lineLen (Position {..}, marker) : _ ->- if snd begin == n- then annotate (markerColor isError marker) (if withUnicode then "┬" else "^") <> showMarkers (n + 1) lineLen- else annotate (markerColor isError marker) (if withUnicode then "─" else "-") <> showMarkers (n + 1) lineLen- -- if the marker just started on this column, output a caret, else output a dash+ annotate+ (markerColor isError marker)+ ( if snd begin == n+ then (if withUnicode then "┬" else "^") <> fold (replicate (IntMap.lookupDefault 0 n widths - 1) if withUnicode then "─" else "-")+ else fold (replicate (IntMap.lookupDefault 0 n widths) if withUnicode then "─" else "-")+ )+ <> showMarkers (n + 1) lineLen showMessages specialPrefix ms lineLen = case List.safeUncons ms of Nothing -> mempty -- no more messages to show@@ -467,15 +480,16 @@ allColumns _ [] = (1, []) allColumns n ms@((Position (_, bc) _ _, col) : ms') | n == bc = bimap (+ 1) (col :) (allColumns (n + 1) ms')- | n < bc = bimap (+ 1) (space :) (allColumns (n + 1) ms)- | otherwise = bimap (+ 1) (space :) (allColumns (n + 1) ms')+ | n < bc = bimap (+ 1) (replicate (IntMap.lookupDefault 0 n widths) space <>) (allColumns (n + 1) ms)+ | otherwise = bimap (+ 1) (replicate (IntMap.lookupDefault 0 n widths) space <>) (allColumns (n + 1) ms') -- transform the list of remaining markers into a single document line hasSuccessor = length filteredPipes /= length pipes lineStart pipes = let (n, docs) = allColumns 1 $ List.sortOn (snd . begin . fst) pipes- in dotPrefix leftLen withUnicode <+> specialPrefix <> fold docs <> pretty (replicate (bc - n) ' ')+ numberOfSpaces = sum [IntMap.lookupDefault 0 x widths | x <- [n .. bc - 1]] -- bc - n+ in dotPrefix leftLen withUnicode <+> specialPrefix <> fold docs <> pretty (replicate numberOfSpaces ' ') -- the start of the line contains the "dot"-prefix as well as all the pipes for all the still not rendered marker messages prefix =@@ -485,11 +499,11 @@ pipesBeforeRendered = pipesBefore <&> second \marker -> annotate (markerColor isError marker) (if withUnicode then "│" else "|") -- pre-render pipes which are before because they will be shown - lastBeginPosition = snd . begin . fst <$> List.safeLast (List.sortOn (Down . snd . begin . fst) pipesAfter)+ lastBeginPosition = snd . begin . fst <$> List.safeHead (List.sortOn (snd . begin . fst) pipesAfter) lineLen = case lastBeginPosition of Nothing -> 0- Just col -> col - bc+ Just col -> sum [IntMap.lookupDefault 0 x widths | x <- [bc .. col - 1]] currentPipe = if
test/megaparsec/Spec.hs view
@@ -30,8 +30,8 @@ res2 = first (errorDiagnosticFromBundle Nothing "Parse error on input" Nothing) $ MP.runParser @Void (MP.some MP.decimal <* MP.eof) filename content2 case res1 of- Left diag -> printDiagnostic stdout True True (addFile diag filename (Text.unpack content1) :: Diagnostic String)+ Left diag -> printDiagnostic stdout True True 4 (addFile diag filename (Text.unpack content1) :: Diagnostic String) Right res -> print res case res2 of- Left diag -> printDiagnostic stdout True True (addFile diag filename (Text.unpack content2) :: Diagnostic String)+ Left diag -> printDiagnostic stdout True True 4 (addFile diag filename (Text.unpack content2) :: Diagnostic String) Right res -> print res
test/parsec/Repro2.hs view
@@ -29,8 +29,8 @@ main :: IO () main = do- either (printDiagnostic stderr True True) print $ diagParse parser1 "issues/2.txt" "\\1"- either (printDiagnostic stderr True True) print $ diagParse parser2 "issues/2.txt" "\\1"+ either (printDiagnostic stderr True True 4) print $ diagParse parser1 "issues/2.txt" "\\1"+ either (printDiagnostic stderr True True 4) print $ diagParse parser2 "issues/2.txt" "\\1" -- smaller example op' :: String -> Parser String
test/parsec/Spec.hs view
@@ -30,10 +30,10 @@ res2 = first (errorDiagnosticFromParseError Nothing "Parse error on input" Nothing) $ P.parse (P.many1 P.digit <* P.eof) filename content2 case res1 of- Left diag -> printDiagnostic stdout True True (addFile diag filename (Text.unpack content1) :: Diagnostic String)+ Left diag -> printDiagnostic stdout True True 4 (addFile diag filename (Text.unpack content1) :: Diagnostic String) Right res -> print res case res2 of- Left diag -> printDiagnostic stdout True True (addFile diag filename (Text.unpack content2) :: Diagnostic String)+ Left diag -> printDiagnostic stdout True True 4 (addFile diag filename (Text.unpack content2) :: Diagnostic String) Right res -> print res -- all issue reproduction
test/rendering/Spec.hs view
@@ -31,7 +31,7 @@ ("somefile.zc", "let id<a>(x : a) : a := x\n + 1"), ("err.nst", "\n\n\n\n = jmp g\n\n g: forall(s: Ts, e: Tc).{ %r0: *s64 | s -> e }"), ("unsized.nst", "main: forall(a: Ta, s: Ts, e: Tc).{ %r5: forall().{| s -> e } | s -> %r5 }\n = salloc a\n ; sfree\n"),- ("unicode.txt", "±⅀\t★♲♥🎉⑳⓴ჳᏁℳ爪")+ ("unicode.txt", "±⅀\t★♲♥🎉汉⑳⓴ჳᏁℳ爪") ] let reports =@@ -70,9 +70,9 @@ let diag = HashMap.foldlWithKey' addFile (foldr (flip addReport) def reports) files hPutStrLn stdout "\n\nWith unicode: ─────────────────────────\n"- printDiagnostic stdout True True diag+ printDiagnostic stdout True True 4 diag hPutStrLn stdout "\n\nWithout unicode: ----------------------\n"- printDiagnostic stdout False True diag+ printDiagnostic stdout False True 4 diag #ifdef USE_AESON hPutStrLn stdout "\n\nAs JSON: ------------------------------\n" BS.hPutStr stdout (diagnosticToJson diag)@@ -354,5 +354,7 @@ err (Just "❎") "ⓈⓉⓇⒶⓃⒼⒺ ⓊⓃⒾⒸⓄⒹⒺ"- [(Position (1, 1) (1, 7) "unicode.txt", This "should work fine 🎉")]+ [ (Position (1, 1) (1, 7) "unicode.txt", This "should work fine 🎉"),+ (Position (1, 7) (1, 9) "unicode.txt", Where "After TAB")+ ] []