hstyle-0.2.0.3: src/HStyle/Rule.hs
{-# LANGUAGE ExistentialQuantification, OverloadedStrings #-}
module HStyle.Rule
( Rule (..)
, FileState (..)
, Options (..)
, runRule
) where
import Control.Monad (foldM, forM_, unless)
import qualified Data.Text as T
import qualified Data.Text.IO as T
import qualified Language.Haskell.Exts.Annotated as H
import HStyle.Block
import HStyle.Selector
import HStyle.Checker
import HStyle.Fixer
-- | Compose the elements of a rule. Use ExistentialQuantification so the
-- internal state of a rule cannot be touched from the outside.
data Rule = forall a. Rule (Selector a) (Checker a) (Fixer a)
data FileState = FileState
{ -- | A block holding the file contents
fileBlock :: Block
, -- | Flag indicating whether or not the in-memory representation differs
-- from the file on disk
fileUpdated :: Bool
, -- | Flag indicating that all checks were OK
fileOk :: Bool
} deriving (Show)
-- | Options for checking files
data Options = Options
{ -- | Attempt to fix files
optionsFix :: Bool
, -- | Be quiet
optionsQuiet :: Bool
} deriving (Show)
-- | Represents fixing status
data Fix
= DontFix -- ^ User doesn't want to fix it
| CouldntFix -- ^ Our library is unable to fix it
| Fixed -- ^ Fixed, result
deriving (Eq, Show)
runRule :: Options -> FilePath
-> (H.Module H.SrcSpanInfo, [H.Comment])
-> FileState -> Rule
-> IO FileState
runRule options file mdc fileState (Rule selector checker fixer) =
foldM step fileState $ selector mdc $ fileBlock fileState
where
step fs (x, b) = checkBlock options file checker fixer fs x b
checkBlock :: Options -> FilePath -> Checker a -> Fixer a -> FileState
-> a -> Block
-> IO FileState
checkBlock options file checker fixer fs x block = do
-- Determine problems, and attempt to fix (lazily)
let problems = checker x block
(fix, block') = case (optionsFix options, fixer x block) of
(False, _) -> (DontFix, block)
(True, Nothing) -> (CouldntFix, block)
(True, Just b) -> (Fixed, b)
-- Output our results for this check
forM_ problems $ \(i, problem) -> do
let line = absoluteLineNumber i block
T.putStrLn $ T.pack file `T.append` ":" `T.append`
T.pack (show line) `T.append` ": " `T.append` problem
unless (optionsQuiet options) $ do
T.putStrLn " Found:"
T.putStr $ prettyBlock 4 block
case fix of
DontFix -> return ()
CouldntFix -> T.putStrLn " (Couldn't automatically fix)"
Fixed -> do
T.putStrLn " Fixed to:"
T.putStr $ prettyBlock 4 block'
T.putStrLn ""
-- Return updated file state
return fs
{ fileBlock = updateSubBlock block block' (fileBlock fs)
, fileUpdated = fileUpdated fs || fix == Fixed
, fileOk = fileOk fs && null problems
}