module GHCMod.Options ( parseArgs, GhcModCommands(..) ) where import Options.Applicative import Options.Applicative.Types import Language.Haskell.GhcMod.Types import Control.Arrow import GHCMod.Options.Commands import GHCMod.Version import GHCMod.Options.DocUtils parseArgs :: IO (Options, GhcModCommands) parseArgs = execParser opts where opts = info (argAndCmdSpec <**> helpVersion) $$ fullDesc ## header "ghc-mod: Happy Haskell Programming" helpVersion :: Parser (a -> a) helpVersion = helper <*> abortOption (InfoMsg ghcModVersion) $$ long "version" ## help "Print the version of the program." <*> argument r $$ value id ## metavar "" where r :: ReadM (a -> a) r = do v <- readerAsk case v of "help" -> readerAbort ShowHelpText "version" -> readerAbort $ InfoMsg ghcModVersion _ -> return id argAndCmdSpec :: Parser (Options, GhcModCommands) argAndCmdSpec = (,) <$> globalArgSpec <*> commandsSpec splitOn :: Eq a => a -> [a] -> ([a], [a]) splitOn c = second (drop 1) . break (==c) getLogLevel :: Int -> GmLogLevel getLogLevel = toEnum . min 7 logLevelParser :: Parser GmLogLevel logLevelParser = getLogLevel <$> silentSwitch <||> logLevelSwitch <||> logLevelOption where logLevelOption = option int $$ long "verbose" ## metavar "LEVEL" ## value 4 ## showDefault ## help "Set log level. (0-7)" logLevelSwitch = (4+) . length <$> many $$ flag' () $$ short 'v' ## help "Increase log level" silentSwitch = flag' 0 $$ long "silent" ## short 's' ## help "Be silent, set log level to 0" outputOptsSpec :: Parser OutputOpts outputOptsSpec = OutputOpts <$> logLevelParser <*> flag PlainStyle LispStyle $$ long "tolisp" ## short 'l' ## help "Format output as an S-Expression" <*> LineSeparator <$$> strOption $$ long "boundary" ## long "line-separator" ## short 'b' ## metavar "SEP" ## value "\0" ## showDefault ## help "Output line separator" <*> optional $$ splitOn ',' <$$> strOption $$ long "line-prefix" ## metavar "OUT,ERR" ## help "Output prefixes" programsArgSpec :: Parser Programs programsArgSpec = Programs <$> strOption $$ long "with-ghc" ## value "ghc" ## showDefault ## help "GHC executable to use" <*> strOption $$ long "with-ghc-pkg" ## value "ghc-pkg" ## showDefault ## help "ghc-pkg executable to use (only needed when guessing from GHC path fails)" <*> strOption $$ long "with-cabal" ## value "cabal" ## showDefault ## help "cabal-install executable to use" <*> strOption $$ long "with-stack" ## value "stack" ## showDefault ## help "stack executable to use" globalArgSpec :: Parser Options globalArgSpec = Options <$> outputOptsSpec <*> programsArgSpec <*> many $$ strOption $$ long "ghcOpt" ## long "ghc-option" ## short 'g' ## metavar "OPT" ## help "Option to be passed to GHC" <*> many fileMappingSpec where {- File map docs: CLI options: * `--map-file "file1.hs=file2.hs"` can be used to tell ghc-mod that it should take source code for `file1.hs` from `file2.hs`. `file1.hs` can be either full path, or path relative to project root. `file2.hs` has to be either relative to project root, or full path (preferred). * `--map-file "file.hs"` can be used to tell ghc-mod that it should take source code for `file.hs` from stdin. File end marker is `\n\EOT\n`, i.e. `\x0A\x04\x0A`. `file.hs` may or may not exist, and should be either full path, or relative to project root. Interactive commands: * `map-file file.hs` -- tells ghc-modi to read `file.hs` source from stdin. Works the same as second form of `--map-file` CLI option. * `unmap-file file.hs` -- unloads previously mapped file, so that it's no longer mapped. `file.hs` can be full path or relative to project root, either will work. Exposed functions: * `loadMappedFile :: FilePath -> FilePath -> GhcModT m ()` -- maps `FilePath`, given as first argument to take source from `FilePath` given as second argument. Works exactly the same as first form of `--map-file` CLI option. * `loadMappedFileSource :: FilePath -> String -> GhcModT m ()` -- maps `FilePath`, given as first argument to have source as given by second argument. Works exactly the same as second form of `--map-file` CLI option, sans reading from stdin. * `unloadMappedFile :: FilePath -> GhcModT m ()` -- unmaps `FilePath`, given as first argument, and removes any temporary files created when file was mapped. Works exactly the same as `unmap-file` interactive command -} fileMappingSpec = getFileMapping . splitOn '=' <$> strOption $$ long "map-file" ## metavar "MAPPING" ## help "Redirect one file to another, --map-file \"file1.hs=file2.hs\"" getFileMapping = second (\i -> if null i then Nothing else Just i)