kate Library API Documentation

kateautoindent.cpp

00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2003 Jesse Yurkovich <yurkjes@iit.edu> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License version 2 as published by the Free Software Foundation. 00007 00008 This library is distributed in the hope that it will be useful, 00009 but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00011 Library General Public License for more details. 00012 00013 You should have received a copy of the GNU Library General Public License 00014 along with this library; see the file COPYING.LIB. If not, write to 00015 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00016 Boston, MA 02111-1307, USA. 00017 */ 00018 00019 #include "kateautoindent.h" 00020 00021 #include "kateconfig.h" 00022 #include "katehighlight.h" 00023 #include "kateview.h" 00024 00025 #include <klocale.h> 00026 #include <kdebug.h> 00027 00028 // BEGIN KateAutoIndent 00029 00030 KateAutoIndent *KateAutoIndent::createIndenter (KateDocument *doc, uint mode) 00031 { 00032 if (mode == KateDocumentConfig::imCStyle) 00033 return new KateCSmartIndent (doc); 00034 else if (mode == KateDocumentConfig::imPythonStyle) 00035 return new KatePythonIndent (doc); 00036 00037 return new KateAutoIndent (doc); 00038 } 00039 00040 QStringList KateAutoIndent::listModes () 00041 { 00042 QStringList l; 00043 00044 l << modeDescription(KateDocumentConfig::imNormal); 00045 l << modeDescription(KateDocumentConfig::imCStyle); 00046 l << modeDescription(KateDocumentConfig::imPythonStyle); 00047 00048 return l; 00049 } 00050 00051 QString KateAutoIndent::modeName (uint mode) 00052 { 00053 if (mode == KateDocumentConfig::imCStyle) 00054 return QString ("cstyle"); 00055 else if (mode == KateDocumentConfig::imPythonStyle) 00056 return QString ("python"); 00057 00058 return QString ("normal"); 00059 } 00060 00061 QString KateAutoIndent::modeDescription (uint mode) 00062 { 00063 if (mode == KateDocumentConfig::imCStyle) 00064 return i18n ("C Style"); 00065 else if (mode == KateDocumentConfig::imPythonStyle) 00066 return i18n ("Python Style"); 00067 00068 return i18n ("Normal"); 00069 } 00070 00071 uint KateAutoIndent::modeNumber (const QString &name) 00072 { 00073 if (modeName(KateDocumentConfig::imCStyle) == name) 00074 return KateDocumentConfig::imCStyle; 00075 else if (modeName(KateDocumentConfig::imPythonStyle) == name) 00076 return KateDocumentConfig::imPythonStyle; 00077 00078 return KateDocumentConfig::imNormal; 00079 } 00080 00081 KateAutoIndent::KateAutoIndent (KateDocument *_doc) 00082 : doc(_doc) 00083 { 00084 } 00085 KateAutoIndent::~KateAutoIndent () 00086 { 00087 } 00088 00089 void KateAutoIndent::updateConfig () 00090 { 00091 KateDocumentConfig *config = doc->config(); 00092 00093 useSpaces = config->configFlags() & KateDocument::cfSpaceIndent || config->configFlags() & KateDocumentConfig::cfReplaceTabsDyn; 00094 keepProfile = config->configFlags() & KateDocument::cfKeepIndentProfile; 00095 tabWidth = config->tabWidth(); 00096 indentWidth = (config->configFlags() & KateDocument::cfSpaceIndent) ? config->indentationWidth() : tabWidth; 00097 00098 commentAttrib = 255; 00099 doxyCommentAttrib = 255; 00100 regionAttrib = 255; 00101 symbolAttrib = 255; 00102 alertAttrib = 255; 00103 tagAttrib = 255; 00104 wordAttrib = 255; 00105 00106 KateHlItemDataList items; 00107 doc->highlight()->getKateHlItemDataListCopy (0, items); 00108 00109 for (uint i=0; i<items.count(); i++) 00110 { 00111 QString name = items.at(i)->name; 00112 if (name.find("Comment") != -1 && commentAttrib == 255) 00113 { 00114 commentAttrib = i; 00115 } 00116 else if (name.find("Region Marker") != -1 && regionAttrib == 255) 00117 { 00118 regionAttrib = i; 00119 } 00120 else if (name.find("Symbol") != -1 && symbolAttrib == 255) 00121 { 00122 symbolAttrib = i; 00123 } 00124 else if (name.find("Alert") != -1) 00125 { 00126 alertAttrib = i; 00127 } 00128 else if (name.find("Comment") != -1 && commentAttrib != 255 && doxyCommentAttrib == 255) 00129 { 00130 doxyCommentAttrib = i; 00131 } 00132 else if (name.find("Tags") != -1 && tagAttrib == 255) 00133 { 00134 tagAttrib = i; 00135 } 00136 else if (name.find("Word") != -1 && wordAttrib == 255) 00137 { 00138 wordAttrib = i; 00139 } 00140 } 00141 } 00142 00143 bool KateAutoIndent::isBalanced (KateDocCursor &begin, const KateDocCursor &end, QChar open, QChar close, uint &pos) const 00144 { 00145 int parenOpen = 0; 00146 bool atLeastOne = false; 00147 bool getNext = false; 00148 00149 pos = doc->plainKateTextLine(begin.line())->firstChar(); 00150 00151 // Iterate one-by-one finding opening and closing chars 00152 // Assume that open and close are 'Symbol' characters 00153 while (begin < end) 00154 { 00155 QChar c = begin.currentChar(); 00156 if (begin.currentAttrib() == symbolAttrib) 00157 { 00158 if (c == open) 00159 { 00160 if (!atLeastOne) 00161 { 00162 atLeastOne = true; 00163 getNext = true; 00164 pos = measureIndent(begin) + 1; 00165 } 00166 parenOpen++; 00167 } 00168 else if (c == close) 00169 { 00170 parenOpen--; 00171 } 00172 } 00173 else if (getNext && !c.isSpace()) 00174 { 00175 getNext = false; 00176 pos = measureIndent(begin); 00177 } 00178 00179 if (atLeastOne && parenOpen <= 0) 00180 return true; 00181 00182 begin.moveForward(1); 00183 } 00184 00185 return (atLeastOne) ? false : true; 00186 } 00187 00188 bool KateAutoIndent::skipBlanks (KateDocCursor &cur, KateDocCursor &max, bool newline) const 00189 { 00190 int curLine = cur.line(); 00191 if (newline) 00192 cur.moveForward(1); 00193 00194 if (cur >= max) 00195 return false; 00196 00197 do 00198 { 00199 uchar attrib = cur.currentAttrib(); 00200 if (attrib != commentAttrib && attrib != doxyCommentAttrib && attrib != regionAttrib && attrib != alertAttrib && attrib != tagAttrib && attrib != wordAttrib) 00201 { 00202 QChar c = cur.currentChar(); 00203 if (!c.isNull() && !c.isSpace()) 00204 break; 00205 } 00206 00207 // Make sure col is 0 if we spill into next line i.e. count the '\n' as a character 00208 if (!cur.moveForward(1)) 00209 break; 00210 if (curLine != cur.line()) 00211 { 00212 if (!newline) 00213 break; 00214 curLine = cur.line(); 00215 cur.setCol(0); 00216 } 00217 } while (cur < max); 00218 00219 if (cur > max) 00220 cur = max; 00221 return true; 00222 } 00223 00224 uint KateAutoIndent::measureIndent (KateDocCursor &cur) const 00225 { 00226 if (useSpaces && !keepProfile) 00227 return cur.col(); 00228 00229 return doc->plainKateTextLine(cur.line())->cursorX(cur.col(), tabWidth); 00230 } 00231 00232 QString KateAutoIndent::tabString(uint pos) const 00233 { 00234 QString s; 00235 pos = QMIN (pos, 80); // sanity check for large values of pos 00236 00237 if (!useSpaces) 00238 { 00239 while (pos >= tabWidth) 00240 { 00241 s += '\t'; 00242 pos -= tabWidth; 00243 } 00244 } 00245 while (pos > 0) 00246 { 00247 s += ' '; 00248 pos--; 00249 } 00250 return s; 00251 } 00252 00253 void KateAutoIndent::processNewline (KateDocCursor &begin, bool /*needContinue*/) 00254 { 00255 int line = begin.line() - 1; 00256 int pos = begin.col(); 00257 00258 while ((line > 0) && (pos < 0)) // search a not empty text line 00259 pos = doc->plainKateTextLine(--line)->firstChar(); 00260 00261 if (pos > 0) 00262 { 00263 uint indent = doc->plainKateTextLine(line)->cursorX(pos, tabWidth); 00264 QString filler = tabString (indent); 00265 doc->insertText (begin.line(), 0, filler); 00266 begin.setCol(filler.length()); 00267 } 00268 else 00269 begin.setCol(0); 00270 } 00271 00272 //END 00273 00274 // BEGIN KateCSmartIndent 00275 00276 KateCSmartIndent::KateCSmartIndent (KateDocument *doc) 00277 : KateAutoIndent (doc), 00278 allowSemi (false), 00279 processingBlock (false) 00280 { 00281 00282 } 00283 00284 KateCSmartIndent::~KateCSmartIndent () 00285 { 00286 00287 } 00288 00289 void KateCSmartIndent::processLine (KateDocCursor &line) 00290 { 00291 KateTextLine::Ptr textLine = doc->plainKateTextLine(line.line()); 00292 00293 int firstChar = textLine->firstChar(); 00294 // Empty line is worthless ... but only when doing more than 1 line 00295 if (firstChar == -1 && processingBlock) 00296 return; 00297 00298 uint indent = 0; 00299 00300 // TODO Here we do not check for beginning and ending comments ... 00301 QChar first = textLine->getChar(firstChar); 00302 QChar last = textLine->getChar(textLine->lastChar()); 00303 00304 if (first == '}') 00305 { 00306 indent = findOpeningBrace(line); 00307 } 00308 else if (first == ')') 00309 { 00310 indent = findOpeningParen(line); 00311 } 00312 else if (first == '{') 00313 { 00314 // If this is the first brace, we keep the indent at 0 00315 KateDocCursor temp(line.line(), firstChar, doc); 00316 if (!firstOpeningBrace(temp)) 00317 indent = calcIndent(temp, false); 00318 } 00319 else if (first == ':') 00320 { 00321 // Initialization lists (handle c++ and c#) 00322 int pos = findOpeningBrace(line); 00323 if (pos == 0) 00324 indent = indentWidth; 00325 else 00326 indent = pos + (indentWidth * 2); 00327 } 00328 else if (last == ':') 00329 { 00330 if (textLine->stringAtPos (firstChar, "case") || 00331 textLine->stringAtPos (firstChar, "default") || 00332 textLine->stringAtPos (firstChar, "public") || 00333 textLine->stringAtPos (firstChar, "private") || 00334 textLine->stringAtPos (firstChar, "protected") || 00335 textLine->stringAtPos (firstChar, "signals") || 00336 textLine->stringAtPos (firstChar, "slots")) 00337 { 00338 indent = findOpeningBrace(line) + indentWidth; 00339 } 00340 } 00341 else if (first == '*') 00342 { 00343 if (last == '/') 00344 { 00345 int lineEnd = textLine->lastChar(); 00346 if (lineEnd > 0 && textLine->getChar(lineEnd - 1) == '*') 00347 { 00348 indent = findOpeningComment(line); 00349 if (textLine->attribute(firstChar) == doxyCommentAttrib) 00350 indent++; 00351 } 00352 else 00353 return; 00354 } 00355 else 00356 { 00357 KateDocCursor temp = line; 00358 if (textLine->attribute(firstChar) == doxyCommentAttrib) 00359 indent = calcIndent(temp, false) + 1; 00360 else 00361 indent = calcIndent(temp, true); 00362 } 00363 } 00364 else if (first == '#') 00365 { 00366 // c# regions 00367 if (textLine->stringAtPos (firstChar, "#region") || 00368 textLine->stringAtPos (firstChar, "#endregion")) 00369 { 00370 KateDocCursor temp = line; 00371 indent = calcIndent(temp, true); 00372 } 00373 } 00374 else 00375 { 00376 // Everything else ... 00377 if (first == '/' && last != '/') 00378 return; 00379 00380 KateDocCursor temp = line; 00381 indent = calcIndent(temp, true); 00382 if (indent == 0) 00383 { 00384 KateAutoIndent::processNewline(line, true); 00385 return; 00386 } 00387 } 00388 00389 // Slightly faster if we don't indent what we don't have to 00390 if (indent != measureIndent(line) || first == '}' || first == '{' || first == '#') 00391 { 00392 doc->removeText(line.line(), 0, line.line(), firstChar); 00393 QString filler = tabString(indent); 00394 if (indent > 0) doc->insertText(line.line(), 0, filler); 00395 if (!processingBlock) line.setCol(filler.length()); 00396 } 00397 } 00398 00399 void KateCSmartIndent::processSection (KateDocCursor &begin, KateDocCursor &end) 00400 { 00401 KateDocCursor cur = begin; 00402 QTime t; 00403 t.start(); 00404 00405 processingBlock = (end.line() - cur.line() > 0) ? true : false; 00406 00407 while (cur.line() <= end.line()) 00408 { 00409 processLine (cur); 00410 if (!cur.gotoNextLine()) 00411 break; 00412 } 00413 00414 processingBlock = false; 00415 kdDebug(13000) << "+++ total: " << t.elapsed() << endl; 00416 } 00417 00418 bool KateCSmartIndent::handleDoxygen (KateDocCursor &begin) 00419 { 00420 // Factor out the rather involved Doxygen stuff here ... 00421 int line = begin.line(); 00422 int first = -1; 00423 while ((line > 0) && (first < 0)) 00424 first = doc->plainKateTextLine(--line)->firstChar(); 00425 00426 if (first > 0) 00427 { 00428 KateTextLine::Ptr textLine = doc->plainKateTextLine(line); 00429 bool insideDoxygen = false; 00430 if (textLine->attribute(first) == doxyCommentAttrib || textLine->attribute(textLine->lastChar()) == doxyCommentAttrib) 00431 { 00432 if (!textLine->endingWith("*/")) 00433 insideDoxygen = true; 00434 } 00435 00436 // Align the *'s and then go ahead and insert one too ... 00437 if (insideDoxygen) 00438 { 00439 textLine = doc->plainKateTextLine(begin.line()); 00440 first = textLine->firstChar(); 00441 int indent = findOpeningComment(begin); 00442 QString filler = tabString (indent); 00443 00444 bool doxygenAutoInsert = doc->config()->configFlags() & KateDocumentConfig::cfDoxygenAutoTyping; 00445 if ( doxygenAutoInsert && 00446 (!textLine->stringAtPos(first, "*/") && !textLine->stringAtPos(first, "*"))) 00447 { 00448 filler = filler + " * "; 00449 } 00450 00451 doc->removeText (begin.line(), 0, begin.line(), first); 00452 doc->insertText (begin.line(), 0, filler); 00453 begin.setCol(filler.length()); 00454 00455 return true; 00456 } 00457 } 00458 00459 return false; 00460 } 00461 00462 void KateCSmartIndent::processNewline (KateDocCursor &begin, bool needContinue) 00463 { 00464 if (!handleDoxygen (begin)) 00465 { 00466 KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line()); 00467 bool inMiddle = textLine->firstChar() > -1; 00468 00469 int indent = calcIndent (begin, needContinue); 00470 00471 if (indent > 0 || inMiddle) 00472 { 00473 QString filler = tabString (indent); 00474 doc->insertText (begin.line(), 0, filler); 00475 begin.setCol(filler.length()); 00476 00477 // Handles cases where user hits enter at the beginning or middle of text 00478 if (inMiddle) 00479 { 00480 processLine(begin); 00481 begin.setCol(textLine->firstChar()); 00482 } 00483 } 00484 else 00485 { 00486 KateAutoIndent::processNewline (begin, needContinue); 00487 begin.setCol(begin.col() - 1); 00488 } 00489 00490 if (begin.col() < 0) 00491 begin.setCol(0); 00492 } 00493 } 00494 00495 void KateCSmartIndent::processChar(QChar c) 00496 { 00497 static const QString triggers("}{)/:;#n"); 00498 if (triggers.find(c, true) == -1) 00499 return; 00500 00501 KateView *view = doc->activeView(); 00502 KateDocCursor begin(view->cursorLine(), 0, doc); 00503 00504 if (c == 'n') 00505 { 00506 KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line()); 00507 if (textLine->getChar(textLine->firstChar()) != '#') 00508 return; 00509 } 00510 00511 processLine(begin); 00512 } 00513 00514 uint KateCSmartIndent::calcIndent(KateDocCursor &begin, bool needContinue) 00515 { 00516 KateTextLine::Ptr textLine; 00517 KateDocCursor cur = begin; 00518 00519 uint anchorIndent = 0; 00520 int anchorPos = 0; 00521 int parenCount = 0; // Possibly in a multiline for stmt. Used to skip ';' ... 00522 bool found = false; 00523 bool isSpecial = false; 00524 00525 //kdDebug() << "calcIndent begin line:" << begin.line() << " col:" << begin.col() << endl; 00526 00527 // Find Indent Anchor Point 00528 while (cur.gotoPreviousLine()) 00529 { 00530 isSpecial = found = false; 00531 textLine = doc->plainKateTextLine(cur.line()); 00532 00533 // Skip comments and handle cases like if (...) { stmt; 00534 int pos = textLine->lastChar(); 00535 int openCount = 0; 00536 int otherAnchor = -1; 00537 do 00538 { 00539 if (textLine->attribute(pos) == symbolAttrib) 00540 { 00541 QChar tc = textLine->getChar (pos); 00542 if ((tc == ';' || tc == ':' || tc == ',') && otherAnchor == -1 && parenCount <= 0) 00543 otherAnchor = pos; 00544 else if (tc == ')') 00545 parenCount++; 00546 else if (tc == '(') 00547 parenCount--; 00548 else if (tc == '}') 00549 openCount--; 00550 else if (tc == '{') 00551 { 00552 openCount++; 00553 if (openCount == 1) 00554 break; 00555 } 00556 } 00557 } while (--pos >= textLine->firstChar()); 00558 00559 if (openCount != 0 || otherAnchor != -1) 00560 { 00561 found = true; 00562 QChar c; 00563 if (openCount > 0) 00564 c = '{'; 00565 else if (openCount < 0) 00566 c = '}'; 00567 else if (otherAnchor >= 0) 00568 c = textLine->getChar (otherAnchor); 00569 00570 int specialIndent = 0; 00571 if (c == ':' && needContinue) 00572 { 00573 QChar ch; 00574 specialIndent = textLine->firstChar(); 00575 if (textLine->stringAtPos(specialIndent, "case")) 00576 ch = textLine->getChar(specialIndent + 4); 00577 else if (textLine->stringAtPos(specialIndent, "default")) 00578 ch = textLine->getChar(specialIndent + 7); 00579 else if (textLine->stringAtPos(specialIndent, "public")) 00580 ch = textLine->getChar(specialIndent + 6); 00581 else if (textLine->stringAtPos(specialIndent, "private")) 00582 ch = textLine->getChar(specialIndent + 7); 00583 else if (textLine->stringAtPos(specialIndent, "protected")) 00584 ch = textLine->getChar(specialIndent + 9); 00585 else if (textLine->stringAtPos(specialIndent, "signals")) 00586 ch = textLine->getChar(specialIndent + 7); 00587 else if (textLine->stringAtPos(specialIndent, "slots")) 00588 ch = textLine->getChar(specialIndent + 5); 00589 00590 if (ch.isNull() || (!ch.isSpace() && ch != '(' && ch != ':')) 00591 continue; 00592 00593 KateDocCursor lineBegin = cur; 00594 lineBegin.setCol(specialIndent); 00595 specialIndent = measureIndent(lineBegin); 00596 isSpecial = true; 00597 } 00598 00599 // Move forward past blank lines 00600 KateDocCursor skip = cur; 00601 skip.setCol(textLine->lastChar()); 00602 bool result = skipBlanks(skip, begin, true); 00603 00604 anchorPos = skip.col(); 00605 anchorIndent = measureIndent(skip); 00606 00607 //kdDebug() << "calcIndent anchorPos:" << anchorPos << " anchorIndent:" << anchorIndent << " at line:" << skip.line() << endl; 00608 00609 // Accept if it's before requested position or if it was special 00610 if (result && skip < begin) 00611 { 00612 cur = skip; 00613 break; 00614 } 00615 else if (isSpecial) 00616 { 00617 anchorIndent = specialIndent; 00618 break; 00619 } 00620 00621 // Are these on a line by themselves? (i.e. both last and first char) 00622 if ((c == '{' || c == '}') && textLine->getChar(textLine->firstChar()) == c) 00623 { 00624 cur.setCol(anchorPos = textLine->firstChar()); 00625 anchorIndent = measureIndent (cur); 00626 break; 00627 } 00628 } 00629 } 00630 00631 if (!found) 00632 return 0; 00633 00634 uint continueIndent = (needContinue) ? calcContinue (cur, begin) : 0; 00635 //kdDebug() << "calcIndent continueIndent:" << continueIndent << endl; 00636 00637 // Move forward from anchor and determine last known reference character 00638 // Braces take precedance over others ... 00639 textLine = doc->plainKateTextLine(cur.line()); 00640 QChar lastChar = textLine->getChar (anchorPos); 00641 int lastLine = cur.line(); 00642 if (lastChar == '#' || lastChar == '[') 00643 { 00644 // Never continue if # or [ is encountered at this point here 00645 // A fail-safe really... most likely an #include, #region, or a c# attribute 00646 continueIndent = 0; 00647 } 00648 00649 int openCount = 0; 00650 while (cur.validPosition() && cur < begin) 00651 { 00652 if (!skipBlanks(cur, begin, true)) 00653 return 0; 00654 00655 QChar tc = cur.currentChar(); 00656 //kdDebug() << " cur.line:" << cur.line() << " cur.col:" << cur.col() << " currentChar '" << tc << "' " << textLine->attribute(cur.col()) << endl; 00657 if (cur == begin || tc.isNull()) 00658 break; 00659 00660 if (!tc.isSpace() && cur < begin) 00661 { 00662 uchar attrib = cur.currentAttrib(); 00663 if (tc == '{' && attrib == symbolAttrib) 00664 openCount++; 00665 else if (tc == '}' && attrib == symbolAttrib) 00666 openCount--; 00667 00668 lastChar = tc; 00669 lastLine = cur.line(); 00670 } 00671 } 00672 if (openCount > 0) // Open braces override 00673 lastChar = '{'; 00674 00675 uint indent = 0; 00676 //kdDebug() << "calcIndent lastChar '" << lastChar << "'" << endl; 00677 00678 if (lastChar == '{' || (lastChar == ':' && isSpecial && needContinue)) 00679 { 00680 indent = anchorIndent + indentWidth; 00681 } 00682 else if (lastChar == '}') 00683 { 00684 indent = anchorIndent; 00685 } 00686 else if (lastChar == ';') 00687 { 00688 indent = anchorIndent + ((allowSemi && needContinue) ? continueIndent : 0); 00689 } 00690 else if (lastChar == ',') 00691 { 00692 textLine = doc->plainKateTextLine(lastLine); 00693 KateDocCursor start(lastLine, textLine->firstChar(), doc); 00694 KateDocCursor finish(lastLine, textLine->lastChar(), doc); 00695 uint pos = 0; 00696 00697 if (isBalanced(start, finish, QChar('('), QChar(')'), pos)) 00698 indent = anchorIndent; 00699 else 00700 { 00701 // TODO: Config option. If we're below 48, go ahead and line them up 00702 indent = ((pos < 48) ? pos : anchorIndent + (indentWidth * 2)); 00703 } 00704 } 00705 else if (!lastChar.isNull()) 00706 { 00707 if (anchorIndent != 0) 00708 indent = anchorIndent + continueIndent; 00709 else 00710 indent = continueIndent; 00711 } 00712 00713 return indent; 00714 } 00715 00716 uint KateCSmartIndent::calcContinue(KateDocCursor &start, KateDocCursor &end) 00717 { 00718 KateDocCursor cur = start; 00719 00720 bool needsBalanced = true; 00721 bool isFor = false; 00722 allowSemi = false; 00723 00724 KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line()); 00725 00726 // Handle cases such as } while (s ... by skipping the leading symbol 00727 if (textLine->attribute(cur.col()) == symbolAttrib) 00728 { 00729 cur.moveForward(1); 00730 skipBlanks(cur, end, false); 00731 } 00732 00733 if (textLine->getChar(cur.col()) == '}') 00734 { 00735 skipBlanks(cur, end, true); 00736 if (cur.line() != start.line()) 00737 textLine = doc->plainKateTextLine(cur.line()); 00738 00739 if (textLine->stringAtPos(cur.col(), "else")) 00740 cur.setCol(cur.col() + 4); 00741 else 00742 return indentWidth * 2; 00743 00744 needsBalanced = false; 00745 } 00746 else if (textLine->stringAtPos(cur.col(), "else")) 00747 { 00748 cur.setCol(cur.col() + 4); 00749 needsBalanced = false; 00750 if (textLine->stringAtPos(textLine->nextNonSpaceChar(cur.col()), "if")) 00751 { 00752 cur.setCol(textLine->nextNonSpaceChar(cur.col()) + 2); 00753 needsBalanced = true; 00754 } 00755 } 00756 else if (textLine->stringAtPos(cur.col(), "if")) 00757 { 00758 cur.setCol(cur.col() + 2); 00759 } 00760 else if (textLine->stringAtPos(cur.col(), "do")) 00761 { 00762 cur.setCol(cur.col() + 2); 00763 needsBalanced = false; 00764 } 00765 else if (textLine->stringAtPos(cur.col(), "for")) 00766 { 00767 cur.setCol(cur.col() + 3); 00768 isFor = true; 00769 } 00770 else if (textLine->stringAtPos(cur.col(), "while")) 00771 { 00772 cur.setCol(cur.col() + 5); 00773 } 00774 else if (textLine->stringAtPos(cur.col(), "switch")) 00775 { 00776 cur.setCol(cur.col() + 6); 00777 } 00778 else if (textLine->stringAtPos(cur.col(), "using")) 00779 { 00780 cur.setCol(cur.col() + 5); 00781 } 00782 else 00783 { 00784 return indentWidth * 2; 00785 } 00786 00787 uint openPos = 0; 00788 if (needsBalanced && !isBalanced (cur, end, QChar('('), QChar(')'), openPos)) 00789 { 00790 allowSemi = isFor; 00791 if (openPos > 0) 00792 return (openPos - textLine->firstChar()); 00793 else 00794 return indentWidth * 2; 00795 } 00796 00797 // Check if this statement ends a line now 00798 skipBlanks(cur, end, false); 00799 if (cur == end) 00800 return indentWidth; 00801 00802 if (skipBlanks(cur, end, true)) 00803 { 00804 if (cur == end) 00805 return indentWidth; 00806 else 00807 return indentWidth + calcContinue(cur, end); 00808 } 00809 00810 return 0; 00811 } 00812 00813 uint KateCSmartIndent::findOpeningBrace(KateDocCursor &start) 00814 { 00815 KateDocCursor cur = start; 00816 int count = 1; 00817 00818 // Move backwards 1 by 1 and find the opening brace 00819 // Return the indent of that line 00820 while (cur.moveBackward(1)) 00821 { 00822 if (cur.currentAttrib() == symbolAttrib) 00823 { 00824 QChar ch = cur.currentChar(); 00825 if (ch == '{') 00826 count--; 00827 else if (ch == '}') 00828 count++; 00829 00830 if (count == 0) 00831 { 00832 KateDocCursor temp(cur.line(), doc->plainKateTextLine(cur.line())->firstChar(), doc); 00833 return measureIndent(temp); 00834 } 00835 } 00836 } 00837 00838 return 0; 00839 } 00840 00841 bool KateCSmartIndent::firstOpeningBrace(KateDocCursor &start) 00842 { 00843 KateDocCursor cur = start; 00844 00845 // Are we the first opening brace at this level? 00846 while(cur.moveBackward(1)) 00847 { 00848 if (cur.currentAttrib() == symbolAttrib) 00849 { 00850 QChar ch = cur.currentChar(); 00851 if (ch == '{') 00852 return false; 00853 else if (ch == '}' && cur.col() == 0) 00854 break; 00855 } 00856 } 00857 00858 return true; 00859 } 00860 00861 uint KateCSmartIndent::findOpeningParen(KateDocCursor &start) 00862 { 00863 KateDocCursor cur = start; 00864 int count = 1; 00865 00866 // Move backwards 1 by 1 and find the opening ( 00867 // Return the indent of that line 00868 while (cur.moveBackward(1)) 00869 { 00870 if (cur.currentAttrib() == symbolAttrib) 00871 { 00872 QChar ch = cur.currentChar(); 00873 if (ch == '(') 00874 count--; 00875 else if (ch == ')') 00876 count++; 00877 00878 if (count == 0) 00879 return measureIndent(cur); 00880 } 00881 } 00882 00883 return 0; 00884 } 00885 00886 uint KateCSmartIndent::findOpeningComment(KateDocCursor &start) 00887 { 00888 KateDocCursor cur = start; 00889 00890 // Find the line with the opening /* and return the proper indent 00891 do 00892 { 00893 KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line()); 00894 00895 int pos = textLine->string().find("/*", false); 00896 if (pos >= 0) 00897 { 00898 KateDocCursor temp(cur.line(), pos, doc); 00899 return measureIndent(temp); 00900 } 00901 00902 } while (cur.gotoPreviousLine()); 00903 00904 return 0; 00905 } 00906 00907 // END 00908 00909 // BEGIN KatePythonIndent 00910 00911 QRegExp KatePythonIndent::endWithColon = QRegExp( "^[^#]*:\\s*(#.*)?$" ); 00912 QRegExp KatePythonIndent::stopStmt = QRegExp( "^\\s*(break|continue|raise|return|pass)\\b.*" ); 00913 QRegExp KatePythonIndent::blockBegin = QRegExp( "^\\s*(def|if|elif|else|for|while|try)\\b.*" ); 00914 00915 KatePythonIndent::KatePythonIndent (KateDocument *doc) 00916 : KateAutoIndent (doc) 00917 { 00918 } 00919 KatePythonIndent::~KatePythonIndent () 00920 { 00921 } 00922 00923 void KatePythonIndent::processNewline (KateDocCursor &begin, bool /*newline*/) 00924 { 00925 int prevLine = begin.line() - 1; 00926 int prevPos = begin.col(); 00927 00928 while ((prevLine > 0) && (prevPos < 0)) // search a not empty text line 00929 prevPos = doc->plainKateTextLine(--prevLine)->firstChar(); 00930 00931 int prevBlock = prevLine; 00932 int prevBlockPos = prevPos; 00933 int extraIndent = calcExtra (prevBlock, prevBlockPos, begin); 00934 00935 int indent = doc->plainKateTextLine(prevBlock)->cursorX(prevBlockPos, tabWidth); 00936 if (extraIndent == 0) 00937 { 00938 if (!stopStmt.exactMatch(doc->plainKateTextLine(prevLine)->string())) 00939 { 00940 if (endWithColon.exactMatch(doc->plainKateTextLine(prevLine)->string())) 00941 indent += indentWidth; 00942 else 00943 indent = doc->plainKateTextLine(prevLine)->cursorX(prevPos, tabWidth); 00944 } 00945 } 00946 else 00947 indent += extraIndent; 00948 00949 if (indent > 0) 00950 { 00951 QString filler = tabString (indent); 00952 doc->insertText (begin.line(), 0, filler); 00953 begin.setCol(filler.length()); 00954 } 00955 else 00956 begin.setCol(0); 00957 } 00958 00959 int KatePythonIndent::calcExtra (int &prevBlock, int &pos, KateDocCursor &end) 00960 { 00961 int nestLevel = 0; 00962 bool levelFound = false; 00963 while ((prevBlock > 0)) 00964 { 00965 if (blockBegin.exactMatch(doc->plainKateTextLine(prevBlock)->string())) 00966 { 00967 if ((!levelFound && nestLevel == 0) || (levelFound && nestLevel - 1 <= 0)) 00968 { 00969 pos = doc->plainKateTextLine(prevBlock)->firstChar(); 00970 break; 00971 } 00972 00973 nestLevel --; 00974 } 00975 else if (stopStmt.exactMatch(doc->plainKateTextLine(prevBlock)->string())) 00976 { 00977 nestLevel ++; 00978 levelFound = true; 00979 } 00980 00981 --prevBlock; 00982 } 00983 00984 KateDocCursor cur (prevBlock, pos, doc); 00985 QChar c; 00986 int extraIndent = 0; 00987 while (cur.line() < end.line()) 00988 { 00989 c = cur.currentChar(); 00990 00991 if (c == '(') 00992 extraIndent += indentWidth; 00993 else if (c == ')') 00994 extraIndent -= indentWidth; 00995 else if (c == ':') 00996 break; 00997 00998 if (c.isNull() || c == '#') 00999 cur.gotoNextLine(); 01000 else 01001 cur.moveForward(1); 01002 } 01003 01004 return extraIndent; 01005 } 01006 01007 // END 01008 01009 // 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.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Sep 29 09:42:42 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003