• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

Kate

katecmds.cpp

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

Kate

Skip menu "Kate"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal