• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • 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()->indentationScripts();
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)->information().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)->information().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)->information().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   : doc(_doc), m_normal (false), m_script (0)
00102 {
00103   // don't call updateConfig() here, document might is not ready for that....
00104 }
00105 
00106 KateAutoIndent::~KateAutoIndent ()
00107 {
00108 }
00109 
00110 QString KateAutoIndent::tabString (int length, int align) const
00111 {
00112   QString s;
00113   length = qMin (length, 256); // sanity check for large values of pos
00114   int spaces = qBound(0, align - length, 256);
00115 
00116   if (!useSpaces)
00117   {
00118     s.append (QString (length / tabWidth, '\t'));
00119     length = length % tabWidth;
00120   }
00121   s.append(QString(length + spaces, ' '));
00122 
00123   return s;
00124 }
00125 
00126 bool KateAutoIndent::doIndent(int line, int indentDepth, int align)
00127 {
00128   kDebug (13060) << "doIndent: line: " << line << " indentDepth: " << indentDepth << " align: " << align;
00129 
00130   KateTextLine::Ptr textline = doc->plainKateTextLine(line);
00131 
00132   // textline not found, cu
00133   if (!textline)
00134     return false;
00135 
00136   // sanity check
00137   if (indentDepth < 0)
00138     indentDepth = 0;
00139 
00140   int first_char = textline->firstChar();
00141   if (first_char < 0)
00142     first_char = textline->length();
00143 
00144   // Preserve existing "tabs then spaces" alignment if and only if:
00145   //  - no alignment was passed to doIndent and
00146   //  - we aren't using spaces for indentation and
00147   //  - we aren't rounding indentation up to the next multiple of the indentation width and
00148   //  - we aren't using a combination to tabs and spaces for alignment, or in other words
00149   //    the indent width is a multiple of the tab width.
00150   bool preserveAlignment = !useSpaces && keepExtra && indentWidth % tabWidth == 0;
00151   if (align == 0 && preserveAlignment)
00152   {
00153     // Count the number of consecutive spaces at the end of the existing indentation
00154     QString oldIndentation = textline->string(0, first_char);
00155     int i = oldIndentation.size() - 1;
00156     while (i >= 0 && oldIndentation.at(i) == ' ')
00157       --i;
00158     // Use the passed indentDepth as the alignment, and set the indentDepth to
00159     // that value minus the number of spaces found (but don't let it get negative).
00160     align = indentDepth;
00161     indentDepth = qMax(0, align - (oldIndentation.size() - 1 - i));
00162   }
00163 
00164   QString indentString = tabString(indentDepth, align);
00165 
00166   // remove leading whitespace, then insert the leading indentation
00167   doc->editStart ();
00168   doc->editRemoveText (line, 0, first_char);
00169   doc->editInsertText (line, 0, indentString);
00170   doc->editEnd ();
00171 
00172   return true;
00173 }
00174 
00175 bool KateAutoIndent::doIndentRelative(int line, int change)
00176 {
00177   kDebug (13060) << "doIndentRelative: line: " << line << " change: " << change;
00178 
00179   KateTextLine::Ptr textline = doc->plainKateTextLine(line);
00180 
00181   // get indent width of current line
00182   int indentDepth = textline->indentDepth (tabWidth);
00183   int extraSpaces = indentDepth % indentWidth;
00184 
00185   // add change
00186   indentDepth += change;
00187 
00188   // if keepExtra is off, snap to a multiple of the indentWidth
00189   if (!keepExtra && extraSpaces > 0)
00190   {
00191     if (change < 0)
00192       indentDepth += indentWidth - extraSpaces;
00193     else
00194       indentDepth -= extraSpaces;
00195   }
00196 
00197   // do indent
00198   return doIndent(line, indentDepth);
00199 }
00200 
00201 void KateAutoIndent::keepIndent ( int line )
00202 {
00203   // no line in front, no work...
00204   if (line <= 0)
00205     return;
00206 
00207   KateTextLine::Ptr prevTextLine = doc->plainKateTextLine(line-1);
00208   KateTextLine::Ptr textLine     = doc->plainKateTextLine(line);
00209 
00210   // textline not found, cu
00211   if (!prevTextLine || !textLine)
00212     return;
00213 
00214   const QString previousWhitespace = prevTextLine->leadingWhitespace();
00215 
00216   // remove leading whitespace, then insert the leading indentation
00217   doc->editStart ();
00218 
00219   if (!keepExtra)
00220   {
00221     const QString currentWhitespace = textLine->leadingWhitespace();
00222     doc->editRemoveText (line, 0, currentWhitespace.length());
00223   }
00224 
00225   doc->editInsertText (line, 0, previousWhitespace);
00226   doc->editEnd ();
00227 }
00228 
00229 void KateAutoIndent::scriptIndent (KateView *view, const KTextEditor::Cursor &position, QChar typedChar)
00230 {
00231   QPair<int, int> result = m_script->indent (view, position, typedChar, indentWidth);
00232   int newIndentInChars = result.first;
00233 
00234   // handle negative values special
00235   if (newIndentInChars < -1)
00236     return;
00237 
00238   // reuse indentation of the previous line, just like the "normal" indenter
00239   if (newIndentInChars == -1)
00240   {
00241     // keep indent of previous line
00242     keepIndent (position.line());
00243 
00244     return;
00245   }
00246 
00247   int align = result.second;
00248   if (align > 0)
00249     kDebug (13060) << "Align: " << align;
00250 
00251   // we got a positive or zero indent to use...
00252   doIndent (position.line(), newIndentInChars, align);
00253 }
00254 
00255 bool KateAutoIndent::isStyleProvided(KateIndentScript *script)
00256 {
00257   QString requiredStyle = script->information().requiredStyle;
00258   return (requiredStyle.isEmpty() || requiredStyle == doc->highlight()->style());
00259 }
00260 
00261 void KateAutoIndent::setMode (const QString &name)
00262 {
00263   // bail out, already set correct mode...
00264   if (m_mode == name)
00265     return;
00266   
00267   // cleanup
00268   m_script = 0;
00269   m_normal = false;
00270 
00271   // first, catch easy stuff... normal mode and none, easy...
00272   if ( name.isEmpty() || name == MODE_NONE )
00273   {
00274     m_mode = MODE_NONE;
00275     return;
00276   }
00277   
00278   if ( name == MODE_NORMAL )
00279   {
00280     m_normal = true;
00281     m_mode = MODE_NORMAL;
00282     return;
00283   }
00284   
00285   // handle script indenters, if any for this name...
00286   KateIndentScript *script = KateGlobal::self()->scriptManager()->indentationScript(name);
00287   if ( script )
00288   {
00289     if (isStyleProvided(script))
00290     {
00291       m_script = script;
00292       m_mode = name;
00293       
00294       kDebug( 13060 ) << "mode: " << name << "accepted";
00295       return;
00296     }
00297     else
00298     {
00299       kWarning( 13060 ) << "mode" << name << "requires a different highlight style";
00300     }
00301   }
00302   else 
00303   {
00304     kWarning( 13060 ) << "mode" << name << "does not exist";
00305   }
00306   
00307   // Fall back to normal
00308   m_normal = true;
00309   m_mode = MODE_NORMAL;
00310 }
00311 
00312 void KateAutoIndent::checkRequiredStyle()
00313 {
00314   if (m_script)
00315   {
00316     if (!isStyleProvided(m_script))
00317     {
00318       kDebug( 13060 ) << "mode" << m_mode << "requires a different highlight style";
00319       doc->config()->setIndentationMode(MODE_NORMAL);
00320     }
00321   }
00322 }
00323 
00324 void KateAutoIndent::updateConfig ()
00325 {
00326   KateDocumentConfig *config = doc->config();
00327 
00328   useSpaces   = config->configFlags() & KateDocumentConfig::cfReplaceTabsDyn;
00329   keepExtra   = config->configFlags() & KateDocumentConfig::cfKeepExtraSpaces;
00330   tabWidth    = config->tabWidth();
00331   indentWidth = config->indentationWidth();
00332 }
00333 
00334 
00335 bool KateAutoIndent::changeIndent (const KTextEditor::Range &range, int change)
00336 {
00337   QList<int> skippedLines;
00338 
00339   // loop over all lines given...
00340   for (int line = range.start().line () < 0 ? 0 : range.start().line ();
00341        line <= qMin (range.end().line (), doc->lines()-1); ++line)
00342   {
00343     // don't indent empty lines
00344     if (doc->line(line).isEmpty())
00345     {
00346       skippedLines.append (line);
00347       continue;
00348     }
00349     // don't indent the last line when the cursor is on the first column
00350     if (line == range.end().line() && range.end().column() == 0)
00351     {
00352       skippedLines.append (line);
00353       continue;
00354     }
00355 
00356     doIndentRelative(line, change * indentWidth);
00357   }
00358 
00359   if (skippedLines.count() > range.numberOfLines())
00360   {
00361     // all lines were empty, so indent them nevertheless
00362     foreach (int line, skippedLines)
00363       doIndentRelative(line, change * indentWidth);
00364   }
00365 
00366   return true;
00367 }
00368 
00369 void KateAutoIndent::indent (KateView *view, const KTextEditor::Range &range)
00370 {
00371   // no script, do nothing...
00372   if (!m_script)
00373     return;
00374 
00375   doc->pushEditState();
00376   doc->editStart();
00377   // loop over all lines given...
00378   for (int line = range.start().line () < 0 ? 0 : range.start().line ();
00379        line <= qMin (range.end().line (), doc->lines()-1); ++line)
00380   {
00381     // let the script indent for us...
00382     scriptIndent (view, KTextEditor::Cursor (line, 0), QChar());
00383   }
00384   doc->editEnd ();
00385   doc->popEditState();
00386 }
00387 
00388 void KateAutoIndent::userTypedChar (KateView *view, const KTextEditor::Cursor &position, QChar typedChar)
00389 {
00390   // normal mode
00391   if (m_normal)
00392   {
00393     // only indent on new line, per default
00394     if (typedChar != '\n')
00395       return;
00396 
00397     // keep indent of previous line
00398     keepIndent (position.line());
00399 
00400     return;
00401   }
00402 
00403   // no script, do nothing...
00404   if (!m_script)
00405     return;
00406 
00407   // does the script allow this char as trigger?
00408   if (typedChar != '\n' && !m_script->triggerCharacters().contains(typedChar))
00409     return;
00410 
00411   // let the script indent for us...
00412   scriptIndent (view, position, typedChar);
00413 }
00414 //END KateAutoIndent
00415 
00416 //BEGIN KateViewIndentAction
00417 KateViewIndentationAction::KateViewIndentationAction(KateDocument *_doc, const QString& text, QObject *parent)
00418        : KActionMenu (text, parent), doc(_doc)
00419 {
00420   connect(menu(),SIGNAL(aboutToShow()),this,SLOT(slotAboutToShow()));
00421   actionGroup = new QActionGroup(menu());
00422 }
00423 
00424 void KateViewIndentationAction::slotAboutToShow()
00425 {
00426   QStringList modes = KateAutoIndent::listModes ();
00427 
00428   menu()->clear ();
00429   foreach (QAction *action, actionGroup->actions()) {
00430     actionGroup->removeAction(action);
00431   }
00432   for (int z=0; z<modes.size(); ++z) {
00433     QAction *action = menu()->addAction( '&' + KateAutoIndent::modeDescription(z).replace('&', "&&") );
00434     actionGroup->addAction(action);
00435     action->setCheckable( true );
00436     action->setData( z );
00437 
00438     QString requiredStyle = KateAutoIndent::modeRequiredStyle(z);
00439     action->setEnabled(requiredStyle.isEmpty() || requiredStyle == doc->highlight()->style());
00440 
00441     if ( doc->config()->indentationMode() == KateAutoIndent::modeName (z) )
00442       action->setChecked( true );
00443   }
00444 
00445   disconnect( menu(), SIGNAL( triggered( QAction* ) ), this, SLOT( setMode( QAction* ) ) );
00446   connect( menu(), SIGNAL( triggered( QAction* ) ), this, SLOT( setMode( QAction* ) ) );
00447 }
00448 
00449 void KateViewIndentationAction::setMode (QAction *action)
00450 {
00451   // set new mode
00452   doc->config()->setIndentationMode(KateAutoIndent::modeName (action->data().toInt()));
00453 }
00454 //END KateViewIndentationAction
00455 
00456 // 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
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
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