# kate Library API Documentation

Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# katecmds.cpp

    00001 /* This file is part of the KDE libraries
    00002    Copyright (C) 2003 - 2005 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 #include "katetextline.h"
    00028 #include "../interfaces/katecmd.h"
    00029
    00030 #include <kdebug.h>
    00031 #include <klocale.h>
    00032 #include <kurl.h>
    00033 #include <kshellcompletion.h>
    00034
    00035 #include <qregexp.h>
    00036
    00037
    00038 //BEGIN CoreCommands
    00039 // syncs a config flag in the document with a boolean value
    00040 static void setDocFlag( KateDocumentConfig::ConfigFlags flag, bool enable,
    00041                   KateDocument *doc )
    00042 {
    00043   doc->config()->setConfigFlags( flag, enable );
    00044 }
    00045
    00046 // this returns wheather the string s could be converted to
    00047 // a bool value, one of on|off|1|0|true|false. the argument val is
    00048 // set to the extracted value in case of success
    00049 static bool getBoolArg( QString s, bool *val  )
    00050 {
    00051   bool res( false );
    00052   s = s.lower();
    00053   res = (s == "on" || s == "1" || s == "true");
    00054   if ( res )
    00055   {
    00056     *val = true;
    00057     return true;
    00058   }
    00059   res = (s == "off" || s == "0" || s == "false");
    00060   if ( res )
    00061   {
    00062     *val = false;
    00063     return true;
    00064   }
    00065   return false;
    00066 }
    00067
    00068 QStringList KateCommands::CoreCommands::cmds()
    00069 {
    00070   QStringList l;
    00071   l << "indent" << "unindent" << "cleanindent"
    00072     << "comment" << "uncomment" << "goto" << "kill-line"
    00073     << "set-tab-width" << "set-replace-tabs" << "set-show-tabs"
    00074     << "set-remove-trailing-space"
    00075     << "set-indent-spaces" << "set-indent-width" << "set-mixed-indent"
    00076     << "set-indent-mode" << "set-auto-indent"
    00077     << "set-line-numbers" << "set-folding-markers" << "set-icon-border"
    00078     << "set-word-wrap" << "set-word-wrap-column"
    00079     << "set-replace-tabs-save" << "set-remove-trailing-space-save"
    00080     << "set-highlight";
    00081   return l;
    00082 }
    00083
    00084 bool KateCommands::CoreCommands::exec(Kate::View *view,
    00085                             const QString &_cmd,
    00086                             QString &errorMsg)
    00087 {
    00088 #define KCC_ERR(s) { errorMsg=s; return false; }
    00089   // cast it hardcore, we know that it is really a kateview :)
    00090   KateView *v = (KateView*) view;
    00091
    00092   if ( ! v )
    00093     KCC_ERR( i18n("Could not access view") );
    00094
    00095   //create a list of args
    00096   QStringList args( QStringList::split( QRegExp("\\s+"), _cmd ) );
    00097   QString cmd ( args.first() );
    00098   args.remove( args.first() );
    00099
    00100   // ALL commands that takes no arguments.
    00101   if ( cmd == "indent" )
    00102   {
    00103     v->indent();
    00104     return true;
    00105   }
    00106   else if ( cmd == "unindent" )
    00107   {
    00108     v->unIndent();
    00109     return true;
    00110   }
    00111   else if ( cmd == "cleanindent" )
    00112   {
    00113     v->cleanIndent();
    00114     return true;
    00115   }
    00116   else if ( cmd == "comment" )
    00117   {
    00118     v->comment();
    00119     return true;
    00120   }
    00121   else if ( cmd == "uncomment" )
    00122   {
    00123     v->uncomment();
    00124     return true;
    00125   }
    00126   else if ( cmd == "kill-line" )
    00127   {
    00128     v->killLine();
    00129     return true;
    00130   }
    00131   else if ( cmd == "set-indent-mode" )
    00132   {
    00133     bool ok(false);
    00134     int val ( args.first().toInt( &ok ) );
    00135     if ( ok )
    00136     {
    00137       if ( val < 0 )
    00138         KCC_ERR( i18n("Mode must be at least 0.") );
    00139       v->doc()->config()->setIndentationMode( val );
    00140     }
    00141     else
    00142       v->doc()->config()->setIndentationMode( KateAutoIndent::modeNumber( args.first() ) );
    00143     return true;
    00144   }
    00145   else if ( cmd == "set-highlight" )
    00146   {
    00147     QString val = _cmd.section( ' ', 1 ).lower();
    00148     for ( uint i=0; i < v->doc()->hlModeCount(); i++ )
    00149     {
    00150       if ( v->doc()->hlModeName( i ).lower() == val )
    00151       {
    00152         v->doc()->setHlMode( i );
    00153         return true;
    00154       }
    00155     }
    00156     KCC_ERR( i18n("No such highlight '%1'").arg( args.first() ) );
    00157   }
    00158
    00159   // ALL commands that takes exactly one integer argument.
    00160   else if ( cmd == "set-tab-width" ||
    00161             cmd == "set-indent-width" ||
    00162             cmd == "set-word-wrap-column" ||
    00163             cmd == "goto" )
    00164   {
    00165     // find a integer value > 0
    00166     if ( ! args.count() )
    00167       KCC_ERR( i18n("Missing argument. Usage: %1 <value>").arg( cmd ) );
    00168     bool ok;
    00169     int val ( args.first().toInt( &ok ) );
    00170     if ( !ok )
    00171       KCC_ERR( i18n("Failed to convert argument '%1' to integer.")
    00172                 .arg( args.first() ) );
    00173
    00174     if ( cmd == "set-tab-width" )
    00175     {
    00176       if ( val < 1 )
    00177         KCC_ERR( i18n("Width must be at least 1.") );
    00178       v->setTabWidth( val );
    00179     }
    00180     else if ( cmd == "set-indent-width" )
    00181     {
    00182       if ( val < 1 )
    00183         KCC_ERR( i18n("Width must be at least 1.") );
    00184       v->doc()->config()->setIndentationWidth( val );
    00185     }
    00186     else if ( cmd == "set-word-wrap-column" )
    00187     {
    00188       if ( val < 2 )
    00189         KCC_ERR( i18n("Column must be at least 1.") );
    00190       v->doc()->setWordWrapAt( val );
    00191     }
    00192     else if ( cmd == "goto" )
    00193     {
    00194       if ( val < 1 )
    00195         KCC_ERR( i18n("Line must be at least 1") );
    00196       if ( (uint)val > v->doc()->numLines() )
    00197         KCC_ERR( i18n("There is not that many lines in this document") );
    00198       v->gotoLineNumber( val - 1 );
    00199     }
    00200     return true;
    00201   }
    00202
    00203   // ALL commands that takes 1 boolean argument.
    00204   else if ( cmd == "set-icon-border" ||
    00205             cmd == "set-folding-markers" ||
    00206             cmd == "set-line-numbers" ||
    00207             cmd == "set-replace-tabs" ||
    00208             cmd == "set-remove-trailing-space" ||
    00209             cmd == "set-show-tabs" ||
    00210             cmd == "set-indent-spaces" ||
    00211             cmd == "set-mixed-indent" ||
    00212             cmd == "set-word-wrap" ||
    00213             cmd == "set-replace-tabs-save" ||
    00214             cmd == "set-remove-trailing-space-save" )
    00215   {
    00216     if ( ! args.count() )
    00217       KCC_ERR( i18n("Usage: %1 on|off|1|0|true|false").arg( cmd ) );
    00218     bool enable;
    00219     if ( getBoolArg( args.first(), &enable ) )
    00220     {
    00221       if ( cmd == "set-icon-border" )
    00222         v->setIconBorder( enable );
    00223       else if (cmd == "set-folding-markers")
    00224         v->setFoldingMarkersOn( enable );
    00225       else if ( cmd == "set-line-numbers" )
    00226         v->setLineNumbersOn( enable );
    00227       else if ( cmd == "set-replace-tabs" )
    00228         setDocFlag( KateDocumentConfig::cfReplaceTabsDyn, enable, v->doc() );
    00229       else if ( cmd == "set-remove-trailing-space" )
    00230         setDocFlag( KateDocumentConfig::cfRemoveTrailingDyn, enable, v->doc() );
    00231       else if ( cmd == "set-show-tabs" )
    00232         setDocFlag( KateDocumentConfig::cfShowTabs, enable, v->doc() );
    00233       else if ( cmd == "set-indent-spaces" )
    00234         setDocFlag( KateDocumentConfig::cfSpaceIndent, enable, v->doc() );
    00235       else if ( cmd == "set-mixed-indent" )
    00236       {
    00237         // this is special, in that everything is set up -- space-indent is enabled,
    00238         // and a indent-width is set if it is 0 (to tabwidth/2)
    00239         setDocFlag( KateDocumentConfig::cfMixedIndent, enable, v->doc() );
    00240         if ( enable )
    00241         {
    00242           setDocFlag(  KateDocumentConfig::cfSpaceIndent, enable, v->doc() );
    00243           if ( ! v->doc()->config()->indentationWidth() )
    00244             v->doc()->config()->setIndentationWidth( v->tabWidth()/2 );
    00245         }
    00246       }
    00247       else if ( cmd == "set-word-wrap" )
    00248         v->doc()->setWordWrap( enable );
    00249       else if ( cmd == "set-replace-tabs-save" )
    00250         setDocFlag( KateDocumentConfig::cfReplaceTabs, enable, v->doc() );
    00251       else if ( cmd == "set-remove-trailing-space-save" )
    00252         setDocFlag( KateDocumentConfig::cfRemoveSpaces, enable, v->doc() );
    00253
    00254       return true;
    00255     }
    00256     else
    00257       KCC_ERR( i18n("Bad argument '%1'. Usage: %2 on|off|1|0|true|false")
    00258                .arg( args.first() ).arg( cmd ) );
    00259   }
    00260
    00261   // unlikely..
    00262   KCC_ERR( i18n("Unknown command '%1'").arg(cmd) );
    00263 }
    00264
    00265 KCompletion *KateCommands::CoreCommands::completionObject( const QString &cmd, Kate::View *view )
    00266 {
    00267   if ( cmd == "set-highlight" )
    00268   {
    00269     KateView *v = (KateView*)view;
    00270     QStringList l;
    00271     for ( uint i = 0; i < v->doc()->hlModeCount(); i++ )
    00272       l << v->doc()->hlModeName( i );
    00273
    00274     KateCmdShellCompletion *co = new KateCmdShellCompletion();
    00275     co->setItems( l );
    00276     co->setIgnoreCase( true );
    00277     return co;
    00278   }
    00279   return 0L;
    00280 }
    00281 //END CoreCommands
    00282
    00283 //BEGIN SedReplace
    00284 static void replace(QString &s, const QString &needle, const QString &with)
    00285 {
    00286   int pos=0;
    00287   while (1)
    00288   {
    00289     pos=s.find(needle, pos);
    00290     if (pos==-1) break;
    00291     s.replace(pos, needle.length(), with);
    00292     pos+=with.length();
    00293   }
    00294
    00295 }
    00296
    00297 static int backslashString(const QString &haystack, const QString &needle, int index)
    00298 {
    00299   int len=haystack.length();
    00300   int searchlen=needle.length();
    00301   bool evenCount=true;
    00302   while (index<len)
    00303   {
    00304     if (haystack[index]=='\\')
    00305     {
    00306       evenCount=!evenCount;
    00307     }
    00308     else
    00309     {  // isn't a slash
    00310       if (!evenCount)
    00311       {
    00312         if (haystack.mid(index, searchlen)==needle)
    00313           return index-1;
    00314       }
    00315       evenCount=true;
    00316     }
    00317     index++;
    00318
    00319   }
    00320
    00321   return -1;
    00322 }
    00323
    00324 // exchange "\t" for the actual tab character, for example
    00325 static void exchangeAbbrevs(QString &str)
    00326 {
    00327   // the format is (findreplace)*[nullzero]
    00328   const char *magic="a\x07t\tn\n";
    00329
    00330   while (*magic)
    00331   {
    00332     int index=0;
    00333     char replace=magic[1];
    00334     while ((index=backslashString(str, QChar(*magic), index))!=-1)
    00335     {
    00336       str.replace(index, 2, QChar(replace));
    00337       index++;
    00338     }
    00339     magic++;
    00340     magic++;
    00341   }
    00342 }
    00343
    00344 int KateCommands::SedReplace::sedMagic( KateDocument *doc, int &line,
    00345                                         const QString &find, const QString &repOld, const QString &delim,
    00346                                         bool noCase, bool repeat,
    00347                                         uint startcol, int endcol )
    00348 {
    00349   KateTextLine *ln = doc->kateTextLine( line );
    00350   if ( ! ln || ! ln->length() ) return 0;
    00351
    00352   // HANDLING "\n"s in PATTERN
    00353   // * Create a list of patterns, splitting PATTERN on (unescaped) "\n"
    00354   // * insert $s and ^s to match line ends/beginnings
    00355   // * When matching patterhs after the first one, replace \N with the captured
    00356   //   text.
    00357   // * If all patterns in the list match sequentiel lines, there is a match, so
    00358   // * remove line/start to line + patterns.count()-1/patterns.last.length
    00359   // * handle capatures by putting them in one list.
    00360   // * the existing insertion is fine, including the line calculation.
    00361
    00362   QStringList patterns = QStringList::split( QRegExp("(^\\\\n|(?![^\\\\])\\\\n)"), find, true );
    00363   if ( patterns.count() > 1 )
    00364   {
    00365     for ( uint i = 0; i < patterns.count(); i++ )
    00366     {
    00367       if ( i < patterns.count() - 1 )
    00368         patterns[i].append("$");
    00369       if ( i )
    00370         patterns[i].prepend("^");
    00371
    00372        kdDebug(13025)<<"patterns["<<i<<"] ="<<patterns[i]<<endl;
    00373     }
    00374   }
    00375
    00376   QRegExp matcher(patterns[0], noCase);
    00377
    00378   uint len;
    00379   int matches = 0;
    00380
    00381   while ( ln->searchText( startcol, matcher, &startcol, &len ) )
    00382   {
    00383
    00384     if ( endcol >= 0  && startcol + len > (uint)endcol )
    00385       break;
    00386
    00387     matches++;
    00388
    00389
    00390     QString rep=repOld;
    00391
    00392     // now set the backreferences in the replacement
    00393     QStringList backrefs=matcher.capturedTexts();
    00394     int refnum=1;
    00395
    00396     QStringList::Iterator i = backrefs.begin();
    00397     ++i;
    00398
    00399     for (; i!=backrefs.end(); ++i)
    00400     {
    00401       // I need to match "\\" or "", but not "\"
    00402       QString number=QString::number(refnum);
    00403
    00404       int index=0;
    00405       while (index!=-1)
    00406       {
    00407         index=backslashString(rep, number, index);
    00408         if (index>=0)
    00409         {
    00410           rep.replace(index, 2, *i);
    00411           index+=(*i).length();
    00412         }
    00413       }
    00414
    00415       refnum++;
    00416     }
    00417
    00418     replace(rep, "\\\\", "\\");
    00419     replace(rep, "\\" + delim, delim);
    00420
    00421     doc->removeText( line, startcol, line, startcol + len );
    00422     doc->insertText( line, startcol, rep );
    00423
    00424     // TODO if replace contains \n,
    00425     // change the line number and
    00426     // check for text that needs be searched behind the last inserted newline.
    00427     int lns = rep.contains('\n');
    00428     if ( lns )
    00429     {
    00430       line += lns;
    00431
    00432       if ( doc->lineLength( line ) > 0 && ( endcol < 0 || (uint)endcol  >= startcol + len ) )
    00433       {
    00434       //  if ( endcol  >= startcol + len )
    00435           endcol -= (startcol + len);
    00436           uint sc = rep.length() - rep.findRev('\n') - 1;
    00437         matches += sedMagic( doc, line, find, repOld, delim, noCase, repeat, sc, endcol );
    00438       }
    00439     }
    00440
    00441     if (!repeat) break;
    00442     startcol+=rep.length();
    00443
    00444     // sanity check -- avoid infinite loops eg with %s,.*,,g ;)
    00445     uint ll = ln->length();
    00446     if ( ! ll || startcol > ll )
    00447       break;
    00448   }
    00449
    00450   return matches;
    00451 }
    00452
    00453 bool KateCommands::SedReplace::exec (Kate::View *view, const QString &cmd, QString &msg)
    00454 {
    00455    kdDebug(13025)<<"SedReplace::execCmd( "<<cmd<<" )"<<endl;
    00456
    00457   QRegExp delim("^[$%]?s\\s*([^\\w\\s])");
    00458   if ( delim.search( cmd ) < 0 ) return false;
    00459
    00460   bool fullFile=cmd[0]=='%';
    00461   bool noCase=cmd[cmd.length()-1]=='i' || cmd[cmd.length()-2]=='i';
    00462   bool repeat=cmd[cmd.length()-1]=='g' || cmd[cmd.length()-2]=='g';
    00463   bool onlySelect=cmd[0]=='$';
    00464
    00465   QString d = delim.cap(1);
    00466    kdDebug(13025)<<"SedReplace: delimiter is '"<<d<<"'"<<endl;
    00467
    00468   QRegExp splitter( QString("^[$%]?s\\s*")  + d + "((?:[^\\\\\\" + d + "]|\\\\.)*)\\" + d +"((?:[^\\\\\\" + d + "]|\\\\.)*)\\" + d + "[ig]{0,2}$" );
    00469   if (splitter.search(cmd)<0) return false;
    00470
    00471   QString find=splitter.cap(1);
    00472    kdDebug(13025)<< "SedReplace: find=" << find.latin1() <<endl;
    00473
    00474   QString replace=splitter.cap(2);
    00475   exchangeAbbrevs(replace);
    00476    kdDebug(13025)<< "SedReplace: replace=" << replace.latin1() <<endl;
    00477
    00478   KateDocument *doc = ((KateView*)view)->doc();
    00479   if ( ! doc ) return false;
    00480
    00481   doc->editStart();
    00482
    00483   int res = 0;
    00484
    00485   if (fullFile)
    00486   {
    00487     uint numLines=doc->numLines();
    00488     for (int line=0; (uint)line < numLines; line++)
    00489     {
    00490       res += sedMagic( doc, line, find, replace, d, !noCase, repeat );
    00491       if ( ! repeat && res ) break;
    00492     }
    00493   }
    00494   else if (onlySelect)
    00495   {
    00496     int startline = doc->selStartLine();
    00497     uint startcol = doc->selStartCol();
    00498     int endcol = -1;
    00499     do {
    00500       if ( startline == doc->selEndLine() )
    00501         endcol = doc->selEndCol();
    00502
    00503       res += sedMagic( doc, startline, find, replace, d, !noCase, repeat, startcol, endcol );
    00504
    00505       /*if ( startcol )*/ startcol = 0;
    00506
    00507       startline++;
    00508     } while ( (int)startline <= doc->selEndLine() );
    00509   }
    00510   else // just this line
    00511   {
    00512     int line=view->cursorLine();
    00513     res += sedMagic(doc, line, find, replace, d, !noCase, repeat);
    00514   }
    00515
    00516   msg = i18n("1 replacement done", "%n replacements done",res );
    00517
    00518   doc->editEnd();
    00519
    00520   return true;
    00521 }
    00522 //END SedReplace
    00523
    00524 //BEGIN Character
    00525 bool KateCommands::Character::exec (Kate::View *view, const QString &_cmd, QString &)
    00526 {
    00527   QString cmd = _cmd;
    00528
    00529   // hex, octal, base 9+1
    00530   QRegExp num("^char *(0?x[0-9A-Fa-f]{1,4}|0[0-7]{1,6}|[0-9]{1,3})$");
    00531   if (num.search(cmd)==-1) return false;
    00532
    00533   cmd=num.cap(1);
    00534
    00535   // identify the base
    00536
    00537   unsigned short int number=0;
    00538   int base=10;
    00539   if (cmd[0]=='x' || cmd.left(2)=="0x")
    00540   {
    00541     cmd.replace(QRegExp("^0?x"), "");
    00542     base=16;
    00543   }
    00544   else if (cmd[0]=='0')
    00545     base=8;
    00546   bool ok;
    00547   number=cmd.toUShort(&ok, base);
    00548   if (!ok || number==0) return false;
    00549   if (number<=255)
    00550   {
    00551     char buf[2];
    00552     buf[0]=(char)number;
    00553     buf[1]=0;
    00554     view->insertText(QString(buf));
    00555   }
    00556   else
    00557   { // do the unicode thing
    00558     QChar c(number);
    00559     view->insertText(QString(&c, 1));
    00560   }
    00561
    00562   return true;
    00563 }
    00564 //END Character
    00565
    00566 //BEGIN Date
    00567 bool KateCommands::Date::exec (Kate::View *view, const QString &cmd, QString &)
    00568 {
    00569   if (cmd.left(4) != "date")
    00570     return false;
    00571
    00572   if (QDateTime::currentDateTime().toString(cmd.mid(5, cmd.length()-5)).length() > 0)
    00573     view->insertText(QDateTime::currentDateTime().toString(cmd.mid(5, cmd.length()-5)));
    00574   else
    00575     view->insertText(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));
    00576
    00577   return true;
    00578 }
    00579 //END Date
    00580
    00581 // kate: space-indent on; indent-width 2; replace-tabs on;

This file is part of the documentation for kate Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:36:30 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003