completer_command.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. #include "completer_command.h"
  2. #include "completion_generator.h"
  3. #include "last_getopt.h"
  4. #include "wrap.h"
  5. #include <library/cpp/colorizer/colors.h>
  6. #include <util/string/subst.h>
  7. namespace NLastGetopt {
  8. TString MakeInfo(TStringBuf command, TStringBuf flag) {
  9. TString info = (
  10. "This command generates shell script with completion function and prints it to `stdout`, "
  11. "allowing one to re-direct the output to the file of their choosing. "
  12. "Where you place the file will depend on which shell and operating system you are using."
  13. "\n"
  14. "\n"
  15. "\n"
  16. "{B}BASH (Linux){R}:"
  17. "\n"
  18. "\n"
  19. "For system-wide commands, completion files are stored in `/etc/bash_completion.d/`. "
  20. "For user commands, they are stored in `~/.local/share/bash-completion/completions`. "
  21. "So, pipe output of this script to a file in one of these directories:"
  22. "\n"
  23. "\n"
  24. " $ mkdir -p ~/.local/share/bash-completion/completions"
  25. "\n"
  26. " $ {command} {completion} bash >"
  27. "\n"
  28. " ~/.local/share/bash-completion/completions/{command}"
  29. "\n"
  30. "\n"
  31. "You'll need to restart your shell for changes to take effect."
  32. "\n"
  33. "\n"
  34. "\n"
  35. "{B}BASH (OSX){R}:"
  36. "\n"
  37. "\n"
  38. "You'll need `bash-completion` (or `bash-completion@2` if you're using non-default, newer bash) "
  39. "homebrew formula. Completion files are stored in `/usr/local/etc/bash_completion.d`:"
  40. "\n"
  41. "\n"
  42. " $ mkdir -p $(brew --prefix)/etc/bash_completion.d"
  43. "\n"
  44. " $ {command} {completion} bash >"
  45. "\n"
  46. " $(brew --prefix)/etc/bash_completion.d/{command}"
  47. "\n"
  48. "\n"
  49. "Alternatively, just source the script in your `~/.bash_profile`."
  50. "\n"
  51. "\n"
  52. "You'll need to restart your shell for changes to take effect."
  53. "\n"
  54. "\n"
  55. "\n"
  56. "{B}ZSH{R}:"
  57. "\n"
  58. "\n"
  59. "Zsh looks for completion scripts in any directory listed in `$fpath` variable. We recommend placing "
  60. "completions to `~/.zfunc`:"
  61. "\n"
  62. "\n"
  63. " $ mkdir -m755 -p ~/.zfunc"
  64. "\n"
  65. " $ {command} {completion} zsh > ~/.zfunc/_{command}"
  66. "\n"
  67. "\n"
  68. "Add the following lines to your `.zshrc` just before `compinit`:"
  69. "\n"
  70. "\n"
  71. " fpath+=~/.zfunc"
  72. "\n"
  73. "\n"
  74. "You'll need to restart your shell for changes to take effect.");
  75. SubstGlobal(info, "{command}", command);
  76. SubstGlobal(info, "{completion}", flag);
  77. SubstGlobal(info, "{B}", NColorizer::StdErr().LightDefault());
  78. SubstGlobal(info, "{R}", NColorizer::StdErr().Reset());
  79. return info;
  80. }
  81. NComp::ICompleterPtr ShellChoiceCompleter() {
  82. return NComp::Choice({{"zsh"}, {"bash"}});
  83. }
  84. TOpt MakeCompletionOpt(const TOpts* opts, TString command, TString name) {
  85. return TOpt()
  86. .AddLongName(name)
  87. .Help("generate tab completion script for zsh or bash")
  88. .CompletionHelp("generate tab completion script")
  89. .OptionalArgument("shell-syntax")
  90. .CompletionArgHelp("shell syntax for completion script")
  91. .IfPresentDisableCompletion()
  92. .Completer(ShellChoiceCompleter())
  93. .Handler1T<TString>([opts, command, name](TStringBuf shell) {
  94. if (shell.empty()) {
  95. Cerr << Wrap(80, MakeInfo(command, "--" + name)) << Endl;
  96. } else if (shell == "bash") {
  97. TBashCompletionGenerator(opts).Generate(command, Cout);
  98. } else if (shell == "zsh") {
  99. TZshCompletionGenerator(opts).Generate(command, Cout);
  100. } else {
  101. Cerr << "Unknown shell name " << TString{shell}.Quote() << Endl;
  102. exit(1);
  103. }
  104. exit(0);
  105. });
  106. }
  107. class TCompleterMode: public TMainClassArgs {
  108. public:
  109. TCompleterMode(const TModChooser* modChooser, TString command, TString modName)
  110. : Command_(std::move(command))
  111. , Modes_(modChooser)
  112. , ModName_(std::move(modName))
  113. {
  114. }
  115. protected:
  116. void RegisterOptions(NLastGetopt::TOpts& opts) override {
  117. TMainClassArgs::RegisterOptions(opts);
  118. opts.SetTitle("Generate tab completion scripts for zsh or bash");
  119. opts.AddSection("Description", MakeInfo(Command_, ModName_));
  120. opts.SetFreeArgsNum(1);
  121. opts.GetFreeArgSpec(0)
  122. .Title("<shell-syntax>")
  123. .Help("shell syntax for completion script (bash or zsh)")
  124. .CompletionArgHelp("shell syntax for completion script")
  125. .Completer(ShellChoiceCompleter());
  126. }
  127. int DoRun(NLastGetopt::TOptsParseResult&& parsedOptions) override {
  128. auto arg = parsedOptions.GetFreeArgs()[0];
  129. arg.to_lower();
  130. if (arg == "bash") {
  131. TBashCompletionGenerator(Modes_).Generate(Command_, Cout);
  132. } else if (arg == "zsh") {
  133. TZshCompletionGenerator(Modes_).Generate(Command_, Cout);
  134. } else {
  135. Cerr << "Unknown shell name " << arg.Quote() << Endl;
  136. parsedOptions.PrintUsage();
  137. return 1;
  138. }
  139. return 0;
  140. }
  141. private:
  142. TString Command_;
  143. const TModChooser* Modes_;
  144. TString ModName_;
  145. };
  146. THolder<TMainClassArgs> MakeCompletionMod(const TModChooser* modChooser, TString command, TString modName) {
  147. return MakeHolder<TCompleterMode>(modChooser, std::move(command), std::move(modName));
  148. }
  149. }