aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQyriad <qyriad@qyriad.me>2024-05-31 18:29:10 -0600
committerQyriad <qyriad@qyriad.me>2024-06-17 13:08:02 +0000
commit010d93393e7dd978927f1c805c9b0c64e96b0ecc (patch)
tree8e132d0453ed81c22197e8be17575a5b55bd31f6
parent6aead00a014cce25c46869af7f4394f22d01d10b (diff)
repl: implement tab completing :colon commands
This uses a minor hack in which we check the rl_line_buffer global variable to workaround editline not including the colon in its completion callback. Fixes #361 Change-Id: Id159d209c537443ef5e37a975982e8e12ce1f486
-rw-r--r--doc/manual/rl-next/repl-complete-colon.md8
-rw-r--r--src/libcmd/repl.cc63
2 files changed, 71 insertions, 0 deletions
diff --git a/doc/manual/rl-next/repl-complete-colon.md b/doc/manual/rl-next/repl-complete-colon.md
new file mode 100644
index 000000000..19733575b
--- /dev/null
+++ b/doc/manual/rl-next/repl-complete-colon.md
@@ -0,0 +1,8 @@
+---
+synopsis: "`nix repl` now allows tab-completing the special repl :colon commands"
+cls: 1367
+credits: Qyriad
+category: Improvements
+---
+
+The REPL (`nix repl`) supports pressing `<TAB>` to complete a partial expression, but now also supports completing the special :colon commands as well (`:b`, `:edit`, `:doc`, etc), if the line starts with a colon.
diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc
index 49865aa90..24ab8acb9 100644
--- a/src/libcmd/repl.cc
+++ b/src/libcmd/repl.cc
@@ -1,7 +1,9 @@
+#include <editline.h>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <climits>
+#include <string_view>
#include "box_ptr.hh"
#include "repl-interacter.hh"
@@ -79,6 +81,8 @@ enum class ProcessLineResult {
PromptAgain,
};
+using namespace std::literals::string_view_literals;
+
struct NixRepl
: AbstractNixRepl
, detail::ReplCompleterMixin
@@ -86,6 +90,35 @@ struct NixRepl
, gc
#endif
{
+ /* clang-format: off */
+ static constexpr std::array COMMANDS = {
+ "add"sv, "a"sv,
+ "load"sv, "l"sv,
+ "load-flake"sv, "lf"sv,
+ "reload"sv, "r"sv,
+ "edit"sv, "e"sv,
+ "t"sv,
+ "u"sv,
+ "b"sv,
+ "bl"sv,
+ "i"sv,
+ "sh"sv,
+ "log"sv,
+ "print"sv, "p"sv,
+ "quit"sv, "q"sv,
+ "doc"sv,
+ "te"sv,
+ };
+
+ static constexpr std::array DEBUG_COMMANDS = {
+ "env"sv,
+ "bt"sv, "backtrace"sv,
+ "st"sv,
+ "c"sv, "continue"sv,
+ "s"sv, "step"sv,
+ };
+ /* clang-format: on */
+
size_t debugTraceIndex;
Strings loadedFiles;
@@ -323,6 +356,36 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
{
StringSet completions;
+ // We should only complete colon commands if there's a colon at the beginning,
+ // but editline (for... whatever reason) doesn't *give* us the colon in the
+ // completion callback. If the user types :rel<TAB>, `prefix` will only be `rel`.
+ // Luckily, editline provides a global variable for its current buffer, so we can
+ // check for the presence of a colon there.
+ if (rl_line_buffer != nullptr && rl_line_buffer[0] == ':') {
+ for (auto const & colonCmd : this->COMMANDS) {
+ if (colonCmd.starts_with(prefix)) {
+ completions.insert(std::string(colonCmd));
+ }
+ }
+
+ if (state->debugRepl) {
+ for (auto const & colonCmd : this->DEBUG_COMMANDS) {
+ if (colonCmd.starts_with(prefix)) {
+ completions.insert(std::string(colonCmd));
+ }
+ }
+ }
+
+ // If there were : command completions, then we should only return those,
+ // because otherwise this is not valid Nix syntax.
+ // However if we didn't get any completions, then this could be something
+ // like `:b pkgs.hel<TAB>`, in which case we should do expression completion
+ // as normal.
+ if (!completions.empty()) {
+ return completions;
+ }
+ }
+
size_t start = prefix.find_last_of(" \n\r\t(){}[]");
std::string prev, cur;
if (start == std::string::npos) {