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