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
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
00096 KateView *v = (KateView*) view;
00097
00098 if ( ! v )
00099 KCC_ERR( i18n("Could not access view") );
00100
00101
00102 QStringList args(_cmd.split( QRegExp("\\s+"), QString::SkipEmptyParts)) ;
00103 QString cmd ( args.takeFirst() );
00104
00105
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
00167 else if ( cmd == "set-tab-width" ||
00168 cmd == "set-indent-width" ||
00169 cmd == "set-word-wrap-column" ||
00170 cmd == "goto" )
00171 {
00172
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
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
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
00279
00280
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 {
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
00322 static void exchangeAbbrevs(QString &str)
00323 {
00324
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
00350
00351
00352
00353
00354
00355
00356
00357
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
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
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
00422
00423
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
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
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
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 startcol = 0;
00512
00513 startline++;
00514 } while ( startline <= kview->selectionRange().end().line() );
00515 }
00516 else
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
00530
00531
00532 bool KateCommands::Character::exec (KTextEditor::View *view, const QString &_cmd, QString &)
00533 {
00534 QString cmd = _cmd;
00535
00536
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
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 {
00566 QChar c(number);
00567
00568 view->document()->insertText(view->cursorPosition(), QString(&c, 1));
00569 }
00570
00571 return true;
00572 }
00573
00574
00575
00576
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
00591
00592