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