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