00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "kateautoindent.h"
00021 #include "kateautoindent.moc"
00022
00023 #include "kateconfig.h"
00024 #include "katehighlight.h"
00025 #include "kateview.h"
00026
00027 #include <klocale.h>
00028 #include <kdebug.h>
00029 #include <kpopupmenu.h>
00030
00031
00032
00033 KateAutoIndent *KateAutoIndent::createIndenter (KateDocument *doc, uint mode)
00034 {
00035 if (mode == KateDocumentConfig::imNormal)
00036 return new KateNormalIndent (doc);
00037 else if (mode == KateDocumentConfig::imCStyle)
00038 return new KateCSmartIndent (doc);
00039 else if (mode == KateDocumentConfig::imPythonStyle)
00040 return new KatePythonIndent (doc);
00041 else if (mode == KateDocumentConfig::imXmlStyle)
00042 return new KateXmlIndent (doc);
00043 else if (mode == KateDocumentConfig::imCSAndS)
00044 return new KateCSAndSIndent (doc);
00045 else if ( mode == KateDocumentConfig::imVarIndent )
00046 return new KateVarIndent ( doc );
00047
00048 return new KateAutoIndent (doc);
00049 }
00050
00051 QStringList KateAutoIndent::listModes ()
00052 {
00053 QStringList l;
00054
00055 l << modeDescription(KateDocumentConfig::imNone);
00056 l << modeDescription(KateDocumentConfig::imNormal);
00057 l << modeDescription(KateDocumentConfig::imCStyle);
00058 l << modeDescription(KateDocumentConfig::imPythonStyle);
00059 l << modeDescription(KateDocumentConfig::imXmlStyle);
00060 l << modeDescription(KateDocumentConfig::imCSAndS);
00061 l << modeDescription( KateDocumentConfig::imVarIndent );
00062
00063 return l;
00064 }
00065
00066 QString KateAutoIndent::modeName (uint mode)
00067 {
00068 if (mode == KateDocumentConfig::imNormal)
00069 return QString ("normal");
00070 else if (mode == KateDocumentConfig::imCStyle)
00071 return QString ("cstyle");
00072 else if (mode == KateDocumentConfig::imPythonStyle)
00073 return QString ("python");
00074 else if (mode == KateDocumentConfig::imXmlStyle)
00075 return QString ("xml");
00076 else if (mode == KateDocumentConfig::imCSAndS)
00077 return QString ("csands");
00078 else if ( mode == KateDocumentConfig::imVarIndent )
00079 return QString( "varindent" );
00080
00081 return QString ("none");
00082 }
00083
00084 QString KateAutoIndent::modeDescription (uint mode)
00085 {
00086 if (mode == KateDocumentConfig::imNormal)
00087 return i18n ("Normal");
00088 else if (mode == KateDocumentConfig::imCStyle)
00089 return i18n ("C Style");
00090 else if (mode == KateDocumentConfig::imPythonStyle)
00091 return i18n ("Python Style");
00092 else if (mode == KateDocumentConfig::imXmlStyle)
00093 return i18n ("XML Style");
00094 else if (mode == KateDocumentConfig::imCSAndS)
00095 return i18n ("S&S C Style");
00096 else if ( mode == KateDocumentConfig::imVarIndent )
00097 return i18n("Variable Based Indenter");
00098
00099 return i18n ("None");
00100 }
00101
00102 uint KateAutoIndent::modeNumber (const QString &name)
00103 {
00104 if (modeName(KateDocumentConfig::imNormal) == name)
00105 return KateDocumentConfig::imNormal;
00106 else if (modeName(KateDocumentConfig::imCStyle) == name)
00107 return KateDocumentConfig::imCStyle;
00108 else if (modeName(KateDocumentConfig::imPythonStyle) == name)
00109 return KateDocumentConfig::imPythonStyle;
00110 else if (modeName(KateDocumentConfig::imXmlStyle) == name)
00111 return KateDocumentConfig::imXmlStyle;
00112 else if (modeName(KateDocumentConfig::imCSAndS) == name)
00113 return KateDocumentConfig::imCSAndS;
00114 else if ( modeName( KateDocumentConfig::imVarIndent ) == name )
00115 return KateDocumentConfig::imVarIndent;
00116
00117 return KateDocumentConfig::imNone;
00118 }
00119
00120 KateAutoIndent::KateAutoIndent (KateDocument *_doc)
00121 : doc(_doc)
00122 {
00123 }
00124 KateAutoIndent::~KateAutoIndent ()
00125 {
00126 }
00127
00128
00129
00130
00131 KateViewIndentationAction::KateViewIndentationAction(KateDocument *_doc, const QString& text, QObject* parent, const char* name)
00132 : KActionMenu (text, parent, name), doc(_doc)
00133 {
00134 connect(popupMenu(),SIGNAL(aboutToShow()),this,SLOT(slotAboutToShow()));
00135 }
00136
00137 void KateViewIndentationAction::slotAboutToShow()
00138 {
00139 QStringList modes = KateAutoIndent::listModes ();
00140
00141 popupMenu()->clear ();
00142 for (uint z=0; z<modes.size(); ++z)
00143 popupMenu()->insertItem ( '&' + KateAutoIndent::modeDescription(z), this, SLOT(setMode(int)), 0, z);
00144
00145 popupMenu()->setItemChecked (doc->config()->indentationMode(), true);
00146 }
00147
00148 void KateViewIndentationAction::setMode (int mode)
00149 {
00150 doc->config()->setIndentationMode((uint)mode);
00151 }
00152
00153
00154
00155
00156 KateNormalIndent::KateNormalIndent (KateDocument *_doc)
00157 : KateAutoIndent (_doc)
00158 {
00159 }
00160 KateNormalIndent::~KateNormalIndent ()
00161 {
00162 }
00163
00164 void KateNormalIndent::updateConfig ()
00165 {
00166 KateDocumentConfig *config = doc->config();
00167
00168 useSpaces = config->configFlags() & KateDocument::cfSpaceIndent || config->configFlags() & KateDocumentConfig::cfReplaceTabsDyn;
00169 mixedIndent = useSpaces && config->configFlags() & KateDocumentConfig::cfMixedIndent;
00170 keepProfile = config->configFlags() & KateDocument::cfKeepIndentProfile;
00171 tabWidth = config->tabWidth();
00172 indentWidth = useSpaces? config->indentationWidth() : tabWidth;
00173
00174 commentAttrib = 255;
00175 doxyCommentAttrib = 255;
00176 regionAttrib = 255;
00177 symbolAttrib = 255;
00178 alertAttrib = 255;
00179 tagAttrib = 255;
00180 wordAttrib = 255;
00181 keywordAttrib = 255;
00182 normalAttrib = 255;
00183 extensionAttrib = 255;
00184
00185 KateHlItemDataList items;
00186 doc->highlight()->getKateHlItemDataListCopy (0, items);
00187
00188 for (uint i=0; i<items.count(); i++)
00189 {
00190 QString name = items.at(i)->name;
00191 if (name.find("Comment") != -1 && commentAttrib == 255)
00192 {
00193 commentAttrib = i;
00194 }
00195 else if (name.find("Region Marker") != -1 && regionAttrib == 255)
00196 {
00197 regionAttrib = i;
00198 }
00199 else if (name.find("Symbol") != -1 && symbolAttrib == 255)
00200 {
00201 symbolAttrib = i;
00202 }
00203 else if (name.find("Alert") != -1)
00204 {
00205 alertAttrib = i;
00206 }
00207 else if (name.find("Comment") != -1 && commentAttrib != 255 && doxyCommentAttrib == 255)
00208 {
00209 doxyCommentAttrib = i;
00210 }
00211 else if (name.find("Tags") != -1 && tagAttrib == 255)
00212 {
00213 tagAttrib = i;
00214 }
00215 else if (name.find("Word") != -1 && wordAttrib == 255)
00216 {
00217 wordAttrib = i;
00218 }
00219 else if (name.find("Keyword") != -1 && keywordAttrib == 255)
00220 {
00221 keywordAttrib = i;
00222 }
00223 else if (name.find("Normal") != -1 && normalAttrib == 255)
00224 {
00225 normalAttrib = i;
00226 }
00227 else if (name.find("Extensions") != -1 && extensionAttrib == 255)
00228 {
00229 extensionAttrib = i;
00230 }
00231 }
00232 }
00233
00234 bool KateNormalIndent::isBalanced (KateDocCursor &begin, const KateDocCursor &end, QChar open, QChar close, uint &pos) const
00235 {
00236 int parenOpen = 0;
00237 bool atLeastOne = false;
00238 bool getNext = false;
00239
00240 pos = doc->plainKateTextLine(begin.line())->firstChar();
00241
00242
00243
00244 while (begin < end)
00245 {
00246 QChar c = begin.currentChar();
00247 if (begin.currentAttrib() == symbolAttrib)
00248 {
00249 if (c == open)
00250 {
00251 if (!atLeastOne)
00252 {
00253 atLeastOne = true;
00254 getNext = true;
00255 pos = measureIndent(begin) + 1;
00256 }
00257 parenOpen++;
00258 }
00259 else if (c == close)
00260 {
00261 parenOpen--;
00262 }
00263 }
00264 else if (getNext && !c.isSpace())
00265 {
00266 getNext = false;
00267 pos = measureIndent(begin);
00268 }
00269
00270 if (atLeastOne && parenOpen <= 0)
00271 return true;
00272
00273 begin.moveForward(1);
00274 }
00275
00276 return (atLeastOne) ? false : true;
00277 }
00278
00279 bool KateNormalIndent::skipBlanks (KateDocCursor &cur, KateDocCursor &max, bool newline) const
00280 {
00281 int curLine = cur.line();
00282 if (newline)
00283 cur.moveForward(1);
00284
00285 if (cur >= max)
00286 return false;
00287
00288 do
00289 {
00290 uchar attrib = cur.currentAttrib();
00291 if (attrib != commentAttrib && attrib != doxyCommentAttrib && attrib != regionAttrib && attrib != alertAttrib && attrib != tagAttrib && attrib != wordAttrib)
00292 {
00293 QChar c = cur.currentChar();
00294 if (!c.isNull() && !c.isSpace())
00295 break;
00296 }
00297
00298
00299 if (!cur.moveForward(1))
00300 break;
00301 if (curLine != cur.line())
00302 {
00303 if (!newline)
00304 break;
00305 curLine = cur.line();
00306 cur.setCol(0);
00307 }
00308 } while (cur < max);
00309
00310 if (cur > max)
00311 cur = max;
00312 return true;
00313 }
00314
00315 uint KateNormalIndent::measureIndent (KateDocCursor &cur) const
00316 {
00317 if (useSpaces && !mixedIndent)
00318 return cur.col();
00319
00320 return doc->plainKateTextLine(cur.line())->cursorX(cur.col(), tabWidth);
00321 }
00322
00323 QString KateNormalIndent::tabString(uint pos) const
00324 {
00325 QString s;
00326 pos = QMIN (pos, 80);
00327
00328 if (!useSpaces || mixedIndent)
00329 {
00330 while (pos >= tabWidth)
00331 {
00332 s += '\t';
00333 pos -= tabWidth;
00334 }
00335 }
00336 while (pos > 0)
00337 {
00338 s += ' ';
00339 pos--;
00340 }
00341 return s;
00342 }
00343
00344 void KateNormalIndent::processNewline (KateDocCursor &begin, bool )
00345 {
00346 int line = begin.line() - 1;
00347 int pos = begin.col();
00348
00349 while ((line > 0) && (pos < 0))
00350 pos = doc->plainKateTextLine(--line)->firstChar();
00351
00352 if (pos > 0)
00353 {
00354 QString filler = doc->text(line, 0, line, pos);
00355 doc->insertText(begin.line(), 0, filler);
00356 begin.setCol(filler.length());
00357 }
00358 else
00359 begin.setCol(0);
00360 }
00361
00362
00363
00364
00365
00366 KateCSmartIndent::KateCSmartIndent (KateDocument *doc)
00367 : KateNormalIndent (doc),
00368 allowSemi (false),
00369 processingBlock (false)
00370 {
00371 kdDebug(13030)<<"CREATING KATECSMART INTDETER"<<endl;
00372 }
00373
00374 KateCSmartIndent::~KateCSmartIndent ()
00375 {
00376
00377 }
00378
00379 void KateCSmartIndent::processLine (KateDocCursor &line)
00380 {
00381 kdDebug(13030)<<"PROCESSING LINE "<<line.line()<<endl;
00382 KateTextLine::Ptr textLine = doc->plainKateTextLine(line.line());
00383
00384 int firstChar = textLine->firstChar();
00385
00386 if (firstChar == -1 && processingBlock)
00387 return;
00388
00389 uint indent = 0;
00390
00391
00392 QChar first = textLine->getChar(firstChar);
00393 QChar last = textLine->getChar(textLine->lastChar());
00394
00395 if (first == '}')
00396 {
00397 indent = findOpeningBrace(line);
00398 }
00399 else if (first == ')')
00400 {
00401 indent = findOpeningParen(line);
00402 }
00403 else if (first == '{')
00404 {
00405
00406 KateDocCursor temp(line.line(), firstChar, doc);
00407 if (!firstOpeningBrace(temp))
00408 indent = calcIndent(temp, false);
00409 }
00410 else if (first == ':')
00411 {
00412
00413 int pos = findOpeningBrace(line);
00414 if (pos == 0)
00415 indent = indentWidth;
00416 else
00417 indent = pos + (indentWidth * 2);
00418 }
00419 else if (last == ':')
00420 {
00421 if (textLine->stringAtPos (firstChar, "case") ||
00422 textLine->stringAtPos (firstChar, "default") ||
00423 textLine->stringAtPos (firstChar, "public") ||
00424 textLine->stringAtPos (firstChar, "private") ||
00425 textLine->stringAtPos (firstChar, "protected") ||
00426 textLine->stringAtPos (firstChar, "signals") ||
00427 textLine->stringAtPos (firstChar, "slots"))
00428 {
00429 indent = findOpeningBrace(line) + indentWidth;
00430 }
00431 }
00432 else if (first == '*')
00433 {
00434 if (last == '/')
00435 {
00436 int lineEnd = textLine->lastChar();
00437 if (lineEnd > 0 && textLine->getChar(lineEnd - 1) == '*')
00438 {
00439 indent = findOpeningComment(line);
00440 if (textLine->attribute(firstChar) == doxyCommentAttrib)
00441 indent++;
00442 }
00443 else
00444 return;
00445 }
00446 else
00447 {
00448 KateDocCursor temp = line;
00449 if (textLine->attribute(firstChar) == doxyCommentAttrib)
00450 indent = calcIndent(temp, false) + 1;
00451 else
00452 indent = calcIndent(temp, true);
00453 }
00454 }
00455 else if (first == '#')
00456 {
00457
00458 if (textLine->stringAtPos (firstChar, "#region") ||
00459 textLine->stringAtPos (firstChar, "#endregion"))
00460 {
00461 KateDocCursor temp = line;
00462 indent = calcIndent(temp, true);
00463 }
00464 }
00465 else
00466 {
00467
00468 if (first == '/' && last != '/')
00469 return;
00470
00471 KateDocCursor temp = line;
00472 indent = calcIndent(temp, true);
00473 if (indent == 0)
00474 {
00475 KateNormalIndent::processNewline(line, true);
00476 return;
00477 }
00478 }
00479
00480
00481 if (indent != measureIndent(line) || first == '}' || first == '{' || first == '#')
00482 {
00483 doc->removeText(line.line(), 0, line.line(), firstChar);
00484 QString filler = tabString(indent);
00485 if (indent > 0) doc->insertText(line.line(), 0, filler);
00486 if (!processingBlock) line.setCol(filler.length());
00487 }
00488 }
00489
00490 void KateCSmartIndent::processSection (KateDocCursor &begin, KateDocCursor &end)
00491 {
00492 kdDebug(13030)<<"PROCESS SECTION"<<endl;
00493 KateDocCursor cur = begin;
00494 QTime t;
00495 t.start();
00496
00497 processingBlock = (end.line() - cur.line() > 0) ? true : false;
00498
00499 while (cur.line() <= end.line())
00500 {
00501 processLine (cur);
00502 if (!cur.gotoNextLine())
00503 break;
00504 }
00505
00506 processingBlock = false;
00507 kdDebug(13030) << "+++ total: " << t.elapsed() << endl;
00508 }
00509
00510 bool KateCSmartIndent::handleDoxygen (KateDocCursor &begin)
00511 {
00512
00513 int line = begin.line();
00514 int first = -1;
00515 while ((line > 0) && (first < 0))
00516 first = doc->plainKateTextLine(--line)->firstChar();
00517
00518 if (first >= 0)
00519 {
00520 KateTextLine::Ptr textLine = doc->plainKateTextLine(line);
00521 bool insideDoxygen = false;
00522 if (textLine->attribute(first) == doxyCommentAttrib || textLine->attribute(textLine->lastChar()) == doxyCommentAttrib)
00523 {
00524 if (!textLine->endingWith("*/"))
00525 insideDoxygen = true;
00526 }
00527
00528
00529 if (insideDoxygen)
00530 {
00531 textLine = doc->plainKateTextLine(begin.line());
00532 first = textLine->firstChar();
00533 int indent = findOpeningComment(begin);
00534 QString filler = tabString (indent);
00535
00536 bool doxygenAutoInsert = doc->config()->configFlags() & KateDocumentConfig::cfDoxygenAutoTyping;
00537 if ( doxygenAutoInsert &&
00538 (!textLine->stringAtPos(first, "*/") && !textLine->stringAtPos(first, "*")))
00539 {
00540 filler = filler + " * ";
00541 }
00542
00543 doc->removeText (begin.line(), 0, begin.line(), first);
00544 doc->insertText (begin.line(), 0, filler);
00545 begin.setCol(filler.length());
00546
00547 return true;
00548 }
00549 }
00550
00551 return false;
00552 }
00553
00554 void KateCSmartIndent::processNewline (KateDocCursor &begin, bool needContinue)
00555 {
00556 if (!handleDoxygen (begin))
00557 {
00558 KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
00559 bool inMiddle = textLine->firstChar() > -1;
00560
00561 int indent = calcIndent (begin, needContinue);
00562
00563 if (indent > 0 || inMiddle)
00564 {
00565 QString filler = tabString (indent);
00566 doc->insertText (begin.line(), 0, filler);
00567 begin.setCol(filler.length());
00568
00569
00570 if (inMiddle)
00571 {
00572 processLine(begin);
00573 begin.setCol(textLine->firstChar());
00574 }
00575 }
00576 else
00577 {
00578 KateNormalIndent::processNewline (begin, needContinue);
00579 }
00580
00581 if (begin.col() < 0)
00582 begin.setCol(0);
00583 }
00584 }
00585
00586 void KateCSmartIndent::processChar(QChar c)
00587 {
00588 static const QString triggers("}{)/:;#n");
00589 if (triggers.find(c) < 0)
00590 return;
00591
00592 KateView *view = doc->activeView();
00593 KateDocCursor begin(view->cursorLine(), 0, doc);
00594
00595 KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
00596 if (c == 'n')
00597 {
00598 if (textLine->getChar(textLine->firstChar()) != '#')
00599 return;
00600 }
00601
00602
00603 if ( textLine->attribute( begin.col() ) == doxyCommentAttrib )
00604 return;
00605
00606 processLine(begin);
00607 }
00608
00609
00610 uint KateCSmartIndent::calcIndent(KateDocCursor &begin, bool needContinue)
00611 {
00612 KateTextLine::Ptr textLine;
00613 KateDocCursor cur = begin;
00614
00615 uint anchorIndent = 0;
00616 int anchorPos = 0;
00617 int parenCount = 0;
00618 bool found = false;
00619 bool isSpecial = false;
00620
00621
00622
00623
00624 while (cur.gotoPreviousLine())
00625 {
00626 isSpecial = found = false;
00627 textLine = doc->plainKateTextLine(cur.line());
00628
00629
00630 int pos = textLine->lastChar();
00631 int openCount = 0;
00632 int otherAnchor = -1;
00633 do
00634 {
00635 if (textLine->attribute(pos) == symbolAttrib)
00636 {
00637 QChar tc = textLine->getChar (pos);
00638 if ((tc == ';' || tc == ':' || tc == ',') && otherAnchor == -1 && parenCount <= 0)
00639 otherAnchor = pos;
00640 else if (tc == ')')
00641 parenCount++;
00642 else if (tc == '(')
00643 parenCount--;
00644 else if (tc == '}')
00645 openCount--;
00646 else if (tc == '{')
00647 {
00648 openCount++;
00649 if (openCount == 1)
00650 break;
00651 }
00652 }
00653 } while (--pos >= textLine->firstChar());
00654
00655 if (openCount != 0 || otherAnchor != -1)
00656 {
00657 found = true;
00658 QChar c;
00659 if (openCount > 0)
00660 c = '{';
00661 else if (openCount < 0)
00662 c = '}';
00663 else if (otherAnchor >= 0)
00664 c = textLine->getChar (otherAnchor);
00665
00666 int specialIndent = 0;
00667 if (c == ':' && needContinue)
00668 {
00669 QChar ch;
00670 specialIndent = textLine->firstChar();
00671 if (textLine->stringAtPos(specialIndent, "case"))
00672 ch = textLine->getChar(specialIndent + 4);
00673 else if (textLine->stringAtPos(specialIndent, "default"))
00674 ch = textLine->getChar(specialIndent + 7);
00675 else if (textLine->stringAtPos(specialIndent, "public"))
00676 ch = textLine->getChar(specialIndent + 6);
00677 else if (textLine->stringAtPos(specialIndent, "private"))
00678 ch = textLine->getChar(specialIndent + 7);
00679 else if (textLine->stringAtPos(specialIndent, "protected"))
00680 ch = textLine->getChar(specialIndent + 9);
00681 else if (textLine->stringAtPos(specialIndent, "signals"))
00682 ch = textLine->getChar(specialIndent + 7);
00683 else if (textLine->stringAtPos(specialIndent, "slots"))
00684 ch = textLine->getChar(specialIndent + 5);
00685
00686 if (ch.isNull() || (!ch.isSpace() && ch != '(' && ch != ':'))
00687 continue;
00688
00689 KateDocCursor lineBegin = cur;
00690 lineBegin.setCol(specialIndent);
00691 specialIndent = measureIndent(lineBegin);
00692 isSpecial = true;
00693 }
00694
00695
00696 KateDocCursor skip = cur;
00697 skip.setCol(textLine->lastChar());
00698 bool result = skipBlanks(skip, begin, true);
00699
00700 anchorPos = skip.col();
00701 anchorIndent = measureIndent(skip);
00702
00703
00704
00705
00706 if (result && skip < begin)
00707 {
00708 cur = skip;
00709 break;
00710 }
00711 else if (isSpecial)
00712 {
00713 anchorIndent = specialIndent;
00714 break;
00715 }
00716
00717
00718 if ((c == '{' || c == '}') && textLine->getChar(textLine->firstChar()) == c)
00719 {
00720 cur.setCol(anchorPos = textLine->firstChar());
00721 anchorIndent = measureIndent (cur);
00722 break;
00723 }
00724 }
00725 }
00726
00727 if (!found)
00728 return 0;
00729
00730 uint continueIndent = (needContinue) ? calcContinue (cur, begin) : 0;
00731
00732
00733
00734
00735 textLine = doc->plainKateTextLine(cur.line());
00736 QChar lastChar = textLine->getChar (anchorPos);
00737 int lastLine = cur.line();
00738 if (lastChar == '#' || lastChar == '[')
00739 {
00740
00741
00742 continueIndent = 0;
00743 }
00744
00745 int openCount = 0;
00746 while (cur.validPosition() && cur < begin)
00747 {
00748 if (!skipBlanks(cur, begin, true))
00749 return 0;
00750
00751 QChar tc = cur.currentChar();
00752
00753 if (cur == begin || tc.isNull())
00754 break;
00755
00756 if (!tc.isSpace() && cur < begin)
00757 {
00758 uchar attrib = cur.currentAttrib();
00759 if (tc == '{' && attrib == symbolAttrib)
00760 openCount++;
00761 else if (tc == '}' && attrib == symbolAttrib)
00762 openCount--;
00763
00764 lastChar = tc;
00765 lastLine = cur.line();
00766 }
00767 }
00768 if (openCount > 0)
00769 lastChar = '{';
00770
00771 uint indent = 0;
00772
00773
00774 if (lastChar == '{' || (lastChar == ':' && isSpecial && needContinue))
00775 {
00776 indent = anchorIndent + indentWidth;
00777 }
00778 else if (lastChar == '}')
00779 {
00780 indent = anchorIndent;
00781 }
00782 else if (lastChar == ';')
00783 {
00784 indent = anchorIndent + ((allowSemi && needContinue) ? continueIndent : 0);
00785 }
00786 else if (lastChar == ',')
00787 {
00788 textLine = doc->plainKateTextLine(lastLine);
00789 KateDocCursor start(lastLine, textLine->firstChar(), doc);
00790 KateDocCursor finish(lastLine, textLine->lastChar(), doc);
00791 uint pos = 0;
00792
00793 if (isBalanced(start, finish, QChar('('), QChar(')'), pos))
00794 indent = anchorIndent;
00795 else
00796 {
00797
00798 indent = ((pos < 48) ? pos : anchorIndent + (indentWidth * 2));
00799 }
00800 }
00801 else if (!lastChar.isNull())
00802 {
00803 if (anchorIndent != 0)
00804 indent = anchorIndent + continueIndent;
00805 else
00806 indent = continueIndent;
00807 }
00808
00809 return indent;
00810 }
00811
00812 uint KateCSmartIndent::calcContinue(KateDocCursor &start, KateDocCursor &end)
00813 {
00814 KateDocCursor cur = start;
00815
00816 bool needsBalanced = true;
00817 bool isFor = false;
00818 allowSemi = false;
00819
00820 KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
00821
00822
00823 if (textLine->attribute(cur.col()) == symbolAttrib)
00824 {
00825 cur.moveForward(1);
00826 skipBlanks(cur, end, false);
00827 }
00828
00829 if (textLine->getChar(cur.col()) == '}')
00830 {
00831 skipBlanks(cur, end, true);
00832 if (cur.line() != start.line())
00833 textLine = doc->plainKateTextLine(cur.line());
00834
00835 if (textLine->stringAtPos(cur.col(), "else"))
00836 cur.setCol(cur.col() + 4);
00837 else
00838 return indentWidth * 2;
00839
00840 needsBalanced = false;
00841 }
00842 else if (textLine->stringAtPos(cur.col(), "else"))
00843 {
00844 cur.setCol(cur.col() + 4);
00845 needsBalanced = false;
00846 if (textLine->stringAtPos(textLine->nextNonSpaceChar(cur.col()), "if"))
00847 {
00848 cur.setCol(textLine->nextNonSpaceChar(cur.col()) + 2);
00849 needsBalanced = true;
00850 }
00851 }
00852 else if (textLine->stringAtPos(cur.col(), "if"))
00853 {
00854 cur.setCol(cur.col() + 2);
00855 }
00856 else if (textLine->stringAtPos(cur.col(), "do"))
00857 {
00858 cur.setCol(cur.col() + 2);
00859 needsBalanced = false;
00860 }
00861 else if (textLine->stringAtPos(cur.col(), "for"))
00862 {
00863 cur.setCol(cur.col() + 3);
00864 isFor = true;
00865 }
00866 else if (textLine->stringAtPos(cur.col(), "while"))
00867 {
00868 cur.setCol(cur.col() + 5);
00869 }
00870 else if (textLine->stringAtPos(cur.col(), "switch"))
00871 {
00872 cur.setCol(cur.col() + 6);
00873 }
00874 else if (textLine->stringAtPos(cur.col(), "using"))
00875 {
00876 cur.setCol(cur.col() + 5);
00877 }
00878 else
00879 {
00880 return indentWidth * 2;
00881 }
00882
00883 uint openPos = 0;
00884 if (needsBalanced && !isBalanced (cur, end, QChar('('), QChar(')'), openPos))
00885 {
00886 allowSemi = isFor;
00887 if (openPos > 0)
00888 return (openPos - textLine->firstChar());
00889 else
00890 return indentWidth * 2;
00891 }
00892
00893
00894 skipBlanks(cur, end, false);
00895 if (cur == end)
00896 return indentWidth;
00897
00898 if (skipBlanks(cur, end, true))
00899 {
00900 if (cur == end)
00901 return indentWidth;
00902 else
00903 return indentWidth + calcContinue(cur, end);
00904 }
00905
00906 return 0;
00907 }
00908
00909 uint KateCSmartIndent::findOpeningBrace(KateDocCursor &start)
00910 {
00911 KateDocCursor cur = start;
00912 int count = 1;
00913
00914
00915
00916 while (cur.moveBackward(1))
00917 {
00918 if (cur.currentAttrib() == symbolAttrib)
00919 {
00920 QChar ch = cur.currentChar();
00921 if (ch == '{')
00922 count--;
00923 else if (ch == '}')
00924 count++;
00925
00926 if (count == 0)
00927 {
00928 KateDocCursor temp(cur.line(), doc->plainKateTextLine(cur.line())->firstChar(), doc);
00929 return measureIndent(temp);
00930 }
00931 }
00932 }
00933
00934 return 0;
00935 }
00936
00937 bool KateCSmartIndent::firstOpeningBrace(KateDocCursor &start)
00938 {
00939 KateDocCursor cur = start;
00940
00941
00942 while(cur.moveBackward(1))
00943 {
00944 if (cur.currentAttrib() == symbolAttrib)
00945 {
00946 QChar ch = cur.currentChar();
00947 if (ch == '{')
00948 return false;
00949 else if (ch == '}' && cur.col() == 0)
00950 break;
00951 }
00952 }
00953
00954 return true;
00955 }
00956
00957 uint KateCSmartIndent::findOpeningParen(KateDocCursor &start)
00958 {
00959 KateDocCursor cur = start;
00960 int count = 1;
00961
00962
00963
00964 while (cur.moveBackward(1))
00965 {
00966 if (cur.currentAttrib() == symbolAttrib)
00967 {
00968 QChar ch = cur.currentChar();
00969 if (ch == '(')
00970 count--;
00971 else if (ch == ')')
00972 count++;
00973
00974 if (count == 0)
00975 return measureIndent(cur);
00976 }
00977 }
00978
00979 return 0;
00980 }
00981
00982 uint KateCSmartIndent::findOpeningComment(KateDocCursor &start)
00983 {
00984 KateDocCursor cur = start;
00985
00986
00987 do
00988 {
00989 KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
00990
00991 int pos = textLine->string().find("/*", false);
00992 if (pos >= 0)
00993 {
00994 KateDocCursor temp(cur.line(), pos, doc);
00995 return measureIndent(temp);
00996 }
00997
00998 } while (cur.gotoPreviousLine());
00999
01000 return 0;
01001 }
01002
01003
01004
01005
01006
01007 QRegExp KatePythonIndent::endWithColon = QRegExp( "^[^#]*:\\s*(#.*)?$" );
01008 QRegExp KatePythonIndent::stopStmt = QRegExp( "^\\s*(break|continue|raise|return|pass)\\b.*" );
01009 QRegExp KatePythonIndent::blockBegin = QRegExp( "^\\s*(def|if|elif|else|for|while|try)\\b.*" );
01010
01011 KatePythonIndent::KatePythonIndent (KateDocument *doc)
01012 : KateNormalIndent (doc)
01013 {
01014 }
01015 KatePythonIndent::~KatePythonIndent ()
01016 {
01017 }
01018
01019 void KatePythonIndent::processNewline (KateDocCursor &begin, bool )
01020 {
01021 int prevLine = begin.line() - 1;
01022 int prevPos = begin.col();
01023
01024 while ((prevLine > 0) && (prevPos < 0))
01025 prevPos = doc->plainKateTextLine(--prevLine)->firstChar();
01026
01027 int prevBlock = prevLine;
01028 int prevBlockPos = prevPos;
01029 int extraIndent = calcExtra (prevBlock, prevBlockPos, begin);
01030
01031 int indent = doc->plainKateTextLine(prevBlock)->cursorX(prevBlockPos, tabWidth);
01032 if (extraIndent == 0)
01033 {
01034 if (!stopStmt.exactMatch(doc->plainKateTextLine(prevLine)->string()))
01035 {
01036 if (endWithColon.exactMatch(doc->plainKateTextLine(prevLine)->string()))
01037 indent += indentWidth;
01038 else
01039 indent = doc->plainKateTextLine(prevLine)->cursorX(prevPos, tabWidth);
01040 }
01041 }
01042 else
01043 indent += extraIndent;
01044
01045 if (indent > 0)
01046 {
01047 QString filler = tabString (indent);
01048 doc->insertText (begin.line(), 0, filler);
01049 begin.setCol(filler.length());
01050 }
01051 else
01052 begin.setCol(0);
01053 }
01054
01055 int KatePythonIndent::calcExtra (int &prevBlock, int &pos, KateDocCursor &end)
01056 {
01057 int nestLevel = 0;
01058 bool levelFound = false;
01059 while ((prevBlock > 0))
01060 {
01061 if (blockBegin.exactMatch(doc->plainKateTextLine(prevBlock)->string()))
01062 {
01063 if ((!levelFound && nestLevel == 0) || (levelFound && nestLevel - 1 <= 0))
01064 {
01065 pos = doc->plainKateTextLine(prevBlock)->firstChar();
01066 break;
01067 }
01068
01069 nestLevel --;
01070 }
01071 else if (stopStmt.exactMatch(doc->plainKateTextLine(prevBlock)->string()))
01072 {
01073 nestLevel ++;
01074 levelFound = true;
01075 }
01076
01077 --prevBlock;
01078 }
01079
01080 KateDocCursor cur (prevBlock, pos, doc);
01081 QChar c;
01082 int extraIndent = 0;
01083 while (cur.line() < end.line())
01084 {
01085 c = cur.currentChar();
01086
01087 if (c == '(')
01088 extraIndent += indentWidth;
01089 else if (c == ')')
01090 extraIndent -= indentWidth;
01091 else if (c == ':')
01092 break;
01093
01094 if (c.isNull() || c == '#')
01095 cur.gotoNextLine();
01096 else
01097 cur.moveForward(1);
01098 }
01099
01100 return extraIndent;
01101 }
01102
01103
01104
01105
01106
01107
01108
01109
01110
01111
01112
01113
01114
01115
01116
01117
01118
01119
01120
01121
01122
01123
01124
01125
01126
01127
01128
01129 const QRegExp KateXmlIndent::startsWithCloseTag("^[ \t]*</");
01130 const QRegExp KateXmlIndent::unclosedDoctype("<!DOCTYPE[^>]*$");
01131
01132 KateXmlIndent::KateXmlIndent (KateDocument *doc)
01133 : KateNormalIndent (doc)
01134 {
01135 }
01136
01137 KateXmlIndent::~KateXmlIndent ()
01138 {
01139 }
01140
01141 void KateXmlIndent::processNewline (KateDocCursor &begin, bool )
01142 {
01143 begin.setCol(processLine(begin.line()));
01144 }
01145
01146 void KateXmlIndent::processChar (QChar c)
01147 {
01148 if(c != '/') return;
01149
01150
01151 KateView *view = doc->activeView();
01152 QString text = doc->plainKateTextLine(view->cursorLine())->string();
01153 if(text.find(startsWithCloseTag) == -1) return;
01154
01155
01156 processLine(view->cursorLine());
01157 }
01158
01159 void KateXmlIndent::processLine (KateDocCursor &line)
01160 {
01161 processLine (line.line());
01162 }
01163
01164 void KateXmlIndent::processSection (KateDocCursor &cur, KateDocCursor &end)
01165 {
01166 int endLine = end.line();
01167 do {
01168 processLine(cur.line());
01169 if(!cur.gotoNextLine()) break;
01170 } while(cur.line() < endLine);
01171 }
01172
01173 void KateXmlIndent::getLineInfo (uint line, uint &prevIndent, int &numTags,
01174 uint &attrCol, bool &unclosedTag)
01175 {
01176 prevIndent = 0;
01177 int firstChar;
01178 KateTextLine::Ptr prevLine = 0;
01179
01180
01181 while(true) {
01182 prevLine = doc->plainKateTextLine(line);
01183 if( (firstChar = prevLine->firstChar()) < 0) {
01184 if(!line--) return;
01185 continue;
01186 }
01187 break;
01188 }
01189 prevIndent = prevLine->cursorX(prevLine->firstChar(), tabWidth);
01190 QString text = prevLine->string();
01191
01192
01193
01194
01195
01196 if(text.find(startsWithCloseTag) != -1) ++numTags;
01197
01198
01199 int lastCh = 0;
01200 uint pos, len = text.length();
01201 bool seenOpen = false;
01202 for(pos = 0; pos < len; ++pos) {
01203 int ch = text.at(pos).unicode();
01204 switch(ch) {
01205 case '<':
01206 seenOpen = true;
01207 unclosedTag = true;
01208 attrCol = pos;
01209 ++numTags;
01210 break;
01211
01212
01213 case '!':
01214 if(lastCh == '<') --numTags;
01215 break;
01216
01217
01218 case '?':
01219 if(lastCh == '<') --numTags;
01220 break;
01221
01222 case '>':
01223 if(!seenOpen) {
01224
01225
01226
01227
01228
01229
01230
01231
01232 prevIndent = 0;
01233
01234 for(uint backLine = line; backLine; ) {
01235
01236 KateTextLine::Ptr x = doc->plainKateTextLine(--backLine);
01237 if(x->string().find('<') == -1) continue;
01238
01239
01240 if(x->string().find(unclosedDoctype) != -1) --numTags;
01241 getLineInfo(backLine, prevIndent, numTags, attrCol, unclosedTag);
01242 break;
01243 }
01244 }
01245 if(lastCh == '/') --numTags;
01246 unclosedTag = false;
01247 break;
01248
01249 case '/':
01250 if(lastCh == '<') numTags -= 2;
01251 break;
01252 }
01253 lastCh = ch;
01254 }
01255
01256 if(unclosedTag) {
01257
01258 do {
01259 lastCh = text.at(++attrCol).unicode();
01260 }while(lastCh && lastCh != ' ' && lastCh != '\t');
01261
01262 while(lastCh == ' ' || lastCh == '\t') {
01263 lastCh = text.at(++attrCol).unicode();
01264 }
01265
01266 attrCol = prevLine->cursorX(attrCol, tabWidth);
01267 }
01268 }
01269
01270 uint KateXmlIndent::processLine (uint line)
01271 {
01272 KateTextLine::Ptr kateLine = doc->plainKateTextLine(line);
01273 if(!kateLine) return 0;
01274
01275
01276 uint prevIndent = 0, attrCol = 0;
01277 int numTags = 0;
01278 bool unclosedTag = false;
01279
01280 if(line) {
01281 getLineInfo(line - 1, prevIndent, numTags, attrCol, unclosedTag);
01282 }
01283
01284
01285 int indent = 0;
01286 if(unclosedTag) indent = attrCol;
01287 else indent = prevIndent + numTags * indentWidth;
01288 if(indent < 0) indent = 0;
01289
01290
01291 if(kateLine->string().find(startsWithCloseTag) != -1) {
01292 indent -= indentWidth;
01293 }
01294 if(indent < 0) indent = 0;
01295
01296
01297 doc->removeText(line, 0, line, kateLine->firstChar());
01298 QString filler = tabString(indent);
01299 doc->insertText(line, 0, filler);
01300
01301 return filler.length();
01302 }
01303
01304
01305
01306
01307
01308 KateCSAndSIndent::KateCSAndSIndent (KateDocument *doc)
01309 : KateNormalIndent (doc)
01310 {
01311 }
01312
01313 void KateCSAndSIndent::updateIndentString()
01314 {
01315 if( useSpaces )
01316 indentString.fill( ' ', indentWidth );
01317 else
01318 indentString = '\t';
01319 }
01320
01321 KateCSAndSIndent::~KateCSAndSIndent ()
01322 {
01323 }
01324
01325 void KateCSAndSIndent::processLine (KateDocCursor &line)
01326 {
01327 KateTextLine::Ptr textLine = doc->plainKateTextLine(line.line());
01328
01329 if (!textLine)
01330 return;
01331
01332 updateIndentString();
01333
01334 const int oldCol = line.col();
01335 QString whitespace = calcIndent(line);
01336
01337 int oldIndent = textLine->firstChar();
01338 if ( oldIndent < 0 )
01339 oldIndent = doc->lineLength( line.line() );
01340 if( oldIndent > 0 )
01341 doc->removeText(line.line(), 0, line.line(), oldIndent);
01342
01343 doc->insertText(line.line(), 0, whitespace);
01344
01345
01346 if ( int(oldCol + whitespace.length()) >= oldIndent )
01347 line.setCol( oldCol + whitespace.length() - oldIndent );
01348 else
01349 line.setCol( 0 );
01350 }
01351
01352 void KateCSAndSIndent::processSection (KateDocCursor &begin, KateDocCursor &end)
01353 {
01354 QTime t; t.start();
01355 for( KateDocCursor cur = begin; cur.line() <= end.line(); )
01356 {
01357 processLine (cur);
01358 if (!cur.gotoNextLine())
01359 break;
01360 }
01361 kdDebug(13030) << "+++ total: " << t.elapsed() << endl;
01362 }
01363
01369 static QString initialWhitespace(const KateTextLine::Ptr &line, int chars, bool convert = true)
01370 {
01371 QString text = line->string(0, chars);
01372 if( (int)text.length() < chars )
01373 {
01374 QString filler; filler.fill(' ',chars - text.length());
01375 text += filler;
01376 }
01377 for( uint n = 0; n < text.length(); ++n )
01378 {
01379 if( text[n] != '\t' && text[n] != ' ' )
01380 {
01381 if( !convert )
01382 return text.left( n );
01383 text[n] = ' ';
01384 }
01385 }
01386 return text;
01387 }
01388
01389 QString KateCSAndSIndent::findOpeningCommentIndentation(const KateDocCursor &start)
01390 {
01391 KateDocCursor cur = start;
01392
01393
01394 do
01395 {
01396 KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
01397
01398 int pos = textLine->string().findRev("/*");
01399
01400 if (pos >= 0)
01401 return initialWhitespace(textLine, pos);
01402 } while (cur.gotoPreviousLine());
01403
01404
01405 kdWarning( 13030 ) << " in a comment, but can't find the start of it" << endl;
01406 return QString::null;
01407 }
01408
01409 bool KateCSAndSIndent::handleDoxygen (KateDocCursor &begin)
01410 {
01411
01412 int line = begin.line();
01413 int first = -1;
01414 while ((line > 0) && (first < 0))
01415 first = doc->plainKateTextLine(--line)->firstChar();
01416
01417
01418 if (first < 0)
01419 return false;
01420
01421 KateTextLine::Ptr textLine = doc->plainKateTextLine(line);
01422
01423
01424
01425
01426
01427 if ( !(textLine->attribute(textLine->lastChar()) == doxyCommentAttrib && !textLine->endingWith("*/")) &&
01428 !(textLine->attribute(textLine->firstChar()) == doxyCommentAttrib && !textLine->string().contains("*/")) )
01429 return false;
01430
01431
01432 textLine = doc->plainKateTextLine(begin.line());
01433 first = textLine->firstChar();
01434 QString indent = findOpeningCommentIndentation(begin);
01435
01436 bool doxygenAutoInsert = doc->config()->configFlags() & KateDocumentConfig::cfDoxygenAutoTyping;
01437
01438
01439 if ( textLine->stringAtPos(first, "*") )
01440 indent = indent + " ";
01441
01442 else if ( doxygenAutoInsert )
01443 indent = indent + " * ";
01444
01445
01446
01447
01448 doc->removeText (begin.line(), 0, begin.line(), first);
01449 doc->insertText (begin.line(), 0, indent);
01450 begin.setCol(indent.length());
01451
01452 return true;
01453 }
01454
01461 void KateCSAndSIndent::processNewline (KateDocCursor &begin, bool )
01462 {
01463
01464 if( handleDoxygen(begin) )
01465 return;
01466
01467
01468
01469
01470
01471 int cursorPos = doc->plainKateTextLine( begin.line() )->firstChar();
01472 if ( cursorPos < 0 )
01473 cursorPos = doc->lineLength( begin.line() );
01474 begin.setCol( cursorPos );
01475
01476 processLine( begin );
01477 }
01478
01483 bool KateCSAndSIndent::startsWithLabel( int line )
01484 {
01485 KateTextLine::Ptr indentLine = doc->plainKateTextLine( line );
01486 const int indentFirst = indentLine->firstChar();
01487
01488 int attrib = indentLine->attribute(indentFirst);
01489 if (attrib != 0 && attrib != keywordAttrib && attrib != normalAttrib && attrib != extensionAttrib)
01490 return false;
01491
01492 const QString lineContents = indentLine->string();
01493 static const QString symbols = QString::fromLatin1(";:[]{}");
01494 const int last = indentLine->lastChar();
01495 for ( int n = indentFirst + 1; n <= last; ++n )
01496 {
01497 QChar c = lineContents[n];
01498
01499 if ( !symbols.contains(c) )
01500 continue;
01501
01502
01503 if ( c != ':' )
01504 return false;
01505
01506
01507 if ( lineContents[n+1] != ':' )
01508 return true;
01509
01510
01511
01512 if ( lineContents[n+2] != ':' )
01513 {
01514 ++n;
01515 continue;
01516 }
01517
01518
01519
01520 return true;
01521 }
01522 return false;
01523 }
01524
01525 template<class T> T min(T a, T b) { return (a < b) ? a : b; }
01526
01527 int KateCSAndSIndent::lastNonCommentChar( const KateDocCursor &line )
01528 {
01529 KateTextLine::Ptr textLine = doc->plainKateTextLine( line.line() );
01530 QString str = textLine->string();
01531
01532
01533 int p = -2;
01534 do p = str.find( "//", p + 2 );
01535 while ( p >= 0 && textLine->attribute(p) != commentAttrib && textLine->attribute(p) != doxyCommentAttrib );
01536
01537
01538 if ( p < 0 )
01539 p = str.length();
01540
01541
01542 while( p > 0 && str[p-1].isSpace() ) --p;
01543 return p - 1;
01544 }
01545
01546 bool KateCSAndSIndent::inForStatement( int line )
01547 {
01548
01549
01550 int parens = 0, semicolons = 0;
01551 for ( ; line >= 0; --line )
01552 {
01553 KateTextLine::Ptr textLine = doc->plainKateTextLine(line);
01554 const int first = textLine->firstChar();
01555 const int last = textLine->lastChar();
01556
01557
01558
01559
01560
01561 for ( int curr = last; curr >= first; --curr )
01562 {
01563 if ( textLine->attribute(curr) != symbolAttrib )
01564 continue;
01565
01566 switch( textLine->getChar(curr) )
01567 {
01568 case ';':
01569 if( ++semicolons > 2 )
01570 return false;
01571 break;
01572 case '{': case '}':
01573 return false;
01574 case ')':
01575 ++parens;
01576 break;
01577 case '(':
01578 if( --parens < 0 )
01579 return true;
01580 break;
01581 }
01582 }
01583 }
01584
01585
01586 return false;
01587 }
01588
01589
01590
01591 bool KateCSAndSIndent::inStatement( const KateDocCursor &begin )
01592 {
01593
01594
01595 KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
01596 const int first = textLine->firstChar();
01597
01598
01599
01600 const int attrib = textLine->attribute(first);
01601 if( first >= 0 && (attrib == 0 || attrib == symbolAttrib) && textLine->getChar(first) == '{' )
01602 return false;
01603
01604 int line;
01605 for ( line = begin.line() - 1; line >= 0; --line )
01606 {
01607 textLine = doc->plainKateTextLine(line);
01608 const int first = textLine->firstChar();
01609 if ( first == -1 )
01610 continue;
01611
01612
01613
01614 if ( textLine->getChar( first ) == '#' )
01615 continue;
01616 KateDocCursor currLine = begin;
01617 currLine.setLine( line );
01618 const int last = lastNonCommentChar( currLine );
01619 if ( last < first )
01620 continue;
01621
01622
01623
01624
01625
01626
01627 const int attrib = textLine->attribute(last);
01628 if ( attrib == commentAttrib || attrib == doxyCommentAttrib )
01629 return false;
01630
01631 char c = textLine->getChar(last);
01632
01633
01634 if ( attrib == symbolAttrib && c == '{' || c == '}' )
01635 return false;
01636
01637
01638 if ( attrib == symbolAttrib && c == ';' )
01639 return inForStatement( line );
01640
01641
01642 if ( attrib == symbolAttrib && c == ':' )
01643 {
01644
01645
01646
01647
01648 if( startsWithLabel( line ) )
01649 {
01650
01651
01652
01653
01654 continue;
01655 }
01656 }
01657
01658
01659 return true;
01660 }
01661
01662 return false;
01663 }
01664
01665 QString KateCSAndSIndent::continuationIndent( const KateDocCursor &begin )
01666 {
01667 if( !inStatement( begin ) )
01668 return QString::null;
01669 return indentString;
01670 }
01671
01675 QString KateCSAndSIndent::calcIndent (const KateDocCursor &begin)
01676 {
01677 KateTextLine::Ptr currLine = doc->plainKateTextLine(begin.line());
01678 int currLineFirst = currLine->firstChar();
01679
01680
01681
01682
01683 if ( currLineFirst >= 0 &&
01684 (currLine->attribute(currLineFirst) == commentAttrib ||
01685 currLine->attribute(currLineFirst) == doxyCommentAttrib) )
01686 return currLine->string( 0, currLineFirst );
01687
01688
01689 if( currLineFirst >= 0 && currLine->getChar(currLineFirst) == '#' )
01690 {
01691 if( !currLine->stringAtPos( currLineFirst+1, QString::fromLatin1("region") ) &&
01692 !currLine->stringAtPos( currLineFirst+1, QString::fromLatin1("endregion") ) )
01693 return QString::null;
01694 }
01695
01696
01697
01698
01699
01700
01701
01702
01703 KateDocCursor cur = begin;
01704 int pos, openBraceCount = 0, openParenCount = 0;
01705 bool lookingForScopeKeywords = true;
01706 const char * const scopeKeywords[] = { "for", "do", "while", "if", "else" };
01707 const char * const blockScopeKeywords[] = { "try", "catch", "switch" };
01708
01709 while (cur.gotoPreviousLine())
01710 {
01711 KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
01712 const int lastChar = textLine->lastChar();
01713 const int firstChar = textLine->firstChar();
01714
01715
01716 for( pos = lastChar; pos >= firstChar; --pos )
01717 {
01718 if (textLine->attribute(pos) == symbolAttrib)
01719 {
01720 char tc = textLine->getChar (pos);
01721 switch( tc )
01722 {
01723 case '(': case '[':
01724 if( ++openParenCount > 0 )
01725 return calcIndentInBracket( begin, cur, pos );
01726 break;
01727 case ')': case ']': openParenCount--; break;
01728 case '{':
01729 if( ++openBraceCount > 0 )
01730 return calcIndentInBrace( begin, cur, pos );
01731 break;
01732 case '}': openBraceCount--; lookingForScopeKeywords = false; break;
01733 case ';':
01734 if( openParenCount == 0 )
01735 lookingForScopeKeywords = false;
01736 break;
01737 }
01738 }
01739
01740
01741
01742 if ( lookingForScopeKeywords && openParenCount == 0 &&
01743 textLine->attribute(pos) == keywordAttrib &&
01744 (pos == 0 || textLine->attribute(pos-1) != keywordAttrib ) )
01745 {
01746 #define ARRLEN( array ) ( sizeof(array)/sizeof(array[0]) )
01747 for( uint n = 0; n < ARRLEN(scopeKeywords); ++n )
01748 if( textLine->stringAtPos(pos, QString::fromLatin1(scopeKeywords[n]) ) )
01749 return calcIndentAfterKeyword( begin, cur, pos, false );
01750 for( uint n = 0; n < ARRLEN(blockScopeKeywords); ++n )
01751 if( textLine->stringAtPos(pos, QString::fromLatin1(blockScopeKeywords[n]) ) )
01752 return calcIndentAfterKeyword( begin, cur, pos, true );
01753 #undef ARRLEN
01754 }
01755 }
01756 }
01757
01758
01759 return QString::null;
01760 }
01761
01762 QString KateCSAndSIndent::calcIndentInBracket(const KateDocCursor &indentCursor, const KateDocCursor &bracketCursor, int bracketPos)
01763 {
01764 KateTextLine::Ptr indentLine = doc->plainKateTextLine(indentCursor.line());
01765 KateTextLine::Ptr bracketLine = doc->plainKateTextLine(bracketCursor.line());
01766
01767
01768
01769 if ( bracketPos > 48 )
01770 {
01771
01772
01773
01774
01775
01776
01777
01778
01779
01780
01781 return indentString + initialWhitespace( bracketLine, bracketLine->firstChar() );
01782 }
01783
01784 const int indentLineFirst = indentLine->firstChar();
01785
01786 int indentTo;
01787 const int attrib = indentLine->attribute(indentLineFirst);
01788 if( indentLineFirst >= 0 && (attrib == 0 || attrib == symbolAttrib) &&
01789 ( indentLine->getChar(indentLineFirst) == ')' || indentLine->getChar(indentLineFirst) == ']' ) )
01790 {
01791
01792 indentTo = bracketPos;
01793 }
01794 else
01795 {
01796
01797 indentTo = bracketLine->nextNonSpaceChar( bracketPos + 1 );
01798 if( indentTo == -1 )
01799 indentTo = bracketPos + 2;
01800 }
01801 return initialWhitespace( bracketLine, indentTo );
01802 }
01803
01804 QString KateCSAndSIndent::calcIndentAfterKeyword(const KateDocCursor &indentCursor, const KateDocCursor &keywordCursor, int keywordPos, bool blockKeyword)
01805 {
01806 KateTextLine::Ptr keywordLine = doc->plainKateTextLine(keywordCursor.line());
01807 KateTextLine::Ptr indentLine = doc->plainKateTextLine(indentCursor.line());
01808
01809 QString whitespaceToKeyword = initialWhitespace( keywordLine, keywordPos, false );
01810 if( blockKeyword )
01811 ;
01812
01813
01814 int first = indentLine->firstChar();
01815
01816 const int attrib = indentLine->attribute(first);
01817 if( first >= 0 && (attrib == 0 || attrib == symbolAttrib) && indentLine->getChar(first) == '{' )
01818 return whitespaceToKeyword;
01819
01820
01821
01822
01823
01824
01825
01826
01827 return indentString + whitespaceToKeyword;
01828 }
01829
01830 QString KateCSAndSIndent::calcIndentInBrace(const KateDocCursor &indentCursor, const KateDocCursor &braceCursor, int bracePos)
01831 {
01832 KateTextLine::Ptr braceLine = doc->plainKateTextLine(braceCursor.line());
01833 const int braceFirst = braceLine->firstChar();
01834
01835 QString whitespaceToOpenBrace = initialWhitespace( braceLine, bracePos, false );
01836
01837
01838
01839
01840
01841 {
01842 if( braceFirst >= 0 && braceLine->attribute(braceFirst) == keywordAttrib &&
01843 braceLine->stringAtPos( braceFirst, QString::fromLatin1( "namespace" ) ) )
01844 return continuationIndent(indentCursor) + whitespaceToOpenBrace;
01845
01846 if( braceCursor.line() > 0 )
01847 {
01848 KateTextLine::Ptr prevLine = doc->plainKateTextLine(braceCursor.line() - 1);
01849 int firstPrev = prevLine->firstChar();
01850 if( firstPrev >= 0 && prevLine->attribute(firstPrev) == keywordAttrib &&
01851 prevLine->stringAtPos( firstPrev, QString::fromLatin1( "namespace" ) ) )
01852 return continuationIndent(indentCursor) + whitespaceToOpenBrace;
01853 }
01854 }
01855
01856 KateTextLine::Ptr indentLine = doc->plainKateTextLine(indentCursor.line());
01857 const int indentFirst = indentLine->firstChar();
01858
01859
01860 if( indentFirst >= 0 && indentLine->getChar(indentFirst) == '}' )
01861 return whitespaceToOpenBrace;
01862
01863
01864
01865 if ( indentFirst >= 0 && indentLine->attribute(indentFirst) == symbolAttrib &&
01866 indentLine->getChar(indentFirst) == ':' && indentLine->getChar(indentFirst+1) != ':' )
01867 {
01868 return indentString + indentString + whitespaceToOpenBrace;
01869 }
01870
01871 const bool continuation = inStatement(indentCursor);
01872
01873 if( !continuation && startsWithLabel( indentCursor.line() ) )
01874 return whitespaceToOpenBrace;
01875
01876
01877 QString continuationIndent = continuation ? indentString : QString::null;
01878 return indentString + continuationIndent + whitespaceToOpenBrace;
01879 }
01880
01881 void KateCSAndSIndent::processChar(QChar c)
01882 {
01883
01884 static const QString triggers("}{)]/:;#n");
01885 if (triggers.find(c) == -1)
01886 return;
01887
01888
01889
01890 KateView *view = doc->activeView();
01891 KateDocCursor begin(view->cursorLine(), 0, doc);
01892
01893 KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
01894 if ( c == 'n' )
01895 {
01896 int first = textLine->firstChar();
01897 if( first < 0 || textLine->getChar(first) != '#' )
01898 return;
01899 }
01900
01901
01902 if ( textLine->attribute( begin.col() ) == doxyCommentAttrib )
01903 return;
01904
01905 processLine(begin);
01906 }
01907
01908
01909
01910
01911 class KateVarIndentPrivate {
01912 public:
01913 QRegExp reIndentAfter, reIndent, reUnindent;
01914 QString triggers;
01915 uint couples;
01916 uchar coupleAttrib;
01917 };
01918
01919 KateVarIndent::KateVarIndent( KateDocument *doc )
01920 : QObject( 0, "variable indenter"), KateNormalIndent( doc )
01921 {
01922 d = new KateVarIndentPrivate;
01923 d->reIndentAfter = QRegExp( doc->variable( "var-indent-indent-after" ) );
01924 d->reIndent = QRegExp( doc->variable( "var-indent-indent" ) );
01925 d->reUnindent = QRegExp( doc->variable( "var-indent-unindent" ) );
01926 d->triggers = doc->variable( "var-indent-triggerchars" );
01927 d->coupleAttrib = 0;
01928
01929 slotVariableChanged( "var-indent-couple-attribute", doc->variable( "var-indent-couple-attribute" ) );
01930 slotVariableChanged( "var-indent-handle-couples", doc->variable( "var-indent-handle-couples" ) );
01931
01932
01933 connect( doc, SIGNAL(variableChanged( const QString&, const QString&) ),
01934 this, SLOT(slotVariableChanged( const QString&, const QString& )) );
01935 }
01936
01937 KateVarIndent::~KateVarIndent()
01938 {
01939 delete d;
01940 }
01941
01942 void KateVarIndent::processNewline ( KateDocCursor &begin, bool )
01943 {
01944
01945 KateDocCursor left( begin.line()-1, 0, doc );
01946 processLine( left );
01947 processLine( begin );
01948 }
01949
01950 void KateVarIndent::processChar ( QChar c )
01951 {
01952
01953 if ( d->triggers.contains( c ) )
01954 {
01955 KateTextLine::Ptr ln = doc->plainKateTextLine( doc->activeView()->cursorLine() );
01956 if ( ln->attribute( doc->activeView()->cursorColumn()-1 ) == commentAttrib )
01957 return;
01958
01959 KateView *view = doc->activeView();
01960 KateDocCursor begin( view->cursorLine(), 0, doc );
01961 kdDebug(13030)<<"variable indenter: process char '"<<c<<", line "<<begin.line()<<endl;
01962 processLine( begin );
01963 }
01964 }
01965
01966 void KateVarIndent::processLine ( KateDocCursor &line )
01967 {
01968 updateConfig();
01969
01970 QString indent;
01971
01972
01973
01974 int ln = line.line();
01975 int pos = -1;
01976 KateTextLine::Ptr ktl = doc->plainKateTextLine( ln );
01977 if ( ! ktl ) return;
01978
01979
01980 KateView *v = doc->activeView();
01981 if ( ktl->firstChar() < 0 && (!v || v->cursorLine() != ln ) )
01982 return;
01983
01984 int fc;
01985 if ( ln > 0 )
01986 do
01987 {
01988
01989 ktl = doc->plainKateTextLine( --ln );
01990 fc = ktl->firstChar();
01991 if ( ktl->attribute( fc ) != commentAttrib )
01992 pos = fc;
01993 }
01994 while ( (ln > 0) && (pos < 0) );
01995
01996 if ( pos < 0 )
01997 pos = 0;
01998 else
01999 pos = ktl->cursorX( pos, tabWidth );
02000
02001 int adjustment = 0;
02002
02003
02004
02005 if ( d->couples & Parens && coupleBalance( ln, '(', ')' ) > 0 )
02006 adjustment++;
02007 else if ( d->couples & Braces && coupleBalance( ln, '{', '}' ) > 0 )
02008 adjustment++;
02009 else if ( d->couples & Brackets && coupleBalance( ln, '[', ']' ) > 0 )
02010 adjustment++;
02011
02012
02013
02014
02015
02016
02017
02018
02019
02020
02021
02022 {
02023 KateTextLine::Ptr tl = doc->plainKateTextLine( line.line() );
02024 int i = tl->firstChar();
02025 if ( i > -1 )
02026 {
02027 QChar ch = tl->getChar( i );
02028 uchar at = tl->attribute( i );
02029 kdDebug(13030)<<"attrib is "<<at<<endl;
02030 if ( d->couples & Parens && ch == ')'
02031 && ( at == d->coupleAttrib
02032 || (! at && hasRelevantOpening( KateDocCursor( line.line(), i, doc ) ))
02033 )
02034 )
02035 adjustment--;
02036 else if ( d->couples & Braces && ch == '}'
02037 && ( at == d->coupleAttrib
02038 || (! at && hasRelevantOpening( KateDocCursor( line.line(), i, doc ) ))
02039 )
02040 )
02041 adjustment--;
02042 else if ( d->couples & Brackets && ch == ']'
02043 && ( at == d->coupleAttrib
02044 || (! at && hasRelevantOpening( KateDocCursor( line.line(), i, doc ) ))
02045 )
02046 )
02047 adjustment--;
02048 }
02049 }
02050 #define ISCOMMENTATTR(attr) (attr==commentAttrib||attr==doxyCommentAttrib)
02051 #define ISCOMMENT (ISCOMMENTATTR(ktl->attribute(ktl->firstChar()))||ISCOMMENTATTR(ktl->attribute(matchpos)))
02052
02053
02054 kdDebug(13030)<<"variable indenter: starting indent: "<<pos<<endl;
02055
02056 int matchpos = 0;
02057 if ( ktl && ! d->reIndentAfter.isEmpty()
02058 && (matchpos = d->reIndentAfter.search( doc->textLine( ln ) )) > -1
02059 && ! ISCOMMENT )
02060 adjustment++;
02061
02062
02063 ktl = doc->plainKateTextLine( line.line() );
02064 if ( ! d->reIndent.isEmpty()
02065 && (matchpos = d->reIndent.search( doc->textLine( line.line() ) )) > -1
02066 && ! ISCOMMENT )
02067 adjustment++;
02068
02069
02070 if ( ! d->reUnindent.isEmpty()
02071 && (matchpos = d->reUnindent.search( doc->textLine( line.line() ) )) > -1
02072 && ! ISCOMMENT )
02073 adjustment--;
02074
02075 kdDebug(13030)<<"variable indenter: adjusting by "<<adjustment<<" units"<<endl;
02076
02077 if ( adjustment > 0 )
02078 pos += indentWidth;
02079 else if ( adjustment < 0 )
02080 pos -= indentWidth;
02081
02082 ln = line.line();
02083 fc = doc->plainKateTextLine( ln )->firstChar();
02084
02085
02086
02087
02088
02089 if ( fc == pos )
02090 return;
02091
02092 if ( fc > 0 )
02093 doc->removeText (ln, 0, ln, fc );
02094
02095 if ( pos > 0 )
02096 indent = tabString( pos );
02097
02098 if ( pos > 0 )
02099 doc->insertText (ln, 0, indent);
02100
02101
02102 line.setCol( pos );
02103 }
02104
02105 void KateVarIndent::processSection (KateDocCursor &begin, KateDocCursor &end)
02106 {
02107 KateDocCursor cur = begin;
02108 while (cur.line() <= end.line())
02109 {
02110 processLine (cur);
02111 if (!cur.gotoNextLine())
02112 break;
02113 }
02114 }
02115
02116 void KateVarIndent::slotVariableChanged( const QString &var, const QString &val )
02117 {
02118 if ( ! var.startsWith("var-indent") )
02119 return;
02120
02121 if ( var == "var-indent-indent-after" )
02122 d->reIndentAfter.setPattern( val );
02123 else if ( var == "var-indent-indent" )
02124 d->reIndent.setPattern( val );
02125 else if ( var == "var-indent-unindent" )
02126 d->reUnindent.setPattern( val );
02127 else if ( var == "var-indent-triggerchars" )
02128 d->triggers = val;
02129 else if ( var == "var-indent-handle-couples" )
02130 {
02131 d->couples = 0;
02132 QStringList l = QStringList::split( " ", val );
02133 if ( l.contains("parens") ) d->couples |= Parens;
02134 if ( l.contains("braces") ) d->couples |= Braces;
02135 if ( l.contains("brackets") ) d->couples |= Brackets;
02136 }
02137 else if ( var == "var-indent-couple-attribute" )
02138 {
02139
02140 KateHlItemDataList items;
02141 doc->highlight()->getKateHlItemDataListCopy (0, items);
02142
02143 for (uint i=0; i<items.count(); i++)
02144 {
02145 if ( items.at(i)->name.section( ':', 1 ) == val )
02146 {
02147 d->coupleAttrib = i;
02148 break;
02149 }
02150 }
02151 }
02152 }
02153
02154 int KateVarIndent::coupleBalance ( int line, const QChar &open, const QChar &close ) const
02155 {
02156 int r = 0;
02157
02158 KateTextLine::Ptr ln = doc->plainKateTextLine( line );
02159 if ( ! ln || ! ln->length() ) return 0;
02160
02161 for ( uint z=0; z < ln->length(); z++ )
02162 {
02163 QChar c = ln->getChar( z );
02164 if ( ln->attribute(z) == d->coupleAttrib )
02165 {
02166 kdDebug(13030)<<z<<", "<<c<<endl;
02167 if (c == open)
02168 r++;
02169 else if (c == close)
02170 r--;
02171 }
02172 }
02173 return r;
02174 }
02175
02176 bool KateVarIndent::hasRelevantOpening( const KateDocCursor &end ) const
02177 {
02178 KateDocCursor cur = end;
02179 int count = 1;
02180
02181 QChar close = cur.currentChar();
02182 QChar opener;
02183 if ( close == '}' ) opener = '{';
02184 else if ( close = ')' ) opener = '(';
02185 else if (close = ']' ) opener = '[';
02186 else return false;
02187
02188
02189 while (cur.moveBackward(1))
02190 {
02191 if (cur.currentAttrib() == d->coupleAttrib)
02192 {
02193 QChar ch = cur.currentChar();
02194 if (ch == opener)
02195 count--;
02196 else if (ch == close)
02197 count++;
02198
02199 if (count == 0)
02200 return true;
02201 }
02202 }
02203
02204 return false;
02205 }
02206
02207
02208
02209
02210