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*(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 int first = indentLine->firstChar();
01865
01866 const int attrib = indentLine->attribute(first);
01867 if( first >= 0 && (attrib == 0 || attrib == symbolAttrib) && indentLine->getChar(first) == '{' )
01868 return whitespaceToKeyword;
01869
01870
01871
01872
01873
01874
01875
01876
01877 return indentString + whitespaceToKeyword;
01878 }
01879
01880 QString KateCSAndSIndent::calcIndentInBrace(const KateDocCursor &indentCursor, const KateDocCursor &braceCursor, int bracePos)
01881 {
01882 KateTextLine::Ptr braceLine = doc->plainKateTextLine(braceCursor.line());
01883 const int braceFirst = braceLine->firstChar();
01884
01885 QString whitespaceToOpenBrace = initialWhitespace( braceLine, bracePos, false );
01886
01887
01888
01889
01890
01891 {
01892 if( braceFirst >= 0 && braceLine->attribute(braceFirst) == keywordAttrib &&
01893 braceLine->stringAtPos( braceFirst, QString::fromLatin1( "namespace" ) ) )
01894 return continuationIndent(indentCursor) + whitespaceToOpenBrace;
01895
01896 if( braceCursor.line() > 0 )
01897 {
01898 KateTextLine::Ptr prevLine = doc->plainKateTextLine(braceCursor.line() - 1);
01899 int firstPrev = prevLine->firstChar();
01900 if( firstPrev >= 0 && prevLine->attribute(firstPrev) == keywordAttrib &&
01901 prevLine->stringAtPos( firstPrev, QString::fromLatin1( "namespace" ) ) )
01902 return continuationIndent(indentCursor) + whitespaceToOpenBrace;
01903 }
01904 }
01905
01906 KateTextLine::Ptr indentLine = doc->plainKateTextLine(indentCursor.line());
01907 const int indentFirst = indentLine->firstChar();
01908
01909
01910 if( indentFirst >= 0 && indentLine->getChar(indentFirst) == '}' )
01911 return whitespaceToOpenBrace;
01912
01913
01914
01915 if ( indentFirst >= 0 && indentLine->attribute(indentFirst) == symbolAttrib &&
01916 indentLine->getChar(indentFirst) == ':' && indentLine->getChar(indentFirst+1) != ':' )
01917 {
01918 return indentString + indentString + whitespaceToOpenBrace;
01919 }
01920
01921 const bool continuation = inStatement(indentCursor);
01922
01923 if( !continuation && startsWithLabel( indentCursor.line() ) )
01924 return whitespaceToOpenBrace;
01925
01926
01927 QString continuationIndent = continuation ? indentString : QString::null;
01928 return indentString + continuationIndent + whitespaceToOpenBrace;
01929 }
01930
01931 void KateCSAndSIndent::processChar(QChar c)
01932 {
01933
01934 static const QString triggers("}{)]/:;#n");
01935 if (triggers.find(c) == -1)
01936 return;
01937
01938
01939
01940 KateView *view = doc->activeView();
01941 KateDocCursor begin(view->cursorLine(), 0, doc);
01942
01943 KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
01944 if ( c == 'n' )
01945 {
01946 int first = textLine->firstChar();
01947 if( first < 0 || textLine->getChar(first) != '#' )
01948 return;
01949 }
01950
01951 if ( textLine->attribute( begin.col() ) == doxyCommentAttrib )
01952 {
01953
01954 if ( c == '/' )
01955 {
01956 int first = textLine->firstChar();
01957
01958
01959 if ( first != -1
01960 && textLine->getChar( first ) == '*'
01961 && textLine->nextNonSpaceChar( first+1 ) == view->cursorColumn()-1 )
01962 doc->removeText( view->cursorLine(), first+1, view->cursorLine(), view->cursorColumn()-1);
01963 }
01964
01965
01966 return;
01967 }
01968
01969 processLine(begin);
01970 }
01971
01972
01973
01974
01975 class KateVarIndentPrivate {
01976 public:
01977 QRegExp reIndentAfter, reIndent, reUnindent;
01978 QString triggers;
01979 uint couples;
01980 uchar coupleAttrib;
01981 };
01982
01983 KateVarIndent::KateVarIndent( KateDocument *doc )
01984 : QObject( 0, "variable indenter"), KateNormalIndent( doc )
01985 {
01986 d = new KateVarIndentPrivate;
01987 d->reIndentAfter = QRegExp( doc->variable( "var-indent-indent-after" ) );
01988 d->reIndent = QRegExp( doc->variable( "var-indent-indent" ) );
01989 d->reUnindent = QRegExp( doc->variable( "var-indent-unindent" ) );
01990 d->triggers = doc->variable( "var-indent-triggerchars" );
01991 d->coupleAttrib = 0;
01992
01993 slotVariableChanged( "var-indent-couple-attribute", doc->variable( "var-indent-couple-attribute" ) );
01994 slotVariableChanged( "var-indent-handle-couples", doc->variable( "var-indent-handle-couples" ) );
01995
01996
01997 connect( doc, SIGNAL(variableChanged( const QString&, const QString&) ),
01998 this, SLOT(slotVariableChanged( const QString&, const QString& )) );
01999 }
02000
02001 KateVarIndent::~KateVarIndent()
02002 {
02003 delete d;
02004 }
02005
02006 void KateVarIndent::processNewline ( KateDocCursor &begin, bool )
02007 {
02008
02009 KateDocCursor left( begin.line()-1, 0, doc );
02010 processLine( left );
02011 processLine( begin );
02012 }
02013
02014 void KateVarIndent::processChar ( QChar c )
02015 {
02016
02017 if ( d->triggers.contains( c ) )
02018 {
02019 KateTextLine::Ptr ln = doc->plainKateTextLine( doc->activeView()->cursorLine() );
02020 if ( ln->attribute( doc->activeView()->cursorColumn()-1 ) == commentAttrib )
02021 return;
02022
02023 KateView *view = doc->activeView();
02024 KateDocCursor begin( view->cursorLine(), 0, doc );
02025 kdDebug(13030)<<"variable indenter: process char '"<<c<<", line "<<begin.line()<<endl;
02026 processLine( begin );
02027 }
02028 }
02029
02030 void KateVarIndent::processLine ( KateDocCursor &line )
02031 {
02032 updateConfig();
02033
02034 QString indent;
02035
02036
02037
02038 int ln = line.line();
02039 int pos = -1;
02040 KateTextLine::Ptr ktl = doc->plainKateTextLine( ln );
02041 if ( ! ktl ) return;
02042
02043
02044 KateView *v = doc->activeView();
02045 if ( (ktl->firstChar() < 0) && (!v || (int)v->cursorLine() != ln ) )
02046 return;
02047
02048 int fc;
02049 if ( ln > 0 )
02050 do
02051 {
02052
02053 ktl = doc->plainKateTextLine( --ln );
02054 fc = ktl->firstChar();
02055 if ( ktl->attribute( fc ) != commentAttrib )
02056 pos = fc;
02057 }
02058 while ( (ln > 0) && (pos < 0) );
02059
02060 if ( pos < 0 )
02061 pos = 0;
02062 else
02063 pos = ktl->cursorX( pos, tabWidth );
02064
02065 int adjustment = 0;
02066
02067
02068
02069 if ( d->couples & Parens && coupleBalance( ln, '(', ')' ) > 0 )
02070 adjustment++;
02071 else if ( d->couples & Braces && coupleBalance( ln, '{', '}' ) > 0 )
02072 adjustment++;
02073 else if ( d->couples & Brackets && coupleBalance( ln, '[', ']' ) > 0 )
02074 adjustment++;
02075
02076
02077
02078
02079
02080
02081
02082
02083
02084
02085
02086 {
02087 KateTextLine::Ptr tl = doc->plainKateTextLine( line.line() );
02088 int i = tl->firstChar();
02089 if ( i > -1 )
02090 {
02091 QChar ch = tl->getChar( i );
02092 uchar at = tl->attribute( i );
02093 kdDebug(13030)<<"attrib is "<<at<<endl;
02094 if ( d->couples & Parens && ch == ')'
02095 && ( at == d->coupleAttrib
02096 || (! at && hasRelevantOpening( KateDocCursor( line.line(), i, doc ) ))
02097 )
02098 )
02099 adjustment--;
02100 else if ( d->couples & Braces && ch == '}'
02101 && ( at == d->coupleAttrib
02102 || (! at && hasRelevantOpening( KateDocCursor( line.line(), i, doc ) ))
02103 )
02104 )
02105 adjustment--;
02106 else if ( d->couples & Brackets && ch == ']'
02107 && ( at == d->coupleAttrib
02108 || (! at && hasRelevantOpening( KateDocCursor( line.line(), i, doc ) ))
02109 )
02110 )
02111 adjustment--;
02112 }
02113 }
02114 #define ISCOMMENTATTR(attr) (attr==commentAttrib||attr==doxyCommentAttrib)
02115 #define ISCOMMENT (ISCOMMENTATTR(ktl->attribute(ktl->firstChar()))||ISCOMMENTATTR(ktl->attribute(matchpos)))
02116
02117
02118 kdDebug(13030)<<"variable indenter: starting indent: "<<pos<<endl;
02119
02120 int matchpos = 0;
02121 if ( ktl && ! d->reIndentAfter.isEmpty()
02122 && (matchpos = d->reIndentAfter.search( doc->textLine( ln ) )) > -1
02123 && ! ISCOMMENT )
02124 adjustment++;
02125
02126
02127 ktl = doc->plainKateTextLine( line.line() );
02128 if ( ! d->reIndent.isEmpty()
02129 && (matchpos = d->reIndent.search( doc->textLine( line.line() ) )) > -1
02130 && ! ISCOMMENT )
02131 adjustment++;
02132
02133
02134 if ( ! d->reUnindent.isEmpty()
02135 && (matchpos = d->reUnindent.search( doc->textLine( line.line() ) )) > -1
02136 && ! ISCOMMENT )
02137 adjustment--;
02138
02139 kdDebug(13030)<<"variable indenter: adjusting by "<<adjustment<<" units"<<endl;
02140
02141 if ( adjustment > 0 )
02142 pos += indentWidth;
02143 else if ( adjustment < 0 )
02144 pos -= indentWidth;
02145
02146 ln = line.line();
02147 fc = doc->plainKateTextLine( ln )->firstChar();
02148
02149
02150
02151
02152
02153 if ( fc == pos )
02154 return;
02155
02156 if ( fc > 0 )
02157 doc->removeText (ln, 0, ln, fc );
02158
02159 if ( pos > 0 )
02160 indent = tabString( pos );
02161
02162 if ( pos > 0 )
02163 doc->insertText (ln, 0, indent);
02164
02165
02166 line.setCol( pos );
02167 }
02168
02169 void KateVarIndent::processSection (const KateDocCursor &begin, const KateDocCursor &end)
02170 {
02171 KateDocCursor cur = begin;
02172 while (cur.line() <= end.line())
02173 {
02174 processLine (cur);
02175 if (!cur.gotoNextLine())
02176 break;
02177 }
02178 }
02179
02180 void KateVarIndent::slotVariableChanged( const QString &var, const QString &val )
02181 {
02182 if ( ! var.startsWith("var-indent") )
02183 return;
02184
02185 if ( var == "var-indent-indent-after" )
02186 d->reIndentAfter.setPattern( val );
02187 else if ( var == "var-indent-indent" )
02188 d->reIndent.setPattern( val );
02189 else if ( var == "var-indent-unindent" )
02190 d->reUnindent.setPattern( val );
02191 else if ( var == "var-indent-triggerchars" )
02192 d->triggers = val;
02193 else if ( var == "var-indent-handle-couples" )
02194 {
02195 d->couples = 0;
02196 QStringList l = QStringList::split( " ", val );
02197 if ( l.contains("parens") ) d->couples |= Parens;
02198 if ( l.contains("braces") ) d->couples |= Braces;
02199 if ( l.contains("brackets") ) d->couples |= Brackets;
02200 }
02201 else if ( var == "var-indent-couple-attribute" )
02202 {
02203
02204 KateHlItemDataList items;
02205 doc->highlight()->getKateHlItemDataListCopy (0, items);
02206
02207 for (uint i=0; i<items.count(); i++)
02208 {
02209 if ( items.at(i)->name.section( ':', 1 ) == val )
02210 {
02211 d->coupleAttrib = i;
02212 break;
02213 }
02214 }
02215 }
02216 }
02217
02218 int KateVarIndent::coupleBalance ( int line, const QChar &open, const QChar &close ) const
02219 {
02220 int r = 0;
02221
02222 KateTextLine::Ptr ln = doc->plainKateTextLine( line );
02223 if ( ! ln || ! ln->length() ) return 0;
02224
02225 for ( uint z=0; z < ln->length(); z++ )
02226 {
02227 QChar c = ln->getChar( z );
02228 if ( ln->attribute(z) == d->coupleAttrib )
02229 {
02230 kdDebug(13030)<<z<<", "<<c<<endl;
02231 if (c == open)
02232 r++;
02233 else if (c == close)
02234 r--;
02235 }
02236 }
02237 return r;
02238 }
02239
02240 bool KateVarIndent::hasRelevantOpening( const KateDocCursor &end ) const
02241 {
02242 KateDocCursor cur = end;
02243 int count = 1;
02244
02245 QChar close = cur.currentChar();
02246 QChar opener;
02247 if ( close == '}' ) opener = '{';
02248 else if ( close = ')' ) opener = '(';
02249 else if (close = ']' ) opener = '[';
02250 else return false;
02251
02252
02253 while (cur.moveBackward(1))
02254 {
02255 if (cur.currentAttrib() == d->coupleAttrib)
02256 {
02257 QChar ch = cur.currentChar();
02258 if (ch == opener)
02259 count--;
02260 else if (ch == close)
02261 count++;
02262
02263 if (count == 0)
02264 return true;
02265 }
02266 }
02267
02268 return false;
02269 }
02270
02271
02272
02273
02274
02275 KateScriptIndent::KateScriptIndent( KateDocument *doc )
02276 : KateNormalIndent( doc )
02277 {
02278 m_script=KateFactory::self()->indentScript ("script-indent-c1-test");
02279 }
02280
02281 KateScriptIndent::~KateScriptIndent()
02282 {
02283 }
02284
02285 void KateScriptIndent::processNewline( KateDocCursor &begin, bool needContinue )
02286 {
02287 kdDebug(13030) << "processNewline" << endl;
02288 KateView *view = doc->activeView();
02289
02290 if (view)
02291 {
02292 QString errorMsg;
02293
02294 QTime t;
02295 t.start();
02296 kdDebug(13030)<<"calling m_script.processChar"<<endl;
02297 if( !m_script.processNewline( view, begin, needContinue , errorMsg ) )
02298 {
02299 kdDebug(13030) << "Error in script-indent: " << errorMsg << endl;
02300 }
02301 kdDebug(13030) << "ScriptIndent::TIME in ms: " << t.elapsed() << endl;
02302 }
02303 }
02304
02305 void KateScriptIndent::processChar( QChar c )
02306 {
02307 kdDebug(13030) << "processChar" << endl;
02308 KateView *view = doc->activeView();
02309
02310 if (view)
02311 {
02312 QString errorMsg;
02313
02314 QTime t;
02315 t.start();
02316 kdDebug(13030)<<"calling m_script.processChar"<<endl;
02317 if( !m_script.processChar( view, c , errorMsg ) )
02318 {
02319 kdDebug(13030) << "Error in script-indent: " << errorMsg << endl;
02320 }
02321 kdDebug(13030) << "ScriptIndent::TIME in ms: " << t.elapsed() << endl;
02322 }
02323 }
02324
02325 void KateScriptIndent::processLine (KateDocCursor &line)
02326 {
02327 kdDebug(13030) << "processLine" << endl;
02328 KateView *view = doc->activeView();
02329
02330 if (view)
02331 {
02332 QString errorMsg;
02333
02334 QTime t;
02335 t.start();
02336 kdDebug(13030)<<"calling m_script.processLine"<<endl;
02337 if( !m_script.processLine( view, line , errorMsg ) )
02338 {
02339 kdDebug(13030) << "Error in script-indent: " << errorMsg << endl;
02340 }
02341 kdDebug(13030) << "ScriptIndent::TIME in ms: " << t.elapsed() << endl;
02342 }
02343 }
02344
02345
02346
02347 #include <qlabel.h>
02348 ScriptIndentConfigPage::ScriptIndentConfigPage ( QWidget *parent, const char *name )
02349 : IndenterConfigPage(parent, name)
02350 {
02351 QLabel* hello = new QLabel("Hello world! Dummy for testing purpose.", this);
02352 hello->show();
02353 }
02354
02355 ScriptIndentConfigPage::~ScriptIndentConfigPage ()
02356 {
02357 }
02358
02359 void ScriptIndentConfigPage::apply ()
02360 {
02361 kdDebug(13030) << "ScriptIndentConfigPagE::apply() was called, save config options now!" << endl;
02362 }
02363
02364
02365