• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

Kate

kateautoindent.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002    Copyright (C) 2003 Jesse Yurkovich <yurkjes@iit.edu>
00003    Copyright (C) 2004 >Anders Lund <anders@alweb.dk> (KateVarIndent class)
00004    Copyright (C) 2005 Dominik Haumann <dhdev@gmx.de> (basic support for config page)
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License version 2 as published by the Free Software Foundation.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018    Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include "kateautoindent.h"
00022 #include "kateautoindent.moc"
00023 
00024 #include "kateconfig.h"
00025 #include "katehighlight.h"
00026 #include "kateglobal.h"
00027 #include "kateindentscript.h"
00028 #include "katescriptmanager.h"
00029 #include "kateview.h"
00030 #include "kateextendedattribute.h"
00031 #include "katedocument.h"
00032 
00033 #include <klocale.h>
00034 #include <kdebug.h>
00035 #include <kmenu.h>
00036 
00037 #include <cctype>
00038 
00039 const QString MODE_NONE = QLatin1String("none");
00040 const QString MODE_NORMAL = QLatin1String("normal");
00041 
00042 //BEGIN KateAutoIndent
00043 
00044 QStringList KateAutoIndent::listModes ()
00045 {
00046   QStringList l;
00047 
00048   for (int i = 0; i < modeCount(); ++i)
00049     l << modeDescription(i);
00050 
00051   return l;
00052 }
00053 
00054 int KateAutoIndent::modeCount ()
00055 {
00056   // inbuild modes + scripts
00057   return 2 + KateGlobal::self()->scriptManager()->indentationScriptCount();
00058 }
00059 
00060 
00061 QString KateAutoIndent::modeName (int mode)
00062 {
00063   if (mode == 0 || mode >= modeCount ())
00064     return MODE_NONE;
00065 
00066   if (mode == 1)
00067     return MODE_NORMAL;
00068 
00069   return KateGlobal::self()->scriptManager()->indentationScriptByIndex(mode-2)->indentHeader().baseName();
00070 }
00071 
00072 QString KateAutoIndent::modeDescription (int mode)
00073 {
00074   if (mode == 0 || mode >= modeCount ())
00075     return i18nc ("Autoindent mode", "None");
00076 
00077   if (mode == 1)
00078     return i18nc ("Autoindent mode", "Normal");
00079 
00080   return KateGlobal::self()->scriptManager()->indentationScriptByIndex(mode-2)->indentHeader().name();
00081 }
00082 
00083 QString KateAutoIndent::modeRequiredStyle(int mode)
00084 {
00085   if (mode == 0 || mode == 1 || mode >= modeCount())
00086     return QString();
00087 
00088   return KateGlobal::self()->scriptManager()->indentationScriptByIndex(mode-2)->indentHeader().requiredStyle();
00089 }
00090 
00091 uint KateAutoIndent::modeNumber (const QString &name)
00092 {
00093   for (int i = 0; i < modeCount(); ++i)
00094     if (modeName(i) == name)
00095       return i;
00096 
00097   return 0;
00098 }
00099 
00100 KateAutoIndent::KateAutoIndent (KateDocument *_doc)
00101   : QObject(_doc), doc(_doc), m_script (0)
00102 {
00103   // don't call updateConfig() here, document might is not ready for that....
00104 
00105   // on script reload, the script pointer is invalid -> force reload
00106   connect(KateGlobal::self()->scriptManager(), SIGNAL(reloaded()),
00107           this, SLOT(reloadScript()));
00108 }
00109 
00110 KateAutoIndent::~KateAutoIndent ()
00111 {
00112 }
00113 
00114 QString KateAutoIndent::tabString (int length, int align) const
00115 {
00116   QString s;
00117   length = qMin (length, 256); // sanity check for large values of pos
00118   int spaces = qBound(0, align - length, 256);
00119 
00120   if (!useSpaces)
00121   {
00122     s.append (QString (length / tabWidth, '\t'));
00123     length = length % tabWidth;
00124   }
00125   s.append(QString(length + spaces, ' '));
00126 
00127   return s;
00128 }
00129 
00130 bool KateAutoIndent::doIndent(int line, int indentDepth, int align)
00131 {
00132   kDebug (13060) << "doIndent: line: " << line << " indentDepth: " << indentDepth << " align: " << align;
00133 
00134   Kate::TextLine textline = doc->plainKateTextLine(line);
00135 
00136   // textline not found, cu
00137   if (!textline)
00138     return false;
00139 
00140   // sanity check
00141   if (indentDepth < 0)
00142     indentDepth = 0;
00143 
00144   const QString oldIndentation = textline->leadingWhitespace();
00145 
00146   // Preserve existing "tabs then spaces" alignment if and only if:
00147   //  - no alignment was passed to doIndent and
00148   //  - we aren't using spaces for indentation and
00149   //  - we aren't rounding indentation up to the next multiple of the indentation width and
00150   //  - we aren't using a combination to tabs and spaces for alignment, or in other words
00151   //    the indent width is a multiple of the tab width.
00152   bool preserveAlignment = !useSpaces && keepExtra && indentWidth % tabWidth == 0;
00153   if (align == 0 && preserveAlignment)
00154   {
00155     // Count the number of consecutive spaces at the end of the existing indentation
00156     int i = oldIndentation.size() - 1;
00157     while (i >= 0 && oldIndentation.at(i) == ' ')
00158       --i;
00159     // Use the passed indentDepth as the alignment, and set the indentDepth to
00160     // that value minus the number of spaces found (but don't let it get negative).
00161     align = indentDepth;
00162     indentDepth = qMax(0, align - (oldIndentation.size() - 1 - i));
00163   }
00164 
00165   QString indentString = tabString(indentDepth, align);
00166 
00167   // remove leading whitespace, then insert the leading indentation
00168   doc->editStart ();
00169   doc->editRemoveText (line, 0, oldIndentation.length());
00170   doc->editInsertText (line, 0, indentString);
00171   doc->editEnd ();
00172 
00173   return true;
00174 }
00175 
00176 bool KateAutoIndent::doIndentRelative(int line, int change)
00177 {
00178   kDebug (13060) << "doIndentRelative: line: " << line << " change: " << change;
00179 
00180   Kate::TextLine textline = doc->plainKateTextLine(line);
00181 
00182   // get indent width of current line
00183   int indentDepth = textline->indentDepth (tabWidth);
00184   int extraSpaces = indentDepth % indentWidth;
00185 
00186   // add change
00187   indentDepth += change;
00188 
00189   // if keepExtra is off, snap to a multiple of the indentWidth
00190   if (!keepExtra && extraSpaces > 0)
00191   {
00192     if (change < 0)
00193       indentDepth += indentWidth - extraSpaces;
00194     else
00195       indentDepth -= extraSpaces;
00196   }
00197 
00198   // do indent
00199   return doIndent(line, indentDepth);
00200 }
00201 
00202 void KateAutoIndent::keepIndent ( int line )
00203 {
00204   // no line in front, no work...
00205   if (line <= 0)
00206     return;
00207 
00208   Kate::TextLine prevTextLine = doc->plainKateTextLine(line-1);
00209   Kate::TextLine textLine     = doc->plainKateTextLine(line);
00210 
00211   // textline not found, cu
00212   if (!prevTextLine || !textLine)
00213     return;
00214 
00215   const QString previousWhitespace = prevTextLine->leadingWhitespace();
00216 
00217   // remove leading whitespace, then insert the leading indentation
00218   doc->editStart ();
00219 
00220   if (!keepExtra)
00221   {
00222     const QString currentWhitespace = textLine->leadingWhitespace();
00223     doc->editRemoveText (line, 0, currentWhitespace.length());
00224   }
00225 
00226   doc->editInsertText (line, 0, previousWhitespace);
00227   doc->editEnd ();
00228 }
00229 
00230 void KateAutoIndent::reloadScript()
00231 {
00232   // small trick to force reload
00233   m_script = 0; // prevent dangling pointer
00234   QString currentMode = m_mode;
00235   m_mode = QString();
00236   setMode(currentMode);
00237 }
00238 
00239 void KateAutoIndent::scriptIndent (KateView *view, const KTextEditor::Cursor &position, QChar typedChar)
00240 {
00241   QPair<int, int> result = m_script->indent (view, position, typedChar, indentWidth);
00242   int newIndentInChars = result.first;
00243 
00244   // handle negative values special
00245   if (newIndentInChars < -1)
00246     return;
00247 
00248   // reuse indentation of the previous line, just like the "normal" indenter
00249   if (newIndentInChars == -1)
00250   {
00251     // keep indent of previous line
00252     keepIndent (position.line());
00253 
00254     return;
00255   }
00256 
00257   int align = result.second;
00258   if (align > 0)
00259     kDebug (13060) << "Align: " << align;
00260 
00261   // we got a positive or zero indent to use...
00262   doIndent (position.line(), newIndentInChars, align);
00263 }
00264 
00265 bool KateAutoIndent::isStyleProvided(const KateIndentScript *script, const KateHighlighting *highlight)
00266 {
00267   QString requiredStyle = script->indentHeader().requiredStyle();
00268   return (requiredStyle.isEmpty() || requiredStyle == highlight->style());
00269 }
00270 
00271 void KateAutoIndent::setMode (const QString &name)
00272 {
00273   // bail out, already set correct mode...
00274   if (m_mode == name)
00275     return;
00276 
00277   // cleanup
00278   m_script = 0;
00279 
00280   // first, catch easy stuff... normal mode and none, easy...
00281   if ( name.isEmpty() || name == MODE_NONE )
00282   {
00283     m_mode = MODE_NONE;
00284     return;
00285   }
00286 
00287   if ( name == MODE_NORMAL )
00288   {
00289     m_mode = MODE_NORMAL;
00290     return;
00291   }
00292 
00293   // handle script indenters, if any for this name...
00294   KateIndentScript *script = KateGlobal::self()->scriptManager()->indentationScript(name);
00295   if ( script )
00296   {
00297     if (isStyleProvided(script, doc->highlight()))
00298     {
00299       m_script = script;
00300       m_mode = name;
00301 
00302       kDebug( 13060 ) << "mode: " << name << "accepted";
00303       return;
00304     }
00305     else
00306     {
00307       kWarning( 13060 ) << "mode" << name << "requires a different highlight style";
00308     }
00309   }
00310   else
00311   {
00312     kWarning( 13060 ) << "mode" << name << "does not exist";
00313   }
00314 
00315   // Fall back to normal
00316   m_mode = MODE_NORMAL;
00317 }
00318 
00319 void KateAutoIndent::checkRequiredStyle()
00320 {
00321   if (m_script)
00322   {
00323     if (!isStyleProvided(m_script, doc->highlight()))
00324     {
00325       kDebug( 13060 ) << "mode" << m_mode << "requires a different highlight style";
00326       doc->config()->setIndentationMode(MODE_NORMAL);
00327     }
00328   }
00329 }
00330 
00331 void KateAutoIndent::updateConfig ()
00332 {
00333   KateDocumentConfig *config = doc->config();
00334 
00335   useSpaces   = config->replaceTabsDyn();
00336   keepExtra   = config->keepExtraSpaces();
00337   tabWidth    = config->tabWidth();
00338   indentWidth = config->indentationWidth();
00339 }
00340 
00341 
00342 bool KateAutoIndent::changeIndent (const KTextEditor::Range &range, int change)
00343 {
00344   QList<int> skippedLines;
00345 
00346   // loop over all lines given...
00347   for (int line = range.start().line () < 0 ? 0 : range.start().line ();
00348        line <= qMin (range.end().line (), doc->lines()-1); ++line)
00349   {
00350     // don't indent empty lines
00351     if (doc->line(line).isEmpty())
00352     {
00353       skippedLines.append (line);
00354       continue;
00355     }
00356     // don't indent the last line when the cursor is on the first column
00357     if (line == range.end().line() && range.end().column() == 0)
00358     {
00359       skippedLines.append (line);
00360       continue;
00361     }
00362 
00363     doIndentRelative(line, change * indentWidth);
00364   }
00365 
00366   if (skippedLines.count() > range.numberOfLines())
00367   {
00368     // all lines were empty, so indent them nevertheless
00369     foreach (int line, skippedLines)
00370       doIndentRelative(line, change * indentWidth);
00371   }
00372 
00373   return true;
00374 }
00375 
00376 void KateAutoIndent::indent (KateView *view, const KTextEditor::Range &range)
00377 {
00378   // no script, do nothing...
00379   if (!m_script)
00380     return;
00381 
00382   doc->pushEditState();
00383   doc->editStart();
00384   // loop over all lines given...
00385   for (int line = range.start().line () < 0 ? 0 : range.start().line ();
00386        line <= qMin (range.end().line (), doc->lines()-1); ++line)
00387   {
00388     // let the script indent for us...
00389     scriptIndent (view, KTextEditor::Cursor (line, 0), QChar());
00390   }
00391   doc->editEnd ();
00392   doc->popEditState();
00393 }
00394 
00395 void KateAutoIndent::userTypedChar (KateView *view, const KTextEditor::Cursor &position, QChar typedChar)
00396 {
00397   // normal mode
00398   if (m_mode == MODE_NORMAL)
00399   {
00400     // only indent on new line, per default
00401     if (typedChar != '\n')
00402       return;
00403 
00404     // keep indent of previous line
00405     keepIndent (position.line());
00406 
00407     return;
00408   }
00409 
00410   // no script, do nothing...
00411   if (!m_script)
00412     return;
00413 
00414   // does the script allow this char as trigger?
00415   if (typedChar != '\n' && !m_script->triggerCharacters().contains(typedChar))
00416     return;
00417 
00418   // let the script indent for us...
00419   scriptIndent (view, position, typedChar);
00420 }
00421 //END KateAutoIndent
00422 
00423 //BEGIN KateViewIndentAction
00424 KateViewIndentationAction::KateViewIndentationAction(KateDocument *_doc, const QString& text, QObject *parent)
00425        : KActionMenu (text, parent), doc(_doc)
00426 {
00427   connect(menu(),SIGNAL(aboutToShow()),this,SLOT(slotAboutToShow()));
00428   actionGroup = new QActionGroup(menu());
00429 }
00430 
00431 void KateViewIndentationAction::slotAboutToShow()
00432 {
00433   QStringList modes = KateAutoIndent::listModes ();
00434 
00435   menu()->clear ();
00436   foreach (QAction *action, actionGroup->actions()) {
00437     actionGroup->removeAction(action);
00438   }
00439   for (int z=0; z<modes.size(); ++z) {
00440     QAction *action = menu()->addAction( '&' + KateAutoIndent::modeDescription(z).replace('&', "&&") );
00441     actionGroup->addAction(action);
00442     action->setCheckable( true );
00443     action->setData( z );
00444 
00445     QString requiredStyle = KateAutoIndent::modeRequiredStyle(z);
00446     action->setEnabled(requiredStyle.isEmpty() || requiredStyle == doc->highlight()->style());
00447 
00448     if ( doc->config()->indentationMode() == KateAutoIndent::modeName (z) )
00449       action->setChecked( true );
00450   }
00451 
00452   disconnect( menu(), SIGNAL( triggered( QAction* ) ), this, SLOT( setMode( QAction* ) ) );
00453   connect( menu(), SIGNAL( triggered( QAction* ) ), this, SLOT( setMode( QAction* ) ) );
00454 }
00455 
00456 void KateViewIndentationAction::setMode (QAction *action)
00457 {
00458   // set new mode
00459   doc->config()->setIndentationMode(KateAutoIndent::modeName (action->data().toInt()));
00460 }
00461 //END KateViewIndentationAction
00462 
00463 // kate: space-indent on; indent-width 2; replace-tabs on;

Kate

Skip menu "Kate"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal