aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--misc/bash/completion.sh10
-rw-r--r--src/libutil/args.cc48
-rw-r--r--src/libutil/args.hh12
-rw-r--r--src/nix/build.cc1
-rw-r--r--src/nix/cat.cc4
-rw-r--r--src/nix/command.cc1
-rw-r--r--src/nix/hash.cc2
-rw-r--r--src/nix/installables.cc3
-rw-r--r--src/nix/ls.cc4
-rw-r--r--src/nix/main.cc5
-rw-r--r--src/nix/repl.cc2
-rw-r--r--src/nix/run.cc2
-rw-r--r--src/nix/sigs.cc3
13 files changed, 82 insertions, 15 deletions
diff --git a/misc/bash/completion.sh b/misc/bash/completion.sh
index 097353b50..93298c369 100644
--- a/misc/bash/completion.sh
+++ b/misc/bash/completion.sh
@@ -1,6 +1,14 @@
function _complete_nix {
+ local have_type
while IFS= read -r line; do
- COMPREPLY+=("$line")
+ if [[ -z $have_type ]]; then
+ have_type=1
+ if [[ $line = filenames ]]; then
+ compopt -o filenames
+ fi
+ else
+ COMPREPLY+=("$line")
+ fi
done < <(NIX_GET_COMPLETIONS=$COMP_CWORD "${COMP_WORDS[@]}")
}
diff --git a/src/libutil/args.cc b/src/libutil/args.cc
index ceaabcfca..320b1b4b2 100644
--- a/src/libutil/args.cc
+++ b/src/libutil/args.cc
@@ -1,6 +1,8 @@
#include "args.hh"
#include "hash.hh"
+#include <glob.h>
+
namespace nix {
void Args::addFlag(Flag && flag_)
@@ -13,6 +15,7 @@ void Args::addFlag(Flag && flag_)
if (flag->shortName) shortFlags[flag->shortName] = flag;
}
+bool pathCompletions = false;
std::shared_ptr<std::set<std::string>> completions;
std::string completionMarker = "___COMPLETE___";
@@ -128,8 +131,11 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
else
throw UsageError("flag '%s' requires %d argument(s)", name, flag.handler.arity);
} else {
- if (needsCompletion(*pos))
+ if (needsCompletion(*pos)) {
+ if (flag.completer)
+ flag.completer(n, *pos);
anyNeedsCompletion = true;
+ }
args.push_back(*pos++);
}
}
@@ -214,6 +220,46 @@ Args::Flag Args::Flag::mkHashTypeFlag(std::string && longName, HashType * ht)
};
}
+void completePath(size_t, std::string_view s)
+{
+ if (auto prefix = needsCompletion(s)) {
+ pathCompletions = true;
+ glob_t globbuf;
+ if (glob((*prefix + "*").c_str(), GLOB_NOESCAPE | GLOB_TILDE, nullptr, &globbuf) == 0) {
+ for (size_t i = 0; i < globbuf.gl_pathc; ++i)
+ completions->insert(globbuf.gl_pathv[i]);
+ globfree(&globbuf);
+ }
+ }
+}
+
+void Args::expectPathArg(const std::string & label, string * dest, bool optional)
+{
+ expectedArgs.push_back({
+ .label = label,
+ .arity = 1,
+ .optional = optional,
+ .handler = {[=](std::vector<std::string> ss) {
+ completePath(0, ss[0]);
+ *dest = ss[0];
+ }}
+ });
+}
+
+void Args::expectPathArgs(const std::string & label, std::vector<std::string> * dest)
+{
+ expectedArgs.push_back({
+ .label = label,
+ .arity = 0,
+ .optional = false,
+ .handler = {[=](std::vector<std::string> ss) {
+ for (auto & s : ss)
+ completePath(0, s);
+ *dest = std::move(ss);
+ }}
+ });
+}
+
Strings argvToStrings(int argc, char * * argv)
{
Strings args;
diff --git a/src/libutil/args.hh b/src/libutil/args.hh
index ae2875e72..f93459c96 100644
--- a/src/libutil/args.hh
+++ b/src/libutil/args.hh
@@ -83,6 +83,7 @@ protected:
std::string category;
Strings labels;
Handler handler;
+ std::function<void(size_t, std::string_view)> completer;
static Flag mkHashTypeFlag(std::string && longName, HashType * ht);
};
@@ -98,8 +99,8 @@ protected:
struct ExpectedArg
{
std::string label;
- size_t arity; // 0 = any
- bool optional;
+ size_t arity = 0; // 0 = any
+ bool optional = false;
std::function<void(std::vector<std::string>)> handler;
};
@@ -182,6 +183,8 @@ public:
}});
}
+ void expectPathArg(const std::string & label, string * dest, bool optional = false);
+
/* Expect 0 or more arguments. */
void expectArgs(const std::string & label, std::vector<std::string> * dest)
{
@@ -190,6 +193,8 @@ public:
}});
}
+ void expectPathArgs(const std::string & label, std::vector<std::string> * dest);
+
friend class MultiCommand;
};
@@ -257,7 +262,10 @@ typedef std::vector<std::pair<std::string, std::string>> Table2;
void printTable(std::ostream & out, const Table2 & table);
extern std::shared_ptr<std::set<std::string>> completions;
+extern bool pathCompletions;
std::optional<std::string> needsCompletion(std::string_view s);
+void completePath(size_t, std::string_view s);
+
}
diff --git a/src/nix/build.cc b/src/nix/build.cc
index 83d47acd4..474337208 100644
--- a/src/nix/build.cc
+++ b/src/nix/build.cc
@@ -18,6 +18,7 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixProfile
.description = "path of the symlink to the build result",
.labels = {"path"},
.handler = {&outLink},
+ .completer = completePath
});
addFlag({
diff --git a/src/nix/cat.cc b/src/nix/cat.cc
index fd91f2036..eeee1e529 100644
--- a/src/nix/cat.cc
+++ b/src/nix/cat.cc
@@ -25,7 +25,7 @@ struct CmdCatStore : StoreCommand, MixCat
{
CmdCatStore()
{
- expectArg("path", &path);
+ expectPathArg("path", &path);
}
std::string description() override
@@ -47,7 +47,7 @@ struct CmdCatNar : StoreCommand, MixCat
CmdCatNar()
{
- expectArg("nar", &narPath);
+ expectPathArg("nar", &narPath);
expectArg("path", &path);
}
diff --git a/src/nix/command.cc b/src/nix/command.cc
index 71b027719..803a36e84 100644
--- a/src/nix/command.cc
+++ b/src/nix/command.cc
@@ -108,6 +108,7 @@ MixProfile::MixProfile()
.description = "profile to update",
.labels = {"path"},
.handler = {&profile},
+ .completer = completePath
});
}
diff --git a/src/nix/hash.cc b/src/nix/hash.cc
index 366314227..0f460c668 100644
--- a/src/nix/hash.cc
+++ b/src/nix/hash.cc
@@ -31,7 +31,7 @@ struct CmdHash : Command
.labels({"modulus"})
.dest(&modulus);
#endif
- expectArgs("paths", &paths);
+ expectPathArgs("paths", &paths);
}
std::string description() override
diff --git a/src/nix/installables.cc b/src/nix/installables.cc
index 57060e9b1..c144a7e70 100644
--- a/src/nix/installables.cc
+++ b/src/nix/installables.cc
@@ -77,7 +77,8 @@ SourceExprCommand::SourceExprCommand()
.shortName = 'f',
.description = "evaluate FILE rather than the default",
.labels = {"file"},
- .handler = {&file}
+ .handler = {&file},
+ .completer = completePath
});
addFlag({
diff --git a/src/nix/ls.cc b/src/nix/ls.cc
index b9716a6a1..aac082422 100644
--- a/src/nix/ls.cc
+++ b/src/nix/ls.cc
@@ -85,7 +85,7 @@ struct CmdLsStore : StoreCommand, MixLs
{
CmdLsStore()
{
- expectArg("path", &path);
+ expectPathArg("path", &path);
}
Examples examples() override
@@ -117,7 +117,7 @@ struct CmdLsNar : Command, MixLs
CmdLsNar()
{
- expectArg("nar", &narPath);
+ expectPathArg("nar", &narPath);
expectArg("path", &path);
}
diff --git a/src/nix/main.cc b/src/nix/main.cc
index c491bc264..fffdeab90 100644
--- a/src/nix/main.cc
+++ b/src/nix/main.cc
@@ -68,7 +68,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
addFlag({
.longName = "help",
.description = "show usage information",
- .handler = {[&]() { showHelpAndExit(); }},
+ .handler = {[&]() { if (!completions) showHelpAndExit(); }},
});
addFlag({
@@ -96,7 +96,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
addFlag({
.longName = "version",
.description = "show version information",
- .handler = {[&]() { printVersion(programName); }},
+ .handler = {[&]() { if (!completions) printVersion(programName); }},
});
addFlag({
@@ -169,6 +169,7 @@ void mainWrapped(int argc, char * * argv)
Finally printCompletions([&]()
{
if (completions) {
+ std::cout << (pathCompletions ? "filenames\n" : "no-filenames\n");
for (auto & s : *completions)
std::cout << s << "\n";
}
diff --git a/src/nix/repl.cc b/src/nix/repl.cc
index 0a6a7ab19..2e7b14d08 100644
--- a/src/nix/repl.cc
+++ b/src/nix/repl.cc
@@ -767,7 +767,7 @@ struct CmdRepl : StoreCommand, MixEvalArgs
CmdRepl()
{
- expectArgs("files", &files);
+ expectPathArgs("files", &files);
}
std::string description() override
diff --git a/src/nix/run.cc b/src/nix/run.cc
index 3e2c8b4f3..3701ffe3d 100644
--- a/src/nix/run.cc
+++ b/src/nix/run.cc
@@ -149,7 +149,7 @@ struct CmdRun : InstallableCommand, RunCommon
CmdRun()
{
- expectArgs("args", &args);
+ expectPathArgs("args", &args);
}
std::string description() override
diff --git a/src/nix/sigs.cc b/src/nix/sigs.cc
index 6c9b9a792..7821a5432 100644
--- a/src/nix/sigs.cc
+++ b/src/nix/sigs.cc
@@ -105,7 +105,8 @@ struct CmdSignPaths : StorePathsCommand
.shortName = 'k',
.description = "file containing the secret signing key",
.labels = {"file"},
- .handler = {&secretKeyFile}
+ .handler = {&secretKeyFile},
+ .completer = completePath
});
}