00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
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
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
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);
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(KateView *view, 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
00133 if (!textline)
00134 return false;
00135
00136
00137 if (indentDepth < 0)
00138 indentDepth = 0;
00139
00140 QString indentString = tabString (indentDepth, align);
00141
00142 int first_char = textline->firstChar();
00143
00144 if (first_char < 0)
00145 first_char = textline->length();
00146
00147
00148 doc->editStart (view);
00149 doc->editRemoveText (line, 0, first_char);
00150 doc->editInsertText (line, 0, indentString);
00151 doc->editEnd ();
00152
00153 return true;
00154 }
00155
00156 bool KateAutoIndent::doIndentRelative(KateView *view, int line, int change)
00157 {
00158 kDebug (13060) << "doIndentRelative: line: " << line << " change: " << change;
00159
00160 KateTextLine::Ptr textline = doc->plainKateTextLine(line);
00161
00162
00163 int indentDepth = textline->indentDepth (tabWidth);
00164 int extraSpaces = indentDepth % indentWidth;
00165
00166
00167 indentDepth += change;
00168
00169
00170 if (!keepExtra && extraSpaces > 0)
00171 {
00172 if (change < 0)
00173 indentDepth += indentWidth - extraSpaces;
00174 else
00175 indentDepth -= extraSpaces;
00176 }
00177
00178
00179 return doIndent(view, line, indentDepth);
00180 }
00181
00182 void KateAutoIndent::keepIndent ( KateView *view, int line )
00183 {
00184
00185 if (line <= 0)
00186 return;
00187
00188 KateTextLine::Ptr textline = doc->plainKateTextLine(line-1);
00189
00190
00191 if (!textline)
00192 return;
00193
00194 doIndent (view, line, textline->indentDepth (tabWidth));
00195 }
00196
00197 void KateAutoIndent::scriptIndent (KateView *view, const KTextEditor::Cursor &position, QChar typedChar)
00198 {
00199 QPair<int, int> result = m_script->indent (view, position, typedChar, indentWidth);
00200 int newIndentInChars = result.first;
00201
00202
00203 if (newIndentInChars < -1)
00204 return;
00205
00206
00207 if (newIndentInChars == -1)
00208 {
00209
00210 keepIndent (view, position.line());
00211
00212 return;
00213 }
00214
00215 int align = result.second;
00216 if (align > 0)
00217 kDebug (13060) << "Align: " << align;
00218
00219
00220 doIndent (view, position.line(), newIndentInChars, align);
00221 }
00222
00223 bool KateAutoIndent::isStyleProvided(KateIndentScript *script)
00224 {
00225 QString requiredStyle = script->information().requiredStyle;
00226 return (requiredStyle.isEmpty() || requiredStyle == doc->highlight()->style());
00227 }
00228
00229 void KateAutoIndent::setMode (const QString &name)
00230 {
00231
00232 if (m_mode == name)
00233 return;
00234
00235
00236 m_script = 0;
00237 m_normal = false;
00238
00239
00240 if ( name.isEmpty() || name == MODE_NONE )
00241 {
00242 m_mode = MODE_NONE;
00243 return;
00244 }
00245
00246 if ( name == MODE_NORMAL )
00247 {
00248 m_normal = true;
00249 m_mode = MODE_NORMAL;
00250 return;
00251 }
00252
00253
00254 KateIndentScript *script = KateGlobal::self()->scriptManager()->indentationScript(name);
00255 if ( script )
00256 {
00257 if (isStyleProvided(script))
00258 {
00259 m_script = script;
00260 m_mode = name;
00261
00262 kDebug( 13060 ) << "mode: " << name << "accepted";
00263 return;
00264 }
00265 else
00266 {
00267 kWarning( 13060 ) << "mode" << name << "requires a different highlight style";
00268 }
00269 }
00270 else
00271 {
00272 kWarning( 13060 ) << "mode" << name << "does not exist";
00273 }
00274
00275
00276 m_normal = true;
00277 m_mode = MODE_NORMAL;
00278 }
00279
00280 void KateAutoIndent::checkRequiredStyle()
00281 {
00282 if (m_script)
00283 {
00284 if (!isStyleProvided(m_script))
00285 {
00286 kDebug( 13060 ) << "mode" << m_mode << "requires a different highlight style";
00287 doc->config()->setIndentationMode(MODE_NORMAL);
00288 }
00289 }
00290 }
00291
00292 void KateAutoIndent::updateConfig ()
00293 {
00294 KateDocumentConfig *config = doc->config();
00295
00296 useSpaces = config->configFlags() & KateDocumentConfig::cfReplaceTabsDyn;
00297 keepExtra = config->configFlags() & KateDocumentConfig::cfKeepExtraSpaces;
00298 tabWidth = config->tabWidth();
00299 indentWidth = config->indentationWidth();
00300 }
00301
00302
00303 bool KateAutoIndent::changeIndent (KateView *view, const KTextEditor::Range &range, int change)
00304 {
00305 QList<int> skippedLines;
00306
00307
00308 for (int line = range.start().line () < 0 ? 0 : range.start().line ();
00309 line <= qMin (range.end().line (), doc->lines()-1); ++line)
00310 {
00311
00312 if (doc->line(line).isEmpty())
00313 {
00314 skippedLines.append (line);
00315 continue;
00316 }
00317
00318 if (line == range.end().line() && range.end().column() == 0)
00319 {
00320 skippedLines.append (line);
00321 continue;
00322 }
00323
00324 doIndentRelative(view, line, change * indentWidth);
00325 }
00326
00327 if (skippedLines.count() > range.numberOfLines())
00328 {
00329
00330 foreach (int line, skippedLines)
00331 doIndentRelative(view, line, change * indentWidth);
00332 }
00333
00334 return true;
00335 }
00336
00337 void KateAutoIndent::indent (KateView *view, const KTextEditor::Range &range)
00338 {
00339
00340 if (!m_script)
00341 return;
00342
00343 doc->pushEditState();
00344 doc->editStart();
00345
00346 for (int line = range.start().line () < 0 ? 0 : range.start().line ();
00347 line <= qMin (range.end().line (), doc->lines()-1); ++line)
00348 {
00349
00350 scriptIndent (view, KTextEditor::Cursor (line, 0), QChar());
00351 }
00352 doc->editEnd ();
00353 doc->popEditState();
00354 }
00355
00356 void KateAutoIndent::userTypedChar (KateView *view, const KTextEditor::Cursor &position, QChar typedChar)
00357 {
00358
00359 if (m_normal)
00360 {
00361
00362 if (typedChar != '\n')
00363 return;
00364
00365
00366 keepIndent (view, position.line());
00367
00368 return;
00369 }
00370
00371
00372 if (!m_script)
00373 return;
00374
00375
00376 if (typedChar != '\n' && !m_script->triggerCharacters().contains(typedChar))
00377 return;
00378
00379
00380 scriptIndent (view, position, typedChar);
00381 }
00382
00383
00384
00385 KateViewIndentationAction::KateViewIndentationAction(KateDocument *_doc, const QString& text, QObject *parent)
00386 : KActionMenu (text, parent), doc(_doc)
00387 {
00388 connect(menu(),SIGNAL(aboutToShow()),this,SLOT(slotAboutToShow()));
00389
00390 }
00391
00392 void KateViewIndentationAction::slotAboutToShow()
00393 {
00394 QStringList modes = KateAutoIndent::listModes ();
00395
00396 menu()->clear ();
00397 for (int z=0; z<modes.size(); ++z) {
00398 QAction *action = menu()->addAction( '&' + KateAutoIndent::modeDescription(z).replace('&', "&&") );
00399 action->setCheckable( true );
00400 action->setData( z );
00401
00402 QString requiredStyle = KateAutoIndent::modeRequiredStyle(z);
00403 action->setEnabled(requiredStyle.isEmpty() || requiredStyle == doc->highlight()->style());
00404
00405 if ( doc->config()->indentationMode() == KateAutoIndent::modeName (z) )
00406 action->setChecked( true );
00407 }
00408
00409 disconnect( menu(), SIGNAL( triggered( QAction* ) ), this, SLOT( setMode( QAction* ) ) );
00410 connect( menu(), SIGNAL( triggered( QAction* ) ), this, SLOT( setMode( QAction* ) ) );
00411 }
00412
00413 void KateViewIndentationAction::setMode (QAction *action)
00414 {
00415
00416 doc->config()->setIndentationMode(KateAutoIndent::modeName (action->data().toInt()));
00417 }
00418
00419
00420