forked from lix-project/lix
Add completion for paths
This commit is contained in:
parent
91ddee6bf0
commit
e0c19ee620
|
@ -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[@]}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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({
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,6 +108,7 @@ MixProfile::MixProfile()
|
||||||
.description = "profile to update",
|
.description = "profile to update",
|
||||||
.labels = {"path"},
|
.labels = {"path"},
|
||||||
.handler = {&profile},
|
.handler = {&profile},
|
||||||
|
.completer = completePath
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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({
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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";
|
||||||
}
|
}
|
||||||
|
|
|
@ -767,7 +767,7 @@ struct CmdRepl : StoreCommand, MixEvalArgs
|
||||||
|
|
||||||
CmdRepl()
|
CmdRepl()
|
||||||
{
|
{
|
||||||
expectArgs("files", &files);
|
expectPathArgs("files", &files);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string description() override
|
std::string description() override
|
||||||
|
|
|
@ -149,7 +149,7 @@ struct CmdRun : InstallableCommand, RunCommon
|
||||||
|
|
||||||
CmdRun()
|
CmdRun()
|
||||||
{
|
{
|
||||||
expectArgs("args", &args);
|
expectPathArgs("args", &args);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string description() override
|
std::string description() override
|
||||||
|
|
|
@ -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
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue