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     }
00488 
00489     if (begin.col() < 0)
00490       begin.setCol(0);
00491   }
00492 }
00493 
00494 void KateCSmartIndent::processChar(QChar c)
00495 {
00496   static const QString triggers("}{)/:;#n");
00497   if (triggers.find(c) < 0)
00498     return;
00499 
00500   KateView *view = doc->activeView();
00501   KateDocCursor begin(view->cursorLine(), 0, doc);
00502 
00503   if (c == 'n')
00504   {
00505     KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
00506     if (textLine->getChar(textLine->firstChar()) != '#')
00507       return;
00508   }
00509 
00510   processLine(begin);
00511 }
00512 
00513 uint KateCSmartIndent::calcIndent(KateDocCursor &begin, bool needContinue)
00514 {
00515   KateTextLine::Ptr textLine;
00516   KateDocCursor cur = begin;
00517 
00518   uint anchorIndent = 0;
00519   int anchorPos = 0;
00520   int parenCount = 0;  // Possibly in a multiline for stmt.  Used to skip ';' ...
00521   bool found = false;
00522   bool isSpecial = false;
00523 
00524   //kdDebug() << "calcIndent begin line:" << begin.line() << " col:" << begin.col() << endl;
00525 
00526   // Find Indent Anchor Point
00527   while (cur.gotoPreviousLine())
00528   {
00529     isSpecial = found = false;
00530     textLine = doc->plainKateTextLine(cur.line());
00531 
00532     // Skip comments and handle cases like if (...) { stmt;
00533     int pos = textLine->lastChar();
00534     int openCount = 0;
00535     int otherAnchor = -1;
00536     do
00537     {
00538       if (textLine->attribute(pos) == symbolAttrib)
00539       {
00540         QChar tc = textLine->getChar (pos);
00541         if ((tc == ';' || tc == ':' || tc == ',') && otherAnchor == -1 && parenCount <= 0)
00542           otherAnchor = pos;
00543         else if (tc == ')')
00544           parenCount++;
00545         else if (tc == '(')
00546           parenCount--;
00547         else if (tc == '}')
00548           openCount--;
00549         else if (tc == '{')
00550         {
00551           openCount++;
00552           if (openCount == 1)
00553             break;
00554         }
00555       }
00556     } while (--pos >= textLine->firstChar());
00557 
00558     if (openCount != 0 || otherAnchor != -1)
00559     {
00560       found = true;
00561       QChar c;
00562       if (openCount > 0)
00563         c = '{';
00564       else if (openCount < 0)
00565         c = '}';
00566       else if (otherAnchor >= 0)
00567         c = textLine->getChar (otherAnchor);
00568 
00569       int specialIndent = 0;
00570       if (c == ':' && needContinue)
00571       {
00572         QChar ch;
00573         specialIndent = textLine->firstChar();
00574         if (textLine->stringAtPos(specialIndent, "case"))
00575           ch = textLine->getChar(specialIndent + 4);
00576         else if (textLine->stringAtPos(specialIndent, "default"))
00577           ch = textLine->getChar(specialIndent + 7);
00578         else if (textLine->stringAtPos(specialIndent, "public"))
00579           ch = textLine->getChar(specialIndent + 6);
00580         else if (textLine->stringAtPos(specialIndent, "private"))
00581           ch = textLine->getChar(specialIndent + 7);
00582         else if (textLine->stringAtPos(specialIndent, "protected"))
00583           ch = textLine->getChar(specialIndent + 9);
00584         else if (textLine->stringAtPos(specialIndent, "signals"))
00585           ch = textLine->getChar(specialIndent + 7);
00586         else if (textLine->stringAtPos(specialIndent, "slots"))
00587           ch = textLine->getChar(specialIndent + 5);
00588 
00589         if (ch.isNull() || (!ch.isSpace() && ch != '(' && ch != ':'))
00590           continue;
00591 
00592         KateDocCursor lineBegin = cur;
00593         lineBegin.setCol(specialIndent);
00594         specialIndent = measureIndent(lineBegin);
00595         isSpecial = true;
00596       }
00597 
00598       // Move forward past blank lines
00599       KateDocCursor skip = cur;
00600       skip.setCol(textLine->lastChar());
00601       bool result = skipBlanks(skip, begin, true);
00602 
00603       anchorPos = skip.col();
00604       anchorIndent = measureIndent(skip);
00605 
00606       //kdDebug() << "calcIndent anchorPos:" << anchorPos << " anchorIndent:" << anchorIndent << " at line:" << skip.line() << endl;
00607 
00608       // Accept if it's before requested position or if it was special
00609       if (result && skip < begin)
00610       {
00611         cur = skip;
00612         break;
00613       }
00614       else if (isSpecial)
00615       {
00616         anchorIndent = specialIndent;
00617         break;
00618       }
00619 
00620       // Are these on a line by themselves? (i.e. both last and first char)
00621       if ((c == '{' || c == '}') && textLine->getChar(textLine->firstChar()) == c)
00622       {
00623         cur.setCol(anchorPos = textLine->firstChar());
00624         anchorIndent = measureIndent (cur);
00625         break;
00626       }
00627     }
00628   }
00629 
00630   if (!found)
00631     return 0;
00632 
00633   uint continueIndent = (needContinue) ? calcContinue (cur, begin) : 0;
00634   //kdDebug() << "calcIndent continueIndent:" << continueIndent << endl;
00635 
00636   // Move forward from anchor and determine last known reference character
00637   // Braces take precedance over others ...
00638   textLine = doc->plainKateTextLine(cur.line());
00639   QChar lastChar = textLine->getChar (anchorPos);
00640   int lastLine = cur.line();
00641   if (lastChar == '#' || lastChar == '[')
00642   {
00643     // Never continue if # or [ is encountered at this point here
00644     // A fail-safe really... most likely an #include, #region, or a c# attribute
00645     continueIndent = 0;
00646   }
00647 
00648   int openCount = 0;
00649   while (cur.validPosition() && cur < begin)
00650   {
00651     if (!skipBlanks(cur, begin, true))
00652       return 0;
00653 
00654     QChar tc = cur.currentChar();
00655     //kdDebug() << "  cur.line:" << cur.line() << " cur.col:" << cur.col() << " currentChar '" << tc << "' " << textLine->attribute(cur.col()) << endl;
00656     if (cur == begin || tc.isNull())
00657       break;
00658 
00659     if (!tc.isSpace() && cur < begin)
00660     {
00661       uchar attrib = cur.currentAttrib();
00662       if (tc == '{' && attrib == symbolAttrib)
00663         openCount++;
00664       else if (tc == '}' && attrib == symbolAttrib)
00665         openCount--;
00666 
00667       lastChar = tc;
00668       lastLine = cur.line();
00669     }
00670   }
00671   if (openCount > 0) // Open braces override
00672     lastChar = '{';
00673 
00674   uint indent = 0;
00675   //kdDebug() << "calcIndent lastChar '" << lastChar << "'" << endl;
00676 
00677   if (lastChar == '{' || (lastChar == ':' && isSpecial && needContinue))
00678   {
00679     indent = anchorIndent + indentWidth;
00680   }
00681   else if (lastChar == '}')
00682   {
00683     indent = anchorIndent;
00684   }
00685   else if (lastChar == ';')
00686   {
00687     indent = anchorIndent + ((allowSemi && needContinue) ? continueIndent : 0);
00688   }
00689   else if (lastChar == ',')
00690   {
00691     textLine = doc->plainKateTextLine(lastLine);
00692     KateDocCursor start(lastLine, textLine->firstChar(), doc);
00693     KateDocCursor finish(lastLine, textLine->lastChar(), doc);
00694     uint pos = 0;
00695 
00696     if (isBalanced(start, finish, QChar('('), QChar(')'), pos))
00697       indent = anchorIndent;
00698     else
00699     {
00700       // TODO: Config option. If we're below 48, go ahead and line them up
00701       indent = ((pos < 48) ? pos : anchorIndent + (indentWidth * 2));
00702     }
00703   }
00704   else if (!lastChar.isNull())
00705   {
00706     if (anchorIndent != 0)
00707       indent = anchorIndent + continueIndent;
00708     else
00709       indent = continueIndent;
00710   }
00711 
00712   return indent;
00713 }
00714 
00715 uint KateCSmartIndent::calcContinue(KateDocCursor &start, KateDocCursor &end)
00716 {
00717   KateDocCursor cur = start;
00718 
00719   bool needsBalanced = true;
00720   bool isFor = false;
00721   allowSemi = false;
00722 
00723   KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
00724 
00725   // Handle cases such as  } while (s ... by skipping the leading symbol
00726   if (textLine->attribute(cur.col()) == symbolAttrib)
00727   {
00728     cur.moveForward(1);
00729     skipBlanks(cur, end, false);
00730   }
00731 
00732   if (textLine->getChar(cur.col()) == '}')
00733   {
00734     skipBlanks(cur, end, true);
00735     if (cur.line() != start.line())
00736       textLine = doc->plainKateTextLine(cur.line());
00737 
00738     if (textLine->stringAtPos(cur.col(), "else"))
00739       cur.setCol(cur.col() + 4);
00740     else
00741       return indentWidth * 2;
00742 
00743     needsBalanced = false;
00744   }
00745   else if (textLine->stringAtPos(cur.col(), "else"))
00746   {
00747     cur.setCol(cur.col() + 4);
00748     needsBalanced = false;
00749     if (textLine->stringAtPos(textLine->nextNonSpaceChar(cur.col()), "if"))
00750     {
00751       cur.setCol(textLine->nextNonSpaceChar(cur.col()) + 2);
00752       needsBalanced = true;
00753     }
00754   }
00755   else if (textLine->stringAtPos(cur.col(), "if"))
00756   {
00757     cur.setCol(cur.col() + 2);
00758   }
00759   else if (textLine->stringAtPos(cur.col(), "do"))
00760   {
00761     cur.setCol(cur.col() + 2);
00762     needsBalanced = false;
00763   }
00764   else if (textLine->stringAtPos(cur.col(), "for"))
00765   {
00766     cur.setCol(cur.col() + 3);
00767     isFor = true;
00768   }
00769   else if (textLine->stringAtPos(cur.col(), "while"))
00770   {
00771     cur.setCol(cur.col() + 5);
00772   }
00773   else if (textLine->stringAtPos(cur.col(), "switch"))
00774   {
00775     cur.setCol(cur.col() + 6);
00776   }
00777   else if (textLine->stringAtPos(cur.col(), "using"))
00778   {
00779     cur.setCol(cur.col() + 5);
00780   }
00781   else
00782   {
00783     return indentWidth * 2;
00784   }
00785 
00786   uint openPos = 0;
00787   if (needsBalanced && !isBalanced (cur, end, QChar('('), QChar(')'), openPos))
00788   {
00789     allowSemi = isFor;
00790     if (openPos > 0)
00791       return (openPos - textLine->firstChar());
00792     else
00793       return indentWidth * 2;
00794   }
00795 
00796   // Check if this statement ends a line now
00797   skipBlanks(cur, end, false);
00798   if (cur == end)
00799     return indentWidth;
00800 
00801   if (skipBlanks(cur, end, true))
00802   {
00803     if (cur == end)
00804       return indentWidth;
00805     else
00806       return indentWidth + calcContinue(cur, end);
00807   }
00808 
00809   return 0;
00810 }
00811 
00812 uint KateCSmartIndent::findOpeningBrace(KateDocCursor &start)
00813 {
00814   KateDocCursor cur = start;
00815   int count = 1;
00816 
00817   // Move backwards 1 by 1 and find the opening brace
00818   // Return the indent of that line
00819   while (cur.moveBackward(1))
00820   {
00821     if (cur.currentAttrib() == symbolAttrib)
00822     {
00823       QChar ch = cur.currentChar();
00824       if (ch == '{')
00825         count--;
00826       else if (ch == '}')
00827         count++;
00828 
00829       if (count == 0)
00830       {
00831         KateDocCursor temp(cur.line(), doc->plainKateTextLine(cur.line())->firstChar(), doc);
00832         return measureIndent(temp);
00833       }
00834     }
00835   }
00836 
00837   return 0;
00838 }
00839 
00840 bool KateCSmartIndent::firstOpeningBrace(KateDocCursor &start)
00841 {
00842   KateDocCursor cur = start;
00843 
00844   // Are we the first opening brace at this level?
00845   while(cur.moveBackward(1))
00846   {
00847     if (cur.currentAttrib() == symbolAttrib)
00848     {
00849       QChar ch = cur.currentChar();
00850       if (ch == '{')
00851         return false;
00852       else if (ch == '}' && cur.col() == 0)
00853         break;
00854     }
00855   }
00856 
00857   return true;
00858 }
00859 
00860 uint KateCSmartIndent::findOpeningParen(KateDocCursor &start)
00861 {
00862   KateDocCursor cur = start;
00863   int count = 1;
00864 
00865   // Move backwards 1 by 1 and find the opening (
00866   // Return the indent of that line
00867   while (cur.moveBackward(1))
00868   {
00869     if (cur.currentAttrib() == symbolAttrib)
00870     {
00871       QChar ch = cur.currentChar();
00872       if (ch == '(')
00873         count--;
00874       else if (ch == ')')
00875         count++;
00876 
00877       if (count == 0)
00878         return measureIndent(cur);
00879     }
00880   }
00881 
00882   return 0;
00883 }
00884 
00885 uint KateCSmartIndent::findOpeningComment(KateDocCursor &start)
00886 {
00887   KateDocCursor cur = start;
00888 
00889   // Find the line with the opening /* and return the proper indent
00890   do
00891   {
00892     KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
00893 
00894     int pos = textLine->string().find("/*", false);
00895     if (pos >= 0)
00896     {
00897       KateDocCursor temp(cur.line(), pos, doc);
00898       return measureIndent(temp);
00899     }
00900 
00901   } while (cur.gotoPreviousLine());
00902 
00903   return 0;
00904 }
00905 
00906 // END
00907 
00908 // BEGIN KatePythonIndent
00909 
00910 QRegExp KatePythonIndent::endWithColon = QRegExp( "^[^#]*:\\s*(#.*)?$" );
00911 QRegExp KatePythonIndent::stopStmt = QRegExp( "^\\s*(break|continue|raise|return|pass)\\b.*" );
00912 QRegExp KatePythonIndent::blockBegin = QRegExp( "^\\s*(def|if|elif|else|for|while|try)\\b.*" );
00913 
00914 KatePythonIndent::KatePythonIndent (KateDocument *doc)
00915   : KateAutoIndent (doc)
00916 {
00917 }
00918 KatePythonIndent::~KatePythonIndent ()
00919 {
00920 }
00921 
00922 void KatePythonIndent::processNewline (KateDocCursor &begin, bool /*newline*/)
00923 {
00924   int prevLine = begin.line() - 1;
00925   int prevPos = begin.col();
00926 
00927   while ((prevLine > 0) && (prevPos < 0)) // search a not empty text line
00928     prevPos = doc->plainKateTextLine(--prevLine)->firstChar();
00929 
00930   int prevBlock = prevLine;
00931   int prevBlockPos = prevPos;
00932   int extraIndent = calcExtra (prevBlock, prevBlockPos, begin);
00933 
00934   int indent = doc->plainKateTextLine(prevBlock)->cursorX(prevBlockPos, tabWidth);
00935   if (extraIndent == 0)
00936   {
00937     if (!stopStmt.exactMatch(doc->plainKateTextLine(prevLine)->string()))
00938     {
00939       if (endWithColon.exactMatch(doc->plainKateTextLine(prevLine)->string()))
00940         indent += indentWidth;
00941       else
00942         indent = doc->plainKateTextLine(prevLine)->cursorX(prevPos, tabWidth);
00943     }
00944   }
00945   else
00946     indent += extraIndent;
00947 
00948   if (indent > 0)
00949   {
00950     QString filler = tabString (indent);
00951     doc->insertText (begin.line(), 0, filler);
00952     begin.setCol(filler.length());
00953   }
00954   else
00955     begin.setCol(0);
00956 }
00957 
00958 int KatePythonIndent::calcExtra (int &prevBlock, int &pos, KateDocCursor &end)
00959 {
00960   int nestLevel = 0;
00961   bool levelFound = false;
00962   while ((prevBlock > 0))
00963   {
00964     if (blockBegin.exactMatch(doc->plainKateTextLine(prevBlock)->string()))
00965     {
00966       if ((!levelFound && nestLevel == 0) || (levelFound && nestLevel - 1 <= 0))
00967       {
00968         pos = doc->plainKateTextLine(prevBlock)->firstChar();
00969         break;
00970       }
00971 
00972       nestLevel --;
00973     }
00974     else if (stopStmt.exactMatch(doc->plainKateTextLine(prevBlock)->string()))
00975     {
00976       nestLevel ++;
00977       levelFound = true;
00978     }
00979 
00980     --prevBlock;
00981   }
00982 
00983   KateDocCursor cur (prevBlock, pos, doc);
00984   QChar c;
00985   int extraIndent = 0;
00986   while (cur.line() < end.line())
00987   {
00988     c = cur.currentChar();
00989 
00990     if (c == '(')
00991       extraIndent += indentWidth;
00992     else if (c == ')')
00993       extraIndent -= indentWidth;
00994     else if (c == ':')
00995       break;
00996 
00997     if (c.isNull() || c == '#')
00998       cur.gotoNextLine();
00999     else
01000       cur.moveForward(1);
01001   }
01002 
01003   return extraIndent;
01004 }
01005 
01006 // END
01007 
01008 // 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.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Jul 22 10:18:48 2005 by doxygen 1.3.6 written by Dimitri van Heesch, © 1997-2003