diff --git a/src/Ghcid.hs b/src/Ghcid.hs index 9cf21c2..06a8640 100644 --- a/src/Ghcid.hs +++ b/src/Ghcid.hs @@ -65,6 +65,7 @@ data Options = Options ,color :: ColorMode ,setup :: [String] ,allow_eval :: Bool + ,single_line_command_marker :: String ,target :: [String] } deriving (Data,Typeable,Show) @@ -104,6 +105,7 @@ options = cmdArgsMode $ Options ,color = Auto &= name "colour" &= name "color" &= opt Always &= typ "always/never/auto" &= help "Color output (defaults to when the terminal supports it)" ,setup = [] &= name "setup" &= typ "COMMAND" &= help "Setup commands to pass to ghci on stdin, usually :set " ,allow_eval = False &= name "allow-eval" &= help "Execute REPL commands in comments" + ,single_line_command_marker = "$>" &= name "eval-mark" &= typ "MARKER" &= help "Replace the command marker \"$>\" with an alternative for single line commands" ,target = [] &= typ "TARGET" &= help "Target Component to build (e.g. lib:foo for Cabal, foo:lib for Stack)" } &= verbosity &= program "ghcid" &= summary ("Auto reloading GHCi daemon v" ++ showVersion version) @@ -258,7 +260,12 @@ mainWithTerminal termSize termOutput = do else id maybe withWaiterNotify withWaiterPoll (poll opts) $ \waiter -> - runGhcid (if allow_eval opts then enableEval session else session) waiter termSize (clear . termOutput . restyle) opts + runGhcid + (customizeSingleLineCommandMarker (single_line_command_marker opts) $ if allow_eval opts then enableEval session else session) + waiter + termSize + (clear . termOutput . restyle) + opts diff --git a/src/Session.hs b/src/Session.hs index 13e7701..06d8fb9 100755 --- a/src/Session.hs +++ b/src/Session.hs @@ -3,8 +3,8 @@ -- | A persistent version of the Ghci session, encoding lots of semantics on top. -- Not suitable for calling multithreaded. module Session( - Session, enableEval, withSession, - sessionStart, sessionReload, + Session, enableEval, customizeSingleLineCommandMarker, + withSession, sessionStart, sessionReload, sessionExecAsync, ) where @@ -35,11 +35,15 @@ data Session = Session ,running :: Var Bool -- ^ Am I actively running an async command ,withThread :: ThreadId -- ^ Thread that called withSession ,allowEval :: Bool -- ^ Is the allow-eval flag set? + ,singleLineCommandMarker :: String -- ^ alternative to "$>" for single line commands } enableEval :: Session -> Session enableEval s = s { allowEval = True } +customizeSingleLineCommandMarker :: String -> Session -> Session +customizeSingleLineCommandMarker m s = s { singleLineCommandMarker = m } + debugShutdown x = when False $ print ("DEBUG SHUTDOWN", x) @@ -56,6 +60,7 @@ withSession f = do debugShutdown "Starting session" withThread <- myThreadId let allowEval = False + let singleLineCommandMarker = "$>" f Session{..} `finally` do debugShutdown "Start finally" modifyVar_ running $ const $ pure False @@ -115,7 +120,7 @@ sessionStart Session{..} cmd setup = do messages <- pure $ qualify dir messages let loaded = loadedModules messages - evals <- performEvals v allowEval loaded + evals <- performEvals v allowEval singleLineCommandMarker loaded -- install a handler forkIO $ do @@ -142,9 +147,9 @@ sessionRestart session@Session{..} = do sessionStart session cmd setup -performEvals :: Ghci -> Bool -> [FilePath] -> IO [Load] -performEvals _ False _ = pure [] -performEvals ghci True reloaded = do +performEvals :: Ghci -> Bool -> String -> [FilePath] -> IO [Load] +performEvals _ False _ _ = pure [] +performEvals ghci True singleLineCommandMarker reloaded = do cmds <- mapM getCommands reloaded fmap join $ forM cmds $ \(file, cmds') -> forM cmds' $ \(num, cmd) -> do @@ -152,29 +157,28 @@ performEvals ghci True reloaded = do execStream ghci cmd $ \_ resp -> modifyIORef ref (resp :) resp <- unlines . reverse <$> readIORef ref pure $ Eval $ EvalResult file (num, 1) cmd resp - - -getCommands :: FilePath -> IO (FilePath, [(Int, String)]) -getCommands fp = do - ls <- readFileUTF8' fp - pure (fp, splitCommands $ zipFrom 1 $ lines ls) - -splitCommands :: [(Int, String)] -> [(Int, String)] -splitCommands [] = [] -splitCommands ((num, line) : ls) - | isCommand line = - let (cmds, xs) = span (isCommand . snd) ls - in (num, unwords $ fmap (drop $ length commandPrefix) $ line : fmap snd cmds) : splitCommands xs - | isMultilineCommandPrefix line = - let (cmds, xs) = break (isMultilineCommandSuffix . snd) ls - in (num, unlines (wrapGhciMultiline (fmap snd cmds))) : splitCommands (drop1 xs) - | otherwise = splitCommands ls - -isCommand :: String -> Bool -isCommand = isPrefixOf commandPrefix - -commandPrefix :: String -commandPrefix = "-- $> " + where + getCommands :: FilePath -> IO (FilePath, [(Int, String)]) + getCommands fp = do + ls <- readFileUTF8' fp + pure (fp, splitCommands $ zipFrom 1 $ lines ls) + + splitCommands :: [(Int, String)] -> [(Int, String)] + splitCommands [] = [] + splitCommands ((num, line) : ls) + | isCommand line = + let (cmds, xs) = span (isCommand . snd) ls + in (num, unwords $ fmap (drop $ length commandPrefix) $ line : fmap snd cmds) : splitCommands xs + | isMultilineCommandPrefix line = + let (cmds, xs) = break (isMultilineCommandSuffix . snd) ls + in (num, unlines (wrapGhciMultiline (fmap snd cmds))) : splitCommands (drop1 xs) + | otherwise = splitCommands ls + + isCommand :: String -> Bool + isCommand = isPrefixOf commandPrefix + + commandPrefix :: String + commandPrefix = "-- " ++ singleLineCommandMarker ++ " " isMultilineCommandPrefix :: String -> Bool isMultilineCommandPrefix = (==) multilineCommandPrefix @@ -212,7 +216,8 @@ sessionReload session@Session{..} = do loaded <- map ((dir ) . snd) <$> showModules ghci let reloaded = loadedModules messages warn <- readIORef warnings - evals <- performEvals ghci allowEval reloaded + evals <- + performEvals ghci allowEval singleLineCommandMarker reloaded -- only keep old warnings from files that are still loaded, but did not reload let validWarn w = loadFile w `elem` loaded && loadFile w `notElem` reloaded