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