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