1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
6import Prelude hiding (lines, read)
7import Control.Applicative ((<|>))
8import Control.Monad.RWS (lift, liftIO)
9import qualified Control.Monad.RWS as RWS
10import qualified Data.ByteString.Builder as B
11import qualified Data.ByteString.Char8 as BS
12import qualified Data.ByteString.Lazy as LBS
13import qualified Data.List as List
14import qualified Data.Map as Map
15import Data.Monoid ((<>))
16import qualified Data.Text as Text
17import qualified Data.Text.Encoding as Text
18import qualified System.Console.Haskeline as Repl
19import qualified System.Directory as Dir
20import qualified System.Exit as Exit
21import System.FilePath ((</>))
22import qualified System.IO as IO
23import qualified System.Process as Proc
25import qualified Elm.Compiler as Elm (version)
26import qualified Elm.Name as N
27import qualified Elm.Project as Project
28import qualified Elm.Package as Pkg
29import qualified Elm.PerUserCache as PerUserCache
30import qualified Elm.Utils as Elm
31import qualified Reporting.Task as Task
32import qualified Reporting.Progress.Repl as Repl
34import qualified CommandLine.Args as Args
41run :: Args.ReplFlags -> IO ()
42run (Args.ReplFlags maybeAlternateInterpreter) =
43 do interpreter <- getInterpreter maybeAlternateInterpreter
44 _ <- Project.getRootWithReplFallback
45 putStrLn welcomeMessage
46 settings <- initSettings
47 let manager = Repl.runInputT settings (Repl.withInterrupt loop)
48 (exitCode,_,_) <- RWS.runRWST manager interpreter initalState
49 Exit.exitWith exitCode
53 RWS.RWST FilePath () State IO
60loop :: Repl.InputT Runner Exit.ExitCode
62 do input <- Repl.handleInterrupt (return Skip) read
63 maybe loop return =<< lift (eval input)
70read :: Repl.InputT Runner Input
75readHelp :: String -> [String] -> Repl.InputT Runner Input
76readHelp starter lines =
77 do input <- Repl.getInputLine starter
83 if not (null line) && last line == '\\'
84 then readHelp "| " (init line : lines)
85 else return $ toInput $ List.intercalate "\n" $ reverse (line : lines)
92eval :: Input -> Runner (Maybe Exit.ExitCode)
99 return (Just Exit.ExitSuccess)
102 do RWS.put initalState
106 report (helpMessage info)
109 Repl.handleInterrupt (report "<cancelled>") $
110 do state@(State _ imports types defs tricks) <- RWS.get
112 Elm.Import name src ->
113 interpret None $ state { _imports = Map.insert name src imports }
116 interpret None $ state { _types = Map.insert name src types }
118 Elm.Def (Just name) src ->
119 interpret (Def name) $ state { _defs = Map.insert name src defs }
121 Elm.Def Nothing src ->
122 interpret None $ state { _tricks = src : tricks }
125 interpret (Expr src) state
128 report "I cannot handle type annotations."
131 report "I cannot handle port declarations."
134toUtf8 :: String -> BS.ByteString
136 LBS.toStrict (B.toLazyByteString (B.stringUtf8 string))
139report :: String -> Runner (Maybe a)
141 do liftIO (putStrLn msg)
145data Output = None | Def N.Name | Expr Text.Text
148interpret :: Output -> State -> Runner (Maybe a)
150 do reporter <- liftIO $ Repl.create
151 result <- liftIO $ Task.run reporter $ compile def state
158 do RWS.put (state { _count = _count state + 1 })
159 maybe (return ()) interpretHelp maybePath
164interpretHelp :: FilePath -> Runner ()
166 do interpreter <- RWS.ask
167 let proc = (Proc.proc interpreter [path]) { Proc.std_in = Proc.CreatePipe }
168 liftIO $ Proc.withCreateProcess proc $ \_ _ _ handle ->
169 do _ <- Proc.waitForProcess handle
181 | Help (Maybe String)
185toInput :: String -> Input
187 case dropWhile (==' ') string of
198 Help (Just (takeWhile (/=' ') rest))
204 Code (Elm.parseEntry string)
214 , _imports :: Map.Map N.Name Text.Text
215 , _types :: Map.Map N.Name Text.Text
216 , _defs :: Map.Map N.Name Text.Text
217 , _tricks :: [Text.Text]
223 State 0 Map.empty Map.empty Map.empty []
226compile :: Output -> State -> Task.Task (Maybe FilePath)
227compile output (State count imports types defs tricks) =
230 (<>) "module ElmRepl exposing (..)\n\n" $
231 addLines imports $ addLines types $ addLines defs $
232 List.foldr (<>) mempty (map Text.encodeUtf8Builder tricks)
236 if Map.null types && Map.null defs && null tricks then
237 evaluate (elmSourceCode <> "unit = ()") Nothing
239 evaluate elmSourceCode Nothing
242 evaluate elmSourceCode (Just name)
246 name = N.addIndex "repl_value_" count
247 text = Text.replace "\n" "\n " expr
248 def = N.toBuilder name <> " =\n " <> Text.encodeUtf8Builder text <> "\n"
250 evaluate (elmSourceCode <> def) (Just name)
253addLines :: Map.Map N.Name Text.Text -> B.Builder -> B.Builder
254addLines dict builder =
255 Map.foldr (\text b -> Text.encodeUtf8Builder text <> b) builder dict
258evaluate :: B.Builder -> Maybe N.Name -> Task.Task (Maybe FilePath)
259evaluate builder maybeName =
260 Project.compileForRepl (LBS.toStrict (B.toLazyByteString builder)) maybeName
267initSettings :: IO (Repl.Settings Runner)
269 do cache <- PerUserCache.getReplRoot
272 { Repl.historyFile = Just (cache </> "history")
273 , Repl.autoAddHistory = True
274 , Repl.complete = Repl.completeWord Nothing " \n" lookupCompletions
278lookupCompletions :: String -> Runner [Repl.Completion]
279lookupCompletions string =
280 do (State _ imports types defs _) <- RWS.get
282 addMatches string False defs $
283 addMatches string False types $
284 addMatches string True imports $
285 addMatches string False commands []
288commands :: Map.Map N.Name ()
290 Map.fromList [ (":exit", ()), (":quit", ()), (":reset", ()), (":help", ()) ]
293addMatches :: String -> Bool -> Map.Map N.Name v -> [Repl.Completion] -> [Repl.Completion]
294addMatches string isFinished dict completions =
295 Map.foldrWithKey (addMatch string isFinished) completions dict
298addMatch :: String -> Bool -> N.Name -> v -> [Repl.Completion] -> [Repl.Completion]
299addMatch string isFinished name _ completions =
300 let suggestion = N.toString name in
301 if List.isPrefixOf string suggestion then
302 Repl.Completion (drop (length string) suggestion) suggestion isFinished : completions
311welcomeMessage :: String
315 "---- Elm " ++ elmVersion ++ " "
317 starter ++ replicate (80 - length starter) '-' ++ "\n"
318 ++ " :help for help, :exit to exit, more at <https://github.com/elm-lang/elm-repl>\n"
319 ++ "--------------------------------------------------------------------------------"
324 Pkg.versionToString Elm.version
331helpMessage :: Maybe String -> String
332helpMessage maybeBadCommand =
333 case maybeBadCommand of
338 "I do not recognize the :" ++ command ++ " command. " ++ genericHelpMessage
341genericHelpMessage :: String
343 "Valid commands include:\n\
345 \ :exit Exit the REPL\n\
346 \ :help Show this information\n\
347 \ :reset Clear all previous imports and definitions\n\
349 \More info at <https://github.com/elm-lang/elm/blob/" ++ elmVersion ++ "/help/repl.md>\n"
356getInterpreter :: Maybe String -> IO FilePath
357getInterpreter maybeName =
360 getInterpreterHelp name (Dir.findExecutable name)
363 getInterpreterHelp "node` or `nodejs" $
364 do exe1 <- Dir.findExecutable "node"
365 exe2 <- Dir.findExecutable "nodejs"
366 return (exe1 <|> exe2)
369getInterpreterHelp :: String -> IO (Maybe FilePath) -> IO FilePath
370getInterpreterHelp name findExe =
371 do maybePath <- findExe
377 do IO.hPutStrLn IO.stderr (exeNotFound name)
381exeNotFound :: String -> String
383 "The REPL relies on node.js to execute JavaScript code outside the browser.\n"
384 ++ "I could not find executable `" ++ name ++ "` on your PATH though!\n\n"
385 ++ "You can install node.js from <http://nodejs.org/>. If it is already installed\n"
386 ++ "but has a different name, use the --interpreter flag."
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list