@@ -5,10 +5,15 @@ module Reporting.Report
8+ , printError, printWarning
11+import Control.Applicative ((<|>))
12+import Control.Monad.Writer (Writer, execWriter, tell)
10import Data.Aeson ((.=))
13import Data.Aeson ((.=))
11import qualified Data.Aeson.Types as Json
14import qualified Data.Aeson.Types as Json
15+import System.Console.ANSI
16+import System.IO (hPutStr, stderr)
13import qualified Reporting.Region as R
18import qualified Reporting.Region as R
@@ -27,13 +32,33 @@ simple title pre post =
29toString :: String -> R.Region -> Report -> String -> String
34toString :: String -> R.Region -> Report -> String -> String
35+toString location region report source =
36+ execWriter (render plain location region report source)
39+printError :: String -> R.Region -> Report -> String -> IO ()
40+printError location region report source =
41+ render (ansi Error) location region report source
44+printWarning :: String -> R.Region -> Report -> String -> IO ()
45+printWarning location region report source =
46+ render (ansi Warning) location region report source
30−toString location region (Report title highlight pre post) source =
57+render renderer location region (Report title highlight pre post) source =
32− [ messageBar title location
58+ do messageBar renderer title location
33− , pre ++ "\n\n"
59+ normal renderer (pre ++ "\n\n")
34− , grabRegion highlight region source ++ "\n"
60+ grabRegion renderer highlight region source
35− , if null post then "\n" else post ++ "\n\n\n"
61+ normal renderer ("\n" ++ if null post then "\n" else post ++ "\n\n\n")
39toJson :: [Json.Pair] -> Report -> (Maybe R.Region, [Json.Pair])
64toJson :: [Json.Pair] -> Report -> (Maybe R.Region, [Json.Pair])
@@ -48,12 +73,55 @@ toJson extraFields (Report title subregion pre post) =
48 (subregion, fields ++ extraFields)
73 (subregion, fields ++ extraFields)
78+data Renderer m = Renderer
79+ { normal :: String -> m ()
80+ , header :: String -> m ()
81+ , accent :: String -> m ()
85+plain :: Renderer (Writer String)
87+ Renderer tell tell tell
90+data Type = Error | Warning
93+ansi :: Type -> Renderer IO
99+ put' intensity color string =
100+ do hSetSGR stderr [SetColor Foreground intensity color]
102+ hSetSGR stderr [Reset]
112+ (put' Dull accentColor)
51-- REPORT HEADER
117-- REPORT HEADER
53−messageBar :: String -> String -> String
119+messageBar :: Renderer m -> String -> String -> m ()
54−messageBar tag location =
120+messageBar renderer tag location =
55− let usedSpace = 4 + length tag + 1 + length location
122+ usedSpace = 4 + length tag + 1 + length location
57 "-- " ++ tag ++ " "
125 "-- " ++ tag ++ " "
58 ++ replicate (max 1 (80 - usedSpace)) '-'
126 ++ replicate (max 1 (80 - usedSpace)) '-'
59 ++ " " ++ location ++ "\n\n"
127 ++ " " ++ location ++ "\n\n"
@@ -61,8 +129,14 @@ messageBar tag location =
62-- REGIONS
130-- REGIONS
64−grabRegion :: Maybe R.Region -> R.Region -> String -> String
135+ -> Maybe R.Region
65−grabRegion maybeSubRegion (R.Region start end) source =
139+grabRegion renderer maybeSubRegion region@(R.Region start end) source =
67 (R.Position startLine startColumn) = start
141 (R.Position startLine startColumn) = start
68 (R.Position endLine endColumn) = end
142 (R.Position endLine endColumn) = end
@@ -76,10 +150,10 @@ grabRegion maybeSubRegion (R.Region start end) source =
77 case relevantLines of
151 case relevantLines of
79− ""
153+ normal renderer ""
81 [sourceLine] ->
155 [sourceLine] ->
82− singleLineRegion startLine sourceLine $
156+ singleLineRegion renderer startLine sourceLine $
83 case maybeSubRegion of
157 case maybeSubRegion of
84 Nothing ->
158 Nothing ->
85 (0, startColumn, endColumn, length sourceLine)
159 (0, startColumn, endColumn, length sourceLine)
@@ -100,44 +174,61 @@ grabRegion maybeSubRegion (R.Region start end) source =
100 filteredFirstLine : init rest ++ [filteredLastLine]
174 filteredFirstLine : init rest ++ [filteredLastLine]
102 lineNumbersWidth =
176 lineNumbersWidth =
103− length (show (endLine + 1))
177+ length (show endLine)
180+ maybeSubRegion <|> Just region
105 numberedLines =
182 numberedLines =
107− (addLineNumber maybeSubRegion lineNumbersWidth)
184+ (addLineNumber renderer subregion lineNumbersWidth)
108− [startLine - 1 .. endLine + 1]
185+ [startLine .. endLine]
109− ("" : focusedRelevantLines ++ [""])
186+ focusedRelevantLines
111− unlines numberedLines
188+ mapM_ (\line -> line >> normal renderer "\n") numberedLines
114−addLineNumber :: Maybe R.Region -> Int -> Int -> String -> String
191+addLineNumber
115−addLineNumber maybeSubRegion width n line =
199+addLineNumber renderer maybeSubRegion width n line =
118− if n < 0 then " " else show n
202+ if n < 0 then " " else show n
205+ replicate (width - length number) ' ' ++ number ++ "|"
120 spacer (R.Region start end) =
207 spacer (R.Region start end) =
121− if R.line start <= n && n <= R.line end
208+ if R.line start <= n && n <= R.line end
122− then ">"
209+ then accent renderer ">"
123− else " "
210+ else normal renderer " "
125− replicate (width - length number) ' ' ++ number
212+ do normal renderer lineNumber
126− ++ "|" ++ maybe " " spacer maybeSubRegion
213+ maybe (normal renderer " ") spacer maybeSubRegion
127− ++ line
214+ normal renderer line
130−singleLineRegion :: Int -> String -> (Int, Int, Int, Int) -> String
217+singleLineRegion
222+ -> (Int, Int, Int, Int)
131−singleLineRegion lineNum sourceLine (start, innerStart, innerEnd, end) =
224+singleLineRegion renderer lineNum sourceLine (start, innerStart, innerEnd, end) =
133− w = length (show (lineNum + 1))
227+ length (show lineNum)
136− addLineNumber Nothing w
138 underline =
229 underline =
139− replicate (innerStart - 1) ' '
230+ replicate (innerStart + width + 1) ' '
140− ++ replicate (max 1 (innerEnd - innerStart)) '^'
231+ ++ replicate (max 1 (innerEnd - innerStart)) '^'
142 (|>) = flip ($)
233 (|>) = flip ($)
@@ -147,8 +238,5 @@ singleLineRegion lineNum sourceLine (start, innerStart, innerEnd, end) =
147 |> take (end - start + 1)
238 |> take (end - start + 1)
148 |> (++) (replicate (start - 1) ' ')
239 |> (++) (replicate (start - 1) ' ')
151− [ number (lineNum-1) ""
152− , number lineNum trimmedSourceLine
241+ do addLineNumber renderer Nothing width lineNum trimmedSourceLine
153− , number (lineNum+1) underline
242+ accent renderer $ "\n" ++ underline