module HsDev.Tools.AutoFix (
Correction(..),
correct, corrections,
autoFix_, autoFix, updateRegion,
CorrectorMatch,
correctors,
match,
findCorrector,
Canonicalize(..),
module Data.Mark
) where
import Control.Applicative
import Data.Aeson
import Data.Maybe (listToMaybe, mapMaybe)
import Data.Mark hiding (at, Editable(..))
import HsDev.Symbols (Canonicalize(..))
import HsDev.Symbols.Location (Location(..), Position(..), moduleSource)
import HsDev.Tools.Base (matchRx, at)
import HsDev.Tools.GhcMod
import HsDev.Util ((.::))
data Correction = Correction {
correctionFile :: FilePath,
correctionType :: String,
description :: String,
message :: String,
solution :: String,
corrector :: Replace String }
deriving (Eq, Read, Show)
instance Canonicalize Correction where
canonicalize c = do
f' <- canonicalize (correctionFile c)
return c { correctionFile = f' }
instance ToJSON Correction where
toJSON (Correction f t desc msg sol cor) = object [
"file" .= f,
"type" .= t,
"description" .= desc,
"message" .= msg,
"solution" .= sol,
"corrector" .= cor]
instance FromJSON Correction where
parseJSON = withObject "correction" $ \v -> Correction <$>
v .:: "file" <*>
v .:: "type" <*>
v .:: "description" <*>
v .:: "message" <*>
v .:: "solution" <*>
v .:: "corrector"
correct :: Correction -> EditM String ()
correct c = run [corrector c]
corrections :: [OutputMessage] -> [Correction]
corrections = mapMaybe toCorrection where
toCorrection :: OutputMessage -> Maybe Correction
toCorrection msg = do
file <- moduleSource $ locationModule (errorLocation msg)
Position l c <- locationPosition (errorLocation msg)
let
pt = Point (pred l) (pred c)
findCorrector file pt (errorMessage msg)
autoFix_ :: [Correction] -> EditM String ()
autoFix_ = mapM_ correct
autoFix :: [Correction] -> [Correction] -> EditM String [Correction]
autoFix fix' up' = autoFix_ fix' >> mapM updateRegion up'
updateRegion :: Correction -> EditM String Correction
updateRegion corr = do
region' <- mapRegion $ replaceRegion (corrector corr)
return $ corr { corrector = (corrector corr) { replaceRegion = region' } }
type CorrectorMatch = FilePath -> Point -> String -> Maybe Correction
correctors :: [CorrectorMatch]
correctors = [
match "^The import of `([\\w\\.]+)' is redundant" $ \g file pt -> Correction file
"Redundant import"
("Redundant import: " ++ (g `at` 1)) ""
"Remove import"
(eraser (pt `regionSize` linesSize 1)),
match "Found:\n (.*?)\nWhy not:\n (.*?)$" $ \g file pt -> Correction file
"Why not?"
("Replace '" ++ (g `at` 1) ++ "' with '" ++ (g `at` 2) ++ "'") ""
"Replace with suggestion"
(replacer (pt `regionSize` stringSize (length $ g `at` 1)) (g `at` 2))]
match :: String -> ((Int -> Maybe String) -> FilePath -> Point -> Correction) -> CorrectorMatch
match pat f file pt str = do
g <- matchRx pat str
return (f g file pt) { correctionFile = file, message = str }
findCorrector :: FilePath -> Point -> String -> Maybe Correction
findCorrector file pt msg = listToMaybe $ mapMaybe (\corr -> corr file pt msg) correctors