ZshCompletionOutput.h 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. // -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*-
  2. /******************************************************************************
  3. *
  4. * file: ZshCompletionOutput.h
  5. *
  6. * Copyright (c) 2006, Oliver Kiddle
  7. * Copyright (c) 2017 Google Inc.
  8. * All rights reserved.
  9. *
  10. * See the file COPYING in the top directory of this distribution for
  11. * more information.
  12. *
  13. * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS
  14. * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  16. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  18. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  19. * DEALINGS IN THE SOFTWARE.
  20. *
  21. *****************************************************************************/
  22. #ifndef TCLAP_ZSHCOMPLETIONOUTPUT_H
  23. #define TCLAP_ZSHCOMPLETIONOUTPUT_H
  24. #ifdef HAVE_CONFIG_H
  25. #include <config.h>
  26. #endif
  27. #include <string>
  28. #include <vector>
  29. #include <list>
  30. #include <iostream>
  31. #include <map>
  32. #include <tclap/CmdLineInterface.h>
  33. #include <tclap/CmdLineOutput.h>
  34. #include <tclap/XorHandler.h>
  35. #include <tclap/Arg.h>
  36. #include <tclap/sstream.h>
  37. namespace TCLAP {
  38. /**
  39. * A class that generates a Zsh completion function as output from the usage()
  40. * method for the given CmdLine and its Args.
  41. */
  42. class ZshCompletionOutput : public CmdLineOutput
  43. {
  44. public:
  45. ZshCompletionOutput();
  46. /**
  47. * Prints the usage to stdout. Can be overridden to
  48. * produce alternative behavior.
  49. * \param c - The CmdLine object the output is generated for.
  50. */
  51. virtual void usage(CmdLineInterface& c);
  52. /**
  53. * Prints the version to stdout. Can be overridden
  54. * to produce alternative behavior.
  55. * \param c - The CmdLine object the output is generated for.
  56. */
  57. virtual void version(CmdLineInterface& c);
  58. /**
  59. * Prints (to stderr) an error message, short usage
  60. * Can be overridden to produce alternative behavior.
  61. * \param c - The CmdLine object the output is generated for.
  62. * \param e - The ArgException that caused the failure.
  63. */
  64. virtual void failure(CmdLineInterface& c,
  65. ArgException& e );
  66. protected:
  67. void basename( std::string& s );
  68. void quoteSpecialChars( std::string& s );
  69. std::string getMutexList( CmdLineInterface& _cmd, Arg* a );
  70. void printOption( Arg* it, std::string mutex );
  71. void printArg( Arg* it );
  72. std::map<std::string, std::string> common;
  73. char theDelimiter;
  74. };
  75. ZshCompletionOutput::ZshCompletionOutput()
  76. : common(std::map<std::string, std::string>()),
  77. theDelimiter('=')
  78. {
  79. common["host"] = "_hosts";
  80. common["hostname"] = "_hosts";
  81. common["file"] = "_files";
  82. common["filename"] = "_files";
  83. common["user"] = "_users";
  84. common["username"] = "_users";
  85. common["directory"] = "_directories";
  86. common["path"] = "_directories";
  87. common["url"] = "_urls";
  88. }
  89. inline void ZshCompletionOutput::version(CmdLineInterface& _cmd)
  90. {
  91. std::cout << _cmd.getVersion() << std::endl;
  92. }
  93. inline void ZshCompletionOutput::usage(CmdLineInterface& _cmd )
  94. {
  95. std::list<Arg*> argList = _cmd.getArgList();
  96. std::string progName = _cmd.getProgramName();
  97. std::string xversion = _cmd.getVersion();
  98. theDelimiter = _cmd.getDelimiter();
  99. basename(progName);
  100. std::cout << "#compdef " << progName << std::endl << std::endl <<
  101. "# " << progName << " version " << _cmd.getVersion() << std::endl << std::endl <<
  102. "_arguments -s -S";
  103. for (ArgListIterator it = argList.begin(); it != argList.end(); it++)
  104. {
  105. if ( (*it)->shortID().at(0) == '<' )
  106. printArg((*it));
  107. else if ( (*it)->getFlag() != "-" )
  108. printOption((*it), getMutexList(_cmd, *it));
  109. }
  110. std::cout << std::endl;
  111. }
  112. inline void ZshCompletionOutput::failure( CmdLineInterface& _cmd,
  113. ArgException& e )
  114. {
  115. static_cast<void>(_cmd); // unused
  116. std::cout << e.what() << std::endl;
  117. }
  118. inline void ZshCompletionOutput::quoteSpecialChars( std::string& s )
  119. {
  120. size_t idx = s.find_last_of(':');
  121. while ( idx != std::string::npos )
  122. {
  123. s.insert(idx, 1, '\\');
  124. idx = s.find_last_of(':', idx);
  125. }
  126. idx = s.find_last_of('\'');
  127. while ( idx != std::string::npos )
  128. {
  129. s.insert(idx, "'\\'");
  130. if (idx == 0)
  131. idx = std::string::npos;
  132. else
  133. idx = s.find_last_of('\'', --idx);
  134. }
  135. }
  136. inline void ZshCompletionOutput::basename( std::string& s )
  137. {
  138. size_t p = s.find_last_of('/');
  139. if ( p != std::string::npos )
  140. {
  141. s.erase(0, p + 1);
  142. }
  143. }
  144. inline void ZshCompletionOutput::printArg(Arg* a)
  145. {
  146. static int count = 1;
  147. std::cout << " \\" << std::endl << " '";
  148. if ( a->acceptsMultipleValues() )
  149. std::cout << '*';
  150. else
  151. std::cout << count++;
  152. std::cout << ':';
  153. if ( !a->isRequired() )
  154. std::cout << ':';
  155. std::cout << a->getName() << ':';
  156. std::map<std::string, std::string>::iterator compArg = common.find(a->getName());
  157. if ( compArg != common.end() )
  158. {
  159. std::cout << compArg->second;
  160. }
  161. else
  162. {
  163. std::cout << "_guard \"^-*\" " << a->getName();
  164. }
  165. std::cout << '\'';
  166. }
  167. inline void ZshCompletionOutput::printOption(Arg* a, std::string mutex)
  168. {
  169. std::string flag = a->flagStartChar() + a->getFlag();
  170. std::string name = a->nameStartString() + a->getName();
  171. std::string desc = a->getDescription();
  172. // remove full stop and capitalization from description as
  173. // this is the convention for zsh function
  174. if (!desc.compare(0, 12, "(required) "))
  175. {
  176. desc.erase(0, 12);
  177. }
  178. if (!desc.compare(0, 15, "(OR required) "))
  179. {
  180. desc.erase(0, 15);
  181. }
  182. size_t len = desc.length();
  183. if (len && desc.at(--len) == '.')
  184. {
  185. desc.erase(len);
  186. }
  187. if (len)
  188. {
  189. desc.replace(0, 1, 1, tolower(desc.at(0)));
  190. }
  191. std::cout << " \\" << std::endl << " '" << mutex;
  192. if ( a->getFlag().empty() )
  193. {
  194. std::cout << name;
  195. }
  196. else
  197. {
  198. std::cout << "'{" << flag << ',' << name << "}'";
  199. }
  200. if ( theDelimiter == '=' && a->isValueRequired() )
  201. std::cout << "=-";
  202. quoteSpecialChars(desc);
  203. std::cout << '[' << desc << ']';
  204. if ( a->isValueRequired() )
  205. {
  206. std::string arg = a->shortID();
  207. // Example arg: "[-A <integer>] ..."
  208. size_t pos = arg.rfind(" ...");
  209. if (pos != std::string::npos) {
  210. arg.erase(pos);
  211. }
  212. arg.erase(0, arg.find_last_of(theDelimiter) + 1);
  213. if ( arg.at(arg.length()-1) == ']' )
  214. arg.erase(arg.length()-1);
  215. if ( arg.at(arg.length()-1) == ']' )
  216. {
  217. arg.erase(arg.length()-1);
  218. }
  219. if ( arg.at(0) == '<' )
  220. {
  221. arg.erase(arg.length()-1);
  222. arg.erase(0, 1);
  223. }
  224. size_t p = arg.find('|');
  225. if ( p != std::string::npos )
  226. {
  227. do
  228. {
  229. arg.replace(p, 1, 1, ' ');
  230. }
  231. while ( (p = arg.find_first_of('|', p)) != std::string::npos );
  232. quoteSpecialChars(arg);
  233. std::cout << ": :(" << arg << ')';
  234. }
  235. else
  236. {
  237. std::cout << ':' << arg;
  238. std::map<std::string, std::string>::iterator compArg = common.find(arg);
  239. if ( compArg != common.end() )
  240. {
  241. std::cout << ':' << compArg->second;
  242. }
  243. }
  244. }
  245. std::cout << '\'';
  246. }
  247. inline std::string ZshCompletionOutput::getMutexList( CmdLineInterface& _cmd, Arg* a)
  248. {
  249. XorHandler xorHandler = _cmd.getXorHandler();
  250. std::vector< std::vector<Arg*> > xorList = xorHandler.getXorList();
  251. if (a->getName() == "help" || a->getName() == "version")
  252. {
  253. return "(-)";
  254. }
  255. ostringstream list;
  256. if ( a->acceptsMultipleValues() )
  257. {
  258. list << '*';
  259. }
  260. for ( int i = 0; static_cast<unsigned int>(i) < xorList.size(); i++ )
  261. {
  262. for ( ArgVectorIterator it = xorList[i].begin();
  263. it != xorList[i].end();
  264. it++)
  265. if ( a == (*it) )
  266. {
  267. list << '(';
  268. for ( ArgVectorIterator iu = xorList[i].begin();
  269. iu != xorList[i].end();
  270. iu++ )
  271. {
  272. bool notCur = (*iu) != a;
  273. bool hasFlag = !(*iu)->getFlag().empty();
  274. if ( iu != xorList[i].begin() && (notCur || hasFlag) )
  275. list << ' ';
  276. if (hasFlag)
  277. list << (*iu)->flagStartChar() << (*iu)->getFlag() << ' ';
  278. if ( notCur || hasFlag )
  279. list << (*iu)->nameStartString() << (*iu)->getName();
  280. }
  281. list << ')';
  282. return list.str();
  283. }
  284. }
  285. // wasn't found in xor list
  286. if (!a->getFlag().empty()) {
  287. list << "(" << a->flagStartChar() << a->getFlag() << ' ' <<
  288. a->nameStartString() << a->getName() << ')';
  289. }
  290. return list.str();
  291. }
  292. } //namespace TCLAP
  293. #endif