• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

Kate

katecmds.cpp

Go to the documentation of this file.
00001 /*  This file is part of the KDE libraries and the Kate part.
00002  *
00003  *  Copyright (C) 2003-2005 Anders Lund <anders@alweb.dk>
00004  *  Copyright (C) 2001-2010 Christoph Cullmann <cullmann@kde.org>
00005  *  Copyright (C) 2001 Charles Samuels <charles@kde.org>
00006  *
00007  *  This library is free software; you can redistribute it and/or
00008  *  modify it under the terms of the GNU Library General Public
00009  *  License as published by the Free Software Foundation; either
00010  *  version 2 of the License, or (at your option) any later version.
00011  *
00012  *  This library is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  *  Library General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU Library General Public License
00018  *  along with this library; see the file COPYING.LIB.  If not, write to
00019  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00020  *  Boston, MA 02110-1301, USA.
00021  */
00022 
00023 #include "katecmds.h"
00024 
00025 #include "katedocument.h"
00026 #include "kateview.h"
00027 #include "kateconfig.h"
00028 #include "kateautoindent.h"
00029 #include "katetextline.h"
00030 #include "katesyntaxmanager.h"
00031 #include "kateglobal.h"
00032 #include "kateviglobal.h"
00033 #include "katerenderer.h"
00034 #include "katecmd.h"
00035 
00036 #include <kdebug.h>
00037 #include <klocale.h>
00038 #include <kurl.h>
00039 #include <kshellcompletion.h>
00040 
00041 #include <QtCore/QRegExp>
00042 
00043 //BEGIN CoreCommands
00044 // this returns wheather the string s could be converted to
00045 // a bool value, one of on|off|1|0|true|false. the argument val is
00046 // set to the extracted value in case of success
00047 static bool getBoolArg( const QString &t, bool *val  )
00048 {
00049   bool res( false );
00050   QString s = t.toLower();
00051   res = (s == "on" || s == "1" || s == "true");
00052   if ( res )
00053   {
00054     *val = true;
00055     return true;
00056   }
00057   res = (s == "off" || s == "0" || s == "false");
00058   if ( res )
00059   {
00060     *val = false;
00061     return true;
00062   }
00063   return false;
00064 }
00065 
00066 const QStringList &KateCommands::CoreCommands::cmds()
00067 {
00068   static QStringList l;
00069 
00070   if (l.isEmpty())
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-width"
00076     << "set-indent-mode" << "set-auto-indent"
00077     << "set-line-numbers" << "set-folding-markers" << "set-icon-border"
00078     << "set-wrap-cursor"
00079     << "set-word-wrap" << "set-word-wrap-column"
00080     << "set-replace-tabs-save" << "set-remove-trailing-space-save"
00081     << "set-highlight" << "set-mode" << "set-show-indent"
00082     << "print";
00083 
00084   return l;
00085 }
00086 
00087 bool KateCommands::CoreCommands::exec(KTextEditor::View *view,
00088                             const QString &_cmd,
00089                             QString &errorMsg)
00090 {
00091   return exec( view, _cmd, errorMsg, KTextEditor::Range::invalid() );
00092 }
00093 
00094 bool KateCommands::CoreCommands::exec(KTextEditor::View *view,
00095                             const QString &_cmd,
00096                             QString &errorMsg,
00097                             const KTextEditor::Range& range)
00098 {
00099 #define KCC_ERR(s) { errorMsg=s; return false; }
00100   // cast it hardcore, we know that it is really a kateview :)
00101   KateView *v = (KateView*) view;
00102 
00103   if ( ! v )
00104     KCC_ERR( i18n("Could not access view") );
00105 
00106   //create a list of args
00107   QStringList args(_cmd.split( QRegExp("\\s+"), QString::SkipEmptyParts)) ;
00108   QString cmd ( args.takeFirst() );
00109 
00110   // ALL commands that takes no arguments.
00111   if ( cmd == "indent" )
00112   {
00113     if ( range.isValid() ) {
00114       v->doc()->editStart();
00115       for ( int line = range.start().line(); line <= range.end().line(); line++ ) {
00116         v->doc()->indent( KTextEditor::Range(line, 0, line, 0), 1 );
00117       }
00118       v->doc()->editEnd();
00119     } else {
00120       v->indent();
00121     }
00122     return true;
00123   }
00124   else if ( cmd == "unindent" )
00125   {
00126     if ( range.isValid() ) {
00127       v->doc()->editStart();
00128       for ( int line = range.start().line(); line <= range.end().line(); line++ ) {
00129         v->doc()->indent( KTextEditor::Range(line, 0, line, 0), -1 );
00130       }
00131       v->doc()->editEnd();
00132     } else {
00133       v->unIndent();
00134     }
00135     return true;
00136   }
00137   else if ( cmd == "cleanindent" )
00138   {
00139     if ( range.isValid() ) {
00140       v->doc()->editStart();
00141       for ( int line = range.start().line(); line <= range.end().line(); line++ ) {
00142         v->doc()->indent( KTextEditor::Range(line, 0, line, 0), 0 );
00143       }
00144       v->doc()->editEnd();
00145     } else {
00146       v->cleanIndent();
00147     }
00148     return true;
00149   }
00150   else if ( cmd == "comment" )
00151   {
00152     if ( range.isValid() ) {
00153       v->doc()->editStart();
00154       for ( int line = range.start().line(); line <= range.end().line(); line++ ) {
00155         v->doc()->comment( v, line, 0, 1 );
00156       }
00157       v->doc()->editEnd();
00158     } else {
00159       v->comment();
00160     }
00161     return true;
00162   }
00163   else if ( cmd == "uncomment" )
00164   {
00165     if ( range.isValid() ) {
00166       v->doc()->editStart();
00167       for ( int line = range.start().line(); line <= range.end().line(); line++ ) {
00168         v->doc()->comment( v, line, 0, -1 );
00169       }
00170       v->doc()->editEnd();
00171     } else {
00172       v->uncomment();
00173     }
00174     return true;
00175   }
00176   else if ( cmd == "kill-line" )
00177   {
00178     if ( range.isValid() ) {
00179       v->doc()->editStart();
00180       for ( int line = range.start().line(); line <= range.end().line(); line++ ) {
00181         v->doc()->removeLine( range.start().line() );
00182       }
00183       v->doc()->editEnd();
00184     } else {
00185       v->killLine();
00186     }
00187     return true;
00188   }
00189   else if ( cmd == "print" )
00190   {
00191     v->doc()->printDialog();
00192     return true;
00193   }
00194 
00195   // ALL commands that take a string argument
00196   else if ( cmd == "set-indent-mode" ||
00197             cmd == "set-highlight" ||
00198             cmd == "set-mode" )
00199   {
00200     // need at least one item, otherwise args.first() crashes
00201     if ( ! args.count() )
00202       KCC_ERR( i18n("Missing argument. Usage: %1 <value>",  cmd ) );
00203 
00204     if ( cmd == "set-indent-mode" )
00205     {
00206       v->doc()->config()->setIndentationMode( args.first() );
00207       return true;
00208     }
00209     else if ( cmd == "set-highlight" )
00210     {
00211       if ( v->doc()->setHighlightingMode( args.first()) )
00212       {
00213         ((KateDocument*)v->doc())->setDontChangeHlOnSave ();
00214         return true;
00215       }
00216 
00217       KCC_ERR( i18n("No such highlighting '%1'",  args.first() ) );
00218     }
00219     else if ( cmd == "set-mode" )
00220     {
00221       if ( v->doc()->setMode( args.first()) )
00222         return true;
00223 
00224       KCC_ERR( i18n("No such mode '%1'",  args.first() ) );
00225     }
00226   }
00227   // ALL commands that takes exactly one integer argument.
00228   else if ( cmd == "set-tab-width" ||
00229             cmd == "set-indent-width" ||
00230             cmd == "set-word-wrap-column" ||
00231             cmd == "goto" )
00232   {
00233     // find a integer value > 0
00234     if ( ! args.count() )
00235       KCC_ERR( i18n("Missing argument. Usage: %1 <value>",  cmd ) );
00236     bool ok;
00237     int val ( args.first().toInt( &ok, 10 ) ); // use base 10 even if the string starts with '0'
00238     if ( !ok )
00239       KCC_ERR( i18n("Failed to convert argument '%1' to integer.",
00240                   args.first() ) );
00241 
00242     if ( cmd == "set-tab-width" )
00243     {
00244       if ( val < 1 )
00245         KCC_ERR( i18n("Width must be at least 1.") );
00246       v->doc()->config()->setTabWidth( val );
00247     }
00248     else if ( cmd == "set-indent-width" )
00249     {
00250       if ( val < 1 )
00251         KCC_ERR( i18n("Width must be at least 1.") );
00252       v->doc()->config()->setIndentationWidth( val );
00253     }
00254     else if ( cmd == "set-word-wrap-column" )
00255     {
00256       if ( val < 2 )
00257         KCC_ERR( i18n("Column must be at least 1.") );
00258       v->doc()->setWordWrapAt( val );
00259     }
00260     else if ( cmd == "goto" )
00261     {
00262       if ( args.first().at(0) == '-' || args.first().at(0) == '+' ) {
00263         // if the number starts with a minus or plus sign, add/subract the number
00264         val = v->cursorPosition().line() + val;
00265       } else {
00266         val--; // convert given line number to the internal representation of line numbers
00267       }
00268 
00269       // constrain cursor to the range [0, number of lines]
00270       if ( val < 0 ) {
00271         val = 0;
00272       } else if ( val > v->doc()->lines()-1 ) {
00273         val = v->doc()->lines()-1;
00274       }
00275 
00276       v->setCursorPosition( KTextEditor::Cursor( val, 0 ) );
00277       return true;
00278     }
00279     return true;
00280   }
00281 
00282   // ALL commands that takes 1 boolean argument.
00283   else if ( cmd == "set-icon-border" ||
00284             cmd == "set-folding-markers" ||
00285             cmd == "set-line-numbers" ||
00286             cmd == "set-replace-tabs" ||
00287             cmd == "set-remove-trailing-space" ||
00288             cmd == "set-show-tabs" ||
00289             cmd == "set-word-wrap" ||
00290             cmd == "set-wrap-cursor" ||
00291             cmd == "set-replace-tabs-save" ||
00292             cmd == "set-remove-trailing-space-save" ||
00293             cmd == "set-show-indent" )
00294   {
00295     if ( ! args.count() )
00296       KCC_ERR( i18n("Usage: %1 on|off|1|0|true|false",  cmd ) );
00297     bool enable = false;
00298     KateDocumentConfig * const config = v->doc()->config();
00299     if ( getBoolArg( args.first(), &enable ) )
00300     {
00301       if ( cmd == "set-icon-border" )
00302         v->setIconBorder( enable );
00303       else if (cmd == "set-folding-markers")
00304         v->setFoldingMarkersOn( enable );
00305       else if ( cmd == "set-line-numbers" )
00306         v->setLineNumbersOn( enable );
00307       else if ( cmd == "set-show-indent" )
00308         v->renderer()->setShowIndentLines( enable );
00309       else if ( cmd == "set-replace-tabs" )
00310         config->setReplaceTabsDyn( enable );
00311       else if ( cmd == "set-remove-trailing-space" )
00312         config->setRemoveTrailingDyn( enable );
00313       else if ( cmd == "set-show-tabs" )
00314         config->setShowTabs( enable );
00315       else if ( cmd == "set-show-trailing-spaces" )
00316         config->setShowSpaces( enable );
00317       else if ( cmd == "set-word-wrap" )
00318         v->doc()->setWordWrap( enable );
00319       else if ( cmd == "set-remove-trailing-space-save" )
00320         config->setRemoveSpaces( enable );
00321       else if ( cmd == "set-wrap-cursor" )
00322         config->setWrapCursor( enable );
00323 
00324       return true;
00325     }
00326     else
00327       KCC_ERR( i18n("Bad argument '%1'. Usage: %2 on|off|1|0|true|false",
00328                  args.first() ,  cmd ) );
00329   }
00330 
00331   // unlikely..
00332   KCC_ERR( i18n("Unknown command '%1'", cmd) );
00333 }
00334 
00335 bool KateCommands::CoreCommands::supportsRange(const QString &range)
00336 {
00337   static QStringList l;
00338 
00339   if (l.isEmpty())
00340   l << "indent" << "unindent" << "cleanindent"
00341     << "comment" << "uncomment" << "kill-line";
00342 
00343   return l.contains(range);
00344 }
00345 
00346 KCompletion *KateCommands::CoreCommands::completionObject( KTextEditor::View *view, const QString &cmd )
00347 {
00348   Q_UNUSED(view)
00349 
00350   if ( cmd == "set-highlight" )
00351   {
00352     QStringList l;
00353     for ( int i = 0; i < KateHlManager::self()->highlights(); i++ )
00354       l << KateHlManager::self()->hlName (i);
00355 
00356     KateCmdShellCompletion *co = new KateCmdShellCompletion();
00357     co->setItems( l );
00358     co->setIgnoreCase( true );
00359     return co;
00360   }
00361   return 0L;
00362 }
00363 //END CoreCommands
00364 
00365 // BEGIN ViCommands
00366 const QStringList &KateCommands::ViCommands::cmds()
00367 {
00368   static QStringList l;
00369 
00370   if (l.isEmpty())
00371   l << "nnoremap" << "nn";
00372 
00373   return l;
00374 }
00375 
00376 bool KateCommands::ViCommands::exec(KTextEditor::View *view,
00377                             const QString &_cmd,
00378                             QString &msg)
00379 {
00380   return exec( view, _cmd, msg, KTextEditor::Range::invalid() );
00381 }
00382 
00383 bool KateCommands::ViCommands::exec(KTextEditor::View *view,
00384                             const QString &_cmd,
00385                             QString &msg,
00386                             const KTextEditor::Range& range)
00387 {
00388   Q_UNUSED(range)
00389   // cast it hardcore, we know that it is really a kateview :)
00390   KateView *v = (KateView*) view;
00391 
00392   if ( !v ) {
00393     msg = i18n("Could not access view");
00394     return false;
00395   }
00396 
00397   //create a list of args
00398   QStringList args(_cmd.split( QRegExp("\\s+"), QString::SkipEmptyParts)) ;
00399   QString cmd ( args.takeFirst() );
00400 
00401   // ALL commands that takes no arguments.
00402   if ( cmd == "nnoremap" || cmd == "nn" )
00403   {
00404     if ( args.count() == 1 ) {
00405       msg = KateGlobal::self()->viInputModeGlobal()->getMapping( NormalMode, args.at( 0 ), true );
00406       if ( msg.isEmpty() ) {
00407         msg = i18n( "No mapping found for \"%1\"", args.at(0) );
00408         return false;
00409       } else {
00410         msg = i18n( "\"%1\" is mapped to \"%2\"", args.at( 0 ), msg );
00411       }
00412     } else if ( args.count() == 2 ) {
00413       KateGlobal::self()->viInputModeGlobal()->addMapping( NormalMode, args.at( 0 ), args.at( 1 ) );
00414     } else {
00415       msg = i18n("Missing argument(s). Usage: %1 <from> [<to>]",  cmd );
00416       return false;
00417     }
00418 
00419     return true;
00420   }
00421 
00422   // should not happen :)
00423   msg = i18n("Unknown command '%1'", cmd);
00424   return false;
00425 }
00426 
00427 bool KateCommands::ViCommands::supportsRange(const QString &range)
00428 {
00429   Q_UNUSED(range)
00430   return false; // no commands support a range yet
00431 }
00432 
00433 KCompletion *KateCommands::ViCommands::completionObject( KTextEditor::View *view, const QString &cmd )
00434 {
00435   Q_UNUSED(view)
00436 
00437   KateView *v = (KateView*) view;
00438 
00439   if ( v && ( cmd == "nn" || cmd == "nnoremap" ) )
00440   {
00441     QStringList l = KateGlobal::self()->viInputModeGlobal()->getMappings( NormalMode );
00442 
00443     KateCmdShellCompletion *co = new KateCmdShellCompletion();
00444     co->setItems( l );
00445     co->setIgnoreCase( false );
00446     return co;
00447   }
00448   return 0L;
00449 }
00450 //END ViCommands
00451 
00452 // BEGIN AppCommands
00453 KateCommands::AppCommands::AppCommands()
00454     : KTextEditor::Command()
00455 {
00456     re_write.setPattern("w"); // temporarily add :w
00457     //re_write.setPattern("w(a)?");
00458     //re_quit.setPattern("(w)?q?(a)?");
00459     //re_exit.setPattern("x(a)?");
00460     //re_changeBuffer.setPattern("b(n|p)");
00461     //re_edit.setPattern("e(dit)?");
00462     //re_new.setPattern("(v)?new");
00463 }
00464 
00465 const QStringList& KateCommands::AppCommands::cmds()
00466 {
00467     static QStringList l;
00468 
00469     if (l.empty()) {
00470         //l << "q" << "qa" << "w" << "wq" << "wa" << "wqa" << "x" << "xa"
00471           //<< "bn" << "bp" << "new" << "vnew" << "e" << "edit" << "enew";
00472         l << "w";
00473     }
00474 
00475     return l;
00476 }
00477 
00478 bool KateCommands::AppCommands::exec(KTextEditor::View *view,
00479                                      const QString &cmd, QString &msg )
00480 {
00481     QStringList args(cmd.split( QRegExp("\\s+"), QString::SkipEmptyParts)) ;
00482     QString command( args.takeFirst() );
00483 
00484     if (re_write.exactMatch(command)) { // w, wa
00485 /*        if (!re_write.cap(1).isEmpty()) { // [a]ll
00486             view->document()->saveAll();
00487             msg = i18n("All documents written to disk");
00488         } else { // w*/
00489             // Save file
00490             view->document()->documentSave();
00491             msg = i18n("Document written to disk");
00492         //}
00493     }
00494     /*else if (re_quit.exactMatch(command)) { // q qa wq wqa
00495         if (!re_quit.cap(2).isEmpty()) { // a[ll] qa wqa
00496             if (!re_quit.cap(1).isEmpty()) { // [w]rite wqa
00497                 view->document()->saveAll();
00498             }
00499             view->document()->closeAll();
00500         } else { // q wq
00501             if (!re_quit.cap(1).isEmpty() && view->document()->isModified()) { // [w]rite wq
00502                 view->document()->documentSave();
00503             }
00504             view->document()->closeDocument();
00505          }
00506     } else if (re_exit.exactMatch(command)) { // x xa
00507         if (!re_exit.cap(1).isEmpty()) { // a[ll] xa
00508           view->document()->saveAll();
00509           view->document()->closeAll();
00510         } else { // x
00511             if (view->document()->isModified()) {
00512                 view->document()->documentSave();
00513             }
00514             view->document()->closeDocument();
00515         }
00516     }
00517     else if (re_changeBuffer.exactMatch(command)) {
00518         if (re_changeBuffer.cap(1) == "n") { // next document
00519           view->document()->switchToNextDocument();
00520         }
00521         else { // previous document
00522           view->document()->switchToPreviousDocument();
00523         }
00524     }
00525     else if (re_edit.exactMatch(command)) {
00526         view->document()->documentReload();
00527     }
00528     else if (re_new.exactMatch(command)) {
00529         if (re_new.cap(1) == "v") { // vertical split
00530           view->document()->splitViewSpaceVert();
00531         } else {                    // horizontal split
00532           view->document()->splitViewSpaceHoriz();
00533         }
00534         view->document()->newDocument();
00535     }
00536     else if (command == "enew") {
00537         view->document()->newDocument();
00538     }*/
00539     return true;
00540 }
00541 
00542 bool KateCommands::AppCommands::help(KTextEditor::View *view, const QString &cmd, QString &msg)
00543 {
00544     Q_UNUSED(view);
00545 
00546     if (re_write.exactMatch(cmd)) {
00547         msg = "<p><b>w/wa &mdash; write document(s) to disk</b></p>"
00548               "<p>Usage: <tt><b>w[a]</b></tt></p>"
00549               "<p>Writes the current document(s) to disk. "
00550               "It can be called in two ways:<br />"
00551               " <tt>w</tt> &mdash; writes the current document to disk<br />"
00552               " <tt>wa</tt> &mdash; writes all document to disk.</p>"
00553               "<p>If no file name is associated with the document, "
00554               "a file dialog will be shown.</p>";
00555         return true;
00556     }
00557     /*else if (re_quit.exactMatch(cmd)) {
00558         msg = "<p><b>q/qa/wq/wqa &mdash; [write and] quit</b></p>"
00559               "<p>Usage: <tt><b>[w]q[a]</b></tt></p>"
00560               "<p>Quits the application. If <tt>w</tt> is prepended, it also writes"
00561               " the document(s) to disk. This command "
00562               "can be called in several ways:<br />"
00563               " <tt>q</tt> &mdash; closes the current view..<br />"
00564               " <tt>qa</tt> &mdash; closes all view, effectively quitting the application.<br />"
00565               " <tt>wq</tt> &mdash; writes the current document to disk and closes its view.<br />"
00566               " <tt>wqa</tt> &mdash; writes all document to disk and quits.</p>"
00567               "<p>In all cases, if the view being closed is the last view, the application quits. "
00568               "If no file name is associated with the document and it should be written to disk, "
00569               "a file dialog will be shown.</p>";
00570         return true;
00571     }
00572     else if (re_exit.exactMatch(cmd)) {
00573         msg = "<p><b>x/xa &mdash; write and quit</b></p>"
00574               "<p>Usage: <tt><b>x[a]</b></tt></p>"
00575               "<p>Saves document(s) and quits (e<b>x</b>its). This command "
00576               "can be called in two ways:<br />"
00577               " <tt>x</tt> &mdash; closes the current view..<br />"
00578               " <tt>xa</tt> &mdash; closes all view, effectively quitting the application.</p>"
00579               "<p>In all cases, if the view being closed is the last view, the application quits. "
00580               "If no file name is associated with the document and it should be written to disk, "
00581               "a file dialog will be shown.</p>"
00582               "<p>Unlike the 'w' commands, this command only writes the doucment if it is modified."
00583               "</p>";
00584         return true;
00585     }
00586     else if (re_changeBuffer.exactMatch(cmd)) {
00587         msg = "<p><b>bp/bn &mdash; switch no previous/next document</b></p>"
00588               "<p>Usage: <tt><b>bp/bn</b></tt></p>"
00589               "<p>Goes to <b>p</b>revious or <b>n</b>ext document (\"<b>b</b>uffer\"). The two"
00590               " commands are:<br />"
00591               " <tt>bp</tt> &mdash; goes to the document before the current one in the document"
00592               " list.<br />"
00593               " <tt>bn</tt> &mdash; goes to the document after the current one in the document"
00594               " list.<br />"
00595               "<p>Both commands wrap around, i.e., if you go past the last document you and up"
00596               " at the first and vice versa.</p>";
00597         return true;
00598     }
00599     else if (re_new.exactMatch(cmd)) {
00600         msg = "<p><b>[v]new &mdash; split view and create new document</b></p>"
00601               "<p>Usage: <tt><b>[v]new</b></tt></p>"
00602               "<p>Splits the current view and opens a new document in the new view."
00603               " This command can be called in two ways:<br />"
00604               " <tt>new</tt> &mdash; splits the view horizontally and opens a new document.<br />"
00605               " <tt>vnew</tt> &mdash; splits the view vertically and opens a new document.<br />"
00606               "</p>";
00607         return true;
00608     }
00609     else if (re_edit.exactMatch(cmd)) {
00610         msg = "<p><b>e[dit] &mdash; reload current document</b></p>"
00611               "<p>Usage: <tt><b>e[dit]</b></tt></p>"
00612               "<p>Starts <b>e</b>diting the current document again. This is useful to re-edit"
00613              " the current file, when it has been changed by another program.</p>";
00614         return true;
00615     }*/
00616 
00617     return false;
00618 }
00619 //END AppCommands
00620 
00621 //BEGIN SedReplace
00622 static void replace(QString &s, const QString &needle, const QString &with)
00623 {
00624   int pos=0;
00625   while (1)
00626   {
00627     pos=s.indexOf(needle, pos);
00628     if (pos==-1) break;
00629     s.replace(pos, needle.length(), with);
00630     pos+=with.length();
00631   }
00632 
00633 }
00634 
00635 static int backslashString(const QString &haystack, const QString &needle, int index)
00636 {
00637   int len=haystack.length();
00638   int searchlen=needle.length();
00639   bool evenCount=true;
00640   while (index<len)
00641   {
00642     if (haystack[index]=='\\')
00643     {
00644       evenCount=!evenCount;
00645     }
00646     else
00647     {  // isn't a slash
00648       if (!evenCount)
00649       {
00650         if (haystack.mid(index, searchlen)==needle)
00651           return index-1;
00652       }
00653       evenCount=true;
00654     }
00655     index++;
00656 
00657   }
00658 
00659   return -1;
00660 }
00661 
00662 // exchange "\t" for the actual tab character, for example
00663 static void exchangeAbbrevs(QString &str)
00664 {
00665   // the format is (findreplace)*[nullzero]
00666   const char *magic="a\x07t\tn\n";
00667 
00668   while (*magic)
00669   {
00670     int index=0;
00671     char replace=magic[1];
00672     while ((index=backslashString(str, QString (QChar::fromAscii(*magic)), index))!=-1)
00673     {
00674       str.replace(index, 2, QChar(replace));
00675       index++;
00676     }
00677     magic++;
00678     magic++;
00679   }
00680 }
00681 
00682 int KateCommands::SedReplace::sedMagic( KateDocument *doc, int &line,
00683                                         const QString &find, const QString &repOld, const QString &delim,
00684                                         bool noCase, bool repeat,
00685                                         int startcol, int endcol )
00686 {
00687   Kate::TextLine ln = doc->kateTextLine( line );
00688   if ( ! ln || ! ln->length() ) return 0;
00689 
00690   // HANDLING "\n"s in PATTERN
00691   // * Create a list of patterns, splitting PATTERN on (unescaped) "\n"
00692   // * insert $s and ^s to match line ends/beginnings
00693   // * When matching patterhs after the first one, replace \N with the captured
00694   //   text.
00695   // * If all patterns in the list match sequentiel lines, there is a match, so
00696   // * remove line/start to line + patterns.count()-1/patterns.last.length
00697   // * handle capatures by putting them in one list.
00698   // * the existing insertion is fine, including the line calculation.
00699 
00700   QStringList patterns(find.split( QRegExp("(^\\\\n|(?![^\\\\])\\\\n)"), QString::KeepEmptyParts));
00701   if ( patterns.count() > 1 )
00702   {
00703     for ( int i = 0; i < patterns.count(); i++ )
00704     {
00705       if ( i < patterns.count() - 1 )
00706         patterns[i].append("$");
00707       if ( i )
00708         patterns[i].prepend("^");
00709 
00710        kDebug(13025)<<"patterns["<<i<<"] ="<<patterns[i];
00711     }
00712   }
00713 
00714   QRegExp matcher(patterns[0], noCase ?Qt::CaseSensitive:Qt::CaseInsensitive);
00715 
00716   int matches = 0;
00717 
00718   while ( (startcol = matcher.indexIn(ln->string(), startcol)) >= 0 )
00719   {
00720     const int len = matcher.matchedLength();
00721 
00722     if ( endcol >= 0  && startcol + len > endcol )
00723       break;
00724 
00725     matches++;
00726 
00727 
00728     QString rep=repOld;
00729 
00730     // now set the backreferences in the replacement
00731     const QStringList backrefs=matcher.capturedTexts();
00732     int refnum=1;
00733 
00734     QStringList::ConstIterator i = backrefs.begin();
00735     ++i;
00736 
00737     for (; i!=backrefs.end(); ++i)
00738     {
00739       // I need to match "\\" or "", but not "\"
00740       QString number=QString::number(refnum);
00741 
00742       int index=0;
00743       while (index!=-1)
00744       {
00745         index=backslashString(rep, number, index);
00746         if (index>=0)
00747         {
00748           rep.replace(index, 2, *i);
00749           index+=(*i).length();
00750         }
00751       }
00752 
00753       refnum++;
00754     }
00755 
00756     replace(rep, "\\\\", "\\");
00757     replace(rep, "\\" + delim, delim);
00758 
00759     doc->removeText( KTextEditor::Range (line, startcol, line, startcol + len) );
00760     doc->insertText( KTextEditor::Cursor (line, startcol), rep );
00761 
00762     // TODO if replace contains \n,
00763     // change the line number and
00764     // check for text that needs be searched behind the last inserted newline.
00765     int lns = rep.count(QChar::fromLatin1('\n'));
00766     if ( lns > 0 )
00767     {
00768       line += lns;
00769 
00770       if ( doc->lineLength( line ) > 0 && ( endcol < 0 || endcol  >= startcol + len ) )
00771       {
00772       //  if ( endcol  >= startcol + len )
00773           endcol -= (startcol + len);
00774           uint sc = rep.length() - rep.lastIndexOf('\n') - 1;
00775         matches += sedMagic( doc, line, find, repOld, delim, noCase, repeat, sc, endcol );
00776       }
00777     }
00778 
00779     if (!repeat) break;
00780     startcol+=rep.length();
00781 
00782     // sanity check -- avoid infinite loops eg with %s,.*,,g ;)
00783     int ll = ln->length();
00784     if ( ! ll || startcol > ll )
00785       break;
00786   }
00787 
00788   return matches;
00789 }
00790 
00791 bool KateCommands::SedReplace::exec (KTextEditor::View *view, const QString &cmd, QString &msg)
00792 {
00793   return exec(view, cmd, msg, KTextEditor::Range::invalid());
00794 }
00795 
00796 bool KateCommands::SedReplace::exec (class KTextEditor::View *view, const QString &cmd,
00797     QString &msg, const KTextEditor::Range &r)
00798 {
00799   kDebug(13025)<<"SedReplace::execCmd( "<<cmd<<" )";
00800   if (r.isValid()) {
00801     kDebug(13025)<<"Range: " << r;
00802   }
00803 
00804   QRegExp delim("^s\\s*([^\\w\\s])");
00805   if ( delim.indexIn( cmd ) < 0 ) return false;
00806 
00807   bool noCase=cmd[cmd.length()-1]=='i' || cmd[cmd.length()-2]=='i';
00808   bool repeat=cmd[cmd.length()-1]=='g' || cmd[cmd.length()-2]=='g';
00809 
00810   QString d = delim.cap(1);
00811   kDebug(13025)<<"SedReplace: delimiter is '"<<d<<"'";
00812 
00813   QRegExp splitter( QString("^s\\s*")  + d + "((?:[^\\\\\\" + d + "]|\\\\.)*)\\"
00814       + d +"((?:[^\\\\\\" + d + "]|\\\\.)*)(\\" + d + "[ig]{0,2})?$" );
00815   if (splitter.indexIn(cmd)<0) return false;
00816 
00817   QString find=splitter.cap(1);
00818   kDebug(13025)<< "SedReplace: find=" << find;
00819 
00820   QString replace=splitter.cap(2);
00821   exchangeAbbrevs(replace);
00822   kDebug(13025)<< "SedReplace: replace=" << replace;
00823 
00824   if ( find.contains("\\n") )
00825   {
00826     // FIXME: make replacing newlines work
00827     msg = i18n("Sorry, but Kate is not able to replace newlines, yet");
00828     return false;
00829   }
00830 
00831   KateDocument *doc = ((KateView*)view)->doc();
00832   if ( ! doc ) return false;
00833 
00834   doc->editStart();
00835 
00836   int replacementsDone = 0;
00837   int linesTouched = 0;
00838   int linesAdded = 0;
00839 
00840   if (r.isValid()) { // given range
00841     for (int line = r.start().line(); line <= r.end().line()+linesAdded; line++) {
00842       int temp = replacementsDone;
00843       int r = sedMagic( doc, line, find, replace, d, !noCase, repeat );
00844       replacementsDone += r;
00845 
00846       // if we replaced the text with n newlines, we have n new lines to look at
00847       if (replace.contains('\n') ) {
00848         linesAdded += r * replace.count('\n');
00849       }
00850 
00851       if (replacementsDone > temp) {
00852         linesTouched++;
00853       }
00854     }
00855   } else { // current line
00856     int line= view->cursorPosition().line();
00857     replacementsDone += sedMagic(doc, line, find, replace, d, !noCase, repeat);
00858     if (replacementsDone > 0) {
00859       linesTouched = 1;
00860     }
00861   }
00862 
00863   msg = i18ncp("%2 is the translation of the next message",
00864                "1 replacement done on %2", "%1 replacements done on %2", replacementsDone,
00865                i18ncp("substituted into the previous message",
00866                       "1 line", "%1 lines", linesTouched));
00867 
00868   doc->editEnd();
00869 
00870   return true;
00871 }
00872 
00873 //END SedReplace
00874 
00875 //BEGIN Character
00876 bool KateCommands::Character::exec (KTextEditor::View *view, const QString &_cmd, QString &)
00877 {
00878   QString cmd = _cmd;
00879 
00880   // hex, octal, base 9+1
00881   QRegExp num("^char *(0?x[0-9A-Fa-f]{1,4}|0[0-7]{1,6}|[0-9]{1,5})$");
00882   if (num.indexIn(cmd)==-1) return false;
00883 
00884   cmd=num.cap(1);
00885 
00886   // identify the base
00887 
00888   unsigned short int number=0;
00889   int base=10;
00890   if (cmd[0]=='x' || cmd.startsWith(QLatin1String("0x")))
00891   {
00892     cmd.remove(QRegExp("^0?x"));
00893     base=16;
00894   }
00895   else if (cmd[0]=='0')
00896     base=8;
00897   bool ok;
00898   number=cmd.toUShort(&ok, base);
00899   if (!ok || number==0) return false;
00900   if (number<=255)
00901   {
00902     char buf[2];
00903     buf[0]=(char)number;
00904     buf[1]=0;
00905 
00906     view->document()->insertText(view->cursorPosition(), QString(buf));
00907   }
00908   else
00909   { // do the unicode thing
00910     QChar c(number);
00911 
00912     view->document()->insertText(view->cursorPosition(), QString(&c, 1));
00913   }
00914 
00915   return true;
00916 }
00917 
00918 //END Character
00919 
00920 //BEGIN Date
00921 bool KateCommands::Date::exec (KTextEditor::View *view, const QString &cmd, QString &)
00922 {
00923   if (!cmd.startsWith(QLatin1String("date")))
00924     return false;
00925 
00926   if (QDateTime::currentDateTime().toString(cmd.mid(5, cmd.length()-5)).length() > 0)
00927     view->document()->insertText(view->cursorPosition(), QDateTime::currentDateTime().toString(cmd.mid(5, cmd.length()-5)));
00928   else
00929     view->document()->insertText(view->cursorPosition(), QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));
00930 
00931   return true;
00932 }
00933 
00934 //END Date
00935 
00936 // 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
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
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