00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "kateautoindent.h"
00020
00021 #include "kateconfig.h"
00022 #include "katehighlight.h"
00023 #include "kateview.h"
00024
00025 #include <klocale.h>
00026
00027
00028
00029 KateAutoIndent *KateAutoIndent::createIndenter (KateDocument *doc, uint mode)
00030 {
00031 if (mode == KateDocumentConfig::imCStyle)
00032 return new KateCSmartIndent (doc);
00033 else if (mode == KateDocumentConfig::imPythonStyle)
00034 return new KatePythonIndent (doc);
00035
00036 return new KateAutoIndent (doc);
00037 }
00038
00039 QStringList KateAutoIndent::listModes ()
00040 {
00041 QStringList l;
00042
00043 l << modeDescription(KateDocumentConfig::imNormal);
00044 l << modeDescription(KateDocumentConfig::imCStyle);
00045 l << modeDescription(KateDocumentConfig::imPythonStyle);
00046
00047 return l;
00048 }
00049
00050 QString KateAutoIndent::modeName (uint mode)
00051 {
00052 if (mode == KateDocumentConfig::imCStyle)
00053 return QString ("cstyle");
00054 else if (mode == KateDocumentConfig::imPythonStyle)
00055 return QString ("python");
00056
00057 return QString ("normal");
00058 }
00059
00060 QString KateAutoIndent::modeDescription (uint mode)
00061 {
00062 if (mode == KateDocumentConfig::imCStyle)
00063 return i18n ("C Style");
00064 else if (mode == KateDocumentConfig::imPythonStyle)
00065 return i18n ("Python Style");
00066
00067 return i18n ("Normal");
00068 }
00069
00070 uint KateAutoIndent::modeNumber (const QString &name)
00071 {
00072 if (modeName(KateDocumentConfig::imCStyle) == name)
00073 return KateDocumentConfig::imCStyle;
00074 else if (modeName(KateDocumentConfig::imPythonStyle) == name)
00075 return KateDocumentConfig::imPythonStyle;
00076
00077 return KateDocumentConfig::imNormal;
00078 }
00079
00080 KateAutoIndent::KateAutoIndent (KateDocument *_doc)
00081 : doc(_doc)
00082 {
00083 }
00084 KateAutoIndent::~KateAutoIndent ()
00085 {
00086 }
00087
00088 void KateAutoIndent::updateConfig ()
00089 {
00090 KateDocumentConfig *config = doc->config();
00091
00092 useSpaces = config->configFlags() & KateDocument::cfSpaceIndent;
00093 tabWidth = config->tabWidth();
00094 indentWidth = (useSpaces) ? config->indentationWidth() : tabWidth;
00095
00096 commentAttrib = 0;
00097 ItemDataList items;
00098 doc->highlight()->getItemDataListCopy (0, items);
00099
00100 for (uint i=0; i<items.count(); i++)
00101 {
00102 if (items.at(i)->name.find("Comment") != -1)
00103 {
00104 commentAttrib = i;
00105 break;
00106 }
00107 }
00108 }
00109
00110 bool KateAutoIndent::isBalanced (KateDocCursor &begin, const KateDocCursor &end, QChar open, QChar close) const
00111 {
00112 int parenOpen = 0;
00113 int curLine = begin.line();
00114 uchar attrib = 0;
00115 bool parenFound = false;
00116
00117 TextLine::Ptr textLine = doc->kateTextLine(curLine);
00118
00119
00120
00121
00122 while (begin < end)
00123 {
00124 if (curLine != begin.line())
00125 {
00126 curLine = begin.line();
00127 textLine = doc->kateTextLine(curLine);
00128 }
00129
00130 QChar c = textLine->getChar(begin.col());
00131 if (c == open)
00132 {
00133 if (!parenFound)
00134 {
00135 parenFound = true;
00136 attrib = textLine->attribute(begin.col());
00137 }
00138 else if (textLine->attribute(begin.col()) != attrib)
00139 {
00140 begin.moveForward(1);
00141 continue;
00142 }
00143
00144 parenOpen ++;
00145 }
00146 else if (c == close && textLine->attribute(begin.col()) == attrib)
00147 {
00148 parenOpen --;
00149 }
00150 else if (!parenFound && !c.isSpace())
00151 {
00152 return false;
00153 }
00154
00155 if (parenFound && parenOpen <= 0)
00156 return true;
00157
00158 begin.moveForward(1);
00159 }
00160
00161 return false;
00162 }
00163
00164 bool KateAutoIndent::skipBlanks (KateDocCursor &cur, KateDocCursor &max, bool newline) const
00165 {
00166 int curLine = cur.line();
00167 if (newline)
00168 cur.moveForward(1);
00169
00170 if (cur >= max)
00171 return false;
00172
00173 TextLine::Ptr textLine = doc->kateTextLine(curLine);
00174 do
00175 {
00176 if (textLine->attribute(cur.col()) != commentAttrib)
00177 {
00178 QChar c = textLine->getChar(cur.col());
00179 if (!c.isNull() && !c.isSpace())
00180 break;
00181 }
00182
00183
00184 if (!cur.moveForward(1))
00185 break;
00186 if (curLine != cur.line())
00187 {
00188 if (!newline)
00189 break;
00190 textLine = doc->kateTextLine(curLine = cur.line());
00191 cur.setCol(0);
00192 }
00193 } while (cur < max);
00194
00195 if (cur > max)
00196 cur = max;
00197 return true;
00198 }
00199
00200 uint KateAutoIndent::measureIndent (KateDocCursor &cur) const
00201 {
00202 if (useSpaces)
00203 return cur.col();
00204
00205 return doc->kateTextLine(cur.line())->cursorX(cur.col(), tabWidth);
00206 }
00207
00208 QString KateAutoIndent::tabString(uint pos) const
00209 {
00210 QString s;
00211 pos = QMIN (pos, 80);
00212
00213 if (!useSpaces)
00214 {
00215 while (pos >= tabWidth)
00216 {
00217 s += '\t';
00218 pos -= tabWidth;
00219 }
00220 }
00221 while (pos > 0)
00222 {
00223 s += ' ';
00224 pos--;
00225 }
00226 return s;
00227 }
00228
00229 void KateAutoIndent::processNewline (KateDocCursor &begin, bool )
00230 {
00231 int line = begin.line() - 1;
00232 int pos = begin.col();
00233
00234 while ((line > 0) && (pos < 0))
00235 pos = doc->kateTextLine(--line)->firstChar();
00236
00237 if (pos > 0)
00238 {
00239 uint indent = doc->kateTextLine(line)->cursorX(pos, tabWidth);
00240 QString filler = tabString (indent);
00241 doc->insertText (begin.line(), 0, filler);
00242 begin.setCol(filler.length());
00243 }
00244 else
00245 begin.setCol(0);
00246 }
00247
00248
00249
00250
00251
00252 KateCSmartIndent::KateCSmartIndent (KateDocument *doc)
00253 : KateAutoIndent (doc),
00254 allowSemi (false)
00255 {
00256
00257 }
00258
00259 KateCSmartIndent::~KateCSmartIndent ()
00260 {
00261
00262 }
00263
00264 void KateCSmartIndent::processNewline (KateDocCursor &begin, bool needContinue)
00265 {
00266 uint indent = calcIndent (begin, needContinue);
00267
00268 if (indent > 0)
00269 {
00270 QString filler = tabString (indent);
00271 doc->insertText (begin.line(), 0, filler);
00272 begin.setCol(filler.length());
00273 }
00274 else
00275 {
00276
00277 KateAutoIndent::processNewline (begin, needContinue);
00278 }
00279 }
00280
00281 void KateCSmartIndent::processChar(QChar c)
00282 {
00283 if (c != '}' && c != '{' && c != '#' && c != ':')
00284 return;
00285 KateView *view = doc->activeView();
00286 KateDocCursor begin(view->cursorLine(), view->cursorColumnReal() - 1, doc);
00287
00288
00289 TextLine::Ptr textLine = doc->kateTextLine(begin.line());
00290 if (c != ':')
00291 {
00292 if (textLine->firstChar() != begin.col())
00293 return;
00294 }
00295
00296
00297 if (c == '#')
00298 {
00299 doc->removeText(begin.line(), 0, begin.line(), begin.col());
00300 }
00301 else if (c == ':')
00302 {
00303 int lineStart = textLine->firstChar();
00304 if (textLine->stringAtPos (lineStart, "public") ||
00305 textLine->stringAtPos (lineStart, "private") ||
00306 textLine->stringAtPos (lineStart, "protected") ||
00307 textLine->stringAtPos (lineStart, "case"))
00308 {
00309 int line = begin.line();
00310 int pos = 0;
00311 while (line > 0)
00312 {
00313 textLine = doc->kateTextLine(--line);
00314 pos = textLine->lastChar();
00315 if (pos >= 0 && textLine->getChar(pos) == '{')
00316 return;
00317 if (pos >= 0 && textLine->getChar(pos) == ':')
00318 break;
00319 }
00320
00321 KateDocCursor temp(line, textLine->firstChar(), doc);
00322 doc->removeText(begin.line(), 0, begin.line(), lineStart);
00323 doc->insertText(begin.line(), 0, tabString( measureIndent(temp) ));
00324 }
00325 }
00326 else
00327 {
00328 int indent = calcIndent(begin, false);
00329 if (c == '}')
00330 {
00331 if (indent - (int)indentWidth >= 0)
00332 indent -= indentWidth;
00333 }
00334
00335 if (indent > (int)measureIndent(begin))
00336 indent = measureIndent(begin);
00337
00338 doc->removeText(begin.line(), 0, begin.line(), begin.col());
00339 doc->insertText(begin.line(), 0, tabString(indent));
00340 }
00341 }
00342
00343 uint KateCSmartIndent::calcIndent(KateDocCursor &begin, bool needContinue)
00344 {
00345 TextLine::Ptr textLine;
00346 KateDocCursor cur = begin;
00347
00348 uint anchorIndent = 0;
00349 int anchorPos = 0;
00350 bool found = false;
00351 bool isSpecial = false;
00352
00353
00354 while (cur.gotoPreviousLine())
00355 {
00356 isSpecial = found = false;
00357 textLine = doc->kateTextLine(cur.line());
00358
00359
00360 int pos = textLine->lastChar();
00361 int openCount = 0;
00362 int otherAnchor = -1;
00363 do
00364 {
00365 if (textLine->attribute (pos) != commentAttrib)
00366 {
00367 QChar tc = textLine->getChar (pos);
00368 if ((tc == ';' || tc == ':') && otherAnchor == -1)
00369 otherAnchor = pos;
00370 else if (tc == '}')
00371 openCount --;
00372 else if (tc == '{')
00373 {
00374 openCount ++;
00375 if (openCount == 1)
00376 break;
00377 }
00378 else if (tc == '(' || tc == ')')
00379 break;
00380 }
00381 } while (--pos >= textLine->firstChar());
00382
00383 if (openCount != 0 || otherAnchor != -1)
00384 {
00385 found = true;
00386 QChar c;
00387 if (openCount > 0)
00388 c = '{';
00389 else if (openCount < 0)
00390 c = '}';
00391 else if (otherAnchor >= 0)
00392 c = textLine->getChar (otherAnchor);
00393
00394 int specialIndent = 0;
00395 if (c == ':' && needContinue)
00396 {
00397 QChar ch;
00398 specialIndent = textLine->firstChar();
00399 if (textLine->stringAtPos(specialIndent, "case"))
00400 ch = textLine->getChar(specialIndent + 4);
00401 else if (textLine->stringAtPos(specialIndent, "public"))
00402 ch = textLine->getChar(specialIndent + 6);
00403 else if (textLine->stringAtPos(specialIndent, "private"))
00404 ch = textLine->getChar(specialIndent + 7);
00405 else if (textLine->stringAtPos(specialIndent, "protected"))
00406 ch = textLine->getChar(specialIndent + 9);
00407
00408 if (ch.isNull() || (!ch.isSpace() && ch != '(' && ch != ':'))
00409 continue;
00410
00411 KateDocCursor lineBegin = cur;
00412 lineBegin.setCol(specialIndent);
00413 specialIndent = measureIndent(lineBegin);
00414 isSpecial = true;
00415 }
00416
00417
00418 KateDocCursor skip = cur;
00419 skip.setCol(textLine->lastChar());
00420 bool result = skipBlanks(skip, begin, true);
00421
00422 anchorPos = skip.col();
00423 anchorIndent = measureIndent(skip);
00424
00425
00426 if (result && skip < begin)
00427 {
00428 cur = skip;
00429 break;
00430 }
00431 else if (isSpecial)
00432 {
00433 anchorIndent = specialIndent;
00434 break;
00435 }
00436
00437
00438 if ((c == '{' || c == '}') && textLine->getChar(textLine->firstChar()) == c)
00439 {
00440 cur.setCol(anchorPos = textLine->firstChar());
00441 anchorIndent = measureIndent (cur);
00442 break;
00443 }
00444 }
00445 }
00446
00447 if (!found)
00448 return 0;
00449
00450 uint continueIndent = (needContinue) ? calcContinue (cur, begin) : 0;
00451
00452
00453
00454 QChar lastChar = textLine->getChar (anchorPos);
00455 int openCount = 0;
00456 if (cur < begin)
00457 {
00458 do
00459 {
00460 if (!skipBlanks(cur, begin, true))
00461 return 0;
00462
00463 QChar tc = cur.currentChar();
00464 if (cur == begin || tc.isNull())
00465 break;
00466
00467 if (!tc.isSpace() && cur < begin)
00468 {
00469 if (tc == '{')
00470 openCount ++;
00471 else if (tc == '}')
00472 openCount --;
00473
00474 lastChar = tc;
00475 }
00476 } while (cur.validPosition() && cur < begin);
00477 }
00478
00479 if (openCount > 0)
00480 lastChar = '{';
00481
00482 uint indent = 0;
00483 if (lastChar == '{' || (lastChar == ':' && isSpecial && needContinue))
00484 {
00485 indent = anchorIndent + indentWidth;
00486 }
00487 else if (lastChar == '}')
00488 {
00489 indent = anchorIndent;
00490 }
00491 else if (lastChar == ';')
00492 {
00493 indent = anchorIndent + ((allowSemi && needContinue) ? continueIndent : 0);
00494 }
00495 else if (!lastChar.isNull() && anchorIndent != 0)
00496 {
00497 indent = anchorIndent + continueIndent;
00498 }
00499
00500 return indent;
00501 }
00502
00503 uint KateCSmartIndent::calcContinue(KateDocCursor &start, KateDocCursor &end)
00504 {
00505 KateDocCursor cur = start;
00506
00507 bool needsBalanced = false;
00508 bool isFor = false;
00509 allowSemi = false;
00510
00511 TextLine::Ptr textLine = doc->kateTextLine(cur.line());
00512 uint length = textLine->length();
00513
00514 if (textLine->getChar(cur.col()) == '}')
00515 {
00516 skipBlanks(cur, end, true);
00517 if (cur.line() != start.line())
00518 textLine = doc->kateTextLine(cur.line());
00519
00520 if (textLine->stringAtPos(cur.col(), "else"))
00521 cur.setCol(cur.col() + 4);
00522 else
00523 return indentWidth * 2;
00524 }
00525 else if (textLine->stringAtPos(cur.col(), "else"))
00526 {
00527 cur.setCol(cur.col() + 4);
00528 if (textLine->stringAtPos(textLine->nextNonSpaceChar(cur.col()), "if"))
00529 {
00530 cur.setCol(textLine->nextNonSpaceChar(cur.col()) + 2);
00531 needsBalanced = true;
00532 }
00533 }
00534 else if (textLine->stringAtPos(cur.col(), "do"))
00535 {
00536 cur.setCol(cur.col() + 2);
00537 }
00538 else if (textLine->stringAtPos(cur.col(), "for"))
00539 {
00540 cur.setCol(cur.col() + 3);
00541 isFor = needsBalanced = true;
00542 }
00543 else if (textLine->stringAtPos(cur.col(), "if"))
00544 {
00545 cur.setCol(cur.col() + 2);
00546 needsBalanced = true;
00547 }
00548 else if (textLine->stringAtPos(cur.col(), "while"))
00549 {
00550 cur.setCol(cur.col() + 5);
00551 needsBalanced = true;
00552 }
00553 else
00554 return indentWidth * 2;
00555
00556 if (needsBalanced && !isBalanced (cur, end, QChar('('), QChar(')')))
00557 {
00558 allowSemi = isFor;
00559 return indentWidth * 2;
00560 }
00561
00562 skipBlanks(cur, end, false);
00563 if (cur == end || (cur.col() == (int)length-1))
00564 return indentWidth;
00565
00566 if (skipBlanks(cur, end, true))
00567 {
00568 if (cur == end)
00569 return indentWidth;
00570 else
00571 return indentWidth + calcContinue(cur, end);
00572 }
00573
00574 return 0;
00575 }
00576
00577
00578
00579
00580
00581 QRegExp KatePythonIndent::endWithColon = QRegExp( "^[^#]*:\\s*(#.*)?$" );
00582 QRegExp KatePythonIndent::stopStmt = QRegExp( "^\\s*(break|continue|raise|return|pass)\\b.*" );
00583 QRegExp KatePythonIndent::blockBegin = QRegExp( "^\\s*(def|if|elif|else|for|while|try)\\b.*" );
00584
00585 KatePythonIndent::KatePythonIndent (KateDocument *doc)
00586 : KateAutoIndent (doc)
00587 {
00588 }
00589 KatePythonIndent::~KatePythonIndent ()
00590 {
00591 }
00592
00593 void KatePythonIndent::processNewline (KateDocCursor &begin, bool )
00594 {
00595 int prevLine = begin.line() - 1;
00596 int prevPos = begin.col();
00597
00598 while ((prevLine > 0) && (prevPos < 0))
00599 prevPos = doc->kateTextLine(--prevLine)->firstChar();
00600
00601 int prevBlock = prevLine;
00602 int prevBlockPos = prevPos;
00603 int extraIndent = calcExtra (prevBlock, prevBlockPos, begin);
00604
00605 int indent = doc->kateTextLine(prevBlock)->cursorX(prevBlockPos, tabWidth);
00606 if (extraIndent == 0)
00607 {
00608 if (!stopStmt.exactMatch(doc->kateTextLine(prevLine)->string()))
00609 {
00610 if (endWithColon.exactMatch(doc->kateTextLine(prevLine)->string()))
00611 indent += indentWidth;
00612 else
00613 indent = doc->kateTextLine(prevLine)->cursorX(prevPos, tabWidth);
00614 }
00615 }
00616 else
00617 indent += extraIndent;
00618
00619 if (indent > 0)
00620 {
00621 QString filler = tabString (indent);
00622 doc->insertText (begin.line(), 0, filler);
00623 begin.setCol(filler.length());
00624 }
00625 else
00626 begin.setCol(0);
00627 }
00628
00629 int KatePythonIndent::calcExtra (int &prevBlock, int &pos, KateDocCursor &end)
00630 {
00631 int nestLevel = 0;
00632 bool levelFound = false;
00633 while ((prevBlock > 0))
00634 {
00635 if (blockBegin.exactMatch(doc->kateTextLine(prevBlock)->string()))
00636 {
00637 if ((!levelFound && nestLevel == 0) || (levelFound && nestLevel - 1 <= 0))
00638 {
00639 pos = doc->kateTextLine(prevBlock)->firstChar();
00640 break;
00641 }
00642
00643 nestLevel --;
00644 }
00645 else if (stopStmt.exactMatch(doc->kateTextLine(prevBlock)->string()))
00646 {
00647 nestLevel ++;
00648 levelFound = true;
00649 }
00650
00651 --prevBlock;
00652 }
00653
00654 KateDocCursor cur (prevBlock, pos, doc);
00655 QChar c;
00656 int extraIndent = 0;
00657 while (cur.line() < end.line())
00658 {
00659 c = cur.currentChar();
00660
00661 if (c == '(')
00662 extraIndent += indentWidth;
00663 else if (c == ')')
00664 extraIndent -= indentWidth;
00665 else if (c == ':')
00666 break;
00667
00668 if (c.isNull() || c == '#')
00669 cur.gotoNextLine();
00670 else
00671 cur.moveForward(1);
00672 }
00673
00674 return extraIndent;
00675 }
00676
00677
00678
00679