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