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