diff options
author | amatanhead <amatanhead@yandex-team.ru> | 2022-02-10 16:50:04 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:50:04 +0300 |
commit | b6f3a80f7c2c8b7dbb0c01b056fdc1fd8cd820e9 (patch) | |
tree | 5d5cb817648f650d76cf1076100726fd9b8448e8 /library/cpp/getopt | |
parent | 8879605a63ac17539be5b3bd41b529791f4d4b02 (diff) | |
download | ydb-b6f3a80f7c2c8b7dbb0c01b056fdc1fd8cd820e9.tar.gz |
Restoring authorship annotation for <amatanhead@yandex-team.ru>. Commit 2 of 2.
Diffstat (limited to 'library/cpp/getopt')
28 files changed, 3442 insertions, 3442 deletions
diff --git a/library/cpp/getopt/last_getopt_demo/demo.cpp b/library/cpp/getopt/last_getopt_demo/demo.cpp index 9dc8ceae85..79426a9cc9 100644 --- a/library/cpp/getopt/last_getopt_demo/demo.cpp +++ b/library/cpp/getopt/last_getopt_demo/demo.cpp @@ -2,241 +2,241 @@ #include <library/cpp/getopt/modchooser.h> #include <library/cpp/colorizer/colors.h> -// For the sake of this example, let's implement Wget - -Y_COMPLETER(HeaderCompleter) { - AddCompletion("Host"); - AddCompletion("Referer"); - - bool addPostHeaders = false; - - for (int i = 0; i < argc; ++i) { +// For the sake of this example, let's implement Wget + +Y_COMPLETER(HeaderCompleter) { + AddCompletion("Host"); + AddCompletion("Referer"); + + bool addPostHeaders = false; + + for (int i = 0; i < argc; ++i) { if (argv[i] == TStringBuf("--method") && i + 1 < argc) { - auto method = TString(argv[i + 1]); - method.to_upper(); - addPostHeaders = method == "POST" || method == "PUT"; - break; + auto method = TString(argv[i + 1]); + method.to_upper(); + addPostHeaders = method == "POST" || method == "PUT"; + break; } else if (argv[i] == TStringBuf("--post-data") || argv[i] == TStringBuf("--post-file")) { - addPostHeaders = true; - break; - } - } - - if (addPostHeaders) { - AddCompletion("Content-Type"); - AddCompletion("Content-Encoding"); - AddCompletion("Content-Language"); - AddCompletion("Content-Length"); - AddCompletion("Content-Location"); - AddCompletion("Content-MD5"); - AddCompletion("Content-Range"); - } + addPostHeaders = true; + break; + } + } + + if (addPostHeaders) { + AddCompletion("Content-Type"); + AddCompletion("Content-Encoding"); + AddCompletion("Content-Language"); + AddCompletion("Content-Length"); + AddCompletion("Content-Location"); + AddCompletion("Content-MD5"); + AddCompletion("Content-Range"); + } } -class TMain: public TMainClassArgs { - bool Background_; - size_t Timeout_; - TString ExplicitMethod_; - TString ImplicitMethod_ = "GET"; - TString UserAgent_; - TMaybe<TString> PostData_; - TMaybe<TString> PostFile_; - TVector<TString> Headers_; - -protected: - void RegisterOptions(NLastGetopt::TOpts& opts) override { +class TMain: public TMainClassArgs { + bool Background_; + size_t Timeout_; + TString ExplicitMethod_; + TString ImplicitMethod_ = "GET"; + TString UserAgent_; + TMaybe<TString> PostData_; + TMaybe<TString> PostFile_; + TVector<TString> Headers_; + +protected: + void RegisterOptions(NLastGetopt::TOpts& opts) override { // Brief description for the whole program, will appear in the beginning of a help message. - opts.SetTitle("last_getopt_demo -- like wget, but doesn't actually do anything"); - - // Built-in options. - opts.AddHelpOption('h'); - opts.AddCompletionOption("last_getopt_demo"); - - // Custom options. - - opts.AddLongOption('V', "version") - .Help("print version and exit") - .IfPresentDisableCompletion() - .NoArgument() - .Handler([]() { - Cerr << "last_getopt_demo 1.0.0" << Endl; - exit(0); - }); - - opts.AddLongOption('b', "background") - .Help("go to background immediately after startup") - .StoreTrue(&Background_); - - opts.AddLongOption("timeout") - .RequiredArgument("timeout") - .DefaultValue("60000") - .Help("specify timeout in milliseconds for each request") - .CompletionHelp("specify timeout for each request") - .CompletionArgHelp("timeout (ms)") - .StoreResult(&Timeout_) - .Completer(NLastGetopt::NComp::Choice({{"1000"}, {"5000"}, {"10000"}, {"60000"}})); - - opts.AddLongOption("method") - .RequiredArgument("http-method") - .Help("specify HTTP method") - .CompletionArgHelp("http method") - .StoreResult(&ExplicitMethod_) - .Completer( - NLastGetopt::NComp::Choice( - {{"GET", "request representation of the specified resource"}, - {"HEAD", "request response identical to that of GET, but without response body"}, - {"POST", "submit an entry to the specified resource"}, - {"PUT", "replace representation of the specified resource with the request body"}, - {"DELETE", "delete the specified resource"}, - {"CONNECT", "establish a tunnel to the server identified by the target resource"}, - {"OPTIONS", "describe the communication options for the target resource"}, - {"TRACE", "perform a message loop-back test"}, - {"PATCH", "apply partial modifications to the specified resource"}})); - - opts.AddLongOption('U', "user-agent") - .RequiredArgument("agent-string") - .DefaultValue("LastGetoptDemo/1.0.0") - .Help("identify as `agent-string` to the HTTP server") - .CompletionHelp("set custom user agent for each HTTP request") - .CompletionArgHelp("user agent string") - .StoreResult(&UserAgent_); - - opts.AddLongOption("post-data") - .RequiredArgument("string") - .Help("use POST method and send the specified data in the request body (cannot be used with --post-file)") - .CompletionHelp("use POST method and send the specified data in the request body") - .CompletionArgHelp("POST data string") - .StoreResultT<TString>(&PostData_) - .Handler0([this]() { - ImplicitMethod_ = "POST"; - }); - - opts.AddLongOption("post-file") - .RequiredArgument("file") - .Help("use POST method and send contents of the specified file in the request body (cannot be used with --post-data)") - .CompletionHelp("use POST method and send contents of the specified file in the request body") - .CompletionArgHelp("POST file") - .StoreResultT<TString>(&PostFile_) - .Handler0([this]() { - ImplicitMethod_ = "POST"; - }) - .Completer(NLastGetopt::NComp::File()); - - // These two options can't be together. - opts.MutuallyExclusive("post-file", "post-data"); - - opts.AddLongOption("header") - .RequiredArgument("header-line") - .Help("send `header-line` along with the rest of the headers in each HTTP request") - .CompletionHelp("add header to each HTTP request") - .CompletionArgHelp("header string") - .AppendTo(&Headers_) - .AllowMultipleCompletion() - .Completer(NLastGetopt::NComp::LaunchSelf(HeaderCompleter)); - - // Setting up free arguments. - - // We are going to have one mandatory argument and unlimited number of optional arguments. - opts.SetFreeArgsMin(1); - opts.SetFreeArgsMax(NLastGetopt::TOpts::UNLIMITED_ARGS); - - // Configuration for the first argument. - opts.GetFreeArgSpec(0) - .Title("URL") - .Help("URL for download") - .CompletionArgHelp("URL for download") - .Completer(NLastGetopt::NComp::Url()); - - // Configuration for optional arguments. - opts.GetTrailingArgSpec() - .Title("URL") - .CompletionArgHelp("URL for download") - .Completer(NLastGetopt::NComp::Url()); - + opts.SetTitle("last_getopt_demo -- like wget, but doesn't actually do anything"); + + // Built-in options. + opts.AddHelpOption('h'); + opts.AddCompletionOption("last_getopt_demo"); + + // Custom options. + + opts.AddLongOption('V', "version") + .Help("print version and exit") + .IfPresentDisableCompletion() + .NoArgument() + .Handler([]() { + Cerr << "last_getopt_demo 1.0.0" << Endl; + exit(0); + }); + + opts.AddLongOption('b', "background") + .Help("go to background immediately after startup") + .StoreTrue(&Background_); + + opts.AddLongOption("timeout") + .RequiredArgument("timeout") + .DefaultValue("60000") + .Help("specify timeout in milliseconds for each request") + .CompletionHelp("specify timeout for each request") + .CompletionArgHelp("timeout (ms)") + .StoreResult(&Timeout_) + .Completer(NLastGetopt::NComp::Choice({{"1000"}, {"5000"}, {"10000"}, {"60000"}})); + + opts.AddLongOption("method") + .RequiredArgument("http-method") + .Help("specify HTTP method") + .CompletionArgHelp("http method") + .StoreResult(&ExplicitMethod_) + .Completer( + NLastGetopt::NComp::Choice( + {{"GET", "request representation of the specified resource"}, + {"HEAD", "request response identical to that of GET, but without response body"}, + {"POST", "submit an entry to the specified resource"}, + {"PUT", "replace representation of the specified resource with the request body"}, + {"DELETE", "delete the specified resource"}, + {"CONNECT", "establish a tunnel to the server identified by the target resource"}, + {"OPTIONS", "describe the communication options for the target resource"}, + {"TRACE", "perform a message loop-back test"}, + {"PATCH", "apply partial modifications to the specified resource"}})); + + opts.AddLongOption('U', "user-agent") + .RequiredArgument("agent-string") + .DefaultValue("LastGetoptDemo/1.0.0") + .Help("identify as `agent-string` to the HTTP server") + .CompletionHelp("set custom user agent for each HTTP request") + .CompletionArgHelp("user agent string") + .StoreResult(&UserAgent_); + + opts.AddLongOption("post-data") + .RequiredArgument("string") + .Help("use POST method and send the specified data in the request body (cannot be used with --post-file)") + .CompletionHelp("use POST method and send the specified data in the request body") + .CompletionArgHelp("POST data string") + .StoreResultT<TString>(&PostData_) + .Handler0([this]() { + ImplicitMethod_ = "POST"; + }); + + opts.AddLongOption("post-file") + .RequiredArgument("file") + .Help("use POST method and send contents of the specified file in the request body (cannot be used with --post-data)") + .CompletionHelp("use POST method and send contents of the specified file in the request body") + .CompletionArgHelp("POST file") + .StoreResultT<TString>(&PostFile_) + .Handler0([this]() { + ImplicitMethod_ = "POST"; + }) + .Completer(NLastGetopt::NComp::File()); + + // These two options can't be together. + opts.MutuallyExclusive("post-file", "post-data"); + + opts.AddLongOption("header") + .RequiredArgument("header-line") + .Help("send `header-line` along with the rest of the headers in each HTTP request") + .CompletionHelp("add header to each HTTP request") + .CompletionArgHelp("header string") + .AppendTo(&Headers_) + .AllowMultipleCompletion() + .Completer(NLastGetopt::NComp::LaunchSelf(HeaderCompleter)); + + // Setting up free arguments. + + // We are going to have one mandatory argument and unlimited number of optional arguments. + opts.SetFreeArgsMin(1); + opts.SetFreeArgsMax(NLastGetopt::TOpts::UNLIMITED_ARGS); + + // Configuration for the first argument. + opts.GetFreeArgSpec(0) + .Title("URL") + .Help("URL for download") + .CompletionArgHelp("URL for download") + .Completer(NLastGetopt::NComp::Url()); + + // Configuration for optional arguments. + opts.GetTrailingArgSpec() + .Title("URL") + .CompletionArgHelp("URL for download") + .Completer(NLastGetopt::NComp::Url()); + // Let's add more text to our help. A nice description and examples. - - opts.AddSection( - "Description", - + + opts.AddSection( + "Description", + "LastGetoptDemo is a showcase of library/cpp/getopt capabilities. It mimics interface of Wget " - "but doesn't actually do anything." - "\n\n" - "GNU Wget, on the other hand, is a free utility for non-interactive download of files from the Web." - "It supports HTTP, HTTPS, and FTP protocols, as well as retrieval through HTTP proxies." - "\n\n" - "Wget is non-interactive, meaning that it can work in the background, while the user is not logged on. " - "This allows you to start a retrieval and disconnect from the system, letting Wget finish the work. " - "By contrast, most of the Web browsers require constant user's presence, " - "which can be a great hindrance when transferring a lot of data." - "\n\n" - "Wget can follow links in HTML, XHTML, and CSS pages, to create local versions of remote web sites, " - "fully recreating the directory structure of the original site. " - "This is sometimes referred to as \"recursive downloading.\" " - "While doing that, Wget respects the Robot Exclusion Standard (/robots.txt). " - "Wget can be instructed to convert the links in downloaded files to point at the local files, " - "for offline viewing." - "\n\n" - "Wget has been designed for robustness over slow or unstable network connections; " - "if a download fails due to a network problem, " - "it will keep retrying until the whole file has been retrieved. " - "If the server supports regetting, " - "it will instruct the server to continue the download from where it left off." - "\n\n" - "Wget does not support Client Revocation Lists (CRLs) so the HTTPS certificate " - "you are connecting to might be revoked by the siteowner."); - - // We will use colors for this one. - auto& colors = NColorizer::StdErr(); - opts.AddSection( - "Examples", - - TStringBuilder() - << "Download a file:" - << "\n" - << colors.Cyan() - << " $ last_getopt_demo https://wordpress.org/latest.zip" - << colors.Reset() - << "\n" - << "Download a file in background, set custom user agent:" - << "\n" - << colors.Cyan() - << " $ last_getopt_demo -b -U 'Wget/1.0.0' https://wordpress.org/latest.zip" - << colors.Reset()); + "but doesn't actually do anything." + "\n\n" + "GNU Wget, on the other hand, is a free utility for non-interactive download of files from the Web." + "It supports HTTP, HTTPS, and FTP protocols, as well as retrieval through HTTP proxies." + "\n\n" + "Wget is non-interactive, meaning that it can work in the background, while the user is not logged on. " + "This allows you to start a retrieval and disconnect from the system, letting Wget finish the work. " + "By contrast, most of the Web browsers require constant user's presence, " + "which can be a great hindrance when transferring a lot of data." + "\n\n" + "Wget can follow links in HTML, XHTML, and CSS pages, to create local versions of remote web sites, " + "fully recreating the directory structure of the original site. " + "This is sometimes referred to as \"recursive downloading.\" " + "While doing that, Wget respects the Robot Exclusion Standard (/robots.txt). " + "Wget can be instructed to convert the links in downloaded files to point at the local files, " + "for offline viewing." + "\n\n" + "Wget has been designed for robustness over slow or unstable network connections; " + "if a download fails due to a network problem, " + "it will keep retrying until the whole file has been retrieved. " + "If the server supports regetting, " + "it will instruct the server to continue the download from where it left off." + "\n\n" + "Wget does not support Client Revocation Lists (CRLs) so the HTTPS certificate " + "you are connecting to might be revoked by the siteowner."); + + // We will use colors for this one. + auto& colors = NColorizer::StdErr(); + opts.AddSection( + "Examples", + + TStringBuilder() + << "Download a file:" + << "\n" + << colors.Cyan() + << " $ last_getopt_demo https://wordpress.org/latest.zip" + << colors.Reset() + << "\n" + << "Download a file in background, set custom user agent:" + << "\n" + << colors.Cyan() + << " $ last_getopt_demo -b -U 'Wget/1.0.0' https://wordpress.org/latest.zip" + << colors.Reset()); + } + + int DoRun(NLastGetopt::TOptsParseResult&& parsedOptions) override { + using namespace NColorizer; + + TString method = ExplicitMethod_ ? ExplicitMethod_ : ImplicitMethod_; + + Cerr << ST_LIGHT << "Settings:" << RESET << Endl; + Cerr << GREEN << " Background: " << RESET << Background_ << Endl; + Cerr << GREEN << " Timeout: " << RESET << Timeout_ << Endl; + Cerr << GREEN << " Method: " << RESET << method.Quote() << Endl; + Cerr << GREEN << " UserAgent: " << RESET << UserAgent_.Quote() << Endl; + Cerr << GREEN << " PostData: " << RESET << (PostData_ ? PostData_->Quote() : "Nothing") << Endl; + Cerr << GREEN << " PostFile: " << RESET << (PostFile_ ? PostFile_->Quote() : "Nothing") << Endl; + + Cerr << ST_LIGHT << "Headers:" << RESET << Endl; + for (auto& header : Headers_) { + Cerr << " " << header.Quote() << Endl; + } + if (!Headers_) { + Cerr << GREEN << " no headers" << RESET << Endl; + } + + Cerr << ST_LIGHT << "Will download the following URLs:" << RESET << Endl; + for (auto& arg : parsedOptions.GetFreeArgs()) { + Cerr << " " << arg.Quote() << Endl; + } + if (!parsedOptions.GetFreeArgs()) { + Cerr << " no urls" << Endl; + } + return 0; } +}; - int DoRun(NLastGetopt::TOptsParseResult&& parsedOptions) override { - using namespace NColorizer; - - TString method = ExplicitMethod_ ? ExplicitMethod_ : ImplicitMethod_; - - Cerr << ST_LIGHT << "Settings:" << RESET << Endl; - Cerr << GREEN << " Background: " << RESET << Background_ << Endl; - Cerr << GREEN << " Timeout: " << RESET << Timeout_ << Endl; - Cerr << GREEN << " Method: " << RESET << method.Quote() << Endl; - Cerr << GREEN << " UserAgent: " << RESET << UserAgent_.Quote() << Endl; - Cerr << GREEN << " PostData: " << RESET << (PostData_ ? PostData_->Quote() : "Nothing") << Endl; - Cerr << GREEN << " PostFile: " << RESET << (PostFile_ ? PostFile_->Quote() : "Nothing") << Endl; - - Cerr << ST_LIGHT << "Headers:" << RESET << Endl; - for (auto& header : Headers_) { - Cerr << " " << header.Quote() << Endl; - } - if (!Headers_) { - Cerr << GREEN << " no headers" << RESET << Endl; - } - - Cerr << ST_LIGHT << "Will download the following URLs:" << RESET << Endl; - for (auto& arg : parsedOptions.GetFreeArgs()) { - Cerr << " " << arg.Quote() << Endl; - } - if (!parsedOptions.GetFreeArgs()) { - Cerr << " no urls" << Endl; - } - return 0; - } -}; - -int main(int argc, const char** argv) { - NLastGetopt::NComp::TCustomCompleter::FireCustomCompleter(argc, argv); - TMain().Run(argc, argv); +int main(int argc, const char** argv) { + NLastGetopt::NComp::TCustomCompleter::FireCustomCompleter(argc, argv); + TMain().Run(argc, argv); } diff --git a/library/cpp/getopt/last_getopt_demo/ya.make b/library/cpp/getopt/last_getopt_demo/ya.make index b9fd815bdc..53f1cfc122 100644 --- a/library/cpp/getopt/last_getopt_demo/ya.make +++ b/library/cpp/getopt/last_getopt_demo/ya.make @@ -1,6 +1,6 @@ PROGRAM(last_getopt_demo) -OWNER(amatanhead) +OWNER(amatanhead) PEERDIR( library/cpp/getopt diff --git a/library/cpp/getopt/small/completer.cpp b/library/cpp/getopt/small/completer.cpp index 0536e4cccf..3fff684adb 100644 --- a/library/cpp/getopt/small/completer.cpp +++ b/library/cpp/getopt/small/completer.cpp @@ -1,367 +1,367 @@ -#include "completer.h" - -#include "completion_generator.h" - -#include <util/string/cast.h> -#include <util/generic/fwd.h> - -using NLastGetopt::NEscaping::Q; -using NLastGetopt::NEscaping::QQ; -using NLastGetopt::NEscaping::C; -using NLastGetopt::NEscaping::CC; -using NLastGetopt::NEscaping::S; -using NLastGetopt::NEscaping::SS; -using NLastGetopt::NEscaping::B; -using NLastGetopt::NEscaping::BB; - -namespace NLastGetopt::NComp { -#define L out.Line() -#define I auto Y_GENERATE_UNIQUE_ID(indent) = out.Indent() - - TCompleterManager::TCompleterManager(TStringBuf command) - : Command_(command) - , Id_(0) - { - } - - TStringBuf TCompleterManager::GetCompleterID(const ICompleter* completer) { - return Queue_.emplace_back(TStringBuilder() << "_" << Command_ << "__completer_" << ++Id_, completer).first; - } - - void TCompleterManager::GenerateZsh(TFormattedOutput& out) { - while (!Queue_.empty()) { - auto[name, completer] = Queue_.back(); - Queue_.pop_back(); - - L << "(( $+functions[" << name << "] )) ||"; - L << name << "() {"; - { - I; - completer->GenerateZsh(out, *this); - } - L << "}"; - L; - } - } - - class TAlternativeCompleter: public ICompleter { - public: - TAlternativeCompleter(TVector<TAlternative> alternatives) - : Alternatives_(std::move(alternatives)) - { - } - - void GenerateBash(TFormattedOutput& out) const override { - for (auto& alternative: Alternatives_) { - if (alternative.Completer != nullptr) { - alternative.Completer->GenerateBash(out); - } - } - } - - TStringBuf GenerateZshAction(TCompleterManager& manager) const override { - return manager.GetCompleterID(this); - } - - void GenerateZsh(TFormattedOutput& out, TCompleterManager& manager) const override { - // We should use '_alternative' here, but it doesn't process escape codes in group descriptions, - // so we dispatch alternatives ourselves. - - L << "local expl action"; - - size_t i = 0; - for (auto& alternative: Alternatives_) { - auto tag = "alt-" + ToString(++i); - auto action = alternative.Completer ? alternative.Completer->GenerateZshAction(manager) : TStringBuf(); - - L; - - if (action.empty()) { - L << "_message -e " << SS(tag) << " " << SS(alternative.Description); - } else if (action.StartsWith("((") && action.EndsWith("))")) { - L << "action=" << action.substr(1, action.size() - 2); - L << "_describe -t " << SS(tag) << " " << SS(alternative.Description) << " action -M 'r:|[_-]=* r:|=*'"; - } else if (action.StartsWith("(") && action.EndsWith(")")) { - L << "action=" << action << ""; - L << "_describe -t " << SS(tag) << " " << SS(alternative.Description) << " action -M 'r:|[_-]=* r:|=*'"; - } else if (action.StartsWith(' ')) { - L << action.substr(1); - } else { - L << "_description " << SS(tag) << " expl " << SS(alternative.Description); - TStringBuf word, args; - action.Split(' ', word, args); - L << word << " \"${expl[@]}\" " << args; - } - } - } - - private: - TVector<TAlternative> Alternatives_; - }; - - ICompleterPtr Alternative(TVector<TAlternative> alternatives) { - return MakeSimpleShared<TAlternativeCompleter>(std::move(alternatives)); - } - - class TSimpleCompleter: public ICompleter { - public: - TSimpleCompleter(TString bashCode, TString action) - : BashCode(std::move(bashCode)) - , Action(std::move(action)) - { - } - - void GenerateBash(TFormattedOutput& out) const override { - if (BashCode) { - L << BashCode; - } - } - - TStringBuf GenerateZshAction(TCompleterManager&) const override { - return Action; - } - - void GenerateZsh(TFormattedOutput&, TCompleterManager&) const override { - Y_FAIL("unreachable"); - } - - private: - TString BashCode; - TString Action; - }; - - ICompleterPtr Choice(TVector<TChoice> choices) { - auto bash = TStringBuilder() << "COMPREPLY+=( $(compgen -W '"; - TStringBuf sep = ""; - for (auto& choice : choices) { - bash << sep << B(choice.Choice); - sep = " "; - } - bash << "' -- ${cur}) )"; - - auto action = TStringBuilder(); - action << "(("; - for (auto& choice: choices) { - action << " " << SS(choice.Choice); - if (choice.Description) {{ - action << ":" << SS(choice.Description); - }} - } - action << "))"; - return MakeSimpleShared<TSimpleCompleter>(bash, action); - } - - TString Compgen(TStringBuf flags) { - return TStringBuilder() << "COMPREPLY+=( $(compgen " << flags << " -- ${cur}) )"; - } - - ICompleterPtr Default() { - return MakeSimpleShared<TSimpleCompleter>("", "_default"); - } - - ICompleterPtr File(TString pattern) { - if (pattern) { - pattern = " -g " + SS(pattern); - } - return MakeSimpleShared<TSimpleCompleter>("", "_files" + pattern); - } - - ICompleterPtr Directory() { - return MakeSimpleShared<TSimpleCompleter>("", "_files -/"); - } - - ICompleterPtr Host() { - return MakeSimpleShared<TSimpleCompleter>(Compgen("-A hostname"), "_hosts"); - } - - ICompleterPtr Pid() { - return MakeSimpleShared<TSimpleCompleter>("", "_pids"); - } - - ICompleterPtr User() { - return MakeSimpleShared<TSimpleCompleter>(Compgen("-A user"), "_users"); - } - - ICompleterPtr Group() { - // For some reason, OSX freezes when trying to perform completion for groups. - // You can try removing this ifdef and debugging it, but be prepared to force-shutdown your machine - // (and possibly reinstall OSX if force-shutdown breaks anything). -#ifdef _darwin_ - return MakeSimpleShared<TSimpleCompleter>("", ""); -#else - return MakeSimpleShared<TSimpleCompleter>(Compgen("-A group"), "_groups"); -#endif - } - - ICompleterPtr Url() { - return MakeSimpleShared<TSimpleCompleter>("", "_urls"); - } - - ICompleterPtr Tty() { - return MakeSimpleShared<TSimpleCompleter>("", "_ttys"); - } - - ICompleterPtr NetInterface() { - return MakeSimpleShared<TSimpleCompleter>("", "_net_interfaces"); - } - - ICompleterPtr TimeZone() { - return MakeSimpleShared<TSimpleCompleter>("", "_time_zone"); - } - - ICompleterPtr Signal() { - return MakeSimpleShared<TSimpleCompleter>(Compgen("-A signal"), "_signals"); - } - - ICompleterPtr Domain() { - return MakeSimpleShared<TSimpleCompleter>("", "_domains"); - } - - namespace { - TCustomCompleter* Head = nullptr; +#include "completer.h" + +#include "completion_generator.h" + +#include <util/string/cast.h> +#include <util/generic/fwd.h> + +using NLastGetopt::NEscaping::Q; +using NLastGetopt::NEscaping::QQ; +using NLastGetopt::NEscaping::C; +using NLastGetopt::NEscaping::CC; +using NLastGetopt::NEscaping::S; +using NLastGetopt::NEscaping::SS; +using NLastGetopt::NEscaping::B; +using NLastGetopt::NEscaping::BB; + +namespace NLastGetopt::NComp { +#define L out.Line() +#define I auto Y_GENERATE_UNIQUE_ID(indent) = out.Indent() + + TCompleterManager::TCompleterManager(TStringBuf command) + : Command_(command) + , Id_(0) + { + } + + TStringBuf TCompleterManager::GetCompleterID(const ICompleter* completer) { + return Queue_.emplace_back(TStringBuilder() << "_" << Command_ << "__completer_" << ++Id_, completer).first; + } + + void TCompleterManager::GenerateZsh(TFormattedOutput& out) { + while (!Queue_.empty()) { + auto[name, completer] = Queue_.back(); + Queue_.pop_back(); + + L << "(( $+functions[" << name << "] )) ||"; + L << name << "() {"; + { + I; + completer->GenerateZsh(out, *this); + } + L << "}"; + L; + } + } + + class TAlternativeCompleter: public ICompleter { + public: + TAlternativeCompleter(TVector<TAlternative> alternatives) + : Alternatives_(std::move(alternatives)) + { + } + + void GenerateBash(TFormattedOutput& out) const override { + for (auto& alternative: Alternatives_) { + if (alternative.Completer != nullptr) { + alternative.Completer->GenerateBash(out); + } + } + } + + TStringBuf GenerateZshAction(TCompleterManager& manager) const override { + return manager.GetCompleterID(this); + } + + void GenerateZsh(TFormattedOutput& out, TCompleterManager& manager) const override { + // We should use '_alternative' here, but it doesn't process escape codes in group descriptions, + // so we dispatch alternatives ourselves. + + L << "local expl action"; + + size_t i = 0; + for (auto& alternative: Alternatives_) { + auto tag = "alt-" + ToString(++i); + auto action = alternative.Completer ? alternative.Completer->GenerateZshAction(manager) : TStringBuf(); + + L; + + if (action.empty()) { + L << "_message -e " << SS(tag) << " " << SS(alternative.Description); + } else if (action.StartsWith("((") && action.EndsWith("))")) { + L << "action=" << action.substr(1, action.size() - 2); + L << "_describe -t " << SS(tag) << " " << SS(alternative.Description) << " action -M 'r:|[_-]=* r:|=*'"; + } else if (action.StartsWith("(") && action.EndsWith(")")) { + L << "action=" << action << ""; + L << "_describe -t " << SS(tag) << " " << SS(alternative.Description) << " action -M 'r:|[_-]=* r:|=*'"; + } else if (action.StartsWith(' ')) { + L << action.substr(1); + } else { + L << "_description " << SS(tag) << " expl " << SS(alternative.Description); + TStringBuf word, args; + action.Split(' ', word, args); + L << word << " \"${expl[@]}\" " << args; + } + } + } + + private: + TVector<TAlternative> Alternatives_; + }; + + ICompleterPtr Alternative(TVector<TAlternative> alternatives) { + return MakeSimpleShared<TAlternativeCompleter>(std::move(alternatives)); + } + + class TSimpleCompleter: public ICompleter { + public: + TSimpleCompleter(TString bashCode, TString action) + : BashCode(std::move(bashCode)) + , Action(std::move(action)) + { + } + + void GenerateBash(TFormattedOutput& out) const override { + if (BashCode) { + L << BashCode; + } + } + + TStringBuf GenerateZshAction(TCompleterManager&) const override { + return Action; + } + + void GenerateZsh(TFormattedOutput&, TCompleterManager&) const override { + Y_FAIL("unreachable"); + } + + private: + TString BashCode; + TString Action; + }; + + ICompleterPtr Choice(TVector<TChoice> choices) { + auto bash = TStringBuilder() << "COMPREPLY+=( $(compgen -W '"; + TStringBuf sep = ""; + for (auto& choice : choices) { + bash << sep << B(choice.Choice); + sep = " "; + } + bash << "' -- ${cur}) )"; + + auto action = TStringBuilder(); + action << "(("; + for (auto& choice: choices) { + action << " " << SS(choice.Choice); + if (choice.Description) {{ + action << ":" << SS(choice.Description); + }} + } + action << "))"; + return MakeSimpleShared<TSimpleCompleter>(bash, action); + } + + TString Compgen(TStringBuf flags) { + return TStringBuilder() << "COMPREPLY+=( $(compgen " << flags << " -- ${cur}) )"; + } + + ICompleterPtr Default() { + return MakeSimpleShared<TSimpleCompleter>("", "_default"); + } + + ICompleterPtr File(TString pattern) { + if (pattern) { + pattern = " -g " + SS(pattern); + } + return MakeSimpleShared<TSimpleCompleter>("", "_files" + pattern); + } + + ICompleterPtr Directory() { + return MakeSimpleShared<TSimpleCompleter>("", "_files -/"); + } + + ICompleterPtr Host() { + return MakeSimpleShared<TSimpleCompleter>(Compgen("-A hostname"), "_hosts"); + } + + ICompleterPtr Pid() { + return MakeSimpleShared<TSimpleCompleter>("", "_pids"); + } + + ICompleterPtr User() { + return MakeSimpleShared<TSimpleCompleter>(Compgen("-A user"), "_users"); + } + + ICompleterPtr Group() { + // For some reason, OSX freezes when trying to perform completion for groups. + // You can try removing this ifdef and debugging it, but be prepared to force-shutdown your machine + // (and possibly reinstall OSX if force-shutdown breaks anything). +#ifdef _darwin_ + return MakeSimpleShared<TSimpleCompleter>("", ""); +#else + return MakeSimpleShared<TSimpleCompleter>(Compgen("-A group"), "_groups"); +#endif + } + + ICompleterPtr Url() { + return MakeSimpleShared<TSimpleCompleter>("", "_urls"); + } + + ICompleterPtr Tty() { + return MakeSimpleShared<TSimpleCompleter>("", "_ttys"); + } + + ICompleterPtr NetInterface() { + return MakeSimpleShared<TSimpleCompleter>("", "_net_interfaces"); + } + + ICompleterPtr TimeZone() { + return MakeSimpleShared<TSimpleCompleter>("", "_time_zone"); + } + + ICompleterPtr Signal() { + return MakeSimpleShared<TSimpleCompleter>(Compgen("-A signal"), "_signals"); + } + + ICompleterPtr Domain() { + return MakeSimpleShared<TSimpleCompleter>("", "_domains"); + } + + namespace { + TCustomCompleter* Head = nullptr; TStringBuf SpecialFlag = "---CUSTOM-COMPLETION---"; - } - - void TCustomCompleter::FireCustomCompleter(int argc, const char** argv) { - if (!argc) { - return; - } - - for (int i = 1; i < argc - 4; ++i) { - if (SpecialFlag == argv[i]) { - auto name = TStringBuf(argv[i + 1]); - auto curIdx = FromString<int>(argv[i + 2]); - auto prefix = TStringBuf(argv[i + 3]); - auto suffix = TStringBuf(argv[i + 4]); - - auto cur = TStringBuf(); - if (0 <= curIdx && curIdx < i) { - cur = TStringBuf(argv[curIdx]); - } - if (cur && !prefix && !suffix) { - prefix = cur; // bash does not send prefix and suffix - } - - auto head = Head; - while (head) { - if (head->GetUniqueName() == name) { - head->GenerateCompletions(i, argv, curIdx, cur, prefix, suffix); - } - head = head->Next_; - } - - exit(0); - } - } - } - - void TCustomCompleter::RegisterCustomCompleter(TCustomCompleter* completer) noexcept { - Y_VERIFY(completer); - completer->Next_ = Head; - Head = completer; - } - - void TCustomCompleter::AddCompletion(TStringBuf completion) { - Cout << completion << Endl; // this was easy =) - // TODO: support option descriptions and messages - } - - void TMultipartCustomCompleter::GenerateCompletions(int argc, const char** argv, int curIdx, TStringBuf cur, TStringBuf prefix, TStringBuf suffix) { - auto root = TStringBuf(); - if (prefix.Contains(Sep_)) { - auto tmp = TStringBuf(); - prefix.RSplit(Sep_, root, tmp); - } - - if (root) { - Cout << root << Sep_ << Endl; - } else { - Cout << Endl; - } - - Cout << Sep_ << Endl; - - GenerateCompletionParts(argc, argv, curIdx, cur, prefix, suffix, root); - } - - class TLaunchSelf: public ICompleter { - public: - TLaunchSelf(TCustomCompleter* completer) - : Completer_(completer) - { - } - - void GenerateBash(TFormattedOutput& out) const override { - L << "IFS=$'\\n'"; + } + + void TCustomCompleter::FireCustomCompleter(int argc, const char** argv) { + if (!argc) { + return; + } + + for (int i = 1; i < argc - 4; ++i) { + if (SpecialFlag == argv[i]) { + auto name = TStringBuf(argv[i + 1]); + auto curIdx = FromString<int>(argv[i + 2]); + auto prefix = TStringBuf(argv[i + 3]); + auto suffix = TStringBuf(argv[i + 4]); + + auto cur = TStringBuf(); + if (0 <= curIdx && curIdx < i) { + cur = TStringBuf(argv[curIdx]); + } + if (cur && !prefix && !suffix) { + prefix = cur; // bash does not send prefix and suffix + } + + auto head = Head; + while (head) { + if (head->GetUniqueName() == name) { + head->GenerateCompletions(i, argv, curIdx, cur, prefix, suffix); + } + head = head->Next_; + } + + exit(0); + } + } + } + + void TCustomCompleter::RegisterCustomCompleter(TCustomCompleter* completer) noexcept { + Y_VERIFY(completer); + completer->Next_ = Head; + Head = completer; + } + + void TCustomCompleter::AddCompletion(TStringBuf completion) { + Cout << completion << Endl; // this was easy =) + // TODO: support option descriptions and messages + } + + void TMultipartCustomCompleter::GenerateCompletions(int argc, const char** argv, int curIdx, TStringBuf cur, TStringBuf prefix, TStringBuf suffix) { + auto root = TStringBuf(); + if (prefix.Contains(Sep_)) { + auto tmp = TStringBuf(); + prefix.RSplit(Sep_, root, tmp); + } + + if (root) { + Cout << root << Sep_ << Endl; + } else { + Cout << Endl; + } + + Cout << Sep_ << Endl; + + GenerateCompletionParts(argc, argv, curIdx, cur, prefix, suffix, root); + } + + class TLaunchSelf: public ICompleter { + public: + TLaunchSelf(TCustomCompleter* completer) + : Completer_(completer) + { + } + + void GenerateBash(TFormattedOutput& out) const override { + L << "IFS=$'\\n'"; L << "COMPREPLY+=( $(compgen -W \"$(${words[@]} " << SpecialFlag << " " << Completer_->GetUniqueName() << " \"${cword}\" \"\" \"\" 2> /dev/null)\" -- ${cur}) )"; - L << "IFS=$' \\t\\n'"; - } - - TStringBuf GenerateZshAction(TCompleterManager& manager) const override { - return manager.GetCompleterID(this); - } - - void GenerateZsh(TFormattedOutput& out, TCompleterManager&) const override { + L << "IFS=$' \\t\\n'"; + } + + TStringBuf GenerateZshAction(TCompleterManager& manager) const override { + return manager.GetCompleterID(this); + } + + void GenerateZsh(TFormattedOutput& out, TCompleterManager&) const override { L << "compadd ${@} ${expl[@]} -- \"${(@f)$(${words_orig[@]} " << SpecialFlag << " " << Completer_->GetUniqueName() << " \"${current_orig}\" \"${prefix_orig}\" \"${suffix_orig}\" 2> /dev/null)}\""; - } - - private: - TCustomCompleter* Completer_; - }; - - ICompleterPtr LaunchSelf(TCustomCompleter& completer) { - return MakeSimpleShared<TLaunchSelf>(&completer); - } - - class TLaunchSelfMultiPart: public ICompleter { - public: - TLaunchSelfMultiPart(TCustomCompleter* completer) - : Completer_(completer) - { - } - - void GenerateBash(TFormattedOutput& out) const override { - L << "IFS=$'\\n'"; + } + + private: + TCustomCompleter* Completer_; + }; + + ICompleterPtr LaunchSelf(TCustomCompleter& completer) { + return MakeSimpleShared<TLaunchSelf>(&completer); + } + + class TLaunchSelfMultiPart: public ICompleter { + public: + TLaunchSelfMultiPart(TCustomCompleter* completer) + : Completer_(completer) + { + } + + void GenerateBash(TFormattedOutput& out) const override { + L << "IFS=$'\\n'"; L << "items=( $(${words[@]} " << SpecialFlag << " " << Completer_->GetUniqueName() << " \"${cword}\" \"\" \"\" 2> /dev/null) )"; - L << "candidates=$(compgen -W \"${items[*]:1}\" -- \"$cur\")"; - L << "COMPREPLY+=( $candidates )"; - L << "[[ $candidates == *\"${items[1]}\" ]] && need_space=\"\""; - L << "IFS=$' \\t\\n'"; - } - - TStringBuf GenerateZshAction(TCompleterManager& manager) const override { - return manager.GetCompleterID(this); - } - - void GenerateZsh(TFormattedOutput& out, TCompleterManager&) const override { + L << "candidates=$(compgen -W \"${items[*]:1}\" -- \"$cur\")"; + L << "COMPREPLY+=( $candidates )"; + L << "[[ $candidates == *\"${items[1]}\" ]] && need_space=\"\""; + L << "IFS=$' \\t\\n'"; + } + + TStringBuf GenerateZshAction(TCompleterManager& manager) const override { + return manager.GetCompleterID(this); + } + + void GenerateZsh(TFormattedOutput& out, TCompleterManager&) const override { L << "local items=( \"${(@f)$(${words_orig[@]} " << SpecialFlag << " " << Completer_->GetUniqueName() << " \"${current_orig}\" \"${prefix_orig}\" \"${suffix_orig}\" 2> /dev/null)}\" )"; - L; - L << "local rempat=${items[1]}"; - L << "shift items"; - L; - L << "local sep=${items[1]}"; - L << "shift items"; - L; - L << "local files=( ${items:#*\"${sep}\"} )"; - L << "local filenames=( ${files#\"${rempat}\"} )"; - L << "local dirs=( ${(M)items:#*\"${sep}\"} )"; - L << "local dirnames=( ${dirs#\"${rempat}\"} )"; - L; - L << "local need_suf"; - L << "compset -S \"${sep}*\" || need_suf=\"1\""; - L; - L << "compadd ${@} ${expl[@]} -d filenames -- ${(q)files}"; - L << "compadd ${@} ${expl[@]} ${need_suf:+-S\"${sep}\"} -q -d dirnames -- ${(q)dirs%\"${sep}\"}"; - } - - private: - TCustomCompleter* Completer_; - }; - - ICompleterPtr LaunchSelfMultiPart(TCustomCompleter& completer) { - return MakeSimpleShared<TLaunchSelfMultiPart>(&completer); - } - -#undef I -#undef L -} + L; + L << "local rempat=${items[1]}"; + L << "shift items"; + L; + L << "local sep=${items[1]}"; + L << "shift items"; + L; + L << "local files=( ${items:#*\"${sep}\"} )"; + L << "local filenames=( ${files#\"${rempat}\"} )"; + L << "local dirs=( ${(M)items:#*\"${sep}\"} )"; + L << "local dirnames=( ${dirs#\"${rempat}\"} )"; + L; + L << "local need_suf"; + L << "compset -S \"${sep}*\" || need_suf=\"1\""; + L; + L << "compadd ${@} ${expl[@]} -d filenames -- ${(q)files}"; + L << "compadd ${@} ${expl[@]} ${need_suf:+-S\"${sep}\"} -q -d dirnames -- ${(q)dirs%\"${sep}\"}"; + } + + private: + TCustomCompleter* Completer_; + }; + + ICompleterPtr LaunchSelfMultiPart(TCustomCompleter& completer) { + return MakeSimpleShared<TLaunchSelfMultiPart>(&completer); + } + +#undef I +#undef L +} diff --git a/library/cpp/getopt/small/completer.h b/library/cpp/getopt/small/completer.h index 6a2fdbbbf4..4136f13add 100644 --- a/library/cpp/getopt/small/completer.h +++ b/library/cpp/getopt/small/completer.h @@ -1,306 +1,306 @@ -#pragma once - -#include "formatted_output.h" - -#include <util/generic/strbuf.h> -#include <util/generic/hash.h> - -#include <utility> -#include <util/generic/fwd.h> - -namespace NLastGetopt::NComp { - class ICompleter; - - class TCompleterManager { - public: - TCompleterManager(TStringBuf command); - - /// Register new completer and get its function name. - TStringBuf GetCompleterID(const ICompleter* completer); - - /// Generate zsh code for all collected completers. - void GenerateZsh(TFormattedOutput& out); - - private: - TStringBuf Command_; - size_t Id_; - TVector<std::pair<TString, const ICompleter*>> Queue_; - }; - - class ICompleter { - public: - virtual ~ICompleter() = default; - - public: - /// Generate arbitrary bash code that modifies `COMPREPLY`. - virtual void GenerateBash(TFormattedOutput& out) const = 0; - - /// Generate action that will be used with `_arguments`. If this completer requires a separate function, - /// register it in the given manager and return function name assigned by manager. - /// Supported forms are '()', '(items...)', '((items...))', 'command ...' and ' command ...'. - /// Other forms, such as '{eval-string}', '->state', '=action' are not supported. - virtual TStringBuf GenerateZshAction(TCompleterManager& manager) const = 0; - - /// Generate body of a zsh function (if Action points to a custom function). - virtual void GenerateZsh(TFormattedOutput& out, TCompleterManager& manager) const = 0; - }; - - using ICompleterPtr = TSimpleSharedPtr<ICompleter>; - - /// Generate default completions. - /// Output of this completer depends on shell settings. - /// Usually ut generates file paths. - ICompleterPtr Default(); - - struct TAlternative { - /// Description for this group of completions. Leave empty to use completer's default description. - TString Description; - - /// Completer that generates values - ICompleterPtr Completer; - - TAlternative(ICompleterPtr completer) - : Description("") - , Completer(std::move(completer)) - { - } - - TAlternative(TString description, ICompleterPtr completer) - : Description(std::move(description)) - , Completer(std::move(completer)) - { - } - }; - - /// Run multiple completers and unite their output. - /// Each completer's output placed in a separate group with its own description. - ICompleterPtr Alternative(TVector<TAlternative> alternatives); - - struct TChoice { - /// Option value. - TString Choice; - - /// Description for a value. - TString Description = ""; - - TChoice(TString choice) - : Choice(std::move(choice)) - { - } - - TChoice(TString choice, TString description) - : Choice(std::move(choice)) - , Description(std::move(description)) - { - } - }; - - /// Complete items from a predefined list of choices. - ICompleterPtr Choice(TVector<TChoice> choices); - - /// Complete files and directories. May filter results by pattern, e.g. `*.txt`. - ICompleterPtr File(TString pattern= ""); - - /// Complete directories. - ICompleterPtr Directory(); - - /// Complete hosts. - ICompleterPtr Host(); - - /// Complete process IDs. - ICompleterPtr Pid(); - - /// Complete users that're found in the system. - ICompleterPtr User(); - - /// Complete user groups that're found in the system. - /// N: for some reason, - ICompleterPtr Group(); - - /// Complete URLs. - ICompleterPtr Url(); - - /// Complete TTY interfaces. - ICompleterPtr Tty(); - - /// Complete network interfaces. - ICompleterPtr NetInterface(); - - /// Complete timezone identifiers. - ICompleterPtr TimeZone(); - - /// Complete unix signal identifiers, e.g. `ABRT` or `KILL`. - ICompleterPtr Signal(); - - /// Complete domains. - ICompleterPtr Domain(); - - /// Custom completer. See `LaunchSelf` below. - class TCustomCompleter { - public: - static void FireCustomCompleter(int argc, const char** argv); - static void RegisterCustomCompleter(TCustomCompleter* completer) noexcept; - - struct TReg { - TReg(TCustomCompleter* completer) noexcept { - TCustomCompleter::RegisterCustomCompleter(completer); - } - }; - - public: - virtual ~TCustomCompleter() = default; - - public: - /// @param argc total number of command line arguments. - /// @param argv array of command line arguments. - /// @param curIdx index of the currently completed argument, may be equal to `argc` if cursor is at the end - /// of line. - /// @param cur currently completed argument. - /// @param prefix part of the currently completed argument before the cursor. - /// @param suffix part of the currently completed argument after the cursor. - virtual void GenerateCompletions(int argc, const char** argv, int curIdx, TStringBuf cur, TStringBuf prefix, TStringBuf suffix) = 0; - virtual TStringBuf GetUniqueName() const = 0; - - protected: - void AddCompletion(TStringBuf completion); - - private: - TCustomCompleter* Next_ = nullptr; - }; - - /// Custom completer for objects that consist of multiple parts split by a common separator, such as file paths. - class TMultipartCustomCompleter: public TCustomCompleter { - public: - TMultipartCustomCompleter(TStringBuf sep) - : Sep_(sep) - { - Y_VERIFY(!Sep_.empty()); - } - - public: - void GenerateCompletions(int argc, const char** argv, int curIdx, TStringBuf cur, TStringBuf prefix, TStringBuf suffix) final; - - public: - /// @param argc same as in `GenerateCompletions`. - /// @param argv same as in `GenerateCompletions`. - /// @param curIdx same as in `GenerateCompletions`. - /// @param cur same as in `GenerateCompletions`. - /// @param prefix same as in `GenerateCompletions`. - /// @param suffix same as in `GenerateCompletions`. - /// @param root part of the currently completed argument before the last separator. - virtual void GenerateCompletionParts(int argc, const char** argv, int curIdx, TStringBuf cur, TStringBuf prefix, TStringBuf suffix, TStringBuf root) = 0; - - protected: - TStringBuf Sep_; - }; - -#define Y_COMPLETER(N) \ -class T##N: public ::NLastGetopt::NComp::TCustomCompleter { \ - public: \ - void GenerateCompletions(int, const char**, int, TStringBuf, TStringBuf, TStringBuf) override; \ - TStringBuf GetUniqueName() const override { return #N; } \ - }; \ - T##N N = T##N(); \ - ::NLastGetopt::NComp::TCustomCompleter::TReg _Reg_##N = &N; \ - void T##N::GenerateCompletions( \ - Y_DECLARE_UNUSED int argc, \ - Y_DECLARE_UNUSED const char** argv, \ - Y_DECLARE_UNUSED int curIdx, \ - Y_DECLARE_UNUSED TStringBuf cur, \ - Y_DECLARE_UNUSED TStringBuf prefix, \ - Y_DECLARE_UNUSED TStringBuf suffix) - -#define Y_MULTIPART_COMPLETER(N, SEP) \ -class T##N: public ::NLastGetopt::NComp::TMultipartCustomCompleter { \ - public: \ - T##N() : ::NLastGetopt::NComp::TMultipartCustomCompleter(SEP) {} \ - void GenerateCompletionParts(int, const char**, int, TStringBuf, TStringBuf, TStringBuf, TStringBuf) override; \ - TStringBuf GetUniqueName() const override { return #N; } \ - }; \ - T##N N = T##N(); \ - ::NLastGetopt::NComp::TCustomCompleter::TReg _Reg_##N = &N; \ - void T##N::GenerateCompletionParts( \ - Y_DECLARE_UNUSED int argc, \ - Y_DECLARE_UNUSED const char** argv, \ - Y_DECLARE_UNUSED int curIdx, \ - Y_DECLARE_UNUSED TStringBuf cur, \ - Y_DECLARE_UNUSED TStringBuf prefix, \ - Y_DECLARE_UNUSED TStringBuf suffix, \ - Y_DECLARE_UNUSED TStringBuf root) - - /// Launches this binary with a specially formed flags and retrieves completions from stdout. - /// - /// Your application must be set up in a certain way for this to work. - /// - /// First, create a custom completer: - /// - /// ``` - /// Y_COMPLETER(SomeUniqueName) { - /// AddCompletion("foo"); - /// AddCompletion("bar"); - /// AddCompletion("baz"); - /// } - /// ``` - /// - /// Then, use it with this function. - /// - /// On completion attempt, completer will call your binary with some special arguments. - /// - /// In your main, before any other logic, call `TCustomCompleter::FireCustomCompleter`. This function will - /// check for said special arguments and invoke the right completer: - /// - /// ``` - /// int main(int argc, const char** argv) { - /// TCustomCompleter::FireCustomCompleter(argc, argv); - /// ... - /// } - /// ``` - ICompleterPtr LaunchSelf(TCustomCompleter& completer); - - /// Launches this binary with a specially formed flags and retrieves completions from stdout. - /// - /// Your application must be set up in a certain way for this to work. See `LaunchSelf` for more info. - /// - /// Multipart completion is designed for objects that consist of multiple parts split by a common separator. - /// It is ideal for completing remote file paths, for example. - /// - /// Multipart completers format stdout in the following way. - /// - /// On the first line, they print a common prefix that should be stripped from all completions. For example, - /// if you complete paths, this prefix would be a common directory. - /// - /// On the second line, they print a separator. If some completion ends with this separator, shell will not add - /// a whitespace after the item is completed. For example, if you complete paths, the separator would be a slash. - /// - /// On the following lines, they print completions, as formed by the `AddCompletion` function. - /// - /// For example, if a user invokes completion like this: - /// - /// ``` - /// $ program //home/logs/<tab><tab> - /// ``` - /// - /// The stdout might look like this: - /// - /// ``` - /// //home/logs/ - /// / - /// //home/logs/access-log - /// //home/logs/redir-log - /// //home/logs/blockstat-log - /// ``` - /// - /// Then autocompletion will look like this: - /// - /// ``` - /// $ program //home/logs/<tab><tab> - /// -- yt path -- - /// access-log redir-log blockstat-log - /// ``` - /// - /// Note: stdout lines with completion suggestions *must* be formatted by the `AddCompletion` function - /// because their format may change in the future. - /// - /// Note: we recommend using `Y_MULTIPART_COMPLETER` because it will handle all stdout printing for you. - ICompleterPtr LaunchSelfMultiPart(TCustomCompleter& completer); -} +#pragma once + +#include "formatted_output.h" + +#include <util/generic/strbuf.h> +#include <util/generic/hash.h> + +#include <utility> +#include <util/generic/fwd.h> + +namespace NLastGetopt::NComp { + class ICompleter; + + class TCompleterManager { + public: + TCompleterManager(TStringBuf command); + + /// Register new completer and get its function name. + TStringBuf GetCompleterID(const ICompleter* completer); + + /// Generate zsh code for all collected completers. + void GenerateZsh(TFormattedOutput& out); + + private: + TStringBuf Command_; + size_t Id_; + TVector<std::pair<TString, const ICompleter*>> Queue_; + }; + + class ICompleter { + public: + virtual ~ICompleter() = default; + + public: + /// Generate arbitrary bash code that modifies `COMPREPLY`. + virtual void GenerateBash(TFormattedOutput& out) const = 0; + + /// Generate action that will be used with `_arguments`. If this completer requires a separate function, + /// register it in the given manager and return function name assigned by manager. + /// Supported forms are '()', '(items...)', '((items...))', 'command ...' and ' command ...'. + /// Other forms, such as '{eval-string}', '->state', '=action' are not supported. + virtual TStringBuf GenerateZshAction(TCompleterManager& manager) const = 0; + + /// Generate body of a zsh function (if Action points to a custom function). + virtual void GenerateZsh(TFormattedOutput& out, TCompleterManager& manager) const = 0; + }; + + using ICompleterPtr = TSimpleSharedPtr<ICompleter>; + + /// Generate default completions. + /// Output of this completer depends on shell settings. + /// Usually ut generates file paths. + ICompleterPtr Default(); + + struct TAlternative { + /// Description for this group of completions. Leave empty to use completer's default description. + TString Description; + + /// Completer that generates values + ICompleterPtr Completer; + + TAlternative(ICompleterPtr completer) + : Description("") + , Completer(std::move(completer)) + { + } + + TAlternative(TString description, ICompleterPtr completer) + : Description(std::move(description)) + , Completer(std::move(completer)) + { + } + }; + + /// Run multiple completers and unite their output. + /// Each completer's output placed in a separate group with its own description. + ICompleterPtr Alternative(TVector<TAlternative> alternatives); + + struct TChoice { + /// Option value. + TString Choice; + + /// Description for a value. + TString Description = ""; + + TChoice(TString choice) + : Choice(std::move(choice)) + { + } + + TChoice(TString choice, TString description) + : Choice(std::move(choice)) + , Description(std::move(description)) + { + } + }; + + /// Complete items from a predefined list of choices. + ICompleterPtr Choice(TVector<TChoice> choices); + + /// Complete files and directories. May filter results by pattern, e.g. `*.txt`. + ICompleterPtr File(TString pattern= ""); + + /// Complete directories. + ICompleterPtr Directory(); + + /// Complete hosts. + ICompleterPtr Host(); + + /// Complete process IDs. + ICompleterPtr Pid(); + + /// Complete users that're found in the system. + ICompleterPtr User(); + + /// Complete user groups that're found in the system. + /// N: for some reason, + ICompleterPtr Group(); + + /// Complete URLs. + ICompleterPtr Url(); + + /// Complete TTY interfaces. + ICompleterPtr Tty(); + + /// Complete network interfaces. + ICompleterPtr NetInterface(); + + /// Complete timezone identifiers. + ICompleterPtr TimeZone(); + + /// Complete unix signal identifiers, e.g. `ABRT` or `KILL`. + ICompleterPtr Signal(); + + /// Complete domains. + ICompleterPtr Domain(); + + /// Custom completer. See `LaunchSelf` below. + class TCustomCompleter { + public: + static void FireCustomCompleter(int argc, const char** argv); + static void RegisterCustomCompleter(TCustomCompleter* completer) noexcept; + + struct TReg { + TReg(TCustomCompleter* completer) noexcept { + TCustomCompleter::RegisterCustomCompleter(completer); + } + }; + + public: + virtual ~TCustomCompleter() = default; + + public: + /// @param argc total number of command line arguments. + /// @param argv array of command line arguments. + /// @param curIdx index of the currently completed argument, may be equal to `argc` if cursor is at the end + /// of line. + /// @param cur currently completed argument. + /// @param prefix part of the currently completed argument before the cursor. + /// @param suffix part of the currently completed argument after the cursor. + virtual void GenerateCompletions(int argc, const char** argv, int curIdx, TStringBuf cur, TStringBuf prefix, TStringBuf suffix) = 0; + virtual TStringBuf GetUniqueName() const = 0; + + protected: + void AddCompletion(TStringBuf completion); + + private: + TCustomCompleter* Next_ = nullptr; + }; + + /// Custom completer for objects that consist of multiple parts split by a common separator, such as file paths. + class TMultipartCustomCompleter: public TCustomCompleter { + public: + TMultipartCustomCompleter(TStringBuf sep) + : Sep_(sep) + { + Y_VERIFY(!Sep_.empty()); + } + + public: + void GenerateCompletions(int argc, const char** argv, int curIdx, TStringBuf cur, TStringBuf prefix, TStringBuf suffix) final; + + public: + /// @param argc same as in `GenerateCompletions`. + /// @param argv same as in `GenerateCompletions`. + /// @param curIdx same as in `GenerateCompletions`. + /// @param cur same as in `GenerateCompletions`. + /// @param prefix same as in `GenerateCompletions`. + /// @param suffix same as in `GenerateCompletions`. + /// @param root part of the currently completed argument before the last separator. + virtual void GenerateCompletionParts(int argc, const char** argv, int curIdx, TStringBuf cur, TStringBuf prefix, TStringBuf suffix, TStringBuf root) = 0; + + protected: + TStringBuf Sep_; + }; + +#define Y_COMPLETER(N) \ +class T##N: public ::NLastGetopt::NComp::TCustomCompleter { \ + public: \ + void GenerateCompletions(int, const char**, int, TStringBuf, TStringBuf, TStringBuf) override; \ + TStringBuf GetUniqueName() const override { return #N; } \ + }; \ + T##N N = T##N(); \ + ::NLastGetopt::NComp::TCustomCompleter::TReg _Reg_##N = &N; \ + void T##N::GenerateCompletions( \ + Y_DECLARE_UNUSED int argc, \ + Y_DECLARE_UNUSED const char** argv, \ + Y_DECLARE_UNUSED int curIdx, \ + Y_DECLARE_UNUSED TStringBuf cur, \ + Y_DECLARE_UNUSED TStringBuf prefix, \ + Y_DECLARE_UNUSED TStringBuf suffix) + +#define Y_MULTIPART_COMPLETER(N, SEP) \ +class T##N: public ::NLastGetopt::NComp::TMultipartCustomCompleter { \ + public: \ + T##N() : ::NLastGetopt::NComp::TMultipartCustomCompleter(SEP) {} \ + void GenerateCompletionParts(int, const char**, int, TStringBuf, TStringBuf, TStringBuf, TStringBuf) override; \ + TStringBuf GetUniqueName() const override { return #N; } \ + }; \ + T##N N = T##N(); \ + ::NLastGetopt::NComp::TCustomCompleter::TReg _Reg_##N = &N; \ + void T##N::GenerateCompletionParts( \ + Y_DECLARE_UNUSED int argc, \ + Y_DECLARE_UNUSED const char** argv, \ + Y_DECLARE_UNUSED int curIdx, \ + Y_DECLARE_UNUSED TStringBuf cur, \ + Y_DECLARE_UNUSED TStringBuf prefix, \ + Y_DECLARE_UNUSED TStringBuf suffix, \ + Y_DECLARE_UNUSED TStringBuf root) + + /// Launches this binary with a specially formed flags and retrieves completions from stdout. + /// + /// Your application must be set up in a certain way for this to work. + /// + /// First, create a custom completer: + /// + /// ``` + /// Y_COMPLETER(SomeUniqueName) { + /// AddCompletion("foo"); + /// AddCompletion("bar"); + /// AddCompletion("baz"); + /// } + /// ``` + /// + /// Then, use it with this function. + /// + /// On completion attempt, completer will call your binary with some special arguments. + /// + /// In your main, before any other logic, call `TCustomCompleter::FireCustomCompleter`. This function will + /// check for said special arguments and invoke the right completer: + /// + /// ``` + /// int main(int argc, const char** argv) { + /// TCustomCompleter::FireCustomCompleter(argc, argv); + /// ... + /// } + /// ``` + ICompleterPtr LaunchSelf(TCustomCompleter& completer); + + /// Launches this binary with a specially formed flags and retrieves completions from stdout. + /// + /// Your application must be set up in a certain way for this to work. See `LaunchSelf` for more info. + /// + /// Multipart completion is designed for objects that consist of multiple parts split by a common separator. + /// It is ideal for completing remote file paths, for example. + /// + /// Multipart completers format stdout in the following way. + /// + /// On the first line, they print a common prefix that should be stripped from all completions. For example, + /// if you complete paths, this prefix would be a common directory. + /// + /// On the second line, they print a separator. If some completion ends with this separator, shell will not add + /// a whitespace after the item is completed. For example, if you complete paths, the separator would be a slash. + /// + /// On the following lines, they print completions, as formed by the `AddCompletion` function. + /// + /// For example, if a user invokes completion like this: + /// + /// ``` + /// $ program //home/logs/<tab><tab> + /// ``` + /// + /// The stdout might look like this: + /// + /// ``` + /// //home/logs/ + /// / + /// //home/logs/access-log + /// //home/logs/redir-log + /// //home/logs/blockstat-log + /// ``` + /// + /// Then autocompletion will look like this: + /// + /// ``` + /// $ program //home/logs/<tab><tab> + /// -- yt path -- + /// access-log redir-log blockstat-log + /// ``` + /// + /// Note: stdout lines with completion suggestions *must* be formatted by the `AddCompletion` function + /// because their format may change in the future. + /// + /// Note: we recommend using `Y_MULTIPART_COMPLETER` because it will handle all stdout printing for you. + ICompleterPtr LaunchSelfMultiPart(TCustomCompleter& completer); +} diff --git a/library/cpp/getopt/small/completer_command.cpp b/library/cpp/getopt/small/completer_command.cpp index 1a0c793bd5..5e593eec7e 100644 --- a/library/cpp/getopt/small/completer_command.cpp +++ b/library/cpp/getopt/small/completer_command.cpp @@ -1,165 +1,165 @@ -#include "completer_command.h" - -#include "completion_generator.h" -#include "last_getopt.h" -#include "wrap.h" - +#include "completer_command.h" + +#include "completion_generator.h" +#include "last_getopt.h" +#include "wrap.h" + #include <library/cpp/colorizer/colors.h> - -#include <util/string/subst.h> - -namespace NLastGetopt { - TString MakeInfo(TStringBuf command, TStringBuf flag) { - TString info = ( - "This command generates shell script with completion function and prints it to `stdout`, " - "allowing one to re-direct the output to the file of their choosing. " - "Where you place the file will depend on which shell and operating system you are using." - "\n" - "\n" - "\n" - "{B}BASH (Linux){R}:" - "\n" - "\n" - "For system-wide commands, completion files are stored in `/etc/bash_completion.d/`. " - "For user commands, they are stored in `~/.local/share/bash-completion/completions`. " - "So, pipe output of this script to a file in one of these directories:" - "\n" - "\n" - " $ mkdir -p ~/.local/share/bash-completion/completions" - "\n" - " $ {command} {completion} bash >" - "\n" - " ~/.local/share/bash-completion/completions/{command}" - "\n" - "\n" - "You'll need to restart your shell for changes to take effect." - "\n" - "\n" - "\n" - "{B}BASH (OSX){R}:" - "\n" - "\n" - "You'll need `bash-completion` (or `bash-completion@2` if you're using non-default, newer bash) " - "homebrew formula. Completion files are stored in `/usr/local/etc/bash_completion.d`:" - "\n" - "\n" - " $ mkdir -p $(brew --prefix)/etc/bash_completion.d" - "\n" - " $ {command} {completion} bash >" - "\n" - " $(brew --prefix)/etc/bash_completion.d/{command}" - "\n" - "\n" - "Alternatively, just source the script in your `~/.bash_profile`." - "\n" - "\n" - "You'll need to restart your shell for changes to take effect." - "\n" - "\n" - "\n" - "{B}ZSH{R}:" - "\n" - "\n" - "Zsh looks for completion scripts in any directory listed in `$fpath` variable. We recommend placing " - "completions to `~/.zfunc`:" - "\n" - "\n" - " $ mkdir -p ~/.zfunc" - "\n" - " $ {command} {completion} zsh > ~/.zfunc/_{command}" - "\n" - "\n" - "Add the following lines to your `.zshrc` just before `compinit`:" - "\n" - "\n" - " fpath+=~/.zfunc" - "\n" - "\n" - "You'll need to restart your shell for changes to take effect."); - SubstGlobal(info, "{command}", command); - SubstGlobal(info, "{completion}", flag); - SubstGlobal(info, "{B}", NColorizer::StdErr().LightDefault()); - SubstGlobal(info, "{R}", NColorizer::StdErr().Reset()); - return info; - } - - NComp::ICompleterPtr ShellChoiceCompleter() { - return NComp::Choice({{"zsh"}, {"bash"}}); - } - - TOpt MakeCompletionOpt(const TOpts* opts, TString command, TString name) { - return TOpt() - .AddLongName(name) - .Help("generate tab completion script for zsh or bash") - .CompletionHelp("generate tab completion script") - .OptionalArgument("shell-syntax") - .CompletionArgHelp("shell syntax for completion script") - .IfPresentDisableCompletion() - .Completer(ShellChoiceCompleter()) - .Handler1T<TString>([opts, command, name](TStringBuf shell) { - if (shell.empty()) { - Cerr << Wrap(80, MakeInfo(command, "--" + name)) << Endl; - } else if (shell == "bash") { - TBashCompletionGenerator(opts).Generate(command, Cout); - } else if (shell == "zsh") { - TZshCompletionGenerator(opts).Generate(command, Cout); - } else { + +#include <util/string/subst.h> + +namespace NLastGetopt { + TString MakeInfo(TStringBuf command, TStringBuf flag) { + TString info = ( + "This command generates shell script with completion function and prints it to `stdout`, " + "allowing one to re-direct the output to the file of their choosing. " + "Where you place the file will depend on which shell and operating system you are using." + "\n" + "\n" + "\n" + "{B}BASH (Linux){R}:" + "\n" + "\n" + "For system-wide commands, completion files are stored in `/etc/bash_completion.d/`. " + "For user commands, they are stored in `~/.local/share/bash-completion/completions`. " + "So, pipe output of this script to a file in one of these directories:" + "\n" + "\n" + " $ mkdir -p ~/.local/share/bash-completion/completions" + "\n" + " $ {command} {completion} bash >" + "\n" + " ~/.local/share/bash-completion/completions/{command}" + "\n" + "\n" + "You'll need to restart your shell for changes to take effect." + "\n" + "\n" + "\n" + "{B}BASH (OSX){R}:" + "\n" + "\n" + "You'll need `bash-completion` (or `bash-completion@2` if you're using non-default, newer bash) " + "homebrew formula. Completion files are stored in `/usr/local/etc/bash_completion.d`:" + "\n" + "\n" + " $ mkdir -p $(brew --prefix)/etc/bash_completion.d" + "\n" + " $ {command} {completion} bash >" + "\n" + " $(brew --prefix)/etc/bash_completion.d/{command}" + "\n" + "\n" + "Alternatively, just source the script in your `~/.bash_profile`." + "\n" + "\n" + "You'll need to restart your shell for changes to take effect." + "\n" + "\n" + "\n" + "{B}ZSH{R}:" + "\n" + "\n" + "Zsh looks for completion scripts in any directory listed in `$fpath` variable. We recommend placing " + "completions to `~/.zfunc`:" + "\n" + "\n" + " $ mkdir -p ~/.zfunc" + "\n" + " $ {command} {completion} zsh > ~/.zfunc/_{command}" + "\n" + "\n" + "Add the following lines to your `.zshrc` just before `compinit`:" + "\n" + "\n" + " fpath+=~/.zfunc" + "\n" + "\n" + "You'll need to restart your shell for changes to take effect."); + SubstGlobal(info, "{command}", command); + SubstGlobal(info, "{completion}", flag); + SubstGlobal(info, "{B}", NColorizer::StdErr().LightDefault()); + SubstGlobal(info, "{R}", NColorizer::StdErr().Reset()); + return info; + } + + NComp::ICompleterPtr ShellChoiceCompleter() { + return NComp::Choice({{"zsh"}, {"bash"}}); + } + + TOpt MakeCompletionOpt(const TOpts* opts, TString command, TString name) { + return TOpt() + .AddLongName(name) + .Help("generate tab completion script for zsh or bash") + .CompletionHelp("generate tab completion script") + .OptionalArgument("shell-syntax") + .CompletionArgHelp("shell syntax for completion script") + .IfPresentDisableCompletion() + .Completer(ShellChoiceCompleter()) + .Handler1T<TString>([opts, command, name](TStringBuf shell) { + if (shell.empty()) { + Cerr << Wrap(80, MakeInfo(command, "--" + name)) << Endl; + } else if (shell == "bash") { + TBashCompletionGenerator(opts).Generate(command, Cout); + } else if (shell == "zsh") { + TZshCompletionGenerator(opts).Generate(command, Cout); + } else { Cerr << "Unknown shell name " << TString{shell}.Quote() << Endl; - exit(1); - } - exit(0); - }); - } - - class TCompleterMode: public TMainClassArgs { - public: - TCompleterMode(const TModChooser* modChooser, TString command, TString modName) - : Command_(std::move(command)) - , Modes_(modChooser) - , ModName_(std::move(modName)) - { - } - - protected: - void RegisterOptions(NLastGetopt::TOpts& opts) override { - TMainClassArgs::RegisterOptions(opts); - - opts.SetTitle("Generate tab completion scripts for zsh or bash"); - - opts.AddSection("Description", MakeInfo(Command_, ModName_)); - - opts.SetFreeArgsNum(1); - opts.GetFreeArgSpec(0) - .Title("<shell-syntax>") - .Help("shell syntax for completion script (bash or zsh)") - .CompletionArgHelp("shell syntax for completion script") - .Completer(ShellChoiceCompleter()); - } - - int DoRun(NLastGetopt::TOptsParseResult&& parsedOptions) override { - auto arg = parsedOptions.GetFreeArgs()[0]; - arg.to_lower(); - - if (arg == "bash") { - TBashCompletionGenerator(Modes_).Generate(Command_, Cout); - } else if (arg == "zsh") { - TZshCompletionGenerator(Modes_).Generate(Command_, Cout); - } else { - Cerr << "Unknown shell name " << arg.Quote() << Endl; - parsedOptions.PrintUsage(); - return 1; - } - - return 0; - } - - private: - TString Command_; - const TModChooser* Modes_; - TString ModName_; - }; - - THolder<TMainClassArgs> MakeCompletionMod(const TModChooser* modChooser, TString command, TString modName) { + exit(1); + } + exit(0); + }); + } + + class TCompleterMode: public TMainClassArgs { + public: + TCompleterMode(const TModChooser* modChooser, TString command, TString modName) + : Command_(std::move(command)) + , Modes_(modChooser) + , ModName_(std::move(modName)) + { + } + + protected: + void RegisterOptions(NLastGetopt::TOpts& opts) override { + TMainClassArgs::RegisterOptions(opts); + + opts.SetTitle("Generate tab completion scripts for zsh or bash"); + + opts.AddSection("Description", MakeInfo(Command_, ModName_)); + + opts.SetFreeArgsNum(1); + opts.GetFreeArgSpec(0) + .Title("<shell-syntax>") + .Help("shell syntax for completion script (bash or zsh)") + .CompletionArgHelp("shell syntax for completion script") + .Completer(ShellChoiceCompleter()); + } + + int DoRun(NLastGetopt::TOptsParseResult&& parsedOptions) override { + auto arg = parsedOptions.GetFreeArgs()[0]; + arg.to_lower(); + + if (arg == "bash") { + TBashCompletionGenerator(Modes_).Generate(Command_, Cout); + } else if (arg == "zsh") { + TZshCompletionGenerator(Modes_).Generate(Command_, Cout); + } else { + Cerr << "Unknown shell name " << arg.Quote() << Endl; + parsedOptions.PrintUsage(); + return 1; + } + + return 0; + } + + private: + TString Command_; + const TModChooser* Modes_; + TString ModName_; + }; + + THolder<TMainClassArgs> MakeCompletionMod(const TModChooser* modChooser, TString command, TString modName) { return MakeHolder<TCompleterMode>(modChooser, std::move(command), std::move(modName)); - } -} + } +} diff --git a/library/cpp/getopt/small/completer_command.h b/library/cpp/getopt/small/completer_command.h index 4aba6dbfde..974cc4617c 100644 --- a/library/cpp/getopt/small/completer_command.h +++ b/library/cpp/getopt/small/completer_command.h @@ -1,11 +1,11 @@ -#pragma once - -#include "modchooser.h" - -namespace NLastGetopt { - /// Create an option that generates completion. - TOpt MakeCompletionOpt(const TOpts* opts, TString command, TString optName = "completion"); - - /// Create a mode that generates completion. - THolder<TMainClassArgs> MakeCompletionMod(const TModChooser* modChooser, TString command, TString modName = "completion"); -} +#pragma once + +#include "modchooser.h" + +namespace NLastGetopt { + /// Create an option that generates completion. + TOpt MakeCompletionOpt(const TOpts* opts, TString command, TString optName = "completion"); + + /// Create a mode that generates completion. + THolder<TMainClassArgs> MakeCompletionMod(const TModChooser* modChooser, TString command, TString modName = "completion"); +} diff --git a/library/cpp/getopt/small/completion_generator.cpp b/library/cpp/getopt/small/completion_generator.cpp index 0ff83e1c07..ac41988217 100644 --- a/library/cpp/getopt/small/completion_generator.cpp +++ b/library/cpp/getopt/small/completion_generator.cpp @@ -1,791 +1,791 @@ -#include "completion_generator.h" - +#include "completion_generator.h" + #include <util/generic/overloaded.h> - -#include <util/string/ascii.h> -#include <util/generic/hash_set.h> - -#include "last_getopt_parse_result.h" - -using NLastGetopt::NEscaping::Q; -using NLastGetopt::NEscaping::QQ; -using NLastGetopt::NEscaping::C; -using NLastGetopt::NEscaping::CC; -using NLastGetopt::NEscaping::S; -using NLastGetopt::NEscaping::SS; -using NLastGetopt::NEscaping::B; -using NLastGetopt::NEscaping::BB; - -namespace NLastGetopt { - -#define L out.Line() -#define I auto Y_GENERATE_UNIQUE_ID(indent) = out.Indent() - - TCompletionGenerator::TCompletionGenerator(const TModChooser* modChooser) - : Options_(modChooser) - { - Y_VERIFY(modChooser != nullptr); - } - - TCompletionGenerator::TCompletionGenerator(const TOpts* opts) - : Options_(opts) - { - Y_VERIFY(opts != nullptr); - } - - void TZshCompletionGenerator::Generate(TStringBuf command, IOutputStream& stream) { - TFormattedOutput out; - NComp::TCompleterManager manager{command}; - - L << "#compdef " << command; - L; - L << "_" << command << "() {"; - { - I; - L << "local state line desc modes context curcontext=\"$curcontext\" ret=1"; - L << "local words_orig=(\"${words[@]}\")"; - L << "local current_orig=\"$((CURRENT - 1))\""; - L << "local prefix_orig=\"$PREFIX\""; - L << "local suffix_orig=\"$SUFFIX\""; - L; + +#include <util/string/ascii.h> +#include <util/generic/hash_set.h> + +#include "last_getopt_parse_result.h" + +using NLastGetopt::NEscaping::Q; +using NLastGetopt::NEscaping::QQ; +using NLastGetopt::NEscaping::C; +using NLastGetopt::NEscaping::CC; +using NLastGetopt::NEscaping::S; +using NLastGetopt::NEscaping::SS; +using NLastGetopt::NEscaping::B; +using NLastGetopt::NEscaping::BB; + +namespace NLastGetopt { + +#define L out.Line() +#define I auto Y_GENERATE_UNIQUE_ID(indent) = out.Indent() + + TCompletionGenerator::TCompletionGenerator(const TModChooser* modChooser) + : Options_(modChooser) + { + Y_VERIFY(modChooser != nullptr); + } + + TCompletionGenerator::TCompletionGenerator(const TOpts* opts) + : Options_(opts) + { + Y_VERIFY(opts != nullptr); + } + + void TZshCompletionGenerator::Generate(TStringBuf command, IOutputStream& stream) { + TFormattedOutput out; + NComp::TCompleterManager manager{command}; + + L << "#compdef " << command; + L; + L << "_" << command << "() {"; + { + I; + L << "local state line desc modes context curcontext=\"$curcontext\" ret=1"; + L << "local words_orig=(\"${words[@]}\")"; + L << "local current_orig=\"$((CURRENT - 1))\""; + L << "local prefix_orig=\"$PREFIX\""; + L << "local suffix_orig=\"$SUFFIX\""; + L; std::visit(TOverloaded{ - [&out, &manager](const TModChooser* modChooser) { - GenerateModesCompletion(out, *modChooser, manager); - }, - [&out, &manager](const TOpts* opts) { - GenerateOptsCompletion(out, *opts, manager); - } - }, Options_); - L; - L << "return ret"; - } - L << "}"; - L; - manager.GenerateZsh(out); - - out.Print(stream); - } - - void TZshCompletionGenerator::GenerateModesCompletion(TFormattedOutput& out, const TModChooser& chooser, NComp::TCompleterManager& manager) { - auto modes = chooser.GetUnsortedModes(); - - L << "_arguments -C \\"; - L << " '(- : *)'{-h,--help}'[show help information]' \\"; - if (chooser.GetVersionHandler() != nullptr) { - L << " '(- : *)'{-v,--version}'[display version information]' \\"; - } - if (!chooser.IsSvnRevisionOptionDisabled()) { - L << " '(- : *)--svnrevision[show build information]' \\"; - } - L << " '(-v --version -h --help --svnrevision)1: :->modes' \\"; - L << " '(-v --version -h --help --svnrevision)*:: :->args' \\"; - L << " && ret=0"; - L; - L << "case $state in"; - { - I; - - L << "modes)"; - { - I; - - size_t tag = 0; - bool empty = true; - - L << "desc='modes'"; - L << "modes=("; - for (auto& mode : modes) { - if (mode->Hidden) { - continue; - } - if (!mode->Name.empty()) { - I; - if (!mode->Description.empty()) { - L << QQ(mode->Name) << ":" << QQ(mode->Description); - } else { - L << QQ(mode->Name); - } - empty = false; - } else { - L << ")"; - if (!empty) { - L << "_describe -t 'mode-group-" << tag << "' $desc modes"; - } - L; - if (mode->Description.empty()) { - L << "desc='modes'"; - } else { - L << "desc=" << SS(mode->Description); - } - L << "modes=("; - empty = true; - ++tag; - } - } - L << ")"; - if (!empty) { - L << "_describe -t 'mode-group-" << tag << "' $desc modes"; - } - L; - - L << ";;"; - } - - L << "args)"; - { - I; - - L << "case $line[1] in"; - { - I; - - for (auto& mode : modes) { - if (mode->Name.empty() || mode->Hidden) { - continue; - } - - auto& line = L << SS(mode->Name); - for (auto& alias : mode->Aliases) { - line << "|" << SS(alias); - } - line << ")"; - - { - I; - - if (auto mainArgs = dynamic_cast<TMainClassArgs*>(mode->Main)) { - GenerateOptsCompletion(out, mainArgs->GetOptions(), manager); - } else if (auto mainModes = dynamic_cast<TMainClassModes*>(mode->Main)) { - GenerateModesCompletion(out, mainModes->GetSubModes(), manager); - } else { - GenerateDefaultOptsCompletion(out, manager); - } - - L << ";;"; - } - } - } - L << "esac"; - L << ";;"; - } - } - L << "esac"; - } - - void TZshCompletionGenerator::GenerateOptsCompletion(TFormattedOutput& out, const TOpts& opts, NComp::TCompleterManager& manager) { - L << "_arguments -s \\"; - { - I; - - if (opts.ArgPermutation_ == EArgPermutation::REQUIRE_ORDER) { - L << "-S \\"; - } - - for (auto opt: opts.GetOpts()) { - if (!opt->Hidden_) { - GenerateOptCompletion(out, opts, *opt, manager); - } - } - - auto argSpecs = opts.GetFreeArgSpecs(); - size_t numFreeArgs = opts.GetFreeArgsMax(); - bool unlimitedArgs = false; - if (numFreeArgs == TOpts::UNLIMITED_ARGS) { - numFreeArgs = argSpecs.empty() ? 0 : (argSpecs.rbegin()->first + 1); - unlimitedArgs = true; - } - - for (size_t i = 0; i < numFreeArgs; ++i) { - auto& spec = argSpecs[i]; - auto& line = L << "'" << (i + 1) << ":"; - if (spec.IsOptional()) { - line << ":"; - } - auto argHelp = spec.GetCompletionArgHelp(opts.GetDefaultFreeArgTitle()); - if (argHelp) { - line << Q(argHelp); - } else { - line << " "; - } - line << ":"; - if (spec.Completer_) { - line << spec.Completer_->GenerateZshAction(manager); - } else { - line << "_default"; - } - line << "' \\"; - } - - if (unlimitedArgs) { - auto& spec = opts.GetTrailingArgSpec(); - auto& line = L << "'*:"; - auto argHelp = spec.GetCompletionArgHelp(opts.GetDefaultFreeArgTitle()); - if (argHelp) { - line << Q(argHelp); - } else { - line << " "; - } - line << ":"; - if (spec.Completer_) { - line << spec.Completer_->GenerateZshAction(manager); - } else { - line << "_default"; - } - line << "' \\"; - } - - L << "&& ret=0"; - } - } - - void TZshCompletionGenerator::GenerateDefaultOptsCompletion(TFormattedOutput& out, NComp::TCompleterManager&) { - L << "_arguments \\"; - L << " '(- *)'{-h,--help}'[show help information]' \\"; - L << " '(- *)--svnrevision[show build information]' \\"; - L << " '(-h --help --svnrevision)*: :_files' \\"; - L << " && ret=0"; - } - - void TZshCompletionGenerator::GenerateOptCompletion(TFormattedOutput& out, const TOpts& opts, const TOpt& opt, NComp::TCompleterManager& manager) { - auto& line = L; - - THashSet<TString> disableOptions; - if (opt.DisableCompletionForOptions_) { - disableOptions.insert("-"); - } else { - if (!opt.AllowMultipleCompletion_) { - for (auto shortName: opt.GetShortNames()) { - disableOptions.insert(TString("-") + shortName); - } - for (auto& longName: opt.GetLongNames()) { - disableOptions.insert("--" + longName); - } - } - for (auto disabledShortName : opt.DisableCompletionForChar_) { - auto disabledOpt = opts.FindCharOption(disabledShortName); - if (disabledOpt) { - for (auto shortName: disabledOpt->GetShortNames()) { - disableOptions.insert(TString("-") + shortName); - } - for (auto& longName: disabledOpt->GetLongNames()) { - disableOptions.insert("--" + longName); - } - } else { - disableOptions.insert(TString("-") + disabledShortName); - } - } - for (auto& disabledLongName : opt.DisableCompletionForLongName_) { - auto disabledOpt = opts.FindLongOption(disabledLongName); - if (disabledOpt) { - for (auto shortName: disabledOpt->GetShortNames()) { - disableOptions.insert(TString("-") + shortName); - } - for (auto& longName: disabledOpt->GetLongNames()) { - disableOptions.insert("--" + longName); - } - } else { - disableOptions.insert("--" + disabledLongName); - } - } - } - if (opt.DisableCompletionForFreeArgs_) { - disableOptions.insert(":"); - disableOptions.insert("*"); - } else { - for (auto i : opt.DisableCompletionForFreeArg_) { - disableOptions.insert(ToString(i + 1)); - } - } - - TStringBuf sep = ""; - - if (!disableOptions.empty()) { - line << "'("; - for (auto& disableOption : disableOptions) { - line << sep << disableOption; - sep = " "; - } - line << ")"; - } - - sep = ""; - TStringBuf mul = ""; - TStringBuf quot = ""; - - if (opt.GetShortNames().size() + opt.GetLongNames().size() > 1) { - if (!disableOptions.empty()) { - line << "'"; - } - line << "{"; - quot = "'"; - } else { - if (disableOptions.empty()) { - line << "'"; - } - } - - if (opt.AllowMultipleCompletion_) { - mul = "*"; - } - - for (auto& flag : opt.GetShortNames()) { - line << sep << quot << mul << "-" << Q(TStringBuf(&flag, 1)) << quot; - sep = ","; - } - - for (auto& flag : opt.GetLongNames()) { - line << sep << quot << mul << "--" << Q(flag) << quot; - sep = ","; - } - - if (opt.GetShortNames().size() + opt.GetLongNames().size() > 1) { - line << "}'"; - } - - if (opt.GetCompletionHelp()) { - line << "["; - line << Q(opt.GetCompletionHelp()); - line << "]"; - } - - if (opt.HasArg_ != EHasArg::NO_ARGUMENT) { - if (opt.HasArg_ == EHasArg::OPTIONAL_ARGUMENT) { - line << ":"; - } - - line << ":"; - - if (opt.GetCompletionArgHelp()) { - line << C(opt.GetCompletionArgHelp()); - } else { - line << " "; - } - - line << ":"; - - if (opt.Completer_) { - line << C(opt.Completer_->GenerateZshAction(manager)); - } else { - line << "_default"; - } - } - - line << "' \\"; - } - - void TBashCompletionGenerator::Generate(TStringBuf command, IOutputStream& stream) { - TFormattedOutput out; - NComp::TCompleterManager manager{command}; - - L << "_" << command << "() {"; - { - I; - L << "COMPREPLY=()"; - L; - L << "local i args opts items candidates"; - L; - L << "local cur prev words cword"; - L << "_get_comp_words_by_ref -n \"\\\"'><=;|&(:\" cur prev words cword"; - L; - L << "local need_space=\"1\""; - L << "local IFS=$' \\t\\n'"; - L; + [&out, &manager](const TModChooser* modChooser) { + GenerateModesCompletion(out, *modChooser, manager); + }, + [&out, &manager](const TOpts* opts) { + GenerateOptsCompletion(out, *opts, manager); + } + }, Options_); + L; + L << "return ret"; + } + L << "}"; + L; + manager.GenerateZsh(out); + + out.Print(stream); + } + + void TZshCompletionGenerator::GenerateModesCompletion(TFormattedOutput& out, const TModChooser& chooser, NComp::TCompleterManager& manager) { + auto modes = chooser.GetUnsortedModes(); + + L << "_arguments -C \\"; + L << " '(- : *)'{-h,--help}'[show help information]' \\"; + if (chooser.GetVersionHandler() != nullptr) { + L << " '(- : *)'{-v,--version}'[display version information]' \\"; + } + if (!chooser.IsSvnRevisionOptionDisabled()) { + L << " '(- : *)--svnrevision[show build information]' \\"; + } + L << " '(-v --version -h --help --svnrevision)1: :->modes' \\"; + L << " '(-v --version -h --help --svnrevision)*:: :->args' \\"; + L << " && ret=0"; + L; + L << "case $state in"; + { + I; + + L << "modes)"; + { + I; + + size_t tag = 0; + bool empty = true; + + L << "desc='modes'"; + L << "modes=("; + for (auto& mode : modes) { + if (mode->Hidden) { + continue; + } + if (!mode->Name.empty()) { + I; + if (!mode->Description.empty()) { + L << QQ(mode->Name) << ":" << QQ(mode->Description); + } else { + L << QQ(mode->Name); + } + empty = false; + } else { + L << ")"; + if (!empty) { + L << "_describe -t 'mode-group-" << tag << "' $desc modes"; + } + L; + if (mode->Description.empty()) { + L << "desc='modes'"; + } else { + L << "desc=" << SS(mode->Description); + } + L << "modes=("; + empty = true; + ++tag; + } + } + L << ")"; + if (!empty) { + L << "_describe -t 'mode-group-" << tag << "' $desc modes"; + } + L; + + L << ";;"; + } + + L << "args)"; + { + I; + + L << "case $line[1] in"; + { + I; + + for (auto& mode : modes) { + if (mode->Name.empty() || mode->Hidden) { + continue; + } + + auto& line = L << SS(mode->Name); + for (auto& alias : mode->Aliases) { + line << "|" << SS(alias); + } + line << ")"; + + { + I; + + if (auto mainArgs = dynamic_cast<TMainClassArgs*>(mode->Main)) { + GenerateOptsCompletion(out, mainArgs->GetOptions(), manager); + } else if (auto mainModes = dynamic_cast<TMainClassModes*>(mode->Main)) { + GenerateModesCompletion(out, mainModes->GetSubModes(), manager); + } else { + GenerateDefaultOptsCompletion(out, manager); + } + + L << ";;"; + } + } + } + L << "esac"; + L << ";;"; + } + } + L << "esac"; + } + + void TZshCompletionGenerator::GenerateOptsCompletion(TFormattedOutput& out, const TOpts& opts, NComp::TCompleterManager& manager) { + L << "_arguments -s \\"; + { + I; + + if (opts.ArgPermutation_ == EArgPermutation::REQUIRE_ORDER) { + L << "-S \\"; + } + + for (auto opt: opts.GetOpts()) { + if (!opt->Hidden_) { + GenerateOptCompletion(out, opts, *opt, manager); + } + } + + auto argSpecs = opts.GetFreeArgSpecs(); + size_t numFreeArgs = opts.GetFreeArgsMax(); + bool unlimitedArgs = false; + if (numFreeArgs == TOpts::UNLIMITED_ARGS) { + numFreeArgs = argSpecs.empty() ? 0 : (argSpecs.rbegin()->first + 1); + unlimitedArgs = true; + } + + for (size_t i = 0; i < numFreeArgs; ++i) { + auto& spec = argSpecs[i]; + auto& line = L << "'" << (i + 1) << ":"; + if (spec.IsOptional()) { + line << ":"; + } + auto argHelp = spec.GetCompletionArgHelp(opts.GetDefaultFreeArgTitle()); + if (argHelp) { + line << Q(argHelp); + } else { + line << " "; + } + line << ":"; + if (spec.Completer_) { + line << spec.Completer_->GenerateZshAction(manager); + } else { + line << "_default"; + } + line << "' \\"; + } + + if (unlimitedArgs) { + auto& spec = opts.GetTrailingArgSpec(); + auto& line = L << "'*:"; + auto argHelp = spec.GetCompletionArgHelp(opts.GetDefaultFreeArgTitle()); + if (argHelp) { + line << Q(argHelp); + } else { + line << " "; + } + line << ":"; + if (spec.Completer_) { + line << spec.Completer_->GenerateZshAction(manager); + } else { + line << "_default"; + } + line << "' \\"; + } + + L << "&& ret=0"; + } + } + + void TZshCompletionGenerator::GenerateDefaultOptsCompletion(TFormattedOutput& out, NComp::TCompleterManager&) { + L << "_arguments \\"; + L << " '(- *)'{-h,--help}'[show help information]' \\"; + L << " '(- *)--svnrevision[show build information]' \\"; + L << " '(-h --help --svnrevision)*: :_files' \\"; + L << " && ret=0"; + } + + void TZshCompletionGenerator::GenerateOptCompletion(TFormattedOutput& out, const TOpts& opts, const TOpt& opt, NComp::TCompleterManager& manager) { + auto& line = L; + + THashSet<TString> disableOptions; + if (opt.DisableCompletionForOptions_) { + disableOptions.insert("-"); + } else { + if (!opt.AllowMultipleCompletion_) { + for (auto shortName: opt.GetShortNames()) { + disableOptions.insert(TString("-") + shortName); + } + for (auto& longName: opt.GetLongNames()) { + disableOptions.insert("--" + longName); + } + } + for (auto disabledShortName : opt.DisableCompletionForChar_) { + auto disabledOpt = opts.FindCharOption(disabledShortName); + if (disabledOpt) { + for (auto shortName: disabledOpt->GetShortNames()) { + disableOptions.insert(TString("-") + shortName); + } + for (auto& longName: disabledOpt->GetLongNames()) { + disableOptions.insert("--" + longName); + } + } else { + disableOptions.insert(TString("-") + disabledShortName); + } + } + for (auto& disabledLongName : opt.DisableCompletionForLongName_) { + auto disabledOpt = opts.FindLongOption(disabledLongName); + if (disabledOpt) { + for (auto shortName: disabledOpt->GetShortNames()) { + disableOptions.insert(TString("-") + shortName); + } + for (auto& longName: disabledOpt->GetLongNames()) { + disableOptions.insert("--" + longName); + } + } else { + disableOptions.insert("--" + disabledLongName); + } + } + } + if (opt.DisableCompletionForFreeArgs_) { + disableOptions.insert(":"); + disableOptions.insert("*"); + } else { + for (auto i : opt.DisableCompletionForFreeArg_) { + disableOptions.insert(ToString(i + 1)); + } + } + + TStringBuf sep = ""; + + if (!disableOptions.empty()) { + line << "'("; + for (auto& disableOption : disableOptions) { + line << sep << disableOption; + sep = " "; + } + line << ")"; + } + + sep = ""; + TStringBuf mul = ""; + TStringBuf quot = ""; + + if (opt.GetShortNames().size() + opt.GetLongNames().size() > 1) { + if (!disableOptions.empty()) { + line << "'"; + } + line << "{"; + quot = "'"; + } else { + if (disableOptions.empty()) { + line << "'"; + } + } + + if (opt.AllowMultipleCompletion_) { + mul = "*"; + } + + for (auto& flag : opt.GetShortNames()) { + line << sep << quot << mul << "-" << Q(TStringBuf(&flag, 1)) << quot; + sep = ","; + } + + for (auto& flag : opt.GetLongNames()) { + line << sep << quot << mul << "--" << Q(flag) << quot; + sep = ","; + } + + if (opt.GetShortNames().size() + opt.GetLongNames().size() > 1) { + line << "}'"; + } + + if (opt.GetCompletionHelp()) { + line << "["; + line << Q(opt.GetCompletionHelp()); + line << "]"; + } + + if (opt.HasArg_ != EHasArg::NO_ARGUMENT) { + if (opt.HasArg_ == EHasArg::OPTIONAL_ARGUMENT) { + line << ":"; + } + + line << ":"; + + if (opt.GetCompletionArgHelp()) { + line << C(opt.GetCompletionArgHelp()); + } else { + line << " "; + } + + line << ":"; + + if (opt.Completer_) { + line << C(opt.Completer_->GenerateZshAction(manager)); + } else { + line << "_default"; + } + } + + line << "' \\"; + } + + void TBashCompletionGenerator::Generate(TStringBuf command, IOutputStream& stream) { + TFormattedOutput out; + NComp::TCompleterManager manager{command}; + + L << "_" << command << "() {"; + { + I; + L << "COMPREPLY=()"; + L; + L << "local i args opts items candidates"; + L; + L << "local cur prev words cword"; + L << "_get_comp_words_by_ref -n \"\\\"'><=;|&(:\" cur prev words cword"; + L; + L << "local need_space=\"1\""; + L << "local IFS=$' \\t\\n'"; + L; std::visit(TOverloaded{ - [&out, &manager](const TModChooser* modChooser) { - GenerateModesCompletion(out, *modChooser, manager, 1); - }, - [&out, &manager](const TOpts* opts) { - GenerateOptsCompletion(out, *opts, manager, 1); - } - }, Options_); - L; - L; - L << "__ltrim_colon_completions \"$cur\""; - L; - L << "IFS=$'\\n'"; - L << "if [ ${#COMPREPLY[@]} -ne 0 ]; then"; - { - I; - L << "if [[ -z $need_space ]]; then"; - { - I; - L << "COMPREPLY=( $(printf \"%q\\n\" \"${COMPREPLY[@]}\") )"; - } - L << "else"; - { - I; - L << "COMPREPLY=( $(printf \"%q \\n\" \"${COMPREPLY[@]}\") )"; - } - L << "fi"; - } - L << "fi"; - L; - L << "return 0"; - } - L << "}"; - L; - L << "complete -o nospace -o default -F _" << command << " " << command; - - out.Print(stream); - } - - void TBashCompletionGenerator::GenerateModesCompletion(TFormattedOutput& out, const TModChooser& chooser, NComp::TCompleterManager& manager, size_t level) { - auto modes = chooser.GetUnsortedModes(); - - L << "if [[ ${cword} == " << level << " ]] ; then"; - { - I; - L << "if [[ ${cur} == -* ]] ; then"; - { - I; - auto& line = L << "COMPREPLY+=( $(compgen -W '-h --help"; - if (chooser.GetVersionHandler() != nullptr) { - line << " -v --version"; - } - if (!chooser.IsSvnRevisionOptionDisabled()) { - line << " --svnrevision"; - } - line << "' -- ${cur}) )"; - } - L << "else"; - { - I; - auto& line = L << "COMPREPLY+=( $(compgen -W '"; - TStringBuf sep = ""; - for (auto& mode : modes) { + [&out, &manager](const TModChooser* modChooser) { + GenerateModesCompletion(out, *modChooser, manager, 1); + }, + [&out, &manager](const TOpts* opts) { + GenerateOptsCompletion(out, *opts, manager, 1); + } + }, Options_); + L; + L; + L << "__ltrim_colon_completions \"$cur\""; + L; + L << "IFS=$'\\n'"; + L << "if [ ${#COMPREPLY[@]} -ne 0 ]; then"; + { + I; + L << "if [[ -z $need_space ]]; then"; + { + I; + L << "COMPREPLY=( $(printf \"%q\\n\" \"${COMPREPLY[@]}\") )"; + } + L << "else"; + { + I; + L << "COMPREPLY=( $(printf \"%q \\n\" \"${COMPREPLY[@]}\") )"; + } + L << "fi"; + } + L << "fi"; + L; + L << "return 0"; + } + L << "}"; + L; + L << "complete -o nospace -o default -F _" << command << " " << command; + + out.Print(stream); + } + + void TBashCompletionGenerator::GenerateModesCompletion(TFormattedOutput& out, const TModChooser& chooser, NComp::TCompleterManager& manager, size_t level) { + auto modes = chooser.GetUnsortedModes(); + + L << "if [[ ${cword} == " << level << " ]] ; then"; + { + I; + L << "if [[ ${cur} == -* ]] ; then"; + { + I; + auto& line = L << "COMPREPLY+=( $(compgen -W '-h --help"; + if (chooser.GetVersionHandler() != nullptr) { + line << " -v --version"; + } + if (!chooser.IsSvnRevisionOptionDisabled()) { + line << " --svnrevision"; + } + line << "' -- ${cur}) )"; + } + L << "else"; + { + I; + auto& line = L << "COMPREPLY+=( $(compgen -W '"; + TStringBuf sep = ""; + for (auto& mode : modes) { if (!mode->Hidden && !mode->NoCompletion) { line << sep << B(mode->Name); sep = " "; } - } - line << "' -- ${cur}) )"; - } - L << "fi"; - } - L << "else"; - { - I; - L << "case \"${words[" << level << "]}\" in"; - { - I; - - for (auto& mode : modes) { + } + line << "' -- ${cur}) )"; + } + L << "fi"; + } + L << "else"; + { + I; + L << "case \"${words[" << level << "]}\" in"; + { + I; + + for (auto& mode : modes) { if (mode->Name.empty() || mode->Hidden || mode->NoCompletion) { - continue; - } - - auto& line = L << BB(mode->Name); - for (auto& alias : mode->Aliases) { - line << "|" << BB(alias); - } - line << ")"; - - { - I; - - if (auto mainArgs = dynamic_cast<TMainClassArgs*>(mode->Main)) { - GenerateOptsCompletion(out, mainArgs->GetOptions(), manager, level + 1); - } else if (auto mainModes = dynamic_cast<TMainClassModes*>(mode->Main)) { - GenerateModesCompletion(out, mainModes->GetSubModes(), manager, level + 1); - } else { - GenerateDefaultOptsCompletion(out, manager); - } - - L << ";;"; - } - } - } - L << "esac"; - } - L << "fi"; - } - - void TBashCompletionGenerator::GenerateOptsCompletion(TFormattedOutput& out, const TOpts& opts, NComp::TCompleterManager&, size_t level) { - auto unorderedOpts = opts.GetOpts(); - - L << "if [[ ${cur} == -* ]] ; then"; - { - I; - auto& line = L << "COMPREPLY+=( $(compgen -W '"; - TStringBuf sep = ""; - for (auto& opt : unorderedOpts) { + continue; + } + + auto& line = L << BB(mode->Name); + for (auto& alias : mode->Aliases) { + line << "|" << BB(alias); + } + line << ")"; + + { + I; + + if (auto mainArgs = dynamic_cast<TMainClassArgs*>(mode->Main)) { + GenerateOptsCompletion(out, mainArgs->GetOptions(), manager, level + 1); + } else if (auto mainModes = dynamic_cast<TMainClassModes*>(mode->Main)) { + GenerateModesCompletion(out, mainModes->GetSubModes(), manager, level + 1); + } else { + GenerateDefaultOptsCompletion(out, manager); + } + + L << ";;"; + } + } + } + L << "esac"; + } + L << "fi"; + } + + void TBashCompletionGenerator::GenerateOptsCompletion(TFormattedOutput& out, const TOpts& opts, NComp::TCompleterManager&, size_t level) { + auto unorderedOpts = opts.GetOpts(); + + L << "if [[ ${cur} == -* ]] ; then"; + { + I; + auto& line = L << "COMPREPLY+=( $(compgen -W '"; + TStringBuf sep = ""; + for (auto& opt : unorderedOpts) { if (opt->IsHidden()) { continue; } - for (auto& shortName : opt->GetShortNames()) { - line << sep << "-" << B(TStringBuf(&shortName, 1)); - sep = " "; - } - for (auto& longName: opt->GetLongNames()) { - line << sep << "--" << B(longName); - sep = " "; - } - } - line << "' -- ${cur}) )"; - } - L << "else"; - { - I; - L << "case ${prev} in"; - { - I; - for (auto& opt : unorderedOpts) { + for (auto& shortName : opt->GetShortNames()) { + line << sep << "-" << B(TStringBuf(&shortName, 1)); + sep = " "; + } + for (auto& longName: opt->GetLongNames()) { + line << sep << "--" << B(longName); + sep = " "; + } + } + line << "' -- ${cur}) )"; + } + L << "else"; + { + I; + L << "case ${prev} in"; + { + I; + for (auto& opt : unorderedOpts) { if (opt->HasArg_ == EHasArg::NO_ARGUMENT || opt->IsHidden()) { - continue; - } - - auto& line = L; - TStringBuf sep = ""; - for (auto& shortName : opt->GetShortNames()) { - line << sep << "'-" << B(TStringBuf(&shortName, 1)) << "'"; - sep = "|"; - } - for (auto& longName: opt->GetLongNames()) { - line << sep << "'--" << B(longName) << "'"; - sep = "|"; - } - line << ")"; - { - I; - if (opt->Completer_ != nullptr) { - opt->Completer_->GenerateBash(out); - } - L << ";;"; - } - } - - L << "*)"; - { - I; - - L << "args=0"; - auto& line = L << "opts='@("; - TStringBuf sep = ""; - for (auto& opt : unorderedOpts) { + continue; + } + + auto& line = L; + TStringBuf sep = ""; + for (auto& shortName : opt->GetShortNames()) { + line << sep << "'-" << B(TStringBuf(&shortName, 1)) << "'"; + sep = "|"; + } + for (auto& longName: opt->GetLongNames()) { + line << sep << "'--" << B(longName) << "'"; + sep = "|"; + } + line << ")"; + { + I; + if (opt->Completer_ != nullptr) { + opt->Completer_->GenerateBash(out); + } + L << ";;"; + } + } + + L << "*)"; + { + I; + + L << "args=0"; + auto& line = L << "opts='@("; + TStringBuf sep = ""; + for (auto& opt : unorderedOpts) { if (opt->HasArg_ == EHasArg::NO_ARGUMENT || opt->IsHidden()) { - continue; - } - for (auto& shortName : opt->GetShortNames()) { - line << sep << "-" << B(TStringBuf(&shortName, 1)); - sep = "|"; - } - for (auto& longName: opt->GetLongNames()) { - line << sep << "--" << B(longName); - sep = "|"; - } - } - line << ")'"; - L << "for (( i=" << level << "; i < cword; i++ )); do"; - { - I; - L << "if [[ ${words[i]} != -* && ${words[i-1]} != $opts ]]; then"; - { - I; - L << "(( args++ ))"; - } - L << "fi"; - } - L << "done"; - L; - - auto argSpecs = opts.GetFreeArgSpecs(); - size_t numFreeArgs = opts.GetFreeArgsMax(); - bool unlimitedArgs = false; - if (numFreeArgs == TOpts::UNLIMITED_ARGS) { - numFreeArgs = argSpecs.empty() ? 0 : (argSpecs.rbegin()->first + 1); - unlimitedArgs = true; - } - - L << "case ${args} in"; - { - I; - - for (size_t i = 0; i < numFreeArgs; ++i) { - L << i << ")"; - { - I; - auto& spec = argSpecs[i]; - if (spec.Completer_ != nullptr) { - spec.Completer_->GenerateBash(out); - } - L << ";;"; - } - } - if (unlimitedArgs) { - L << "*)"; - { - I; - auto& spec = opts.GetTrailingArgSpec(); - if (spec.Completer_ != nullptr) { - spec.Completer_->GenerateBash(out); - } - L << ";;"; - } - } - } - L << "esac"; - L << ";;"; - } - } - L << "esac"; - } - L << "fi"; - } - - void TBashCompletionGenerator::GenerateDefaultOptsCompletion(TFormattedOutput& out, NComp::TCompleterManager&) { - L << "if [[ ${cur} == -* ]] ; then"; - { - I; - L << "COMPREPLY+=( $(compgen -W '-h --help --svnrevision' -- ${cur}) )"; - } - L << "fi"; - } - -#undef I -#undef L - - TString NEscaping::Q(TStringBuf string) { - TStringBuilder out; - out.reserve(string.size()); - for (auto c: string) { - switch (c) { - case '\a': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - case '\v': - out << " "; - break; - case '\\': - out << "\\\\"; - break; - case '\'': - out << "''"; - break; - case '\"': - out << "\\\""; - break; - case '[': - out << "\\["; - break; - case ']': - out << "\\]"; - break; - case ':': - out << "\\:"; - break; - case '+': - out << "\\+"; - break; - case '=': - out << "\\="; - break; - default: - out << c; - break; - } - } - return out; - } - - TString NEscaping::QQ(TStringBuf string) { - auto q = Q(string); - return "'" + q + "'"; - } - - TString NEscaping::C(TStringBuf string) { - TStringBuilder out; - out.reserve(string.size() + 1); - for (auto c: string) { - switch (c) { - case '\a': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - case '\v': - out << " "; - break; - case '\'': - out << "''"; - break; - case ':': - out << "\\:"; - break; - default: - out << c; - break; - } - } - return out; - } - - TString NEscaping::CC(TStringBuf string) { - auto c = C(string); - return "'" + c + "'"; - } - - TString NEscaping::S(TStringBuf string) { - TStringBuilder out; - out.reserve(string.size() + 1); - for (auto c: string) { - switch (c) { - case '\a': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - case '\v': - out << " "; - break; - case '\'': - out << "''"; - break; - default: - out << c; - break; - } - } - return out; - } - - TString NEscaping::SS(TStringBuf string) { - auto s = S(string); - return "'" + s + "'"; - } - - TString NEscaping::B(TStringBuf string) { - TStringBuilder out; - out.reserve(string.size() + 1); - for (auto c: string) { - switch (c) { - case '\a': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - case '\v': - out << " "; - break; - case '\'': - out << "'\"'\"'"; - break; - default: - out << c; - break; - } - } - return out; - } - - TString NEscaping::BB(TStringBuf string) { - auto b = B(string); - return "'" + b + "'"; - } -} + continue; + } + for (auto& shortName : opt->GetShortNames()) { + line << sep << "-" << B(TStringBuf(&shortName, 1)); + sep = "|"; + } + for (auto& longName: opt->GetLongNames()) { + line << sep << "--" << B(longName); + sep = "|"; + } + } + line << ")'"; + L << "for (( i=" << level << "; i < cword; i++ )); do"; + { + I; + L << "if [[ ${words[i]} != -* && ${words[i-1]} != $opts ]]; then"; + { + I; + L << "(( args++ ))"; + } + L << "fi"; + } + L << "done"; + L; + + auto argSpecs = opts.GetFreeArgSpecs(); + size_t numFreeArgs = opts.GetFreeArgsMax(); + bool unlimitedArgs = false; + if (numFreeArgs == TOpts::UNLIMITED_ARGS) { + numFreeArgs = argSpecs.empty() ? 0 : (argSpecs.rbegin()->first + 1); + unlimitedArgs = true; + } + + L << "case ${args} in"; + { + I; + + for (size_t i = 0; i < numFreeArgs; ++i) { + L << i << ")"; + { + I; + auto& spec = argSpecs[i]; + if (spec.Completer_ != nullptr) { + spec.Completer_->GenerateBash(out); + } + L << ";;"; + } + } + if (unlimitedArgs) { + L << "*)"; + { + I; + auto& spec = opts.GetTrailingArgSpec(); + if (spec.Completer_ != nullptr) { + spec.Completer_->GenerateBash(out); + } + L << ";;"; + } + } + } + L << "esac"; + L << ";;"; + } + } + L << "esac"; + } + L << "fi"; + } + + void TBashCompletionGenerator::GenerateDefaultOptsCompletion(TFormattedOutput& out, NComp::TCompleterManager&) { + L << "if [[ ${cur} == -* ]] ; then"; + { + I; + L << "COMPREPLY+=( $(compgen -W '-h --help --svnrevision' -- ${cur}) )"; + } + L << "fi"; + } + +#undef I +#undef L + + TString NEscaping::Q(TStringBuf string) { + TStringBuilder out; + out.reserve(string.size()); + for (auto c: string) { + switch (c) { + case '\a': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': + out << " "; + break; + case '\\': + out << "\\\\"; + break; + case '\'': + out << "''"; + break; + case '\"': + out << "\\\""; + break; + case '[': + out << "\\["; + break; + case ']': + out << "\\]"; + break; + case ':': + out << "\\:"; + break; + case '+': + out << "\\+"; + break; + case '=': + out << "\\="; + break; + default: + out << c; + break; + } + } + return out; + } + + TString NEscaping::QQ(TStringBuf string) { + auto q = Q(string); + return "'" + q + "'"; + } + + TString NEscaping::C(TStringBuf string) { + TStringBuilder out; + out.reserve(string.size() + 1); + for (auto c: string) { + switch (c) { + case '\a': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': + out << " "; + break; + case '\'': + out << "''"; + break; + case ':': + out << "\\:"; + break; + default: + out << c; + break; + } + } + return out; + } + + TString NEscaping::CC(TStringBuf string) { + auto c = C(string); + return "'" + c + "'"; + } + + TString NEscaping::S(TStringBuf string) { + TStringBuilder out; + out.reserve(string.size() + 1); + for (auto c: string) { + switch (c) { + case '\a': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': + out << " "; + break; + case '\'': + out << "''"; + break; + default: + out << c; + break; + } + } + return out; + } + + TString NEscaping::SS(TStringBuf string) { + auto s = S(string); + return "'" + s + "'"; + } + + TString NEscaping::B(TStringBuf string) { + TStringBuilder out; + out.reserve(string.size() + 1); + for (auto c: string) { + switch (c) { + case '\a': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': + out << " "; + break; + case '\'': + out << "'\"'\"'"; + break; + default: + out << c; + break; + } + } + return out; + } + + TString NEscaping::BB(TStringBuf string) { + auto b = B(string); + return "'" + b + "'"; + } +} diff --git a/library/cpp/getopt/small/completion_generator.h b/library/cpp/getopt/small/completion_generator.h index ac076e4d90..4241bb7d6c 100644 --- a/library/cpp/getopt/small/completion_generator.h +++ b/library/cpp/getopt/small/completion_generator.h @@ -1,69 +1,69 @@ -#pragma once - -#include "completer.h" -#include "formatted_output.h" -#include "last_getopt_opts.h" -#include "modchooser.h" - -#include <util/generic/variant.h> -#include <util/string/builder.h> - -namespace NLastGetopt { - class TCompletionGenerator { - public: - explicit TCompletionGenerator(const TModChooser* modChooser); - explicit TCompletionGenerator(const TOpts* opts); - virtual ~TCompletionGenerator() = default; - - public: - virtual void Generate(TStringBuf command, IOutputStream& stream) = 0; - - protected: +#pragma once + +#include "completer.h" +#include "formatted_output.h" +#include "last_getopt_opts.h" +#include "modchooser.h" + +#include <util/generic/variant.h> +#include <util/string/builder.h> + +namespace NLastGetopt { + class TCompletionGenerator { + public: + explicit TCompletionGenerator(const TModChooser* modChooser); + explicit TCompletionGenerator(const TOpts* opts); + virtual ~TCompletionGenerator() = default; + + public: + virtual void Generate(TStringBuf command, IOutputStream& stream) = 0; + + protected: std::variant<const TModChooser*, const TOpts*> Options_; - }; - - class TZshCompletionGenerator: public TCompletionGenerator { - public: - using TCompletionGenerator::TCompletionGenerator; - - public: - void Generate(TStringBuf command, IOutputStream& stream) override; - - private: - static void GenerateModesCompletion(TFormattedOutput& out, const TModChooser& chooser, NComp::TCompleterManager& manager); - static void GenerateOptsCompletion(TFormattedOutput& out, const TOpts& opts, NComp::TCompleterManager& manager); - static void GenerateDefaultOptsCompletion(TFormattedOutput& out, NComp::TCompleterManager& manager); - static void GenerateOptCompletion(TFormattedOutput& out, const TOpts& opts, const TOpt& opt, NComp::TCompleterManager& manager); - }; - - class TBashCompletionGenerator: public TCompletionGenerator { - public: - using TCompletionGenerator::TCompletionGenerator; - - public: - void Generate(TStringBuf command, IOutputStream& stream) override; - - private: - static void GenerateModesCompletion(TFormattedOutput& out, const TModChooser& chooser, NComp::TCompleterManager& manager, size_t level); - static void GenerateOptsCompletion(TFormattedOutput& out, const TOpts& opts, NComp::TCompleterManager& manager, size_t level); - static void GenerateDefaultOptsCompletion(TFormattedOutput& out, NComp::TCompleterManager& manager); - }; - - namespace NEscaping { - /// Escape ':', '-', '=', '[', ']' for use in zsh _arguments - TString Q(TStringBuf string); - TString QQ(TStringBuf string); - - /// Escape colons for use in zsh _alternative and _arguments - TString C(TStringBuf string); - TString CC(TStringBuf string); - - /// Simple escape for use in zsh single-quoted strings - TString S(TStringBuf string); - TString SS(TStringBuf string); - - /// Simple escape for use in bash single-quoted strings - TString B(TStringBuf string); - TString BB(TStringBuf string); - } -} + }; + + class TZshCompletionGenerator: public TCompletionGenerator { + public: + using TCompletionGenerator::TCompletionGenerator; + + public: + void Generate(TStringBuf command, IOutputStream& stream) override; + + private: + static void GenerateModesCompletion(TFormattedOutput& out, const TModChooser& chooser, NComp::TCompleterManager& manager); + static void GenerateOptsCompletion(TFormattedOutput& out, const TOpts& opts, NComp::TCompleterManager& manager); + static void GenerateDefaultOptsCompletion(TFormattedOutput& out, NComp::TCompleterManager& manager); + static void GenerateOptCompletion(TFormattedOutput& out, const TOpts& opts, const TOpt& opt, NComp::TCompleterManager& manager); + }; + + class TBashCompletionGenerator: public TCompletionGenerator { + public: + using TCompletionGenerator::TCompletionGenerator; + + public: + void Generate(TStringBuf command, IOutputStream& stream) override; + + private: + static void GenerateModesCompletion(TFormattedOutput& out, const TModChooser& chooser, NComp::TCompleterManager& manager, size_t level); + static void GenerateOptsCompletion(TFormattedOutput& out, const TOpts& opts, NComp::TCompleterManager& manager, size_t level); + static void GenerateDefaultOptsCompletion(TFormattedOutput& out, NComp::TCompleterManager& manager); + }; + + namespace NEscaping { + /// Escape ':', '-', '=', '[', ']' for use in zsh _arguments + TString Q(TStringBuf string); + TString QQ(TStringBuf string); + + /// Escape colons for use in zsh _alternative and _arguments + TString C(TStringBuf string); + TString CC(TStringBuf string); + + /// Simple escape for use in zsh single-quoted strings + TString S(TStringBuf string); + TString SS(TStringBuf string); + + /// Simple escape for use in bash single-quoted strings + TString B(TStringBuf string); + TString BB(TStringBuf string); + } +} diff --git a/library/cpp/getopt/small/formatted_output.cpp b/library/cpp/getopt/small/formatted_output.cpp index fe355c2723..bf1b366f25 100644 --- a/library/cpp/getopt/small/formatted_output.cpp +++ b/library/cpp/getopt/small/formatted_output.cpp @@ -1,36 +1,36 @@ -#include "formatted_output.h" - -namespace NLastGetopt { - - TFormattedOutput::IndentGuard::IndentGuard(TFormattedOutput* output) - : Output(output) - { - Output->IndentLevel_ += 2; - } - - TFormattedOutput::IndentGuard::~IndentGuard() { - Output->IndentLevel_ -= 2; - } - - TFormattedOutput::IndentGuard TFormattedOutput::Indent() { - return IndentGuard(this); - } - - TStringBuilder& TFormattedOutput::Line() { - return Lines_.emplace_back(IndentLevel_, TStringBuilder()).second; - } - - void TFormattedOutput::Print(IOutputStream& out) { - for (auto&[indent, line] : Lines_) { - if (indent && !line.empty()) { - TTempBuf buf(indent); - Fill(buf.Data(), buf.Data() + indent, ' '); - out.Write(buf.Data(), indent); - } - out << line; - if (!line.EndsWith('\n')) { - out << Endl; - } - } - } -} +#include "formatted_output.h" + +namespace NLastGetopt { + + TFormattedOutput::IndentGuard::IndentGuard(TFormattedOutput* output) + : Output(output) + { + Output->IndentLevel_ += 2; + } + + TFormattedOutput::IndentGuard::~IndentGuard() { + Output->IndentLevel_ -= 2; + } + + TFormattedOutput::IndentGuard TFormattedOutput::Indent() { + return IndentGuard(this); + } + + TStringBuilder& TFormattedOutput::Line() { + return Lines_.emplace_back(IndentLevel_, TStringBuilder()).second; + } + + void TFormattedOutput::Print(IOutputStream& out) { + for (auto&[indent, line] : Lines_) { + if (indent && !line.empty()) { + TTempBuf buf(indent); + Fill(buf.Data(), buf.Data() + indent, ' '); + out.Write(buf.Data(), indent); + } + out << line; + if (!line.EndsWith('\n')) { + out << Endl; + } + } + } +} diff --git a/library/cpp/getopt/small/formatted_output.h b/library/cpp/getopt/small/formatted_output.h index f239dd7fdb..6fd16b73f9 100644 --- a/library/cpp/getopt/small/formatted_output.h +++ b/library/cpp/getopt/small/formatted_output.h @@ -1,32 +1,32 @@ -#pragma once - -#include <util/generic/algorithm.h> -#include <util/generic/vector.h> -#include <util/string/builder.h> -#include <util/memory/tempbuf.h> - -namespace NLastGetopt { - /// Utility for printing indented lines. Used by completion generators. - class TFormattedOutput { - public: - struct IndentGuard { - explicit IndentGuard(TFormattedOutput* output); - virtual ~IndentGuard(); - TFormattedOutput* Output; - }; - - public: - /// Increase indentation and return a RAII object that'll decrease it back automatically. - IndentGuard Indent(); - - /// Append a new indented line to the stream. - TStringBuilder& Line(); - - /// Collect all lines into a stream. - void Print(IOutputStream& out); - - private: - int IndentLevel_ = 0; - TVector<std::pair<int, TStringBuilder>> Lines_; - }; -} +#pragma once + +#include <util/generic/algorithm.h> +#include <util/generic/vector.h> +#include <util/string/builder.h> +#include <util/memory/tempbuf.h> + +namespace NLastGetopt { + /// Utility for printing indented lines. Used by completion generators. + class TFormattedOutput { + public: + struct IndentGuard { + explicit IndentGuard(TFormattedOutput* output); + virtual ~IndentGuard(); + TFormattedOutput* Output; + }; + + public: + /// Increase indentation and return a RAII object that'll decrease it back automatically. + IndentGuard Indent(); + + /// Append a new indented line to the stream. + TStringBuilder& Line(); + + /// Collect all lines into a stream. + void Print(IOutputStream& out); + + private: + int IndentLevel_ = 0; + TVector<std::pair<int, TStringBuilder>> Lines_; + }; +} diff --git a/library/cpp/getopt/small/last_getopt.h b/library/cpp/getopt/small/last_getopt.h index 0540f69313..07687bc914 100644 --- a/library/cpp/getopt/small/last_getopt.h +++ b/library/cpp/getopt/small/last_getopt.h @@ -40,7 +40,7 @@ namespace NLastGetopt { TValue last = mutableValue ? NPrivate::OptFromString<TValue>(mutableValue, parser->CurOpt()) : first; if (last < first) { - throw TUsageException() << "failed to parse opt " << NPrivate::OptToString(parser->CurOpt()) << " value " << TString(val).Quote() << ": the second argument is less than the first one"; + throw TUsageException() << "failed to parse opt " << NPrivate::OptToString(parser->CurOpt()) << " value " << TString(val).Quote() << ": the second argument is less than the first one"; } for (++last; first < last; ++first) { @@ -100,7 +100,7 @@ namespace NLastGetopt { if (curval.IsInited()) { TStringBuf key, value; if (!curval.TrySplit(KVDelim, key, value)) { - throw TUsageException() << "failed to parse opt " << NPrivate::OptToString(curOpt) + throw TUsageException() << "failed to parse opt " << NPrivate::OptToString(curOpt) << " value " << TString(curval).Quote() << ": expected key" << KVDelim << "value format"; } Func(NPrivate::OptFromString<TKey>(key, curOpt), NPrivate::OptFromString<TValue>(value, curOpt)); @@ -119,10 +119,10 @@ namespace NLastGetopt { const TpArg& arg = curval.IsInited() ? OptFromString<TpArg>(curval, parser->CurOpt()) : Def_; try { Func_(arg); - } catch (const TUsageException&) { - throw; + } catch (const TUsageException&) { + throw; } catch (...) { - throw TUsageException() << "failed to handle opt " << OptToString(parser->CurOpt()) + throw TUsageException() << "failed to handle opt " << OptToString(parser->CurOpt()) << " value " << TString(curval).Quote() << ": " << CurrentExceptionMessage(); } } diff --git a/library/cpp/getopt/small/last_getopt_opt.cpp b/library/cpp/getopt/small/last_getopt_opt.cpp index 2839cca53f..9a99437f4b 100644 --- a/library/cpp/getopt/small/last_getopt_opt.cpp +++ b/library/cpp/getopt/small/last_getopt_opt.cpp @@ -54,7 +54,7 @@ namespace NLastGetopt { TOpt& TOpt::AddShortName(unsigned char c) { ; if (!IsAllowedShortName(c)) - throw TUsageException() << "option char '" << c << "' is not allowed"; + throw TUsageException() << "option char '" << c << "' is not allowed"; Chars_.push_back(c); return *this; } @@ -75,7 +75,7 @@ namespace NLastGetopt { ; unsigned char c = 0; if (!IsAllowedLongName(name, &c)) - throw TUsageException() << "option char '" << c + throw TUsageException() << "option char '" << c << "' in long '" << name << "' is not allowed"; LongNames_.push_back(name); return *this; @@ -102,12 +102,12 @@ namespace NLastGetopt { } } - TOpt& TOpt::IfPresentDisableCompletionFor(const TOpt& opt) { - if (opt.GetLongNames()) { - IfPresentDisableCompletionFor(opt.GetName()); - } else { - IfPresentDisableCompletionFor(opt.GetChar()); - } - return *this; - } + TOpt& TOpt::IfPresentDisableCompletionFor(const TOpt& opt) { + if (opt.GetLongNames()) { + IfPresentDisableCompletionFor(opt.GetName()); + } else { + IfPresentDisableCompletionFor(opt.GetChar()); + } + return *this; + } } diff --git a/library/cpp/getopt/small/last_getopt_opt.h b/library/cpp/getopt/small/last_getopt_opt.h index a4e97e6a0a..a8dd5adca9 100644 --- a/library/cpp/getopt/small/last_getopt_opt.h +++ b/library/cpp/getopt/small/last_getopt_opt.h @@ -1,6 +1,6 @@ #pragma once -#include "completer.h" +#include "completer.h" #include "last_getopt_handlers.h" #include <util/string/split.h> @@ -54,24 +54,24 @@ namespace NLastGetopt { typedef TVector<TSimpleSharedPtr<IOptHandler>> TOptHandlers; public: - bool Hidden_ = false; // is visible in help - TString ArgTitle_; // the name of argument in help output - TString Help_; // the help string - TString CompletionHelp_; // the help string that's used in completion script, a shorter version of Help_ - TString CompletionArgHelp_; // the description of argument in completion script + bool Hidden_ = false; // is visible in help + TString ArgTitle_; // the name of argument in help output + TString Help_; // the help string + TString CompletionHelp_; // the help string that's used in completion script, a shorter version of Help_ + TString CompletionArgHelp_; // the description of argument in completion script EHasArg HasArg_ = DEFAULT_HAS_ARG; // the argument parsing politics bool Required_ = false; // option existence politics - bool AllowMultipleCompletion_ = false; // let the completer know that this option can occur more than once - - bool DisableCompletionForOptions_ = false; - bool DisableCompletionForFreeArgs_ = false; - TShortNames DisableCompletionForChar_; - TLongNames DisableCompletionForLongName_; - TVector<size_t> DisableCompletionForFreeArg_; - NComp::ICompleterPtr Completer_; - + bool AllowMultipleCompletion_ = false; // let the completer know that this option can occur more than once + + bool DisableCompletionForOptions_ = false; + bool DisableCompletionForFreeArgs_ = false; + TShortNames DisableCompletionForChar_; + TLongNames DisableCompletionForLongName_; + TVector<size_t> DisableCompletionForFreeArg_; + NComp::ICompleterPtr Completer_; + private: //Handlers information const void* UserValue_ = nullptr; @@ -335,37 +335,37 @@ namespace NLastGetopt { } /** - * Set help string that appears with `--help`. Unless `CompletionHelp` is given, this message will also be used - * in completion script. In this case, don't make it too long, don't start it with a capital letter and don't - * end it with a full stop. - * - * Note that `Help`, `CompletionHelp` and `CompletionArgHelp` are not the same. `Help` is printed in program - * usage (when you call `program --help`), `CompletionHelp` is printed when completer lists available - * options, and `CompletionArgHelp` is printed when completer shows available values for the option. - * - * Example of good help message: - * - * ``` - * opts.AddLongOption('t', "timeout") - * .Help("specify query timeout in milliseconds") - * .CompletionHelp("specify query timeout") - * .CompletionArgHelp("query timeout (ms) [default=500]"); - * ``` - * - * Notice how `Help` and `CompletionArgHelp` have units in them, but `CompletionHelp` don't. - * - * Another good example is the help option: - * - * ``` - * opts.AddLongOption('h', "help") - * .Help("print this message and exit") - * .CompletionHelp("print help message and exit"); - * ``` - * - * Notice how `Help` mentions 'this message', but `CompletionHelp` mentions just 'help message'. - * - * See more on completion descriptions codestyle: - * https://github.com/zsh-users/zsh/blob/master/Etc/completion-style-guide#L43 + * Set help string that appears with `--help`. Unless `CompletionHelp` is given, this message will also be used + * in completion script. In this case, don't make it too long, don't start it with a capital letter and don't + * end it with a full stop. + * + * Note that `Help`, `CompletionHelp` and `CompletionArgHelp` are not the same. `Help` is printed in program + * usage (when you call `program --help`), `CompletionHelp` is printed when completer lists available + * options, and `CompletionArgHelp` is printed when completer shows available values for the option. + * + * Example of good help message: + * + * ``` + * opts.AddLongOption('t', "timeout") + * .Help("specify query timeout in milliseconds") + * .CompletionHelp("specify query timeout") + * .CompletionArgHelp("query timeout (ms) [default=500]"); + * ``` + * + * Notice how `Help` and `CompletionArgHelp` have units in them, but `CompletionHelp` don't. + * + * Another good example is the help option: + * + * ``` + * opts.AddLongOption('h', "help") + * .Help("print this message and exit") + * .CompletionHelp("print help message and exit"); + * ``` + * + * Notice how `Help` mentions 'this message', but `CompletionHelp` mentions just 'help message'. + * + * See more on completion descriptions codestyle: + * https://github.com/zsh-users/zsh/blob/master/Etc/completion-style-guide#L43 */ TOpt& Help(const TString& help) { Help_ = help; @@ -373,178 +373,178 @@ namespace NLastGetopt { } /** - * Get help string. + * Get help string. */ - const TString& GetHelp() const { + const TString& GetHelp() const { return Help_; } /** - * Set help string that appears when argument completer lists available options. - * - * See `Help` function for info on how this is different from setting `Help` and `CompletionArgHelp`. - * - * Use shorter messages for this message. Don't start them with a capital letter and don't end them - * with a full stop. De aware that argument name and default value will not be printed by completer. - * - * In zsh, these messages will look like this: - * - * ``` - * $ program -<tab><tab> - * -- option -- - * --help -h -- print help message and exit - * --timeout -t -- specify query timeout - * ``` - */ - TOpt& CompletionHelp(const TString& help) { - CompletionHelp_ = help; - return *this; - } - - /** - * Get help string that appears when argument completer lists available options. - */ - const TString& GetCompletionHelp() const { - return CompletionHelp_ ? CompletionHelp_ : Help_; - } - - /** - * Set help string that appears when completer suggests available values. - * - * See `Help` function for info on how this is different from setting `Help` and `CompletionHelp`. - * - * In zsh, these messages will look like this: - * - * ``` - * $ program --timeout <tab><tab> - * -- query timeout (ms) [default=500] -- - * 50 100 250 500 1000 - * ``` - */ - TOpt& CompletionArgHelp(const TString& help) { - CompletionArgHelp_ = help; - return *this; - } - - /** - * @return argument help string for use in completion script. - */ - const TString& GetCompletionArgHelp() const { - return CompletionArgHelp_ ? CompletionArgHelp_ : ArgTitle_; - } - - /** - * Let the completer know that this option can occur more than once. - */ - TOpt& AllowMultipleCompletion(bool allowMultipleCompletion = true) { - AllowMultipleCompletion_ = allowMultipleCompletion; - return *this; - } - - /** - * @return true if completer will offer completion for this option multiple times. - */ - bool MultipleCompletionAllowed() const { - return AllowMultipleCompletion_; - } - - /** - * Tell the completer to disable further completion if this option is present. - * This is useful for options like `--help`. - * - * Note: this only works in zsh. - * - * @return self - */ - TOpt& IfPresentDisableCompletion(bool value = true) { - IfPresentDisableCompletionForOptions(value); - IfPresentDisableCompletionForFreeArgs(value); - return *this; - } - - /** - * Tell the completer to disable completion for all options if this option is already present in the input. - * Free arguments will still be completed. - * - * Note: this only works in zsh. - * - * @return self - */ - TOpt& IfPresentDisableCompletionForOptions(bool value = true) { - DisableCompletionForOptions_ = value; - return *this; - } - - /** - * Tell the completer to disable option `c` if this option is already present in the input. - * For example, if you have two options `-a` and `-r` that are mutually exclusive, disable `-r` for `-a` and - * disable `-a` for `-r`, like this: - * - * ``` - * opts.AddLongOption('a', "acquire").IfPresentDisableCompletionFor('r'); - * opts.AddLongOption('r', "release").IfPresentDisableCompletionFor('a'); - * ``` - * - * This way, if user enabled option `-a`, completer will not suggest option `-r`. - * - * Note that we don't have to disable all flags for a single option. That is, disabling `-r` in the above - * example disables `--release` automatically. - * - * Note: this only works in zsh. - * - * @param c char option that should be disabled when completer hits this option. - */ - TOpt& IfPresentDisableCompletionFor(char c) { - DisableCompletionForChar_.push_back(c); - return *this; - } - - /** - * Like `IfPresentDisableCompletionFor(char c)`, but for long options. - */ - TOpt& IfPresentDisableCompletionFor(const TString& name) { - DisableCompletionForLongName_.push_back(name); - return *this; - } - - /** - * Like `IfPresentDisableCompletionFor(char c)`, but for long options. - */ - TOpt& IfPresentDisableCompletionFor(const TOpt& opt); - - /** - * Tell the completer to disable completion for the given free argument if this option is present. - * - * Note: this only works in zsh. - * - * @param arg index of free arg - */ - TOpt& IfPresentDisableCompletionForFreeArg(size_t index) { - DisableCompletionForFreeArg_.push_back(index); - return *this; - } - - /** - * Assign a completer for this option. - */ - TOpt& Completer(NComp::ICompleterPtr completer) { - Completer_ = std::move(completer); - return *this; - } - - /** - * Tell the completer to disable completion for the all free arguments if this option is present. - * - * Note: this only works in zsh. - */ - TOpt& IfPresentDisableCompletionForFreeArgs(bool value = true) { - DisableCompletionForFreeArgs_ = value; - return *this; - } - - /** - * Run handlers for this option. - */ + * Set help string that appears when argument completer lists available options. + * + * See `Help` function for info on how this is different from setting `Help` and `CompletionArgHelp`. + * + * Use shorter messages for this message. Don't start them with a capital letter and don't end them + * with a full stop. De aware that argument name and default value will not be printed by completer. + * + * In zsh, these messages will look like this: + * + * ``` + * $ program -<tab><tab> + * -- option -- + * --help -h -- print help message and exit + * --timeout -t -- specify query timeout + * ``` + */ + TOpt& CompletionHelp(const TString& help) { + CompletionHelp_ = help; + return *this; + } + + /** + * Get help string that appears when argument completer lists available options. + */ + const TString& GetCompletionHelp() const { + return CompletionHelp_ ? CompletionHelp_ : Help_; + } + + /** + * Set help string that appears when completer suggests available values. + * + * See `Help` function for info on how this is different from setting `Help` and `CompletionHelp`. + * + * In zsh, these messages will look like this: + * + * ``` + * $ program --timeout <tab><tab> + * -- query timeout (ms) [default=500] -- + * 50 100 250 500 1000 + * ``` + */ + TOpt& CompletionArgHelp(const TString& help) { + CompletionArgHelp_ = help; + return *this; + } + + /** + * @return argument help string for use in completion script. + */ + const TString& GetCompletionArgHelp() const { + return CompletionArgHelp_ ? CompletionArgHelp_ : ArgTitle_; + } + + /** + * Let the completer know that this option can occur more than once. + */ + TOpt& AllowMultipleCompletion(bool allowMultipleCompletion = true) { + AllowMultipleCompletion_ = allowMultipleCompletion; + return *this; + } + + /** + * @return true if completer will offer completion for this option multiple times. + */ + bool MultipleCompletionAllowed() const { + return AllowMultipleCompletion_; + } + + /** + * Tell the completer to disable further completion if this option is present. + * This is useful for options like `--help`. + * + * Note: this only works in zsh. + * + * @return self + */ + TOpt& IfPresentDisableCompletion(bool value = true) { + IfPresentDisableCompletionForOptions(value); + IfPresentDisableCompletionForFreeArgs(value); + return *this; + } + + /** + * Tell the completer to disable completion for all options if this option is already present in the input. + * Free arguments will still be completed. + * + * Note: this only works in zsh. + * + * @return self + */ + TOpt& IfPresentDisableCompletionForOptions(bool value = true) { + DisableCompletionForOptions_ = value; + return *this; + } + + /** + * Tell the completer to disable option `c` if this option is already present in the input. + * For example, if you have two options `-a` and `-r` that are mutually exclusive, disable `-r` for `-a` and + * disable `-a` for `-r`, like this: + * + * ``` + * opts.AddLongOption('a', "acquire").IfPresentDisableCompletionFor('r'); + * opts.AddLongOption('r', "release").IfPresentDisableCompletionFor('a'); + * ``` + * + * This way, if user enabled option `-a`, completer will not suggest option `-r`. + * + * Note that we don't have to disable all flags for a single option. That is, disabling `-r` in the above + * example disables `--release` automatically. + * + * Note: this only works in zsh. + * + * @param c char option that should be disabled when completer hits this option. + */ + TOpt& IfPresentDisableCompletionFor(char c) { + DisableCompletionForChar_.push_back(c); + return *this; + } + + /** + * Like `IfPresentDisableCompletionFor(char c)`, but for long options. + */ + TOpt& IfPresentDisableCompletionFor(const TString& name) { + DisableCompletionForLongName_.push_back(name); + return *this; + } + + /** + * Like `IfPresentDisableCompletionFor(char c)`, but for long options. + */ + TOpt& IfPresentDisableCompletionFor(const TOpt& opt); + + /** + * Tell the completer to disable completion for the given free argument if this option is present. + * + * Note: this only works in zsh. + * + * @param arg index of free arg + */ + TOpt& IfPresentDisableCompletionForFreeArg(size_t index) { + DisableCompletionForFreeArg_.push_back(index); + return *this; + } + + /** + * Assign a completer for this option. + */ + TOpt& Completer(NComp::ICompleterPtr completer) { + Completer_ = std::move(completer); + return *this; + } + + /** + * Tell the completer to disable completion for the all free arguments if this option is present. + * + * Note: this only works in zsh. + */ + TOpt& IfPresentDisableCompletionForFreeArgs(bool value = true) { + DisableCompletionForFreeArgs_ = value; + return *this; + } + + /** + * Run handlers for this option. + */ void FireHandlers(const TOptsParser* parser) const; private: @@ -716,97 +716,97 @@ namespace NLastGetopt { */ struct TFreeArgSpec { TFreeArgSpec() = default; - TFreeArgSpec(const TString& title, const TString& help = TString(), bool optional = false) - : Title_(title) - , Help_(help) - , Optional_(optional) + TFreeArgSpec(const TString& title, const TString& help = TString(), bool optional = false) + : Title_(title) + , Help_(help) + , Optional_(optional) { } - TString Title_; - TString Help_; - TString CompletionArgHelp_; - - bool Optional_ = false; - NComp::ICompleterPtr Completer_ = nullptr; - - /** - * Check if this argument have default values for its title and help. - */ - bool IsDefault() const { - return Title_.empty() && Help_.empty(); - } - - /** - * Set argument title. - */ - TFreeArgSpec& Title(TString title) { - Title_ = std::move(title); - return *this; - } - - /** - * Get argument title. If title is empty, returns a default one. - */ - TStringBuf GetTitle(TStringBuf defaultTitle) const { - return Title_ ? TStringBuf(Title_) : defaultTitle; - } - - /** - * Set help string that appears with `--help`. Unless `CompletionHelp` is given, this message will also be used - * in completion script. In this case, don't make it too long, don't start it with a capital letter and don't - * end it with a full stop. - * - * See `TOpt::Help` function for more on how `Help` and `CompletionArgHelp` differ one from another. - */ - TFreeArgSpec& Help(TString help) { - Help_ = std::move(help); - return *this; - } - - /** - * Get help string that appears with `--help`. - */ - TStringBuf GetHelp() const { - return Help_; - } - - /** - * Set help string that appears when completer suggests values fot this argument. - */ - TFreeArgSpec& CompletionArgHelp(TString completionArgHelp) { - CompletionArgHelp_ = std::move(completionArgHelp); - return *this; - } - - /** - * Get help string that appears when completer suggests values fot this argument. - */ - TStringBuf GetCompletionArgHelp(TStringBuf defaultTitle) const { - return CompletionArgHelp_ ? TStringBuf(CompletionArgHelp_) : GetTitle(defaultTitle); - } - - /** - * Mark this argument as optional. This setting only affects help printing, it doesn't affect parsing. - */ - TFreeArgSpec& Optional(bool optional = true) { - Optional_ = optional; - return *this; - } - - /** - * Check if this argument is optional. - */ - bool IsOptional() const { - return Optional_; - } - - /** - * Set completer for this argument. - */ - TFreeArgSpec& Completer(NComp::ICompleterPtr completer) { - Completer_ = std::move(completer); - return *this; - } + TString Title_; + TString Help_; + TString CompletionArgHelp_; + + bool Optional_ = false; + NComp::ICompleterPtr Completer_ = nullptr; + + /** + * Check if this argument have default values for its title and help. + */ + bool IsDefault() const { + return Title_.empty() && Help_.empty(); + } + + /** + * Set argument title. + */ + TFreeArgSpec& Title(TString title) { + Title_ = std::move(title); + return *this; + } + + /** + * Get argument title. If title is empty, returns a default one. + */ + TStringBuf GetTitle(TStringBuf defaultTitle) const { + return Title_ ? TStringBuf(Title_) : defaultTitle; + } + + /** + * Set help string that appears with `--help`. Unless `CompletionHelp` is given, this message will also be used + * in completion script. In this case, don't make it too long, don't start it with a capital letter and don't + * end it with a full stop. + * + * See `TOpt::Help` function for more on how `Help` and `CompletionArgHelp` differ one from another. + */ + TFreeArgSpec& Help(TString help) { + Help_ = std::move(help); + return *this; + } + + /** + * Get help string that appears with `--help`. + */ + TStringBuf GetHelp() const { + return Help_; + } + + /** + * Set help string that appears when completer suggests values fot this argument. + */ + TFreeArgSpec& CompletionArgHelp(TString completionArgHelp) { + CompletionArgHelp_ = std::move(completionArgHelp); + return *this; + } + + /** + * Get help string that appears when completer suggests values fot this argument. + */ + TStringBuf GetCompletionArgHelp(TStringBuf defaultTitle) const { + return CompletionArgHelp_ ? TStringBuf(CompletionArgHelp_) : GetTitle(defaultTitle); + } + + /** + * Mark this argument as optional. This setting only affects help printing, it doesn't affect parsing. + */ + TFreeArgSpec& Optional(bool optional = true) { + Optional_ = optional; + return *this; + } + + /** + * Check if this argument is optional. + */ + bool IsOptional() const { + return Optional_; + } + + /** + * Set completer for this argument. + */ + TFreeArgSpec& Completer(NComp::ICompleterPtr completer) { + Completer_ = std::move(completer); + return *this; + } }; } diff --git a/library/cpp/getopt/small/last_getopt_opts.cpp b/library/cpp/getopt/small/last_getopt_opts.cpp index 936489c200..03c432849f 100644 --- a/library/cpp/getopt/small/last_getopt_opts.cpp +++ b/library/cpp/getopt/small/last_getopt_opts.cpp @@ -1,12 +1,12 @@ -#include "completer_command.h" +#include "completer_command.h" #include "last_getopt_opts.h" -#include "wrap.h" -#include "last_getopt_parser.h" +#include "wrap.h" +#include "last_getopt_parser.h" #include <library/cpp/colorizer/colors.h> #include <util/stream/format.h> -#include <util/charset/utf8.h> +#include <util/charset/utf8.h> #include <stdlib.h> @@ -70,7 +70,7 @@ namespace NLastGetopt { , AllowUnknownCharOptions_(false) , AllowUnknownLongOptions_(false) , FreeArgsMin_(0) - , FreeArgsMax_(UNLIMITED_ARGS) + , FreeArgsMax_(UNLIMITED_ARGS) { if (!optstring.empty()) { AddCharOptions(optstring); @@ -225,33 +225,33 @@ namespace NLastGetopt { return *Opts_.back(); } - TOpt& TOpts::AddCompletionOption(TString command, TString longName) { - if (TOpt* o = FindLongOption(longName)) { - return *o; - } - - return AddOption(MakeCompletionOpt(this, std::move(command), std::move(longName))); - } - - namespace { - auto MutuallyExclusiveHandler(const TOpt* cur, const TOpt* other) { - return [cur, other](const TOptsParser* p) { - if (p->Seen(other)) { - throw TUsageException() - << "option " << cur->ToShortString() - << " can't appear together with option " << other->ToShortString(); - } - }; - } - } - - void TOpts::MutuallyExclusiveOpt(TOpt& opt1, TOpt& opt2) { - opt1.Handler1(MutuallyExclusiveHandler(&opt1, &opt2)) - .IfPresentDisableCompletionFor(opt2); - opt2.Handler1(MutuallyExclusiveHandler(&opt2, &opt1)) - .IfPresentDisableCompletionFor(opt1); - } - + TOpt& TOpts::AddCompletionOption(TString command, TString longName) { + if (TOpt* o = FindLongOption(longName)) { + return *o; + } + + return AddOption(MakeCompletionOpt(this, std::move(command), std::move(longName))); + } + + namespace { + auto MutuallyExclusiveHandler(const TOpt* cur, const TOpt* other) { + return [cur, other](const TOptsParser* p) { + if (p->Seen(other)) { + throw TUsageException() + << "option " << cur->ToShortString() + << " can't appear together with option " << other->ToShortString(); + } + }; + } + } + + void TOpts::MutuallyExclusiveOpt(TOpt& opt1, TOpt& opt2) { + opt1.Handler1(MutuallyExclusiveHandler(&opt1, &opt2)) + .IfPresentDisableCompletionFor(opt2); + opt2.Handler1(MutuallyExclusiveHandler(&opt2, &opt1)) + .IfPresentDisableCompletionFor(opt1); + } + size_t TOpts::IndexOf(const TOpt* opt) const { TOptsVector::const_iterator it = std::find(Opts_.begin(), Opts_.end(), opt); if (it == Opts_.end()) @@ -259,19 +259,19 @@ namespace NLastGetopt { return it - Opts_.begin(); } - TStringBuf TOpts::GetFreeArgTitle(size_t pos) const { + TStringBuf TOpts::GetFreeArgTitle(size_t pos) const { if (FreeArgSpecs_.contains(pos)) { - return FreeArgSpecs_.at(pos).GetTitle(DefaultFreeArgTitle_); + return FreeArgSpecs_.at(pos).GetTitle(DefaultFreeArgTitle_); } - return DefaultFreeArgTitle_; + return DefaultFreeArgTitle_; } - void TOpts::SetFreeArgTitle(size_t pos, const TString& title, const TString& help, bool optional) { - FreeArgSpecs_[pos] = TFreeArgSpec(title, help, optional); + void TOpts::SetFreeArgTitle(size_t pos, const TString& title, const TString& help, bool optional) { + FreeArgSpecs_[pos] = TFreeArgSpec(title, help, optional); } - TFreeArgSpec& TOpts::GetFreeArgSpec(size_t pos) { - return FreeArgSpecs_[pos]; + TFreeArgSpec& TOpts::GetFreeArgSpec(size_t pos) { + return FreeArgSpecs_[pos]; } static TString FormatOption(const TOpt* option, const NColorizer::TColors& colors) { @@ -325,29 +325,29 @@ namespace NLastGetopt { } os << "[OPTIONS]"; - ui32 numDescribedFlags = FreeArgSpecs_.empty() ? 0 : FreeArgSpecs_.rbegin()->first + 1; - ui32 numArgsToShow = Max(FreeArgsMin_, FreeArgsMax_ == UNLIMITED_ARGS ? numDescribedFlags : FreeArgsMax_); - - for (ui32 i = 0, nonOptionalFlagsPrinted = 0; i < numArgsToShow; ++i) { - bool isOptional = nonOptionalFlagsPrinted >= FreeArgsMin_ || FreeArgSpecs_.Value(i, TFreeArgSpec()).Optional_; - - nonOptionalFlagsPrinted += !isOptional; - - os << " "; - - if (isOptional) - os << "["; - - os << GetFreeArgTitle(i); - - if (isOptional) - os << "]"; - } - - if (FreeArgsMax_ == UNLIMITED_ARGS) { - os << " [" << TrailingArgSpec_.GetTitle(DefaultFreeArgTitle_) << "]..."; - } - + ui32 numDescribedFlags = FreeArgSpecs_.empty() ? 0 : FreeArgSpecs_.rbegin()->first + 1; + ui32 numArgsToShow = Max(FreeArgsMin_, FreeArgsMax_ == UNLIMITED_ARGS ? numDescribedFlags : FreeArgsMax_); + + for (ui32 i = 0, nonOptionalFlagsPrinted = 0; i < numArgsToShow; ++i) { + bool isOptional = nonOptionalFlagsPrinted >= FreeArgsMin_ || FreeArgSpecs_.Value(i, TFreeArgSpec()).Optional_; + + nonOptionalFlagsPrinted += !isOptional; + + os << " "; + + if (isOptional) + os << "["; + + os << GetFreeArgTitle(i); + + if (isOptional) + os << "]"; + } + + if (FreeArgsMax_ == UNLIMITED_ARGS) { + os << " [" << TrailingArgSpec_.GetTitle(DefaultFreeArgTitle_) << "]..."; + } + os << Endl; } @@ -361,7 +361,7 @@ namespace NLastGetopt { TVector<TString> leftColumn(Opts_.size()); TVector<size_t> leftColumnSizes(leftColumn.size()); - const size_t kMaxLeftWidth = 25; + const size_t kMaxLeftWidth = 25; size_t leftWidth = 0; size_t requiredOptionsCount = 0; NColorizer::TColors disabledColors(false); @@ -371,14 +371,14 @@ namespace NLastGetopt { if (opt->IsHidden()) continue; leftColumn[i] = FormatOption(opt, colors); - size_t leftColumnSize = leftColumn[i].size(); - if (colors.IsTTY()) { - leftColumnSize -= NColorizer::TotalAnsiEscapeCodeLen(leftColumn[i]); - } + size_t leftColumnSize = leftColumn[i].size(); + if (colors.IsTTY()) { + leftColumnSize -= NColorizer::TotalAnsiEscapeCodeLen(leftColumn[i]); + } leftColumnSizes[i] = leftColumnSize; - if (leftColumnSize <= kMaxLeftWidth) { - leftWidth = Max(leftWidth, leftColumnSize); - } + if (leftColumnSize <= kMaxLeftWidth) { + leftWidth = Max(leftWidth, leftColumnSize); + } if (opt->IsRequired()) requiredOptionsCount++; } @@ -410,59 +410,59 @@ namespace NLastGetopt { continue; if (leftColumnSizes[i] > leftWidth && !opt->GetHelp().empty()) { - os << SPad << leftColumn[i] << Endl << SPad << leftPadding << ' '; + os << SPad << leftColumn[i] << Endl << SPad << leftPadding << ' '; } else { os << SPad << leftColumn[i] << ' '; if (leftColumnSizes[i] < leftWidth) os << TStringBuf(leftPadding.data(), leftWidth - leftColumnSizes[i]); } - TStringBuf help = opt->GetHelp(); - while (help && isspace(help.back())) { - help.Chop(1); + TStringBuf help = opt->GetHelp(); + while (help && isspace(help.back())) { + help.Chop(1); + } + size_t lastLineLength = 0; + bool helpHasParagraphs = false; + if (help) { + os << Wrap(Wrap_, help, SPad + leftPadding + " ", &lastLineLength, &helpHasParagraphs); } - size_t lastLineLength = 0; - bool helpHasParagraphs = false; - if (help) { - os << Wrap(Wrap_, help, SPad + leftPadding + " ", &lastLineLength, &helpHasParagraphs); - } if (opt->HasDefaultValue()) { - auto quotedDef = QuoteForHelp(opt->GetDefaultValue()); - if (helpHasParagraphs) { - os << Endl << Endl << SPad << leftPadding << " "; - os << "Default: " << colors.CyanColor() << quotedDef << colors.OldColor() << "."; - } else if (help.EndsWith('.')) { - os << Endl << SPad << leftPadding << " "; - os << "Default: " << colors.CyanColor() << quotedDef << colors.OldColor() << "."; - } else if (help) { - if (SPad.size() + leftWidth + 1 + lastLineLength + 12 + quotedDef.size() > Wrap_) { - os << Endl << SPad << leftPadding << " "; - } else { - os << " "; - } - os << "(default: " << colors.CyanColor() << quotedDef << colors.OldColor() << ")"; - } else { - os << "default: " << colors.CyanColor() << quotedDef << colors.OldColor(); - } + auto quotedDef = QuoteForHelp(opt->GetDefaultValue()); + if (helpHasParagraphs) { + os << Endl << Endl << SPad << leftPadding << " "; + os << "Default: " << colors.CyanColor() << quotedDef << colors.OldColor() << "."; + } else if (help.EndsWith('.')) { + os << Endl << SPad << leftPadding << " "; + os << "Default: " << colors.CyanColor() << quotedDef << colors.OldColor() << "."; + } else if (help) { + if (SPad.size() + leftWidth + 1 + lastLineLength + 12 + quotedDef.size() > Wrap_) { + os << Endl << SPad << leftPadding << " "; + } else { + os << " "; + } + os << "(default: " << colors.CyanColor() << quotedDef << colors.OldColor() << ")"; + } else { + os << "default: " << colors.CyanColor() << quotedDef << colors.OldColor(); + } } os << Endl; - - if (helpHasParagraphs) { - os << Endl; - } + + if (helpHasParagraphs) { + os << Endl; + } } } - + PrintFreeArgsDesc(os, colors); - - for (auto& [heading, text] : Sections) { - os << Endl << colors.BoldColor() << heading << colors.OldColor() << ":" << Endl; - - os << SPad << Wrap(Wrap_, text, SPad) << Endl; - } - + + for (auto& [heading, text] : Sections) { + os << Endl << colors.BoldColor() << heading << colors.OldColor() << ":" << Endl; + + os << SPad << Wrap(Wrap_, text, SPad) << Endl; + } + osIn << os.Str(); } @@ -479,8 +479,8 @@ namespace NLastGetopt { leftFreeWidth = Max(leftFreeWidth, GetFreeArgTitle(i).size()); } - if (!TrailingArgSpec_.IsDefault()) { - leftFreeWidth = Max(leftFreeWidth, TrailingArgSpec_.GetTitle(DefaultFreeArgTitle_).size()); + if (!TrailingArgSpec_.IsDefault()) { + leftFreeWidth = Max(leftFreeWidth, TrailingArgSpec_.GetTitle(DefaultFreeArgTitle_).size()); } leftFreeWidth = Min(leftFreeWidth, size_t(30)); @@ -488,32 +488,32 @@ namespace NLastGetopt { os << " min: " << colors.GreenColor() << FreeArgsMin_ << colors.OldColor() << ","; os << " max: " << colors.GreenColor(); - if (FreeArgsMax_ != UNLIMITED_ARGS) { + if (FreeArgsMax_ != UNLIMITED_ARGS) { os << FreeArgsMax_; } else { os << "unlimited"; } - os << colors.OldColor() << Endl; + os << colors.OldColor() << Endl; const size_t limit = FreeArgSpecs_.empty() ? 0 : FreeArgSpecs_.rbegin()->first; for (size_t i = 0; i <= limit; ++i) { - if (!FreeArgSpecs_.contains(i)) { - continue; - } - - if (auto help = FreeArgSpecs_.at(i).GetHelp()) { - auto title = GetFreeArgTitle(i); - os << SPad << colors.GreenColor() << RightPad(title, leftFreeWidth, ' ') << colors.OldColor() - << SPad << help << Endl; - } - } - - if (FreeArgsMax_ == UNLIMITED_ARGS) { - auto title = TrailingArgSpec_.GetTitle(DefaultFreeArgTitle_); - if (auto help = TrailingArgSpec_.GetHelp()) { - os << SPad << colors.GreenColor() << RightPad(title, leftFreeWidth, ' ') << colors.OldColor() - << SPad << help << Endl; - } + if (!FreeArgSpecs_.contains(i)) { + continue; + } + + if (auto help = FreeArgSpecs_.at(i).GetHelp()) { + auto title = GetFreeArgTitle(i); + os << SPad << colors.GreenColor() << RightPad(title, leftFreeWidth, ' ') << colors.OldColor() + << SPad << help << Endl; + } + } + + if (FreeArgsMax_ == UNLIMITED_ARGS) { + auto title = TrailingArgSpec_.GetTitle(DefaultFreeArgTitle_); + if (auto help = TrailingArgSpec_.GetHelp()) { + os << SPad << colors.GreenColor() << RightPad(title, leftFreeWidth, ' ') << colors.OldColor() + << SPad << help << Endl; + } } } } diff --git a/library/cpp/getopt/small/last_getopt_opts.h b/library/cpp/getopt/small/last_getopt_opts.h index fe4f3b9a7d..825b99c871 100644 --- a/library/cpp/getopt/small/last_getopt_opts.h +++ b/library/cpp/getopt/small/last_getopt_opts.h @@ -15,32 +15,32 @@ namespace NLastGetopt { }; /** - * NLastGetopt::TOpts is a storage of program options' parse rules. - * It contains information about all options, free args, some parsing options - * and rules about interaction between options. - * - * The main point for defining program options. - * - * The parsing rules determined by the following parts: - * - Arguments permutation. It is expected free args be after named args. - * This point adjusts how to treat breaking this expectation. - * if REQUIRE_ORDER is choosen, the exception during parsing will be raised, - * the special string " -- " will be treated as end of named - * options: all options after it will be parsed as free args - * if PERMUTE is choosen, arguments will be rearranged in correct order, - * if RETURN_IN_ORDER is choosen, all free args will be ommited (TODO: looks very strange) - * - Using '+' as a prefix instead '--' for long names - * - Using "-" as a prefix for both short and long names - * - Allowing unknown options - * - */ + * NLastGetopt::TOpts is a storage of program options' parse rules. + * It contains information about all options, free args, some parsing options + * and rules about interaction between options. + * + * The main point for defining program options. + * + * The parsing rules determined by the following parts: + * - Arguments permutation. It is expected free args be after named args. + * This point adjusts how to treat breaking this expectation. + * if REQUIRE_ORDER is choosen, the exception during parsing will be raised, + * the special string " -- " will be treated as end of named + * options: all options after it will be parsed as free args + * if PERMUTE is choosen, arguments will be rearranged in correct order, + * if RETURN_IN_ORDER is choosen, all free args will be ommited (TODO: looks very strange) + * - Using '+' as a prefix instead '--' for long names + * - Using "-" as a prefix for both short and long names + * - Allowing unknown options + * + */ class TOpts { friend class TOptsParseResult; friend class TOptsParser; public: - static constexpr const ui32 UNLIMITED_ARGS = Max<ui32>(); - + static constexpr const ui32 UNLIMITED_ARGS = Max<ui32>(); + typedef TVector<TSimpleSharedPtr<TOpt>> TOptsVector; TOptsVector Opts_; // infomation about named (short and long) options TVector<std::function<void(TStringBuf)>> ArgBindings_; @@ -53,32 +53,32 @@ namespace NLastGetopt { bool AllowUnknownCharOptions_ = false; bool AllowUnknownLongOptions_ = false; - ui32 Wrap_ = 80; - + ui32 Wrap_ = 80; + private: ui32 FreeArgsMin_; // minimal number of free args ui32 FreeArgsMax_; // maximal number of free args - TMap<ui32, TFreeArgSpec> FreeArgSpecs_; // mapping [free arg position] -> [free arg specification] - TFreeArgSpec TrailingArgSpec_; // spec for the trailing argument (when arguments are unlimited) - TString DefaultFreeArgTitle_ = "ARG"; // title that's used for free args without a title + TMap<ui32, TFreeArgSpec> FreeArgSpecs_; // mapping [free arg position] -> [free arg specification] + TFreeArgSpec TrailingArgSpec_; // spec for the trailing argument (when arguments are unlimited) + TString DefaultFreeArgTitle_ = "ARG"; // title that's used for free args without a title TString Title; // title of the help string TString CustomCmdLineDescr; // user defined help string TString CustomUsage; // user defined usage string - TVector<std::pair<TString, TString>> Sections; // additional help entries to print after usage - + TVector<std::pair<TString, TString>> Sections; // additional help entries to print after usage + public: /** - * Constructs TOpts from string as in getopt(3) - */ + * Constructs TOpts from string as in getopt(3) + */ TOpts(const TStringBuf& optstring = TStringBuf()); /** - * Constructs TOpts from string as in getopt(3) and - * additionally adds help option (for '?') and svn-verstion option (for 'V') - */ + * Constructs TOpts from string as in getopt(3) and + * additionally adds help option (for '?') and svn-verstion option (for 'V') + */ static TOpts Default(const TStringBuf& optstring = TStringBuf()) { TOpts opts(optstring); opts.AddHelpOption(); @@ -87,161 +87,161 @@ namespace NLastGetopt { } /** - * Checks correctness of options' descriptions. - * Throws TConfException if validation failed. - * Check consist of: - * -not intersecting of names - * -compability of settings, that responsable for freeArgs parsing - */ + * Checks correctness of options' descriptions. + * Throws TConfException if validation failed. + * Check consist of: + * -not intersecting of names + * -compability of settings, that responsable for freeArgs parsing + */ void Validate() const; /** - * Search for the option with given long name - * @param name long name for search - * @return ptr on result (nullptr if not found) - */ + * Search for the option with given long name + * @param name long name for search + * @return ptr on result (nullptr if not found) + */ const TOpt* FindLongOption(const TStringBuf& name) const; /** - * Search for the option with given short name - * @param c short name for search - * @return ptr on result (nullptr if not found) - */ + * Search for the option with given short name + * @param c short name for search + * @return ptr on result (nullptr if not found) + */ const TOpt* FindCharOption(char c) const; /** - * Search for the option with given long name - * @param name long name for search - * @return ptr on result (nullptr if not found) - */ + * Search for the option with given long name + * @param name long name for search + * @return ptr on result (nullptr if not found) + */ TOpt* FindLongOption(const TStringBuf& name); /** - * Search for the option with given short name - * @param c short name for search - * @return ptr on result (nullptr if not found) - */ + * Search for the option with given short name + * @param c short name for search + * @return ptr on result (nullptr if not found) + */ TOpt* FindCharOption(char c); /** - * Search for the option with given name - * @param name name for search - * @return ptr on result (nullptr if not found) - */ - /// @{ - - const TOpt* FindOption(const TStringBuf& name) const { - return FindLongOption(name); - } - - TOpt* FindOption(const TStringBuf& name) { - return FindLongOption(name); - } - - const TOpt* FindOption(char c) const { - return FindCharOption(c); - } - - TOpt* FindOption(char c) { - return FindCharOption(c); - } - - /// @} - - /** - * Sets title of the help string - * @param title title to set - */ + * Search for the option with given name + * @param name name for search + * @return ptr on result (nullptr if not found) + */ + /// @{ + + const TOpt* FindOption(const TStringBuf& name) const { + return FindLongOption(name); + } + + TOpt* FindOption(const TStringBuf& name) { + return FindLongOption(name); + } + + const TOpt* FindOption(char c) const { + return FindCharOption(c); + } + + TOpt* FindOption(char c) { + return FindCharOption(c); + } + + /// @} + + /** + * Sets title of the help string + * @param title title to set + */ void SetTitle(const TString& title) { Title = title; } /** - * @return true if there is an option with given long name - * - * @param name long name for search - */ + * @return true if there is an option with given long name + * + * @param name long name for search + */ bool HasLongOption(const TString& name) const { return FindLongOption(name) != nullptr; } /** - * @return true if there is an option with given short name - * - * @param char short name for search - */ + * @return true if there is an option with given short name + * + * @param char short name for search + */ bool HasCharOption(char c) const { return FindCharOption(c) != nullptr; } /** - * Search for the option with given long name - * @param name long name for search - * @return ref on result (throw exception if not found) - */ + * Search for the option with given long name + * @param name long name for search + * @return ref on result (throw exception if not found) + */ const TOpt& GetLongOption(const TStringBuf& name) const; /** - * Search for the option with given long name - * @param name long name for search - * @return ref on result (throw exception if not found) - */ + * Search for the option with given long name + * @param name long name for search + * @return ref on result (throw exception if not found) + */ TOpt& GetLongOption(const TStringBuf& name); /** - * Search for the option with given short name - * @param c short name for search - * @return ref on result (throw exception if not found) - */ + * Search for the option with given short name + * @param c short name for search + * @return ref on result (throw exception if not found) + */ const TOpt& GetCharOption(char c) const; /** - * Search for the option with given short name - * @param c short name for search - * @return ref on result (throw exception if not found) - */ + * Search for the option with given short name + * @param c short name for search + * @return ref on result (throw exception if not found) + */ TOpt& GetCharOption(char c); /** - * Search for the option with given name - * @param name name for search - * @return ref on result (throw exception if not found) - */ - /// @{ - - const TOpt& GetOption(const TStringBuf& name) const { - return GetLongOption(name); - } - - TOpt& GetOption(const TStringBuf& name) { - return GetLongOption(name); - } - - const TOpt& GetOption(char c) const { - return GetCharOption(c); - } - - TOpt& GetOption(char c) { - return GetCharOption(c); - } - - /// @} - - /** - * @return true if short options exist - */ + * Search for the option with given name + * @param name name for search + * @return ref on result (throw exception if not found) + */ + /// @{ + + const TOpt& GetOption(const TStringBuf& name) const { + return GetLongOption(name); + } + + TOpt& GetOption(const TStringBuf& name) { + return GetLongOption(name); + } + + const TOpt& GetOption(char c) const { + return GetCharOption(c); + } + + TOpt& GetOption(char c) { + return GetCharOption(c); + } + + /// @} + + /** + * @return true if short options exist + */ bool HasAnyShortOption() const; /** - * @return true if long options exist - */ + * @return true if long options exist + */ bool HasAnyLongOption() const; /** - * Creates new [option description (TOpt)] as a copy of given one - * @param option source - * @return reference for created option - */ + * Creates new [option description (TOpt)] as a copy of given one + * @param option source + * @return reference for created option + */ TOpt& AddOption(const TOpt& option); /** @@ -261,30 +261,30 @@ namespace NLastGetopt { } /** - * Creates options list from string as in getopt(3) - * - * @param optstring source - */ + * Creates options list from string as in getopt(3) + * + * @param optstring source + */ void AddCharOptions(const TStringBuf& optstring); /** - * Creates new [option description (TOpt)] with given short name and given help string - * - * @param c short name - * @param help help string - * @return reference for created option - */ + * Creates new [option description (TOpt)] with given short name and given help string + * + * @param c short name + * @param help help string + * @return reference for created option + */ TOpt& AddCharOption(char c, const TString& help = "") { return AddCharOption(c, DEFAULT_HAS_ARG, help); } /** - * Creates new [option description (TOpt)] with given short name and given help string - * - * @param c short name - * @param help help string - * @return reference for created option - */ + * Creates new [option description (TOpt)] with given short name and given help string + * + * @param c short name + * @param help help string + * @return reference for created option + */ TOpt& AddCharOption(char c, EHasArg hasArg, const TString& help = "") { TOpt option; option.AddShortName(c); @@ -294,24 +294,24 @@ namespace NLastGetopt { } /** - * Creates new [option description (TOpt)] with given long name and given help string - * - * @param name long name - * @param help help string - * @return reference for created option - */ + * Creates new [option description (TOpt)] with given long name and given help string + * + * @param name long name + * @param help help string + * @return reference for created option + */ TOpt& AddLongOption(const TString& name, const TString& help = "") { return AddLongOption(0, name, help); } /** - * Creates new [option description (TOpt)] with given long and short names and given help string - * - * @param c short name - * @param name long name - * @param help help string - * @return reference for created option - */ + * Creates new [option description (TOpt)] with given long and short names and given help string + * + * @param c short name + * @param name long name + * @param help help string + * @return reference for created option + */ TOpt& AddLongOption(char c, const TString& name, const TString& help = "") { TOpt option; if (c != 0) @@ -322,12 +322,12 @@ namespace NLastGetopt { } /** - * Creates new [option description (TOpt)] for help printing, - * adds appropriate handler for it - * If "help" option already exist, will add given short name to it. - * - * @param c new short name for help option - */ + * Creates new [option description (TOpt)] for help printing, + * adds appropriate handler for it + * If "help" option already exist, will add given short name to it. + * + * @param c new short name for help option + */ TOpt& AddHelpOption(char c = '?') { if (TOpt* o = FindLongOption("help")) { if (!o->CharIs(c)) @@ -336,17 +336,17 @@ namespace NLastGetopt { } return AddLongOption(c, "help", "print usage") .HasArg(NO_ARGUMENT) - .IfPresentDisableCompletion() + .IfPresentDisableCompletion() .Handler(&PrintUsageAndExit); } /** - * Creates new [option description (TOpt)] for svn-revision printing, - * adds appropriate handler for it. - * If "svnversion" option already exist, will add given short name to it. - * - * @param c new short name for "svnversion" option - */ + * Creates new [option description (TOpt)] for svn-revision printing, + * adds appropriate handler for it. + * If "svnversion" option already exist, will add given short name to it. + * + * @param c new short name for "svnversion" option + */ TOpt& AddVersionOption(char c = 'V') { if (TOpt* o = FindLongOption("svnrevision")) { if (!o->CharIs(c)) @@ -355,22 +355,22 @@ namespace NLastGetopt { } return AddLongOption(c, "svnrevision", "print svn version") .HasArg(NO_ARGUMENT) - .IfPresentDisableCompletion() + .IfPresentDisableCompletion() .Handler(&PrintVersionAndExit); } /** - * Creates new option for generating completion shell scripts. - * - * @param command name of command that should be completed (typically corresponds to the executable name). - */ - TOpt& AddCompletionOption(TString command, TString longName = "completion"); - - /** - * Creates or finds option with given short name - * - * @param c new short name for search/create - */ + * Creates new option for generating completion shell scripts. + * + * @param command name of command that should be completed (typically corresponds to the executable name). + */ + TOpt& AddCompletionOption(TString command, TString longName = "completion"); + + /** + * Creates or finds option with given short name + * + * @param c new short name for search/create + */ TOpt& CharOption(char c) { const TOpt* opt = FindCharOption(c); if (opt != nullptr) { @@ -382,99 +382,99 @@ namespace NLastGetopt { } /** - * Indicate that some options can't appear together. - * - * Note: this is not transitive. - * - * Note: don't use this on options with default values. If option with default value wasn't specified, - * parser will run handlers for default value, thus triggering a false-positive exclusivity check. - */ - template <typename T1, typename T2> - void MutuallyExclusive(T1&& opt1, T2&& opt2) { - MutuallyExclusiveOpt(GetOption(std::forward<T1>(opt1)), GetOption(std::forward<T2>(opt2))); - } - - /** - * Like `MutuallyExclusive`, but accepts `TOpt`s instead of option names. - */ - void MutuallyExclusiveOpt(TOpt& opt1, TOpt& opt2); - - /** - * @return index of option - * - * @param opt pointer of option to search - */ + * Indicate that some options can't appear together. + * + * Note: this is not transitive. + * + * Note: don't use this on options with default values. If option with default value wasn't specified, + * parser will run handlers for default value, thus triggering a false-positive exclusivity check. + */ + template <typename T1, typename T2> + void MutuallyExclusive(T1&& opt1, T2&& opt2) { + MutuallyExclusiveOpt(GetOption(std::forward<T1>(opt1)), GetOption(std::forward<T2>(opt2))); + } + + /** + * Like `MutuallyExclusive`, but accepts `TOpt`s instead of option names. + */ + void MutuallyExclusiveOpt(TOpt& opt1, TOpt& opt2); + + /** + * @return index of option + * + * @param opt pointer of option to search + */ size_t IndexOf(const TOpt* opt) const; /** - * Replace help string with given - * - * @param decr new help string - */ + * Replace help string with given + * + * @param decr new help string + */ void SetCmdLineDescr(const TString& descr) { CustomCmdLineDescr = descr; } /** - * Replace usage string with given - * - * @param usage new usage string - */ + * Replace usage string with given + * + * @param usage new usage string + */ void SetCustomUsage(const TString& usage) { CustomUsage = usage; } /** - * Add a section to print after the main usage spec. - */ - void AddSection(TString title, TString text) { - Sections.emplace_back(std::move(title), std::move(text)); - } - - /** - * Add section with examples. - * - * @param examples text of this section - */ - void SetExamples(TString examples) { - AddSection("Examples", std::move(examples)); - } - - /** - * Set minimal number of free args - * - * @param min new value - */ + * Add a section to print after the main usage spec. + */ + void AddSection(TString title, TString text) { + Sections.emplace_back(std::move(title), std::move(text)); + } + + /** + * Add section with examples. + * + * @param examples text of this section + */ + void SetExamples(TString examples) { + AddSection("Examples", std::move(examples)); + } + + /** + * Set minimal number of free args + * + * @param min new value + */ void SetFreeArgsMin(size_t min) { FreeArgsMin_ = ui32(min); } - + + /** + * Get current minimal number of free args + */ + ui32 GetFreeArgsMin() const { + return FreeArgsMin_; + } + /** - * Get current minimal number of free args - */ - ui32 GetFreeArgsMin() const { - return FreeArgsMin_; - } - - /** - * Set maximal number of free args - * + * Set maximal number of free args + * * @param max new value - */ + */ void SetFreeArgsMax(size_t max) { FreeArgsMax_ = ui32(max); FreeArgsMax_ = Max<ui32>(FreeArgsMax_, ArgBindings_.size()); } /** - * Get current maximal number of free args - */ - ui32 GetFreeArgsMax() const { - return FreeArgsMax_; - } - - /** + * Get current maximal number of free args + */ + ui32 GetFreeArgsMax() const { + return FreeArgsMax_; + } + + /** * Get mapping for free args */ const TMap<ui32, TFreeArgSpec>& GetFreeArgSpecs() const { @@ -482,161 +482,161 @@ namespace NLastGetopt { } /** - * Set exact expected number of free args - * - * @param count new value - */ + * Set exact expected number of free args + * + * @param count new value + */ void SetFreeArgsNum(size_t count) { FreeArgsMin_ = ui32(count); FreeArgsMax_ = ui32(count); } /** - * Set minimal and maximal number of free args - * - * @param min new value for minimal - * @param max new value for maximal - */ + * Set minimal and maximal number of free args + * + * @param min new value for minimal + * @param max new value for maximal + */ void SetFreeArgsNum(size_t min, size_t max) { FreeArgsMin_ = ui32(min); FreeArgsMax_ = ui32(max); } /** - * Set title and help string of free argument - * - * @param pos index of argument - * @param title new value for argument title - * @param help new value for help string - * @param optional indicates that the flag's help string should be rendered as for optional flag; - * does not affect actual flags parsing - */ - void SetFreeArgTitle(size_t pos, const TString& title, const TString& help = TString(), bool optional = false); - - /** - * Get free argument's spec for further modification. - */ - TFreeArgSpec& GetFreeArgSpec(size_t pos); - - /** - * Legacy, don't use. Same as `SetTrailingArgTitle`. - * Older versions of lastgetopt didn't have destinction between default title and title - * for the trailing argument. - */ - void SetFreeArgDefaultTitle(const TString& title, const TString& help = TString()) { - SetTrailingArgTitle(title, help); - } - - /** - * Set default title that will be used for all arguments that have no title. - */ - void SetDefaultFreeArgTitle(TString title) { - DefaultFreeArgTitle_ = std::move(title); - } - - /** - * Set default title that will be used for all arguments that have no title. - */ - const TString& GetDefaultFreeArgTitle() const { - return DefaultFreeArgTitle_; - } - - /** - * Set title and help for the trailing argument. - * - * This title and help are used to render the last repeated argument when max number of arguments is unlimited. - */ - /// @{ - void SetTrailingArgTitle(TString title) { - TrailingArgSpec_.Title(std::move(title)); - } - void SetTrailingArgTitle(TString title, TString help) { - TrailingArgSpec_.Title(std::move(title)); - TrailingArgSpec_.Help(std::move(help)); - } - /// @} - - /** - * Get spec for the trailing argument. - * - * This spec is used to render the last repeated argument when max number of arguments is unlimited. - */ - /// @{ - TFreeArgSpec& GetTrailingArgSpec() { - return TrailingArgSpec_; - } - const TFreeArgSpec& GetTrailingArgSpec() const { - return TrailingArgSpec_; - } - /// @} - - /** - * Set the rule of parsing single dash as prefix of long names - * - * @param value new value of the option - */ + * Set title and help string of free argument + * + * @param pos index of argument + * @param title new value for argument title + * @param help new value for help string + * @param optional indicates that the flag's help string should be rendered as for optional flag; + * does not affect actual flags parsing + */ + void SetFreeArgTitle(size_t pos, const TString& title, const TString& help = TString(), bool optional = false); + + /** + * Get free argument's spec for further modification. + */ + TFreeArgSpec& GetFreeArgSpec(size_t pos); + + /** + * Legacy, don't use. Same as `SetTrailingArgTitle`. + * Older versions of lastgetopt didn't have destinction between default title and title + * for the trailing argument. + */ + void SetFreeArgDefaultTitle(const TString& title, const TString& help = TString()) { + SetTrailingArgTitle(title, help); + } + + /** + * Set default title that will be used for all arguments that have no title. + */ + void SetDefaultFreeArgTitle(TString title) { + DefaultFreeArgTitle_ = std::move(title); + } + + /** + * Set default title that will be used for all arguments that have no title. + */ + const TString& GetDefaultFreeArgTitle() const { + return DefaultFreeArgTitle_; + } + + /** + * Set title and help for the trailing argument. + * + * This title and help are used to render the last repeated argument when max number of arguments is unlimited. + */ + /// @{ + void SetTrailingArgTitle(TString title) { + TrailingArgSpec_.Title(std::move(title)); + } + void SetTrailingArgTitle(TString title, TString help) { + TrailingArgSpec_.Title(std::move(title)); + TrailingArgSpec_.Help(std::move(help)); + } + /// @} + + /** + * Get spec for the trailing argument. + * + * This spec is used to render the last repeated argument when max number of arguments is unlimited. + */ + /// @{ + TFreeArgSpec& GetTrailingArgSpec() { + return TrailingArgSpec_; + } + const TFreeArgSpec& GetTrailingArgSpec() const { + return TrailingArgSpec_; + } + /// @} + + /** + * Set the rule of parsing single dash as prefix of long names + * + * @param value new value of the option + */ void SetAllowSingleDashForLong(bool value) { AllowSingleDashForLong_ = value; } /** - * Wrap help text at this number of characters. 0 to disable wrapping. - */ - void SetWrap(ui32 wrap = 80) { - Wrap_ = wrap; - } - - /** - * Print usage string - * - * @param program prefix of result (path to the program) - * @param os destination stream - * @param colors colorizer - */ + * Wrap help text at this number of characters. 0 to disable wrapping. + */ + void SetWrap(ui32 wrap = 80) { + Wrap_ = wrap; + } + + /** + * Print usage string + * + * @param program prefix of result (path to the program) + * @param os destination stream + * @param colors colorizer + */ void PrintUsage(const TStringBuf& program, IOutputStream& os, const NColorizer::TColors& colors) const; /** - * Print usage string - * - * @param program prefix of result (path to the program) - * @param os destination stream - */ + * Print usage string + * + * @param program prefix of result (path to the program) + * @param os destination stream + */ void PrintUsage(const TStringBuf& program, IOutputStream& os = Cout) const; /** - * Get list of options in order of definition. - */ - TVector<const TOpt*> GetOpts() const { - auto ret = TVector<const TOpt*>(Reserve(Opts_.size())); - for (auto& opt : Opts_) { - ret.push_back(opt.Get()); - } - return ret; - } + * Get list of options in order of definition. + */ + TVector<const TOpt*> GetOpts() const { + auto ret = TVector<const TOpt*>(Reserve(Opts_.size())); + for (auto& opt : Opts_) { + ret.push_back(opt.Get()); + } + return ret; + } - private: + private: /** - * @return argument title of a free argument - * - * @param pos position of the argument - */ - TStringBuf GetFreeArgTitle(size_t pos) const; + * @return argument title of a free argument + * + * @param pos position of the argument + */ + TStringBuf GetFreeArgTitle(size_t pos) const; /** - * Print usage helper - * - * @param program prefix of result (path to the program) - * @param os destination stream - * @param colors colorizer - */ + * Print usage helper + * + * @param program prefix of result (path to the program) + * @param os destination stream + * @param colors colorizer + */ void PrintCmdLine(const TStringBuf& program, IOutputStream& os, const NColorizer::TColors& colors) const; /** - * Print usage helper - * - * @param os destination stream - * @param colors colorizer - */ + * Print usage helper + * + * @param os destination stream + * @param colors colorizer + */ void PrintFreeArgsDesc(IOutputStream& os, const NColorizer::TColors& colors) const; }; diff --git a/library/cpp/getopt/small/last_getopt_parse_result.cpp b/library/cpp/getopt/small/last_getopt_parse_result.cpp index ac27736a14..f4b5607a90 100644 --- a/library/cpp/getopt/small/last_getopt_parse_result.cpp +++ b/library/cpp/getopt/small/last_getopt_parse_result.cpp @@ -42,7 +42,7 @@ namespace NLastGetopt { const TOptParseResult* r = FindOptParseResult(opt, includeDefault); if (!r || r->Empty()) { try { - throw TUsageException() << "option " << opt->ToShortString() << " is unspecified"; + throw TUsageException() << "option " << opt->ToShortString() << " is unspecified"; } catch (...) { HandleError(); // unreachable @@ -99,10 +99,10 @@ namespace NLastGetopt { return Parser_->ProgramName_; } - void TOptsParseResult::PrintUsage(IOutputStream& os) const { - Parser_->Opts_->PrintUsage(Parser_->ProgramName_, os); - } - + void TOptsParseResult::PrintUsage(IOutputStream& os) const { + Parser_->Opts_->PrintUsage(Parser_->ProgramName_, os); + } + size_t TOptsParseResult::GetFreeArgsPos() const { return Parser_->Pos_; } @@ -147,7 +147,7 @@ namespace NLastGetopt { if (Parser_->Opts_->FindLongOption("help") != nullptr) { Cerr << "Try '" << Parser_->ProgramName_ << " --help' for more information." << Endl; } else { - PrintUsage(); + PrintUsage(); } } exit(1); diff --git a/library/cpp/getopt/small/last_getopt_parse_result.h b/library/cpp/getopt/small/last_getopt_parse_result.h index a4baf8ca6b..1ab6f598c9 100644 --- a/library/cpp/getopt/small/last_getopt_parse_result.h +++ b/library/cpp/getopt/small/last_getopt_parse_result.h @@ -1,15 +1,15 @@ #pragma once #include "last_getopt_opts.h" -#include "last_getopt_parser.h" +#include "last_getopt_parser.h" namespace NLastGetopt { /** - * NLastGetopt::TOptParseResult contains all arguments for exactly one TOpt, - * that have been fetched during parsing - * - * The class is a wraper over a vector of nil-terminated strings. - */ + * NLastGetopt::TOptParseResult contains all arguments for exactly one TOpt, + * that have been fetched during parsing + * + * The class is a wraper over a vector of nil-terminated strings. + */ class TOptParseResult { public: typedef TVector<const char*> TValues; @@ -56,14 +56,14 @@ namespace NLastGetopt { }; /** - * NLastGetopt::TOptsParseResult contains result of parsing argc,argv be parser. - * - * In most common case constructed by argc,argv pair and rules (TOpts). - * The instance being constructed validates rules and performs parsing, stores result for futher access. - * - * If error during parsing occures, the program aborts with exit code 1. - * Note, that if PERMUTE mode is on, then data, pointed by argv can be changed. - */ + * NLastGetopt::TOptsParseResult contains result of parsing argc,argv be parser. + * + * In most common case constructed by argc,argv pair and rules (TOpts). + * The instance being constructed validates rules and performs parsing, stores result for futher access. + * + * If error during parsing occures, the program aborts with exit code 1. + * Note, that if PERMUTE mode is on, then data, pointed by argv can be changed. + */ class TOptsParseResult { private: THolder<TOptsParser> Parser_; //The instance of parser. @@ -78,49 +78,49 @@ namespace NLastGetopt { TOptParseResult& OptParseResult(); /** - * Searchs for object in given container - * - * @param vec container - * @param opt ptr for required object - * - * @retunr ptr on corresponding TOptParseResult - */ + * Searchs for object in given container + * + * @param vec container + * @param opt ptr for required object + * + * @retunr ptr on corresponding TOptParseResult + */ static const TOptParseResult* FindParseResult(const TdVec& vec, const TOpt* opt); protected: /** - * Performs parsing of comand line arguments. - */ + * Performs parsing of comand line arguments. + */ void Init(const TOpts* options, int argc, const char** argv); TOptsParseResult() = default; public: /** - * The action in case of parser failure. - * Allows to asjust behavior in derived classes. - * By default prints error string and aborts the program - */ + * The action in case of parser failure. + * Allows to asjust behavior in derived classes. + * By default prints error string and aborts the program + */ virtual void HandleError() const; /** - * Constructs object by parsing arguments with given rules - * - * @param options ptr on parsing rules - * @param argc - * @param argv - */ + * Constructs object by parsing arguments with given rules + * + * @param options ptr on parsing rules + * @param argc + * @param argv + */ TOptsParseResult(const TOpts* options, int argc, const char* argv[]) { Init(options, argc, argv); } /** - * Constructs object by parsing arguments with given rules - * - * @param options ptr on parsing rules - * @param argc - * @param argv - */ + * Constructs object by parsing arguments with given rules + * + * @param options ptr on parsing rules + * @param argc + * @param argv + */ TOptsParseResult(const TOpts* options, int argc, char* argv[]) { Init(options, argc, const_cast<const char**>(argv)); } @@ -128,146 +128,146 @@ namespace NLastGetopt { virtual ~TOptsParseResult() = default; /** - * Search for TOptParseResult that corresponds to given option (TOpt) - * - * @param opt ptr on required object - * @param includeDefault search in results obtained from default values - * - * @return ptr on result - */ + * Search for TOptParseResult that corresponds to given option (TOpt) + * + * @param opt ptr on required object + * @param includeDefault search in results obtained from default values + * + * @return ptr on result + */ const TOptParseResult* FindOptParseResult(const TOpt* opt, bool includeDefault = false) const; /** - * Search for TOptParseResult that corresponds to given long name - * - * @param name long name of required object - * @param includeDefault search in results obtained from default values - * - * @return ptr on result - */ + * Search for TOptParseResult that corresponds to given long name + * + * @param name long name of required object + * @param includeDefault search in results obtained from default values + * + * @return ptr on result + */ const TOptParseResult* FindLongOptParseResult(const TString& name, bool includeDefault = false) const; /** - * Search for TOptParseResult that corresponds to given short name - * - * @param c short name of required object - * @param includeDefault search in results obtained from default values - * - * @return ptr on result - */ + * Search for TOptParseResult that corresponds to given short name + * + * @param c short name of required object + * @param includeDefault search in results obtained from default values + * + * @return ptr on result + */ const TOptParseResult* FindCharOptParseResult(char c, bool includeDefault = false) const; /** - * @return argv[0] - */ + * @return argv[0] + */ TString GetProgramName() const; /** - * Print usage string. - */ - void PrintUsage(IOutputStream& os = Cout) const; - - /** - * @return position in [premuted argv] of the first free argument - */ + * Print usage string. + */ + void PrintUsage(IOutputStream& os = Cout) const; + + /** + * @return position in [premuted argv] of the first free argument + */ size_t GetFreeArgsPos() const; /** - * @return number of fetched free arguments - */ + * @return number of fetched free arguments + */ size_t GetFreeArgCount() const; /** - * @return all fetched free arguments - */ + * @return all fetched free arguments + */ TVector<TString> GetFreeArgs() const; /** - * @return true if given option exist in results of parsing - * - * @param opt ptr on required object - * @param includeDefault search in results obtained from default values - * - */ + * @return true if given option exist in results of parsing + * + * @param opt ptr on required object + * @param includeDefault search in results obtained from default values + * + */ bool Has(const TOpt* opt, bool includeDefault = false) const; /** - * @return nil terminated string on the last fetched argument of givne option - * - * @param opt ptr on required object - * @param includeDefault search in results obtained from default values - */ + * @return nil terminated string on the last fetched argument of givne option + * + * @param opt ptr on required object + * @param includeDefault search in results obtained from default values + */ const char* Get(const TOpt* opt, bool includeDefault = true) const; /** - * @return nil terminated string on the last fetched argument of givne option - * if option haven't been fetched, given defaultValue will be returned - * - * @param opt ptr on required object - * @param defaultValue - */ + * @return nil terminated string on the last fetched argument of givne option + * if option haven't been fetched, given defaultValue will be returned + * + * @param opt ptr on required object + * @param defaultValue + */ const char* GetOrElse(const TOpt* opt, const char* defaultValue) const; /** - * @return true if given option exist in results of parsing - * - * @param name long name of required object - * @param includeDefault search in results obtained from default values - * - */ + * @return true if given option exist in results of parsing + * + * @param name long name of required object + * @param includeDefault search in results obtained from default values + * + */ bool Has(const TString& name, bool includeDefault = false) const; /** - * @return nil terminated string on the last fetched argument of givne option - * - * @param name long name of required object - * @param includeDefault search in results obtained from default values - */ + * @return nil terminated string on the last fetched argument of givne option + * + * @param name long name of required object + * @param includeDefault search in results obtained from default values + */ const char* Get(const TString& name, bool includeDefault = true) const; /** - * @return nil terminated string on the last fetched argument of givne option - * if option haven't been fetched, given defaultValue will be returned - * - * @param name long name of required object - * @param defaultValue - */ + * @return nil terminated string on the last fetched argument of givne option + * if option haven't been fetched, given defaultValue will be returned + * + * @param name long name of required object + * @param defaultValue + */ const char* GetOrElse(const TString& name, const char* defaultValue) const; /** - * @return true if given option exist in results of parsing - * - * @param c short name of required object - * @param includeDefault search in results obtained from default values - * - */ + * @return true if given option exist in results of parsing + * + * @param c short name of required object + * @param includeDefault search in results obtained from default values + * + */ bool Has(char name, bool includeDefault = false) const; /** - * @return nil terminated string on the last fetched argument of givne option - * - * @param c short name of required object - * @param includeDefault search in results obtained from default values - */ + * @return nil terminated string on the last fetched argument of givne option + * + * @param c short name of required object + * @param includeDefault search in results obtained from default values + */ const char* Get(char name, bool includeDefault = true) const; /** - * @return nil terminated string on the last fetched argument of givne option - * if option haven't been fetched, given defaultValue will be returned - * - * @param c short name of required object - * @param defaultValue - */ + * @return nil terminated string on the last fetched argument of givne option + * if option haven't been fetched, given defaultValue will be returned + * + * @param c short name of required object + * @param defaultValue + */ const char* GetOrElse(char name, const char* defaultValue) const; /** - * for givne option return parsed value of the last fetched argument - * if option haven't been fetched, HandleError action is called - * - * @param opt required option (one of: ptr, short name, long name). - * - * @return FromString<T>(last feteched argument) - */ + * for givne option return parsed value of the last fetched argument + * if option haven't been fetched, HandleError action is called + * + * @param opt required option (one of: ptr, short name, long name). + * + * @return FromString<T>(last feteched argument) + */ template <typename T, typename TKey> T Get(const TKey opt) const { const char* value = Get(opt); @@ -280,14 +280,14 @@ namespace NLastGetopt { } /** - * for givne option return parsed value of the last fetched argument - * if option haven't been fetched, given defaultValue will be returned - * - * @param opt required option (one of: ptr, short name, long name). - * @param defaultValue - * - * @return FromString<T>(last feteched argument) - */ + * for givne option return parsed value of the last fetched argument + * if option haven't been fetched, given defaultValue will be returned + * + * @param opt required option (one of: ptr, short name, long name). + * @param defaultValue + * + * @return FromString<T>(last feteched argument) + */ template <typename T, typename TKey> T GetOrElse(const TKey opt, const T& defaultValue) const { if (Has(opt)) @@ -298,11 +298,11 @@ namespace NLastGetopt { }; /** - * NLastGetopt::TOptsParseResultException contains result of parsing argc,argv be parser. - * - * Unlike TOptsParseResult, if error during parsing occures, an exception is thrown. - * - */ + * NLastGetopt::TOptsParseResultException contains result of parsing argc,argv be parser. + * + * Unlike TOptsParseResult, if error during parsing occures, an exception is thrown. + * + */ class TOptsParseResultException: public TOptsParseResult { public: TOptsParseResultException(const TOpts* options, int argc, const char* argv[]) { diff --git a/library/cpp/getopt/small/last_getopt_parser.cpp b/library/cpp/getopt/small/last_getopt_parser.cpp index 10eebc3c45..7668b12a03 100644 --- a/library/cpp/getopt/small/last_getopt_parser.cpp +++ b/library/cpp/getopt/small/last_getopt_parser.cpp @@ -11,7 +11,7 @@ namespace NLastGetopt { Opts_ = opts; if (argc < 1) - throw TUsageException() << "argv must have at least one argument"; + throw TUsageException() << "argv must have at least one argument"; Argc_ = argc; Argv_ = argv; @@ -66,11 +66,11 @@ namespace NLastGetopt { Y_ASSERT(!Stopped_); if (Opts_->FreeArgsMin_ == Opts_->FreeArgsMax_ && Argc_ - Pos_ != Opts_->FreeArgsMin_) - throw TUsageException() << "required exactly " << Opts_->FreeArgsMin_ << " free args"; + throw TUsageException() << "required exactly " << Opts_->FreeArgsMin_ << " free args"; else if (Argc_ - Pos_ < Opts_->FreeArgsMin_) - throw TUsageException() << "required at least " << Opts_->FreeArgsMin_ << " free args"; + throw TUsageException() << "required at least " << Opts_->FreeArgsMin_ << " free args"; else if (Argc_ - Pos_ > Opts_->FreeArgsMax_) - throw TUsageException() << "required at most " << Opts_->FreeArgsMax_ << " free args"; + throw TUsageException() << "required at most " << Opts_->FreeArgsMax_ << " free args"; return false; } @@ -83,7 +83,7 @@ namespace NLastGetopt { Y_ASSERT(EIO_NONE != IsOpt(arg)); if (!Opts_->AllowUnknownCharOptions_) - throw TUsageException() << "unknown option '" << EscapeC(arg[sop]) + throw TUsageException() << "unknown option '" << EscapeC(arg[sop]) << "' in '" << arg << "'"; TempCurrentOpt_.Reset(new TOpt); @@ -152,13 +152,13 @@ namespace NLastGetopt { } else if (Opts_->AllowUnknownLongOptions_) { return false; } else { - throw TUsageException() << "unknown option '" << optionName + throw TUsageException() << "unknown option '" << optionName << "' in '" << Argv_[pos] << "'"; } } if (arg.IsInited()) { if (option->GetHasArg() == NO_ARGUMENT) - throw TUsageException() << "option " << optionName << " must have no arg"; + throw TUsageException() << "option " << optionName << " must have no arg"; return Commit(option, arg, pos + 1, 0); } ++pos; @@ -175,7 +175,7 @@ namespace NLastGetopt { } if (pos == Argc_) { if (opt->GetHasArg() == REQUIRED_ARGUMENT) - throw TUsageException() << "option " << opt->ToShortString() << " must have arg"; + throw TUsageException() << "option " << opt->ToShortString() << " must have arg"; return Commit(opt, nullptr, pos, 0); } const TStringBuf arg(Argv_[pos]); diff --git a/library/cpp/getopt/small/last_getopt_parser.h b/library/cpp/getopt/small/last_getopt_parser.h index 19c34dce51..2cf8a6c308 100644 --- a/library/cpp/getopt/small/last_getopt_parser.h +++ b/library/cpp/getopt/small/last_getopt_parser.h @@ -88,26 +88,26 @@ namespace NLastGetopt { /// fetch next argument, false if no more arguments left bool Next(); - bool Seen(const TOpt* opt) const { - return OptsSeen_.contains(opt); - } - - bool Seen(TStringBuf name) const { - if (auto opt = Opts_->FindLongOption(name)) { - return Seen(opt); - } else { - return false; - } - } - - bool Seen(char name) const { - if (auto opt = Opts_->FindCharOption(name)) { - return Seen(opt); - } else { - return false; - } - } - + bool Seen(const TOpt* opt) const { + return OptsSeen_.contains(opt); + } + + bool Seen(TStringBuf name) const { + if (auto opt = Opts_->FindLongOption(name)) { + return Seen(opt); + } else { + return false; + } + } + + bool Seen(char name) const { + if (auto opt = Opts_->FindCharOption(name)) { + return Seen(opt); + } else { + return false; + } + } + const TOpt* CurOpt() const { return CurrentOpt_; } diff --git a/library/cpp/getopt/small/last_getopt_support.h b/library/cpp/getopt/small/last_getopt_support.h index d0e0baf77a..17bed3e614 100644 --- a/library/cpp/getopt/small/last_getopt_support.h +++ b/library/cpp/getopt/small/last_getopt_support.h @@ -16,13 +16,13 @@ namespace NLastGetopt { class TException: public yexception { }; - /// TOpts configuration is incorrect + /// TOpts configuration is incorrect class TConfException: public TException { }; - /// User passed incorrect arguments, parsing failed - /// Note: use `throw TUsageException()` instead of `ythrow TUsageException()` to prevent appearence of stacktrace - /// and location of the `ythrow` statment in error messages. + /// User passed incorrect arguments, parsing failed + /// Note: use `throw TUsageException()` instead of `ythrow TUsageException()` to prevent appearence of stacktrace + /// and location of the `ythrow` statment in error messages. class TUsageException: public TException { }; @@ -166,7 +166,7 @@ namespace NLastGetopt { try { return OptFromStringImpl<T>(value); } catch (...) { - throw TUsageException() << "failed to parse opt " << OptToString(opt) << " value " << TString(value).Quote() << ": " << CurrentExceptionMessage(); + throw TUsageException() << "failed to parse opt " << OptToString(opt) << " value " << TString(value).Quote() << ": " << CurrentExceptionMessage(); } } diff --git a/library/cpp/getopt/small/modchooser.cpp b/library/cpp/getopt/small/modchooser.cpp index 64d18862be..2fa5cfd070 100644 --- a/library/cpp/getopt/small/modchooser.cpp +++ b/library/cpp/getopt/small/modchooser.cpp @@ -1,7 +1,7 @@ -#include "completer.h" -#include "completer_command.h" -#include "completion_generator.h" -#include "last_getopt.h" +#include "completer.h" +#include "completer_command.h" +#include "completion_generator.h" +#include "last_getopt.h" #include "modchooser.h" #include <library/cpp/colorizer/colors.h> @@ -10,54 +10,54 @@ #include <util/stream/format.h> #include <util/generic/yexception.h> #include <util/generic/ptr.h> -#include <util/string/builder.h> -#include <util/string/join.h> +#include <util/string/builder.h> +#include <util/string/join.h> -class PtrWrapper: public TMainClass { +class PtrWrapper: public TMainClass { public: explicit PtrWrapper(const TMainFunctionPtr main) : Main(main) { } - int operator()(const int argc, const char** argv) override { - return Main(argc, argv); + int operator()(const int argc, const char** argv) override { + return Main(argc, argv); } private: TMainFunctionPtr Main; }; -class PtrvWrapper: public TMainClass { +class PtrvWrapper: public TMainClass { public: explicit PtrvWrapper(const TMainFunctionPtrV main) : Main(main) { } - int operator()(const int argc, const char** argv) override { - TVector<TString> nargv(argv, argv + argc); - return Main(nargv); + int operator()(const int argc, const char** argv) override { + TVector<TString> nargv(argv, argv + argc); + return Main(nargv); } private: TMainFunctionPtrV Main; }; -class ClassWrapper: public TMainClass { +class ClassWrapper: public TMainClass { public: - explicit ClassWrapper(TMainClassV* main) + explicit ClassWrapper(TMainClassV* main) : Main(main) { } - int operator()(const int argc, const char** argv) override { - TVector<TString> nargv(argv, argv + argc); - return (*Main)(nargv); + int operator()(const int argc, const char** argv) override { + TVector<TString> nargv(argv, argv + argc); + return (*Main)(nargv); } private: - TMainClassV* Main; + TMainClassV* Main; }; TModChooser::TMode::TMode(const TString& name, TMainClass* main, const TString& descr, bool hidden, bool noCompletion) @@ -83,7 +83,7 @@ TModChooser::~TModChooser() = default; void TModChooser::AddMode(const TString& mode, const TMainFunctionRawPtr func, const TString& description, bool hidden, bool noCompletion) { AddMode(mode, TMainFunctionPtr(func), description, hidden, noCompletion); } - + void TModChooser::AddMode(const TString& mode, const TMainFunctionRawPtrV func, const TString& description, bool hidden, bool noCompletion) { AddMode(mode, TMainFunctionPtrV(func), description, hidden, noCompletion); } @@ -109,8 +109,8 @@ void TModChooser::AddMode(const TString& mode, TMainClass* func, const TString& void TModChooser::AddMode(const TString& mode, TMainClassV* func, const TString& description, bool hidden, bool noCompletion) { Wrappers.push_back(MakeHolder<ClassWrapper>(func)); AddMode(mode, Wrappers.back().Get(), description, hidden, noCompletion); -} - +} + void TModChooser::AddGroupModeDescription(const TString& description, bool hidden, bool noCompletion) { UnsortedModes.push_back(MakeHolder<TMode>(TString(), nullptr, description.data(), hidden, noCompletion)); } @@ -119,15 +119,15 @@ void TModChooser::SetDefaultMode(const TString& mode) { DefaultMode = mode; } -void TModChooser::AddAlias(const TString& alias, const TString& mode) { - if (!Modes.FindPtr(mode)) { - ythrow yexception() << "TMode '" << mode << "' not found in TModChooser."; - } - - Modes[mode]->Aliases.push_back(alias); - Modes[alias] = Modes[mode]; -} - +void TModChooser::AddAlias(const TString& alias, const TString& mode) { + if (!Modes.FindPtr(mode)) { + ythrow yexception() << "TMode '" << mode << "' not found in TModChooser."; + } + + Modes[mode]->Aliases.push_back(alias); + Modes[alias] = Modes[mode]; +} + void TModChooser::SetDescription(const TString& descr) { Description = descr; } @@ -157,18 +157,18 @@ void TModChooser::DisableSvnRevisionOption() { } void TModChooser::AddCompletions(TString progName, const TString& name, bool hidden, bool noCompletion) { - if (CompletionsGenerator == nullptr) { - CompletionsGenerator = NLastGetopt::MakeCompletionMod(this, std::move(progName), name); + if (CompletionsGenerator == nullptr) { + CompletionsGenerator = NLastGetopt::MakeCompletionMod(this, std::move(progName), name); AddMode(name, CompletionsGenerator.Get(), "generate autocompletion files", hidden, noCompletion); - } + } } -int TModChooser::Run(const int argc, const char** argv) const { - Y_ENSURE(argc, "Can't run TModChooser with empty list of arguments."); +int TModChooser::Run(const int argc, const char** argv) const { + Y_ENSURE(argc, "Can't run TModChooser with empty list of arguments."); bool shiftArgs = true; TString modeName; - if (argc == 1) { + if (argc == 1) { if (DefaultMode.empty()) { PrintHelp(argv[0]); return 0; @@ -205,89 +205,89 @@ int TModChooser::Run(const int argc, const char** argv) const { } if (shiftArgs) { - TString firstArg; + TString firstArg; TVector<const char*> nargv(Reserve(argc)); - + if (PrintShortCommandInUsage) { - firstArg = modeIter->second->Name; + firstArg = modeIter->second->Name; } else { - firstArg = argv[0] + TString(" ") + modeIter->second->Name; + firstArg = argv[0] + TString(" ") + modeIter->second->Name; } - nargv.push_back(firstArg.data()); - - for (int i = 2; i < argc; ++i) { + nargv.push_back(firstArg.data()); + + for (int i = 2; i < argc; ++i) { nargv.push_back(argv[i]); } // According to the standard, "argv[argc] shall be a null pointer" (5.1.2.2.1). // http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1336 nargv.push_back(nullptr); - + return (*modeIter->second->Main)(nargv.size() - 1, nargv.data()); } else { - return (*modeIter->second->Main)(argc, argv); + return (*modeIter->second->Main)(argc, argv); } } -int TModChooser::Run(const TVector<TString>& argv) const { +int TModChooser::Run(const TVector<TString>& argv) const { TVector<const char*> nargv(Reserve(argv.size() + 1)); - for (auto& arg : argv) { - nargv.push_back(arg.c_str()); - } + for (auto& arg : argv) { + nargv.push_back(arg.c_str()); + } // According to the standard, "argv[argc] shall be a null pointer" (5.1.2.2.1). // http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1336 nargv.push_back(nullptr); return Run(nargv.size() - 1, nargv.data()); -} - -size_t TModChooser::TMode::CalculateFullNameLen() const { - size_t len = Name.size(); - if (Aliases) { - len += 2; - for (auto& alias : Aliases) { - len += alias.size() + 1; - } - } - return len; -} - -TString TModChooser::TMode::FormatFullName(size_t pad) const { - TStringBuilder name; - if (Aliases) { - name << "{"; - } - - name << NColorizer::StdErr().GreenColor(); - name << Name; - name << NColorizer::StdErr().OldColor(); - - if (Aliases) { - for (const auto& alias : Aliases) { - name << "|" << NColorizer::StdErr().GreenColor() << alias << NColorizer::StdErr().OldColor(); - } - name << "}"; - } - - auto len = CalculateFullNameLen(); - if (pad > len) { - name << TString(" ") * (pad - len); - } - - return name; -} - +} + +size_t TModChooser::TMode::CalculateFullNameLen() const { + size_t len = Name.size(); + if (Aliases) { + len += 2; + for (auto& alias : Aliases) { + len += alias.size() + 1; + } + } + return len; +} + +TString TModChooser::TMode::FormatFullName(size_t pad) const { + TStringBuilder name; + if (Aliases) { + name << "{"; + } + + name << NColorizer::StdErr().GreenColor(); + name << Name; + name << NColorizer::StdErr().OldColor(); + + if (Aliases) { + for (const auto& alias : Aliases) { + name << "|" << NColorizer::StdErr().GreenColor() << alias << NColorizer::StdErr().OldColor(); + } + name << "}"; + } + + auto len = CalculateFullNameLen(); + if (pad > len) { + name << TString(" ") * (pad - len); + } + + return name; +} + void TModChooser::PrintHelp(const TString& progName) const { - Cerr << Description << Endl << Endl; + Cerr << Description << Endl << Endl; Cerr << NColorizer::StdErr().BoldColor() << "Usage" << NColorizer::StdErr().OldColor() << ": " << progName << " MODE [MODE_OPTIONS]" << Endl; Cerr << Endl; Cerr << NColorizer::StdErr().BoldColor() << "Modes" << NColorizer::StdErr().OldColor() << ":" << Endl; size_t maxModeLen = 0; - for (const auto& [name, mode] : Modes) { - if (name != mode->Name) - continue; // this is an alias - maxModeLen = Max(maxModeLen, mode->CalculateFullNameLen()); - } + for (const auto& [name, mode] : Modes) { + if (name != mode->Name) + continue; // this is an alias + maxModeLen = Max(maxModeLen, mode->CalculateFullNameLen()); + } if (ShowSeparated) { for (const auto& unsortedMode : UnsortedModes) @@ -300,14 +300,14 @@ void TModChooser::PrintHelp(const TString& progName) const { } } } else { - for (const auto& mode : Modes) { - if (mode.first != mode.second->Name) - continue; // this is an alias + for (const auto& mode : Modes) { + if (mode.first != mode.second->Name) + continue; // this is an alias if (!mode.second->Hidden) { Cerr << " " << mode.second->FormatFullName(maxModeLen + 4) << mode.second->Description << Endl; } - } + } } Cerr << Endl; @@ -319,54 +319,54 @@ void TModChooser::PrintHelp(const TString& progName) const { } return; } - -TVersionHandlerPtr TModChooser::GetVersionHandler() const { - return VersionHandler; -} - -bool TModChooser::IsSvnRevisionOptionDisabled() const { - return SvnRevisionOptionDisabled; -} - -int TMainClassArgs::Run(int argc, const char** argv) { - return DoRun(NLastGetopt::TOptsParseResult(&GetOptions(), argc, argv)); -} - -const NLastGetopt::TOpts& TMainClassArgs::GetOptions() { - if (Opts_.Empty()) { - Opts_ = NLastGetopt::TOpts(); - RegisterOptions(Opts_.GetRef()); - } - - return Opts_.GetRef(); -} - -void TMainClassArgs::RegisterOptions(NLastGetopt::TOpts& opts) { - opts.AddHelpOption('h'); -} - -int TMainClassArgs::operator()(const int argc, const char** argv) { - return Run(argc, argv); -} - -int TMainClassModes::operator()(const int argc, const char** argv) { - return Run(argc, argv); -} - -int TMainClassModes::Run(int argc, const char** argv) { - auto& chooser = GetSubModes(); - return chooser.Run(argc, argv); -} - -const TModChooser& TMainClassModes::GetSubModes() { - if (Modes_.Empty()) { - Modes_.ConstructInPlace(); - RegisterModes(Modes_.GetRef()); - } - - return Modes_.GetRef(); -} - -void TMainClassModes::RegisterModes(TModChooser& modes) { - modes.SetModesHelpOption("-h"); -} + +TVersionHandlerPtr TModChooser::GetVersionHandler() const { + return VersionHandler; +} + +bool TModChooser::IsSvnRevisionOptionDisabled() const { + return SvnRevisionOptionDisabled; +} + +int TMainClassArgs::Run(int argc, const char** argv) { + return DoRun(NLastGetopt::TOptsParseResult(&GetOptions(), argc, argv)); +} + +const NLastGetopt::TOpts& TMainClassArgs::GetOptions() { + if (Opts_.Empty()) { + Opts_ = NLastGetopt::TOpts(); + RegisterOptions(Opts_.GetRef()); + } + + return Opts_.GetRef(); +} + +void TMainClassArgs::RegisterOptions(NLastGetopt::TOpts& opts) { + opts.AddHelpOption('h'); +} + +int TMainClassArgs::operator()(const int argc, const char** argv) { + return Run(argc, argv); +} + +int TMainClassModes::operator()(const int argc, const char** argv) { + return Run(argc, argv); +} + +int TMainClassModes::Run(int argc, const char** argv) { + auto& chooser = GetSubModes(); + return chooser.Run(argc, argv); +} + +const TModChooser& TMainClassModes::GetSubModes() { + if (Modes_.Empty()) { + Modes_.ConstructInPlace(); + RegisterModes(Modes_.GetRef()); + } + + return Modes_.GetRef(); +} + +void TMainClassModes::RegisterModes(TModChooser& modes) { + modes.SetModesHelpOption("-h"); +} diff --git a/library/cpp/getopt/small/modchooser.h b/library/cpp/getopt/small/modchooser.h index 95f5a5944e..0a8de6d50b 100644 --- a/library/cpp/getopt/small/modchooser.h +++ b/library/cpp/getopt/small/modchooser.h @@ -1,7 +1,7 @@ #pragma once -#include "last_getopt_opts.h" - +#include "last_getopt_opts.h" + #include <util/generic/map.h> #include <util/generic/string.h> #include <util/generic/vector.h> @@ -60,12 +60,12 @@ public: //! Set default mode (if not specified explicitly) void SetDefaultMode(const TString& mode); - void AddAlias(const TString& alias, const TString& mode); - + void AddAlias(const TString& alias, const TString& mode); + //! Set main program description. void SetDescription(const TString& descr); - //! Set modes help option name (-? is by default) + //! Set modes help option name (-? is by default) void SetModesHelpOption(const TString& helpOption); //! Specify handler for '--version' parameter @@ -83,7 +83,7 @@ public: void DisableSvnRevisionOption(); void AddCompletions(TString progName, const TString& name = "completion", bool hidden = false, bool noCompletion = false); - + /*! Run appropriate mode. * * In this method following things happen: @@ -104,11 +104,11 @@ public: struct TMode { TString Name; - TMainClass* Main; + TMainClass* Main; TString Description; bool Hidden; bool NoCompletion; - TVector<TString> Aliases; + TVector<TString> Aliases; TMode() : Main(nullptr) @@ -116,24 +116,24 @@ public: } TMode(const TString& name, TMainClass* main, const TString& descr, bool hidden, bool noCompletion); - - // Full name includes primary name and aliases. Also, will add ANSI colors. - size_t CalculateFullNameLen() const; - TString FormatFullName(size_t pad) const; + + // Full name includes primary name and aliases. Also, will add ANSI colors. + size_t CalculateFullNameLen() const; + TString FormatFullName(size_t pad) const; }; - TVector<const TMode*> GetUnsortedModes() const { - auto ret = TVector<const TMode*>(Reserve(UnsortedModes.size())); - for (auto& mode : UnsortedModes) { - ret.push_back(mode.Get()); - } - return ret; - } - - TVersionHandlerPtr GetVersionHandler() const; - - bool IsSvnRevisionOptionDisabled() const; - + TVector<const TMode*> GetUnsortedModes() const { + auto ret = TVector<const TMode*>(Reserve(UnsortedModes.size())); + for (auto& mode : UnsortedModes) { + ret.push_back(mode.Get()); + } + return ret; + } + + TVersionHandlerPtr GetVersionHandler() const; + + bool IsSvnRevisionOptionDisabled() const; + private: //! Main program description. TString Description; @@ -142,10 +142,10 @@ private: TString ModesHelpOption; //! Wrappers around all modes. - TVector<THolder<TMainClass>> Wrappers; + TVector<THolder<TMainClass>> Wrappers; //! Modes - TMap<TString, TMode*> Modes; + TMap<TString, TMode*> Modes; TString DefaultMode; @@ -165,51 +165,51 @@ private: TString SeparationString; //! Unsorted list of options - TVector<THolder<TMode>> UnsortedModes; - - //! Mode that generates completions - THolder<TMainClass> CompletionsGenerator; + TVector<THolder<TMode>> UnsortedModes; + + //! Mode that generates completions + THolder<TMainClass> CompletionsGenerator; }; - -//! Mode class that allows introspecting its console arguments. -class TMainClassArgs: public TMainClass { -public: + +//! Mode class that allows introspecting its console arguments. +class TMainClassArgs: public TMainClass { +public: int operator()(int argc, const char** argv) final; - -public: - //! Run this mode. - int Run(int argc, const char** argv); - - //! Get console arguments for this mode. - const NLastGetopt::TOpts& GetOptions(); - -protected: - //! Fill given empty `TOpts` with options. - virtual void RegisterOptions(NLastGetopt::TOpts& opts); - - //! Actual mode logic. Takes parsed options and returns exit code. - virtual int DoRun(NLastGetopt::TOptsParseResult&& parsedOptions) = 0; - -private: - TMaybe<NLastGetopt::TOpts> Opts_; -}; - -//! Mode class that uses sub-modes to dispatch commands further. -class TMainClassModes: public TMainClass { -public: + +public: + //! Run this mode. + int Run(int argc, const char** argv); + + //! Get console arguments for this mode. + const NLastGetopt::TOpts& GetOptions(); + +protected: + //! Fill given empty `TOpts` with options. + virtual void RegisterOptions(NLastGetopt::TOpts& opts); + + //! Actual mode logic. Takes parsed options and returns exit code. + virtual int DoRun(NLastGetopt::TOptsParseResult&& parsedOptions) = 0; + +private: + TMaybe<NLastGetopt::TOpts> Opts_; +}; + +//! Mode class that uses sub-modes to dispatch commands further. +class TMainClassModes: public TMainClass { +public: int operator()(int argc, const char** argv) final; - -public: - //! Run this mode. - int Run(int argc, const char** argv); - - //! Get sub-modes for this mode. - const TModChooser& GetSubModes(); - -protected: - //! Fill given modchooser with sub-modes. - virtual void RegisterModes(TModChooser& modes); - -private: - TMaybe<TModChooser> Modes_; -}; + +public: + //! Run this mode. + int Run(int argc, const char** argv); + + //! Get sub-modes for this mode. + const TModChooser& GetSubModes(); + +protected: + //! Fill given modchooser with sub-modes. + virtual void RegisterModes(TModChooser& modes); + +private: + TMaybe<TModChooser> Modes_; +}; diff --git a/library/cpp/getopt/small/wrap.cpp b/library/cpp/getopt/small/wrap.cpp index 8002028a2a..9fbd38842a 100644 --- a/library/cpp/getopt/small/wrap.cpp +++ b/library/cpp/getopt/small/wrap.cpp @@ -1,99 +1,99 @@ -#include "wrap.h" - +#include "wrap.h" + #include <library/cpp/colorizer/colors.h> - -#include <util/generic/string.h> -#include <util/stream/str.h> -#include <util/charset/utf8.h> - -#include <cctype> - -namespace NLastGetopt { - TString Wrap(ui32 width, TStringBuf text, TStringBuf indent, size_t* lastLineLen, bool* hasParagraphs) { - if (width == 0) { - return TString(text); - } - - if (width >= indent.size()) { - width -= indent.size(); - } - - if (hasParagraphs) { - *hasParagraphs = false; - } - - TString res; - auto os = TStringOutput(res); - - const char* spaceBegin = text.begin(); - const char* wordBegin = text.begin(); - const char* wordEnd = text.begin(); - const char* end = text.end(); - - size_t lenSoFar = 0; - - bool isPreParagraph = false; - - do { - spaceBegin = wordBegin = wordEnd; - - while (wordBegin < end && *wordBegin == ' ') { - wordBegin++; - } - - if (wordBegin == end) { - break; - } - - wordEnd = wordBegin; - - while (wordEnd < end && *wordEnd != ' ' && *wordEnd != '\n') { - wordEnd++; - } - - auto spaces = TStringBuf(spaceBegin, wordBegin); - auto word = TStringBuf(wordBegin, wordEnd); - - size_t spaceLen = spaces.size(); - - size_t wordLen = 0; - if (!GetNumberOfUTF8Chars(word.data(), word.size(), wordLen)) { - wordLen = word.size(); // not a utf8 string -- just use its binary size - } - wordLen -= NColorizer::TotalAnsiEscapeCodeLen(word); - - // Empty word means we've found a bunch of whitespaces followed by newline. - // We don't want to print trailing whitespaces. - if (word) { - // We can't fit this word into the line -- insert additional line break. - // We shouldn't insert line breaks if we're at the beginning of a line, hence `lenSoFar` check. - if (lenSoFar && lenSoFar + spaceLen + wordLen > width) { - os << Endl << indent << word; - lenSoFar = wordLen; - } else { - os << spaces << word; - lenSoFar += spaceLen + wordLen; - } - isPreParagraph = false; - } - - if (wordEnd != end && *wordEnd == '\n') { - os << Endl << indent; - lenSoFar = 0; - wordEnd++; - if (hasParagraphs && isPreParagraph) { - *hasParagraphs = true; - } else { - isPreParagraph = true; - } - continue; - } - } while (wordEnd < end); - - if (lastLineLen) { - *lastLineLen = lenSoFar; - } - - return res; - } -} + +#include <util/generic/string.h> +#include <util/stream/str.h> +#include <util/charset/utf8.h> + +#include <cctype> + +namespace NLastGetopt { + TString Wrap(ui32 width, TStringBuf text, TStringBuf indent, size_t* lastLineLen, bool* hasParagraphs) { + if (width == 0) { + return TString(text); + } + + if (width >= indent.size()) { + width -= indent.size(); + } + + if (hasParagraphs) { + *hasParagraphs = false; + } + + TString res; + auto os = TStringOutput(res); + + const char* spaceBegin = text.begin(); + const char* wordBegin = text.begin(); + const char* wordEnd = text.begin(); + const char* end = text.end(); + + size_t lenSoFar = 0; + + bool isPreParagraph = false; + + do { + spaceBegin = wordBegin = wordEnd; + + while (wordBegin < end && *wordBegin == ' ') { + wordBegin++; + } + + if (wordBegin == end) { + break; + } + + wordEnd = wordBegin; + + while (wordEnd < end && *wordEnd != ' ' && *wordEnd != '\n') { + wordEnd++; + } + + auto spaces = TStringBuf(spaceBegin, wordBegin); + auto word = TStringBuf(wordBegin, wordEnd); + + size_t spaceLen = spaces.size(); + + size_t wordLen = 0; + if (!GetNumberOfUTF8Chars(word.data(), word.size(), wordLen)) { + wordLen = word.size(); // not a utf8 string -- just use its binary size + } + wordLen -= NColorizer::TotalAnsiEscapeCodeLen(word); + + // Empty word means we've found a bunch of whitespaces followed by newline. + // We don't want to print trailing whitespaces. + if (word) { + // We can't fit this word into the line -- insert additional line break. + // We shouldn't insert line breaks if we're at the beginning of a line, hence `lenSoFar` check. + if (lenSoFar && lenSoFar + spaceLen + wordLen > width) { + os << Endl << indent << word; + lenSoFar = wordLen; + } else { + os << spaces << word; + lenSoFar += spaceLen + wordLen; + } + isPreParagraph = false; + } + + if (wordEnd != end && *wordEnd == '\n') { + os << Endl << indent; + lenSoFar = 0; + wordEnd++; + if (hasParagraphs && isPreParagraph) { + *hasParagraphs = true; + } else { + isPreParagraph = true; + } + continue; + } + } while (wordEnd < end); + + if (lastLineLen) { + *lastLineLen = lenSoFar; + } + + return res; + } +} diff --git a/library/cpp/getopt/small/wrap.h b/library/cpp/getopt/small/wrap.h index 223c21e720..e98028688d 100644 --- a/library/cpp/getopt/small/wrap.h +++ b/library/cpp/getopt/small/wrap.h @@ -1,16 +1,16 @@ -#pragma once - -#include <util/generic/fwd.h> -#include <util/generic/strbuf.h> - -namespace NLastGetopt { - /** - * Split text to multiple lines so that each line fits the given width. - * Can work with UTF8, understands ANSI escape codes. - * - * @param indent will print this string after each newline. - * @param lastLineLen output: will set to number of unicode codepoints in the last printed line. - * @param hasParagraphs output: will set to true if there are two consecutive newlines in the text. - */ - TString Wrap(ui32 width, TStringBuf text, TStringBuf indent = "", size_t* lastLineLen = nullptr, bool* hasParagraphs = nullptr); -} +#pragma once + +#include <util/generic/fwd.h> +#include <util/generic/strbuf.h> + +namespace NLastGetopt { + /** + * Split text to multiple lines so that each line fits the given width. + * Can work with UTF8, understands ANSI escape codes. + * + * @param indent will print this string after each newline. + * @param lastLineLen output: will set to number of unicode codepoints in the last printed line. + * @param hasParagraphs output: will set to true if there are two consecutive newlines in the text. + */ + TString Wrap(ui32 width, TStringBuf text, TStringBuf indent = "", size_t* lastLineLen = nullptr, bool* hasParagraphs = nullptr); +} diff --git a/library/cpp/getopt/small/ya.make b/library/cpp/getopt/small/ya.make index 90bc114f0a..96de0f04b1 100644 --- a/library/cpp/getopt/small/ya.make +++ b/library/cpp/getopt/small/ya.make @@ -7,10 +7,10 @@ PEERDIR( ) SRCS( - completer.cpp - completer_command.cpp - completion_generator.cpp - formatted_output.cpp + completer.cpp + completer_command.cpp + completion_generator.cpp + formatted_output.cpp last_getopt.cpp last_getopt_easy_setup.cpp last_getopt_opt.cpp @@ -21,7 +21,7 @@ SRCS( opt.cpp opt2.cpp posix_getopt.cpp - wrap.cpp + wrap.cpp ygetopt.cpp ) diff --git a/library/cpp/getopt/ut/last_getopt_ut.cpp b/library/cpp/getopt/ut/last_getopt_ut.cpp index 9d2a46a51d..c99a1d053d 100644 --- a/library/cpp/getopt/ut/last_getopt_ut.cpp +++ b/library/cpp/getopt/ut/last_getopt_ut.cpp @@ -612,9 +612,9 @@ Y_UNIT_TEST_SUITE(TLastGetoptTests) { opts.AddCharOption('d').DefaultValue("42"); opts.AddCharOption('s').DefaultValue("str_default"); opts.SetFreeArgsNum(123, 456); - opts.SetFreeArgTitle(0, "first_free_arg", "help"); - opts.SetFreeArgTitle(2, "second_free_arg"); - opts.AddSection("Section", "Section\n text"); + opts.SetFreeArgTitle(0, "first_free_arg", "help"); + opts.SetFreeArgTitle(2, "second_free_arg"); + opts.AddSection("Section", "Section\n text"); const char* cmd[] = {prog}; TOptsParser parser(&opts, Y_ARRAY_SIZE(cmd), cmd); TStringStream out; @@ -636,8 +636,8 @@ Y_UNIT_TEST_SUITE(TLastGetoptTests) { UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.GreenColor() << "123" << colors.OldColor()) != TString::npos); UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.GreenColor() << "456" << colors.OldColor()) != TString::npos); UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.GreenColor() << "first_free_arg" << colors.OldColor()) != TString::npos); - // free args without help not rendered even if they have custom title - UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.GreenColor() << "second_free_arg" << colors.OldColor()) == TString::npos); + // free args without help not rendered even if they have custom title + UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.GreenColor() << "second_free_arg" << colors.OldColor()) == TString::npos); // find signatures UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.BoldColor() << "Usage" << colors.OldColor()) != TString::npos); @@ -645,10 +645,10 @@ Y_UNIT_TEST_SUITE(TLastGetoptTests) { UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.BoldColor() << "Optional parameters" << colors.OldColor()) != TString::npos); UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.BoldColor() << "Free args" << colors.OldColor()) != TString::npos); - // find sections - UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.BoldColor() << "Section" << colors.OldColor() << ":") != TString::npos); - UNIT_ASSERT(out.Str().find(TStringBuilder() << " Section\n text") != TString::npos); - + // find sections + UNIT_ASSERT(out.Str().find(TStringBuilder() << colors.BoldColor() << "Section" << colors.OldColor() << ":") != TString::npos); + UNIT_ASSERT(out.Str().find(TStringBuilder() << " Section\n text") != TString::npos); + // print without colors TStringStream out2; opts.PrintUsage(prog, out2); diff --git a/library/cpp/getopt/ut/wrap.cpp b/library/cpp/getopt/ut/wrap.cpp index 2d78a8ba4d..3444eea102 100644 --- a/library/cpp/getopt/ut/wrap.cpp +++ b/library/cpp/getopt/ut/wrap.cpp @@ -1,96 +1,96 @@ #include <library/cpp/testing/unittest/registar.h> - + #include <library/cpp/getopt/small/wrap.h> - -Y_UNIT_TEST_SUITE(Wrap) { - Y_UNIT_TEST(TestWrapping) { - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "a b c d eeffeff").Quote(), - TString("a b c\nd\neeffeff").Quote() - ); - - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "a b\nc d\neeffeff").Quote(), - TString("a b\nc d\neeffeff").Quote() - ); - - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "a b\n c d\neeffeff").Quote(), - TString("a b\n c\nd\neeffeff").Quote() - ); - - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "a b\nx c d\neeffeff").Quote(), - TString("a b\nx\nc d\neeffeff").Quote() - ); - - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "a b\nx \n c d\neeffeff").Quote(), - TString("a b\nx\n c\nd\neeffeff").Quote() - ); - - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "a b\nx \n c d\neeffeff").Quote(), - TString("a b\nx\n c\nd\neeffeff").Quote() - ); - } - - Y_UNIT_TEST(TestWrappingIndent) { - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "a b c d", "|>").Quote(), - TString("a b\n|>c d").Quote() - ); - - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "a b\n\nc d", "|>").Quote(), - TString("a b\n|>\n|>c d").Quote() - ); - } - - Y_UNIT_TEST(TestWrappingAnsi) { - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5m").Quote(), - TString("\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5m").Quote() - ); - - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "a \033[1;2;3;4;5mb c\033[1;2;3;4;5m \033[1;2;3;4;5md e f").Quote(), - TString("a \033[1;2;3;4;5mb c\033[1;2;3;4;5m\n\033[1;2;3;4;5md e f").Quote() - ); - - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "a b \033[1;2;3;4;5m c d").Quote(), - TString("a b \033[1;2;3;4;5m\nc d").Quote() - ); - - UNIT_ASSERT_STRINGS_EQUAL( - NLastGetopt::Wrap(5, "a b \033[1;2;3;4;5m c d").Quote(), - TString("a b\n\033[1;2;3;4;5m c d").Quote() - ); - } - - Y_UNIT_TEST(TestTextInfo) { - size_t lastLineLen; - bool hasParagraphs; - - NLastGetopt::Wrap(5, "a b c d e", "", &lastLineLen, &hasParagraphs); - UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 3); - UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, false); - - NLastGetopt::Wrap(5, "a b c\n\nd e f h", "", &lastLineLen, &hasParagraphs); - UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 1); - UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, true); - - NLastGetopt::Wrap(5, "a b c\n\n", "", &lastLineLen, &hasParagraphs); - UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 0); - UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, true); - - NLastGetopt::Wrap(5, "\n \na b c", "", &lastLineLen, &hasParagraphs); - UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 5); - UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, true); - - NLastGetopt::Wrap(5, "\nx\na b c", "", &lastLineLen, &hasParagraphs); - UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 5); - UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, false); - } -} + +Y_UNIT_TEST_SUITE(Wrap) { + Y_UNIT_TEST(TestWrapping) { + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "a b c d eeffeff").Quote(), + TString("a b c\nd\neeffeff").Quote() + ); + + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "a b\nc d\neeffeff").Quote(), + TString("a b\nc d\neeffeff").Quote() + ); + + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "a b\n c d\neeffeff").Quote(), + TString("a b\n c\nd\neeffeff").Quote() + ); + + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "a b\nx c d\neeffeff").Quote(), + TString("a b\nx\nc d\neeffeff").Quote() + ); + + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "a b\nx \n c d\neeffeff").Quote(), + TString("a b\nx\n c\nd\neeffeff").Quote() + ); + + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "a b\nx \n c d\neeffeff").Quote(), + TString("a b\nx\n c\nd\neeffeff").Quote() + ); + } + + Y_UNIT_TEST(TestWrappingIndent) { + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "a b c d", "|>").Quote(), + TString("a b\n|>c d").Quote() + ); + + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "a b\n\nc d", "|>").Quote(), + TString("a b\n|>\n|>c d").Quote() + ); + } + + Y_UNIT_TEST(TestWrappingAnsi) { + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5m").Quote(), + TString("\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5mx\033[1;2;3;4;5m").Quote() + ); + + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "a \033[1;2;3;4;5mb c\033[1;2;3;4;5m \033[1;2;3;4;5md e f").Quote(), + TString("a \033[1;2;3;4;5mb c\033[1;2;3;4;5m\n\033[1;2;3;4;5md e f").Quote() + ); + + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "a b \033[1;2;3;4;5m c d").Quote(), + TString("a b \033[1;2;3;4;5m\nc d").Quote() + ); + + UNIT_ASSERT_STRINGS_EQUAL( + NLastGetopt::Wrap(5, "a b \033[1;2;3;4;5m c d").Quote(), + TString("a b\n\033[1;2;3;4;5m c d").Quote() + ); + } + + Y_UNIT_TEST(TestTextInfo) { + size_t lastLineLen; + bool hasParagraphs; + + NLastGetopt::Wrap(5, "a b c d e", "", &lastLineLen, &hasParagraphs); + UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 3); + UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, false); + + NLastGetopt::Wrap(5, "a b c\n\nd e f h", "", &lastLineLen, &hasParagraphs); + UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 1); + UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, true); + + NLastGetopt::Wrap(5, "a b c\n\n", "", &lastLineLen, &hasParagraphs); + UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 0); + UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, true); + + NLastGetopt::Wrap(5, "\n \na b c", "", &lastLineLen, &hasParagraphs); + UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 5); + UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, true); + + NLastGetopt::Wrap(5, "\nx\na b c", "", &lastLineLen, &hasParagraphs); + UNIT_ASSERT_VALUES_EQUAL(lastLineLen, 5); + UNIT_ASSERT_VALUES_EQUAL(hasParagraphs, false); + } +} diff --git a/library/cpp/getopt/ut/ya.make b/library/cpp/getopt/ut/ya.make index c2e928b6f0..8d00669efb 100644 --- a/library/cpp/getopt/ut/ya.make +++ b/library/cpp/getopt/ut/ya.make @@ -8,7 +8,7 @@ SRCS( opt2_ut.cpp opt_ut.cpp posix_getopt_ut.cpp - wrap.cpp + wrap.cpp ygetopt_ut.cpp ) |