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->stringAtPos(textLine->lastChar()-1, "*/"))
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 if ( textLine->attribute( begin.col() ) == doxyCommentAttrib )
00603 {
00604
00605 if ( c == '/' )
00606 {
00607 int first = textLine->firstChar();
00608
00609
00610 if ( first != -1
00611 && textLine->getChar( first ) == '*'
00612 && textLine->nextNonSpaceChar( first+1 ) == view->cursorColumn()-1 )
00613 doc->removeText( view->cursorLine(), first+1, view->cursorLine(), view->cursorColumn()-1);
00614 }
00615
00616
00617 return;
00618 }
00619
00620 processLine(begin);
00621 }
00622
00623
00624 uint KateCSmartIndent::calcIndent(KateDocCursor &begin, bool needContinue)
00625 {
00626 KateTextLine::Ptr textLine;
00627 KateDocCursor cur = begin;
00628
00629 uint anchorIndent = 0;
00630 int anchorPos = 0;
00631 int parenCount = 0;
00632 bool found = false;
00633 bool isSpecial = false;
00634
00635
00636
00637
00638 while (cur.gotoPreviousLine())
00639 {
00640 isSpecial = found = false;
00641 textLine = doc->plainKateTextLine(cur.line());
00642
00643
00644 int pos = textLine->lastChar();
00645 int openCount = 0;
00646 int otherAnchor = -1;
00647 do
00648 {
00649 if (textLine->attribute(pos) == symbolAttrib)
00650 {
00651 QChar tc = textLine->getChar (pos);
00652 if ((tc == ';' || tc == ':' || tc == ',') && otherAnchor == -1 && parenCount <= 0)
00653 otherAnchor = pos;
00654 else if (tc == ')')
00655 parenCount++;
00656 else if (tc == '(')
00657 parenCount--;
00658 else if (tc == '}')
00659 openCount--;
00660 else if (tc == '{')
00661 {
00662 openCount++;
00663 if (openCount == 1)
00664 break;
00665 }
00666 }
00667 } while (--pos >= textLine->firstChar());
00668
00669 if (openCount != 0 || otherAnchor != -1)
00670 {
00671 found = true;
00672 QChar c;
00673 if (openCount > 0)
00674 c = '{';
00675 else if (openCount < 0)
00676 c = '}';
00677 else if (otherAnchor >= 0)
00678 c = textLine->getChar (otherAnchor);
00679
00680 int specialIndent = 0;
00681 if (c == ':' && needContinue)
00682 {
00683 QChar ch;
00684 specialIndent = textLine->firstChar();
00685 if (textLine->stringAtPos(specialIndent, "case"))
00686 ch = textLine->getChar(specialIndent + 4);
00687 else if (textLine->stringAtPos(specialIndent, "default"))
00688 ch = textLine->getChar(specialIndent + 7);
00689 else if (textLine->stringAtPos(specialIndent, "public"))
00690 ch = textLine->getChar(specialIndent + 6);
00691 else if (textLine->stringAtPos(specialIndent, "private"))
00692 ch = textLine->getChar(specialIndent + 7);
00693 else if (textLine->stringAtPos(specialIndent, "protected"))
00694 ch = textLine->getChar(specialIndent + 9);
00695 else if (textLine->stringAtPos(specialIndent, "signals"))
00696 ch = textLine->getChar(specialIndent + 7);
00697 else if (textLine->stringAtPos(specialIndent, "slots"))
00698 ch = textLine->getChar(specialIndent + 5);
00699
00700 if (ch.isNull() || (!ch.isSpace() && ch != '(' && ch != ':'))
00701 continue;
00702
00703 KateDocCursor lineBegin = cur;
00704 lineBegin.setCol(specialIndent);
00705 specialIndent = measureIndent(lineBegin);
00706 isSpecial = true;
00707 }
00708
00709
00710 KateDocCursor skip = cur;
00711 skip.setCol(textLine->lastChar());
00712 bool result = skipBlanks(skip, begin, true);
00713
00714 anchorPos = skip.col();
00715 anchorIndent = measureIndent(skip);
00716
00717
00718
00719
00720 if (result && skip < begin)
00721 {
00722 cur = skip;
00723 break;
00724 }
00725 else if (isSpecial)
00726 {
00727 anchorIndent = specialIndent;
00728 break;
00729 }
00730
00731
00732 if ((c == '{' || c == '}') && textLine->getChar(textLine->firstChar()) == c)
00733 {
00734 cur.setCol(anchorPos = textLine->firstChar());
00735 anchorIndent = measureIndent (cur);
00736 break;
00737 }
00738 }
00739 }
00740
00741 if (!found)
00742 return 0;
00743
00744 uint continueIndent = (needContinue) ? calcContinue (cur, begin) : 0;
00745
00746
00747
00748
00749 textLine = doc->plainKateTextLine(cur.line());
00750 QChar lastChar = textLine->getChar (anchorPos);
00751 int lastLine = cur.line();
00752 if (lastChar == '#' || lastChar == '[')
00753 {
00754
00755
00756 continueIndent = 0;
00757 }
00758
00759 int openCount = 0;
00760 while (cur.validPosition() && cur < begin)
00761 {
00762 if (!skipBlanks(cur, begin, true))
00763 return 0;
00764
00765 QChar tc = cur.currentChar();
00766
00767 if (cur == begin || tc.isNull())
00768 break;
00769
00770 if (!tc.isSpace() && cur < begin)
00771 {
00772 uchar attrib = cur.currentAttrib();
00773 if (tc == '{' && attrib == symbolAttrib)
00774 openCount++;
00775 else if (tc == '}' && attrib == symbolAttrib)
00776 openCount--;
00777
00778 lastChar = tc;
00779 lastLine = cur.line();
00780 }
00781 }
00782 if (openCount > 0)
00783 lastChar = '{';
00784
00785 uint indent = 0;
00786
00787
00788 if (lastChar == '{' || (lastChar == ':' && isSpecial && needContinue))
00789 {
00790 indent = anchorIndent + indentWidth;
00791 }
00792 else if (lastChar == '}')
00793 {
00794 indent = anchorIndent;
00795 }
00796 else if (lastChar == ';')
00797 {
00798 indent = anchorIndent + ((allowSemi && needContinue) ? continueIndent : 0);
00799 }
00800 else if (lastChar == ',')
00801 {
00802 textLine = doc->plainKateTextLine(lastLine);
00803 KateDocCursor start(lastLine, textLine->firstChar(), doc);
00804 KateDocCursor finish(lastLine, textLine->lastChar(), doc);
00805 uint pos = 0;
00806
00807 if (isBalanced(start, finish, QChar('('), QChar(')'), pos))
00808 indent = anchorIndent;
00809 else
00810 {
00811
00812 indent = ((pos < 48) ? pos : anchorIndent + (indentWidth * 2));
00813 }
00814 }
00815 else if (!lastChar.isNull())
00816 {
00817 if (anchorIndent != 0)
00818 indent = anchorIndent + continueIndent;
00819 else
00820 indent = continueIndent;
00821 }
00822
00823 return indent;
00824 }
00825
00826 uint KateCSmartIndent::calcContinue(KateDocCursor &start, KateDocCursor &end)
00827 {
00828 KateDocCursor cur = start;
00829
00830 bool needsBalanced = true;
00831 bool isFor = false;
00832 allowSemi = false;
00833
00834 KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
00835
00836
00837 if (textLine->attribute(cur.col()) == symbolAttrib)
00838 {
00839 cur.moveForward(1);
00840 skipBlanks(cur, end, false);
00841 }
00842
00843 if (textLine->getChar(cur.col()) == '}')
00844 {
00845 skipBlanks(cur, end, true);
00846 if (cur.line() != start.line())
00847 textLine = doc->plainKateTextLine(cur.line());
00848
00849 if (textLine->stringAtPos(cur.col(), "else"))
00850 cur.setCol(cur.col() + 4);
00851 else
00852 return indentWidth * 2;
00853
00854 needsBalanced = false;
00855 }
00856 else if (textLine->stringAtPos(cur.col(), "else"))
00857 {
00858 cur.setCol(cur.col() + 4);
00859 needsBalanced = false;
00860 if (textLine->stringAtPos(textLine->nextNonSpaceChar(cur.col()), "if"))
00861 {
00862 cur.setCol(textLine->nextNonSpaceChar(cur.col()) + 2);
00863 needsBalanced = true;
00864 }
00865 }
00866 else if (textLine->stringAtPos(cur.col(), "if"))
00867 {
00868 cur.setCol(cur.col() + 2);
00869 }
00870 else if (textLine->stringAtPos(cur.col(), "do"))
00871 {
00872 cur.setCol(cur.col() + 2);
00873 needsBalanced = false;
00874 }
00875 else if (textLine->stringAtPos(cur.col(), "for"))
00876 {
00877 cur.setCol(cur.col() + 3);
00878 isFor = true;
00879 }
00880 else if (textLine->stringAtPos(cur.col(), "while"))
00881 {
00882 cur.setCol(cur.col() + 5);
00883 }
00884 else if (textLine->stringAtPos(cur.col(), "switch"))
00885 {
00886 cur.setCol(cur.col() + 6);
00887 }
00888 else if (textLine->stringAtPos(cur.col(), "using"))
00889 {
00890 cur.setCol(cur.col() + 5);
00891 }
00892 else
00893 {
00894 return indentWidth * 2;
00895 }
00896
00897 uint openPos = 0;
00898 if (needsBalanced && !isBalanced (cur, end, QChar('('), QChar(')'), openPos))
00899 {
00900 allowSemi = isFor;
00901 if (openPos > 0)
00902 return (openPos - textLine->firstChar());
00903 else
00904 return indentWidth * 2;
00905 }
00906
00907
00908 skipBlanks(cur, end, false);
00909 if (cur == end)
00910 return indentWidth;
00911
00912 if (skipBlanks(cur, end, true))
00913 {
00914 if (cur == end)
00915 return indentWidth;
00916 else
00917 return indentWidth + calcContinue(cur, end);
00918 }
00919
00920 return 0;
00921 }
00922
00923 uint KateCSmartIndent::findOpeningBrace(KateDocCursor &start)
00924 {
00925 KateDocCursor cur = start;
00926 int count = 1;
00927
00928
00929
00930 while (cur.moveBackward(1))
00931 {
00932 if (cur.currentAttrib() == symbolAttrib)
00933 {
00934 QChar ch = cur.currentChar();
00935 if (ch == '{')
00936 count--;
00937 else if (ch == '}')
00938 count++;
00939
00940 if (count == 0)
00941 {
00942 KateDocCursor temp(cur.line(), doc->plainKateTextLine(cur.line())->firstChar(), doc);
00943 return measureIndent(temp);
00944 }
00945 }
00946 }
00947
00948 return 0;
00949 }
00950
00951 bool KateCSmartIndent::firstOpeningBrace(KateDocCursor &start)
00952 {
00953 KateDocCursor cur = start;
00954
00955
00956 while(cur.moveBackward(1))
00957 {
00958 if (cur.currentAttrib() == symbolAttrib)
00959 {
00960 QChar ch = cur.currentChar();
00961 if (ch == '{')
00962 return false;
00963 else if (ch == '}' && cur.col() == 0)
00964 break;
00965 }
00966 }
00967
00968 return true;
00969 }
00970
00971 uint KateCSmartIndent::findOpeningParen(KateDocCursor &start)
00972 {
00973 KateDocCursor cur = start;
00974 int count = 1;
00975
00976
00977
00978 while (cur.moveBackward(1))
00979 {
00980 if (cur.currentAttrib() == symbolAttrib)
00981 {
00982 QChar ch = cur.currentChar();
00983 if (ch == '(')
00984 count--;
00985 else if (ch == ')')
00986 count++;
00987
00988 if (count == 0)
00989 return measureIndent(cur);
00990 }
00991 }
00992
00993 return 0;
00994 }
00995
00996 uint KateCSmartIndent::findOpeningComment(KateDocCursor &start)
00997 {
00998 KateDocCursor cur = start;
00999
01000
01001 do
01002 {
01003 KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
01004
01005 int pos = textLine->string().find("/*", false);
01006 if (pos >= 0)
01007 {
01008 KateDocCursor temp(cur.line(), pos, doc);
01009 return measureIndent(temp);
01010 }
01011
01012 } while (cur.gotoPreviousLine());
01013
01014 return 0;
01015 }
01016
01017
01018
01019
01020
01021 QRegExp KatePythonIndent::endWithColon = QRegExp( "^[^#]*:\\s*(#.*)?$" );
01022 QRegExp KatePythonIndent::stopStmt = QRegExp( "^\\s*(break|continue|raise|return|pass)\\b.*" );
01023 QRegExp KatePythonIndent::blockBegin = QRegExp( "^\\s*(def|if|elif|else|for|while|try)\\b.*" );
01024
01025 KatePythonIndent::KatePythonIndent (KateDocument *doc)
01026 : KateNormalIndent (doc)
01027 {
01028 }
01029 KatePythonIndent::~KatePythonIndent ()
01030 {
01031 }
01032
01033 void KatePythonIndent::processNewline (KateDocCursor &begin, bool )
01034 {
01035 int prevLine = begin.line() - 1;
01036 int prevPos = begin.col();
01037
01038 while ((prevLine > 0) && (prevPos < 0))
01039 prevPos = doc->plainKateTextLine(--prevLine)->firstChar();
01040
01041 int prevBlock = prevLine;
01042 int prevBlockPos = prevPos;
01043 int extraIndent = calcExtra (prevBlock, prevBlockPos, begin);
01044
01045 int indent = doc->plainKateTextLine(prevBlock)->cursorX(prevBlockPos, tabWidth);
01046 if (extraIndent == 0)
01047 {
01048 if (!stopStmt.exactMatch(doc->plainKateTextLine(prevLine)->string()))
01049 {
01050 if (endWithColon.exactMatch(doc->plainKateTextLine(prevLine)->string()))
01051 indent += indentWidth;
01052 else
01053 indent = doc->plainKateTextLine(prevLine)->cursorX(prevPos, tabWidth);
01054 }
01055 }
01056 else
01057 indent += extraIndent;
01058
01059 if (indent > 0)
01060 {
01061 QString filler = tabString (indent);
01062 doc->insertText (begin.line(), 0, filler);
01063 begin.setCol(filler.length());
01064 }
01065 else
01066 begin.setCol(0);
01067 }
01068
01069 int KatePythonIndent::calcExtra (int &prevBlock, int &pos, KateDocCursor &end)
01070 {
01071 int nestLevel = 0;
01072 bool levelFound = false;
01073 while ((prevBlock > 0))
01074 {
01075 if (blockBegin.exactMatch(doc->plainKateTextLine(prevBlock)->string()))
01076 {
01077 if ((!levelFound && nestLevel == 0) || (levelFound && nestLevel - 1 <= 0))
01078 {
01079 pos = doc->plainKateTextLine(prevBlock)->firstChar();
01080 break;
01081 }
01082
01083 nestLevel --;
01084 }
01085 else if (stopStmt.exactMatch(doc->plainKateTextLine(prevBlock)->string()))
01086 {
01087 nestLevel ++;
01088 levelFound = true;
01089 }
01090
01091 --prevBlock;
01092 }
01093
01094 KateDocCursor cur (prevBlock, pos, doc);
01095 QChar c;
01096 int extraIndent = 0;
01097 while (cur.line() < end.line())
01098 {
01099 c = cur.currentChar();
01100
01101 if (c == '(')
01102 extraIndent += indentWidth;
01103 else if (c == ')')
01104 extraIndent -= indentWidth;
01105 else if (c == ':')
01106 break;
01107
01108 if (c.isNull() || c == '#')
01109 cur.gotoNextLine();
01110 else
01111 cur.moveForward(1);
01112 }
01113
01114 return extraIndent;
01115 }
01116
01117
01118
01119
01120
01121
01122
01123
01124
01125
01126
01127
01128
01129
01130
01131
01132
01133
01134
01135
01136
01137
01138
01139
01140
01141
01142
01143 const QRegExp KateXmlIndent::startsWithCloseTag("^[ \t]*</");
01144 const QRegExp KateXmlIndent::unclosedDoctype("<!DOCTYPE[^>]*$");
01145
01146 KateXmlIndent::KateXmlIndent (KateDocument *doc)
01147 : KateNormalIndent (doc)
01148 {
01149 }
01150
01151 KateXmlIndent::~KateXmlIndent ()
01152 {
01153 }
01154
01155 void KateXmlIndent::processNewline (KateDocCursor &begin, bool )
01156 {
01157 begin.setCol(processLine(begin.line()));
01158 }
01159
01160 void KateXmlIndent::processChar (QChar c)
01161 {
01162 if(c != '/') return;
01163
01164
01165 KateView *view = doc->activeView();
01166 QString text = doc->plainKateTextLine(view->cursorLine())->string();
01167 if(text.find(startsWithCloseTag) == -1) return;
01168
01169
01170 processLine(view->cursorLine());
01171 }
01172
01173 void KateXmlIndent::processLine (KateDocCursor &line)
01174 {
01175 processLine (line.line());
01176 }
01177
01178 void KateXmlIndent::processSection (KateDocCursor &cur, KateDocCursor &end)
01179 {
01180 int endLine = end.line();
01181 do {
01182 processLine(cur.line());
01183 if(!cur.gotoNextLine()) break;
01184 } while(cur.line() < endLine);
01185 }
01186
01187 void KateXmlIndent::getLineInfo (uint line, uint &prevIndent, int &numTags,
01188 uint &attrCol, bool &unclosedTag)
01189 {
01190 prevIndent = 0;
01191 int firstChar;
01192 KateTextLine::Ptr prevLine = 0;
01193
01194
01195 while(true) {
01196 prevLine = doc->plainKateTextLine(line);
01197 if( (firstChar = prevLine->firstChar()) < 0) {
01198 if(!line--) return;
01199 continue;
01200 }
01201 break;
01202 }
01203 prevIndent = prevLine->cursorX(prevLine->firstChar(), tabWidth);
01204 QString text = prevLine->string();
01205
01206
01207
01208
01209
01210 if(text.find(startsWithCloseTag) != -1) ++numTags;
01211
01212
01213 int lastCh = 0;
01214 uint pos, len = text.length();
01215 bool seenOpen = false;
01216 for(pos = 0; pos < len; ++pos) {
01217 int ch = text.at(pos).unicode();
01218 switch(ch) {
01219 case '<':
01220 seenOpen = true;
01221 unclosedTag = true;
01222 attrCol = pos;
01223 ++numTags;
01224 break;
01225
01226
01227 case '!':
01228 if(lastCh == '<') --numTags;
01229 break;
01230
01231
01232 case '?':
01233 if(lastCh == '<') --numTags;
01234 break;
01235
01236 case '>':
01237 if(!seenOpen) {
01238
01239
01240
01241
01242
01243
01244
01245
01246 prevIndent = 0;
01247
01248 for(uint backLine = line; backLine; ) {
01249
01250 KateTextLine::Ptr x = doc->plainKateTextLine(--backLine);
01251 if(x->string().find('<') == -1) continue;
01252
01253
01254 if(x->string().find(unclosedDoctype) != -1) --numTags;
01255 getLineInfo(backLine, prevIndent, numTags, attrCol, unclosedTag);
01256 break;
01257 }
01258 }
01259 if(lastCh == '/') --numTags;
01260 unclosedTag = false;
01261 break;
01262
01263 case '/':
01264 if(lastCh == '<') numTags -= 2;
01265 break;
01266 }
01267 lastCh = ch;
01268 }
01269
01270 if(unclosedTag) {
01271
01272 do {
01273 lastCh = text.at(++attrCol).unicode();
01274 }while(lastCh && lastCh != ' ' && lastCh != '\t');
01275
01276 while(lastCh == ' ' || lastCh == '\t') {
01277 lastCh = text.at(++attrCol).unicode();
01278 }
01279
01280 attrCol = prevLine->cursorX(attrCol, tabWidth);
01281 }
01282 }
01283
01284 uint KateXmlIndent::processLine (uint line)
01285 {
01286 KateTextLine::Ptr kateLine = doc->plainKateTextLine(line);
01287 if(!kateLine) return 0;
01288
01289
01290 uint prevIndent = 0, attrCol = 0;
01291 int numTags = 0;
01292 bool unclosedTag = false;
01293
01294 if(line) {
01295 getLineInfo(line - 1, prevIndent, numTags, attrCol, unclosedTag);
01296 }
01297
01298
01299 int indent = 0;
01300 if(unclosedTag) indent = attrCol;
01301 else indent = prevIndent + numTags * indentWidth;
01302 if(indent < 0) indent = 0;
01303
01304
01305 if(kateLine->string().find(startsWithCloseTag) != -1) {
01306 indent -= indentWidth;
01307 }
01308 if(indent < 0) indent = 0;
01309
01310
01311 doc->removeText(line, 0, line, kateLine->firstChar());
01312 QString filler = tabString(indent);
01313 doc->insertText(line, 0, filler);
01314
01315 return filler.length();
01316 }
01317
01318
01319
01320
01321
01322 KateCSAndSIndent::KateCSAndSIndent (KateDocument *doc)
01323 : KateNormalIndent (doc)
01324 {
01325 }
01326
01327 void KateCSAndSIndent::updateIndentString()
01328 {
01329 if( useSpaces )
01330 indentString.fill( ' ', indentWidth );
01331 else
01332 indentString = '\t';
01333 }
01334
01335 KateCSAndSIndent::~KateCSAndSIndent ()
01336 {
01337 }
01338
01339 void KateCSAndSIndent::processLine (KateDocCursor &line)
01340 {
01341 KateTextLine::Ptr textLine = doc->plainKateTextLine(line.line());
01342
01343 if (!textLine)
01344 return;
01345
01346 updateIndentString();
01347
01348 const int oldCol = line.col();
01349 QString whitespace = calcIndent(line);
01350
01351 int oldIndent = textLine->firstChar();
01352 if ( oldIndent < 0 )
01353 oldIndent = doc->lineLength( line.line() );
01354 if( oldIndent > 0 )
01355 doc->removeText(line.line(), 0, line.line(), oldIndent);
01356
01357 doc->insertText(line.line(), 0, whitespace);
01358
01359
01360 if ( int(oldCol + whitespace.length()) >= oldIndent )
01361 line.setCol( oldCol + whitespace.length() - oldIndent );
01362 else
01363 line.setCol( 0 );
01364 }
01365
01366 void KateCSAndSIndent::processSection (KateDocCursor &begin, KateDocCursor &end)
01367 {
01368 QTime t; t.start();
01369 for( KateDocCursor cur = begin; cur.line() <= end.line(); )
01370 {
01371 processLine (cur);
01372 if (!cur.gotoNextLine())
01373 break;
01374 }
01375 kdDebug(13030) << "+++ total: " << t.elapsed() << endl;
01376 }
01377
01383 static QString initialWhitespace(const KateTextLine::Ptr &line, int chars, bool convert = true)
01384 {
01385 QString text = line->string(0, chars);
01386 if( (int)text.length() < chars )
01387 {
01388 QString filler; filler.fill(' ',chars - text.length());
01389 text += filler;
01390 }
01391 for( uint n = 0; n < text.length(); ++n )
01392 {
01393 if( text[n] != '\t' && text[n] != ' ' )
01394 {
01395 if( !convert )
01396 return text.left( n );
01397 text[n] = ' ';
01398 }
01399 }
01400 return text;
01401 }
01402
01403 QString KateCSAndSIndent::findOpeningCommentIndentation(const KateDocCursor &start)
01404 {
01405 KateDocCursor cur = start;
01406
01407
01408 do
01409 {
01410 KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
01411
01412 int pos = textLine->string().findRev("/*");
01413
01414 if (pos >= 0)
01415 return initialWhitespace(textLine, pos);
01416 } while (cur.gotoPreviousLine());
01417
01418
01419 kdWarning( 13030 ) << " in a comment, but can't find the start of it" << endl;
01420 return QString::null;
01421 }
01422
01423 bool KateCSAndSIndent::handleDoxygen (KateDocCursor &begin)
01424 {
01425
01426 int line = begin.line();
01427 int first = -1;
01428 while ((line > 0) && (first < 0))
01429 first = doc->plainKateTextLine(--line)->firstChar();
01430
01431
01432 if (first < 0)
01433 return false;
01434
01435 KateTextLine::Ptr textLine = doc->plainKateTextLine(line);
01436
01437
01438
01439
01440
01441 if ( !(textLine->attribute(textLine->lastChar()) == doxyCommentAttrib && !textLine->endingWith("*/")) &&
01442 !(textLine->attribute(textLine->firstChar()) == doxyCommentAttrib && !textLine->string().contains("*/")) )
01443 return false;
01444
01445
01446 textLine = doc->plainKateTextLine(begin.line());
01447 first = textLine->firstChar();
01448 QString indent = findOpeningCommentIndentation(begin);
01449
01450 bool doxygenAutoInsert = doc->config()->configFlags() & KateDocumentConfig::cfDoxygenAutoTyping;
01451
01452
01453 if ( textLine->stringAtPos(first, "*") )
01454 indent = indent + " ";
01455
01456 else if ( doxygenAutoInsert )
01457 indent = indent + " * ";
01458
01459
01460
01461
01462 doc->removeText (begin.line(), 0, begin.line(), first);
01463 doc->insertText (begin.line(), 0, indent);
01464 begin.setCol(indent.length());
01465
01466 return true;
01467 }
01468
01475 void KateCSAndSIndent::processNewline (KateDocCursor &begin, bool )
01476 {
01477
01478 if( handleDoxygen(begin) )
01479 return;
01480
01481
01482
01483
01484
01485 int cursorPos = doc->plainKateTextLine( begin.line() )->firstChar();
01486 if ( cursorPos < 0 )
01487 cursorPos = doc->lineLength( begin.line() );
01488 begin.setCol( cursorPos );
01489
01490 processLine( begin );
01491 }
01492
01497 bool KateCSAndSIndent::startsWithLabel( int line )
01498 {
01499 KateTextLine::Ptr indentLine = doc->plainKateTextLine( line );
01500 const int indentFirst = indentLine->firstChar();
01501
01502 int attrib = indentLine->attribute(indentFirst);
01503 if (attrib != 0 && attrib != keywordAttrib && attrib != normalAttrib && attrib != extensionAttrib)
01504 return false;
01505
01506 const QString lineContents = indentLine->string();
01507 static const QString symbols = QString::fromLatin1(";:[]{}");
01508 const int last = indentLine->lastChar();
01509 for ( int n = indentFirst + 1; n <= last; ++n )
01510 {
01511 QChar c = lineContents[n];
01512
01513 if ( !symbols.contains(c) )
01514 continue;
01515
01516
01517 if ( c != ':' )
01518 return false;
01519
01520
01521 if ( lineContents[n+1] != ':' )
01522 return true;
01523
01524
01525
01526 if ( lineContents[n+2] != ':' )
01527 {
01528 ++n;
01529 continue;
01530 }
01531
01532
01533
01534 return true;
01535 }
01536 return false;
01537 }
01538
01539 template<class T> T min(T a, T b) { return (a < b) ? a : b; }
01540
01541 int KateCSAndSIndent::lastNonCommentChar( const KateDocCursor &line )
01542 {
01543 KateTextLine::Ptr textLine = doc->plainKateTextLine( line.line() );
01544 QString str = textLine->string();
01545
01546
01547 int p = -2;
01548 do p = str.find( "//", p + 2 );
01549 while ( p >= 0 && textLine->attribute(p) != commentAttrib && textLine->attribute(p) != doxyCommentAttrib );
01550
01551
01552 if ( p < 0 )
01553 p = str.length();
01554
01555
01556 while( p > 0 && str[p-1].isSpace() ) --p;
01557 return p - 1;
01558 }
01559
01560 bool KateCSAndSIndent::inForStatement( int line )
01561 {
01562
01563
01564 int parens = 0, semicolons = 0;
01565 for ( ; line >= 0; --line )
01566 {
01567 KateTextLine::Ptr textLine = doc->plainKateTextLine(line);
01568 const int first = textLine->firstChar();
01569 const int last = textLine->lastChar();
01570
01571
01572
01573
01574
01575 for ( int curr = last; curr >= first; --curr )
01576 {
01577 if ( textLine->attribute(curr) != symbolAttrib )
01578 continue;
01579
01580 switch( textLine->getChar(curr) )
01581 {
01582 case ';':
01583 if( ++semicolons > 2 )
01584 return false;
01585 break;
01586 case '{': case '}':
01587 return false;
01588 case ')':
01589 ++parens;
01590 break;
01591 case '(':
01592 if( --parens < 0 )
01593 return true;
01594 break;
01595 }
01596 }
01597 }
01598
01599
01600 return false;
01601 }
01602
01603
01604
01605 bool KateCSAndSIndent::inStatement( const KateDocCursor &begin )
01606 {
01607
01608
01609 KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
01610 const int first = textLine->firstChar();
01611
01612
01613
01614 const int attrib = textLine->attribute(first);
01615 if( first >= 0 && (attrib == 0 || attrib == symbolAttrib) && textLine->getChar(first) == '{' )
01616 return false;
01617
01618 int line;
01619 for ( line = begin.line() - 1; line >= 0; --line )
01620 {
01621 textLine = doc->plainKateTextLine(line);
01622 const int first = textLine->firstChar();
01623 if ( first == -1 )
01624 continue;
01625
01626
01627
01628 if ( textLine->getChar( first ) == '#' )
01629 continue;
01630 KateDocCursor currLine = begin;
01631 currLine.setLine( line );
01632 const int last = lastNonCommentChar( currLine );
01633 if ( last < first )
01634 continue;
01635
01636
01637
01638
01639
01640
01641 const int attrib = textLine->attribute(last);
01642 if ( attrib == commentAttrib || attrib == doxyCommentAttrib )
01643 return false;
01644
01645 char c = textLine->getChar(last);
01646
01647
01648 if ( attrib == symbolAttrib && c == '{' || c == '}' )
01649 return false;
01650
01651
01652 if ( attrib == symbolAttrib && c == ';' )
01653 return inForStatement( line );
01654
01655
01656 if ( attrib == symbolAttrib && c == ':' )
01657 {
01658
01659
01660
01661
01662 if( startsWithLabel( line ) )
01663 {
01664
01665
01666
01667
01668 continue;
01669 }
01670 }
01671
01672
01673 return true;
01674 }
01675
01676 return false;
01677 }
01678
01679 QString KateCSAndSIndent::continuationIndent( const KateDocCursor &begin )
01680 {
01681 if( !inStatement( begin ) )
01682 return QString::null;
01683 return indentString;
01684 }
01685
01689 QString KateCSAndSIndent::calcIndent (const KateDocCursor &begin)
01690 {
01691 KateTextLine::Ptr currLine = doc->plainKateTextLine(begin.line());
01692 int currLineFirst = currLine->firstChar();
01693
01694
01695
01696
01697 if ( currLineFirst >= 0 &&
01698 (currLine->attribute(currLineFirst) == commentAttrib ||
01699 currLine->attribute(currLineFirst) == doxyCommentAttrib) )
01700 return currLine->string( 0, currLineFirst );
01701
01702
01703 if( currLineFirst >= 0 && currLine->getChar(currLineFirst) == '#' )
01704 {
01705 if( !currLine->stringAtPos( currLineFirst+1, QString::fromLatin1("region") ) &&
01706 !currLine->stringAtPos( currLineFirst+1, QString::fromLatin1("endregion") ) )
01707 return QString::null;
01708 }
01709
01710
01711
01712
01713
01714
01715
01716
01717 KateDocCursor cur = begin;
01718 int pos, openBraceCount = 0, openParenCount = 0;
01719 bool lookingForScopeKeywords = true;
01720 const char * const scopeKeywords[] = { "for", "do", "while", "if", "else" };
01721 const char * const blockScopeKeywords[] = { "try", "catch", "switch" };
01722
01723 while (cur.gotoPreviousLine())
01724 {
01725 KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
01726 const int lastChar = textLine->lastChar();
01727 const int firstChar = textLine->firstChar();
01728
01729
01730 for( pos = lastChar; pos >= firstChar; --pos )
01731 {
01732 if (textLine->attribute(pos) == symbolAttrib)
01733 {
01734 char tc = textLine->getChar (pos);
01735 switch( tc )
01736 {
01737 case '(': case '[':
01738 if( ++openParenCount > 0 )
01739 return calcIndentInBracket( begin, cur, pos );
01740 break;
01741 case ')': case ']': openParenCount--; break;
01742 case '{':
01743 if( ++openBraceCount > 0 )
01744 return calcIndentInBrace( begin, cur, pos );
01745 break;
01746 case '}': openBraceCount--; lookingForScopeKeywords = false; break;
01747 case ';':
01748 if( openParenCount == 0 )
01749 lookingForScopeKeywords = false;
01750 break;
01751 }
01752 }
01753
01754
01755
01756 if ( lookingForScopeKeywords && openParenCount == 0 &&
01757 textLine->attribute(pos) == keywordAttrib &&
01758 (pos == 0 || textLine->attribute(pos-1) != keywordAttrib ) )
01759 {
01760 #define ARRLEN( array ) ( sizeof(array)/sizeof(array[0]) )
01761 for( uint n = 0; n < ARRLEN(scopeKeywords); ++n )
01762 if( textLine->stringAtPos(pos, QString::fromLatin1(scopeKeywords[n]) ) )
01763 return calcIndentAfterKeyword( begin, cur, pos, false );
01764 for( uint n = 0; n < ARRLEN(blockScopeKeywords); ++n )
01765 if( textLine->stringAtPos(pos, QString::fromLatin1(blockScopeKeywords[n]) ) )
01766 return calcIndentAfterKeyword( begin, cur, pos, true );
01767 #undef ARRLEN
01768 }
01769 }
01770 }
01771
01772
01773 return QString::null;
01774 }
01775
01776 QString KateCSAndSIndent::calcIndentInBracket(const KateDocCursor &indentCursor, const KateDocCursor &bracketCursor, int bracketPos)
01777 {
01778 KateTextLine::Ptr indentLine = doc->plainKateTextLine(indentCursor.line());
01779 KateTextLine::Ptr bracketLine = doc->plainKateTextLine(bracketCursor.line());
01780
01781
01782
01783 if ( bracketPos > 48 )
01784 {
01785
01786
01787
01788
01789
01790
01791
01792
01793
01794
01795 return indentString + initialWhitespace( bracketLine, bracketLine->firstChar() );
01796 }
01797
01798 const int indentLineFirst = indentLine->firstChar();
01799
01800 int indentTo;
01801 const int attrib = indentLine->attribute(indentLineFirst);
01802 if( indentLineFirst >= 0 && (attrib == 0 || attrib == symbolAttrib) &&
01803 ( indentLine->getChar(indentLineFirst) == ')' || indentLine->getChar(indentLineFirst) == ']' ) )
01804 {
01805
01806 indentTo = bracketPos;
01807 }
01808 else
01809 {
01810
01811 indentTo = bracketLine->nextNonSpaceChar( bracketPos + 1 );
01812 if( indentTo == -1 )
01813 indentTo = bracketPos + 2;
01814 }
01815 return initialWhitespace( bracketLine, indentTo );
01816 }
01817
01818 QString KateCSAndSIndent::calcIndentAfterKeyword(const KateDocCursor &indentCursor, const KateDocCursor &keywordCursor, int keywordPos, bool blockKeyword)
01819 {
01820 KateTextLine::Ptr keywordLine = doc->plainKateTextLine(keywordCursor.line());
01821 KateTextLine::Ptr indentLine = doc->plainKateTextLine(indentCursor.line());
01822
01823 QString whitespaceToKeyword = initialWhitespace( keywordLine, keywordPos, false );
01824 if( blockKeyword )
01825 ;
01826
01827
01828 int first = indentLine->firstChar();
01829
01830 const int attrib = indentLine->attribute(first);
01831 if( first >= 0 && (attrib == 0 || attrib == symbolAttrib) && indentLine->getChar(first) == '{' )
01832 return whitespaceToKeyword;
01833
01834
01835
01836
01837
01838
01839
01840
01841 return indentString + whitespaceToKeyword;
01842 }
01843
01844 QString KateCSAndSIndent::calcIndentInBrace(const KateDocCursor &indentCursor, const KateDocCursor &braceCursor, int bracePos)
01845 {
01846 KateTextLine::Ptr braceLine = doc->plainKateTextLine(braceCursor.line());
01847 const int braceFirst = braceLine->firstChar();
01848
01849 QString whitespaceToOpenBrace = initialWhitespace( braceLine, bracePos, false );
01850
01851
01852
01853
01854
01855 {
01856 if( braceFirst >= 0 && braceLine->attribute(braceFirst) == keywordAttrib &&
01857 braceLine->stringAtPos( braceFirst, QString::fromLatin1( "namespace" ) ) )
01858 return continuationIndent(indentCursor) + whitespaceToOpenBrace;
01859
01860 if( braceCursor.line() > 0 )
01861 {
01862 KateTextLine::Ptr prevLine = doc->plainKateTextLine(braceCursor.line() - 1);
01863 int firstPrev = prevLine->firstChar();
01864 if( firstPrev >= 0 && prevLine->attribute(firstPrev) == keywordAttrib &&
01865 prevLine->stringAtPos( firstPrev, QString::fromLatin1( "namespace" ) ) )
01866 return continuationIndent(indentCursor) + whitespaceToOpenBrace;
01867 }
01868 }
01869
01870 KateTextLine::Ptr indentLine = doc->plainKateTextLine(indentCursor.line());
01871 const int indentFirst = indentLine->firstChar();
01872
01873
01874 if( indentFirst >= 0 && indentLine->getChar(indentFirst) == '}' )
01875 return whitespaceToOpenBrace;
01876
01877
01878
01879 if ( indentFirst >= 0 && indentLine->attribute(indentFirst) == symbolAttrib &&
01880 indentLine->getChar(indentFirst) == ':' && indentLine->getChar(indentFirst+1) != ':' )
01881 {
01882 return indentString + indentString + whitespaceToOpenBrace;
01883 }
01884
01885 const bool continuation = inStatement(indentCursor);
01886
01887 if( !continuation && startsWithLabel( indentCursor.line() ) )
01888 return whitespaceToOpenBrace;
01889
01890
01891 QString continuationIndent = continuation ? indentString : QString::null;
01892 return indentString + continuationIndent + whitespaceToOpenBrace;
01893 }
01894
01895 void KateCSAndSIndent::processChar(QChar c)
01896 {
01897
01898 static const QString triggers("}{)]/:;#n");
01899 if (triggers.find(c) == -1)
01900 return;
01901
01902
01903
01904 KateView *view = doc->activeView();
01905 KateDocCursor begin(view->cursorLine(), 0, doc);
01906
01907 KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
01908 if ( c == 'n' )
01909 {
01910 int first = textLine->firstChar();
01911 if( first < 0 || textLine->getChar(first) != '#' )
01912 return;
01913 }
01914
01915 if ( textLine->attribute( begin.col() ) == doxyCommentAttrib )
01916 {
01917
01918 if ( c == '/' )
01919 {
01920 int first = textLine->firstChar();
01921
01922
01923 if ( first != -1
01924 && textLine->getChar( first ) == '*'
01925 && textLine->nextNonSpaceChar( first+1 ) == view->cursorColumn()-1 )
01926 doc->removeText( view->cursorLine(), first+1, view->cursorLine(), view->cursorColumn()-1);
01927 }
01928
01929
01930 return;
01931 }
01932
01933 processLine(begin);
01934 }
01935
01936
01937
01938
01939 class KateVarIndentPrivate {
01940 public:
01941 QRegExp reIndentAfter, reIndent, reUnindent;
01942 QString triggers;
01943 uint couples;
01944 uchar coupleAttrib;
01945 };
01946
01947 KateVarIndent::KateVarIndent( KateDocument *doc )
01948 : QObject( 0, "variable indenter"), KateNormalIndent( doc )
01949 {
01950 d = new KateVarIndentPrivate;
01951 d->reIndentAfter = QRegExp( doc->variable( "var-indent-indent-after" ) );
01952 d->reIndent = QRegExp( doc->variable( "var-indent-indent" ) );
01953 d->reUnindent = QRegExp( doc->variable( "var-indent-unindent" ) );
01954 d->triggers = doc->variable( "var-indent-triggerchars" );
01955 d->coupleAttrib = 0;
01956
01957 slotVariableChanged( "var-indent-couple-attribute", doc->variable( "var-indent-couple-attribute" ) );
01958 slotVariableChanged( "var-indent-handle-couples", doc->variable( "var-indent-handle-couples" ) );
01959
01960
01961 connect( doc, SIGNAL(variableChanged( const QString&, const QString&) ),
01962 this, SLOT(slotVariableChanged( const QString&, const QString& )) );
01963 }
01964
01965 KateVarIndent::~KateVarIndent()
01966 {
01967 delete d;
01968 }
01969
01970 void KateVarIndent::processNewline ( KateDocCursor &begin, bool )
01971 {
01972
01973 KateDocCursor left( begin.line()-1, 0, doc );
01974 processLine( left );
01975 processLine( begin );
01976 }
01977
01978 void KateVarIndent::processChar ( QChar c )
01979 {
01980
01981 if ( d->triggers.contains( c ) )
01982 {
01983 KateTextLine::Ptr ln = doc->plainKateTextLine( doc->activeView()->cursorLine() );
01984 if ( ln->attribute( doc->activeView()->cursorColumn()-1 ) == commentAttrib )
01985 return;
01986
01987 KateView *view = doc->activeView();
01988 KateDocCursor begin( view->cursorLine(), 0, doc );
01989 kdDebug(13030)<<"variable indenter: process char '"<<c<<", line "<<begin.line()<<endl;
01990 processLine( begin );
01991 }
01992 }
01993
01994 void KateVarIndent::processLine ( KateDocCursor &line )
01995 {
01996 updateConfig();
01997
01998 QString indent;
01999
02000
02001
02002 int ln = line.line();
02003 int pos = -1;
02004 KateTextLine::Ptr ktl = doc->plainKateTextLine( ln );
02005 if ( ! ktl ) return;
02006
02007
02008 KateView *v = doc->activeView();
02009 if ( ktl->firstChar() < 0 && (!v || v->cursorLine() != ln ) )
02010 return;
02011
02012 int fc;
02013 if ( ln > 0 )
02014 do
02015 {
02016
02017 ktl = doc->plainKateTextLine( --ln );
02018 fc = ktl->firstChar();
02019 if ( ktl->attribute( fc ) != commentAttrib )
02020 pos = fc;
02021 }
02022 while ( (ln > 0) && (pos < 0) );
02023
02024 if ( pos < 0 )
02025 pos = 0;
02026 else
02027 pos = ktl->cursorX( pos, tabWidth );
02028
02029 int adjustment = 0;
02030
02031
02032
02033 if ( d->couples & Parens && coupleBalance( ln, '(', ')' ) > 0 )
02034 adjustment++;
02035 else if ( d->couples & Braces && coupleBalance( ln, '{', '}' ) > 0 )
02036 adjustment++;
02037 else if ( d->couples & Brackets && coupleBalance( ln, '[', ']' ) > 0 )
02038 adjustment++;
02039
02040
02041
02042
02043
02044
02045
02046
02047
02048
02049
02050 {
02051 KateTextLine::Ptr tl = doc->plainKateTextLine( line.line() );
02052 int i = tl->firstChar();
02053 if ( i > -1 )
02054 {
02055 QChar ch = tl->getChar( i );
02056 uchar at = tl->attribute( i );
02057 kdDebug(13030)<<"attrib is "<<at<<endl;
02058 if ( d->couples & Parens && ch == ')'
02059 && ( at == d->coupleAttrib
02060 || (! at && hasRelevantOpening( KateDocCursor( line.line(), i, doc ) ))
02061 )
02062 )
02063 adjustment--;
02064 else if ( d->couples & Braces && ch == '}'
02065 && ( at == d->coupleAttrib
02066 || (! at && hasRelevantOpening( KateDocCursor( line.line(), i, doc ) ))
02067 )
02068 )
02069 adjustment--;
02070 else if ( d->couples & Brackets && ch == ']'
02071 && ( at == d->coupleAttrib
02072 || (! at && hasRelevantOpening( KateDocCursor( line.line(), i, doc ) ))
02073 )
02074 )
02075 adjustment--;
02076 }
02077 }
02078 #define ISCOMMENTATTR(attr) (attr==commentAttrib||attr==doxyCommentAttrib)
02079 #define ISCOMMENT (ISCOMMENTATTR(ktl->attribute(ktl->firstChar()))||ISCOMMENTATTR(ktl->attribute(matchpos)))
02080
02081
02082 kdDebug(13030)<<"variable indenter: starting indent: "<<pos<<endl;
02083
02084 int matchpos = 0;
02085 if ( ktl && ! d->reIndentAfter.isEmpty()
02086 && (matchpos = d->reIndentAfter.search( doc->textLine( ln ) )) > -1
02087 && ! ISCOMMENT )
02088 adjustment++;
02089
02090
02091 ktl = doc->plainKateTextLine( line.line() );
02092 if ( ! d->reIndent.isEmpty()
02093 && (matchpos = d->reIndent.search( doc->textLine( line.line() ) )) > -1
02094 && ! ISCOMMENT )
02095 adjustment++;
02096
02097
02098 if ( ! d->reUnindent.isEmpty()
02099 && (matchpos = d->reUnindent.search( doc->textLine( line.line() ) )) > -1
02100 && ! ISCOMMENT )
02101 adjustment--;
02102
02103 kdDebug(13030)<<"variable indenter: adjusting by "<<adjustment<<" units"<<endl;
02104
02105 if ( adjustment > 0 )
02106 pos += indentWidth;
02107 else if ( adjustment < 0 )
02108 pos -= indentWidth;
02109
02110 ln = line.line();
02111 fc = doc->plainKateTextLine( ln )->firstChar();
02112
02113
02114
02115
02116
02117 if ( fc == pos )
02118 return;
02119
02120 if ( fc > 0 )
02121 doc->removeText (ln, 0, ln, fc );
02122
02123 if ( pos > 0 )
02124 indent = tabString( pos );
02125
02126 if ( pos > 0 )
02127 doc->insertText (ln, 0, indent);
02128
02129
02130 line.setCol( pos );
02131 }
02132
02133 void KateVarIndent::processSection (KateDocCursor &begin, KateDocCursor &end)
02134 {
02135 KateDocCursor cur = begin;
02136 while (cur.line() <= end.line())
02137 {
02138 processLine (cur);
02139 if (!cur.gotoNextLine())
02140 break;
02141 }
02142 }
02143
02144 void KateVarIndent::slotVariableChanged( const QString &var, const QString &val )
02145 {
02146 if ( ! var.startsWith("var-indent") )
02147 return;
02148
02149 if ( var == "var-indent-indent-after" )
02150 d->reIndentAfter.setPattern( val );
02151 else if ( var == "var-indent-indent" )
02152 d->reIndent.setPattern( val );
02153 else if ( var == "var-indent-unindent" )
02154 d->reUnindent.setPattern( val );
02155 else if ( var == "var-indent-triggerchars" )
02156 d->triggers = val;
02157 else if ( var == "var-indent-handle-couples" )
02158 {
02159 d->couples = 0;
02160 QStringList l = QStringList::split( " ", val );
02161 if ( l.contains("parens") ) d->couples |= Parens;
02162 if ( l.contains("braces") ) d->couples |= Braces;
02163 if ( l.contains("brackets") ) d->couples |= Brackets;
02164 }
02165 else if ( var == "var-indent-couple-attribute" )
02166 {
02167
02168 KateHlItemDataList items;
02169 doc->highlight()->getKateHlItemDataListCopy (0, items);
02170
02171 for (uint i=0; i<items.count(); i++)
02172 {
02173 if ( items.at(i)->name.section( ':', 1 ) == val )
02174 {
02175 d->coupleAttrib = i;
02176 break;
02177 }
02178 }
02179 }
02180 }
02181
02182 int KateVarIndent::coupleBalance ( int line, const QChar &open, const QChar &close ) const
02183 {
02184 int r = 0;
02185
02186 KateTextLine::Ptr ln = doc->plainKateTextLine( line );
02187 if ( ! ln || ! ln->length() ) return 0;
02188
02189 for ( uint z=0; z < ln->length(); z++ )
02190 {
02191 QChar c = ln->getChar( z );
02192 if ( ln->attribute(z) == d->coupleAttrib )
02193 {
02194 kdDebug(13030)<<z<<", "<<c<<endl;
02195 if (c == open)
02196 r++;
02197 else if (c == close)
02198 r--;
02199 }
02200 }
02201 return r;
02202 }
02203
02204 bool KateVarIndent::hasRelevantOpening( const KateDocCursor &end ) const
02205 {
02206 KateDocCursor cur = end;
02207 int count = 1;
02208
02209 QChar close = cur.currentChar();
02210 QChar opener;
02211 if ( close == '}' ) opener = '{';
02212 else if ( close = ')' ) opener = '(';
02213 else if (close = ']' ) opener = '[';
02214 else return false;
02215
02216
02217 while (cur.moveBackward(1))
02218 {
02219 if (cur.currentAttrib() == d->coupleAttrib)
02220 {
02221 QChar ch = cur.currentChar();
02222 if (ch == opener)
02223 count--;
02224 else if (ch == close)
02225 count++;
02226
02227 if (count == 0)
02228 return true;
02229 }
02230 }
02231
02232 return false;
02233 }
02234
02235
02236
02237
02238