{-# LANGUAGE ApplicativeDo #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE StrictData #-} module Refact.Options (Options (..), optionsWithHelp) where import Data.Char (isDigit) import Options.Applicative import Refact.Compat (MonadFail') import Refact.Internal (Verbosity (..)) import Text.Read (readMaybe) data Options = Options { -- | Where to process hints optionsTarget :: Maybe FilePath, -- | The refactorings to process optionsRefactFile :: Maybe FilePath, optionsInplace :: Bool, -- | Whether to overwrite the file inplace optionsOutput :: Maybe FilePath, optionsVerbosity :: Verbosity, -- | Ask before applying each hint optionsStep :: Bool, optionsDebug :: Bool, optionsRoundtrip :: Bool, optionsVersion :: Bool, optionsLanguage :: [String], optionsPos :: Maybe (Int, Int) } options :: Parser Options options = do optionsTarget <- optional (argument str (metavar "TARGET")) optionsRefactFile <- option (Just <$> str) $ mconcat [ long "refact-file", value Nothing, help $ "A file which specifies which refactorings to perform. " ++ "If not specified, it will be read from stdin, in which case TARGET must be specified." ] optionsInplace <- switch $ mconcat [ long "inplace", short 'i', help "Whether to overwrite the target inplace" ] optionsOutput <- optional . strOption $ mconcat [ long "output", short 'o', help "Name of the file to output to", metavar "FILE" ] optionsVerbosity <- option (str >>= parseVerbosity) $ mconcat [ long "verbosity", short 'v', value Normal, help "Specify verbosity, 0, 1 or 2. The default is 1 and 0 is silent." ] optionsStep <- switch $ mconcat [ short 's', long "step", help "Ask before applying each refactoring" ] optionsDebug <- switch $ mconcat [ long "debug", help "Output the GHC AST for debugging", internal ] optionsRoundtrip <- switch $ mconcat [ long "roundtrip", help "Run ghc-exactprint on the file", internal ] optionsVersion <- switch $ mconcat [ long "version", help "Display version number" ] optionsLanguage <- many . strOption $ mconcat [ long "language", short 'X', help "Language extensions (e.g. LambdaCase, RankNTypes)", metavar "Extensions" ] optionsPos <- option (Just <$> (str >>= parsePos)) $ mconcat [ long "pos", value Nothing, metavar ",", help "Apply hints relevant to a specific position" ] pure Options {..} optionsWithHelp :: ParserInfo Options optionsWithHelp = info (helper <*> options) $ mconcat [ fullDesc, progDesc "Automatically perform refactorings on haskell source files", header "refactor" ] parseVerbosity :: Monad m => String -> m Verbosity parseVerbosity = pure . \case "0" -> Silent "1" -> Normal "2" -> Loud _ -> Normal parsePos :: MonadFail' m => String -> m (Int, Int) parsePos s = case span isDigit s of (line, ',' : col) -> case (,) <$> readMaybe line <*> readMaybe col of Just l -> pure l Nothing -> fail "Invalid input" _ -> fail "Invalid input"