completer.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. #pragma once
  2. #include "formatted_output.h"
  3. #include <util/generic/strbuf.h>
  4. #include <util/generic/hash.h>
  5. #include <utility>
  6. #include <util/generic/fwd.h>
  7. namespace NLastGetopt::NComp {
  8. class ICompleter;
  9. class TCompleterManager {
  10. public:
  11. TCompleterManager(TStringBuf command);
  12. /// Register new completer and get its function name.
  13. TStringBuf GetCompleterID(const ICompleter* completer);
  14. /// Generate zsh code for all collected completers.
  15. void GenerateZsh(TFormattedOutput& out);
  16. private:
  17. TStringBuf Command_;
  18. size_t Id_;
  19. TVector<std::pair<TString, const ICompleter*>> Queue_;
  20. };
  21. class ICompleter {
  22. public:
  23. virtual ~ICompleter() = default;
  24. public:
  25. /// Generate arbitrary bash code that modifies `COMPREPLY`.
  26. virtual void GenerateBash(TFormattedOutput& out) const = 0;
  27. /// Generate action that will be used with `_arguments`. If this completer requires a separate function,
  28. /// register it in the given manager and return function name assigned by manager.
  29. /// Supported forms are '()', '(items...)', '((items...))', 'command ...' and ' command ...'.
  30. /// Other forms, such as '{eval-string}', '->state', '=action' are not supported.
  31. virtual TStringBuf GenerateZshAction(TCompleterManager& manager) const = 0;
  32. /// Generate body of a zsh function (if Action points to a custom function).
  33. virtual void GenerateZsh(TFormattedOutput& out, TCompleterManager& manager) const = 0;
  34. };
  35. using ICompleterPtr = TSimpleSharedPtr<ICompleter>;
  36. /// Generate default completions.
  37. /// Output of this completer depends on shell settings.
  38. /// Usually ut generates file paths.
  39. ICompleterPtr Default();
  40. struct TAlternative {
  41. /// Description for this group of completions. Leave empty to use completer's default description.
  42. TString Description;
  43. /// Completer that generates values
  44. ICompleterPtr Completer;
  45. TAlternative(ICompleterPtr completer)
  46. : Description("")
  47. , Completer(std::move(completer))
  48. {
  49. }
  50. TAlternative(TString description, ICompleterPtr completer)
  51. : Description(std::move(description))
  52. , Completer(std::move(completer))
  53. {
  54. }
  55. };
  56. /// Run multiple completers and unite their output.
  57. /// Each completer's output placed in a separate group with its own description.
  58. ICompleterPtr Alternative(TVector<TAlternative> alternatives);
  59. struct TChoice {
  60. /// Option value.
  61. TString Choice;
  62. /// Description for a value.
  63. TString Description = "";
  64. TChoice(TString choice)
  65. : Choice(std::move(choice))
  66. {
  67. }
  68. TChoice(TString choice, TString description)
  69. : Choice(std::move(choice))
  70. , Description(std::move(description))
  71. {
  72. }
  73. };
  74. /// Complete items from a predefined list of choices.
  75. ICompleterPtr Choice(TVector<TChoice> choices);
  76. /// Complete files and directories. May filter results by pattern, e.g. `*.txt`.
  77. ICompleterPtr File(TString pattern= "");
  78. /// Complete directories.
  79. ICompleterPtr Directory();
  80. /// Complete hosts.
  81. ICompleterPtr Host();
  82. /// Complete process IDs.
  83. ICompleterPtr Pid();
  84. /// Complete users that're found in the system.
  85. ICompleterPtr User();
  86. /// Complete user groups that're found in the system.
  87. /// N: for some reason,
  88. ICompleterPtr Group();
  89. /// Complete URLs.
  90. ICompleterPtr Url();
  91. /// Complete TTY interfaces.
  92. ICompleterPtr Tty();
  93. /// Complete network interfaces.
  94. ICompleterPtr NetInterface();
  95. /// Complete timezone identifiers.
  96. ICompleterPtr TimeZone();
  97. /// Complete unix signal identifiers, e.g. `ABRT` or `KILL`.
  98. ICompleterPtr Signal();
  99. /// Complete domains.
  100. ICompleterPtr Domain();
  101. /// Custom completer. See `LaunchSelf` below.
  102. class TCustomCompleter {
  103. public:
  104. static void FireCustomCompleter(int argc, const char** argv);
  105. static void RegisterCustomCompleter(TCustomCompleter* completer) noexcept;
  106. struct TReg {
  107. TReg(TCustomCompleter* completer) noexcept {
  108. TCustomCompleter::RegisterCustomCompleter(completer);
  109. }
  110. };
  111. public:
  112. virtual ~TCustomCompleter() = default;
  113. public:
  114. /// @param argc total number of command line arguments.
  115. /// @param argv array of command line arguments.
  116. /// @param curIdx index of the currently completed argument, may be equal to `argc` if cursor is at the end
  117. /// of line.
  118. /// @param cur currently completed argument.
  119. /// @param prefix part of the currently completed argument before the cursor.
  120. /// @param suffix part of the currently completed argument after the cursor.
  121. virtual void GenerateCompletions(int argc, const char** argv, int curIdx, TStringBuf cur, TStringBuf prefix, TStringBuf suffix) = 0;
  122. virtual TStringBuf GetUniqueName() const = 0;
  123. protected:
  124. void AddCompletion(TStringBuf completion);
  125. private:
  126. TCustomCompleter* Next_ = nullptr;
  127. };
  128. /// Custom completer for objects that consist of multiple parts split by a common separator, such as file paths.
  129. class TMultipartCustomCompleter: public TCustomCompleter {
  130. public:
  131. TMultipartCustomCompleter(TStringBuf sep)
  132. : Sep_(sep)
  133. {
  134. Y_ABORT_UNLESS(!Sep_.empty());
  135. }
  136. public:
  137. void GenerateCompletions(int argc, const char** argv, int curIdx, TStringBuf cur, TStringBuf prefix, TStringBuf suffix) final;
  138. public:
  139. /// @param argc same as in `GenerateCompletions`.
  140. /// @param argv same as in `GenerateCompletions`.
  141. /// @param curIdx same as in `GenerateCompletions`.
  142. /// @param cur same as in `GenerateCompletions`.
  143. /// @param prefix same as in `GenerateCompletions`.
  144. /// @param suffix same as in `GenerateCompletions`.
  145. /// @param root part of the currently completed argument before the last separator.
  146. virtual void GenerateCompletionParts(int argc, const char** argv, int curIdx, TStringBuf cur, TStringBuf prefix, TStringBuf suffix, TStringBuf root) = 0;
  147. protected:
  148. TStringBuf Sep_;
  149. };
  150. #define Y_COMPLETER(N) \
  151. class T##N: public ::NLastGetopt::NComp::TCustomCompleter { \
  152. public: \
  153. void GenerateCompletions(int, const char**, int, TStringBuf, TStringBuf, TStringBuf) override; \
  154. TStringBuf GetUniqueName() const override { return #N; } \
  155. }; \
  156. T##N N = T##N(); \
  157. ::NLastGetopt::NComp::TCustomCompleter::TReg _Reg_##N = &N; \
  158. void T##N::GenerateCompletions( \
  159. Y_DECLARE_UNUSED int argc, \
  160. Y_DECLARE_UNUSED const char** argv, \
  161. Y_DECLARE_UNUSED int curIdx, \
  162. Y_DECLARE_UNUSED TStringBuf cur, \
  163. Y_DECLARE_UNUSED TStringBuf prefix, \
  164. Y_DECLARE_UNUSED TStringBuf suffix)
  165. #define Y_MULTIPART_COMPLETER(N, SEP) \
  166. class T##N: public ::NLastGetopt::NComp::TMultipartCustomCompleter { \
  167. public: \
  168. T##N() : ::NLastGetopt::NComp::TMultipartCustomCompleter(SEP) {} \
  169. void GenerateCompletionParts(int, const char**, int, TStringBuf, TStringBuf, TStringBuf, TStringBuf) override; \
  170. TStringBuf GetUniqueName() const override { return #N; } \
  171. }; \
  172. T##N N = T##N(); \
  173. ::NLastGetopt::NComp::TCustomCompleter::TReg _Reg_##N = &N; \
  174. void T##N::GenerateCompletionParts( \
  175. Y_DECLARE_UNUSED int argc, \
  176. Y_DECLARE_UNUSED const char** argv, \
  177. Y_DECLARE_UNUSED int curIdx, \
  178. Y_DECLARE_UNUSED TStringBuf cur, \
  179. Y_DECLARE_UNUSED TStringBuf prefix, \
  180. Y_DECLARE_UNUSED TStringBuf suffix, \
  181. Y_DECLARE_UNUSED TStringBuf root)
  182. /// Launches this binary with a specially formed flags and retrieves completions from stdout.
  183. ///
  184. /// Your application must be set up in a certain way for this to work.
  185. ///
  186. /// First, create a custom completer:
  187. ///
  188. /// ```
  189. /// Y_COMPLETER(SomeUniqueName) {
  190. /// AddCompletion("foo");
  191. /// AddCompletion("bar");
  192. /// AddCompletion("baz");
  193. /// }
  194. /// ```
  195. ///
  196. /// Then, use it with this function.
  197. ///
  198. /// On completion attempt, completer will call your binary with some special arguments.
  199. ///
  200. /// In your main, before any other logic, call `TCustomCompleter::FireCustomCompleter`. This function will
  201. /// check for said special arguments and invoke the right completer:
  202. ///
  203. /// ```
  204. /// int main(int argc, const char** argv) {
  205. /// TCustomCompleter::FireCustomCompleter(argc, argv);
  206. /// ...
  207. /// }
  208. /// ```
  209. ICompleterPtr LaunchSelf(TCustomCompleter& completer);
  210. /// Launches this binary with a specially formed flags and retrieves completions from stdout.
  211. ///
  212. /// Your application must be set up in a certain way for this to work. See `LaunchSelf` for more info.
  213. ///
  214. /// Multipart completion is designed for objects that consist of multiple parts split by a common separator.
  215. /// It is ideal for completing remote file paths, for example.
  216. ///
  217. /// Multipart completers format stdout in the following way.
  218. ///
  219. /// On the first line, they print a common prefix that should be stripped from all completions. For example,
  220. /// if you complete paths, this prefix would be a common directory.
  221. ///
  222. /// On the second line, they print a separator. If some completion ends with this separator, shell will not add
  223. /// a whitespace after the item is completed. For example, if you complete paths, the separator would be a slash.
  224. ///
  225. /// On the following lines, they print completions, as formed by the `AddCompletion` function.
  226. ///
  227. /// For example, if a user invokes completion like this:
  228. ///
  229. /// ```
  230. /// $ program //home/logs/<tab><tab>
  231. /// ```
  232. ///
  233. /// The stdout might look like this:
  234. ///
  235. /// ```
  236. /// //home/logs/
  237. /// /
  238. /// //home/logs/access-log
  239. /// //home/logs/redir-log
  240. /// //home/logs/blockstat-log
  241. /// ```
  242. ///
  243. /// Then autocompletion will look like this:
  244. ///
  245. /// ```
  246. /// $ program //home/logs/<tab><tab>
  247. /// -- yt path --
  248. /// access-log redir-log blockstat-log
  249. /// ```
  250. ///
  251. /// Note: stdout lines with completion suggestions *must* be formatted by the `AddCompletion` function
  252. /// because their format may change in the future.
  253. ///
  254. /// Note: we recommend using `Y_MULTIPART_COMPLETER` because it will handle all stdout printing for you.
  255. ICompleterPtr LaunchSelfMultiPart(TCustomCompleter& completer);
  256. }