From 2ed0a35bd0e194ae9bd37b189c3f4bb59f6c6845 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Sun, 17 Jul 2016 12:17:44 +1000 Subject: [PATCH] Repl UX improvements - Use `"> "` as repl prompt - Add basic help message at startup - Skips parsing if an empty line is entered - Adds previous expressions to linenoise history so that you can get back to them using the arrow keys - Quit repl on ^C and ^D --- src/repl.rs | 13 ++++++++-- std/repl.glu | 72 +++++++++++++++++++++++++++------------------------- 2 files changed, 48 insertions(+), 37 deletions(-) diff --git a/src/repl.rs b/src/repl.rs index 7f33ed8b95..48c491f840 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -77,9 +77,18 @@ fn find_info(args: WithVM) -> IO> { } fn compile_repl(vm: &Thread) -> Result<(), Box> { - fn input(s: &str) -> IO> { - IO::Value(::linenoise::input(s)) + fn input(prompt: &str) -> IO> { + let input = ::linenoise::input(prompt); + + if let Some(ref input) = input { + if !input.trim().is_empty() { + ::linenoise::history_add(input); + } + } + + IO::Value(input) } + try!(vm.define_global("repl_prim", record!( type_of_expr => primitive!(1 type_of_expr), diff --git a/std/repl.glu b/std/repl.glu index 1408c91234..3527665a42 100644 --- a/std/repl.glu +++ b/std/repl.glu @@ -2,12 +2,10 @@ let prelude = import "std/prelude.glu" let map = import "std/map.glu" let string = import "std/string.glu" let { Map } = map -let { (>>=), return, (>>), join, map = fmap, lift2, forM_ } = prelude.make_Monad prelude.monad_IO -let { Eq, Option, Result, Monoid } = prelude -let { (==) } = string.eq -let (++) = string.monoid.(<>) -let { singleton, find, insert, monoid, to_list } - = map.make string.ord +let { (>>=), return, (>>), forM_ } = prelude.make_Monad prelude.monad_IO +let { Eq, Result, Monoid } = prelude +let { (<>) = (++) } = string.monoid +let { singleton, find, monoid, to_list } = map.make string.ord let { (<>), empty } = monoid @@ -23,35 +21,31 @@ let load_file filename: String -> IO String = | Ok expr -> io.load_script modulename expr | Err msg -> return msg -type Cmd = { info: String, action: String -> IO Bool } +type Cmd = { + info: String, + action: String -> IO Bool +} let commands: Map String Cmd = + let print_result result = + match result with + | Ok x -> io.print x + | Err x -> io.print x + let commands = ref empty let cmds = singleton "q" { info = "Quit the REPL", action = \_ -> return False } <> singleton "t" { info = "Prints the type with an expression", - action = \arg -> repl_prim.type_of_expr arg >>= \result -> - match result with - | Ok x -> io.print x - | Err x -> io.print x - >> return True + action = \arg -> repl_prim.type_of_expr arg >>= print_result >> return True } <> singleton "i" { info = "Prints information about the given name", - action = \arg -> repl_prim.find_info arg >>= \result -> - match result with - | Ok x -> io.print x - | Err x -> io.print x - >> return True + action = \arg -> repl_prim.find_info arg >>= print_result >> return True } <> singleton "k" { info = "Prints the kind with the given type", - action = \arg -> repl_prim.find_kind arg >>= \result -> - match result with - | Ok x -> io.print x - | Err x -> io.print x - >> return True + action = \arg -> repl_prim.find_kind arg >>= print_result >> return True } <> singleton "l" { info = "Loads the file at 'folder/module.ext' and stores it at 'module'", @@ -71,7 +65,7 @@ let commands: Map String Cmd = commands <- cmds load commands -let do_command line: String -> IO Bool = +let do_command line: String -> IO Bool = let cmd = string.slice line 1 2 let arg = if string.length line >= 3 then string.trim (string.slice line 3 (string.length line)) else "" match find cmd commands with @@ -81,25 +75,33 @@ let do_command line: String -> IO Bool = let store line: String -> IO Bool = let line = string.trim line match string.find line " " with - | Some bind_end -> + | Some bind_end -> let binding = string.slice line 0 bind_end let expr = string.slice line bind_end (string.length line) io.load_script binding expr >> return True | None -> io.print "Expected binding in definition" >> return True -let loop x: () -> IO () = repl_prim.input ">>" >>= \line_opt -> - match line_opt with - | None -> loop () - | Some line -> - (if string.starts_with line ":" then +let loop x: () -> IO () = + let run_line line = + if string.is_empty (string.trim line) then + return True + else if string.starts_with line ":" then do_command line else if string.starts_with line "def " then store (string.slice line 4 (string.length line)) else - io.catch (io.run_expr line) return >>= io.print >> return True) >>= \continue -> - if continue then - loop () - else - return () + io.catch (io.run_expr line) return + >>= io.print + >> return True + + repl_prim.input "> " >>= \line_opt -> + match line_opt with + | None -> return () + | Some line -> run_line line >>= \continue -> + if continue then loop () else return () + +let run x: () -> IO () = + io.print "gluon (:h for help, :q to quit)" + >>= loop -loop +run