aboutsummaryrefslogtreecommitdiff
path: root/src/nix
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2020-05-12 18:26:13 +0200
committerGitHub <noreply@github.com>2020-05-12 18:26:13 +0200
commit215f09d7656b394f8bcf45c9aa3a4c2b0287469d (patch)
tree777d0d22fe0b66882efb0784b2e95b7eb9c3d145 /src/nix
parentfbade0b7cc123f9e952115e14456f77fdd4fedf1 (diff)
parentb8b2dbf27204bc6ce5f57fa5fc5c76f9265fcee1 (diff)
Merge pull request #3587 from NixOS/bash-completion
Generic shell completion support for the 'nix' command
Diffstat (limited to 'src/nix')
-rw-r--r--src/nix/build.cc1
-rw-r--r--src/nix/cat.cc12
-rw-r--r--src/nix/command.cc1
-rw-r--r--src/nix/command.hh14
-rw-r--r--src/nix/flake.cc9
-rw-r--r--src/nix/hash.cc6
-rw-r--r--src/nix/installables.cc120
-rw-r--r--src/nix/ls.cc12
-rw-r--r--src/nix/main.cc21
-rw-r--r--src/nix/repl.cc6
-rw-r--r--src/nix/run.cc6
-rw-r--r--src/nix/sigs.cc3
12 files changed, 190 insertions, 21 deletions
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..b528a0507 100644
--- a/src/nix/cat.cc
+++ b/src/nix/cat.cc
@@ -25,7 +25,11 @@ struct CmdCatStore : StoreCommand, MixCat
{
CmdCatStore()
{
- expectArg("path", &path);
+ expectArgs({
+ .label = "path",
+ .handler = {&path},
+ .completer = completePath
+ });
}
std::string description() override
@@ -47,7 +51,11 @@ struct CmdCatNar : StoreCommand, MixCat
CmdCatNar()
{
- expectArg("nar", &narPath);
+ expectArgs({
+ .label = "nar",
+ .handler = {&narPath},
+ .completer = completePath
+ });
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/command.hh b/src/nix/command.hh
index 6b4781303..faa19c8ea 100644
--- a/src/nix/command.hh
+++ b/src/nix/command.hh
@@ -38,6 +38,8 @@ struct EvalCommand : virtual StoreCommand, MixEvalArgs
ref<EvalState> getEvalState();
std::shared_ptr<EvalState> evalState;
+
+ void completeFlakeRef(std::string_view prefix);
};
struct MixFlakeOptions : virtual Args
@@ -63,6 +65,8 @@ struct SourceExprCommand : virtual Args, EvalCommand, MixFlakeOptions
virtual Strings getDefaultFlakeAttrPaths();
virtual Strings getDefaultFlakeAttrPathPrefixes();
+
+ void completeInstallable(std::string_view prefix);
};
enum RealiseMode { Build, NoBuild, DryRun };
@@ -73,10 +77,7 @@ struct InstallablesCommand : virtual Args, SourceExprCommand
{
std::vector<std::shared_ptr<Installable>> installables;
- InstallablesCommand()
- {
- expectArgs("installables", &_installables);
- }
+ InstallablesCommand();
void prepare() override;
@@ -92,10 +93,7 @@ struct InstallableCommand : virtual Args, SourceExprCommand
{
std::shared_ptr<Installable> installable;
- InstallableCommand()
- {
- expectArg("installable", &_installable, true);
- }
+ InstallableCommand();
void prepare() override;
diff --git a/src/nix/flake.cc b/src/nix/flake.cc
index 6eee781aa..b6cc7eb54 100644
--- a/src/nix/flake.cc
+++ b/src/nix/flake.cc
@@ -28,7 +28,14 @@ public:
FlakeCommand()
{
- expectArg("flake-url", &flakeUrl, true);
+ expectArgs({
+ .label = "flake-url",
+ .optional = true,
+ .handler = {&flakeUrl},
+ .completer = {[&](size_t, std::string_view prefix) {
+ completeFlakeRef(prefix);
+ }}
+ });
}
FlakeRef getFlakeRef()
diff --git a/src/nix/hash.cc b/src/nix/hash.cc
index 366314227..d5636eb47 100644
--- a/src/nix/hash.cc
+++ b/src/nix/hash.cc
@@ -31,7 +31,11 @@ struct CmdHash : Command
.labels({"modulus"})
.dest(&modulus);
#endif
- expectArgs("paths", &paths);
+ expectArgs({
+ .label = "paths",
+ .handler = {&paths},
+ .completer = completePath
+ });
}
std::string description() override
diff --git a/src/nix/installables.cc b/src/nix/installables.cc
index a06022f8c..cae85b34e 100644
--- a/src/nix/installables.cc
+++ b/src/nix/installables.cc
@@ -11,6 +11,7 @@
#include "flake/flake.hh"
#include "eval-cache.hh"
#include "url.hh"
+#include "registry.hh"
#include <regex>
#include <queue>
@@ -77,7 +78,8 @@ SourceExprCommand::SourceExprCommand()
.shortName = 'f',
.description = "evaluate FILE rather than the default",
.labels = {"file"},
- .handler = {&file}
+ .handler = {&file},
+ .completer = completePath
});
addFlag({
@@ -105,6 +107,76 @@ Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes()
};
}
+void SourceExprCommand::completeInstallable(std::string_view prefix)
+{
+ if (file) return; // FIXME
+
+ /* Look for flake output attributes that match the
+ prefix. */
+ try {
+ auto hash = prefix.find('#');
+ if (hash != std::string::npos) {
+ auto fragment = prefix.substr(hash + 1);
+ auto flakeRefS = std::string(prefix.substr(0, hash));
+ // FIXME: do tilde expansion.
+ auto flakeRef = parseFlakeRef(flakeRefS, absPath("."));
+
+ auto state = getEvalState();
+
+ auto evalCache = openEvalCache(*state,
+ std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlags)),
+ true);
+
+ auto root = evalCache->getRoot();
+
+ /* Complete 'fragment' relative to all the
+ attrpath prefixes as well as the root of the
+ flake. */
+ auto attrPathPrefixes = getDefaultFlakeAttrPathPrefixes();
+ attrPathPrefixes.push_back("");
+
+ for (auto & attrPathPrefixS : attrPathPrefixes) {
+ auto attrPathPrefix = parseAttrPath(*state, attrPathPrefixS);
+ auto attrPathS = attrPathPrefixS + std::string(fragment);
+ auto attrPath = parseAttrPath(*state, attrPathS);
+
+ std::string lastAttr;
+ if (!attrPath.empty() && !hasSuffix(attrPathS, ".")) {
+ lastAttr = attrPath.back();
+ attrPath.pop_back();
+ }
+
+ auto attr = root->findAlongAttrPath(attrPath);
+ if (!attr) continue;
+
+ auto attrs = attr->getAttrs();
+ for (auto & attr2 : attrs) {
+ if (hasPrefix(attr2, lastAttr)) {
+ auto attrPath2 = attr->getAttrPath(attr2);
+ /* Strip the attrpath prefix. */
+ attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size());
+ completions->insert(flakeRefS + "#" + concatStringsSep(".", attrPath2));
+ }
+ }
+ }
+
+ /* And add an empty completion for the default
+ attrpaths. */
+ if (fragment.empty()) {
+ for (auto & attrPath : getDefaultFlakeAttrPaths()) {
+ auto attr = root->findAlongAttrPath(parseAttrPath(*state, attrPath));
+ if (!attr) continue;
+ completions->insert(flakeRefS + "#");
+ }
+ }
+ }
+ } catch (Error & e) {
+ warn(e.msg());
+ }
+
+ completeFlakeRef(prefix);
+}
+
ref<EvalState> EvalCommand::getEvalState()
{
if (!evalState)
@@ -112,6 +184,29 @@ ref<EvalState> EvalCommand::getEvalState()
return ref<EvalState>(evalState);
}
+void EvalCommand::completeFlakeRef(std::string_view prefix)
+{
+ if (prefix == "")
+ completions->insert(".");
+
+ completeDir(0, prefix);
+
+ /* Look for registry entries that match the prefix. */
+ for (auto & registry : fetchers::getRegistries(getStore())) {
+ for (auto & entry : registry->entries) {
+ auto from = entry.from->to_string();
+ if (!hasPrefix(prefix, "flake:") && hasPrefix(from, "flake:")) {
+ std::string from2(from, 6);
+ if (hasPrefix(from2, prefix))
+ completions->insert(from2);
+ } else {
+ if (hasPrefix(from, prefix))
+ completions->insert(from);
+ }
+ }
+ }
+}
+
Buildable Installable::toBuildable()
{
auto buildables = toBuildables();
@@ -551,6 +646,17 @@ StorePathSet toDerivations(ref<Store> store,
return drvPaths;
}
+InstallablesCommand::InstallablesCommand()
+{
+ expectArgs({
+ .label = "installables",
+ .handler = {&_installables},
+ .completer = {[&](size_t, std::string_view prefix) {
+ completeInstallable(prefix);
+ }}
+ });
+}
+
void InstallablesCommand::prepare()
{
if (_installables.empty() && useDefaultInstallables())
@@ -560,6 +666,18 @@ void InstallablesCommand::prepare()
installables = parseInstallables(getStore(), _installables);
}
+InstallableCommand::InstallableCommand()
+{
+ expectArgs({
+ .label = "installable",
+ .optional = true,
+ .handler = {&_installable},
+ .completer = {[&](size_t, std::string_view prefix) {
+ completeInstallable(prefix);
+ }}
+ });
+}
+
void InstallableCommand::prepare()
{
installable = parseInstallable(getStore(), _installable);
diff --git a/src/nix/ls.cc b/src/nix/ls.cc
index b9716a6a1..dc7e370b9 100644
--- a/src/nix/ls.cc
+++ b/src/nix/ls.cc
@@ -85,7 +85,11 @@ struct CmdLsStore : StoreCommand, MixLs
{
CmdLsStore()
{
- expectArg("path", &path);
+ expectArgs({
+ .label = "path",
+ .handler = {&path},
+ .completer = completePath
+ });
}
Examples examples() override
@@ -117,7 +121,11 @@ struct CmdLsNar : Command, MixLs
CmdLsNar()
{
- expectArg("nar", &narPath);
+ expectArgs({
+ .label = "nar",
+ .handler = {&narPath},
+ .completer = completePath
+ });
expectArg("path", &path);
}
diff --git a/src/nix/main.cc b/src/nix/main.cc
index 3915a4896..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({
@@ -166,7 +166,22 @@ void mainWrapped(int argc, char * * argv)
NixArgs args;
- args.parseCmdline(argvToStrings(argc, argv));
+ Finally printCompletions([&]()
+ {
+ if (completions) {
+ std::cout << (pathCompletions ? "filenames\n" : "no-filenames\n");
+ for (auto & s : *completions)
+ std::cout << s << "\n";
+ }
+ });
+
+ try {
+ args.parseCmdline(argvToStrings(argc, argv));
+ } catch (UsageError &) {
+ if (!completions) throw;
+ }
+
+ if (completions) return;
settings.requireExperimentalFeature("nix-command");
diff --git a/src/nix/repl.cc b/src/nix/repl.cc
index 0a6a7ab19..c936f9cc2 100644
--- a/src/nix/repl.cc
+++ b/src/nix/repl.cc
@@ -767,7 +767,11 @@ struct CmdRepl : StoreCommand, MixEvalArgs
CmdRepl()
{
- expectArgs("files", &files);
+ expectArgs({
+ .label = "files",
+ .handler = {&files},
+ .completer = completePath
+ });
}
std::string description() override
diff --git a/src/nix/run.cc b/src/nix/run.cc
index 3e2c8b4f3..f9b1298f1 100644
--- a/src/nix/run.cc
+++ b/src/nix/run.cc
@@ -149,7 +149,11 @@ struct CmdRun : InstallableCommand, RunCommon
CmdRun()
{
- expectArgs("args", &args);
+ expectArgs({
+ .label = "args",
+ .handler = {&args},
+ .completer = completePath
+ });
}
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
});
}