123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791 |
- #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_ABORT_UNLESS(modChooser != nullptr);
- }
- TCompletionGenerator::TCompletionGenerator(const TOpts* opts)
- : Options_(opts)
- {
- Y_ABORT_UNLESS(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;
- 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) {
- 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) {
- 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) {
- 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) {
- 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) {
- 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 + "'";
- }
- }
|