{-# LANGUAGE RecordWildCards, NoMonomorphismRestriction #-}

module Idea(
    Idea(..),
    rawIdea, idea, suggest, suggestRemove, ideaRemove, warn, ignore,
    rawIdeaN, suggestN, ignoreNoSuggestion,
    showIdeasJson, showIdeaANSI,
    Note(..), showNotes,
    Severity(..),
    ) where

import Data.List.Extra
import Config.Type
import HsColour
import Refact.Types hiding (SrcSpan)
import qualified Refact.Types as R
import Prelude
import GHC.Types.SrcLoc
import GHC.Utils.Outputable
import GHC.Util

import Language.Haskell.GhclibParserEx.GHC.Utils.Outputable

-- | An idea suggest by a 'Hint'.
data Idea = Idea
    {Idea -> [String]
ideaModule :: [String] -- ^ The modules the idea is for, usually a singleton.
    ,Idea -> [String]
ideaDecl :: [String] -- ^ The declarations the idea is for, usually a singleton, typically the function name, but may be a type name.
    ,Idea -> Severity
ideaSeverity :: Severity -- ^ The severity of the idea, e.g. 'Warning'.
    ,Idea -> String
ideaHint :: String -- ^ The name of the hint that generated the idea, e.g. @\"Use reverse\"@.
    ,Idea -> SrcSpan
ideaSpan :: SrcSpan -- ^ The source code the idea relates to.
    ,Idea -> String
ideaFrom :: String -- ^ The contents of the source code the idea relates to.
    ,Idea -> Maybe String
ideaTo :: Maybe String -- ^ The suggested replacement, or 'Nothing' for no replacement (e.g. on parse errors).
    ,Idea -> [Note]
ideaNote :: [Note] -- ^ Notes about the effect of applying the replacement.
    ,Idea -> [Refactoring SrcSpan]
ideaRefactoring :: [Refactoring R.SrcSpan] -- ^ How to perform this idea
    }
    deriving Idea -> Idea -> Bool
(Idea -> Idea -> Bool) -> (Idea -> Idea -> Bool) -> Eq Idea
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Idea -> Idea -> Bool
$c/= :: Idea -> Idea -> Bool
== :: Idea -> Idea -> Bool
$c== :: Idea -> Idea -> Bool
Eq

-- I don't use aeson here for 2 reasons:
-- 1) Aeson doesn't esape unicode characters, and I want to (allows me to ignore encoding)
-- 2) I want to control the format so it's slightly human readable as well
showIdeaJson :: Idea -> String
showIdeaJson :: Idea -> String
showIdeaJson idea :: Idea
idea@Idea{ideaSpan :: Idea -> SrcSpan
ideaSpan=srcSpan :: SrcSpan
srcSpan@SrcSpan{Int
String
srcSpanEndColumn :: SrcSpan -> Int
srcSpanEndLine' :: SrcSpan -> Int
srcSpanStartColumn :: SrcSpan -> Int
srcSpanStartLine' :: SrcSpan -> Int
srcSpanFilename :: SrcSpan -> String
srcSpanEndColumn :: Int
srcSpanEndLine' :: Int
srcSpanStartColumn :: Int
srcSpanStartLine' :: Int
srcSpanFilename :: String
..}, String
[String]
[Refactoring SrcSpan]
[Note]
Maybe String
Severity
ideaRefactoring :: [Refactoring SrcSpan]
ideaNote :: [Note]
ideaTo :: Maybe String
ideaFrom :: String
ideaHint :: String
ideaSeverity :: Severity
ideaDecl :: [String]
ideaModule :: [String]
ideaRefactoring :: Idea -> [Refactoring SrcSpan]
ideaNote :: Idea -> [Note]
ideaTo :: Idea -> Maybe String
ideaFrom :: Idea -> String
ideaHint :: Idea -> String
ideaSeverity :: Idea -> Severity
ideaDecl :: Idea -> [String]
ideaModule :: Idea -> [String]
..} = [(String, String)] -> String
forall a. Show a => [(a, String)] -> String
dict
    [(String
"module", [String] -> String
list ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map String -> String
str [String]
ideaModule)
    ,(String
"decl", [String] -> String
list ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map String -> String
str [String]
ideaDecl)
    ,(String
"severity", String -> String
str (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ Severity -> String
forall a. Show a => a -> String
show Severity
ideaSeverity)
    ,(String
"hint", String -> String
str String
ideaHint)
    ,(String
"file", String -> String
str String
srcSpanFilename)
    ,(String
"startLine", Int -> String
forall a. Show a => a -> String
show Int
srcSpanStartLine')
    ,(String
"startColumn", Int -> String
forall a. Show a => a -> String
show Int
srcSpanStartColumn)
    ,(String
"endLine", Int -> String
forall a. Show a => a -> String
show Int
srcSpanEndLine')
    ,(String
"endColumn", Int -> String
forall a. Show a => a -> String
show Int
srcSpanEndColumn)
    ,(String
"from", String -> String
str String
ideaFrom)
    ,(String
"to", String -> (String -> String) -> Maybe String -> String
forall b a. b -> (a -> b) -> Maybe a -> b
maybe String
"null" String -> String
str Maybe String
ideaTo)
    ,(String
"note", [String] -> String
list ((Note -> String) -> [Note] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String -> String
str (String -> String) -> (Note -> String) -> Note -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Note -> String
forall a. Show a => a -> String
show) [Note]
ideaNote))
    ,(String
"refactorings", String -> String
str (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ [Refactoring SrcSpan] -> String
forall a. Show a => a -> String
show [Refactoring SrcSpan]
ideaRefactoring)
    ]
  where
    str :: String -> String
str String
x = String
"\"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
escapeJSON String
x String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\""
    dict :: [(a, String)] -> String
dict [(a, String)]
xs = String
"{" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"," [a -> String
forall a. Show a => a -> String
show a
k String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
":" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
v | (a
k,String
v) <- [(a, String)]
xs] String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"}"
    list :: [String] -> String
list [String]
xs = String
"[" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"," [String]
xs String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"]"

-- | Show a list of 'Idea' values as a JSON string.
showIdeasJson :: [Idea] -> String
showIdeasJson :: [Idea] -> String
showIdeasJson [Idea]
ideas = String
"[" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"\n," ((Idea -> String) -> [Idea] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Idea -> String
showIdeaJson [Idea]
ideas) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"]"

instance Show Idea where
    show :: Idea -> String
show = (String -> String) -> Idea -> String
showEx String -> String
forall a. a -> a
id


-- | Show an 'Idea' with ANSI color codes to give syntax coloring to the Haskell code.
showIdeaANSI :: Idea -> String
showIdeaANSI :: Idea -> String
showIdeaANSI = (String -> String) -> Idea -> String
showEx String -> String
hsColourConsole

showEx :: (String -> String) -> Idea -> String
showEx :: (String -> String) -> Idea -> String
showEx String -> String
tt Idea{String
[String]
[Refactoring SrcSpan]
[Note]
Maybe String
SrcSpan
Severity
ideaRefactoring :: [Refactoring SrcSpan]
ideaNote :: [Note]
ideaTo :: Maybe String
ideaFrom :: String
ideaSpan :: SrcSpan
ideaHint :: String
ideaSeverity :: Severity
ideaDecl :: [String]
ideaModule :: [String]
ideaRefactoring :: Idea -> [Refactoring SrcSpan]
ideaNote :: Idea -> [Note]
ideaTo :: Idea -> Maybe String
ideaFrom :: Idea -> String
ideaSpan :: Idea -> SrcSpan
ideaHint :: Idea -> String
ideaSeverity :: Idea -> Severity
ideaDecl :: Idea -> [String]
ideaModule :: Idea -> [String]
..} = [String] -> String
unlines ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$
    [SrcSpan -> String
showSrcSpan SrcSpan
ideaSpan String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
": " String -> String -> String
forall a. [a] -> [a] -> [a]
++ (if String
ideaHint String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"" then String
"" else Severity -> String
forall a. Show a => a -> String
show Severity
ideaSeverity String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
": " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
ideaHint)] [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++
    String -> Maybe String -> [String]
f String
"Found" (String -> Maybe String
forall a. a -> Maybe a
Just String
ideaFrom) [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ String -> Maybe String -> [String]
f String
"Perhaps" Maybe String
ideaTo [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++
    [String
"Note: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
n | let n :: String
n = [Note] -> String
showNotes [Note]
ideaNote, String
n String -> String -> Bool
forall a. Eq a => a -> a -> Bool
/= String
""]
    where
        f :: String -> Maybe String -> [String]
f String
msg Maybe String
Nothing = []
        f String
msg (Just String
x) | [String] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [String]
xs = [String
msg String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" you should remove it."]
                       | Bool
otherwise = (String
msg String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
":") String -> [String] -> [String]
forall a. a -> [a] -> [a]
: (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String
"  "String -> String -> String
forall a. [a] -> [a] -> [a]
++) [String]
xs
            where xs :: [String]
xs = String -> [String]
lines (String -> [String]) -> String -> [String]
forall a b. (a -> b) -> a -> b
$ String -> String
tt String
x


rawIdea :: Severity -> String -> SrcSpan -> String -> Maybe String -> [Note]-> [Refactoring R.SrcSpan] -> Idea
rawIdea :: Severity
-> String
-> SrcSpan
-> String
-> Maybe String
-> [Note]
-> [Refactoring SrcSpan]
-> Idea
rawIdea = [String]
-> [String]
-> Severity
-> String
-> SrcSpan
-> String
-> Maybe String
-> [Note]
-> [Refactoring SrcSpan]
-> Idea
Idea [] []

rawIdeaN :: Severity -> String -> SrcSpan -> String -> Maybe String -> [Note] -> Idea
rawIdeaN :: Severity
-> String -> SrcSpan -> String -> Maybe String -> [Note] -> Idea
rawIdeaN Severity
a String
b SrcSpan
c String
d Maybe String
e [Note]
f = [String]
-> [String]
-> Severity
-> String
-> SrcSpan
-> String
-> Maybe String
-> [Note]
-> [Refactoring SrcSpan]
-> Idea
Idea [] [] Severity
a String
b SrcSpan
c String
d Maybe String
e [Note]
f []

idea :: (GHC.Utils.Outputable.Outputable a, GHC.Utils.Outputable.Outputable b) =>
         Severity -> String -> Located a -> Located b -> [Refactoring R.SrcSpan] -> Idea
idea :: Severity
-> String
-> Located a
-> Located b
-> [Refactoring SrcSpan]
-> Idea
idea Severity
severity String
hint Located a
from Located b
to =
  Severity
-> String
-> SrcSpan
-> String
-> Maybe String
-> [Note]
-> [Refactoring SrcSpan]
-> Idea
rawIdea Severity
severity String
hint (Located a -> SrcSpan
forall l e. GenLocated l e -> l
getLoc Located a
from) (Located a -> String
forall a. Outputable a => a -> String
unsafePrettyPrint Located a
from) (String -> Maybe String
forall a. a -> Maybe a
Just (String -> Maybe String) -> String -> Maybe String
forall a b. (a -> b) -> a -> b
$ Located b -> String
forall a. Outputable a => a -> String
unsafePrettyPrint Located b
to) []

-- Construct an Idea that suggests "Perhaps you should remove it."
ideaRemove :: Severity -> String -> SrcSpan -> String -> [Refactoring R.SrcSpan] -> Idea
ideaRemove :: Severity
-> String -> SrcSpan -> String -> [Refactoring SrcSpan] -> Idea
ideaRemove Severity
severity String
hint SrcSpan
span String
from = Severity
-> String
-> SrcSpan
-> String
-> Maybe String
-> [Note]
-> [Refactoring SrcSpan]
-> Idea
rawIdea Severity
severity String
hint SrcSpan
span String
from (String -> Maybe String
forall a. a -> Maybe a
Just String
"") []

suggest :: (GHC.Utils.Outputable.Outputable a, GHC.Utils.Outputable.Outputable b) =>
            String -> Located a -> Located b -> [Refactoring R.SrcSpan] -> Idea
suggest :: String -> Located a -> Located b -> [Refactoring SrcSpan] -> Idea
suggest = Severity
-> String
-> Located a
-> Located b
-> [Refactoring SrcSpan]
-> Idea
forall a b.
(Outputable a, Outputable b) =>
Severity
-> String
-> Located a
-> Located b
-> [Refactoring SrcSpan]
-> Idea
idea Severity
Suggestion

suggestRemove :: String -> SrcSpan -> String -> [Refactoring R.SrcSpan] -> Idea
suggestRemove :: String -> SrcSpan -> String -> [Refactoring SrcSpan] -> Idea
suggestRemove = Severity
-> String -> SrcSpan -> String -> [Refactoring SrcSpan] -> Idea
ideaRemove Severity
Suggestion

warn :: (GHC.Utils.Outputable.Outputable a, GHC.Utils.Outputable.Outputable b) =>
         String -> Located a -> Located b -> [Refactoring R.SrcSpan] -> Idea
warn :: String -> Located a -> Located b -> [Refactoring SrcSpan] -> Idea
warn = Severity
-> String
-> Located a
-> Located b
-> [Refactoring SrcSpan]
-> Idea
forall a b.
(Outputable a, Outputable b) =>
Severity
-> String
-> Located a
-> Located b
-> [Refactoring SrcSpan]
-> Idea
idea Severity
Warning

ignoreNoSuggestion :: (GHC.Utils.Outputable.Outputable a)
                    => String -> Located a -> Idea
ignoreNoSuggestion :: String -> Located a -> Idea
ignoreNoSuggestion String
hint Located a
x = Severity
-> String -> SrcSpan -> String -> Maybe String -> [Note] -> Idea
rawIdeaN Severity
Ignore String
hint (Located a -> SrcSpan
forall l e. GenLocated l e -> l
getLoc Located a
x) (Located a -> String
forall a. Outputable a => a -> String
unsafePrettyPrint Located a
x) Maybe String
forall a. Maybe a
Nothing []

ignore :: (GHC.Utils.Outputable.Outputable a) =>
           String -> Located a -> Located a -> [Refactoring R.SrcSpan] -> Idea
ignore :: String -> Located a -> Located a -> [Refactoring SrcSpan] -> Idea
ignore = Severity
-> String
-> Located a
-> Located a
-> [Refactoring SrcSpan]
-> Idea
forall a b.
(Outputable a, Outputable b) =>
Severity
-> String
-> Located a
-> Located b
-> [Refactoring SrcSpan]
-> Idea
idea Severity
Ignore

ideaN :: (GHC.Utils.Outputable.Outputable a) =>
          Severity -> String -> Located a -> Located a -> Idea
ideaN :: Severity -> String -> Located a -> Located a -> Idea
ideaN Severity
severity String
hint Located a
from Located a
to = Severity
-> String
-> Located a
-> Located a
-> [Refactoring SrcSpan]
-> Idea
forall a b.
(Outputable a, Outputable b) =>
Severity
-> String
-> Located a
-> Located b
-> [Refactoring SrcSpan]
-> Idea
idea Severity
severity String
hint Located a
from Located a
to []

suggestN :: (GHC.Utils.Outputable.Outputable a) =>
             String -> Located a -> Located a -> Idea
suggestN :: String -> Located a -> Located a -> Idea
suggestN = Severity -> String -> Located a -> Located a -> Idea
forall a.
Outputable a =>
Severity -> String -> Located a -> Located a -> Idea
ideaN Severity
Suggestion