{-# LANGUAGE RecordWildCards #-}

module Report(writeReport) where

import Idea
import Data.Tuple.Extra
import Data.List.Extra
import qualified Data.List.NonEmpty as NE
import Data.Maybe
import Data.Version
import Timing
import Paths_hlint
import HsColour
import EmbedData
import qualified GHC.Util as GHC


writeTemplate :: FilePath -> [(String,[String])] -> FilePath -> IO ()
writeTemplate :: String -> [(String, [String])] -> String -> IO ()
writeTemplate String
dataDir [(String, [String])]
content String
to =
    String -> String -> IO ()
writeFile String
to forall a b. (a -> b) -> a -> b
$ [String] -> String
unlines forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap String -> [String]
f forall a b. (a -> b) -> a -> b
$ String -> [String]
lines String
reportTemplate
    where
        f :: String -> [String]
f (Char
'$':String
xs) = forall a. a -> Maybe a -> a
fromMaybe [Char
'$'forall a. a -> [a] -> [a]
:String
xs] forall a b. (a -> b) -> a -> b
$ forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup String
xs [(String, [String])]
content
        f String
x = [String
x]


writeReport :: FilePath -> FilePath -> [Idea] -> IO ()
writeReport :: String -> String -> [Idea] -> IO ()
writeReport String
dataDir String
file [Idea]
ideas = forall a. String -> String -> IO a -> IO a
timedIO String
"Report" String
file forall a b. (a -> b) -> a -> b
$ String -> [(String, [String])] -> String -> IO ()
writeTemplate String
dataDir [(String, [String])]
inner String
file
    where
        generateIds :: [String] -> [(String,Int)] -- sorted by name
        generateIds :: [String] -> [(String, Int)]
generateIds = forall a b. (a -> b) -> [a] -> [b]
map (forall a. NonEmpty a -> a
NE.head forall a b c. (a -> b) -> (a -> c) -> a -> (b, c)
&&& forall (t :: * -> *) a. Foldable t => t a -> Int
length) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a. (Foldable f, Eq a) => f a -> [NonEmpty a]
NE.group -- must be already sorted
        files :: [(String, Int)]
files = [String] -> [(String, Int)]
generateIds forall a b. (a -> b) -> a -> b
$ forall a. Ord a => [a] -> [a]
sort forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map (SrcSpan -> String
GHC.srcSpanFilename forall b c a. (b -> c) -> (a -> b) -> a -> c
. Idea -> SrcSpan
ideaSpan) [Idea]
ideas
        hints :: [(String, Int)]
hints = [String] -> [(String, Int)]
generateIds forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map Idea -> String
hintName forall a b. (a -> b) -> a -> b
$ forall b a. Ord b => (a -> b) -> [a] -> [a]
sortOn (forall a. Num a => a -> a
negate forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Enum a => a -> Int
fromEnum forall b c a. (b -> c) -> (a -> b) -> a -> c
. Idea -> Severity
ideaSeverity forall a b c. (a -> b) -> (a -> c) -> a -> (b, c)
&&& Idea -> String
hintName) [Idea]
ideas
        hintName :: Idea -> String
hintName Idea
x = forall a. Show a => a -> String
show (Idea -> Severity
ideaSeverity Idea
x) forall a. [a] -> [a] -> [a]
++ String
": " forall a. [a] -> [a] -> [a]
++ Idea -> String
ideaHint Idea
x

        inner :: [(String, [String])]
inner = if forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Idea]
ideas then [(String, [String])]
emptyInner else [(String, [String])]
nonEmptyInner

        emptyInner :: [(String, [String])]
emptyInner = [(String
"VERSION",[Char
'v' forall a. a -> [a] -> [a]
: Version -> String
showVersion Version
version]),(String
"CONTENT", [String
"No hints"]),
                      (String
"HINTS", [String
"<li>No hints</li>"]),(String
"FILES", [String
"<li>No files</li>"])]

        nonEmptyInner :: [(String, [String])]
nonEmptyInner = [(String
"VERSION",[Char
'v' forall a. a -> [a] -> [a]
: Version -> String
showVersion Version
version]),(String
"CONTENT",[String]
content),
                         (String
"HINTS",forall {a}. Show a => String -> [(String, a)] -> [String]
list String
"hint" [(String, Int)]
hints),(String
"FILES",forall {a}. Show a => String -> [(String, a)] -> [String]
list String
"file" [(String, Int)]
files)]

        content :: [String]
content = forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\Idea
i -> String -> Idea -> [String]
writeIdea (Idea -> String
getClass Idea
i) Idea
i) [Idea]
ideas
        getClass :: Idea -> String
getClass Idea
i = String
"hint" forall a. [a] -> [a] -> [a]
++ forall {b} {b}. Eq b => [(b, b)] -> b -> String
f [(String, Int)]
hints (Idea -> String
hintName Idea
i) forall a. [a] -> [a] -> [a]
++ String
" file" forall a. [a] -> [a] -> [a]
++ forall {b} {b}. Eq b => [(b, b)] -> b -> String
f [(String, Int)]
files (SrcSpan -> String
GHC.srcSpanFilename forall a b. (a -> b) -> a -> b
$ Idea -> SrcSpan
ideaSpan Idea
i)
            where f :: [(b, b)] -> b -> String
f [(b, b)]
xs b
x = forall a. Show a => a -> String
show forall a b. (a -> b) -> a -> b
$ forall a. Partial => Maybe a -> a
fromJust forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> [a] -> Maybe Int
findIndex (forall a. Eq a => a -> a -> Bool
(==) b
x forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a, b) -> a
fst) [(b, b)]
xs

        list :: String -> [(String, a)] -> [String]
list String
mode = forall a b c. Enum a => (a -> b -> c) -> a -> [b] -> [c]
zipWithFrom forall {a} {p}. (Show a, Show p) => p -> (String, a) -> String
f Integer
0
            where
                f :: p -> (String, a) -> String
f p
i (String
name,a
n) = String
"<li><a id=" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show String
id forall a. [a] -> [a] -> [a]
++ String
" href=\"javascript:show('" forall a. [a] -> [a] -> [a]
++ String
id forall a. [a] -> [a] -> [a]
++ String
"')\">" forall a. [a] -> [a] -> [a]
++
                               String -> String
escapeHTML String
name forall a. [a] -> [a] -> [a]
++ String
" (" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show a
n forall a. [a] -> [a] -> [a]
++ String
")</a></li>"
                    where id :: String
id = String
mode forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show p
i


writeIdea :: String -> Idea -> [String]
writeIdea :: String -> Idea -> [String]
writeIdea String
cls Idea{String
[String]
[Refactoring SrcSpan]
[Note]
Maybe String
SrcSpan
Severity
ideaRefactoring :: Idea -> [Refactoring SrcSpan]
ideaNote :: Idea -> [Note]
ideaTo :: Idea -> Maybe String
ideaFrom :: Idea -> String
ideaDecl :: Idea -> [String]
ideaModule :: Idea -> [String]
ideaRefactoring :: [Refactoring SrcSpan]
ideaNote :: [Note]
ideaTo :: Maybe String
ideaFrom :: String
ideaSpan :: SrcSpan
ideaHint :: String
ideaSeverity :: Severity
ideaDecl :: [String]
ideaModule :: [String]
ideaHint :: Idea -> String
ideaSeverity :: Idea -> Severity
ideaSpan :: Idea -> SrcSpan
..} =
    [String
"<div class=" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show String
cls forall a. [a] -> [a] -> [a]
++ String
">"
    ,String -> String
escapeHTML (SrcSpan -> String
GHC.showSrcSpan SrcSpan
ideaSpan forall a. [a] -> [a] -> [a]
++ String
": " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Severity
ideaSeverity forall a. [a] -> [a] -> [a]
++ String
": " forall a. [a] -> [a] -> [a]
++ String
ideaHint) forall a. [a] -> [a] -> [a]
++ String
"<br/>"
    ,String
"Found<br/>"
    ,String -> String
hsColourHTML String
ideaFrom] forall a. [a] -> [a] -> [a]
++
    (case Maybe String
ideaTo of
        Maybe String
Nothing -> []
        Just String
to ->
            [String
"Perhaps" forall a. [a] -> [a] -> [a]
++ (if String
to forall a. Eq a => a -> a -> Bool
== String
"" then String
" you should remove it." else String
"") forall a. [a] -> [a] -> [a]
++ String
"<br/>"
            ,String -> String
hsColourHTML String
to]) forall a. [a] -> [a] -> [a]
++
    [let n :: String
n = [Note] -> String
showNotes [Note]
ideaNote in if String
n forall a. Eq a => a -> a -> Bool
/= String
"" then String
"<span class='note'>Note: " forall a. [a] -> [a] -> [a]
++ String -> String
writeNote String
n forall a. [a] -> [a] -> [a]
++ String
"</span>" else String
""
    ,String
"</div>"
    ,String
""]

-- Unescaped, but may have `backticks` for code
writeNote :: String -> String
writeNote :: String -> String
writeNote = [String] -> String
f forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (Partial, Eq a) => [a] -> [a] -> [[a]]
splitOn String
"`"
    where f :: [String] -> String
f (String
a:String
b:[String]
c) = String -> String
escapeHTML String
a forall a. [a] -> [a] -> [a]
++ String
"<tt>" forall a. [a] -> [a] -> [a]
++ String -> String
escapeHTML String
b forall a. [a] -> [a] -> [a]
++ String
"</tt>" forall a. [a] -> [a] -> [a]
++ [String] -> String
f [String]
c
          f [String]
xs = forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap String -> String
escapeHTML [String]
xs