GHCMod.hs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. {-# LANGUAGE DeriveDataTypeable #-}
  2. module Main where
  3. import Browse
  4. import CabalDev (modifyOptions)
  5. import Check
  6. import Control.Applicative
  7. import Control.Exception
  8. import Data.Typeable
  9. import Data.Version
  10. import Info
  11. import Lang
  12. import Flag
  13. import Lint
  14. import List
  15. import Paths_ghc_mod
  16. import Prelude
  17. import System.Console.GetOpt
  18. import System.Directory
  19. import System.Environment (getArgs)
  20. import System.IO (hPutStr, hPutStrLn, stderr)
  21. import Types
  22. ----------------------------------------------------------------
  23. ghcOptHelp :: String
  24. ghcOptHelp = " [-g GHC_opt1 -g GHC_opt2 ...] "
  25. usage :: String
  26. usage = "ghc-mod version " ++ showVersion version ++ "\n"
  27. ++ "Usage:\n"
  28. ++ "\t ghc-mod list" ++ ghcOptHelp ++ "[-l]\n"
  29. ++ "\t ghc-mod lang [-l]\n"
  30. ++ "\t ghc-mod flag [-l]\n"
  31. ++ "\t ghc-mod browse" ++ ghcOptHelp ++ "[-l] [-o] <module> [<module> ...]\n"
  32. ++ "\t ghc-mod check" ++ ghcOptHelp ++ "<HaskellFile>\n"
  33. ++ "\t ghc-mod expand" ++ ghcOptHelp ++ "<HaskellFile>\n"
  34. ++ "\t ghc-mod info" ++ ghcOptHelp ++ "<HaskellFile> <module> <expression>\n"
  35. ++ "\t ghc-mod type" ++ ghcOptHelp ++ "<HaskellFile> <module> <line-no> <column-no>\n"
  36. ++ "\t ghc-mod lint [-h opt] <HaskellFile>\n"
  37. ++ "\t ghc-mod boot\n"
  38. ++ "\t ghc-mod help\n"
  39. ----------------------------------------------------------------
  40. argspec :: [OptDescr (Options -> Options)]
  41. argspec = [ Option "l" ["tolisp"]
  42. (NoArg (\opts -> opts { outputStyle = LispStyle }))
  43. "print as a list of Lisp"
  44. , Option "h" ["hlintOpt"]
  45. (ReqArg (\h opts -> opts { hlintOpts = h : hlintOpts opts }) "hlintOpt")
  46. "hlint options"
  47. , Option "g" ["ghcOpt"]
  48. (ReqArg (\g opts -> opts { ghcOpts = g : ghcOpts opts }) "ghcOpt")
  49. "GHC options"
  50. , Option "o" ["operators"]
  51. (NoArg (\opts -> opts { operators = True }))
  52. "print operators, too"
  53. , Option "s" ["sandbox"]
  54. (ReqArg (\s opts -> opts { sandbox = Just s }) "path")
  55. "specify cabal-dev sandbox (default 'cabal-dev`)"
  56. ]
  57. parseArgs :: [OptDescr (Options -> Options)] -> [String] -> (Options, [String])
  58. parseArgs spec argv
  59. = case getOpt Permute spec argv of
  60. (o,n,[] ) -> (foldr id defaultOptions o, n)
  61. (_,_,errs) -> throw (CmdArg errs)
  62. ----------------------------------------------------------------
  63. data GHCModError = SafeList
  64. | NoSuchCommand String
  65. | CmdArg [String]
  66. | FileNotExist String deriving (Show, Typeable)
  67. instance Exception GHCModError
  68. ----------------------------------------------------------------
  69. main :: IO ()
  70. main = flip catches handlers $ do
  71. args <- getArgs
  72. let (opt',cmdArg) = parseArgs argspec args
  73. res <- modifyOptions opt' >>= \opt -> case safelist cmdArg 0 of
  74. "browse" -> concat <$> mapM (browseModule opt) (tail cmdArg)
  75. "list" -> listModules opt
  76. "check" -> withFile (checkSyntax opt) (safelist cmdArg 1)
  77. "expand" -> withFile (checkSyntax opt { expandSplice = True })
  78. (safelist cmdArg 1)
  79. "type" -> withFile (typeExpr opt (safelist cmdArg 2) (read $ safelist cmdArg 3) (read $ safelist cmdArg 4)) (safelist cmdArg 1)
  80. "info" -> withFile (infoExpr opt (safelist cmdArg 2) (safelist cmdArg 3)) (safelist cmdArg 1)
  81. "lint" -> withFile (lintSyntax opt) (safelist cmdArg 1)
  82. "lang" -> listLanguages opt
  83. "flag" -> listFlags opt
  84. "boot" -> do
  85. mods <- listModules opt
  86. langs <- listLanguages opt
  87. flags <- listFlags opt
  88. pre <- concat <$> mapM (browseModule opt) preBrowsedModules
  89. return $ mods ++ langs ++ flags ++ pre
  90. cmd -> throw (NoSuchCommand cmd)
  91. putStr res
  92. where
  93. handlers = [Handler handler1, Handler handler2]
  94. handler1 :: ErrorCall -> IO ()
  95. handler1 = print -- for debug
  96. handler2 :: GHCModError -> IO ()
  97. handler2 SafeList = printUsage
  98. handler2 (NoSuchCommand cmd) = do
  99. hPutStrLn stderr $ "\"" ++ cmd ++ "\" not supported"
  100. printUsage
  101. handler2 (CmdArg errs) = do
  102. mapM_ (hPutStr stderr) errs
  103. printUsage
  104. handler2 (FileNotExist file) = do
  105. hPutStrLn stderr $ "\"" ++ file ++ "\" not found"
  106. printUsage
  107. printUsage = hPutStrLn stderr $ '\n' : usageInfo usage argspec
  108. withFile cmd file = do
  109. exist <- doesFileExist file
  110. if exist
  111. then cmd file
  112. else throw (FileNotExist file)
  113. safelist xs idx
  114. | length xs <= idx = throw SafeList
  115. | otherwise = xs !! idx
  116. ----------------------------------------------------------------
  117. preBrowsedModules :: [String]
  118. preBrowsedModules = [
  119. "Prelude"
  120. , "Control.Applicative"
  121. , "Control.Monad"
  122. , "Control.Exception"
  123. , "Data.Char"
  124. , "Data.List"
  125. , "Data.Maybe"
  126. , "System.IO"
  127. ]