forked from lix-project/lix
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
This commit is contained in:
parent
6aead00a01
commit
010d93393e
8
doc/manual/rl-next/repl-complete-colon.md
Normal file
8
doc/manual/rl-next/repl-complete-colon.md
Normal file
|
@ -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.
|
|
@ -1,7 +1,9 @@
|
||||||
|
#include <editline.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
#include "box_ptr.hh"
|
#include "box_ptr.hh"
|
||||||
#include "repl-interacter.hh"
|
#include "repl-interacter.hh"
|
||||||
|
@ -79,6 +81,8 @@ enum class ProcessLineResult {
|
||||||
PromptAgain,
|
PromptAgain,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using namespace std::literals::string_view_literals;
|
||||||
|
|
||||||
struct NixRepl
|
struct NixRepl
|
||||||
: AbstractNixRepl
|
: AbstractNixRepl
|
||||||
, detail::ReplCompleterMixin
|
, detail::ReplCompleterMixin
|
||||||
|
@ -86,6 +90,35 @@ struct NixRepl
|
||||||
, gc
|
, gc
|
||||||
#endif
|
#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;
|
size_t debugTraceIndex;
|
||||||
|
|
||||||
Strings loadedFiles;
|
Strings loadedFiles;
|
||||||
|
@ -323,6 +356,36 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
|
||||||
{
|
{
|
||||||
StringSet completions;
|
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(){}[]");
|
size_t start = prefix.find_last_of(" \n\r\t(){}[]");
|
||||||
std::string prev, cur;
|
std::string prev, cur;
|
||||||
if (start == std::string::npos) {
|
if (start == std::string::npos) {
|
||||||
|
|
Loading…
Reference in a new issue