From 2d729e4f6f45c079ddf149610357e648e805f42c Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 18 Feb 2016 06:27:39 -0600 Subject: [PATCH 1/4] Support multiline input by detecting "unfinished" parse errors. Fixes #4, --- nix-repl.cc | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/nix-repl.cc b/nix-repl.cc index 1077f5d8f..9dddb2603 100644 --- a/nix-repl.cc +++ b/nix-repl.cc @@ -41,7 +41,7 @@ struct NixRepl NixRepl(const Strings & searchPath); void mainLoop(const Strings & files); void completePrefix(string prefix); - bool getLine(string & line); + bool getLine(string & line, const char * prompt); bool processLine(string line); void loadFile(const Path & path); void initEnv(); @@ -96,21 +96,39 @@ void NixRepl::mainLoop(const Strings & files) using_history(); read_history(0); + string input; + while (true) { + // When continuing input from a previous, don't print a prompt, just align to the same + // number of chars as the prompt. + const char * prompt = input.empty() ? "nix-repl> " : " "; string line; - if (!getLine(line)) { + if (!getLine(line, prompt)) { std::cout << std::endl; break; } + input.append(removeWhitespace(line)); + input.push_back('\n'); + try { - if (!processLine(removeWhitespace(line))) return; + if (!processLine(input)) return; + } catch (ParseError & e) { + if (e.msg().find("unexpected $end") != std::string::npos) { + // For parse errors on incomplete input, we continue waiting for the next line of + // input without clearing the input so far. + continue; + } else { + printMsg(lvlError, "error: " + e.msg()); + } } catch (Error & e) { printMsg(lvlError, "error: " + e.msg()); } catch (Interrupted & e) { printMsg(lvlError, "error: " + e.msg()); } + // We handled the current input fully, so we should clear it and read brand new input. + input.clear(); std::cout << std::endl; } } @@ -149,7 +167,7 @@ char * completerThunk(const char * s, int state) } -bool NixRepl::getLine(string & line) +bool NixRepl::getLine(string & line, const char * prompt) { struct sigaction act, old; act.sa_handler = sigintHandler; @@ -164,7 +182,7 @@ bool NixRepl::getLine(string & line) curRepl = this; rl_completion_entry_function = completerThunk; - char * s = readline("nix-repl> "); + char * s = readline(prompt); if (!s) return false; line = chomp(string(s)); free(s); From 64080d26fe9364bc0ea0893f357386ed3121878f Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 18 Feb 2016 06:50:52 -0600 Subject: [PATCH 2/4] Cancel multiline input on Ctrl-C. --- nix-repl.cc | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/nix-repl.cc b/nix-repl.cc index 9dddb2603..8d2fbd919 100644 --- a/nix-repl.cc +++ b/nix-repl.cc @@ -41,7 +41,7 @@ struct NixRepl NixRepl(const Strings & searchPath); void mainLoop(const Strings & files); void completePrefix(string prefix); - bool getLine(string & line, const char * prompt); + bool getLine(string & input, const char * prompt); bool processLine(string line); void loadFile(const Path & path); void initEnv(); @@ -102,15 +102,11 @@ void NixRepl::mainLoop(const Strings & files) // When continuing input from a previous, don't print a prompt, just align to the same // number of chars as the prompt. const char * prompt = input.empty() ? "nix-repl> " : " "; - string line; - if (!getLine(line, prompt)) { + if (!getLine(input, prompt)) { std::cout << std::endl; break; } - input.append(removeWhitespace(line)); - input.push_back('\n'); - try { if (!processLine(input)) return; } catch (ParseError & e) { @@ -167,7 +163,7 @@ char * completerThunk(const char * s, int state) } -bool NixRepl::getLine(string & line, const char * prompt) +bool NixRepl::getLine(string & input, const char * prompt) { struct sigaction act, old; act.sa_handler = sigintHandler; @@ -176,15 +172,17 @@ bool NixRepl::getLine(string & line, const char * prompt) if (sigaction(SIGINT, &act, &old)) throw SysError("installing handler for SIGINT"); - if (sigsetjmp(sigintJmpBuf, 1)) - line = ""; - else { + if (sigsetjmp(sigintJmpBuf, 1)) { + input.clear(); + } else { curRepl = this; rl_completion_entry_function = completerThunk; char * s = readline(prompt); if (!s) return false; - line = chomp(string(s)); + string line = chomp(string(s)); + input.append(removeWhitespace(line)); + input.push_back('\n'); free(s); if (line != "") { add_history(line.c_str()); From 60ba98242f7c976e4e14113d28bced03b32db4f5 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 18 Feb 2016 06:59:51 -0600 Subject: [PATCH 3/4] Fix recognition of REPL commands. --- nix-repl.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix-repl.cc b/nix-repl.cc index 8d2fbd919..e52c3b257 100644 --- a/nix-repl.cc +++ b/nix-repl.cc @@ -286,7 +286,7 @@ bool NixRepl::processLine(string line) string command, arg; if (line[0] == ':') { - size_t p = line.find(' '); + size_t p = line.find_first_of(" \n\r\t"); command = string(line, 0, p); if (p != string::npos) arg = removeWhitespace(string(line, p)); } else { From 56c7f0e8c581c66a968fdae681e9c417817e28d0 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 18 Feb 2016 07:04:55 -0600 Subject: [PATCH 4/4] Fix typo in comment. --- nix-repl.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix-repl.cc b/nix-repl.cc index e52c3b257..3834e572a 100644 --- a/nix-repl.cc +++ b/nix-repl.cc @@ -99,7 +99,7 @@ void NixRepl::mainLoop(const Strings & files) string input; while (true) { - // When continuing input from a previous, don't print a prompt, just align to the same + // When continuing input from previous lines, don't print a prompt, just align to the same // number of chars as the prompt. const char * prompt = input.empty() ? "nix-repl> " : " "; if (!getLine(input, prompt)) {