2014-10-22 22:56:18 +00:00
|
|
|
{-# LANGUAGE ScopedTypeVariables, DeriveDataTypeable #-}
|
2010-06-14 06:38:56 +00:00
|
|
|
|
2010-01-06 05:38:06 +00:00
|
|
|
module Main where
|
|
|
|
|
2014-04-25 13:03:09 +00:00
|
|
|
import Config (cProjectVersion)
|
2014-10-22 22:56:18 +00:00
|
|
|
import MonadUtils (liftIO)
|
2014-09-18 08:05:47 +00:00
|
|
|
import Control.Applicative
|
2015-05-31 08:32:46 +00:00
|
|
|
import Control.Arrow
|
2014-10-22 22:56:18 +00:00
|
|
|
import Control.Monad
|
2014-03-27 05:46:33 +00:00
|
|
|
import Data.Typeable (Typeable)
|
|
|
|
import Data.Version (showVersion)
|
2014-09-18 08:05:47 +00:00
|
|
|
import Data.List
|
2014-10-22 22:56:18 +00:00
|
|
|
import Data.List.Split
|
2014-09-18 08:05:47 +00:00
|
|
|
import Data.Char (isSpace)
|
2015-08-10 08:10:33 +00:00
|
|
|
import Data.Maybe
|
2015-01-12 16:26:46 +00:00
|
|
|
import Exception
|
2013-05-17 01:00:01 +00:00
|
|
|
import Language.Haskell.GhcMod
|
2015-06-07 18:36:49 +00:00
|
|
|
import Language.Haskell.GhcMod.Internal hiding (MonadIO,liftIO)
|
2011-12-26 08:08:00 +00:00
|
|
|
import Paths_ghc_mod
|
2014-03-27 05:46:33 +00:00
|
|
|
import System.Console.GetOpt (OptDescr(..), ArgDescr(..), ArgOrder(..))
|
|
|
|
import qualified System.Console.GetOpt as O
|
2015-08-10 08:10:33 +00:00
|
|
|
import System.FilePath ((</>))
|
|
|
|
import System.Directory (setCurrentDirectory, getAppUserDataDirectory,
|
|
|
|
removeDirectoryRecursive)
|
2015-05-06 14:32:53 +00:00
|
|
|
import System.Environment (getArgs)
|
2013-08-23 02:30:07 +00:00
|
|
|
import System.Exit (exitFailure)
|
2015-08-14 03:57:33 +00:00
|
|
|
import System.IO (stdout, hSetEncoding, utf8, hFlush)
|
2015-05-06 14:32:53 +00:00
|
|
|
import System.Exit (exitSuccess)
|
2014-09-18 08:05:47 +00:00
|
|
|
import Text.PrettyPrint
|
2015-08-03 01:09:56 +00:00
|
|
|
import Prelude
|
2010-01-06 05:38:06 +00:00
|
|
|
|
2014-10-22 22:56:18 +00:00
|
|
|
import Misc
|
|
|
|
|
2015-08-10 09:09:11 +00:00
|
|
|
progVersion :: String -> String
|
|
|
|
progVersion pf =
|
|
|
|
"ghc-mod"++pf++" version " ++ showVersion version ++ " compiled by GHC "
|
|
|
|
++ cProjectVersion ++ "\n"
|
|
|
|
|
|
|
|
ghcModVersion :: String
|
|
|
|
ghcModVersion = progVersion ""
|
|
|
|
|
|
|
|
ghcModiVersion :: String
|
|
|
|
ghcModiVersion = progVersion "i"
|
2014-04-25 13:03:09 +00:00
|
|
|
|
2014-09-18 08:05:47 +00:00
|
|
|
optionUsage :: (String -> String) -> [OptDescr a] -> [String]
|
|
|
|
optionUsage indent opts = concatMap optUsage opts
|
|
|
|
where
|
|
|
|
optUsage (Option so lo dsc udsc) =
|
|
|
|
[ concat $ intersperse ", " $ addLabel `map` allFlags
|
|
|
|
, indent $ udsc
|
|
|
|
, ""
|
|
|
|
]
|
|
|
|
where
|
|
|
|
allFlags = shortFlags ++ longFlags
|
|
|
|
shortFlags = (('-':) . return) `map` so :: [String]
|
|
|
|
longFlags = ("--"++) `map` lo
|
2011-11-14 09:12:18 +00:00
|
|
|
|
2014-09-18 08:05:47 +00:00
|
|
|
addLabel f@('-':'-':_) = f ++ flagLabel "="
|
|
|
|
addLabel f@('-':_) = f ++ flagLabel " "
|
|
|
|
addLabel _ = undefined
|
|
|
|
|
|
|
|
flagLabel s =
|
|
|
|
case dsc of
|
|
|
|
NoArg _ -> ""
|
|
|
|
ReqArg _ label -> s ++ label
|
|
|
|
OptArg _ label -> s ++ "["++label++"]"
|
|
|
|
|
2015-04-29 16:44:46 +00:00
|
|
|
-- TODO: Generate the stuff below automatically
|
2010-01-06 05:38:06 +00:00
|
|
|
usage :: String
|
2014-09-18 08:05:47 +00:00
|
|
|
usage =
|
2015-03-05 15:51:38 +00:00
|
|
|
"Usage: ghc-mod [OPTIONS...] COMMAND [CMD_ARGS...] \n\
|
2014-09-18 08:05:47 +00:00
|
|
|
\*Global Options (OPTIONS)*\n\
|
|
|
|
\ Global options can be specified before and after the command and\n\
|
|
|
|
\ interspersed with command specific options\n\
|
|
|
|
\\n"
|
|
|
|
++ (unlines $ indent <$> optionUsage indent globalArgSpec) ++
|
|
|
|
"*Commands*\n\
|
2015-08-13 04:47:12 +00:00
|
|
|
\ - version\n\
|
2014-09-18 08:05:47 +00:00
|
|
|
\ Print the version of the program.\n\
|
|
|
|
\\n\
|
2015-08-13 04:47:12 +00:00
|
|
|
\ - help\n\
|
2014-09-18 08:05:47 +00:00
|
|
|
\ Print this help message.\n\
|
|
|
|
\\n\
|
2014-12-24 21:35:21 +00:00
|
|
|
\ - list [FLAGS...] | modules [FLAGS...]\n\
|
2014-09-18 08:05:47 +00:00
|
|
|
\ List all visible modules.\n\
|
2015-03-05 17:54:39 +00:00
|
|
|
\ Flags:\n\
|
|
|
|
\ -d\n\
|
|
|
|
\ Print package modules belong to.\n\
|
2014-09-18 08:05:47 +00:00
|
|
|
\\n\
|
|
|
|
\ - lang\n\
|
|
|
|
\ List all known GHC language extensions.\n\
|
|
|
|
\\n\
|
|
|
|
\ - flag\n\
|
|
|
|
\ List GHC -f<bla> flags.\n\
|
|
|
|
\\n\
|
|
|
|
\ - browse [FLAGS...] [PACKAGE:]MODULE...\n\
|
|
|
|
\ List symbols in a module.\n\
|
|
|
|
\ Flags:\n\
|
|
|
|
\ -o\n\
|
|
|
|
\ Also print operators.\n\
|
|
|
|
\ -d\n\
|
|
|
|
\ Print symbols with accompanying signatures.\n\
|
|
|
|
\ -q\n\
|
|
|
|
\ Qualify symbols.\n\
|
|
|
|
\\n\
|
|
|
|
\ - check FILE...\n\
|
|
|
|
\ Load the given files using GHC and report errors/warnings, but\n\
|
|
|
|
\ don't produce output files.\n\
|
|
|
|
\\n\
|
|
|
|
\ - expand FILE...\n\
|
|
|
|
\ Like `check' but also pass `-ddump-splices' to GHC.\n\
|
|
|
|
\\n\
|
|
|
|
\ - info FILE [MODULE] EXPR\n\
|
|
|
|
\ Look up an identifier in the context of FILE (like ghci's `:info')\n\
|
|
|
|
\ MODULE is completely ignored and only allowed for backwards\n\
|
|
|
|
\ compatibility.\n\
|
|
|
|
\\n\
|
|
|
|
\ - type FILE [MODULE] LINE COL\n\
|
|
|
|
\ Get the type of the expression under (LINE,COL).\n\
|
|
|
|
\\n\
|
|
|
|
\ - split FILE [MODULE] LINE COL\n\
|
|
|
|
\ Split a function case by examining a type's constructors.\n\
|
|
|
|
\\n\
|
|
|
|
\ For example given the following code snippet:\n\
|
|
|
|
\\n\
|
|
|
|
\ f :: [a] -> a\n\
|
|
|
|
\ f x = _body\n\
|
|
|
|
\\n\
|
|
|
|
\ would be replaced by:\n\
|
|
|
|
\\n\
|
|
|
|
\ f :: [a] -> a\n\
|
|
|
|
\ f [] = _body\n\
|
|
|
|
\ f (x:xs) = _body\n\
|
|
|
|
\\n\
|
|
|
|
\ (See https://github.com/kazu-yamamoto/ghc-mod/pull/274)\n\
|
|
|
|
\\n\
|
|
|
|
\ - sig FILE MODULE LINE COL\n\
|
|
|
|
\ Generate initial code given a signature.\n\
|
|
|
|
\\n\
|
|
|
|
\ For example when (LINE,COL) is on the signature in the following\n\
|
|
|
|
\ code snippet:\n\
|
|
|
|
\\n\
|
|
|
|
\ func :: [a] -> Maybe b -> (a -> b) -> (a,b)\n\
|
|
|
|
\\n\
|
|
|
|
\ ghc-mod would add the following on the next line:\n\
|
|
|
|
\\n\
|
|
|
|
\ func x y z f = _func_body\n\
|
|
|
|
\\n\
|
|
|
|
\ (See: https://github.com/kazu-yamamoto/ghc-mod/pull/274)\n\
|
|
|
|
\\n\
|
|
|
|
\ - refine FILE MODULE LINE COL EXPR\n\
|
|
|
|
\ Refine the typed hole at (LINE,COL) given EXPR.\n\
|
|
|
|
\\n\
|
|
|
|
\ For example if EXPR is `filter', which has type `(a -> Bool) -> [a]\n\
|
|
|
|
\ -> [a]' and (LINE,COL) is on the hole `_body' in the following\n\
|
|
|
|
\ code snippet:\n\
|
|
|
|
\\n\
|
|
|
|
\ filterNothing :: [Maybe a] -> [a]\n\
|
|
|
|
\ filterNothing xs = _body\n\
|
|
|
|
\\n\
|
|
|
|
\ ghc-mod changes the code to get a value of type `[a]', which\n\
|
|
|
|
\ results in:\n\
|
|
|
|
\\n\
|
|
|
|
\ filterNothing xs = filter _body_1 _body_2\n\
|
|
|
|
\\n\
|
|
|
|
\ (See also: https://github.com/kazu-yamamoto/ghc-mod/issues/311)\n\
|
|
|
|
\\n\
|
|
|
|
\ - auto FILE MODULE LINE COL\n\
|
|
|
|
\ Try to automatically fill the contents of a hole.\n\
|
|
|
|
\\n\
|
|
|
|
\ - find SYMBOL\n\
|
|
|
|
\ List all modules that define SYMBOL.\n\
|
|
|
|
\\n\
|
|
|
|
\ - lint FILE\n\
|
|
|
|
\ Check files using `hlint'.\n\
|
|
|
|
\ Flags:\n\
|
|
|
|
\ -l\n\
|
|
|
|
\ Option to be passed to hlint.\n\
|
|
|
|
\\n\
|
2015-01-12 19:04:41 +00:00
|
|
|
\ - root\n\
|
|
|
|
\ Try to find the project directory. For Cabal projects this is the\n\
|
|
|
|
\ directory containing the cabal file, for projects that use a cabal\n\
|
|
|
|
\ sandbox but have no cabal file this is the directory containing the\n\
|
|
|
|
\ cabal.sandbox.config file and otherwise this is the current\n\
|
|
|
|
\ directory.\n\
|
2014-09-18 08:05:47 +00:00
|
|
|
\\n\
|
|
|
|
\ - doc MODULE\n\
|
|
|
|
\ Try finding the html documentation directory for the given MODULE.\n\
|
|
|
|
\\n\
|
|
|
|
\ - debug\n\
|
|
|
|
\ Print debugging information. Please include the output in any bug\n\
|
|
|
|
\ reports you submit.\n\
|
|
|
|
\\n\
|
2015-03-28 01:33:42 +00:00
|
|
|
\ - debugComponent [MODULE_OR_FILE...]\n\
|
|
|
|
\ Debugging information related to cabal component resolution.\n\
|
|
|
|
\\n\
|
2014-09-18 08:05:47 +00:00
|
|
|
\ - boot\n\
|
2015-04-29 16:44:46 +00:00
|
|
|
\ Internal command used by the emacs frontend.\n\
|
2014-10-22 22:56:18 +00:00
|
|
|
\\n\
|
2015-05-06 14:15:04 +00:00
|
|
|
\ - legacy-interactive\n\
|
|
|
|
\ ghc-modi compatibility mode.\n"
|
2014-10-22 22:56:18 +00:00
|
|
|
where
|
|
|
|
indent = (" "++)
|
|
|
|
|
2014-09-18 08:05:47 +00:00
|
|
|
cmdUsage :: String -> String -> String
|
2015-01-12 16:26:46 +00:00
|
|
|
cmdUsage cmd realUsage =
|
2014-09-18 08:05:47 +00:00
|
|
|
let
|
|
|
|
-- Find command head
|
2015-01-12 16:26:46 +00:00
|
|
|
a = dropWhile (not . isCmdHead) $ lines realUsage
|
2014-09-18 08:05:47 +00:00
|
|
|
-- Take til the end of the current command block
|
|
|
|
b = flip takeWhile a $ \l ->
|
2015-01-12 16:26:46 +00:00
|
|
|
all isSpace l || (isIndented l && (isCmdHead l || isNotCmdHead l))
|
2014-09-18 08:05:47 +00:00
|
|
|
-- Drop extra newline from the end
|
|
|
|
c = dropWhileEnd (all isSpace) b
|
2010-01-06 05:38:06 +00:00
|
|
|
|
2014-09-18 08:05:47 +00:00
|
|
|
isIndented = (" " `isPrefixOf`)
|
|
|
|
isNotCmdHead = ( not . (" - " `isPrefixOf`))
|
2015-01-12 16:26:46 +00:00
|
|
|
|
|
|
|
containsAnyCmdHead s = ((" - ") `isInfixOf` s)
|
|
|
|
containsCurrCmdHead s = ((" - " ++ cmd) `isInfixOf` s)
|
|
|
|
isCmdHead s =
|
|
|
|
containsAnyCmdHead s &&
|
|
|
|
or [ containsCurrCmdHead s
|
|
|
|
, any (cmd `isPrefixOf`) (splitOn " | " s)
|
|
|
|
]
|
2014-09-12 22:09:57 +00:00
|
|
|
|
2014-09-18 08:05:47 +00:00
|
|
|
unindent (' ':' ':' ':' ':l) = l
|
|
|
|
unindent l = l
|
|
|
|
in unlines $ unindent <$> c
|
2015-01-12 16:26:46 +00:00
|
|
|
|
2015-04-29 16:44:46 +00:00
|
|
|
ghcModStyle :: Style
|
|
|
|
ghcModStyle = style { lineLength = 80, ribbonsPerLine = 1.2 }
|
|
|
|
|
2014-09-18 08:05:47 +00:00
|
|
|
----------------------------------------------------------------
|
2014-09-12 22:09:57 +00:00
|
|
|
|
2014-09-18 08:05:47 +00:00
|
|
|
option :: [Char] -> [String] -> String -> ArgDescr a -> OptDescr a
|
|
|
|
option s l udsc dsc = Option s l dsc udsc
|
2014-09-12 22:09:57 +00:00
|
|
|
|
2014-09-18 08:05:47 +00:00
|
|
|
reqArg :: String -> (String -> a) -> ArgDescr a
|
|
|
|
reqArg udsc dsc = ReqArg dsc udsc
|
|
|
|
|
2015-05-06 14:32:53 +00:00
|
|
|
optArg :: String -> (Maybe String -> a) -> ArgDescr a
|
|
|
|
optArg udsc dsc = OptArg dsc udsc
|
2014-09-12 22:09:57 +00:00
|
|
|
|
2015-05-06 14:32:53 +00:00
|
|
|
intToLogLevel :: Int -> GmLogLevel
|
|
|
|
intToLogLevel = toEnum
|
2015-03-06 18:46:56 +00:00
|
|
|
|
2015-08-10 09:09:11 +00:00
|
|
|
globalArgSpec :: [OptDescr (Options -> Either [String] Options)]
|
2015-05-06 14:32:53 +00:00
|
|
|
globalArgSpec =
|
2015-08-03 06:09:24 +00:00
|
|
|
[ option "v" ["verbose"] "Increase or set log level. (0-7)" $
|
2015-08-10 09:09:11 +00:00
|
|
|
optArg "LEVEL" $ \ml o -> Right $ o {
|
2015-05-06 14:32:53 +00:00
|
|
|
logLevel = case ml of
|
|
|
|
Nothing -> increaseLogLevel (logLevel o)
|
2015-08-05 02:06:41 +00:00
|
|
|
Just l -> toEnum $ min 7 $ read l
|
2015-05-06 14:32:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
, option "s" [] "Be silent, set log level to 0" $
|
2015-08-10 09:09:11 +00:00
|
|
|
NoArg $ \o -> Right $ o { logLevel = toEnum 0 }
|
2015-03-06 18:46:56 +00:00
|
|
|
|
2014-09-18 08:05:47 +00:00
|
|
|
, option "l" ["tolisp"] "Format output as an S-Expression" $
|
2015-08-10 09:09:11 +00:00
|
|
|
NoArg $ \o -> Right $ o { outputStyle = LispStyle }
|
2014-09-12 22:09:57 +00:00
|
|
|
|
2015-08-13 04:47:12 +00:00
|
|
|
, option "b" ["boundary", "line-seperator"] "Output line separator"$
|
2015-08-10 09:09:11 +00:00
|
|
|
reqArg "SEP" $ \s o -> Right $ o { lineSeparator = LineSeparator s }
|
2015-08-13 04:47:12 +00:00
|
|
|
, option "" ["line-prefix"] "Output line separator"$
|
|
|
|
reqArg "OUT,ERR" $ \s o -> let
|
|
|
|
[out, err] = splitOn "," s
|
|
|
|
in Right $ o { linePrefix = Just (out, err) }
|
2014-09-12 22:09:57 +00:00
|
|
|
|
2015-04-29 16:44:46 +00:00
|
|
|
, option "g" ["ghcOpt", "ghc-option"] "Option to be passed to GHC" $
|
2015-08-10 09:09:11 +00:00
|
|
|
reqArg "OPT" $ \g o -> Right $
|
2014-09-18 08:05:47 +00:00
|
|
|
o { ghcUserOptions = g : ghcUserOptions o }
|
2014-09-12 22:09:57 +00:00
|
|
|
|
2015-07-02 10:27:31 +00:00
|
|
|
, option "" ["map-file"] "Redirect one file to another, --map-file \"file1.hs=file2.hs\"" $
|
2015-05-31 08:32:46 +00:00
|
|
|
reqArg "OPT" $ \g o ->
|
|
|
|
let m = case second (drop 1) $ span (/='=') g of
|
|
|
|
(s,"") -> (s, MemoryMapping Nothing)
|
|
|
|
(f,t) -> (f, RedirectedMapping t)
|
|
|
|
in
|
|
|
|
Right $ o { fileMappings = m : fileMappings o }
|
|
|
|
|
2015-03-07 18:23:55 +00:00
|
|
|
, option "" ["with-ghc"] "GHC executable to use" $
|
2015-08-10 09:09:11 +00:00
|
|
|
reqArg "PROG" $ \p o -> Right $ o { ghcProgram = p }
|
2015-03-07 18:23:55 +00:00
|
|
|
|
|
|
|
, option "" ["with-ghc-pkg"] "ghc-pkg executable to use (only needed when guessing from GHC path fails)" $
|
2015-08-10 09:09:11 +00:00
|
|
|
reqArg "PROG" $ \p o -> Right $ o { ghcPkgProgram = p }
|
2014-09-18 08:05:47 +00:00
|
|
|
|
|
|
|
, option "" ["with-cabal"] "cabal-install executable to use" $
|
2015-08-10 09:09:11 +00:00
|
|
|
reqArg "PROG" $ \p o -> Right $ o { cabalProgram = p }
|
|
|
|
|
|
|
|
, option "" ["version"] "print version information" $
|
|
|
|
NoArg $ \_ -> Left ["version"]
|
|
|
|
|
|
|
|
, option "" ["help"] "print this help message" $
|
|
|
|
NoArg $ \_ -> Left ["help"]
|
|
|
|
|
2014-09-12 22:09:57 +00:00
|
|
|
]
|
2010-01-06 05:38:06 +00:00
|
|
|
|
2014-10-22 22:56:18 +00:00
|
|
|
|
|
|
|
parseGlobalArgs :: [String] -> Either InvalidCommandLine (Options, [String])
|
2014-09-18 08:05:47 +00:00
|
|
|
parseGlobalArgs argv
|
2015-05-13 09:02:24 +00:00
|
|
|
= case O.getOpt' RequireOrder globalArgSpec argv of
|
2015-08-10 09:09:11 +00:00
|
|
|
(o,r,u,[]) -> case foldr (=<<) (Right defaultOptions) o of
|
|
|
|
Right o' -> Right (o', u ++ r)
|
|
|
|
Left c -> Right (defaultOptions, c)
|
2015-04-29 16:44:46 +00:00
|
|
|
(_,_,u,e) -> Left $ InvalidCommandLine $ Right $
|
|
|
|
"Parsing command line options failed: "
|
|
|
|
++ concat (e ++ map errUnrec u)
|
|
|
|
where
|
|
|
|
errUnrec :: String -> String
|
|
|
|
errUnrec optStr = "unrecognized option `" ++ optStr ++ "'\n"
|
2014-09-18 08:05:47 +00:00
|
|
|
|
2015-08-10 09:09:11 +00:00
|
|
|
parseCommandArgs :: [OptDescr (Options -> Either [String] Options)]
|
2014-09-18 08:05:47 +00:00
|
|
|
-> [String]
|
|
|
|
-> Options
|
|
|
|
-> (Options, [String])
|
|
|
|
parseCommandArgs spec argv opts
|
|
|
|
= case O.getOpt RequireOrder (globalArgSpec ++ spec) argv of
|
2015-08-10 09:09:11 +00:00
|
|
|
(o,r,[]) -> case foldr (=<<) (Right opts) o of
|
|
|
|
Right o' -> (o', r)
|
|
|
|
Left c -> (defaultOptions, c)
|
2014-09-18 08:05:47 +00:00
|
|
|
(_,_,errs) ->
|
2014-10-22 22:56:18 +00:00
|
|
|
fatalError $ "Parsing command options failed: " ++ concat errs
|
2010-06-14 06:38:56 +00:00
|
|
|
|
|
|
|
----------------------------------------------------------------
|
|
|
|
|
2014-09-18 08:05:47 +00:00
|
|
|
data CmdError = UnknownCommand String
|
|
|
|
| NoSuchFileError String
|
|
|
|
| LibraryError GhcModError
|
2010-06-14 06:38:56 +00:00
|
|
|
|
2014-09-18 08:05:47 +00:00
|
|
|
deriving (Show, Typeable)
|
|
|
|
|
|
|
|
instance Exception CmdError
|
2010-01-06 05:38:06 +00:00
|
|
|
|
2014-09-18 08:05:47 +00:00
|
|
|
data InteractiveOptions = InteractiveOptions {
|
|
|
|
ghcModExtensions :: Bool
|
|
|
|
}
|
|
|
|
|
2015-08-14 03:57:33 +00:00
|
|
|
handler :: IOish m => GhcModT m a -> GhcModT m a
|
|
|
|
handler = flip gcatches $
|
|
|
|
[ GHandler $ \(FatalError msg) -> exitError msg
|
|
|
|
, GHandler $ \(InvalidCommandLine e) -> do
|
2014-09-18 08:05:47 +00:00
|
|
|
case e of
|
|
|
|
Left cmd ->
|
2015-01-12 16:26:46 +00:00
|
|
|
exitError $ "Usage for `"++cmd++"' command:\n\n"
|
2015-04-29 16:44:46 +00:00
|
|
|
++ (cmdUsage cmd usage) ++ "\n"
|
|
|
|
++ "ghc-mod: Invalid command line form."
|
|
|
|
Right msg -> exitError $ "ghc-mod: " ++ msg
|
2015-08-14 03:57:33 +00:00
|
|
|
, GHandler $ \(SomeException e) -> exitError $ "ghc-mod: " ++ show e
|
2014-09-18 08:05:47 +00:00
|
|
|
]
|
|
|
|
|
2010-01-06 05:38:06 +00:00
|
|
|
main :: IO ()
|
2015-08-14 03:57:33 +00:00
|
|
|
main = do
|
2013-03-29 12:58:55 +00:00
|
|
|
hSetEncoding stdout utf8
|
2010-01-06 05:38:06 +00:00
|
|
|
args <- getArgs
|
2014-10-22 22:56:18 +00:00
|
|
|
case parseGlobalArgs args of
|
2015-08-13 04:47:12 +00:00
|
|
|
Left e -> throw e
|
|
|
|
Right res -> progMain res
|
2014-10-22 22:56:18 +00:00
|
|
|
|
|
|
|
progMain :: (Options,[String]) -> IO ()
|
2015-08-14 03:57:33 +00:00
|
|
|
progMain (globalOptions,cmdArgs) = hndle $ runGhcModT globalOptions $ handler $ do
|
2015-08-13 04:47:12 +00:00
|
|
|
case globalCommands cmdArgs of
|
|
|
|
Just s -> gmPutStr s
|
|
|
|
Nothing -> ghcCommands cmdArgs
|
|
|
|
where
|
|
|
|
hndle action = do
|
|
|
|
(e, _l) <- action
|
|
|
|
case e of
|
|
|
|
Right _ ->
|
|
|
|
return ()
|
|
|
|
Left ed ->
|
2015-08-14 03:57:33 +00:00
|
|
|
exitError' globalOptions $ renderStyle ghcModStyle (gmeDoc ed)
|
2015-08-13 04:47:12 +00:00
|
|
|
|
|
|
|
globalCommands :: [String] -> Maybe String
|
|
|
|
globalCommands (cmd:_)
|
|
|
|
| cmd == "help" = Just usage
|
|
|
|
| cmd == "version" = Just ghcModVersion
|
|
|
|
globalCommands _ = Nothing
|
2014-10-22 22:56:18 +00:00
|
|
|
|
2015-04-29 16:44:46 +00:00
|
|
|
-- ghc-modi
|
|
|
|
legacyInteractive :: IOish m => GhcModT m ()
|
2015-08-05 06:52:52 +00:00
|
|
|
legacyInteractive = do
|
2015-04-29 16:44:46 +00:00
|
|
|
opt <- options
|
2015-08-14 01:48:29 +00:00
|
|
|
prepareCabalHelper
|
2015-08-14 04:48:56 +00:00
|
|
|
tmpdir <- cradleTempDir <$> cradle
|
|
|
|
symdbreq <- liftIO $ newSymDbReq opt tmpdir
|
2015-08-07 04:47:34 +00:00
|
|
|
world <- getCurrentWorld
|
2015-08-05 06:52:52 +00:00
|
|
|
legacyInteractiveLoop symdbreq world
|
2014-10-22 22:56:18 +00:00
|
|
|
|
2015-08-13 04:47:12 +00:00
|
|
|
bug :: IOish m => String -> GhcModT m ()
|
2014-10-22 22:56:18 +00:00
|
|
|
bug msg = do
|
2015-08-13 04:47:12 +00:00
|
|
|
gmPutStrLn $ notGood $ "BUG: " ++ msg
|
|
|
|
liftIO exitFailure
|
2014-10-22 22:56:18 +00:00
|
|
|
|
|
|
|
notGood :: String -> String
|
|
|
|
notGood msg = "NG " ++ escapeNewlines msg
|
|
|
|
|
|
|
|
escapeNewlines :: String -> String
|
|
|
|
escapeNewlines = replace "\n" "\\n" . replace "\\n" "\\\\n"
|
|
|
|
|
|
|
|
replace :: String -> String -> String -> String
|
|
|
|
replace needle replacement = intercalate replacement . splitOn needle
|
|
|
|
|
|
|
|
legacyInteractiveLoop :: IOish m
|
2015-08-05 06:52:52 +00:00
|
|
|
=> SymDbReq -> World -> GhcModT m ()
|
|
|
|
legacyInteractiveLoop symdbreq world = do
|
2014-10-22 22:56:18 +00:00
|
|
|
liftIO . setCurrentDirectory =<< cradleRootDir <$> cradle
|
|
|
|
|
|
|
|
-- blocking
|
2015-08-05 06:52:52 +00:00
|
|
|
cmdArg <- liftIO $ getLine
|
2014-10-22 22:56:18 +00:00
|
|
|
|
|
|
|
-- after blocking, we need to see if the world has changed.
|
|
|
|
|
2015-08-07 04:47:34 +00:00
|
|
|
changed <- didWorldChange world
|
2014-10-22 22:56:18 +00:00
|
|
|
when changed $ do
|
2015-08-05 06:52:52 +00:00
|
|
|
dropSession
|
2014-10-22 22:56:18 +00:00
|
|
|
|
|
|
|
let (cmd':args') = split (keepDelimsR $ condense $ whenElt isSpace) cmdArg
|
|
|
|
arg = concat args'
|
|
|
|
cmd = dropWhileEnd isSpace cmd'
|
|
|
|
args = dropWhileEnd isSpace `map` args'
|
|
|
|
|
2015-08-14 07:47:45 +00:00
|
|
|
res <- flip gcatches interactiveHandlers $ case dropWhileEnd isSpace cmd of
|
2014-10-22 22:56:18 +00:00
|
|
|
"check" -> checkSyntaxCmd [arg]
|
|
|
|
"lint" -> lintCmd [arg]
|
|
|
|
"find" -> do
|
|
|
|
db <- getDb symdbreq >>= checkDb symdbreq
|
|
|
|
lookupSymbol arg db
|
|
|
|
|
|
|
|
"info" -> infoCmd [head args, concat $ tail args']
|
|
|
|
"type" -> typesCmd args
|
|
|
|
"split" -> splitsCmd args
|
|
|
|
|
|
|
|
"sig" -> sigCmd args
|
|
|
|
"auto" -> autoCmd args
|
|
|
|
"refine" -> refineCmd args
|
|
|
|
|
|
|
|
"boot" -> bootCmd []
|
|
|
|
"browse" -> browseCmd args
|
|
|
|
|
2015-05-31 08:32:46 +00:00
|
|
|
"load" -> loadMappedFile arg (MemoryMapping Nothing)
|
|
|
|
>> return ""
|
|
|
|
|
2015-06-09 09:45:27 +00:00
|
|
|
"unload" -> unloadMappedFile arg
|
2015-05-31 08:32:46 +00:00
|
|
|
>> return ""
|
|
|
|
|
2014-10-22 22:56:18 +00:00
|
|
|
"quit" -> liftIO $ exitSuccess
|
|
|
|
"" -> liftIO $ exitSuccess
|
|
|
|
_ -> fatalError $ "unknown command: `" ++ cmd ++ "'"
|
|
|
|
|
2015-08-13 04:47:12 +00:00
|
|
|
gmPutStr res >> gmPutStrLn "OK" >> liftIO (hFlush stdout)
|
2015-08-05 06:52:52 +00:00
|
|
|
legacyInteractiveLoop symdbreq world
|
2015-08-14 07:47:45 +00:00
|
|
|
where
|
|
|
|
interactiveHandlers =
|
|
|
|
[ GHandler $ \e@(FatalError _) -> throw e
|
|
|
|
, GHandler $ \(SomeException e) -> gmErrStrLn (show e) >> return ""
|
|
|
|
]
|
2014-10-22 22:56:18 +00:00
|
|
|
|
2015-04-29 16:44:46 +00:00
|
|
|
ghcCommands :: IOish m => [String] -> GhcModT m ()
|
2014-10-22 22:56:18 +00:00
|
|
|
ghcCommands [] = fatalError "No command given (try --help)"
|
2015-04-29 16:44:46 +00:00
|
|
|
ghcCommands (cmd:args) = do
|
2015-05-31 08:32:46 +00:00
|
|
|
loadMappedFiles
|
2015-08-13 04:47:12 +00:00
|
|
|
gmPutStr =<< action args
|
2014-09-18 08:05:47 +00:00
|
|
|
where
|
2015-04-29 16:44:46 +00:00
|
|
|
action = case cmd of
|
2014-09-18 08:05:47 +00:00
|
|
|
_ | cmd == "list" || cmd == "modules" -> modulesCmd
|
|
|
|
"lang" -> languagesCmd
|
|
|
|
"flag" -> flagsCmd
|
|
|
|
"browse" -> browseCmd
|
|
|
|
"check" -> checkSyntaxCmd
|
|
|
|
"expand" -> expandTemplateCmd
|
|
|
|
"debug" -> debugInfoCmd
|
2015-08-10 08:10:40 +00:00
|
|
|
"debug-component" -> componentInfoCmd
|
2014-09-18 08:05:47 +00:00
|
|
|
"info" -> infoCmd
|
|
|
|
"type" -> typesCmd
|
|
|
|
"split" -> splitsCmd
|
|
|
|
"sig" -> sigCmd
|
|
|
|
"refine" -> refineCmd
|
|
|
|
"auto" -> autoCmd
|
|
|
|
"find" -> findSymbolCmd
|
|
|
|
"lint" -> lintCmd
|
|
|
|
"root" -> rootInfoCmd
|
|
|
|
"doc" -> pkgDocCmd
|
|
|
|
"dumpsym" -> dumpSymbolCmd
|
|
|
|
"boot" -> bootCmd
|
2015-04-29 16:44:46 +00:00
|
|
|
"legacy-interactive" -> legacyInteractiveCmd
|
2015-08-14 07:33:22 +00:00
|
|
|
-- "nuke-caches" -> nukeCachesCmd
|
2014-09-18 08:05:47 +00:00
|
|
|
_ -> fatalError $ "unknown command: `" ++ cmd ++ "'"
|
|
|
|
|
|
|
|
newtype FatalError = FatalError String deriving (Show, Typeable)
|
|
|
|
instance Exception FatalError
|
|
|
|
|
|
|
|
newtype InvalidCommandLine = InvalidCommandLine (Either String String)
|
|
|
|
deriving (Show, Typeable)
|
|
|
|
instance Exception InvalidCommandLine
|
|
|
|
|
2015-08-14 03:57:33 +00:00
|
|
|
exitError :: IOish m => String -> GhcModT m a
|
|
|
|
exitError msg = gmErrStrLn (dropWhileEnd (=='\n') msg) >> liftIO exitFailure
|
|
|
|
|
|
|
|
exitError' :: Options -> String -> IO a
|
|
|
|
exitError' opts msg =
|
|
|
|
gmUnsafeErrStrLn opts (dropWhileEnd (=='\n') msg) >> liftIO exitFailure
|
2014-09-18 08:05:47 +00:00
|
|
|
|
|
|
|
fatalError :: String -> a
|
2015-04-29 16:44:46 +00:00
|
|
|
fatalError s = throw $ FatalError $ "ghc-mod: " ++ s
|
2014-09-18 08:05:47 +00:00
|
|
|
|
|
|
|
withParseCmd :: IOish m
|
2015-08-10 09:09:11 +00:00
|
|
|
=> [OptDescr (Options -> Either [String] Options)]
|
2014-09-18 08:05:47 +00:00
|
|
|
-> ([String] -> GhcModT m a)
|
|
|
|
-> [String]
|
|
|
|
-> GhcModT m a
|
|
|
|
withParseCmd spec action args = do
|
|
|
|
(opts', rest) <- parseCommandArgs spec args <$> options
|
|
|
|
withOptions (const opts') $ action rest
|
|
|
|
|
2015-01-12 16:26:46 +00:00
|
|
|
withParseCmd' :: (IOish m, ExceptionMonad m)
|
|
|
|
=> String
|
2015-08-10 09:09:11 +00:00
|
|
|
-> [OptDescr (Options -> Either [String] Options)]
|
2015-01-12 16:26:46 +00:00
|
|
|
-> ([String] -> GhcModT m a)
|
|
|
|
-> [String]
|
|
|
|
-> GhcModT m a
|
|
|
|
withParseCmd' cmd spec action args =
|
|
|
|
catchArgs cmd $ withParseCmd spec action args
|
|
|
|
|
|
|
|
catchArgs :: (Monad m, ExceptionMonad m) => String -> m a -> m a
|
|
|
|
catchArgs cmd action =
|
|
|
|
action `gcatch` \(PatternMatchFail _) ->
|
|
|
|
throw $ InvalidCommandLine (Left cmd)
|
|
|
|
|
2014-09-18 08:05:47 +00:00
|
|
|
modulesCmd, languagesCmd, flagsCmd, browseCmd, checkSyntaxCmd, expandTemplateCmd,
|
2015-04-29 16:44:46 +00:00
|
|
|
debugInfoCmd, componentInfoCmd, infoCmd, typesCmd, splitsCmd, sigCmd,
|
|
|
|
refineCmd, autoCmd, findSymbolCmd, lintCmd, rootInfoCmd, pkgDocCmd,
|
2015-08-10 08:10:33 +00:00
|
|
|
dumpSymbolCmd, bootCmd, legacyInteractiveCmd, nukeCachesCmd
|
2014-09-18 08:05:47 +00:00
|
|
|
:: IOish m => [String] -> GhcModT m String
|
|
|
|
|
2015-03-05 17:54:39 +00:00
|
|
|
modulesCmd = withParseCmd' "modules" s $ \[] -> modules
|
|
|
|
where s = modulesArgSpec
|
2015-01-12 16:26:46 +00:00
|
|
|
languagesCmd = withParseCmd' "lang" [] $ \[] -> languages
|
|
|
|
flagsCmd = withParseCmd' "flag" [] $ \[] -> flags
|
|
|
|
debugInfoCmd = withParseCmd' "debug" [] $ \[] -> debugInfo
|
|
|
|
rootInfoCmd = withParseCmd' "root" [] $ \[] -> rootInfo
|
2015-03-28 01:33:42 +00:00
|
|
|
componentInfoCmd = withParseCmd' "debugComponent" [] $ \ts -> componentInfo ts
|
2014-09-18 08:05:47 +00:00
|
|
|
-- internal
|
2015-01-12 16:26:46 +00:00
|
|
|
bootCmd = withParseCmd' "boot" [] $ \[] -> boot
|
2015-08-10 08:10:33 +00:00
|
|
|
nukeCachesCmd = withParseCmd' "nuke-caches" [] $ \[] -> nukeCaches >> return ""
|
2014-09-18 08:05:47 +00:00
|
|
|
|
2015-01-12 16:26:46 +00:00
|
|
|
dumpSymbolCmd = withParseCmd' "dump" [] $ \[tmpdir] -> dumpSymbol tmpdir
|
|
|
|
findSymbolCmd = withParseCmd' "find" [] $ \[sym] -> findSymbol sym
|
|
|
|
pkgDocCmd = withParseCmd' "doc" [] $ \[mdl] -> pkgDoc mdl
|
|
|
|
lintCmd = withParseCmd' "lint" s $ \[file] -> lint file
|
2014-09-18 08:05:47 +00:00
|
|
|
where s = hlintArgSpec
|
2015-03-05 17:54:39 +00:00
|
|
|
browseCmd = withParseCmd s $ \mdls -> concat <$> browse `mapM` mdls
|
2014-09-18 08:05:47 +00:00
|
|
|
where s = browseArgSpec
|
|
|
|
checkSyntaxCmd = withParseCmd [] $ checkAction checkSyntax
|
|
|
|
expandTemplateCmd = withParseCmd [] $ checkAction expandTemplate
|
|
|
|
|
|
|
|
typesCmd = withParseCmd [] $ locAction "type" types
|
|
|
|
splitsCmd = withParseCmd [] $ locAction "split" splits
|
|
|
|
sigCmd = withParseCmd [] $ locAction "sig" sig
|
|
|
|
autoCmd = withParseCmd [] $ locAction "auto" auto
|
|
|
|
refineCmd = withParseCmd [] $ locAction' "refine" refine
|
|
|
|
|
|
|
|
infoCmd = withParseCmd [] $ action
|
2015-06-01 15:10:37 +00:00
|
|
|
where action [file,_,expr] = info file $ Expression expr
|
|
|
|
action [file,expr] = info file $ Expression expr
|
2014-09-18 08:05:47 +00:00
|
|
|
action _ = throw $ InvalidCommandLine (Left "info")
|
|
|
|
|
2015-08-10 09:09:11 +00:00
|
|
|
legacyInteractiveCmd = withParseCmd [] go
|
|
|
|
where
|
|
|
|
go [] =
|
|
|
|
legacyInteractive >> return ""
|
|
|
|
go ("help":[]) =
|
|
|
|
return usage
|
|
|
|
go ("version":[]) =
|
|
|
|
return ghcModiVersion
|
|
|
|
go _ = throw $ InvalidCommandLine (Left "legacy-interactive")
|
2015-04-29 16:44:46 +00:00
|
|
|
|
2014-09-18 08:05:47 +00:00
|
|
|
checkAction :: ([t] -> a) -> [t] -> a
|
|
|
|
checkAction _ [] = throw $ InvalidCommandLine (Right "No files given.")
|
|
|
|
checkAction action files = action files
|
|
|
|
|
|
|
|
locAction :: String -> (String -> Int -> Int -> a) -> [String] -> a
|
|
|
|
locAction _ action [file,_,line,col] = action file (read line) (read col)
|
|
|
|
locAction _ action [file, line,col] = action file (read line) (read col)
|
|
|
|
locAction cmd _ _ = throw $ InvalidCommandLine (Left cmd)
|
|
|
|
|
2015-06-01 15:10:37 +00:00
|
|
|
locAction' :: String -> (String -> Int -> Int -> Expression -> a) -> [String] -> a
|
|
|
|
locAction' _ action [f,_,line,col,expr] = action f (read line) (read col) (Expression expr)
|
|
|
|
locAction' _ action [f, line,col,expr] = action f (read line) (read col) (Expression expr)
|
2014-09-18 08:05:47 +00:00
|
|
|
locAction' cmd _ _ = throw $ InvalidCommandLine (Left cmd)
|
|
|
|
|
2015-03-05 17:54:39 +00:00
|
|
|
|
2015-08-10 09:09:11 +00:00
|
|
|
modulesArgSpec :: [OptDescr (Options -> Either [String] Options)]
|
2015-03-05 17:54:39 +00:00
|
|
|
modulesArgSpec =
|
|
|
|
[ option "d" ["detailed"] "Print package modules belong to." $
|
2015-08-10 09:09:11 +00:00
|
|
|
NoArg $ \o -> Right $ o { detailed = True }
|
2015-03-05 17:54:39 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
|
2015-08-10 09:09:11 +00:00
|
|
|
hlintArgSpec :: [OptDescr (Options -> Either [String] Options)]
|
2014-09-18 08:05:47 +00:00
|
|
|
hlintArgSpec =
|
|
|
|
[ option "h" ["hlintOpt"] "Option to be passed to hlint" $
|
2015-08-10 09:09:11 +00:00
|
|
|
reqArg "hlintOpt" $ \h o -> Right $ o { hlintOpts = h : hlintOpts o }
|
2014-09-18 08:05:47 +00:00
|
|
|
]
|
2015-01-16 14:47:56 +00:00
|
|
|
|
2015-08-10 09:09:11 +00:00
|
|
|
browseArgSpec :: [OptDescr (Options -> Either [String] Options)]
|
2014-09-18 08:05:47 +00:00
|
|
|
browseArgSpec =
|
|
|
|
[ option "o" ["operators"] "Also print operators." $
|
2015-08-10 09:09:11 +00:00
|
|
|
NoArg $ \o -> Right $ o { operators = True }
|
2014-09-18 08:05:47 +00:00
|
|
|
, option "d" ["detailed"] "Print symbols with accompanying signature." $
|
2015-08-10 09:09:11 +00:00
|
|
|
NoArg $ \o -> Right $ o { detailed = True }
|
2014-09-18 08:05:47 +00:00
|
|
|
, option "q" ["qualified"] "Qualify symbols" $
|
2015-08-10 09:09:11 +00:00
|
|
|
NoArg $ \o -> Right $ o { qualified = True }
|
2014-09-18 08:05:47 +00:00
|
|
|
]
|
2015-08-10 08:10:33 +00:00
|
|
|
|
|
|
|
nukeCaches :: IOish m => GhcModT m ()
|
|
|
|
nukeCaches = do
|
|
|
|
chdir <- liftIO $ (</> "cabal-helper") <$> getAppUserDataDirectory "ghc-mod"
|
|
|
|
c <- cradle
|
|
|
|
|
2015-08-12 07:25:13 +00:00
|
|
|
when (cradleProjectType c == CabalProject) $ do
|
2015-08-10 08:10:33 +00:00
|
|
|
let root = cradleRootDir c
|
2015-08-12 07:25:13 +00:00
|
|
|
liftIO $ (trySome . removeDirectoryRecursive) `mapM_` [chdir, root </> "dist"]
|
2015-08-10 08:10:33 +00:00
|
|
|
|
|
|
|
trySome :: IO a -> IO (Either SomeException a)
|
|
|
|
trySome = try
|