kate Library API Documentation

katecmds.cpp

00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2003 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 00028 #include <kdebug.h> 00029 #include <klocale.h> 00030 00031 #include <qregexp.h> 00032 00033 // syncs a config flag in the document with a boolean value 00034 static void setDocFlag( KateDocumentConfig::ConfigFlags flag, bool enable, 00035 KateDocument *doc ) 00036 { 00037 doc->config()->setConfigFlags( flag, enable ); 00038 } 00039 00040 // this returns wheather the string s could be converted to 00041 // a bool value, one of on|off|1|0|true|false. the argument val is 00042 // set to the extracted value in case of success 00043 static bool getBoolArg( QString s, bool *val ) 00044 { 00045 bool res( false ); 00046 s = s.lower(); 00047 res = (s == "on" || s == "1" || s == "true"); 00048 if ( res ) 00049 { 00050 *val = true; 00051 return true; 00052 } 00053 res = (s == "off" || s == "0" || s == "false"); 00054 if ( res ) 00055 { 00056 *val = false; 00057 return true; 00058 } 00059 return false; 00060 } 00061 00062 QStringList KateCommands::CoreCommands::cmds() 00063 { 00064 QStringList l; 00065 l << "indent" << "unindent" << "cleanindent" 00066 << "comment" << "uncomment" 00067 << "set-tab-width" << "set-replace-tabs" << "set-show-tabs" 00068 << "set-remove-trailing-space" 00069 << "set-indent-spaces" << "set-indent-width" << "set-indent-mode" << "set-auto-indent" 00070 << "set-line-numbers" << "set-folding-markers" << "set-icon-border" 00071 << "set-word-wrap" << "set-word-wrap-column" 00072 << "set-replace-tabs-save" << "set-remove-trailing-space-save"; 00073 return l; 00074 } 00075 00076 bool KateCommands::CoreCommands::exec(Kate::View *view, 00077 const QString &_cmd, 00078 QString &errorMsg) 00079 { 00080 #define KCC_ERR(s) { errorMsg=s; return false; } 00081 // cast it hardcore, we know that it is really a kateview :) 00082 KateView *v = (KateView*) view; 00083 00084 if ( ! v ) 00085 KCC_ERR( i18n("Could not access view") ); 00086 00087 //create a list of args 00088 QStringList args( QStringList::split( QRegExp("\\s+"), _cmd ) ); 00089 QString cmd ( args.first() ); 00090 args.remove( args.first() ); 00091 00092 // ALL commands that takes no arguments. 00093 if ( cmd == "indent" ) 00094 { 00095 v->indent(); 00096 return true; 00097 } 00098 else if ( cmd == "unindent" ) 00099 { 00100 v->unIndent(); 00101 return true; 00102 } 00103 else if ( cmd == "cleanindent" ) 00104 { 00105 v->cleanIndent(); 00106 return true; 00107 } 00108 else if ( cmd == "comment" ) 00109 { 00110 v->comment(); 00111 return true; 00112 } 00113 else if ( cmd == "uncomment" ) 00114 { 00115 v->uncomment(); 00116 return true; 00117 } 00118 else if ( cmd == "set-indent-mode" ) 00119 { 00120 bool ok(false); 00121 int val ( args.first().toInt( &ok ) ); 00122 if ( ok ) 00123 { 00124 if ( val < 0 ) 00125 KCC_ERR( i18n("Mode must be at least 0.") ); 00126 v->doc()->config()->setIndentationMode( val ); 00127 } 00128 else 00129 v->doc()->config()->setIndentationMode( KateAutoIndent::modeNumber( args.first() ) ); 00130 return true; 00131 } 00132 00133 // ALL commands that takes exactly one integer argument. 00134 else if ( cmd == "set-tab-width" || 00135 cmd == "set-indent-width" || 00136 cmd == "set-word-wrap-column" ) 00137 { 00138 // find a integer value > 0 00139 if ( ! args.count() ) 00140 KCC_ERR( i18n("Missing argument. Usage: %1 <value>").arg( cmd ) ); 00141 bool ok; 00142 int val ( args.first().toInt( &ok ) ); 00143 if ( !ok ) 00144 KCC_ERR( i18n("Failed to convert argument '%1' to integer.") 00145 .arg( args.first() ) ); 00146 00147 if ( cmd == "set-tab-width" ) 00148 { 00149 if ( val < 1 ) 00150 KCC_ERR( i18n("Width must be at least 1.") ); 00151 v->setTabWidth( val ); 00152 } 00153 else if ( cmd == "set-indent-width" ) 00154 { 00155 if ( val < 1 ) 00156 KCC_ERR( i18n("Width must be at least 1.") ); 00157 v->doc()->config()->setIndentationWidth( val ); 00158 } 00159 else if ( cmd == "set-word-wrap-column" ) 00160 { 00161 if ( val < 2 ) 00162 KCC_ERR( i18n("Column must be at least 1.") ); 00163 v->doc()->setWordWrapAt( val ); 00164 } 00165 return true; 00166 } 00167 00168 // ALL commands that takes 1 boolean argument. 00169 else if ( cmd == "set-icon-border" || 00170 cmd == "set-folding-markers" || 00171 cmd == "set-line-numbers" || 00172 cmd == "set-replace-tabs" || 00173 cmd == "set-remove-trailing-space" || 00174 cmd == "set-show-tabs" || 00175 cmd == "set-indent-spaces" || 00176 cmd == "set-auto-indent" || 00177 cmd == "set-word-wrap" || 00178 cmd == "set-replace-tabs-save" || 00179 cmd == "set-remove-trailing-space-save" ) 00180 { 00181 if ( ! args.count() ) 00182 KCC_ERR( i18n("Usage: %1 on|off|1|0|true|false").arg( cmd ) ); 00183 bool enable; 00184 if ( getBoolArg( args.first(), &enable ) ) 00185 { 00186 if ( cmd == "set-icon-border" ) 00187 v->setIconBorder( enable ); 00188 else if (cmd == "set-folding-markers") 00189 v->setFoldingMarkersOn( enable ); 00190 else if ( cmd == "set-line-numbers" ) 00191 v->setLineNumbersOn( enable ); 00192 else if ( cmd == "set-replace-tabs" ) 00193 setDocFlag( KateDocumentConfig::cfReplaceTabsDyn, enable, v->doc() ); 00194 else if ( cmd == "set-remove-trailing-space" ) 00195 setDocFlag( KateDocumentConfig::cfRemoveTrailingDyn, enable, v->doc() ); 00196 else if ( cmd == "set-show-tabs" ) 00197 setDocFlag( KateDocumentConfig::cfShowTabs, enable, v->doc() ); 00198 else if ( cmd == "set-indent-spaces" ) 00199 setDocFlag( KateDocumentConfig::cfSpaceIndent, enable, v->doc() ); 00200 else if ( cmd == "set-auto-indent" ) 00201 setDocFlag( KateDocumentConfig::cfAutoIndent, enable, v->doc() ); 00202 else if ( cmd == "set-word-wrap" ) 00203 v->doc()->setWordWrap( enable ); 00204 else if ( cmd == "set-replace-tabs-save" ) 00205 setDocFlag( KateDocumentConfig::cfReplaceTabs, enable, v->doc() ); 00206 else if ( cmd == "set-remove-trailing-space-save" ) 00207 setDocFlag( KateDocumentConfig::cfRemoveSpaces, enable, v->doc() ); 00208 00209 return true; 00210 } 00211 else 00212 KCC_ERR( i18n("Bad argument '%1'. Usage: %2 on|off|1|0|true|false") 00213 .arg( args.first() ).arg( cmd ) ); 00214 } 00215 00216 // unlikely.. 00217 KCC_ERR( i18n("Unknown command '%1'").arg(cmd) ); 00218 } 00219 00220 static void replace(QString &s, const QString &needle, const QString &with) 00221 { 00222 int pos=0; 00223 while (1) 00224 { 00225 pos=s.find(needle, pos); 00226 if (pos==-1) break; 00227 s.replace(pos, needle.length(), with); 00228 pos+=with.length(); 00229 } 00230 00231 } 00232 00233 static int backslashString(const QString &haystack, const QString &needle, int index) 00234 { 00235 int len=haystack.length(); 00236 int searchlen=needle.length(); 00237 bool evenCount=true; 00238 while (index<len) 00239 { 00240 if (haystack[index]=='\\') 00241 { 00242 evenCount=!evenCount; 00243 } 00244 else 00245 { // isn't a slash 00246 if (!evenCount) 00247 { 00248 if (haystack.mid(index, searchlen)==needle) 00249 return index-1; 00250 } 00251 evenCount=true; 00252 } 00253 index++; 00254 00255 } 00256 00257 return -1; 00258 } 00259 00260 // exchange "\t" for the actual tab character, for example 00261 static void exchangeAbbrevs(QString &str) 00262 { 00263 // the format is (findreplace)*[nullzero] 00264 const char *magic="a\x07t\t"; 00265 00266 while (*magic) 00267 { 00268 int index=0; 00269 char replace=magic[1]; 00270 while ((index=backslashString(str, QChar(*magic), index))!=-1) 00271 { 00272 str.replace(index, 2, QChar(replace)); 00273 index++; 00274 } 00275 magic++; 00276 magic++; 00277 } 00278 } 00279 00280 QString KateCommands::SedReplace::sedMagic(QString textLine, const QString &find, const QString &repOld, bool noCase, bool repeat) 00281 { 00282 00283 QRegExp matcher(find, noCase); 00284 00285 int start=0; 00286 while (start!=-1) 00287 { 00288 start=matcher.search(textLine, start); 00289 00290 if (start==-1) break; 00291 00292 int length=matcher.matchedLength(); 00293 00294 00295 QString rep=repOld; 00296 00297 // now set the backreferences in the replacement 00298 QStringList backrefs=matcher.capturedTexts(); 00299 int refnum=1; 00300 00301 QStringList::Iterator i = backrefs.begin(); 00302 ++i; 00303 00304 for (; i!=backrefs.end(); ++i) 00305 { 00306 // I need to match "\\" or "", but not "\" 00307 QString number=QString::number(refnum); 00308 00309 int index=0; 00310 while (index!=-1) 00311 { 00312 index=backslashString(rep, number, index); 00313 if (index>=0) 00314 { 00315 rep.replace(index, 2, *i); 00316 index+=(*i).length(); 00317 } 00318 } 00319 00320 refnum++; 00321 } 00322 00323 replace(rep, "\\\\", "\\"); 00324 replace(rep, "\\/", "/"); 00325 00326 textLine.replace(start, length, rep); 00327 if (!repeat) break; 00328 start+=rep.length(); 00329 } 00330 00331 00332 return textLine; 00333 } 00334 00335 static void setLineText(Kate::View *view, int line, const QString &text) 00336 { 00337 if (view->getDoc()->insertLine(line, text)) 00338 view->getDoc()->removeLine(line+1); 00339 } 00340 00341 bool KateCommands::SedReplace::exec (Kate::View *view, const QString &cmd, QString &) 00342 { 00343 kdDebug(13010)<<"SedReplace::execCmd()"<<endl; 00344 00345 if (QRegExp("[$%]?s /.+/.*/[ig]*").search(cmd, 0)==-1) 00346 return false; 00347 00348 bool fullFile=cmd[0]=='%'; 00349 bool noCase=cmd[cmd.length()-1]=='i' || cmd[cmd.length()-2]=='i'; 00350 bool repeat=cmd[cmd.length()-1]=='g' || cmd[cmd.length()-2]=='g'; 00351 bool onlySelect=cmd[0]=='$'; 00352 00353 00354 QRegExp splitter("^[$%]?s /((?:[^\\\\/]|\\\\.)*)/((?:[^\\\\/]|\\\\.)*)/[ig]*$"); 00355 if (splitter.search(cmd)<0) return false; 00356 00357 QString find=splitter.cap(1); 00358 kdDebug(13010)<< "SedReplace: find=" << find.latin1() <<endl; 00359 00360 QString replace=splitter.cap(2); 00361 exchangeAbbrevs(replace); 00362 kdDebug(13010)<< "SedReplace: replace=" << replace.latin1() <<endl; 00363 00364 00365 if (fullFile) 00366 { 00367 int numLines=view->getDoc()->numLines(); 00368 for (int line=0; line < numLines; line++) 00369 { 00370 QString text=view->getDoc()->textLine(line); 00371 text=sedMagic(text, find, replace, noCase, repeat); 00372 setLineText(view, line, text); 00373 } 00374 } 00375 else if (onlySelect) 00376 { 00377 // Not implemented 00378 } 00379 else 00380 { // just this line 00381 QString textLine=view->currentTextLine(); 00382 int line=view->cursorLine(); 00383 textLine=sedMagic(textLine, find, replace, noCase, repeat); 00384 setLineText(view, line, textLine); 00385 } 00386 return true; 00387 } 00388 00389 bool KateCommands::Character::exec (Kate::View *view, const QString &_cmd, QString &) 00390 { 00391 QString cmd = _cmd; 00392 00393 // hex, octal, base 9+1 00394 QRegExp num("^char *(0?x[0-9A-Fa-f]{1,4}|0[0-7]{1,6}|[0-9]{1,3})$"); 00395 if (num.search(cmd)==-1) return false; 00396 00397 cmd=num.cap(1); 00398 00399 // identify the base 00400 00401 unsigned short int number=0; 00402 int base=10; 00403 if (cmd[0]=='x' || cmd.left(2)=="0x") 00404 { 00405 cmd.replace(QRegExp("^0?x"), ""); 00406 base=16; 00407 } 00408 else if (cmd[0]=='0') 00409 base=8; 00410 bool ok; 00411 number=cmd.toUShort(&ok, base); 00412 if (!ok || number==0) return false; 00413 if (number<=255) 00414 { 00415 char buf[2]; 00416 buf[0]=(char)number; 00417 buf[1]=0; 00418 view->insertText(QString(buf)); 00419 } 00420 else 00421 { // do the unicode thing 00422 QChar c(number); 00423 view->insertText(QString(&c, 1)); 00424 } 00425 00426 return true; 00427 } 00428 00429 bool KateCommands::Goto::exec (Kate::View *view, const QString &cmd, QString &) 00430 { 00431 if (cmd.left(4) != "goto") 00432 return false; 00433 00434 QStringList args( QStringList::split( QRegExp("\\s+"), cmd ) ); 00435 args.remove( args.first() ); 00436 00437 view->gotoLineNumber (args[0].toInt()); 00438 00439 return true; 00440 } 00441 00442 bool KateCommands::Date::exec (Kate::View *view, const QString &cmd, QString &) 00443 { 00444 if (cmd.left(4) != "date") 00445 return false; 00446 00447 if (QDateTime::currentDateTime().toString(cmd.mid(5, cmd.length()-5)).length() > 0) 00448 view->insertText(QDateTime::currentDateTime().toString(cmd.mid(5, cmd.length()-5))); 00449 else 00450 view->insertText(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")); 00451 00452 return true; 00453 } 00454 00455 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE Logo
This file is part of the documentation for kate Library Version 3.3.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sun Oct 17 11:35:09 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003