diff options
author | Qyriad <qyriad@qyriad.me> | 2024-05-31 18:29:10 -0600 |
---|---|---|
committer | Qyriad <qyriad@qyriad.me> | 2024-06-17 13:08:02 +0000 |
commit | 010d93393e7dd978927f1c805c9b0c64e96b0ecc (patch) | |
tree | 8e132d0453ed81c22197e8be17575a5b55bd31f6 /src/libcmd/repl.cc | |
parent | 6aead00a014cce25c46869af7f4394f22d01d10b (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
Diffstat (limited to 'src/libcmd/repl.cc')
-rw-r--r-- | src/libcmd/repl.cc | 63 |
1 files changed, 63 insertions, 0 deletions
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) { |