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, 2003 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-indent-spaces" << "set-indent-width" << "set-indent-mode" << "set-auto-indent"
00069     << "set-line-numbers" << "set-folding-markers" << "set-icon-border"
00070     << "set-word-wrap" << "set-word-wrap-column";
00071   return l;
00072 }
00073 
00074 bool KateCommands::CoreCommands::exec(Kate::View *view,
00075                             const QString &_cmd,
00076                             QString &errorMsg)
00077 {
00078 #define KCC_ERR(s) { errorMsg=s; return false; }
00079   // cast it hardcore, we know that it is really a kateview :)
00080   KateView *v = (KateView*) view;
00081 
00082   if ( ! v )
00083     KCC_ERR( i18n("Could not access view") );
00084 
00085   //create a list of args
00086   QStringList args( QStringList::split( QRegExp("\\s+"), _cmd ) );
00087   QString cmd ( args.first() );
00088   args.remove( args.first() );
00089 
00090   // ALL commands that takes no arguments.
00091   if ( cmd == "indent" )
00092   {
00093     v->indent();
00094     return true;
00095   }
00096   else if ( cmd == "unindent" )
00097   {
00098     v->unIndent();
00099     return true;
00100   }
00101   else if ( cmd == "cleanindent" )
00102   {
00103     v->cleanIndent();
00104     return true;
00105   }
00106   else if ( cmd == "comment" )
00107   {
00108     v->comment();
00109     return true;
00110   }
00111   else if ( cmd == "uncomment" )
00112   {
00113     v->uncomment();
00114     return true;
00115   }
00116   else if ( cmd == "set-indent-mode" )
00117   {
00118     bool ok(false);
00119     int val ( args.first().toInt( &ok ) );
00120     if ( ok )
00121     {
00122       if ( val < 0 )
00123         KCC_ERR( i18n("Mode must be at least 0.") );
00124       v->doc()->config()->setIndentationMode( val );
00125     }
00126     else
00127       v->doc()->config()->setIndentationMode( KateAutoIndent::modeNumber( args.first() ) );
00128     return true;
00129   }
00130 
00131   // ALL commands that takes exactly one integer argument.
00132   else if ( cmd == "set-tab-width" ||
00133             cmd == "set-indent-width" ||
00134             cmd == "set-word-wrap-column" )
00135   {
00136     // find a integer value > 0
00137     if ( ! args.count() )
00138       KCC_ERR( i18n("Missing argument. Usage: %1 <value>").arg( cmd ) );
00139     bool ok;
00140     int val ( args.first().toInt( &ok ) );
00141     if ( !ok )
00142       KCC_ERR( i18n("Failed to convert argument '%1' to integer.")
00143                 .arg( args.first() ) );
00144 
00145     if ( cmd == "set-tab-width" )
00146     {
00147       if ( val < 1 )
00148         KCC_ERR( i18n("Width must be at least 1.") );
00149       v->setTabWidth( val );
00150     }
00151     else if ( cmd == "set-indent-width" )
00152     {
00153       if ( val < 1 )
00154         KCC_ERR( i18n("Width must be at least 1.") );
00155       v->doc()->config()->setIndentationWidth( val );
00156     }
00157     else if ( cmd == "set-word-wrap-column" )
00158     {
00159       if ( val < 2 )
00160         KCC_ERR( i18n("Column must be at least 1.") );
00161       v->doc()->setWordWrapAt( val );
00162     }
00163     return true;
00164   }
00165 
00166   // ALL commands that takes 1 boolean argument.
00167   else if ( cmd == "set-icon-border" ||
00168             cmd == "set-folding-markers" ||
00169             cmd == "set-line-numbers" ||
00170             cmd == "set-replace-tabs" ||
00171             cmd == "set-show-tabs" ||
00172             cmd == "set-indent-spaces" ||
00173             cmd == "set-auto-indent" ||
00174             cmd == "set-word-wrap" )
00175   {
00176     if ( ! args.count() )
00177       KCC_ERR( i18n("Usage: %1 on|off|1|0|true|false").arg( cmd ) );
00178     bool enable;
00179     if ( getBoolArg( args.first(), &enable ) )
00180     {
00181       if ( cmd == "set-icon-border" )
00182         v->setIconBorder( enable );
00183       else if (cmd == "set-folding-markers")
00184         v->setFoldingMarkersOn( enable );
00185       else if ( cmd == "set-line-numbers" )
00186         v->setLineNumbersOn( enable );
00187       else if ( cmd == "set-replace-tabs" )
00188         setDocFlag( KateDocumentConfig::cfReplaceTabs, enable, v->doc() );
00189       else if ( cmd == "set-show-tabs" )
00190         setDocFlag( KateDocumentConfig::cfShowTabs, enable, v->doc() );
00191       else if ( cmd == "set-indent-spaces" )
00192         setDocFlag( KateDocumentConfig::cfSpaceIndent, enable, v->doc() );
00193       else if ( cmd == "set-auto-indent" )
00194         setDocFlag( KateDocumentConfig::cfAutoIndent, enable, v->doc() );
00195       else if ( cmd == "set-word-wrap" )
00196         v->doc()->setWordWrap( enable );
00197 
00198       return true;
00199     }
00200     else
00201       KCC_ERR( i18n("Bad argument '%1'. Usage: %2 on|off|1|0|true|false")
00202                .arg( args.first() ).arg( cmd ) );
00203   }
00204 
00205   // unlikely..
00206   KCC_ERR( i18n("Unknown command '%1'").arg(cmd) );
00207 }
00208 
00209 static void replace(QString &s, const QString &needle, const QString &with)
00210 {
00211   int pos=0;
00212   while (1)
00213   {
00214     pos=s.find(needle, pos);
00215     if (pos==-1) break;
00216     s.replace(pos, needle.length(), with);
00217     pos+=with.length();
00218   }
00219 
00220 }
00221 
00222 static int backslashString(const QString &haystack, const QString &needle, int index)
00223 {
00224   int len=haystack.length();
00225   int searchlen=needle.length();
00226   bool evenCount=true;
00227   while (index<len)
00228   {
00229     if (haystack[index]=='\\')
00230     {
00231       evenCount=!evenCount;
00232     }
00233     else
00234     {  // isn't a slash
00235       if (!evenCount)
00236       {
00237         if (haystack.mid(index, searchlen)==needle)
00238           return index-1;
00239       }
00240       evenCount=true;
00241     }
00242     index++;
00243 
00244   }
00245 
00246   return -1;
00247 }
00248 
00249 // exchange "\t" for the actual tab character, for example
00250 static void exchangeAbbrevs(QString &str)
00251 {
00252   // the format is (findreplace)*[nullzero]
00253   const char *magic="a\x07t\t";
00254 
00255   while (*magic)
00256   {
00257     int index=0;
00258     char replace=magic[1];
00259     while ((index=backslashString(str, QChar(*magic), index))!=-1)
00260     {
00261       str.replace(index, 2, QChar(replace));
00262       index++;
00263     }
00264     magic++;
00265     magic++;
00266   }
00267 }
00268 
00269 QString KateCommands::SedReplace::sedMagic(QString textLine, const QString &find, const QString &repOld, bool noCase, bool repeat)
00270 {
00271 
00272   QRegExp matcher(find, noCase);
00273 
00274   int start=0;
00275   while (start!=-1)
00276   {
00277     start=matcher.search(textLine, start);
00278 
00279     if (start==-1) break;
00280 
00281     int length=matcher.matchedLength();
00282 
00283 
00284     QString rep=repOld;
00285 
00286     // now set the backreferences in the replacement
00287     QStringList backrefs=matcher.capturedTexts();
00288     int refnum=1;
00289 
00290     QStringList::Iterator i = backrefs.begin();
00291     ++i;
00292 
00293     for (; i!=backrefs.end(); ++i)
00294     {
00295       // I need to match "\\" or "", but not "\"
00296       QString number=QString::number(refnum);
00297 
00298       int index=0;
00299       while (index!=-1)
00300       {
00301         index=backslashString(rep, number, index);
00302         if (index>=0)
00303         {
00304           rep.replace(index, 2, *i);
00305           index+=(*i).length();
00306         }
00307       }
00308 
00309       refnum++;
00310     }
00311 
00312     replace(rep, "\\\\", "\\");
00313     replace(rep, "\\/", "/");
00314 
00315     textLine.replace(start, length, rep);
00316     if (!repeat) break;
00317     start+=rep.length();
00318   }
00319 
00320 
00321   return textLine;
00322 }
00323 
00324 static void setLineText(Kate::View *view, int line, const QString &text)
00325 {
00326   if (view->getDoc()->insertLine(line, text))
00327     view->getDoc()->removeLine(line+1);
00328 }
00329 
00330 bool KateCommands::SedReplace::exec (Kate::View *view, const QString &cmd, QString &)
00331 {
00332   kdDebug(13010)<<"SedReplace::execCmd()"<<endl;
00333 
00334   if (QRegExp("[$%]?s /.+/.*/[ig]*").search(cmd, 0)==-1)
00335     return false;
00336 
00337   bool fullFile=cmd[0]=='%';
00338   bool noCase=cmd[cmd.length()-1]=='i' || cmd[cmd.length()-2]=='i';
00339   bool repeat=cmd[cmd.length()-1]=='g' || cmd[cmd.length()-2]=='g';
00340   bool onlySelect=cmd[0]=='$';
00341 
00342 
00343   QRegExp splitter("^[$%]?s /((?:[^\\\\/]|\\\\.)*)/((?:[^\\\\/]|\\\\.)*)/[ig]*$");
00344   if (splitter.search(cmd)<0) return false;
00345 
00346   QString find=splitter.cap(1);
00347   kdDebug(13010)<< "SedReplace: find=" << find.latin1() <<endl;
00348 
00349   QString replace=splitter.cap(2);
00350   exchangeAbbrevs(replace);
00351   kdDebug(13010)<< "SedReplace: replace=" << replace.latin1() <<endl;
00352 
00353 
00354   if (fullFile)
00355   {
00356     int numLines=view->getDoc()->numLines();
00357     for (int line=0; line < numLines; line++)
00358     {
00359       QString text=view->getDoc()->textLine(line);
00360       text=sedMagic(text, find, replace, noCase, repeat);
00361       setLineText(view, line, text);
00362     }
00363   }
00364   else if (onlySelect)
00365   {
00366     // Not implemented
00367   }
00368   else
00369   { // just this line
00370     QString textLine=view->currentTextLine();
00371     int line=view->cursorLine();
00372     textLine=sedMagic(textLine, find, replace, noCase, repeat);
00373     setLineText(view, line, textLine);
00374   }
00375   return true;
00376 }
00377 
00378 bool KateCommands::Character::exec (Kate::View *view, const QString &_cmd, QString &)
00379 {
00380   QString cmd = _cmd;
00381 
00382   // hex, octal, base 9+1
00383   QRegExp num("^char *(0?x[0-9A-Fa-f]{1,4}|0[0-7]{1,6}|[0-9]{1,3})$");
00384   if (num.search(cmd)==-1) return false;
00385 
00386   cmd=num.cap(1);
00387 
00388   // identify the base
00389 
00390   unsigned short int number=0;
00391   int base=10;
00392   if (cmd[0]=='x' || cmd.left(2)=="0x")
00393   {
00394     cmd.replace(QRegExp("^0?x"), "");
00395     base=16;
00396   }
00397   else if (cmd[0]=='0')
00398     base=8;
00399   bool ok;
00400   number=cmd.toUShort(&ok, base);
00401   if (!ok || number==0) return false;
00402   if (number<=255)
00403   {
00404     char buf[2];
00405     buf[0]=(char)number;
00406     buf[1]=0;
00407     view->insertText(QString(buf));
00408   }
00409   else
00410   { // do the unicode thing
00411     QChar c(number);
00412     view->insertText(QString(&c, 1));
00413   }
00414 
00415   return true;
00416 }
00417 
00418 bool KateCommands::Goto::exec (Kate::View *view, const QString &cmd, QString &)
00419 {
00420   if (cmd.left(4) != "goto")
00421     return false;
00422 
00423   QStringList args( QStringList::split( QRegExp("\\s+"), cmd ) );
00424   args.remove( args.first() );
00425 
00426   view->gotoLineNumber (args[0].toInt());
00427 
00428   return true;
00429 }
00430 
00431 bool KateCommands::Date::exec (Kate::View *view, const QString &cmd, QString &)
00432 {
00433   if (cmd.left(4) != "date")
00434     return false;
00435 
00436   if (QDateTime::currentDateTime().toString(cmd.mid(5, cmd.length()-5)).length() > 0)
00437     view->insertText(QDateTime::currentDateTime().toString(cmd.mid(5, cmd.length()-5)));
00438   else
00439     view->insertText(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));
00440 
00441   return true;
00442 }
00443 
00444 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE Logo
This file is part of the documentation for kate Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Mar 3 19:25:01 2005 by doxygen 1.3.6 written by Dimitri van Heesch, © 1997-2003