Add completion for paths

This commit is contained in:
Eelco Dolstra 2020-05-10 21:35:07 +02:00
parent 91ddee6bf0
commit e0c19ee620
13 changed files with 82 additions and 15 deletions

View File

@ -1,6 +1,14 @@
function _complete_nix { function _complete_nix {
local have_type
while IFS= read -r line; do 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[@]}") done < <(NIX_GET_COMPLETIONS=$COMP_CWORD "${COMP_WORDS[@]}")
} }

View File

@ -1,6 +1,8 @@
#include "args.hh" #include "args.hh"
#include "hash.hh" #include "hash.hh"
#include <glob.h>
namespace nix { namespace nix {
void Args::addFlag(Flag && flag_) void Args::addFlag(Flag && flag_)
@ -13,6 +15,7 @@ void Args::addFlag(Flag && flag_)
if (flag->shortName) shortFlags[flag->shortName] = flag; if (flag->shortName) shortFlags[flag->shortName] = flag;
} }
bool pathCompletions = false;
std::shared_ptr<std::set<std::string>> completions; std::shared_ptr<std::set<std::string>> completions;
std::string completionMarker = "___COMPLETE___"; std::string completionMarker = "___COMPLETE___";
@ -128,8 +131,11 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
else else
throw UsageError("flag '%s' requires %d argument(s)", name, flag.handler.arity); throw UsageError("flag '%s' requires %d argument(s)", name, flag.handler.arity);
} else { } else {
if (needsCompletion(*pos)) if (needsCompletion(*pos)) {
if (flag.completer)
flag.completer(n, *pos);
anyNeedsCompletion = true; anyNeedsCompletion = true;
}
args.push_back(*pos++); 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 argvToStrings(int argc, char * * argv)
{ {
Strings args; Strings args;

View File

@ -83,6 +83,7 @@ protected:
std::string category; std::string category;
Strings labels; Strings labels;
Handler handler; Handler handler;
std::function<void(size_t, std::string_view)> completer;
static Flag mkHashTypeFlag(std::string && longName, HashType * ht); static Flag mkHashTypeFlag(std::string && longName, HashType * ht);
}; };
@ -98,8 +99,8 @@ protected:
struct ExpectedArg struct ExpectedArg
{ {
std::string label; std::string label;
size_t arity; // 0 = any size_t arity = 0; // 0 = any
bool optional; bool optional = false;
std::function<void(std::vector<std::string>)> handler; 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. */ /* Expect 0 or more arguments. */
void expectArgs(const std::string & label, std::vector<std::string> * dest) 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; 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); void printTable(std::ostream & out, const Table2 & table);
extern std::shared_ptr<std::set<std::string>> completions; extern std::shared_ptr<std::set<std::string>> completions;
extern bool pathCompletions;
std::optional<std::string> needsCompletion(std::string_view s); std::optional<std::string> needsCompletion(std::string_view s);
void completePath(size_t, std::string_view s);
} }

View File

@ -18,6 +18,7 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixProfile
.description = "path of the symlink to the build result", .description = "path of the symlink to the build result",
.labels = {"path"}, .labels = {"path"},
.handler = {&outLink}, .handler = {&outLink},
.completer = completePath
}); });
addFlag({ addFlag({

View File

@ -25,7 +25,7 @@ struct CmdCatStore : StoreCommand, MixCat
{ {
CmdCatStore() CmdCatStore()
{ {
expectArg("path", &path); expectPathArg("path", &path);
} }
std::string description() override std::string description() override
@ -47,7 +47,7 @@ struct CmdCatNar : StoreCommand, MixCat
CmdCatNar() CmdCatNar()
{ {
expectArg("nar", &narPath); expectPathArg("nar", &narPath);
expectArg("path", &path); expectArg("path", &path);
} }

View File

@ -108,6 +108,7 @@ MixProfile::MixProfile()
.description = "profile to update", .description = "profile to update",
.labels = {"path"}, .labels = {"path"},
.handler = {&profile}, .handler = {&profile},
.completer = completePath
}); });
} }

View File

@ -31,7 +31,7 @@ struct CmdHash : Command
.labels({"modulus"}) .labels({"modulus"})
.dest(&modulus); .dest(&modulus);
#endif #endif
expectArgs("paths", &paths); expectPathArgs("paths", &paths);
} }
std::string description() override std::string description() override

View File

@ -77,7 +77,8 @@ SourceExprCommand::SourceExprCommand()
.shortName = 'f', .shortName = 'f',
.description = "evaluate FILE rather than the default", .description = "evaluate FILE rather than the default",
.labels = {"file"}, .labels = {"file"},
.handler = {&file} .handler = {&file},
.completer = completePath
}); });
addFlag({ addFlag({

View File

@ -85,7 +85,7 @@ struct CmdLsStore : StoreCommand, MixLs
{ {
CmdLsStore() CmdLsStore()
{ {
expectArg("path", &path); expectPathArg("path", &path);
} }
Examples examples() override Examples examples() override
@ -117,7 +117,7 @@ struct CmdLsNar : Command, MixLs
CmdLsNar() CmdLsNar()
{ {
expectArg("nar", &narPath); expectPathArg("nar", &narPath);
expectArg("path", &path); expectArg("path", &path);
} }

View File

@ -68,7 +68,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
addFlag({ addFlag({
.longName = "help", .longName = "help",
.description = "show usage information", .description = "show usage information",
.handler = {[&]() { showHelpAndExit(); }}, .handler = {[&]() { if (!completions) showHelpAndExit(); }},
}); });
addFlag({ addFlag({
@ -96,7 +96,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
addFlag({ addFlag({
.longName = "version", .longName = "version",
.description = "show version information", .description = "show version information",
.handler = {[&]() { printVersion(programName); }}, .handler = {[&]() { if (!completions) printVersion(programName); }},
}); });
addFlag({ addFlag({
@ -169,6 +169,7 @@ void mainWrapped(int argc, char * * argv)
Finally printCompletions([&]() Finally printCompletions([&]()
{ {
if (completions) { if (completions) {
std::cout << (pathCompletions ? "filenames\n" : "no-filenames\n");
for (auto & s : *completions) for (auto & s : *completions)
std::cout << s << "\n"; std::cout << s << "\n";
} }

View File

@ -767,7 +767,7 @@ struct CmdRepl : StoreCommand, MixEvalArgs
CmdRepl() CmdRepl()
{ {
expectArgs("files", &files); expectPathArgs("files", &files);
} }
std::string description() override std::string description() override

View File

@ -149,7 +149,7 @@ struct CmdRun : InstallableCommand, RunCommon
CmdRun() CmdRun()
{ {
expectArgs("args", &args); expectPathArgs("args", &args);
} }
std::string description() override std::string description() override

View File

@ -105,7 +105,8 @@ struct CmdSignPaths : StorePathsCommand
.shortName = 'k', .shortName = 'k',
.description = "file containing the secret signing key", .description = "file containing the secret signing key",
.labels = {"file"}, .labels = {"file"},
.handler = {&secretKeyFile} .handler = {&secretKeyFile},
.completer = completePath
}); });
} }