{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE CPP #-} module CommandArgs ( HDevTools(..) , loadHDevTools , pathArg ) where import Cabal (findFile) import Data.Version (showVersion) import Paths_hdevtools (version) import qualified Config import System.Console.CmdArgs.Implicit import System.Console.CmdArgs.Explicit (splitArgs) import System.Directory (getCurrentDirectory) import System.Environment (getProgName, withArgs, getArgs) import System.FilePath (takeDirectory) import System.Info (arch, os) programVersion :: String programVersion = "version " ++ showVersion version cabalVersion :: String cabalVersion = "cabal-" ++ VERSION_Cabal fullVersion :: String fullVersion = concat [ programVersion , " (" , "ghc-", Config.cProjectVersion, "-", arch, "-", os , ", ", cabalVersion , ")" ] data HDevTools = Admin { socket :: Maybe FilePath , ghcOpts :: [String] , start_server :: Bool , cabalOpts :: [String] , noDaemon :: Bool , status :: Bool , stop_server :: Bool , debug :: Bool , noStack :: Bool } | Check { socket :: Maybe FilePath , ghcOpts :: [String] , cabalOpts :: [String] , path :: Maybe String , file :: String , json :: Bool , debug :: Bool , noStack :: Bool , noTH :: Bool } | ModuleFile { socket :: Maybe FilePath , ghcOpts :: [String] , cabalOpts :: [String] , module_ :: String , debug :: Bool , noStack :: Bool } | Info { socket :: Maybe FilePath , ghcOpts :: [String] , cabalOpts :: [String] , path :: Maybe String , file :: String , identifier :: String , debug :: Bool , noStack :: Bool , noTH :: Bool } | Type { socket :: Maybe FilePath , ghcOpts :: [String] , cabalOpts :: [String] , path :: Maybe String , file :: String , line :: Int , col :: Int , debug :: Bool , noStack :: Bool , noTH :: Bool } | FindSymbol { socket :: Maybe FilePath , ghcOpts :: [String] , cabalOpts :: [String] , symbol :: String , files :: [String] , debug :: Bool , noStack :: Bool , noTH :: Bool } deriving (Show, Data, Typeable) dummyAdmin :: HDevTools dummyAdmin = Admin { socket = Nothing , ghcOpts = [] , cabalOpts = [] , start_server = False , noDaemon = False , status = False , stop_server = False , debug = False , noStack = False } dummyCheck :: HDevTools dummyCheck = Check { socket = Nothing , ghcOpts = [] , cabalOpts = [] , path = Nothing , file = "" , json = False , debug = False , noTH = False , noStack = False } dummyModuleFile :: HDevTools dummyModuleFile = ModuleFile { socket = Nothing , ghcOpts = [] , cabalOpts = [] , module_ = "" , debug = False , noStack = False } dummyInfo :: HDevTools dummyInfo = Info { socket = Nothing , ghcOpts = [] , cabalOpts = [] , path = Nothing , file = "" , identifier = "" , debug = False , noStack = False , noTH = False } dummyType :: HDevTools dummyType = Type { socket = Nothing , ghcOpts = [] , cabalOpts = [] , path = Nothing , file = "" , line = 0 , col = 0 , debug = False , noStack = False , noTH = False } dummyFindSymbol :: HDevTools dummyFindSymbol = FindSymbol { socket = Nothing , ghcOpts = [] , cabalOpts = [] , symbol = "" , files = [] , debug = False , noStack = False , noTH = False } admin :: Annotate Ann admin = record dummyAdmin [ socket := def += typFile += help "socket file to use" , ghcOpts := def += typ "OPTION" += help "ghc options" , cabalOpts := def += typ "OPTION" += help "cabal options" , start_server := def += help "start server" , noDaemon := def += help "do not daemonize (only if --start-server)" , status := def += help "show status of server" , stop_server := def += help "shutdown the server" , debug := def += help "enable debug output" , noStack := def += name "S" += help "disable stack integration" ] += help "Interactions with the server" check :: Annotate Ann check = record dummyCheck [ socket := def += typFile += help "socket file to use" , ghcOpts := def += typ "OPTION" += help "ghc options" , cabalOpts := def += typ "OPTION" += help "cabal options" , path := def += typFile += help "path to target file" , file := def += typFile += argPos 0 += opt "" , json := def += help "render output as JSON" , debug := def += help "enable debug output" , noStack := def += name "S" += help "disable stack integration" , noTH := def += help "disable template haskell" ] += help "Check a haskell source file for errors and warnings" moduleFile :: Annotate Ann moduleFile = record dummyModuleFile [ socket := def += typFile += help "socket file to use" , ghcOpts := def += typ "OPTION" += help "ghc options" , cabalOpts := def += typ "OPTION" += help "cabal options" , module_ := def += typ "MODULE" += argPos 0 , debug := def += help "enable debug output" , noStack := def += name "S" += help "disable stack integration" ] += help "Get the haskell source file corresponding to a module name" info :: Annotate Ann info = record dummyInfo [ socket := def += typFile += help "socket file to use" , ghcOpts := def += typ "OPTION" += help "ghc options" , cabalOpts := def += typ "OPTION" += help "cabal options" , path := def += typFile += help "path to target file" , file := def += typFile += argPos 0 += opt "" , identifier := def += typ "IDENTIFIER" += argPos 1 , debug := def += help "enable debug output" , noStack := def += name "S" += help "disable stack integration" , noTH := def += help "disable template haskell" ] += help "Get info from GHC about the specified identifier" type_ :: Annotate Ann type_ = record dummyType [ socket := def += typFile += help "socket file to use" , ghcOpts := def += typ "OPTION" += help "ghc options" , cabalOpts := def += typ "OPTION" += help "cabal options" , debug := def += help "enable debug output" , noStack := def += name "S" += help "disable stack integration" , path := def += typFile += help "path to target file" , file := def += typFile += argPos 0 += opt "" , line := def += typ "LINE" += argPos 1 , col := def += typ "COLUMN" += argPos 2 , noTH := def += help "disable template haskell" ] += help "Get the type of the expression at the specified line and column" findSymbol :: Annotate Ann findSymbol = record dummyFindSymbol [ socket := def += typFile += help "socket file to use" , ghcOpts := def += typ "OPTION" += help "ghc options" , cabalOpts := def += typ "OPTION" += help "cabal options" , symbol := def += typ "SYMBOL" += argPos 0 , files := def += typFile += args , debug := def += help "enable debug output" , noStack := def += name "S" += help "disable stack integration" , noTH := def += help "disable template haskell" ] += help "List the modules where the given symbol could be found" full :: String -> Annotate Ann full progName = modes_ [admin += auto, check, moduleFile, info, type_, findSymbol] += helpArg [name "h", groupname "Help"] += versionArg [name "v", groupname "Help"] += program progName += summary (progName ++ ": " ++ fullVersion) fileArg :: HDevTools -> Maybe String fileArg (Admin {}) = Nothing fileArg (ModuleFile {}) = Nothing fileArg a@(Check {}) = Just $ file a fileArg a@(Info {}) = Just $ file a fileArg a@(Type {}) = Just $ file a fileArg (FindSymbol {}) = Nothing pathArg' :: HDevTools -> Maybe String pathArg' (Admin {}) = Nothing pathArg' (ModuleFile {}) = Nothing pathArg' a@(Check {}) = path a pathArg' a@(Info {}) = path a pathArg' a@(Type {}) = path a pathArg' (FindSymbol {}) = Nothing pathArg :: HDevTools -> Maybe String pathArg a = case pathArg' a of Just x -> Just x Nothing -> fileArg a loadHDevTools :: IO HDevTools loadHDevTools = do progName <- getProgName cfg0 <- cmdArgs_ (full progName) dir <- maybe getCurrentDirectory (return . takeDirectory) $ pathArg cfg0 mConfig <- findFile (== ".hdevtoolsrc") dir perProject <- maybe (return []) (\f -> splitArgs `fmap` readFile f) mConfig args0 <- getArgs withArgs (args0 ++ perProject) $ cmdArgs_ (full progName)