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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • kformula
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
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