{-# LANGUAGE RecordWildCards, TupleSections #-}
module Summary (generateSummary) where
import qualified Data.Map as Map
import Control.Monad.Extra
import System.FilePath
import Data.List.Extra
import System.Directory
import Idea
import Apply
import Hint.Type
import Hint.All
import Config.Type
import Test.Annotations
data BuiltinKey = BuiltinKey
{ builtinName :: !String
, builtinSeverity :: !Severity
, builtinRefactoring :: !Bool
} deriving (Eq, Ord)
data BuiltinValue = BuiltinValue
{ builtinInp :: !String
, builtinFrom :: !String
, builtinTo :: !(Maybe String)
}
dedupeBuiltin :: [(BuiltinKey, BuiltinValue)] -> [(BuiltinKey, BuiltinValue)]
dedupeBuiltin = Map.toAscList . Map.fromListWith (\_ old -> old)
generateSummary :: [Setting] -> IO String
generateSummary settings = do
builtinHints <- mkBuiltinSummary
let lhsRhsHints = [hint | SettingMatchExp hint <- settings]
pure $ genBuiltinSummaryMd builtinHints lhsRhsHints
mkBuiltinSummary :: IO [(String, [(BuiltinKey, BuiltinValue)])]
mkBuiltinSummary = forM builtinHints $ \(name, hint) -> (name,) <$> do
let file = "src/Hint" </> name <.> "hs"
b <- doesFileExist file
if not b then do
putStrLn $ "Couldn't find source hint file " ++ file ++ ", some hints will be missing"
pure []
else do
tests <- parseTestFile file
fmap dedupeBuiltin <$> concatForM tests $ \(TestCase _ _ inp _ _) -> do
m <- parseModuleEx defaultParseFlags file (Just inp)
pure $ case m of
Right m -> map (ideaToValue inp) $ applyHints [] hint [m]
Left _ -> []
where
ideaToValue :: String -> Idea -> (BuiltinKey, BuiltinValue)
ideaToValue inp Idea{..} = (k, v)
where
to = fmap (\x -> if "Combine with " `isPrefixOf` x then replace "\\" "/" x else x) ideaTo
k = BuiltinKey ideaHint ideaSeverity (notNull ideaRefactoring)
v = BuiltinValue inp ideaFrom to
genBuiltinSummaryMd :: [(String, [(BuiltinKey, BuiltinValue)])] -> [HintRule] -> String
genBuiltinSummaryMd builtins lhsRhs = unlines $
[ "# Summary of Hints"
, ""
, "This page is auto-generated from `hlint --generate-summary`."
] ++
concat ["" : ("## Builtin " ++ group ) : "" : builtinTable hints | (group, hints) <- builtins] ++
[ ""
, "## Configured hints"
, ""
]
++ lhsRhsTable lhsRhs
row :: [String] -> [String]
row xs = ["<tr>"] ++ xs ++ ["</tr>"]
haskell :: String -> [String]
haskell s
| '\n' `elem` s = ["<pre>", s, "</pre>"]
| otherwise = ["<code>", s, "</code>", "<br>"]
builtinTable :: [(BuiltinKey, BuiltinValue)] -> [String]
builtinTable builtins =
["<table>"]
++ row ["<th>Hint Name</th>", "<th>Hint</th>", "<th>Severity</th>"]
++ concatMap (uncurry showBuiltin) builtins
++ ["</table>"]
showBuiltin :: BuiltinKey -> BuiltinValue -> [String]
showBuiltin BuiltinKey{..} BuiltinValue{..} = row1
where
row1 = row $
[ "<td>" ++ builtinName ++ "</td>"
, "<td>"
, "Example:"
]
++ haskell builtinInp
++ ["Found:"]
++ haskell builtinFrom
++ ["Suggestion:"]
++ haskell to
++ ["Does not support refactoring." | not builtinRefactoring]
++ ["</td>"] ++
[ "<td>" ++ show builtinSeverity ++ "</td>"
]
to = case builtinTo of
Nothing -> ""
Just "" -> "Perhaps you should remove it."
Just s -> s
lhsRhsTable :: [HintRule] -> [String]
lhsRhsTable hints =
["<table>"]
++ row ["<th>Hint Name</th>", "<th>Hint</th>", "<th>Severity</th>"]
++ concatMap showLhsRhs hints
++ ["</table>"]
showLhsRhs :: HintRule -> [String]
showLhsRhs HintRule{..} = row $
[ "<td>" ++ hintRuleName ++ "</td>"
, "<td>"
, "LHS:"
]
++ haskell (show hintRuleLHS)
++ ["RHS:"]
++ haskell (show hintRuleRHS)
++
[ "</td>"
, "<td>" ++ show hintRuleSeverity ++ "</td>"
]