Nix/src/libutil/args.hh

427 lines
11 KiB
C++
Raw Normal View History

#pragma once
///@file
#include <iostream>
#include <functional>
#include <map>
#include <memory>
#include <optional>
#include <nlohmann/json_fwd.hpp>
#include "types.hh"
#include "experimental-features.hh"
namespace nix {
enum struct HashAlgorithm : char;
enum struct HashFormat : int;
class MultiCommand;
Overhaul completions, redo #6693 (#8131) As I complained in https://github.com/NixOS/nix/pull/6784#issuecomment-1421777030 (a comment on the wrong PR, sorry again!), #6693 introduced a second completions mechanism to fix a bug. Having two completion mechanisms isn't so nice. As @thufschmitt also pointed out, it was a bummer to go from `FlakeRef` to `std::string` when collecting flake refs. Now it is `FlakeRefs` again. The underlying issue that sought to work around was that completion of arguments not at the end can still benefit from the information from latter arguments. To fix this better, we rip out that change and simply defer all completion processing until after all the (regular, already-complete) arguments have been passed. In addition, I noticed the original completion logic used some global variables. I do not like global variables, because even if they save lines of code, they also obfuscate the architecture of the code. I got rid of them moved them to a new `RootArgs` class, which now has `parseCmdline` instead of `Args`. The idea is that we have many argument parsers from subcommands and what-not, but only one root args that owns the other per actual parsing invocation. The state that was global is now part of the root args instead. This did, admittedly, add a bunch of new code. And I do feel bad about that. So I went and added a lot of API docs to try to at least make the current state of things clear to the next person. -- This is needed for RFC 134 (tracking issue #7868). It was very hard to modularize `Installable` parsing when there were two completion arguments. I wouldn't go as far as to say it is *easy* now, but at least it is less hard (and the completions test finally passed). Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2023-10-23 15:03:11 +02:00
class RootArgs;
class AddCompletions;
class Args
{
public:
/**
* Return a short one-line description of the command.
*/
virtual std::string description() { return ""; }
virtual bool forceImpureByDefault() { return false; }
/**
* Return documentation about this command, in Markdown format.
*/
2020-12-07 13:04:24 +01:00
virtual std::string doc() { return ""; }
/**
* @brief Get the base directory for the command.
*
* @return Generally the working directory, but in case of a shebang
* interpreter, returns the directory of the script.
*
* This only returns the correct value after parseCmdline() has run.
*/
virtual Path getCommandBaseDir() const;
protected:
/**
* The largest `size_t` is used to indicate the "any" arity, for
* handlers/flags/arguments that accept an arbitrary number of
* arguments.
*/
static const size_t ArityAny = std::numeric_limits<size_t>::max();
/**
* Arguments (flags/options and positional) have a "handler" which is
* caused when the argument is parsed. The handler has an arbitrary side
* effect, including possible affect further command-line parsing.
*
* There are many constructors in order to support many shorthand
* initializations, and this is used a lot.
*/
2020-05-11 15:46:18 +02:00
struct Handler
{
std::function<void(std::vector<std::string>)> fun;
size_t arity;
2023-10-23 15:56:30 +02:00
Handler() = default;
2020-05-11 15:46:18 +02:00
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([dest](std::vector<std::string> ss) { *dest = ss; })
2020-05-11 15:46:18 +02:00
, arity(ArityAny)
{ }
2021-01-08 10:44:55 +01:00
Handler(std::string * dest)
: fun([dest](std::vector<std::string> ss) { *dest = ss[0]; })
2021-01-08 10:44:55 +01:00
, arity(1)
{ }
Handler(std::optional<std::string> * dest)
: fun([dest](std::vector<std::string> ss) { *dest = ss[0]; })
2020-05-11 15:46:18 +02:00
, arity(1)
{ }
template<class T>
Handler(T * dest, const T & val)
: fun([dest, val](std::vector<std::string> ss) { *dest = val; })
2020-05-11 15:46:18 +02:00
, arity(0)
{ }
2021-01-08 10:44:55 +01:00
template<class I>
Handler(I * dest)
: fun([dest](std::vector<std::string> ss) {
*dest = string2IntWithUnitPrefix<I>(ss[0]);
2021-01-08 10:44:55 +01:00
})
, arity(1)
{ }
2021-09-14 19:05:28 +02:00
template<class I>
Handler(std::optional<I> * dest)
: fun([dest](std::vector<std::string> ss) {
2021-09-14 19:05:28 +02:00
*dest = string2IntWithUnitPrefix<I>(ss[0]);
})
, arity(1)
{ }
2020-05-11 15:46:18 +02:00
};
Overhaul completions, redo #6693 (#8131) As I complained in https://github.com/NixOS/nix/pull/6784#issuecomment-1421777030 (a comment on the wrong PR, sorry again!), #6693 introduced a second completions mechanism to fix a bug. Having two completion mechanisms isn't so nice. As @thufschmitt also pointed out, it was a bummer to go from `FlakeRef` to `std::string` when collecting flake refs. Now it is `FlakeRefs` again. The underlying issue that sought to work around was that completion of arguments not at the end can still benefit from the information from latter arguments. To fix this better, we rip out that change and simply defer all completion processing until after all the (regular, already-complete) arguments have been passed. In addition, I noticed the original completion logic used some global variables. I do not like global variables, because even if they save lines of code, they also obfuscate the architecture of the code. I got rid of them moved them to a new `RootArgs` class, which now has `parseCmdline` instead of `Args`. The idea is that we have many argument parsers from subcommands and what-not, but only one root args that owns the other per actual parsing invocation. The state that was global is now part of the root args instead. This did, admittedly, add a bunch of new code. And I do feel bad about that. So I went and added a lot of API docs to try to at least make the current state of things clear to the next person. -- This is needed for RFC 134 (tracking issue #7868). It was very hard to modularize `Installable` parsing when there were two completion arguments. I wouldn't go as far as to say it is *easy* now, but at least it is less hard (and the completions test finally passed). Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2023-10-23 15:03:11 +02:00
/**
* The basic function type of the completion callback.
*
* Used to define `CompleterClosure` and some common case completers
* that individual flags/arguments can use.
*
* The `AddCompletions` that is passed is an interface to the state
* stored as part of the root command
*/
using CompleterFun = void(AddCompletions &, size_t, std::string_view);
Overhaul completions, redo #6693 (#8131) As I complained in https://github.com/NixOS/nix/pull/6784#issuecomment-1421777030 (a comment on the wrong PR, sorry again!), #6693 introduced a second completions mechanism to fix a bug. Having two completion mechanisms isn't so nice. As @thufschmitt also pointed out, it was a bummer to go from `FlakeRef` to `std::string` when collecting flake refs. Now it is `FlakeRefs` again. The underlying issue that sought to work around was that completion of arguments not at the end can still benefit from the information from latter arguments. To fix this better, we rip out that change and simply defer all completion processing until after all the (regular, already-complete) arguments have been passed. In addition, I noticed the original completion logic used some global variables. I do not like global variables, because even if they save lines of code, they also obfuscate the architecture of the code. I got rid of them moved them to a new `RootArgs` class, which now has `parseCmdline` instead of `Args`. The idea is that we have many argument parsers from subcommands and what-not, but only one root args that owns the other per actual parsing invocation. The state that was global is now part of the root args instead. This did, admittedly, add a bunch of new code. And I do feel bad about that. So I went and added a lot of API docs to try to at least make the current state of things clear to the next person. -- This is needed for RFC 134 (tracking issue #7868). It was very hard to modularize `Installable` parsing when there were two completion arguments. I wouldn't go as far as to say it is *easy* now, but at least it is less hard (and the completions test finally passed). Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2023-10-23 15:03:11 +02:00
/**
* The closure type of the completion callback.
*
* This is what is actually stored as part of each Flag / Expected
* Arg.
*/
using CompleterClosure = std::function<CompleterFun>;
Overhaul completions, redo #6693 (#8131) As I complained in https://github.com/NixOS/nix/pull/6784#issuecomment-1421777030 (a comment on the wrong PR, sorry again!), #6693 introduced a second completions mechanism to fix a bug. Having two completion mechanisms isn't so nice. As @thufschmitt also pointed out, it was a bummer to go from `FlakeRef` to `std::string` when collecting flake refs. Now it is `FlakeRefs` again. The underlying issue that sought to work around was that completion of arguments not at the end can still benefit from the information from latter arguments. To fix this better, we rip out that change and simply defer all completion processing until after all the (regular, already-complete) arguments have been passed. In addition, I noticed the original completion logic used some global variables. I do not like global variables, because even if they save lines of code, they also obfuscate the architecture of the code. I got rid of them moved them to a new `RootArgs` class, which now has `parseCmdline` instead of `Args`. The idea is that we have many argument parsers from subcommands and what-not, but only one root args that owns the other per actual parsing invocation. The state that was global is now part of the root args instead. This did, admittedly, add a bunch of new code. And I do feel bad about that. So I went and added a lot of API docs to try to at least make the current state of things clear to the next person. -- This is needed for RFC 134 (tracking issue #7868). It was very hard to modularize `Installable` parsing when there were two completion arguments. I wouldn't go as far as to say it is *easy* now, but at least it is less hard (and the completions test finally passed). Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2023-10-23 15:03:11 +02:00
/**
* Description of flags / options
*
* These are arguments like `-s` or `--long` that can (mostly)
* appear in any order.
*/
struct Flag
{
using ptr = std::shared_ptr<Flag>;
2020-05-04 22:40:19 +02:00
std::string longName;
std::set<std::string> aliases;
char shortName = 0;
std::string description;
std::string category;
2020-05-04 22:40:19 +02:00
Strings labels;
Handler handler;
Overhaul completions, redo #6693 (#8131) As I complained in https://github.com/NixOS/nix/pull/6784#issuecomment-1421777030 (a comment on the wrong PR, sorry again!), #6693 introduced a second completions mechanism to fix a bug. Having two completion mechanisms isn't so nice. As @thufschmitt also pointed out, it was a bummer to go from `FlakeRef` to `std::string` when collecting flake refs. Now it is `FlakeRefs` again. The underlying issue that sought to work around was that completion of arguments not at the end can still benefit from the information from latter arguments. To fix this better, we rip out that change and simply defer all completion processing until after all the (regular, already-complete) arguments have been passed. In addition, I noticed the original completion logic used some global variables. I do not like global variables, because even if they save lines of code, they also obfuscate the architecture of the code. I got rid of them moved them to a new `RootArgs` class, which now has `parseCmdline` instead of `Args`. The idea is that we have many argument parsers from subcommands and what-not, but only one root args that owns the other per actual parsing invocation. The state that was global is now part of the root args instead. This did, admittedly, add a bunch of new code. And I do feel bad about that. So I went and added a lot of API docs to try to at least make the current state of things clear to the next person. -- This is needed for RFC 134 (tracking issue #7868). It was very hard to modularize `Installable` parsing when there were two completion arguments. I wouldn't go as far as to say it is *easy* now, but at least it is less hard (and the completions test finally passed). Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2023-10-23 15:03:11 +02:00
CompleterClosure completer;
2020-05-04 22:40:19 +02:00
std::optional<ExperimentalFeature> experimentalFeature;
static Flag mkHashAlgoFlag(std::string && longName, HashAlgorithm * ha);
static Flag mkHashAlgoFlag(HashAlgorithm * ha) {
return mkHashAlgoFlag("hash-algo", ha);
}
static Flag mkHashAlgoOptFlag(std::string && longName, std::optional<HashAlgorithm> * oha);
static Flag mkHashAlgoOptFlag(std::optional<HashAlgorithm> * oha) {
return mkHashAlgoOptFlag("hash-algo", oha);
}
static Flag mkHashFormatFlagWithDefault(std::string && longName, HashFormat * hf);
static Flag mkHashFormatOptFlag(std::string && longName, std::optional<HashFormat> * ohf);
};
/**
* Index of all registered "long" flag descriptions (flags like
* `--long`).
*/
std::map<std::string, Flag::ptr> longFlags;
/**
* Index of all registered "short" flag descriptions (flags like
* `-s`).
*/
std::map<char, Flag::ptr> shortFlags;
/**
* Process a single flag and its arguments, pulling from an iterator
* of raw CLI args as needed.
*/
virtual bool processFlag(Strings::iterator & pos, Strings::iterator end);
/**
* Description of positional arguments
*
* These are arguments that do not start with a `-`, and for which
* the order does matter.
*/
struct ExpectedArg
{
std::string label;
2020-05-10 21:35:07 +02:00
bool optional = false;
2020-05-11 15:46:18 +02:00
Handler handler;
Overhaul completions, redo #6693 (#8131) As I complained in https://github.com/NixOS/nix/pull/6784#issuecomment-1421777030 (a comment on the wrong PR, sorry again!), #6693 introduced a second completions mechanism to fix a bug. Having two completion mechanisms isn't so nice. As @thufschmitt also pointed out, it was a bummer to go from `FlakeRef` to `std::string` when collecting flake refs. Now it is `FlakeRefs` again. The underlying issue that sought to work around was that completion of arguments not at the end can still benefit from the information from latter arguments. To fix this better, we rip out that change and simply defer all completion processing until after all the (regular, already-complete) arguments have been passed. In addition, I noticed the original completion logic used some global variables. I do not like global variables, because even if they save lines of code, they also obfuscate the architecture of the code. I got rid of them moved them to a new `RootArgs` class, which now has `parseCmdline` instead of `Args`. The idea is that we have many argument parsers from subcommands and what-not, but only one root args that owns the other per actual parsing invocation. The state that was global is now part of the root args instead. This did, admittedly, add a bunch of new code. And I do feel bad about that. So I went and added a lot of API docs to try to at least make the current state of things clear to the next person. -- This is needed for RFC 134 (tracking issue #7868). It was very hard to modularize `Installable` parsing when there were two completion arguments. I wouldn't go as far as to say it is *easy* now, but at least it is less hard (and the completions test finally passed). Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2023-10-23 15:03:11 +02:00
CompleterClosure completer;
};
/**
* Queue of expected positional argument forms.
*
* Positional argument descriptions are inserted on the back.
*
* As positional arguments are passed, these are popped from the
* front, until there are hopefully none left as all args that were
* expected in fact were passed.
*/
std::list<ExpectedArg> expectedArgs;
/**
* List of processed positional argument forms.
*
* All items removed from `expectedArgs` are added here. After all
* arguments were processed, this list should be exactly the same as
* `expectedArgs` was before.
*
* This list is used to extend the lifetime of the argument forms.
* If this is not done, some closures that reference the command
* itself will segfault.
*/
std::list<ExpectedArg> processedArgs;
/**
* Process some positional arugments
*
* @param finish: We have parsed everything else, and these are the only
* arguments left. Used because we accumulate some "pending args" we might
* have left over.
*/
virtual bool processArgs(const Strings & args, bool finish);
virtual Strings::iterator rewriteArgs(Strings & args, Strings::iterator pos)
{ return pos; }
std::set<std::string> hiddenCategories;
/**
* Called after all command line flags before the first non-flag
* argument (if any) have been processed.
*/
virtual void initialFlagsProcessed() {}
public:
2020-05-04 22:40:19 +02:00
void addFlag(Flag && flag);
void removeFlag(const std::string & longName);
2020-05-11 15:46:18 +02:00
void expectArgs(ExpectedArg && arg)
{
expectedArgs.emplace_back(std::move(arg));
}
/**
* Expect a string argument.
*/
void expectArg(const std::string & label, std::string * dest, bool optional = false)
{
2020-05-11 15:46:18 +02:00
expectArgs({
.label = label,
.optional = optional,
2020-05-11 15:46:18 +02:00
.handler = {dest}
});
}
/**
* Expect 0 or more arguments.
*/
void expectArgs(const std::string & label, std::vector<std::string> * dest)
{
2020-05-11 15:46:18 +02:00
expectArgs({
.label = label,
.handler = {dest}
});
}
Overhaul completions, redo #6693 (#8131) As I complained in https://github.com/NixOS/nix/pull/6784#issuecomment-1421777030 (a comment on the wrong PR, sorry again!), #6693 introduced a second completions mechanism to fix a bug. Having two completion mechanisms isn't so nice. As @thufschmitt also pointed out, it was a bummer to go from `FlakeRef` to `std::string` when collecting flake refs. Now it is `FlakeRefs` again. The underlying issue that sought to work around was that completion of arguments not at the end can still benefit from the information from latter arguments. To fix this better, we rip out that change and simply defer all completion processing until after all the (regular, already-complete) arguments have been passed. In addition, I noticed the original completion logic used some global variables. I do not like global variables, because even if they save lines of code, they also obfuscate the architecture of the code. I got rid of them moved them to a new `RootArgs` class, which now has `parseCmdline` instead of `Args`. The idea is that we have many argument parsers from subcommands and what-not, but only one root args that owns the other per actual parsing invocation. The state that was global is now part of the root args instead. This did, admittedly, add a bunch of new code. And I do feel bad about that. So I went and added a lot of API docs to try to at least make the current state of things clear to the next person. -- This is needed for RFC 134 (tracking issue #7868). It was very hard to modularize `Installable` parsing when there were two completion arguments. I wouldn't go as far as to say it is *easy* now, but at least it is less hard (and the completions test finally passed). Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2023-10-23 15:03:11 +02:00
static CompleterFun completePath;
static CompleterFun completeDir;
virtual nlohmann::json toJSON();
friend class MultiCommand;
/**
* The parent command, used if this is a subcommand.
Overhaul completions, redo #6693 (#8131) As I complained in https://github.com/NixOS/nix/pull/6784#issuecomment-1421777030 (a comment on the wrong PR, sorry again!), #6693 introduced a second completions mechanism to fix a bug. Having two completion mechanisms isn't so nice. As @thufschmitt also pointed out, it was a bummer to go from `FlakeRef` to `std::string` when collecting flake refs. Now it is `FlakeRefs` again. The underlying issue that sought to work around was that completion of arguments not at the end can still benefit from the information from latter arguments. To fix this better, we rip out that change and simply defer all completion processing until after all the (regular, already-complete) arguments have been passed. In addition, I noticed the original completion logic used some global variables. I do not like global variables, because even if they save lines of code, they also obfuscate the architecture of the code. I got rid of them moved them to a new `RootArgs` class, which now has `parseCmdline` instead of `Args`. The idea is that we have many argument parsers from subcommands and what-not, but only one root args that owns the other per actual parsing invocation. The state that was global is now part of the root args instead. This did, admittedly, add a bunch of new code. And I do feel bad about that. So I went and added a lot of API docs to try to at least make the current state of things clear to the next person. -- This is needed for RFC 134 (tracking issue #7868). It was very hard to modularize `Installable` parsing when there were two completion arguments. I wouldn't go as far as to say it is *easy* now, but at least it is less hard (and the completions test finally passed). Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2023-10-23 15:03:11 +02:00
*
* Invariant: An Args with a null parent must also be a RootArgs
*
* \todo this would probably be better in the CommandClass.
* getRoot() could be an abstract method that peels off at most one
* layer before recuring.
*/
MultiCommand * parent = nullptr;
/**
Overhaul completions, redo #6693 (#8131) As I complained in https://github.com/NixOS/nix/pull/6784#issuecomment-1421777030 (a comment on the wrong PR, sorry again!), #6693 introduced a second completions mechanism to fix a bug. Having two completion mechanisms isn't so nice. As @thufschmitt also pointed out, it was a bummer to go from `FlakeRef` to `std::string` when collecting flake refs. Now it is `FlakeRefs` again. The underlying issue that sought to work around was that completion of arguments not at the end can still benefit from the information from latter arguments. To fix this better, we rip out that change and simply defer all completion processing until after all the (regular, already-complete) arguments have been passed. In addition, I noticed the original completion logic used some global variables. I do not like global variables, because even if they save lines of code, they also obfuscate the architecture of the code. I got rid of them moved them to a new `RootArgs` class, which now has `parseCmdline` instead of `Args`. The idea is that we have many argument parsers from subcommands and what-not, but only one root args that owns the other per actual parsing invocation. The state that was global is now part of the root args instead. This did, admittedly, add a bunch of new code. And I do feel bad about that. So I went and added a lot of API docs to try to at least make the current state of things clear to the next person. -- This is needed for RFC 134 (tracking issue #7868). It was very hard to modularize `Installable` parsing when there were two completion arguments. I wouldn't go as far as to say it is *easy* now, but at least it is less hard (and the completions test finally passed). Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2023-10-23 15:03:11 +02:00
* Traverse parent pointers until we find the \ref RootArgs "root
* arguments" object.
*/
Overhaul completions, redo #6693 (#8131) As I complained in https://github.com/NixOS/nix/pull/6784#issuecomment-1421777030 (a comment on the wrong PR, sorry again!), #6693 introduced a second completions mechanism to fix a bug. Having two completion mechanisms isn't so nice. As @thufschmitt also pointed out, it was a bummer to go from `FlakeRef` to `std::string` when collecting flake refs. Now it is `FlakeRefs` again. The underlying issue that sought to work around was that completion of arguments not at the end can still benefit from the information from latter arguments. To fix this better, we rip out that change and simply defer all completion processing until after all the (regular, already-complete) arguments have been passed. In addition, I noticed the original completion logic used some global variables. I do not like global variables, because even if they save lines of code, they also obfuscate the architecture of the code. I got rid of them moved them to a new `RootArgs` class, which now has `parseCmdline` instead of `Args`. The idea is that we have many argument parsers from subcommands and what-not, but only one root args that owns the other per actual parsing invocation. The state that was global is now part of the root args instead. This did, admittedly, add a bunch of new code. And I do feel bad about that. So I went and added a lot of API docs to try to at least make the current state of things clear to the next person. -- This is needed for RFC 134 (tracking issue #7868). It was very hard to modularize `Installable` parsing when there were two completion arguments. I wouldn't go as far as to say it is *easy* now, but at least it is less hard (and the completions test finally passed). Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2023-10-23 15:03:11 +02:00
RootArgs & getRoot();
};
/**
* A command is an argument parser that can be executed by calling its
* run() method.
*/
struct Command : virtual public Args
{
friend class MultiCommand;
2023-10-23 17:07:57 +02:00
virtual ~Command() = default;
/**
* Entry point to the command
*/
virtual void run() = 0;
using Category = int;
2020-05-05 15:18:23 +02:00
static constexpr Category catDefault = 0;
virtual std::optional<ExperimentalFeature> experimentalFeature();
2020-05-05 15:18:23 +02:00
virtual Category category() { return catDefault; }
};
using Commands = std::map<std::string, std::function<ref<Command>()>>;
/**
* An argument parser that supports multiple subcommands,
* i.e. <command> <subcommand>.
*/
class MultiCommand : virtual public Args
{
public:
Commands commands;
2020-05-05 15:18:23 +02:00
std::map<Command::Category, std::string> categories;
/**
* Selected command, if any.
*/
2020-05-05 15:18:23 +02:00
std::optional<std::pair<std::string, ref<Command>>> command;
MultiCommand(std::string_view commandName, const Commands & commands);
bool processFlag(Strings::iterator & pos, Strings::iterator end) override;
bool processArgs(const Strings & args, bool finish) override;
nlohmann::json toJSON() override;
protected:
std::string commandName = "";
};
Strings argvToStrings(int argc, char * * argv);
struct Completion {
std::string completion;
std::string description;
bool operator<(const Completion & other) const;
};
Overhaul completions, redo #6693 (#8131) As I complained in https://github.com/NixOS/nix/pull/6784#issuecomment-1421777030 (a comment on the wrong PR, sorry again!), #6693 introduced a second completions mechanism to fix a bug. Having two completion mechanisms isn't so nice. As @thufschmitt also pointed out, it was a bummer to go from `FlakeRef` to `std::string` when collecting flake refs. Now it is `FlakeRefs` again. The underlying issue that sought to work around was that completion of arguments not at the end can still benefit from the information from latter arguments. To fix this better, we rip out that change and simply defer all completion processing until after all the (regular, already-complete) arguments have been passed. In addition, I noticed the original completion logic used some global variables. I do not like global variables, because even if they save lines of code, they also obfuscate the architecture of the code. I got rid of them moved them to a new `RootArgs` class, which now has `parseCmdline` instead of `Args`. The idea is that we have many argument parsers from subcommands and what-not, but only one root args that owns the other per actual parsing invocation. The state that was global is now part of the root args instead. This did, admittedly, add a bunch of new code. And I do feel bad about that. So I went and added a lot of API docs to try to at least make the current state of things clear to the next person. -- This is needed for RFC 134 (tracking issue #7868). It was very hard to modularize `Installable` parsing when there were two completion arguments. I wouldn't go as far as to say it is *easy* now, but at least it is less hard (and the completions test finally passed). Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2023-10-23 15:03:11 +02:00
/**
* The abstract interface for completions callbacks
*
* The idea is to restrict the callback so it can only add additional
* completions to the collection, or set the completion type. By making
* it go through this interface, the callback cannot make any other
* changes, or even view the completions / completion type that have
* been set so far.
*/
class AddCompletions
{
public:
Overhaul completions, redo #6693 (#8131) As I complained in https://github.com/NixOS/nix/pull/6784#issuecomment-1421777030 (a comment on the wrong PR, sorry again!), #6693 introduced a second completions mechanism to fix a bug. Having two completion mechanisms isn't so nice. As @thufschmitt also pointed out, it was a bummer to go from `FlakeRef` to `std::string` when collecting flake refs. Now it is `FlakeRefs` again. The underlying issue that sought to work around was that completion of arguments not at the end can still benefit from the information from latter arguments. To fix this better, we rip out that change and simply defer all completion processing until after all the (regular, already-complete) arguments have been passed. In addition, I noticed the original completion logic used some global variables. I do not like global variables, because even if they save lines of code, they also obfuscate the architecture of the code. I got rid of them moved them to a new `RootArgs` class, which now has `parseCmdline` instead of `Args`. The idea is that we have many argument parsers from subcommands and what-not, but only one root args that owns the other per actual parsing invocation. The state that was global is now part of the root args instead. This did, admittedly, add a bunch of new code. And I do feel bad about that. So I went and added a lot of API docs to try to at least make the current state of things clear to the next person. -- This is needed for RFC 134 (tracking issue #7868). It was very hard to modularize `Installable` parsing when there were two completion arguments. I wouldn't go as far as to say it is *easy* now, but at least it is less hard (and the completions test finally passed). Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2023-10-23 15:03:11 +02:00
/**
* The type of completion we are collecting.
*/
enum class Type {
Normal,
Filenames,
Attrs,
};
2020-05-10 20:32:21 +02:00
Overhaul completions, redo #6693 (#8131) As I complained in https://github.com/NixOS/nix/pull/6784#issuecomment-1421777030 (a comment on the wrong PR, sorry again!), #6693 introduced a second completions mechanism to fix a bug. Having two completion mechanisms isn't so nice. As @thufschmitt also pointed out, it was a bummer to go from `FlakeRef` to `std::string` when collecting flake refs. Now it is `FlakeRefs` again. The underlying issue that sought to work around was that completion of arguments not at the end can still benefit from the information from latter arguments. To fix this better, we rip out that change and simply defer all completion processing until after all the (regular, already-complete) arguments have been passed. In addition, I noticed the original completion logic used some global variables. I do not like global variables, because even if they save lines of code, they also obfuscate the architecture of the code. I got rid of them moved them to a new `RootArgs` class, which now has `parseCmdline` instead of `Args`. The idea is that we have many argument parsers from subcommands and what-not, but only one root args that owns the other per actual parsing invocation. The state that was global is now part of the root args instead. This did, admittedly, add a bunch of new code. And I do feel bad about that. So I went and added a lot of API docs to try to at least make the current state of things clear to the next person. -- This is needed for RFC 134 (tracking issue #7868). It was very hard to modularize `Installable` parsing when there were two completion arguments. I wouldn't go as far as to say it is *easy* now, but at least it is less hard (and the completions test finally passed). Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2023-10-23 15:03:11 +02:00
/**
* Set the type of the completions being collected
*
* \todo it should not be possible to change the type after it has been set.
*/
virtual void setType(Type type) = 0;
2020-05-10 21:35:07 +02:00
Overhaul completions, redo #6693 (#8131) As I complained in https://github.com/NixOS/nix/pull/6784#issuecomment-1421777030 (a comment on the wrong PR, sorry again!), #6693 introduced a second completions mechanism to fix a bug. Having two completion mechanisms isn't so nice. As @thufschmitt also pointed out, it was a bummer to go from `FlakeRef` to `std::string` when collecting flake refs. Now it is `FlakeRefs` again. The underlying issue that sought to work around was that completion of arguments not at the end can still benefit from the information from latter arguments. To fix this better, we rip out that change and simply defer all completion processing until after all the (regular, already-complete) arguments have been passed. In addition, I noticed the original completion logic used some global variables. I do not like global variables, because even if they save lines of code, they also obfuscate the architecture of the code. I got rid of them moved them to a new `RootArgs` class, which now has `parseCmdline` instead of `Args`. The idea is that we have many argument parsers from subcommands and what-not, but only one root args that owns the other per actual parsing invocation. The state that was global is now part of the root args instead. This did, admittedly, add a bunch of new code. And I do feel bad about that. So I went and added a lot of API docs to try to at least make the current state of things clear to the next person. -- This is needed for RFC 134 (tracking issue #7868). It was very hard to modularize `Installable` parsing when there were two completion arguments. I wouldn't go as far as to say it is *easy* now, but at least it is less hard (and the completions test finally passed). Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2023-10-23 15:03:11 +02:00
/**
* Add a single completion to the collection
*/
virtual void add(std::string completion, std::string description = "") = 0;
};
Strings parseShebangContent(std::string_view s);
}