kate Library API Documentation

kateautoindent.cpp

00001 /* This file is part of the KDE libraries
00002    Copyright (C) 2003 Jesse Yurkovich <yurkjes@iit.edu>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License version 2 as published by the Free Software Foundation.
00007 
00008    This library is distributed in the hope that it will be useful,
00009    but WITHOUT ANY WARRANTY; without even the implied warranty of
00010    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011    Library General Public License for more details.
00012 
00013    You should have received a copy of the GNU Library General Public License
00014    along with this library; see the file COPYING.LIB.  If not, write to
00015    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00016    Boston, MA 02111-1307, USA.
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 #include <kdebug.h>
00027 
00028 // BEGIN KateAutoIndent
00029 
00030 KateAutoIndent *KateAutoIndent::createIndenter (KateDocument *doc, uint mode)
00031 {
00032   if (mode == KateDocumentConfig::imCStyle)
00033     return new KateCSmartIndent (doc);
00034   else if (mode == KateDocumentConfig::imPythonStyle)
00035     return new KatePythonIndent (doc);
00036 
00037   return new KateAutoIndent (doc);
00038 }
00039 
00040 QStringList KateAutoIndent::listModes ()
00041 {
00042   QStringList l;
00043 
00044   l << modeDescription(KateDocumentConfig::imNormal);
00045   l << modeDescription(KateDocumentConfig::imCStyle);
00046   l << modeDescription(KateDocumentConfig::imPythonStyle);
00047 
00048   return l;
00049 }
00050 
00051 QString KateAutoIndent::modeName (uint mode)
00052 {
00053   if (mode == KateDocumentConfig::imCStyle)
00054     return QString ("cstyle");
00055   else if (mode == KateDocumentConfig::imPythonStyle)
00056     return QString ("python");
00057 
00058   return QString ("normal");
00059 }
00060 
00061 QString KateAutoIndent::modeDescription (uint mode)
00062 {
00063   if (mode == KateDocumentConfig::imCStyle)
00064     return i18n ("C Style");
00065   else if (mode == KateDocumentConfig::imPythonStyle)
00066     return i18n ("Python Style");
00067 
00068   return i18n ("Normal");
00069 }
00070 
00071 uint KateAutoIndent::modeNumber (const QString &name)
00072 {
00073   if (modeName(KateDocumentConfig::imCStyle) == name)
00074     return KateDocumentConfig::imCStyle;
00075   else if (modeName(KateDocumentConfig::imPythonStyle) == name)
00076     return KateDocumentConfig::imPythonStyle;
00077 
00078   return KateDocumentConfig::imNormal;
00079 }
00080 
00081 KateAutoIndent::KateAutoIndent (KateDocument *_doc)
00082  : doc(_doc)
00083 {
00084 }
00085 KateAutoIndent::~KateAutoIndent ()
00086 {
00087 }
00088 
00089 void KateAutoIndent::updateConfig ()
00090 {
00091   KateDocumentConfig *config = doc->config();
00092 
00093   useSpaces   = config->configFlags() & KateDocument::cfSpaceIndent || config->configFlags() & KateDocumentConfig::cfReplaceTabsDyn;
00094   keepProfile = config->configFlags() & KateDocument::cfKeepIndentProfile;
00095   tabWidth    = config->tabWidth();
00096   indentWidth = (config->configFlags() & KateDocument::cfSpaceIndent) ? config->indentationWidth() : tabWidth;
00097 
00098   commentAttrib = 255;
00099   doxyCommentAttrib = 255;
00100   regionAttrib = 255;
00101   symbolAttrib = 255;
00102   alertAttrib = 255;
00103   tagAttrib = 255;
00104   wordAttrib = 255;
00105 
00106   KateHlItemDataList items;
00107   doc->highlight()->getKateHlItemDataListCopy (0, items);
00108 
00109   for (uint i=0; i<items.count(); i++)
00110   {
00111     QString name = items.at(i)->name;
00112     if (name.find("Comment") != -1 && commentAttrib == 255)
00113     {
00114       commentAttrib = i;
00115     }
00116     else if (name.find("Region Marker") != -1 && regionAttrib == 255)
00117     {
00118       regionAttrib = i;
00119     }
00120     else if (name.find("Symbol") != -1 && symbolAttrib == 255)
00121     {
00122       symbolAttrib = i;
00123     }
00124     else if (name.find("Alert") != -1)
00125     {
00126       alertAttrib = i;
00127     }
00128     else if (name.find("Comment") != -1 && commentAttrib != 255 && doxyCommentAttrib == 255)
00129     {
00130       doxyCommentAttrib = i;
00131     }
00132     else if (name.find("Tags") != -1 && tagAttrib == 255)
00133     {
00134       tagAttrib = i;
00135     }
00136     else if (name.find("Word") != -1 && wordAttrib == 255)
00137     {
00138       wordAttrib = i;
00139     }
00140   }
00141 }
00142 
00143 bool KateAutoIndent::isBalanced (KateDocCursor &begin, const KateDocCursor &end, QChar open, QChar close, uint &pos) const
00144 {
00145   int parenOpen = 0;
00146   bool atLeastOne = false;
00147   bool getNext = false;
00148 
00149   pos = doc->plainKateTextLine(begin.line())->firstChar();
00150 
00151   // Iterate one-by-one finding opening and closing chars
00152   // Assume that open and close are 'Symbol' characters
00153   while (begin < end)
00154   {
00155     QChar c = begin.currentChar();
00156     if (begin.currentAttrib() == symbolAttrib)
00157     {
00158       if (c == open)
00159       {
00160         if (!atLeastOne)
00161         {
00162           atLeastOne = true;
00163           getNext = true;
00164           pos = measureIndent(begin) + 1;
00165         }
00166         parenOpen++;
00167       }
00168       else if (c == close)
00169       {
00170         parenOpen--;
00171       }
00172     }
00173     else if (getNext && !c.isSpace())
00174     {
00175       getNext = false;
00176       pos = measureIndent(begin);
00177     }
00178 
00179     if (atLeastOne && parenOpen <= 0)
00180       return true;
00181 
00182     begin.moveForward(1);
00183   }
00184 
00185   return (atLeastOne) ? false : true;
00186 }
00187 
00188 bool KateAutoIndent::skipBlanks (KateDocCursor &cur, KateDocCursor &max, bool newline) const
00189 {
00190   int curLine = cur.line();
00191   if (newline)
00192     cur.moveForward(1);
00193 
00194   if (cur >= max)
00195     return false;
00196 
00197   do
00198   {
00199     uchar attrib = cur.currentAttrib();
00200     if (attrib != commentAttrib && attrib != doxyCommentAttrib && attrib != regionAttrib && attrib != alertAttrib && attrib != tagAttrib && attrib != wordAttrib)
00201     {
00202       QChar c = cur.currentChar();
00203       if (!c.isNull() && !c.isSpace())
00204         break;
00205     }
00206 
00207     // Make sure col is 0 if we spill into next line  i.e. count the '\n' as a character
00208     if (!cur.moveForward(1))
00209       break;
00210     if (curLine != cur.line())
00211     {
00212       if (!newline)
00213         break;
00214       curLine = cur.line();
00215       cur.setCol(0);
00216     }
00217   } while (cur < max);
00218 
00219   if (cur > max)
00220     cur = max;
00221   return true;
00222 }
00223 
00224 uint KateAutoIndent::measureIndent (KateDocCursor &cur) const
00225 {
00226   if (useSpaces && !keepProfile)
00227     return cur.col();
00228 
00229   return doc->plainKateTextLine(cur.line())->cursorX(cur.col(), tabWidth);
00230 }
00231 
00232 QString KateAutoIndent::tabString(uint pos) const
00233 {
00234   QString s;
00235   pos = QMIN (pos, 80); // sanity check for large values of pos
00236 
00237   if (!useSpaces)
00238   {
00239     while (pos >= tabWidth)
00240     {
00241       s += '\t';
00242       pos -= tabWidth;
00243     }
00244   }
00245   while (pos > 0)
00246   {
00247     s += ' ';
00248     pos--;
00249   }
00250   return s;
00251 }
00252 
00253 void KateAutoIndent::processNewline (KateDocCursor &begin, bool /*needContinue*/)
00254 {
00255   int line = begin.line() - 1;
00256   int pos = begin.col();
00257 
00258   while ((line > 0) && (pos < 0)) // search a not empty text line
00259     pos = doc->plainKateTextLine(--line)->firstChar();
00260 
00261   if (pos > 0)
00262   {
00263     uint indent = doc->plainKateTextLine(line)->cursorX(pos, tabWidth);
00264     QString filler = tabString (indent);
00265     doc->insertText (begin.line(), 0, filler);
00266     begin.setCol(filler.length());
00267   }
00268   else
00269     begin.setCol(0);
00270 }
00271 
00272 //END
00273 
00274 // BEGIN KateCSmartIndent
00275 
00276 KateCSmartIndent::KateCSmartIndent (KateDocument *doc)
00277  :  KateAutoIndent (doc),
00278     allowSemi (false),
00279     processingBlock (false)
00280 {
00281 
00282 }
00283 
00284 KateCSmartIndent::~KateCSmartIndent ()
00285 {
00286 
00287 }
00288 
00289 void KateCSmartIndent::processLine (KateDocCursor &line)
00290 {
00291   KateTextLine::Ptr textLine = doc->plainKateTextLine(line.line());
00292 
00293   int firstChar = textLine->firstChar();
00294   // Empty line is worthless ... but only when doing more than 1 line
00295   if (firstChar == -1 && processingBlock)
00296     return;
00297 
00298   uint indent = 0;
00299 
00300   // TODO Here we do not check for beginning and ending comments ...
00301   QChar first = textLine->getChar(firstChar);
00302   QChar last = textLine->getChar(textLine->lastChar());
00303 
00304   if (first == '}')
00305   {
00306     indent = findOpeningBrace(line);
00307   }
00308   else if (first == ')')
00309   {
00310     indent = findOpeningParen(line);
00311   }
00312   else if (first == '{')
00313   {
00314     // If this is the first brace, we keep the indent at 0
00315     KateDocCursor temp(line.line(), firstChar, doc);
00316     if (!firstOpeningBrace(temp))
00317       indent = calcIndent(temp, false);
00318   }
00319   else if (first == ':')
00320   {
00321     // Initialization lists (handle c++ and c#)
00322     int pos = findOpeningBrace(line);
00323     if (pos == 0)
00324       indent = indentWidth;
00325     else
00326       indent = pos + (indentWidth * 2);
00327   }
00328   else if (last == ':')
00329   {
00330     if (textLine->stringAtPos (firstChar, "case") ||
00331         textLine->stringAtPos (firstChar, "default") ||
00332         textLine->stringAtPos (firstChar, "public") ||
00333         textLine->stringAtPos (firstChar, "private") ||
00334         textLine->stringAtPos (firstChar, "protected") ||
00335         textLine->stringAtPos (firstChar, "signals") ||
00336         textLine->stringAtPos (firstChar, "slots"))
00337     {
00338       indent = findOpeningBrace(line) + indentWidth;
00339     }
00340   }
00341   else if (first == '*')
00342   {
00343     if (last == '/')
00344     {
00345       int lineEnd = textLine->lastChar();
00346       if (lineEnd > 0 && textLine->getChar(lineEnd - 1) == '*')
00347       {
00348         indent = findOpeningComment(line);
00349         if (textLine->attribute(firstChar) == doxyCommentAttrib)
00350           indent++;
00351       }
00352       else
00353         return;
00354     }
00355     else
00356     {
00357       KateDocCursor temp = line;
00358       if (textLine->attribute(firstChar) == doxyCommentAttrib)
00359         indent = calcIndent(temp, false) + 1;
00360       else
00361         indent = calcIndent(temp, true);
00362     }
00363   }
00364   else if (first == '#')
00365   {
00366     // c# regions
00367     if (textLine->stringAtPos (firstChar, "#region") ||
00368         textLine->stringAtPos (firstChar, "#endregion"))
00369     {
00370       KateDocCursor temp = line;
00371       indent = calcIndent(temp, true);
00372     }
00373   }
00374   else
00375   {
00376     // Everything else ...
00377     if (first == '/' && last != '/')
00378       return;
00379 
00380     KateDocCursor temp = line;
00381     indent = calcIndent(temp, true);
00382     if (indent == 0)
00383     {
00384       KateAutoIndent::processNewline(line, true);
00385       return;
00386     }
00387   }
00388 
00389   // Slightly faster if we don't indent what we don't have to
00390   if (indent != measureIndent(line) || first == '}' || first == '{' || first == '#')
00391   {
00392     doc->removeText(line.line(), 0, line.line(), firstChar);
00393     QString filler = tabString(indent);
00394     if (indent > 0) doc->insertText(line.line(), 0, filler);
00395     if (!processingBlock) line.setCol(filler.length());
00396   }
00397 }
00398 
00399 void KateCSmartIndent::processSection (KateDocCursor &begin, KateDocCursor &end)
00400 {
00401   KateDocCursor cur = begin;
00402   QTime t;
00403   t.start();
00404 
00405   processingBlock = (end.line() - cur.line() > 0) ? true : false;
00406 
00407   while (cur.line() <= end.line())
00408   {
00409     processLine (cur);
00410     if (!cur.gotoNextLine())
00411       break;
00412   }
00413 
00414   processingBlock = false;
00415   kdDebug(13000) << "+++ total: " << t.elapsed() << endl;
00416 }
00417 
00418 bool KateCSmartIndent::handleDoxygen (KateDocCursor &begin)
00419 {
00420   // Factor out the rather involved Doxygen stuff here ...
00421   int line = begin.line();
00422   int first = -1;
00423   while ((line > 0) && (first < 0))
00424     first = doc->plainKateTextLine(--line)->firstChar();
00425 
00426   if (first >= 0)
00427   {
00428     KateTextLine::Ptr textLine = doc->plainKateTextLine(line);
00429     bool insideDoxygen = false;
00430     if (textLine->attribute(first) == doxyCommentAttrib || textLine->attribute(textLine->lastChar()) == doxyCommentAttrib)
00431     {
00432       if (!textLine->endingWith("*/"))
00433         insideDoxygen = true;
00434     }
00435 
00436     // Align the *'s and then go ahead and insert one too ...
00437     if (insideDoxygen)
00438     {
00439       textLine = doc->plainKateTextLine(begin.line());
00440       first = textLine->firstChar();
00441       int indent = findOpeningComment(begin);
00442       QString filler = tabString (indent);
00443 
00444       bool doxygenAutoInsert = doc->config()->configFlags() & KateDocumentConfig::cfDoxygenAutoTyping;
00445       if ( doxygenAutoInsert &&
00446            (!textLine->stringAtPos(first, "*/") && !textLine->stringAtPos(first, "*")))
00447       {
00448         filler = filler + " * ";
00449       }
00450 
00451       doc->removeText (begin.line(), 0, begin.line(), first);
00452       doc->insertText (begin.line(), 0, filler);
00453       begin.setCol(filler.length());
00454 
00455       return true;
00456     }
00457   }
00458 
00459   return false;
00460 }
00461 
00462 void KateCSmartIndent::processNewline (KateDocCursor &begin, bool needContinue)
00463 {
00464   if (!handleDoxygen (begin))
00465   {
00466     KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
00467     bool inMiddle = textLine->firstChar() > -1;
00468 
00469     int indent = calcIndent (begin, needContinue);
00470 
00471     if (indent > 0 || inMiddle)
00472     {
00473       QString filler = tabString (indent);
00474       doc->insertText (begin.line(), 0, filler);
00475       begin.setCol(filler.length());
00476 
00477       // Handles cases where user hits enter at the beginning or middle of text
00478       if (inMiddle)
00479       {
00480         processLine(begin);
00481         begin.setCol(textLine->firstChar());
00482       }
00483     }
00484     else
00485     {
00486       KateAutoIndent::processNewline (begin, needContinue);
00487       begin.setCol(begin.col() - 1);
00488     }
00489 
00490     if (begin.col() < 0)
00491       begin.setCol(0);
00492   }
00493 }
00494 
00495 void KateCSmartIndent::processChar(QChar c)
00496 {
00497   static const QString triggers("}{)/:;#n");
00498   if (triggers.find(c, true) == -1)
00499     return;
00500 
00501   KateView *view = doc->activeView();
00502   KateDocCursor begin(view->cursorLine(), 0, doc);
00503 
00504   if (c == 'n')
00505   {
00506     KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
00507     if (textLine->getChar(textLine->firstChar()) != '#')
00508       return;
00509   }
00510 
00511   processLine(begin);
00512 }
00513 
00514 uint KateCSmartIndent::calcIndent(KateDocCursor &begin, bool needContinue)
00515 {
00516   KateTextLine::Ptr textLine;
00517   KateDocCursor cur = begin;
00518 
00519   uint anchorIndent = 0;
00520   int anchorPos = 0;
00521   int parenCount = 0;  // Possibly in a multiline for stmt.  Used to skip ';' ...
00522   bool found = false;
00523   bool isSpecial = false;
00524 
00525   //kdDebug() << "calcIndent begin line:" << begin.line() << " col:" << begin.col() << endl;
00526 
00527   // Find Indent Anchor Point
00528   while (cur.gotoPreviousLine())
00529   {
00530     isSpecial = found = false;
00531     textLine = doc->plainKateTextLine(cur.line());
00532 
00533     // Skip comments and handle cases like if (...) { stmt;
00534     int pos = textLine->lastChar();
00535     int openCount = 0;
00536     int otherAnchor = -1;
00537     do
00538     {
00539       if (textLine->attribute(pos) == symbolAttrib)
00540       {
00541         QChar tc = textLine->getChar (pos);
00542         if ((tc == ';' || tc == ':' || tc == ',') && otherAnchor == -1 && parenCount <= 0)
00543           otherAnchor = pos;
00544         else if (tc == ')')
00545           parenCount++;
00546         else if (tc == '(')
00547           parenCount--;
00548         else if (tc == '}')
00549           openCount--;
00550         else if (tc == '{')
00551         {
00552           openCount++;
00553           if (openCount == 1)
00554             break;
00555         }
00556       }
00557     } while (--pos >= textLine->firstChar());
00558 
00559     if (openCount != 0 || otherAnchor != -1)
00560     {
00561       found = true;
00562       QChar c;
00563       if (openCount > 0)
00564         c = '{';
00565       else if (openCount < 0)
00566         c = '}';
00567       else if (otherAnchor >= 0)
00568         c = textLine->getChar (otherAnchor);
00569 
00570       int specialIndent = 0;
00571       if (c == ':' && needContinue)
00572       {
00573         QChar ch;
00574         specialIndent = textLine->firstChar();
00575         if (textLine->stringAtPos(specialIndent, "case"))
00576           ch = textLine->getChar(specialIndent + 4);
00577         else if (textLine->stringAtPos(specialIndent, "default"))
00578           ch = textLine->getChar(specialIndent + 7);
00579         else if (textLine->stringAtPos(specialIndent, "public"))
00580           ch = textLine->getChar(specialIndent + 6);
00581         else if (textLine->stringAtPos(specialIndent, "private"))
00582           ch = textLine->getChar(specialIndent + 7);
00583         else if (textLine->stringAtPos(specialIndent, "protected"))
00584           ch = textLine->getChar(specialIndent + 9);
00585         else if (textLine->stringAtPos(specialIndent, "signals"))
00586           ch = textLine->getChar(specialIndent + 7);
00587         else if (textLine->stringAtPos(specialIndent, "slots"))
00588           ch = textLine->getChar(specialIndent + 5);
00589 
00590         if (ch.isNull() || (!ch.isSpace() && ch != '(' && ch != ':'))
00591           continue;
00592 
00593         KateDocCursor lineBegin = cur;
00594         lineBegin.setCol(specialIndent);
00595         specialIndent = measureIndent(lineBegin);
00596         isSpecial = true;
00597       }
00598 
00599       // Move forward past blank lines
00600       KateDocCursor skip = cur;
00601       skip.setCol(textLine->lastChar());
00602       bool result = skipBlanks(skip, begin, true);
00603 
00604       anchorPos = skip.col();
00605       anchorIndent = measureIndent(skip);
00606 
00607       //kdDebug() << "calcIndent anchorPos:" << anchorPos << " anchorIndent:" << anchorIndent << " at line:" << skip.line() << endl;
00608 
00609       // Accept if it's before requested position or if it was special
00610       if (result && skip < begin)
00611       {
00612         cur = skip;
00613         break;
00614       }
00615       else if (isSpecial)
00616       {
00617         anchorIndent = specialIndent;
00618         break;
00619       }
00620 
00621       // Are these on a line by themselves? (i.e. both last and first char)
00622       if ((c == '{' || c == '}') && textLine->getChar(textLine->firstChar()) == c)
00623       {
00624         cur.setCol(anchorPos = textLine->firstChar());
00625         anchorIndent = measureIndent (cur);
00626         break;
00627       }
00628     }
00629   }
00630 
00631   if (!found)
00632     return 0;
00633 
00634   uint continueIndent = (needContinue) ? calcContinue (cur, begin) : 0;
00635   //kdDebug() << "calcIndent continueIndent:" << continueIndent << endl;
00636 
00637   // Move forward from anchor and determine last known reference character
00638   // Braces take precedance over others ...
00639   textLine = doc->plainKateTextLine(cur.line());
00640   QChar lastChar = textLine->getChar (anchorPos);
00641   int lastLine = cur.line();
00642   if (lastChar == '#' || lastChar == '[')
00643   {
00644     // Never continue if # or [ is encountered at this point here
00645     // A fail-safe really... most likely an #include, #region, or a c# attribute
00646     continueIndent = 0;
00647   }
00648 
00649   int openCount = 0;
00650   while (cur.validPosition() && cur < begin)
00651   {
00652     if (!skipBlanks(cur, begin, true))
00653       return 0;
00654 
00655     QChar tc = cur.currentChar();
00656     //kdDebug() << "  cur.line:" << cur.line() << " cur.col:" << cur.col() << " currentChar '" << tc << "' " << textLine->attribute(cur.col()) << endl;
00657     if (cur == begin || tc.isNull())
00658       break;
00659 
00660     if (!tc.isSpace() && cur < begin)
00661     {
00662       uchar attrib = cur.currentAttrib();
00663       if (tc == '{' && attrib == symbolAttrib)
00664         openCount++;
00665       else if (tc == '}' && attrib == symbolAttrib)
00666         openCount--;
00667 
00668       lastChar = tc;
00669       lastLine = cur.line();
00670     }
00671   }
00672   if (openCount > 0) // Open braces override
00673     lastChar = '{';
00674 
00675   uint indent = 0;
00676   //kdDebug() << "calcIndent lastChar '" << lastChar << "'" << endl;
00677 
00678   if (lastChar == '{' || (lastChar == ':' && isSpecial && needContinue))
00679   {
00680     indent = anchorIndent + indentWidth;
00681   }
00682   else if (lastChar == '}')
00683   {
00684     indent = anchorIndent;
00685   }
00686   else if (lastChar == ';')
00687   {
00688     indent = anchorIndent + ((allowSemi && needContinue) ? continueIndent : 0);
00689   }
00690   else if (lastChar == ',')
00691   {
00692     textLine = doc->plainKateTextLine(lastLine);
00693     KateDocCursor start(lastLine, textLine->firstChar(), doc);
00694     KateDocCursor finish(lastLine, textLine->lastChar(), doc);
00695     uint pos = 0;
00696 
00697     if (isBalanced(start, finish, QChar('('), QChar(')'), pos))
00698       indent = anchorIndent;
00699     else
00700     {
00701       // TODO: Config option. If we're below 48, go ahead and line them up
00702       indent = ((pos < 48) ? pos : anchorIndent + (indentWidth * 2));
00703     }
00704   }
00705   else if (!lastChar.isNull())
00706   {
00707     if (anchorIndent != 0)
00708       indent = anchorIndent + continueIndent;
00709     else
00710       indent = continueIndent;
00711   }
00712 
00713   return indent;
00714 }
00715 
00716 uint KateCSmartIndent::calcContinue(KateDocCursor &start, KateDocCursor &end)
00717 {
00718   KateDocCursor cur = start;
00719 
00720   bool needsBalanced = true;
00721   bool isFor = false;
00722   allowSemi = false;
00723 
00724   KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
00725 
00726   // Handle cases such as  } while (s ... by skipping the leading symbol
00727   if (textLine->attribute(cur.col()) == symbolAttrib)
00728   {
00729     cur.moveForward(1);
00730     skipBlanks(cur, end, false);
00731   }
00732 
00733   if (textLine->getChar(cur.col()) == '}')
00734   {
00735     skipBlanks(cur, end, true);
00736     if (cur.line() != start.line())
00737       textLine = doc->plainKateTextLine(cur.line());
00738 
00739     if (textLine->stringAtPos(cur.col(), "else"))
00740       cur.setCol(cur.col() + 4);
00741     else
00742       return indentWidth * 2;
00743 
00744     needsBalanced = false;
00745   }
00746   else if (textLine->stringAtPos(cur.col(), "else"))
00747   {
00748     cur.setCol(cur.col() + 4);
00749     needsBalanced = false;
00750     if (textLine->stringAtPos(textLine->nextNonSpaceChar(cur.col()), "if"))
00751     {
00752       cur.setCol(textLine->nextNonSpaceChar(cur.col()) + 2);
00753       needsBalanced = true;
00754     }
00755   }
00756   else if (textLine->stringAtPos(cur.col(), "if"))
00757   {
00758     cur.setCol(cur.col() + 2);
00759   }
00760   else if (textLine->stringAtPos(cur.col(), "do"))
00761   {
00762     cur.setCol(cur.col() + 2);
00763     needsBalanced = false;
00764   }
00765   else if (textLine->stringAtPos(cur.col(), "for"))
00766   {
00767     cur.setCol(cur.col() + 3);
00768     isFor = true;
00769   }
00770   else if (textLine->stringAtPos(cur.col(), "while"))
00771   {
00772     cur.setCol(cur.col() + 5);
00773   }
00774   else if (textLine->stringAtPos(cur.col(), "switch"))
00775   {
00776     cur.setCol(cur.col() + 6);
00777   }
00778   else if (textLine->stringAtPos(cur.col(), "using"))
00779   {
00780     cur.setCol(cur.col() + 5);
00781   }
00782   else
00783   {
00784     return indentWidth * 2;
00785   }
00786 
00787   uint openPos = 0;
00788   if (needsBalanced && !isBalanced (cur, end, QChar('('), QChar(')'), openPos))
00789   {
00790     allowSemi = isFor;
00791     if (openPos > 0)
00792       return (openPos - textLine->firstChar());
00793     else
00794       return indentWidth * 2;
00795   }
00796 
00797   // Check if this statement ends a line now
00798   skipBlanks(cur, end, false);
00799   if (cur == end)
00800     return indentWidth;
00801 
00802   if (skipBlanks(cur, end, true))
00803   {
00804     if (cur == end)
00805       return indentWidth;
00806     else
00807       return indentWidth + calcContinue(cur, end);
00808   }
00809 
00810   return 0;
00811 }
00812 
00813 uint KateCSmartIndent::findOpeningBrace(KateDocCursor &start)
00814 {
00815   KateDocCursor cur = start;
00816   int count = 1;
00817 
00818   // Move backwards 1 by 1 and find the opening brace
00819   // Return the indent of that line
00820   while (cur.moveBackward(1))
00821   {
00822     if (cur.currentAttrib() == symbolAttrib)
00823     {
00824       QChar ch = cur.currentChar();
00825       if (ch == '{')
00826         count--;
00827       else if (ch == '}')
00828         count++;
00829 
00830       if (count == 0)
00831       {
00832         KateDocCursor temp(cur.line(), doc->plainKateTextLine(cur.line())->firstChar(), doc);
00833         return measureIndent(temp);
00834       }
00835     }
00836   }
00837 
00838   return 0;
00839 }
00840 
00841 bool KateCSmartIndent::firstOpeningBrace(KateDocCursor &start)
00842 {
00843   KateDocCursor cur = start;
00844 
00845   // Are we the first opening brace at this level?
00846   while(cur.moveBackward(1))
00847   {
00848     if (cur.currentAttrib() == symbolAttrib)
00849     {
00850       QChar ch = cur.currentChar();
00851       if (ch == '{')
00852         return false;
00853       else if (ch == '}' && cur.col() == 0)
00854         break;
00855     }
00856   }
00857 
00858   return true;
00859 }
00860 
00861 uint KateCSmartIndent::findOpeningParen(KateDocCursor &start)
00862 {
00863   KateDocCursor cur = start;
00864   int count = 1;
00865 
00866   // Move backwards 1 by 1 and find the opening (
00867   // Return the indent of that line
00868   while (cur.moveBackward(1))
00869   {
00870     if (cur.currentAttrib() == symbolAttrib)
00871     {
00872       QChar ch = cur.currentChar();
00873       if (ch == '(')
00874         count--;
00875       else if (ch == ')')
00876         count++;
00877 
00878       if (count == 0)
00879         return measureIndent(cur);
00880     }
00881   }
00882 
00883   return 0;
00884 }
00885 
00886 uint KateCSmartIndent::findOpeningComment(KateDocCursor &start)
00887 {
00888   KateDocCursor cur = start;
00889 
00890   // Find the line with the opening /* and return the proper indent
00891   do
00892   {
00893     KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
00894 
00895     int pos = textLine->string().find("/*", false);
00896     if (pos >= 0)
00897     {
00898       KateDocCursor temp(cur.line(), pos, doc);
00899       return measureIndent(temp);
00900     }
00901 
00902   } while (cur.gotoPreviousLine());
00903 
00904   return 0;
00905 }
00906 
00907 // END
00908 
00909 // BEGIN KatePythonIndent
00910 
00911 QRegExp KatePythonIndent::endWithColon = QRegExp( "^[^#]*:\\s*(#.*)?$" );
00912 QRegExp KatePythonIndent::stopStmt = QRegExp( "^\\s*(break|continue|raise|return|pass)\\b.*" );
00913 QRegExp KatePythonIndent::blockBegin = QRegExp( "^\\s*(def|if|elif|else|for|while|try)\\b.*" );
00914 
00915 KatePythonIndent::KatePythonIndent (KateDocument *doc)
00916   : KateAutoIndent (doc)
00917 {
00918 }
00919 KatePythonIndent::~KatePythonIndent ()
00920 {
00921 }
00922 
00923 void KatePythonIndent::processNewline (KateDocCursor &begin, bool /*newline*/)
00924 {
00925   int prevLine = begin.line() - 1;
00926   int prevPos = begin.col();
00927 
00928   while ((prevLine > 0) && (prevPos < 0)) // search a not empty text line
00929     prevPos = doc->plainKateTextLine(--prevLine)->firstChar();
00930 
00931   int prevBlock = prevLine;
00932   int prevBlockPos = prevPos;
00933   int extraIndent = calcExtra (prevBlock, prevBlockPos, begin);
00934 
00935   int indent = doc->plainKateTextLine(prevBlock)->cursorX(prevBlockPos, tabWidth);
00936   if (extraIndent == 0)
00937   {
00938     if (!stopStmt.exactMatch(doc->plainKateTextLine(prevLine)->string()))
00939     {
00940       if (endWithColon.exactMatch(doc->plainKateTextLine(prevLine)->string()))
00941         indent += indentWidth;
00942       else
00943         indent = doc->plainKateTextLine(prevLine)->cursorX(prevPos, tabWidth);
00944     }
00945   }
00946   else
00947     indent += extraIndent;
00948 
00949   if (indent > 0)
00950   {
00951     QString filler = tabString (indent);
00952     doc->insertText (begin.line(), 0, filler);
00953     begin.setCol(filler.length());
00954   }
00955   else
00956     begin.setCol(0);
00957 }
00958 
00959 int KatePythonIndent::calcExtra (int &prevBlock, int &pos, KateDocCursor &end)
00960 {
00961   int nestLevel = 0;
00962   bool levelFound = false;
00963   while ((prevBlock > 0))
00964   {
00965     if (blockBegin.exactMatch(doc->plainKateTextLine(prevBlock)->string()))
00966     {
00967       if ((!levelFound && nestLevel == 0) || (levelFound && nestLevel - 1 <= 0))
00968       {
00969         pos = doc->plainKateTextLine(prevBlock)->firstChar();
00970         break;
00971       }
00972 
00973       nestLevel --;
00974     }
00975     else if (stopStmt.exactMatch(doc->plainKateTextLine(prevBlock)->string()))
00976     {
00977       nestLevel ++;
00978       levelFound = true;
00979     }
00980 
00981     --prevBlock;
00982   }
00983 
00984   KateDocCursor cur (prevBlock, pos, doc);
00985   QChar c;
00986   int extraIndent = 0;
00987   while (cur.line() < end.line())
00988   {
00989     c = cur.currentChar();
00990 
00991     if (c == '(')
00992       extraIndent += indentWidth;
00993     else if (c == ')')
00994       extraIndent -= indentWidth;
00995     else if (c == ':')
00996       break;
00997 
00998     if (c.isNull() || c == '#')
00999       cur.gotoNextLine();
01000     else
01001       cur.moveForward(1);
01002   }
01003 
01004   return extraIndent;
01005 }
01006 
01007 // END
01008 
01009 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE Logo
This file is part of the documentation for kate Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Sep 23 17:13:29 2004 by doxygen 1.3.8-20040913 written by Dimitri van Heesch, © 1997-2003