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