packages feed

haskell-debugger-0.12.0.0: hdb/Development/Debug/Options/Parser.hs

{-# LANGUAGE MultilineStrings #-}
-- | Options parser using optparse-applicative for the debugger options in
-- 'Development.Debug.Options'
module Development.Debug.Options.Parser
  ( -- * Command line options parsing
    parseHdbOptions
  ) where

import Options.Applicative hiding (command)

import Data.Version
import qualified Options.Applicative
import qualified Paths_haskell_debugger as P

import Colog.Core
import Development.Debug.Options

--------------------------------------------------------------------------------
-- Options parser
--------------------------------------------------------------------------------

-- | Parser for 'HdbDAPServer' options
serverParser :: Parser HdbOptions
serverParser = HdbDAPServer
  <$> option auto
      ( long "port"
     <> short 'p'
     <> metavar "PORT"
     <> help "DAP server port" )
  <*> verbosityParser Debug
  <*> internalInterpreterParser

-- | Parser for 'HdbCLI' options
cliParser :: Parser HdbOptions
cliParser = HdbCLI
  <$> strOption
      ( long "entry-point"
     <> short 'e'
     <> metavar "ENTRY_POINT"
     <> value "main"
     <> help "The name of the function that is called to start execution (default: main)" )
  <*> argument str
      ( metavar "ENTRY_FILE"
     <> help "The relative path from the project root to the file with the entry point for execution" )
  <*> many
        ( argument str
          ( metavar "ENTRY_ARGS..."
         <> help "The arguments passed to the entryPoint. If the entryPoint is main, these arguments are passed as environment arguments (as in getArgs) rather than direct function arguments."
          )
        )
  <*> option (words <$> str)
      ( long "extra-ghc-args"
     <> metavar "GHC_ARGS"
     <> value []
     <> help "Additional flags to pass to the ghc invocation that loads the program for debugging" )
  <*> verbosityParser Warning
  <*> internalInterpreterParser
  <*> (optional $ strOption
      ( long "debuggee-stdin"
     <> metavar "FILE"
     <> help """
          Provide a file to be used as stdin for the debuggee.

          If `hdb` is connected to a terminal, the debuggee input can be
          given interactively, interleaved with the debugger input.

          However, if the debuggee reads from stdin and `hdb` is invoked
          reading from a file or pipe (e.g. `hdb Main.hs < instructions`), then
          the debuggee output must be given separately using this flag (and
          `instructions` should only contain debugger commands).

          Otherwise, the debuggee and debugger will race to read from the file
          (regardless of buffering), which typically results in an error like
          `<stdin>: hGetLine: end of file`

          This flag does not do anything when using --internal-interpreter
        """
      ))

-- | Parser for 'HdbProxy' options
proxyParser :: Parser HdbOptions
proxyParser = HdbProxy
  <$> option auto
      ( long "port"
     <> short 'p'
     <> metavar "PORT"
     <> help "proxy port to which the debugger connects" )
  <*> verbosityParser Warning

-- | Parser for @hdb external-interpreter <write-fd> <read-fd>@
-- See Note [Custom external interpreter]
extInterpParser :: Parser HdbOptions
extInterpParser = HdbExternalInterpreter
  <$> argument auto
    ( metavar "WRITE_FD"
   <> help "external interpreter write file descriptor" )
  <*> argument auto
    ( metavar "READ_FD"
   <> help "external interpreter read file descriptor" )

-- | Combined parser for HdbOptions
hdbOptionsParser :: Parser HdbOptions
hdbOptionsParser = hsubparser
  ( Options.Applicative.command "server"
    ( info serverParser
      ( progDesc "Start the Haskell debugger in DAP server mode" ) )
 <> Options.Applicative.command "cli"
    ( info cliParser
      ( progDesc "Debug a Haskell program in CLI mode" ) )
 <> Options.Applicative.command "proxy"
    ( info proxyParser
      ( progDesc "Internal mode used by the DAP server to proxy the stdin/stdout to the DAP client's terminal" ) )
 <> Options.Applicative.command "external-interpreter"
    ( info extInterpParser
      ( progDesc "Start the custom-for-debugger external interpreter" ) )
  )
  <|> cliParser -- Default to CLI mode if no subcommand

-- | Parser for --version flag
versioner :: Parser (a -> a)
versioner = simpleVersioner $ "Haskell Debugger, version " ++ showVersion P.version

-- | Parser for --verbosity 0
--
-- The default verbosity differs by mode (#86):
-- - DAP server mode: DEBUG
-- - CLI mode: WARNING
verbosityParser :: Severity -> Parser Severity
verbosityParser vdef = option verb
    ( long "verbosity"
   <> short 'v'
   <> metavar "VERBOSITY"
   <> value vdef
   <> help "Logger verbosity in [0..3] interval, where 0 is silent and 3 is debug"
    )
  where
    verb = verbNum =<< auto
    verbNum n = case n :: Int of
      0 -> pure Error
      1 -> pure Warning
      2 -> pure Info
      3 -> pure Debug
      _ -> readerAbort (ErrorMsg "Verbosity must be a value in [0..3]")

-- | Parser for --internal-interpreter
--
-- Prefer running the debuggee on the debugger's internal interpreter rather
-- than using an external interpreter by default.
internalInterpreterParser :: Parser Bool
internalInterpreterParser =
  switch
    ( long "internal-interpreter"
   <> help "Prefer running the debuggee on the debugger's internal interpreter rather than on a separate (external-interpreter) process"
    )

-- | Main parser info
hdbParserInfo :: ParserInfo HdbOptions
hdbParserInfo = info (hdbOptionsParser <**> versioner <**> helper)
  ( fullDesc
 <> header "Haskell debugger supporting both CLI and DAP modes" )

-- | Parse command line arguments
parseHdbOptions :: IO HdbOptions
parseHdbOptions = customExecParser
  defaultPrefs{prefShowHelpOnError = True, prefShowHelpOnEmpty = True}
  hdbParserInfo