BASIS  version 1.2.3 (revision 2104)
ZshCompletionOutput.h
00001 // -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*-
00002 
00003 /****************************************************************************** 
00004  * 
00005  *  file:  ZshCompletionOutput.h
00006  * 
00007  *  Copyright (c) 2006, Oliver Kiddle
00008  *  All rights reverved.
00009  * 
00010  *  See the file COPYING in the top directory of this distribution for
00011  *  more information.
00012  *  
00013  *  THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS 
00014  *  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
00015  *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
00016  *  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
00017  *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
00018  *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
00019  *  DEALINGS IN THE SOFTWARE.
00020  *  
00021  *****************************************************************************/ 
00022 
00023 #ifndef TCLAP_ZSHCOMPLETIONOUTPUT_H
00024 #define TCLAP_ZSHCOMPLETIONOUTPUT_H
00025 
00026 #include <string>
00027 #include <vector>
00028 #include <list>
00029 #include <iostream>
00030 #include <map>
00031 
00032 #include <sbia/tclap/CmdLineInterface.h>
00033 #include <sbia/tclap/CmdLineOutput.h>
00034 #include <sbia/tclap/XorHandler.h>
00035 #include <sbia/tclap/Arg.h>
00036 
00037 namespace TCLAP {
00038 
00039 /**
00040  * A class that generates a Zsh completion function as output from the usage()
00041  * method for the given CmdLine and its Args.
00042  */
00043 class ZshCompletionOutput : public CmdLineOutput
00044 {
00045 
00046     public:
00047 
00048         ZshCompletionOutput();
00049 
00050         /**
00051          * Prints the usage to stdout.  Can be overridden to 
00052          * produce alternative behavior.
00053          * \param c - The CmdLine object the output is generated for. 
00054          */
00055         virtual void usage(CmdLineInterface& c);
00056 
00057         /**
00058          * Prints the version to stdout. Can be overridden 
00059          * to produce alternative behavior.
00060          * \param c - The CmdLine object the output is generated for. 
00061          */
00062         virtual void version(CmdLineInterface& c);
00063 
00064         /**
00065          * Prints (to stderr) an error message, short usage 
00066          * Can be overridden to produce alternative behavior.
00067          * \param c - The CmdLine object the output is generated for. 
00068          * \param e - The ArgException that caused the failure. 
00069          */
00070         virtual void failure(CmdLineInterface& c,
00071                              ArgException& e );
00072 
00073     protected:
00074 
00075         void basename( std::string& s );
00076         void quoteSpecialChars( std::string& s );
00077 
00078         std::string getMutexList( CmdLineInterface& _cmd, Arg* a );
00079         void printOption( Arg* it, std::string mutex );
00080         void printArg( Arg* it );
00081 
00082         std::map<std::string, std::string> common;
00083         char theDelimiter;
00084 };
00085 
00086 ZshCompletionOutput::ZshCompletionOutput()
00087 : common(std::map<std::string, std::string>()),
00088   theDelimiter('=')
00089 {
00090     common["host"] = "_hosts";
00091     common["hostname"] = "_hosts";
00092     common["file"] = "_files";
00093     common["filename"] = "_files";
00094     common["user"] = "_users";
00095     common["username"] = "_users";
00096     common["directory"] = "_directories";
00097     common["path"] = "_directories";
00098     common["url"] = "_urls";
00099 }
00100 
00101 inline void ZshCompletionOutput::version(CmdLineInterface& _cmd)
00102 {
00103     std::cout << _cmd.getVersion() << std::endl;
00104 }
00105 
00106 inline void ZshCompletionOutput::usage(CmdLineInterface& _cmd )
00107 {
00108     std::list<Arg*> argList = _cmd.getArgList();
00109     std::string progName = _cmd.getProgramName();
00110     std::string xversion = _cmd.getVersion();
00111     theDelimiter = _cmd.getDelimiter();
00112     basename(progName);
00113 
00114     std::cout << "#compdef " << progName << std::endl << std::endl <<
00115         "# " << progName << " version " << _cmd.getVersion() << std::endl << std::endl <<
00116         "_arguments -s -S";
00117 
00118     for (ArgListIterator it = argList.begin(); it != argList.end(); it++)
00119     {
00120         if ( (*it)->shortID().at(0) == '<' )
00121             printArg((*it));
00122         else if ( (*it)->getFlag() != "-" )
00123             printOption((*it), getMutexList(_cmd, *it));
00124     }
00125 
00126     std::cout << std::endl;
00127 }
00128 
00129 inline void ZshCompletionOutput::failure( CmdLineInterface& _cmd,
00130                                 ArgException& e )
00131 {
00132     static_cast<void>(_cmd); // unused
00133     std::cout << e.what() << std::endl;
00134 }
00135 
00136 inline void ZshCompletionOutput::quoteSpecialChars( std::string& s )
00137 {
00138     size_t idx = s.find_last_of(':');
00139     while ( idx != std::string::npos )
00140     {
00141         s.insert(idx, 1, '\\');
00142         idx = s.find_last_of(':', idx);
00143     }
00144     idx = s.find_last_of('\'');
00145     while ( idx != std::string::npos )
00146     {
00147         s.insert(idx, "'\\'");
00148         if (idx == 0)
00149             idx = std::string::npos;
00150         else
00151             idx = s.find_last_of('\'', --idx);
00152     }
00153 }
00154 
00155 inline void ZshCompletionOutput::basename( std::string& s )
00156 {
00157     size_t p = s.find_last_of('/');
00158     if ( p != std::string::npos )
00159     {
00160         s.erase(0, p + 1);
00161     }
00162 }
00163 
00164 inline void ZshCompletionOutput::printArg(Arg* a)
00165 {
00166     static int count = 1;
00167 
00168     std::cout << " \\" << std::endl << "  '";
00169     if ( a->acceptsMultipleValues() )
00170         std::cout << '*';
00171     else
00172         std::cout << count++;
00173     std::cout << ':';
00174     if ( !a->isRequired() )
00175         std::cout << ':';
00176 
00177     std::cout << a->getName() << ':';
00178     std::map<std::string, std::string>::iterator compArg = common.find(a->getName());
00179     if ( compArg != common.end() )
00180     {
00181         std::cout << compArg->second;
00182     }
00183     else
00184     {
00185         std::cout << "_guard \"^-*\" " << a->getName();
00186     }
00187     std::cout << '\'';
00188 }
00189 
00190 inline void ZshCompletionOutput::printOption(Arg* a, std::string mutex)
00191 {
00192     std::string flag = a->flagStartChar() + a->getFlag();
00193     std::string name = a->nameStartString() + a->getName();
00194     std::string desc = a->getDescription();
00195 
00196     // remove full stop and capitalisation from description as
00197     // this is the convention for zsh function
00198     if (!desc.compare(0, 12, "(required)  "))
00199     {
00200         desc.erase(0, 12);
00201     }
00202     if (!desc.compare(0, 15, "(OR required)  "))
00203     {
00204         desc.erase(0, 15);
00205     }
00206     size_t len = desc.length();
00207     if (len && desc.at(--len) == '.')
00208     {
00209         desc.erase(len);
00210     }
00211     if (len)
00212     {
00213         desc.replace(0, 1, 1, tolower(desc.at(0)));
00214     }
00215 
00216     std::cout << " \\" << std::endl << "  '" << mutex;
00217 
00218     if ( a->getFlag().empty() )
00219     {
00220         std::cout << name;
00221     }
00222     else
00223     {
00224         std::cout << "'{" << flag << ',' << name << "}'";
00225     }
00226     if ( theDelimiter == '=' && a->isValueRequired() )
00227         std::cout << "=-";
00228     quoteSpecialChars(desc);
00229     std::cout << '[' << desc << ']';
00230 
00231     if ( a->isValueRequired() )
00232     {
00233         std::string arg = a->shortID();
00234         arg.erase(0, arg.find_last_of(theDelimiter) + 1);
00235         if ( arg.at(arg.length()-1) == ']' )
00236             arg.erase(arg.length()-1);
00237         if ( arg.at(arg.length()-1) == ']' )
00238         {
00239             arg.erase(arg.length()-1);
00240         }
00241         if ( arg.at(0) == '<' )
00242         {
00243             arg.erase(arg.length()-1);
00244             arg.erase(0, 1);
00245         }
00246         size_t p = arg.find('|');
00247         if ( p != std::string::npos )
00248         {
00249             do
00250             {
00251                 arg.replace(p, 1, 1, ' ');
00252             }
00253             while ( (p = arg.find_first_of('|', p)) != std::string::npos );
00254             quoteSpecialChars(arg);
00255             std::cout << ": :(" << arg << ')';
00256         }
00257         else
00258         {
00259             std::cout << ':' << arg;
00260             std::map<std::string, std::string>::iterator compArg = common.find(arg);
00261             if ( compArg != common.end() )
00262             {
00263                 std::cout << ':' << compArg->second;
00264             }
00265         }
00266     }
00267 
00268     std::cout << '\'';
00269 }
00270 
00271 inline std::string ZshCompletionOutput::getMutexList( CmdLineInterface& _cmd, Arg* a)
00272 {
00273     XorHandler xorHandler = _cmd.getXorHandler();
00274     std::vector< std::vector<Arg*> > xorList = xorHandler.getXorList();
00275     
00276     if (a->getName() == "help" || a->getName() == "version")
00277     {
00278         return "(-)";
00279     }
00280 
00281     std::ostringstream list;
00282     if ( a->acceptsMultipleValues() )
00283     {
00284         list << '*';
00285     }
00286 
00287     for ( int i = 0; static_cast<unsigned int>(i) < xorList.size(); i++ )
00288     {
00289         for ( ArgVectorIterator it = xorList[i].begin();
00290             it != xorList[i].end();
00291             it++)
00292         if ( a == (*it) )
00293         {
00294             list << '(';
00295             for ( ArgVectorIterator iu = xorList[i].begin();
00296                 iu != xorList[i].end();
00297                 iu++ )
00298             {
00299                 bool notCur = (*iu) != a;
00300                 bool hasFlag = !(*iu)->getFlag().empty();
00301                 if ( iu != xorList[i].begin() && (notCur || hasFlag) )
00302                     list << ' ';
00303                 if (hasFlag)
00304                     list << (*iu)->flagStartChar() << (*iu)->getFlag() << ' ';
00305                 if ( notCur || hasFlag )
00306                     list << (*iu)->nameStartString() << (*iu)->getName();
00307             }
00308             list << ')';
00309             return list.str();
00310         }
00311     }
00312     
00313     // wasn't found in xor list
00314     if (!a->getFlag().empty()) {
00315         list << "(" << a->flagStartChar() << a->getFlag() << ' ' <<
00316             a->nameStartString() << a->getName() << ')';
00317     }
00318     
00319     return list.str();
00320 }
00321 
00322 } //namespace TCLAP
00323 #endif