diff options
-rw-r--r-- | src/libutil/args.cc | 90 | ||||
-rw-r--r-- | src/libutil/args.hh | 123 | ||||
-rw-r--r-- | src/nix/cat.cc | 12 | ||||
-rw-r--r-- | src/nix/command.hh | 5 | ||||
-rw-r--r-- | src/nix/hash.cc | 6 | ||||
-rw-r--r-- | src/nix/installables.cc | 8 | ||||
-rw-r--r-- | src/nix/ls.cc | 12 | ||||
-rw-r--r-- | src/nix/repl.cc | 6 | ||||
-rw-r--r-- | src/nix/run.cc | 6 |
9 files changed, 145 insertions, 123 deletions
diff --git a/src/libutil/args.cc b/src/libutil/args.cc index 257b4b6c4..c389090ae 100644 --- a/src/libutil/args.cc +++ b/src/libutil/args.cc @@ -86,7 +86,7 @@ void Args::printHelp(const string & programName, std::ostream & out) for (auto & exp : expectedArgs) { std::cout << renderLabels({exp.label}); // FIXME: handle arity > 1 - if (exp.arity == 0) std::cout << "..."; + if (exp.handler.arity == ArityAny) std::cout << "..."; if (exp.optional) std::cout << "?"; } std::cout << "\n"; @@ -127,8 +127,8 @@ 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 (auto prefix = needsCompletion(*pos)) - if (flag.completer) + if (flag.completer) + if (auto prefix = needsCompletion(*pos)) flag.completer(n, *prefix); args.push_back(*pos++); } @@ -179,12 +179,17 @@ bool Args::processArgs(const Strings & args, bool finish) bool res = false; - if ((exp.arity == 0 && finish) || - (exp.arity > 0 && args.size() == exp.arity)) + if ((exp.handler.arity == ArityAny && finish) || + (exp.handler.arity != ArityAny && args.size() == exp.handler.arity)) { std::vector<std::string> ss; - for (auto & s : args) ss.push_back(s); - exp.handler(std::move(ss)); + for (const auto &[n, s] : enumerate(args)) { + ss.push_back(s); + if (exp.completer) + if (auto prefix = needsCompletion(s)) + exp.completer(n, *prefix); + } + exp.handler.fun(ss); expectedArgs.pop_front(); res = true; } @@ -214,46 +219,17 @@ Args::Flag Args::Flag::mkHashTypeFlag(std::string && longName, HashType * ht) }; } -void completePath(size_t, std::string_view s) +void completePath(size_t, std::string_view prefix) { - 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); - } + pathCompletions = true; + glob_t globbuf; + if (glob((std::string(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; @@ -301,18 +277,22 @@ void Command::printHelp(const string & programName, std::ostream & out) MultiCommand::MultiCommand(const Commands & commands) : commands(commands) { - expectedArgs.push_back(ExpectedArg{"command", 1, true, [=](std::vector<std::string> ss) { - assert(!command); - if (auto prefix = needsCompletion(ss[0])) { - for (auto & [name, command] : commands) - if (hasPrefix(name, *prefix)) - completions->insert(name); - } - auto i = commands.find(ss[0]); - if (i == commands.end()) - throw UsageError("'%s' is not a recognised command", ss[0]); - command = {ss[0], i->second()}; - }}); + expectArgs({ + .label = "command", + .optional = true, + .handler = {[=](std::string s) { + assert(!command); + if (auto prefix = needsCompletion(s)) { + for (auto & [name, command] : commands) + if (hasPrefix(name, *prefix)) + completions->insert(name); + } + auto i = commands.find(s); + if (i == commands.end()) + throw UsageError("'%s' is not a recognised command", s); + command = {s, i->second()}; + }} + }); categories[Command::catDefault] = "Available commands"; } diff --git a/src/libutil/args.hh b/src/libutil/args.hh index f93459c96..250244162 100644 --- a/src/libutil/args.hh +++ b/src/libutil/args.hh @@ -28,55 +28,60 @@ protected: static const size_t ArityAny = std::numeric_limits<size_t>::max(); + struct Handler + { + std::function<void(std::vector<std::string>)> fun; + size_t arity; + + Handler() {} + + Handler(std::function<void(std::vector<std::string>)> && fun) + : fun(std::move(fun)) + , arity(ArityAny) + { } + + Handler(std::function<void()> && handler) + : fun([handler{std::move(handler)}](std::vector<std::string>) { handler(); }) + , arity(0) + { } + + Handler(std::function<void(std::string)> && handler) + : fun([handler{std::move(handler)}](std::vector<std::string> ss) { + handler(std::move(ss[0])); + }) + , arity(1) + { } + + Handler(std::function<void(std::string, std::string)> && handler) + : fun([handler{std::move(handler)}](std::vector<std::string> ss) { + handler(std::move(ss[0]), std::move(ss[1])); + }) + , arity(2) + { } + + Handler(std::vector<std::string> * dest) + : fun([=](std::vector<std::string> ss) { *dest = ss; }) + , arity(ArityAny) + { } + + template<class T> + Handler(T * dest) + : fun([=](std::vector<std::string> ss) { *dest = ss[0]; }) + , arity(1) + { } + + template<class T> + Handler(T * dest, const T & val) + : fun([=](std::vector<std::string> ss) { *dest = val; }) + , arity(0) + { } + }; + /* Flags. */ struct Flag { typedef std::shared_ptr<Flag> ptr; - struct Handler - { - std::function<void(std::vector<std::string>)> fun; - size_t arity; - - Handler() {} - - Handler(std::function<void(std::vector<std::string>)> && fun) - : fun(std::move(fun)) - , arity(ArityAny) - { } - - Handler(std::function<void()> && handler) - : fun([handler{std::move(handler)}](std::vector<std::string>) { handler(); }) - , arity(0) - { } - - Handler(std::function<void(std::string)> && handler) - : fun([handler{std::move(handler)}](std::vector<std::string> ss) { - handler(std::move(ss[0])); - }) - , arity(1) - { } - - Handler(std::function<void(std::string, std::string)> && handler) - : fun([handler{std::move(handler)}](std::vector<std::string> ss) { - handler(std::move(ss[0]), std::move(ss[1])); - }) - , arity(2) - { } - - template<class T> - Handler(T * dest) - : fun([=](std::vector<std::string> ss) { *dest = ss[0]; }) - , arity(1) - { } - - template<class T> - Handler(T * dest, const T & val) - : fun([=](std::vector<std::string> ss) { *dest = val; }) - , arity(0) - { } - }; - std::string longName; char shortName = 0; std::string description; @@ -99,9 +104,9 @@ protected: struct ExpectedArg { std::string label; - size_t arity = 0; // 0 = any bool optional = false; - std::function<void(std::vector<std::string>)> handler; + Handler handler; + std::function<void(size_t, std::string_view)> completer; }; std::list<ExpectedArg> expectedArgs; @@ -175,26 +180,30 @@ public: }); } + void expectArgs(ExpectedArg && arg) + { + expectedArgs.emplace_back(std::move(arg)); + } + /* Expect a string argument. */ void expectArg(const std::string & label, string * dest, bool optional = false) { - expectedArgs.push_back(ExpectedArg{label, 1, optional, [=](std::vector<std::string> ss) { - *dest = ss[0]; - }}); + expectArgs({ + .label = label, + .optional = true, + .handler = {dest} + }); } - 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) { - expectedArgs.push_back(ExpectedArg{label, 0, false, [=](std::vector<std::string> ss) { - *dest = std::move(ss); - }}); + expectArgs({ + .label = label, + .handler = {dest} + }); } - void expectPathArgs(const std::string & label, std::vector<std::string> * dest); - friend class MultiCommand; }; @@ -266,6 +275,6 @@ extern bool pathCompletions; std::optional<std::string> needsCompletion(std::string_view s); -void completePath(size_t, std::string_view s); +void completePath(size_t, std::string_view prefix); } diff --git a/src/nix/cat.cc b/src/nix/cat.cc index eeee1e529..b528a0507 100644 --- a/src/nix/cat.cc +++ b/src/nix/cat.cc @@ -25,7 +25,11 @@ struct CmdCatStore : StoreCommand, MixCat { CmdCatStore() { - expectPathArg("path", &path); + expectArgs({ + .label = "path", + .handler = {&path}, + .completer = completePath + }); } std::string description() override @@ -47,7 +51,11 @@ struct CmdCatNar : StoreCommand, MixCat CmdCatNar() { - expectPathArg("nar", &narPath); + expectArgs({ + .label = "nar", + .handler = {&narPath}, + .completer = completePath + }); expectArg("path", &path); } diff --git a/src/nix/command.hh b/src/nix/command.hh index 6b4781303..0738c0f91 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -73,10 +73,7 @@ struct InstallablesCommand : virtual Args, SourceExprCommand { std::vector<std::shared_ptr<Installable>> installables; - InstallablesCommand() - { - expectArgs("installables", &_installables); - } + InstallablesCommand(); void prepare() override; diff --git a/src/nix/hash.cc b/src/nix/hash.cc index 0f460c668..d5636eb47 100644 --- a/src/nix/hash.cc +++ b/src/nix/hash.cc @@ -31,7 +31,11 @@ struct CmdHash : Command .labels({"modulus"}) .dest(&modulus); #endif - expectPathArgs("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 c144a7e70..abc642e11 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -571,6 +571,14 @@ StorePathSet toDerivations(ref<Store> store, return drvPaths; } +InstallablesCommand::InstallablesCommand() +{ + expectArgs({ + .label = "installables", + .handler = {&_installables}, + }); +} + void InstallablesCommand::prepare() { if (_installables.empty() && useDefaultInstallables()) diff --git a/src/nix/ls.cc b/src/nix/ls.cc index aac082422..dc7e370b9 100644 --- a/src/nix/ls.cc +++ b/src/nix/ls.cc @@ -85,7 +85,11 @@ struct CmdLsStore : StoreCommand, MixLs { CmdLsStore() { - expectPathArg("path", &path); + expectArgs({ + .label = "path", + .handler = {&path}, + .completer = completePath + }); } Examples examples() override @@ -117,7 +121,11 @@ struct CmdLsNar : Command, MixLs CmdLsNar() { - expectPathArg("nar", &narPath); + expectArgs({ + .label = "nar", + .handler = {&narPath}, + .completer = completePath + }); expectArg("path", &path); } diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 2e7b14d08..c936f9cc2 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -767,7 +767,11 @@ struct CmdRepl : StoreCommand, MixEvalArgs CmdRepl() { - expectPathArgs("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 3701ffe3d..f9b1298f1 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -149,7 +149,11 @@ struct CmdRun : InstallableCommand, RunCommon CmdRun() { - expectPathArgs("args", &args); + expectArgs({ + .label = "args", + .handler = {&args}, + .completer = completePath + }); } std::string description() override |