kate Library API Documentation

katecmds.cpp

00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2003 - 2005 Anders Lund <anders@alweb.dk> 00003 Copyright (C) 2001-2004 Christoph Cullmann <cullmann@kde.org> 00004 Copyright (C) 2001 Charles Samuels <charles@kde.org> 00005 00006 This library is free software; you can redistribute it and/or 00007 modify it under the terms of the GNU Library General Public 00008 License version 2 as published by the Free Software Foundation. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00018 Boston, MA 02111-1307, USA. 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 //BEGIN CoreCommands 00039 // syncs a config flag in the document with a boolean value 00040 static void setDocFlag( KateDocumentConfig::ConfigFlags flag, bool enable, 00041 KateDocument *doc ) 00042 { 00043 doc->config()->setConfigFlags( flag, enable ); 00044 } 00045 00046 // this returns wheather the string s could be converted to 00047 // a bool value, one of on|off|1|0|true|false. the argument val is 00048 // set to the extracted value in case of success 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 // cast it hardcore, we know that it is really a kateview :) 00090 KateView *v = (KateView*) view; 00091 00092 if ( ! v ) 00093 KCC_ERR( i18n("Could not access view") ); 00094 00095 //create a list of args 00096 QStringList args( QStringList::split( QRegExp("\\s+"), _cmd ) ); 00097 QString cmd ( args.first() ); 00098 args.remove( args.first() ); 00099 00100 // ALL commands that takes no arguments. 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 // ALL commands that takes exactly one integer argument. 00160 else if ( cmd == "set-tab-width" || 00161 cmd == "set-indent-width" || 00162 cmd == "set-word-wrap-column" || 00163 cmd == "goto" ) 00164 { 00165 // find a integer value > 0 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 // ALL commands that takes 1 boolean argument. 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 // this is special, in that everything is set up -- space-indent is enabled, 00238 // and a indent-width is set if it is 0 (to tabwidth/2) 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 // unlikely.. 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 //END CoreCommands 00282 00283 //BEGIN SedReplace 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 { // isn't a slash 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 // exchange "\t" for the actual tab character, for example 00325 static void exchangeAbbrevs(QString &str) 00326 { 00327 // the format is (findreplace)*[nullzero] 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 // HANDLING "\n"s in PATTERN 00353 // * Create a list of patterns, splitting PATTERN on (unescaped) "\n" 00354 // * insert $s and ^s to match line ends/beginnings 00355 // * When matching patterhs after the first one, replace \N with the captured 00356 // text. 00357 // * If all patterns in the list match sequentiel lines, there is a match, so 00358 // * remove line/start to line + patterns.count()-1/patterns.last.length 00359 // * handle capatures by putting them in one list. 00360 // * the existing insertion is fine, including the line calculation. 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 // now set the backreferences in the replacement 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 // I need to match "\\" or "", but not "\" 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 // TODO if replace contains \n, 00425 // change the line number and 00426 // check for text that needs be searched behind the last inserted newline. 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 // if ( endcol >= startcol + len ) 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 // sanity check -- avoid infinite loops eg with %s,.*,,g ;) 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 /*if ( startcol )*/ startcol = 0; 00506 00507 startline++; 00508 } while ( (int)startline <= doc->selEndLine() ); 00509 } 00510 else // just this line 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 //END SedReplace 00523 00524 //BEGIN Character 00525 bool KateCommands::Character::exec (Kate::View *view, const QString &_cmd, QString &) 00526 { 00527 QString cmd = _cmd; 00528 00529 // hex, octal, base 9+1 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 // identify the base 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 { // do the unicode thing 00558 QChar c(number); 00559 view->insertText(QString(&c, 1)); 00560 } 00561 00562 return true; 00563 } 00564 //END Character 00565 00566 //BEGIN Date 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 //END Date 00580 00581 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE Logo
This file is part of the documentation for kate Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Apr 14 00:46:03 2005 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003