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