kate Library API Documentation

katecmds.cpp

00001 /* This file is part of the KDE libraries
00002    Copyright (C) 2003 Anders Lund <anders@alweb.dk>
00003    Copyright (C) 2001-2004 Christoph Cullmann <cullmann@kde.org>
00004    Copyright (C) 2001 Charles Samuels <charles@kde.org>
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License version 2 as published by the Free Software Foundation.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00018    Boston, MA 02111-1307, USA.
00019 */
00020 
00021 #include "katecmds.h"
00022 
00023 #include "katedocument.h"
00024 #include "kateview.h"
00025 #include "kateconfig.h"
00026 #include "kateautoindent.h"
00027 
00028 #include <kdebug.h>
00029 #include <klocale.h>
00030 
00031 #include <qregexp.h>
00032 
00033 // syncs a config flag in the document with a boolean value
00034 static void setDocFlag( KateDocumentConfig::ConfigFlags flag, bool enable,
00035                   KateDocument *doc )
00036 {
00037   doc->config()->setConfigFlags( flag, enable );
00038 }
00039 
00040 // this returns wheather the string s could be converted to
00041 // a bool value, one of on|off|1|0|true|false. the argument val is
00042 // set to the extracted value in case of success
00043 static bool getBoolArg( QString s, bool *val  )
00044 {
00045   bool res( false );
00046   s = s.lower();
00047   res = (s == "on" || s == "1" || s == "true");
00048   if ( res )
00049   {
00050     *val = true;
00051     return true;
00052   }
00053   res = (s == "off" || s == "0" || s == "false");
00054   if ( res )
00055   {
00056     *val = false;
00057     return true;
00058   }
00059   return false;
00060 }
00061 
00062 QStringList KateCommands::CoreCommands::cmds()
00063 {
00064   QStringList l;
00065   l << "indent" << "unindent" << "cleanindent"
00066     << "comment" << "uncomment"
00067     << "set-tab-width" << "set-replace-tabs" << "set-show-tabs"
00068     << "set-remove-trailing-space"
00069     << "set-indent-spaces" << "set-indent-width" << "set-indent-mode" << "set-auto-indent"
00070     << "set-line-numbers" << "set-folding-markers" << "set-icon-border"
00071     << "set-word-wrap" << "set-word-wrap-column"
00072     << "set-replace-tabs-save" << "set-remove-trailing-space-save";
00073   return l;
00074 }
00075 
00076 bool KateCommands::CoreCommands::exec(Kate::View *view,
00077                             const QString &_cmd,
00078                             QString &errorMsg)
00079 {
00080 #define KCC_ERR(s) { errorMsg=s; return false; }
00081   // cast it hardcore, we know that it is really a kateview :)
00082   KateView *v = (KateView*) view;
00083 
00084   if ( ! v )
00085     KCC_ERR( i18n("Could not access view") );
00086 
00087   //create a list of args
00088   QStringList args( QStringList::split( QRegExp("\\s+"), _cmd ) );
00089   QString cmd ( args.first() );
00090   args.remove( args.first() );
00091 
00092   // ALL commands that takes no arguments.
00093   if ( cmd == "indent" )
00094   {
00095     v->indent();
00096     return true;
00097   }
00098   else if ( cmd == "unindent" )
00099   {
00100     v->unIndent();
00101     return true;
00102   }
00103   else if ( cmd == "cleanindent" )
00104   {
00105     v->cleanIndent();
00106     return true;
00107   }
00108   else if ( cmd == "comment" )
00109   {
00110     v->comment();
00111     return true;
00112   }
00113   else if ( cmd == "uncomment" )
00114   {
00115     v->uncomment();
00116     return true;
00117   }
00118   else if ( cmd == "set-indent-mode" )
00119   {
00120     bool ok(false);
00121     int val ( args.first().toInt( &ok ) );
00122     if ( ok )
00123     {
00124       if ( val < 0 )
00125         KCC_ERR( i18n("Mode must be at least 0.") );
00126       v->doc()->config()->setIndentationMode( val );
00127     }
00128     else
00129       v->doc()->config()->setIndentationMode( KateAutoIndent::modeNumber( args.first() ) );
00130     return true;
00131   }
00132 
00133   // ALL commands that takes exactly one integer argument.
00134   else if ( cmd == "set-tab-width" ||
00135             cmd == "set-indent-width" ||
00136             cmd == "set-word-wrap-column" )
00137   {
00138     // find a integer value > 0
00139     if ( ! args.count() )
00140       KCC_ERR( i18n("Missing argument. Usage: %1 <value>").arg( cmd ) );
00141     bool ok;
00142     int val ( args.first().toInt( &ok ) );
00143     if ( !ok )
00144       KCC_ERR( i18n("Failed to convert argument '%1' to integer.")
00145                 .arg( args.first() ) );
00146 
00147     if ( cmd == "set-tab-width" )
00148     {
00149       if ( val < 1 )
00150         KCC_ERR( i18n("Width must be at least 1.") );
00151       v->setTabWidth( val );
00152     }
00153     else if ( cmd == "set-indent-width" )
00154     {
00155       if ( val < 1 )
00156         KCC_ERR( i18n("Width must be at least 1.") );
00157       v->doc()->config()->setIndentationWidth( val );
00158     }
00159     else if ( cmd == "set-word-wrap-column" )
00160     {
00161       if ( val < 2 )
00162         KCC_ERR( i18n("Column must be at least 1.") );
00163       v->doc()->setWordWrapAt( val );
00164     }
00165     return true;
00166   }
00167 
00168   // ALL commands that takes 1 boolean argument.
00169   else if ( cmd == "set-icon-border" ||
00170             cmd == "set-folding-markers" ||
00171             cmd == "set-line-numbers" ||
00172             cmd == "set-replace-tabs" ||
00173             cmd == "set-remove-trailing-space" ||
00174             cmd == "set-show-tabs" ||
00175             cmd == "set-indent-spaces" ||
00176             cmd == "set-auto-indent" ||
00177             cmd == "set-word-wrap" ||
00178             cmd == "set-replace-tabs-save" ||
00179             cmd == "set-remove-trailing-space-save" )
00180   {
00181     if ( ! args.count() )
00182       KCC_ERR( i18n("Usage: %1 on|off|1|0|true|false").arg( cmd ) );
00183     bool enable;
00184     if ( getBoolArg( args.first(), &enable ) )
00185     {
00186       if ( cmd == "set-icon-border" )
00187         v->setIconBorder( enable );
00188       else if (cmd == "set-folding-markers")
00189         v->setFoldingMarkersOn( enable );
00190       else if ( cmd == "set-line-numbers" )
00191         v->setLineNumbersOn( enable );
00192       else if ( cmd == "set-replace-tabs" )
00193         setDocFlag( KateDocumentConfig::cfReplaceTabsDyn, enable, v->doc() );
00194       else if ( cmd == "set-remove-trailing-space" )
00195         setDocFlag( KateDocumentConfig::cfRemoveTrailingDyn, enable, v->doc() );
00196       else if ( cmd == "set-show-tabs" )
00197         setDocFlag( KateDocumentConfig::cfShowTabs, enable, v->doc() );
00198       else if ( cmd == "set-indent-spaces" )
00199         setDocFlag( KateDocumentConfig::cfSpaceIndent, enable, v->doc() );
00200       else if ( cmd == "set-auto-indent" )
00201         setDocFlag( KateDocumentConfig::cfAutoIndent, enable, v->doc() );
00202       else if ( cmd == "set-word-wrap" )
00203         v->doc()->setWordWrap( enable );
00204       else if ( cmd == "set-replace-tabs-save" )
00205         setDocFlag( KateDocumentConfig::cfReplaceTabs, enable, v->doc() );
00206       else if ( cmd == "set-remove-trailing-space-save" )
00207         setDocFlag( KateDocumentConfig::cfRemoveSpaces, enable, v->doc() );
00208 
00209       return true;
00210     }
00211     else
00212       KCC_ERR( i18n("Bad argument '%1'. Usage: %2 on|off|1|0|true|false")
00213                .arg( args.first() ).arg( cmd ) );
00214   }
00215 
00216   // unlikely..
00217   KCC_ERR( i18n("Unknown command '%1'").arg(cmd) );
00218 }
00219 
00220 static void replace(QString &s, const QString &needle, const QString &with)
00221 {
00222   int pos=0;
00223   while (1)
00224   {
00225     pos=s.find(needle, pos);
00226     if (pos==-1) break;
00227     s.replace(pos, needle.length(), with);
00228     pos+=with.length();
00229   }
00230 
00231 }
00232 
00233 static int backslashString(const QString &haystack, const QString &needle, int index)
00234 {
00235   int len=haystack.length();
00236   int searchlen=needle.length();
00237   bool evenCount=true;
00238   while (index<len)
00239   {
00240     if (haystack[index]=='\\')
00241     {
00242       evenCount=!evenCount;
00243     }
00244     else
00245     {  // isn't a slash
00246       if (!evenCount)
00247       {
00248         if (haystack.mid(index, searchlen)==needle)
00249           return index-1;
00250       }
00251       evenCount=true;
00252     }
00253     index++;
00254 
00255   }
00256 
00257   return -1;
00258 }
00259 
00260 // exchange "\t" for the actual tab character, for example
00261 static void exchangeAbbrevs(QString &str)
00262 {
00263   // the format is (findreplace)*[nullzero]
00264   const char *magic="a\x07t\t";
00265 
00266   while (*magic)
00267   {
00268     int index=0;
00269     char replace=magic[1];
00270     while ((index=backslashString(str, QChar(*magic), index))!=-1)
00271     {
00272       str.replace(index, 2, QChar(replace));
00273       index++;
00274     }
00275     magic++;
00276     magic++;
00277   }
00278 }
00279 
00280 QString KateCommands::SedReplace::sedMagic(QString textLine, const QString &find, const QString &repOld, bool noCase, bool repeat)
00281 {
00282 
00283   QRegExp matcher(find, noCase);
00284 
00285   int start=0;
00286   while (start!=-1)
00287   {
00288     start=matcher.search(textLine, start);
00289 
00290     if (start==-1) break;
00291 
00292     int length=matcher.matchedLength();
00293 
00294 
00295     QString rep=repOld;
00296 
00297     // now set the backreferences in the replacement
00298     QStringList backrefs=matcher.capturedTexts();
00299     int refnum=1;
00300 
00301     QStringList::Iterator i = backrefs.begin();
00302     ++i;
00303 
00304     for (; i!=backrefs.end(); ++i)
00305     {
00306       // I need to match "\\" or "", but not "\"
00307       QString number=QString::number(refnum);
00308 
00309       int index=0;
00310       while (index!=-1)
00311       {
00312         index=backslashString(rep, number, index);
00313         if (index>=0)
00314         {
00315           rep.replace(index, 2, *i);
00316           index+=(*i).length();
00317         }
00318       }
00319 
00320       refnum++;
00321     }
00322 
00323     replace(rep, "\\\\", "\\");
00324     replace(rep, "\\/", "/");
00325 
00326     textLine.replace(start, length, rep);
00327     if (!repeat) break;
00328     start+=rep.length();
00329   }
00330 
00331 
00332   return textLine;
00333 }
00334 
00335 static void setLineText(Kate::View *view, int line, const QString &text)
00336 {
00337   if (view->getDoc()->insertLine(line, text))
00338     view->getDoc()->removeLine(line+1);
00339 }
00340 
00341 bool KateCommands::SedReplace::exec (Kate::View *view, const QString &cmd, QString &)
00342 {
00343   kdDebug(13010)<<"SedReplace::execCmd()"<<endl;
00344 
00345   if (QRegExp("[$%]?s /.+/.*/[ig]*").search(cmd, 0)==-1)
00346     return false;
00347 
00348   bool fullFile=cmd[0]=='%';
00349   bool noCase=cmd[cmd.length()-1]=='i' || cmd[cmd.length()-2]=='i';
00350   bool repeat=cmd[cmd.length()-1]=='g' || cmd[cmd.length()-2]=='g';
00351   bool onlySelect=cmd[0]=='$';
00352 
00353 
00354   QRegExp splitter("^[$%]?s /((?:[^\\\\/]|\\\\.)*)/((?:[^\\\\/]|\\\\.)*)/[ig]*$");
00355   if (splitter.search(cmd)<0) return false;
00356 
00357   QString find=splitter.cap(1);
00358   kdDebug(13010)<< "SedReplace: find=" << find.latin1() <<endl;
00359 
00360   QString replace=splitter.cap(2);
00361   exchangeAbbrevs(replace);
00362   kdDebug(13010)<< "SedReplace: replace=" << replace.latin1() <<endl;
00363 
00364 
00365   if (fullFile)
00366   {
00367     int numLines=view->getDoc()->numLines();
00368     for (int line=0; line < numLines; line++)
00369     {
00370       QString text=view->getDoc()->textLine(line);
00371       text=sedMagic(text, find, replace, noCase, repeat);
00372       setLineText(view, line, text);
00373     }
00374   }
00375   else if (onlySelect)
00376   {
00377     // Not implemented
00378   }
00379   else
00380   { // just this line
00381     QString textLine=view->currentTextLine();
00382     int line=view->cursorLine();
00383     textLine=sedMagic(textLine, find, replace, noCase, repeat);
00384     setLineText(view, line, textLine);
00385   }
00386   return true;
00387 }
00388 
00389 bool KateCommands::Character::exec (Kate::View *view, const QString &_cmd, QString &)
00390 {
00391   QString cmd = _cmd;
00392 
00393   // hex, octal, base 9+1
00394   QRegExp num("^char *(0?x[0-9A-Fa-f]{1,4}|0[0-7]{1,6}|[0-9]{1,3})$");
00395   if (num.search(cmd)==-1) return false;
00396 
00397   cmd=num.cap(1);
00398 
00399   // identify the base
00400 
00401   unsigned short int number=0;
00402   int base=10;
00403   if (cmd[0]=='x' || cmd.left(2)=="0x")
00404   {
00405     cmd.replace(QRegExp("^0?x"), "");
00406     base=16;
00407   }
00408   else if (cmd[0]=='0')
00409     base=8;
00410   bool ok;
00411   number=cmd.toUShort(&ok, base);
00412   if (!ok || number==0) return false;
00413   if (number<=255)
00414   {
00415     char buf[2];
00416     buf[0]=(char)number;
00417     buf[1]=0;
00418     view->insertText(QString(buf));
00419   }
00420   else
00421   { // do the unicode thing
00422     QChar c(number);
00423     view->insertText(QString(&c, 1));
00424   }
00425 
00426   return true;
00427 }
00428 
00429 bool KateCommands::Goto::exec (Kate::View *view, const QString &cmd, QString &)
00430 {
00431   if (cmd.left(4) != "goto")
00432     return false;
00433 
00434   QStringList args( QStringList::split( QRegExp("\\s+"), cmd ) );
00435   args.remove( args.first() );
00436 
00437   view->gotoLineNumber (args[0].toInt());
00438 
00439   return true;
00440 }
00441 
00442 bool KateCommands::Date::exec (Kate::View *view, const QString &cmd, QString &)
00443 {
00444   if (cmd.left(4) != "date")
00445     return false;
00446 
00447   if (QDateTime::currentDateTime().toString(cmd.mid(5, cmd.length()-5)).length() > 0)
00448     view->insertText(QDateTime::currentDateTime().toString(cmd.mid(5, cmd.length()-5)));
00449   else
00450     view->insertText(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));
00451 
00452   return true;
00453 }
00454 
00455 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE Logo
This file is part of the documentation for kate Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Nov 27 13:52:31 2004 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003