From a6d7cd418385e20feab8d7260a7251f218a0d5bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Na=C3=AFm=20Favier?= Date: Fri, 18 Feb 2022 13:24:39 +0100 Subject: Ensure the completion marker is not processed beyond completion I was surprised to see an error mentioning ___COMPLETE___ when trying to complete a flag argument that had no completer implemented --- src/libutil/args.cc | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) (limited to 'src/libutil/args.cc') diff --git a/src/libutil/args.cc b/src/libutil/args.cc index f970c0e9e..38c748be0 100644 --- a/src/libutil/args.cc +++ b/src/libutil/args.cc @@ -127,11 +127,11 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end) if (flag.handler.arity == ArityAny) break; throw UsageError("flag '%s' requires %d argument(s)", name, flag.handler.arity); } - if (flag.completer) - if (auto prefix = needsCompletion(*pos)) { - anyCompleted = true; + if (auto prefix = needsCompletion(*pos)) { + anyCompleted = true; + if (flag.completer) flag.completer(n, *prefix); - } + } args.push_back(*pos++); } if (!anyCompleted) @@ -146,6 +146,7 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end) && hasPrefix(name, std::string(*prefix, 2))) completions->add("--" + name, flag->description); } + return false; } auto i = longFlags.find(std::string(*pos, 2)); if (i == longFlags.end()) return false; @@ -187,10 +188,12 @@ bool Args::processArgs(const Strings & args, bool finish) { std::vector ss; for (const auto &[n, s] : enumerate(args)) { - ss.push_back(s); - if (exp.completer) - if (auto prefix = needsCompletion(s)) + if (auto prefix = needsCompletion(s)) { + ss.push_back(*prefix); + if (exp.completer) exp.completer(n, *prefix); + } else + ss.push_back(s); } exp.handler.fun(ss); expectedArgs.pop_front(); @@ -322,16 +325,16 @@ MultiCommand::MultiCommand(const Commands & commands_) .optional = true, .handler = {[=](std::string s) { assert(!command); - if (auto prefix = needsCompletion(s)) { - for (auto & [name, command] : commands) - if (hasPrefix(name, *prefix)) - completions->add(name); - } auto i = commands.find(s); if (i == commands.end()) throw UsageError("'%s' is not a recognised command", s); command = {s, i->second()}; command->second->parent = this; + }}, + .completer = {[&](size_t, std::string_view prefix) { + for (auto & [name, command] : commands) + if (hasPrefix(name, prefix)) + completions->add(name); }} }); -- cgit v1.2.3 From 5461ff532d6169be86af703b15cfb49569732cbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Na=C3=AFm=20Favier?= Date: Fri, 18 Feb 2022 13:26:40 +0100 Subject: Make completeDir follow symlinks Allows completing `nix why-depends /run/cur` to /run/current-system --- src/libutil/args.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libutil/args.cc') diff --git a/src/libutil/args.cc b/src/libutil/args.cc index 38c748be0..b60c609a6 100644 --- a/src/libutil/args.cc +++ b/src/libutil/args.cc @@ -290,7 +290,7 @@ static void _completePath(std::string_view prefix, bool onlyDirs) if (glob((std::string(prefix) + "*").c_str(), flags, nullptr, &globbuf) == 0) { for (size_t i = 0; i < globbuf.gl_pathc; ++i) { if (onlyDirs) { - auto st = lstat(globbuf.gl_pathv[i]); + auto st = stat(globbuf.gl_pathv[i]); if (!S_ISDIR(st.st_mode)) continue; } completions->add(globbuf.gl_pathv[i]); -- cgit v1.2.3 From 55c6906701ee7fc7e915f7889fea86957b020f94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Na=C3=AFm=20Favier?= Date: Sat, 19 Feb 2022 14:26:34 +0100 Subject: Perform tilde expansion when completing flake fragments Allows completing `nix build ~/flake#`. We can implement expansion for `~user` later if needed. Not using wordexp(3) since that expands way too much. --- src/libutil/args.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/libutil/args.cc') diff --git a/src/libutil/args.cc b/src/libutil/args.cc index b60c609a6..10f01af5b 100644 --- a/src/libutil/args.cc +++ b/src/libutil/args.cc @@ -282,12 +282,13 @@ static void _completePath(std::string_view prefix, bool onlyDirs) { completionType = ctFilenames; glob_t globbuf; - int flags = GLOB_NOESCAPE | GLOB_TILDE; + int flags = GLOB_NOESCAPE; #ifdef GLOB_ONLYDIR if (onlyDirs) flags |= GLOB_ONLYDIR; #endif - if (glob((std::string(prefix) + "*").c_str(), flags, nullptr, &globbuf) == 0) { + // using expandTilde here instead of GLOB_TILDE(_CHECK) so that ~ expands to /home/user/ + if (glob((expandTilde(prefix) + "*").c_str(), flags, nullptr, &globbuf) == 0) { for (size_t i = 0; i < globbuf.gl_pathc; ++i) { if (onlyDirs) { auto st = stat(globbuf.gl_pathv[i]); @@ -295,8 +296,8 @@ static void _completePath(std::string_view prefix, bool onlyDirs) } completions->add(globbuf.gl_pathv[i]); } - globfree(&globbuf); } + globfree(&globbuf); } void completePath(size_t, std::string_view prefix) -- cgit v1.2.3