00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
00041
00042 static void setDocFlag( KateDocumentConfig::ConfigFlags flag, bool enable,
00043 KateDocument *doc )
00044 {
00045 doc->config()->setConfigFlags( flag, enable );
00046 }
00047
00048
00049
00050
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
00097 KateView *v = (KateView*) view;
00098
00099 if ( ! v )
00100 KCC_ERR( i18n("Could not access view") );
00101
00102
00103 QStringList args(_cmd.split( QRegExp("\\s+"), QString::SkipEmptyParts)) ;
00104 QString cmd ( args.takeFirst() );
00105
00106
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
00173 else if ( cmd == "set-tab-width" ||
00174 cmd == "set-indent-width" ||
00175 cmd == "set-word-wrap-column" ||
00176 cmd == "goto" )
00177 {
00178
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
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
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
00286
00287
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 {
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
00329 static void exchangeAbbrevs(QString &str)
00330 {
00331
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
00357
00358
00359
00360
00361
00362
00363
00364
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
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
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
00429
00430
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
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
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
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 startcol = 0;
00519
00520 startline++;
00521 } while ( startline <= kview->selectionRange().end().line() );
00522 }
00523 else
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
00537
00538
00539 bool KateCommands::Character::exec (KTextEditor::View *view, const QString &_cmd, QString &)
00540 {
00541 QString cmd = _cmd;
00542
00543
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
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 {
00573 QChar c(number);
00574
00575 view->document()->insertText(view->cursorPosition(), QString(&c, 1));
00576 }
00577
00578 return true;
00579 }
00580
00581
00582
00583
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
00598
00599