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 "katefactory.h"
00029 #include "katejscript.h"
00030 #include "katerenderer.h"
00031
00032 #include "../interfaces/katecmd.h"
00033
00034 #include <kdebug.h>
00035 #include <klocale.h>
00036 #include <kurl.h>
00037 #include <kshellcompletion.h>
00038
00039 #include <qregexp.h>
00040
00041
00042
00043
00044 static void setDocFlag( KateDocumentConfig::ConfigFlags flag, bool enable,
00045 KateDocument *doc )
00046 {
00047 doc->config()->setConfigFlags( flag, enable );
00048 }
00049
00050
00051
00052
00053 static bool getBoolArg( QString s, bool *val )
00054 {
00055 bool res( false );
00056 s = s.lower();
00057 res = (s == "on" || s == "1" || s == "true");
00058 if ( res )
00059 {
00060 *val = true;
00061 return true;
00062 }
00063 res = (s == "off" || s == "0" || s == "false");
00064 if ( res )
00065 {
00066 *val = false;
00067 return true;
00068 }
00069 return false;
00070 }
00071
00072 QStringList KateCommands::CoreCommands::cmds()
00073 {
00074 QStringList l;
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-spaces" << "set-indent-width" << "set-mixed-indent"
00080 << "set-indent-mode" << "set-auto-indent"
00081 << "set-line-numbers" << "set-folding-markers" << "set-icon-border"
00082 << "set-word-wrap" << "set-word-wrap-column"
00083 << "set-replace-tabs-save" << "set-remove-trailing-space-save"
00084 << "set-highlight" << "run-myself" << "set-show-indent";
00085 return l;
00086 }
00087
00088 bool KateCommands::CoreCommands::exec(Kate::View *view,
00089 const QString &_cmd,
00090 QString &errorMsg)
00091 {
00092 #define KCC_ERR(s) { errorMsg=s; return false; }
00093
00094 KateView *v = (KateView*) view;
00095
00096 if ( ! v )
00097 KCC_ERR( i18n("Could not access view") );
00098
00099
00100 QStringList args( QStringList::split( QRegExp("\\s+"), _cmd ) );
00101 QString cmd ( args.first() );
00102 args.remove( args.first() );
00103
00104
00105 if ( cmd == "indent" )
00106 {
00107 v->indent();
00108 return true;
00109 }
00110 else if ( cmd == "run-myself" )
00111 {
00112 #ifndef Q_WS_WIN //todo
00113 return KateFactory::self()->jscript()->execute(v, v->doc()->text(), errorMsg);
00114 #else
00115 return 0;
00116 #endif
00117 }
00118 else if ( cmd == "unindent" )
00119 {
00120 v->unIndent();
00121 return true;
00122 }
00123 else if ( cmd == "cleanindent" )
00124 {
00125 v->cleanIndent();
00126 return true;
00127 }
00128 else if ( cmd == "comment" )
00129 {
00130 v->comment();
00131 return true;
00132 }
00133 else if ( cmd == "uncomment" )
00134 {
00135 v->uncomment();
00136 return true;
00137 }
00138 else if ( cmd == "kill-line" )
00139 {
00140 v->killLine();
00141 return true;
00142 }
00143 else if ( cmd == "set-indent-mode" )
00144 {
00145 bool ok(false);
00146 int val ( args.first().toInt( &ok ) );
00147 if ( ok )
00148 {
00149 if ( val < 0 )
00150 KCC_ERR( i18n("Mode must be at least 0.") );
00151 v->doc()->config()->setIndentationMode( val );
00152 }
00153 else
00154 v->doc()->config()->setIndentationMode( KateAutoIndent::modeNumber( args.first() ) );
00155 return true;
00156 }
00157 else if ( cmd == "set-highlight" )
00158 {
00159 QString val = _cmd.section( ' ', 1 ).lower();
00160 for ( uint i=0; i < v->doc()->hlModeCount(); i++ )
00161 {
00162 if ( v->doc()->hlModeName( i ).lower() == val )
00163 {
00164 v->doc()->setHlMode( i );
00165 return true;
00166 }
00167 }
00168 KCC_ERR( i18n("No such highlight '%1'").arg( args.first() ) );
00169 }
00170
00171
00172 else if ( cmd == "set-tab-width" ||
00173 cmd == "set-indent-width" ||
00174 cmd == "set-word-wrap-column" ||
00175 cmd == "goto" )
00176 {
00177
00178 if ( ! args.count() )
00179 KCC_ERR( i18n("Missing argument. Usage: %1 <value>").arg( cmd ) );
00180 bool ok;
00181 int val ( args.first().toInt( &ok ) );
00182 if ( !ok )
00183 KCC_ERR( i18n("Failed to convert argument '%1' to integer.")
00184 .arg( args.first() ) );
00185
00186 if ( cmd == "set-tab-width" )
00187 {
00188 if ( val < 1 )
00189 KCC_ERR( i18n("Width must be at least 1.") );
00190 v->setTabWidth( val );
00191 }
00192 else if ( cmd == "set-indent-width" )
00193 {
00194 if ( val < 1 )
00195 KCC_ERR( i18n("Width must be at least 1.") );
00196 v->doc()->config()->setIndentationWidth( val );
00197 }
00198 else if ( cmd == "set-word-wrap-column" )
00199 {
00200 if ( val < 2 )
00201 KCC_ERR( i18n("Column must be at least 1.") );
00202 v->doc()->setWordWrapAt( val );
00203 }
00204 else if ( cmd == "goto" )
00205 {
00206 if ( val < 1 )
00207 KCC_ERR( i18n("Line must be at least 1") );
00208 if ( (uint)val > v->doc()->numLines() )
00209 KCC_ERR( i18n("There is not that many lines in this document") );
00210 v->gotoLineNumber( val - 1 );
00211 }
00212 return true;
00213 }
00214
00215
00216 else if ( cmd == "set-icon-border" ||
00217 cmd == "set-folding-markers" ||
00218 cmd == "set-line-numbers" ||
00219 cmd == "set-replace-tabs" ||
00220 cmd == "set-remove-trailing-space" ||
00221 cmd == "set-show-tabs" ||
00222 cmd == "set-indent-spaces" ||
00223 cmd == "set-mixed-indent" ||
00224 cmd == "set-word-wrap" ||
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").arg( cmd ) );
00231 bool enable;
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-indent-spaces" )
00249 setDocFlag( KateDocumentConfig::cfSpaceIndent, enable, v->doc() );
00250 else if ( cmd == "set-mixed-indent" )
00251 {
00252
00253
00254 setDocFlag( KateDocumentConfig::cfMixedIndent, enable, v->doc() );
00255 if ( enable )
00256 {
00257 setDocFlag( KateDocumentConfig::cfSpaceIndent, enable, v->doc() );
00258 if ( ! v->doc()->config()->indentationWidth() )
00259 v->doc()->config()->setIndentationWidth( v->tabWidth()/2 );
00260 }
00261 }
00262 else if ( cmd == "set-word-wrap" )
00263 v->doc()->setWordWrap( enable );
00264 else if ( cmd == "set-remove-trailing-space-save" )
00265 setDocFlag( KateDocumentConfig::cfRemoveSpaces, enable, v->doc() );
00266
00267 return true;
00268 }
00269 else
00270 KCC_ERR( i18n("Bad argument '%1'. Usage: %2 on|off|1|0|true|false")
00271 .arg( args.first() ).arg( cmd ) );
00272 }
00273
00274
00275 KCC_ERR( i18n("Unknown command '%1'").arg(cmd) );
00276 }
00277
00278 KCompletion *KateCommands::CoreCommands::completionObject( const QString &cmd, Kate::View *view )
00279 {
00280 if ( cmd == "set-highlight" )
00281 {
00282 KateView *v = (KateView*)view;
00283 QStringList l;
00284 for ( uint i = 0; i < v->doc()->hlModeCount(); i++ )
00285 l << v->doc()->hlModeName( i );
00286
00287 KateCmdShellCompletion *co = new KateCmdShellCompletion();
00288 co->setItems( l );
00289 co->setIgnoreCase( true );
00290 return co;
00291 }
00292 return 0L;
00293 }
00294
00295
00296
00297 static void replace(QString &s, const QString &needle, const QString &with)
00298 {
00299 int pos=0;
00300 while (1)
00301 {
00302 pos=s.find(needle, pos);
00303 if (pos==-1) break;
00304 s.replace(pos, needle.length(), with);
00305 pos+=with.length();
00306 }
00307
00308 }
00309
00310 static int backslashString(const QString &haystack, const QString &needle, int index)
00311 {
00312 int len=haystack.length();
00313 int searchlen=needle.length();
00314 bool evenCount=true;
00315 while (index<len)
00316 {
00317 if (haystack[index]=='\\')
00318 {
00319 evenCount=!evenCount;
00320 }
00321 else
00322 {
00323 if (!evenCount)
00324 {
00325 if (haystack.mid(index, searchlen)==needle)
00326 return index-1;
00327 }
00328 evenCount=true;
00329 }
00330 index++;
00331
00332 }
00333
00334 return -1;
00335 }
00336
00337
00338 static void exchangeAbbrevs(QString &str)
00339 {
00340
00341 const char *magic="a\x07t\tn\n";
00342
00343 while (*magic)
00344 {
00345 int index=0;
00346 char replace=magic[1];
00347 while ((index=backslashString(str, QChar(*magic), index))!=-1)
00348 {
00349 str.replace(index, 2, QChar(replace));
00350 index++;
00351 }
00352 magic++;
00353 magic++;
00354 }
00355 }
00356
00357 int KateCommands::SedReplace::sedMagic( KateDocument *doc, int &line,
00358 const QString &find, const QString &repOld, const QString &delim,
00359 bool noCase, bool repeat,
00360 uint startcol, int endcol )
00361 {
00362 KateTextLine *ln = doc->kateTextLine( line );
00363 if ( ! ln || ! ln->length() ) return 0;
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375 QStringList patterns = QStringList::split( QRegExp("(^\\\\n|(?![^\\\\])\\\\n)"), find, true );
00376
00377 if ( patterns.count() > 1 )
00378 {
00379 for ( uint i = 0; i < patterns.count(); i++ )
00380 {
00381 if ( i < patterns.count() - 1 )
00382 patterns[i].append("$");
00383 if ( i )
00384 patterns[i].prepend("^");
00385
00386 kdDebug(13025)<<"patterns["<<i<<"] ="<<patterns[i]<<endl;
00387 }
00388 }
00389
00390 QRegExp matcher(patterns[0], noCase);
00391
00392 uint len;
00393 int matches = 0;
00394
00395 while ( ln->searchText( startcol, matcher, &startcol, &len ) )
00396 {
00397
00398 if ( endcol >= 0 && startcol + len > (uint)endcol )
00399 break;
00400
00401 matches++;
00402
00403
00404 QString rep=repOld;
00405
00406
00407 QStringList backrefs=matcher.capturedTexts();
00408 int refnum=1;
00409
00410 QStringList::Iterator i = backrefs.begin();
00411 ++i;
00412
00413 for (; i!=backrefs.end(); ++i)
00414 {
00415
00416 QString number=QString::number(refnum);
00417
00418 int index=0;
00419 while (index!=-1)
00420 {
00421 index=backslashString(rep, number, index);
00422 if (index>=0)
00423 {
00424 rep.replace(index, 2, *i);
00425 index+=(*i).length();
00426 }
00427 }
00428
00429 refnum++;
00430 }
00431
00432 replace(rep, "\\\\", "\\");
00433 replace(rep, "\\" + delim, delim);
00434
00435 doc->removeText( line, startcol, line, startcol + len );
00436 doc->insertText( line, startcol, rep );
00437
00438
00439
00440
00441 int lns = rep.contains('\n');
00442 if ( lns )
00443 {
00444 line += lns;
00445
00446 if ( doc->lineLength( line ) > 0 && ( endcol < 0 || (uint)endcol >= startcol + len ) )
00447 {
00448
00449 endcol -= (startcol + len);
00450 uint sc = rep.length() - rep.findRev('\n') - 1;
00451 matches += sedMagic( doc, line, find, repOld, delim, noCase, repeat, sc, endcol );
00452 }
00453 }
00454
00455 if (!repeat) break;
00456 startcol+=rep.length();
00457
00458
00459 uint ll = ln->length();
00460 if ( ! ll || startcol > ll )
00461 break;
00462 }
00463
00464 return matches;
00465 }
00466
00467 bool KateCommands::SedReplace::exec (Kate::View *view, const QString &cmd, QString &msg)
00468 {
00469 kdDebug(13025)<<"SedReplace::execCmd( "<<cmd<<" )"<<endl;
00470
00471 QRegExp delim("^[$%]?s\\s*([^\\w\\s])");
00472 if ( delim.search( cmd ) < 0 ) return false;
00473
00474 bool fullFile=cmd[0]=='%';
00475 bool noCase=cmd[cmd.length()-1]=='i' || cmd[cmd.length()-2]=='i';
00476 bool repeat=cmd[cmd.length()-1]=='g' || cmd[cmd.length()-2]=='g';
00477 bool onlySelect=cmd[0]=='$';
00478
00479 QString d = delim.cap(1);
00480 kdDebug(13025)<<"SedReplace: delimiter is '"<<d<<"'"<<endl;
00481
00482 QRegExp splitter( QString("^[$%]?s\\s*") + d + "((?:[^\\\\\\" + d + "]|\\\\.)*)\\" + d +"((?:[^\\\\\\" + d + "]|\\\\.)*)\\" + d + "[ig]{0,2}$" );
00483 if (splitter.search(cmd)<0) return false;
00484
00485 QString find=splitter.cap(1);
00486 kdDebug(13025)<< "SedReplace: find=" << find.latin1() <<endl;
00487
00488 QString replace=splitter.cap(2);
00489 exchangeAbbrevs(replace);
00490 kdDebug(13025)<< "SedReplace: replace=" << replace.latin1() <<endl;
00491
00492 if ( find.contains("\\n") )
00493 {
00494 msg = i18n("Sorry, but Kate is not able to replace newlines, yet");
00495 return false;
00496 }
00497
00498 KateDocument *doc = ((KateView*)view)->doc();
00499 if ( ! doc ) return false;
00500
00501 doc->editStart();
00502
00503 int res = 0;
00504
00505 if (fullFile)
00506 {
00507 uint numLines=doc->numLines();
00508 for (int line=0; (uint)line < numLines; line++)
00509 {
00510 res += sedMagic( doc, line, find, replace, d, !noCase, repeat );
00511 if ( ! repeat && res ) break;
00512 }
00513 }
00514 else if (onlySelect)
00515 {
00516 int startline = doc->selStartLine();
00517 uint startcol = doc->selStartCol();
00518 int endcol = -1;
00519 do {
00520 if ( startline == doc->selEndLine() )
00521 endcol = doc->selEndCol();
00522
00523 res += sedMagic( doc, startline, find, replace, d, !noCase, repeat, startcol, endcol );
00524
00525 startcol = 0;
00526
00527 startline++;
00528 } while ( (int)startline <= doc->selEndLine() );
00529 }
00530 else
00531 {
00532 int line=view->cursorLine();
00533 res += sedMagic(doc, line, find, replace, d, !noCase, repeat);
00534 }
00535
00536 msg = i18n("1 replacement done", "%n replacements done",res );
00537
00538 doc->editEnd();
00539
00540 return true;
00541 }
00542
00543
00544
00545 bool KateCommands::Character::exec (Kate::View *view, const QString &_cmd, QString &)
00546 {
00547 QString cmd = _cmd;
00548
00549
00550 QRegExp num("^char *(0?x[0-9A-Fa-f]{1,4}|0[0-7]{1,6}|[0-9]{1,3})$");
00551 if (num.search(cmd)==-1) return false;
00552
00553 cmd=num.cap(1);
00554
00555
00556
00557 unsigned short int number=0;
00558 int base=10;
00559 if (cmd[0]=='x' || cmd.left(2)=="0x")
00560 {
00561 cmd.replace(QRegExp("^0?x"), "");
00562 base=16;
00563 }
00564 else if (cmd[0]=='0')
00565 base=8;
00566 bool ok;
00567 number=cmd.toUShort(&ok, base);
00568 if (!ok || number==0) return false;
00569 if (number<=255)
00570 {
00571 char buf[2];
00572 buf[0]=(char)number;
00573 buf[1]=0;
00574 view->insertText(QString(buf));
00575 }
00576 else
00577 {
00578 QChar c(number);
00579 view->insertText(QString(&c, 1));
00580 }
00581
00582 return true;
00583 }
00584
00585
00586
00587 bool KateCommands::Date::exec (Kate::View *view, const QString &cmd, QString &)
00588 {
00589 if (cmd.left(4) != "date")
00590 return false;
00591
00592 if (QDateTime::currentDateTime().toString(cmd.mid(5, cmd.length()-5)).length() > 0)
00593 view->insertText(QDateTime::currentDateTime().toString(cmd.mid(5, cmd.length()-5)));
00594 else
00595 view->insertText(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));
00596
00597 return true;
00598 }
00599
00600
00601