You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdelibs/kate/part/kateautoindent.cpp

2544 lines
74 KiB

/* This file is part of the KDE libraries
Copyright (C) 2003 Jesse Yurkovich <yurkjes@iit.edu>
Copyright (C) 2004 >Anders Lund <anders@alweb.dk> (KateVarIndent class)
Copyright (C) 2005 Dominik Haumann <dhdev@gmx.de> (basic support for config page)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kateautoindent.h"
#include "kateautoindent.moc"
#include "kateconfig.h"
#include "katehighlight.h"
#include "katefactory.h"
#include "katejscript.h"
#include "kateview.h"
#include <klocale.h>
#include <kdebug.h>
#include <kpopupmenu.h>
#include <cctype>
//BEGIN KateAutoIndent
KateAutoIndent *KateAutoIndent::createIndenter (KateDocument *doc, uint mode)
{
if (mode == KateDocumentConfig::imNormal)
return new KateNormalIndent (doc);
else if (mode == KateDocumentConfig::imCStyle)
return new KateCSmartIndent (doc);
else if (mode == KateDocumentConfig::imPythonStyle)
return new KatePythonIndent (doc);
else if (mode == KateDocumentConfig::imXmlStyle)
return new KateXmlIndent (doc);
else if (mode == KateDocumentConfig::imCSAndS)
return new KateCSAndSIndent (doc);
else if ( mode == KateDocumentConfig::imVarIndent )
return new KateVarIndent ( doc );
// else if ( mode == KateDocumentConfig::imScriptIndent)
// return new KateScriptIndent ( doc );
return new KateAutoIndent (doc);
}
QStringList KateAutoIndent::listModes ()
{
QStringList l;
l << modeDescription(KateDocumentConfig::imNone);
l << modeDescription(KateDocumentConfig::imNormal);
l << modeDescription(KateDocumentConfig::imCStyle);
l << modeDescription(KateDocumentConfig::imPythonStyle);
l << modeDescription(KateDocumentConfig::imXmlStyle);
l << modeDescription(KateDocumentConfig::imCSAndS);
l << modeDescription(KateDocumentConfig::imVarIndent);
// l << modeDescription(KateDocumentConfig::imScriptIndent);
return l;
}
QString KateAutoIndent::modeName (uint mode)
{
if (mode == KateDocumentConfig::imNormal)
return QString ("normal");
else if (mode == KateDocumentConfig::imCStyle)
return QString ("cstyle");
else if (mode == KateDocumentConfig::imPythonStyle)
return QString ("python");
else if (mode == KateDocumentConfig::imXmlStyle)
return QString ("xml");
else if (mode == KateDocumentConfig::imCSAndS)
return QString ("csands");
else if ( mode == KateDocumentConfig::imVarIndent )
return QString( "varindent" );
// else if ( mode == KateDocumentConfig::imScriptIndent )
// return QString( "scriptindent" );
return QString ("none");
}
QString KateAutoIndent::modeDescription (uint mode)
{
if (mode == KateDocumentConfig::imNormal)
return i18n ("Normal");
else if (mode == KateDocumentConfig::imCStyle)
return i18n ("C Style");
else if (mode == KateDocumentConfig::imPythonStyle)
return i18n ("Python Style");
else if (mode == KateDocumentConfig::imXmlStyle)
return i18n ("XML Style");
else if (mode == KateDocumentConfig::imCSAndS)
return i18n ("S&S C Style");
else if ( mode == KateDocumentConfig::imVarIndent )
return i18n("Variable Based Indenter");
// else if ( mode == KateDocumentConfig::imScriptIndent )
// return i18n("JavaScript Indenter");
return i18n ("None");
}
uint KateAutoIndent::modeNumber (const QString &name)
{
if (modeName(KateDocumentConfig::imNormal) == name)
return KateDocumentConfig::imNormal;
else if (modeName(KateDocumentConfig::imCStyle) == name)
return KateDocumentConfig::imCStyle;
else if (modeName(KateDocumentConfig::imPythonStyle) == name)
return KateDocumentConfig::imPythonStyle;
else if (modeName(KateDocumentConfig::imXmlStyle) == name)
return KateDocumentConfig::imXmlStyle;
else if (modeName(KateDocumentConfig::imCSAndS) == name)
return KateDocumentConfig::imCSAndS;
else if ( modeName( KateDocumentConfig::imVarIndent ) == name )
return KateDocumentConfig::imVarIndent;
// else if ( modeName( KateDocumentConfig::imScriptIndent ) == name )
// return KateDocumentConfig::imScriptIndent;
return KateDocumentConfig::imNone;
}
bool KateAutoIndent::hasConfigPage (uint mode)
{
// if ( mode == KateDocumentConfig::imScriptIndent )
// return true;
return false;
}
IndenterConfigPage* KateAutoIndent::configPage(QWidget *parent, uint mode)
{
// if ( mode == KateDocumentConfig::imScriptIndent )
// return new ScriptIndentConfigPage(parent, "script_indent_config_page");
return 0;
}
KateAutoIndent::KateAutoIndent (KateDocument *_doc)
: QObject(), doc(_doc)
{
}
KateAutoIndent::~KateAutoIndent ()
{
}
//END KateAutoIndent
//BEGIN KateViewIndentAction
KateViewIndentationAction::KateViewIndentationAction(KateDocument *_doc, const QString& text, QObject* parent, const char* name)
: KActionMenu (text, parent, name), doc(_doc)
{
connect(popupMenu(),SIGNAL(aboutToShow()),this,SLOT(slotAboutToShow()));
}
void KateViewIndentationAction::slotAboutToShow()
{
QStringList modes = KateAutoIndent::listModes ();
popupMenu()->clear ();
for (uint z=0; z<modes.size(); ++z)
popupMenu()->insertItem ( '&' + KateAutoIndent::modeDescription(z).replace('&', "&&"), this, SLOT(setMode(int)), 0, z);
popupMenu()->setItemChecked (doc->config()->indentationMode(), true);
}
void KateViewIndentationAction::setMode (int mode)
{
doc->config()->setIndentationMode((uint)mode);
}
//END KateViewIndentationAction
//BEGIN KateNormalIndent
KateNormalIndent::KateNormalIndent (KateDocument *_doc)
: KateAutoIndent (_doc)
{
// if highlighting changes, update attributes
connect(_doc, SIGNAL(hlChanged()), this, SLOT(updateConfig()));
}
KateNormalIndent::~KateNormalIndent ()
{
}
void KateNormalIndent::updateConfig ()
{
KateDocumentConfig *config = doc->config();
useSpaces = config->configFlags() & KateDocument::cfSpaceIndent || config->configFlags() & KateDocumentConfig::cfReplaceTabsDyn;
mixedIndent = useSpaces && config->configFlags() & KateDocumentConfig::cfMixedIndent;
keepProfile = config->configFlags() & KateDocument::cfKeepIndentProfile;
tabWidth = config->tabWidth();
indentWidth = useSpaces? config->indentationWidth() : tabWidth;
commentAttrib = 255;
doxyCommentAttrib = 255;
regionAttrib = 255;
symbolAttrib = 255;
alertAttrib = 255;
tagAttrib = 255;
wordAttrib = 255;
keywordAttrib = 255;
normalAttrib = 255;
extensionAttrib = 255;
preprocessorAttrib = 255;
stringAttrib = 255;
charAttrib = 255;
KateHlItemDataList items;
doc->highlight()->getKateHlItemDataListCopy (0, items);
for (uint i=0; i<items.count(); i++)
{
QString name = items.at(i)->name;
if (name.find("Comment") != -1 && commentAttrib == 255)
{
commentAttrib = i;
}
else if (name.find("Region Marker") != -1 && regionAttrib == 255)
{
regionAttrib = i;
}
else if (name.find("Symbol") != -1 && symbolAttrib == 255)
{
symbolAttrib = i;
}
else if (name.find("Alert") != -1)
{
alertAttrib = i;
}
else if (name.find("Comment") != -1 && commentAttrib != 255 && doxyCommentAttrib == 255)
{
doxyCommentAttrib = i;
}
else if (name.find("Tags") != -1 && tagAttrib == 255)
{
tagAttrib = i;
}
else if (name.find("Word") != -1 && wordAttrib == 255)
{
wordAttrib = i;
}
else if (name.find("Keyword") != -1 && keywordAttrib == 255)
{
keywordAttrib = i;
}
else if (name.find("Normal") != -1 && normalAttrib == 255)
{
normalAttrib = i;
}
else if (name.find("Extensions") != -1 && extensionAttrib == 255)
{
extensionAttrib = i;
}
else if (name.find("Preprocessor") != -1 && preprocessorAttrib == 255)
{
preprocessorAttrib = i;
}
else if (name.find("String") != -1 && stringAttrib == 255)
{
stringAttrib = i;
}
else if (name.find("Char") != -1 && charAttrib == 255)
{
charAttrib = i;
}
}
}
bool KateNormalIndent::isBalanced (KateDocCursor &begin, const KateDocCursor &end, QChar open, QChar close, uint &pos) const
{
int parenOpen = 0;
bool atLeastOne = false;
bool getNext = false;
pos = doc->plainKateTextLine(begin.line())->firstChar();
// Iterate one-by-one finding opening and closing chars
// Assume that open and close are 'Symbol' characters
while (begin < end)
{
QChar c = begin.currentChar();
if (begin.currentAttrib() == symbolAttrib)
{
if (c == open)
{
if (!atLeastOne)
{
atLeastOne = true;
getNext = true;
pos = measureIndent(begin) + 1;
}
parenOpen++;
}
else if (c == close)
{
parenOpen--;
}
}
else if (getNext && !c.isSpace())
{
getNext = false;
pos = measureIndent(begin);
}
if (atLeastOne && parenOpen <= 0)
return true;
if (!begin.moveForward(1))
break;
}
return (atLeastOne) ? false : true;
}
bool KateNormalIndent::skipBlanks (KateDocCursor &cur, KateDocCursor &max, bool newline) const
{
int curLine = cur.line();
if (newline)
cur.moveForward(1);
if (cur >= max)
return false;
do
{
uchar attrib = cur.currentAttrib();
const QString hlFile = doc->highlight()->hlKeyForAttrib( attrib );
if (attrib != commentAttrib && attrib != regionAttrib && attrib != alertAttrib && attrib != preprocessorAttrib && !hlFile.endsWith("doxygen.xml"))
{
QChar c = cur.currentChar();
if (!c.isNull() && !c.isSpace())
break;
}
if (!cur.moveForward(1))
{
// not able to move forward, so set cur to max
cur = max;
break;
}
// Make sure col is 0 if we spill into next line i.e. count the '\n' as a character
if (curLine != cur.line())
{
if (!newline)
break;
curLine = cur.line();
cur.setCol(0);
}
} while (cur < max);
if (cur > max)
cur = max;
return true;
}
uint KateNormalIndent::measureIndent (KateDocCursor &cur) const
{
// We cannot short-cut by checking for useSpaces because there may be
// tabs in the line despite this setting.
return doc->plainKateTextLine(cur.line())->cursorX(cur.col(), tabWidth);
}
QString KateNormalIndent::tabString(uint pos) const
{
QString s;
pos = kMin (pos, 80U); // sanity check for large values of pos
if (!useSpaces || mixedIndent)
{
while (pos >= tabWidth)
{
s += '\t';
pos -= tabWidth;
}
}
while (pos > 0)
{
s += ' ';
pos--;
}
return s;
}
void KateNormalIndent::processNewline (KateDocCursor &begin, bool /*needContinue*/)
{
int line = begin.line() - 1;
int pos = begin.col();
while ((line > 0) && (pos < 0)) // search a not empty text line
pos = doc->plainKateTextLine(--line)->firstChar();
if (pos > 0)
{
QString filler = doc->text(line, 0, line, pos);
doc->insertText(begin.line(), 0, filler);
begin.setCol(filler.length());
}
else
begin.setCol(0);
}
//END
//BEGIN KateCSmartIndent
KateCSmartIndent::KateCSmartIndent (KateDocument *doc)
: KateNormalIndent (doc),
allowSemi (false),
processingBlock (false)
{
kdDebug(13030)<<"CREATING KATECSMART INTDETER"<<endl;
}
KateCSmartIndent::~KateCSmartIndent ()
{
}
void KateCSmartIndent::processLine (KateDocCursor &line)
{
kdDebug(13030)<<"PROCESSING LINE "<<line.line()<<endl;
KateTextLine::Ptr textLine = doc->plainKateTextLine(line.line());
int firstChar = textLine->firstChar();
// Empty line is worthless ... but only when doing more than 1 line
if (firstChar == -1 && processingBlock)
return;
uint indent = 0;
// TODO Here we do not check for beginning and ending comments ...
QChar first = textLine->getChar(firstChar);
QChar last = textLine->getChar(textLine->lastChar());
if (first == '}')
{
indent = findOpeningBrace(line);
}
else if (first == ')')
{
indent = findOpeningParen(line);
}
else if (first == '{')
{
// If this is the first brace, we keep the indent at 0
KateDocCursor temp(line.line(), firstChar, doc);
if (!firstOpeningBrace(temp))
indent = calcIndent(temp, false);
}
else if (first == ':')
{
// Initialization lists (handle c++ and c#)
int pos = findOpeningBrace(line);
if (pos == 0)
indent = indentWidth;
else
indent = pos + (indentWidth * 2);
}
else if (last == ':')
{
if (textLine->stringAtPos (firstChar, "case") ||
textLine->stringAtPos (firstChar, "default") ||
textLine->stringAtPos (firstChar, "public") ||
textLine->stringAtPos (firstChar, "private") ||
textLine->stringAtPos (firstChar, "protected") ||
textLine->stringAtPos (firstChar, "signals") ||
textLine->stringAtPos (firstChar, "Q_SIGNALS") ||
textLine->stringAtPos (firstChar, "Q_SLOTS") ||
textLine->stringAtPos (firstChar, "slots"))
{
indent = findOpeningBrace(line) + indentWidth;
}
}
else if (first == '*')
{
if (last == '/')
{
int lineEnd = textLine->lastChar();
if (lineEnd > 0 && textLine->getChar(lineEnd - 1) == '*')
{
indent = findOpeningComment(line);
if (textLine->attribute(firstChar) == doxyCommentAttrib)
indent++;
}
else
return;
}
else
{
KateDocCursor temp = line;
if (textLine->attribute(firstChar) == doxyCommentAttrib)
indent = calcIndent(temp, false) + 1;
else
indent = calcIndent(temp, true);
}
}
else if (first == '#')
{
// c# regions
if (textLine->stringAtPos (firstChar, "#region") ||
textLine->stringAtPos (firstChar, "#endregion"))
{
KateDocCursor temp = line;
indent = calcIndent(temp, true);
}
}
else
{
// Everything else ...
if (first == '/' && last != '/')
return;
KateDocCursor temp = line;
indent = calcIndent(temp, true);
if (indent == 0)
{
KateNormalIndent::processNewline(line, true);
return;
}
}
// Slightly faster if we don't indent what we don't have to
if (indent != measureIndent(line) || first == '}' || first == '{' || first == '#')
{
doc->removeText(line.line(), 0, line.line(), firstChar);
QString filler = tabString(indent);
if (indent > 0) doc->insertText(line.line(), 0, filler);
if (!processingBlock) line.setCol(filler.length());
}
}
void KateCSmartIndent::processSection (const KateDocCursor &begin, const KateDocCursor &end)
{
kdDebug(13030)<<"PROCESS SECTION"<<endl;
KateDocCursor cur = begin;
QTime t;
t.start();
processingBlock = (end.line() - cur.line() > 0) ? true : false;
while (cur.line() <= end.line())
{
processLine (cur);
if (!cur.gotoNextLine())
break;
}
processingBlock = false;
kdDebug(13030) << "+++ total: " << t.elapsed() << endl;
}
bool KateCSmartIndent::handleDoxygen (KateDocCursor &begin)
{
// Factor out the rather involved Doxygen stuff here ...
int line = begin.line();
int first = -1;
while ((line > 0) && (first < 0))
first = doc->plainKateTextLine(--line)->firstChar();
if (first >= 0)
{
KateTextLine::Ptr textLine = doc->plainKateTextLine(line);
bool insideDoxygen = false;
bool justAfterDoxygen = false;
if (textLine->attribute(first) == doxyCommentAttrib || textLine->attribute(textLine->lastChar()) == doxyCommentAttrib)
{
const int last = textLine->lastChar();
if (last <= 0 || !(justAfterDoxygen = textLine->stringAtPos(last-1, "*/")))
insideDoxygen = true;
if (justAfterDoxygen)
justAfterDoxygen &= textLine->string().find("/**") < 0;
while (textLine->attribute(first) != doxyCommentAttrib && first <= textLine->lastChar())
first++;
if (textLine->stringAtPos(first, "//"))
return false;
}
// Align the *'s and then go ahead and insert one too ...
if (insideDoxygen)
{
textLine = doc->plainKateTextLine(begin.line());
first = textLine->firstChar();
int indent = findOpeningComment(begin);
QString filler = tabString (indent);
bool doxygenAutoInsert = doc->config()->configFlags() & KateDocumentConfig::cfDoxygenAutoTyping;
if ( doxygenAutoInsert &&
((first < 0) || (!textLine->stringAtPos(first, "*/") && !textLine->stringAtPos(first, "*"))))
{
filler = filler + " * ";
}
doc->removeText (begin.line(), 0, begin.line(), first);
doc->insertText (begin.line(), 0, filler);
begin.setCol(filler.length());
return true;
}
// Align position with beginning of doxygen comment. Otherwise the
// indentation is one too much.
else if (justAfterDoxygen)
{
textLine = doc->plainKateTextLine(begin.line());
first = textLine->firstChar();
int indent = findOpeningComment(begin);
QString filler = tabString (indent);
doc->removeText (begin.line(), 0, begin.line(), first);
doc->insertText (begin.line(), 0, filler);
begin.setCol(filler.length());
return true;
}
}
return false;
}
void KateCSmartIndent::processNewline (KateDocCursor &begin, bool needContinue)
{
if (!handleDoxygen (begin))
{
KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
bool inMiddle = textLine->firstChar() > -1;
int indent = calcIndent (begin, needContinue);
if (indent > 0 || inMiddle)
{
QString filler = tabString (indent);
doc->insertText (begin.line(), 0, filler);
begin.setCol(filler.length());
// Handles cases where user hits enter at the beginning or middle of text
if (inMiddle)
{
processLine(begin);
begin.setCol(textLine->firstChar());
}
}
else
{
KateNormalIndent::processNewline (begin, needContinue);
}
if (begin.col() < 0)
begin.setCol(0);
}
}
/**
* Returns true when the given attribute matches any "colon influence immune"
* attribute
* @param indenter indenter
* @param attr1 attribute of previous char
* @param attr2 attribute of char preceding previous char
* @param prev1 previous character (0 if none)
* @param prev2 character preceding previous character (0 if none)
*/
static inline bool isColonImmune(const KateNormalIndent &indenter,
uchar attr1, uchar attr2,
QChar prev1, QChar prev2)
{
return attr1 == indenter.preprocessorAttrib
// FIXME: no way to discriminate against multiline comment and single
// line comment. Therefore, using prev? is futile.
|| attr1 == indenter.commentAttrib /*&& prev2 != '*' && prev1 != '/'*/
|| attr1 == indenter.doxyCommentAttrib
|| attr1 == indenter.stringAttrib && (attr2 != indenter.stringAttrib
|| (prev1 != '"' || prev2 == '\\' && attr2 == indenter.charAttrib))
|| prev1 == '\'' && attr1 != indenter.charAttrib;
}
/**
* Returns true when the colon is allowed to reindent the current line
* @param indenter current indenter
* @param line current line
* @param curCol column of most recently input character
*/
static inline bool colonPermitsReindent(const KateNormalIndent &indenter,
const KateTextLine::Ptr &line,
int curCol
)
{
const QString txt = line->string(0,curCol);
// do we have any significant preceding colon?
for (int pos = 0; (pos = txt.find(':', pos)) >= 0; pos++) {
if (line->attribute(pos) == indenter.symbolAttrib)
// yes, it has already contributed to this line's indentation, don't
// indent again
return false;
}
// otherwise, check whether this colon is not within an influence
// immune attribute range
return !isColonImmune(indenter, line->attribute(curCol - 1),
line->attribute(curCol - 2),
txt[curCol - 1], txt[curCol - 2]);
}
void KateCSmartIndent::processChar(QChar c)
{
// You may be curious about 'n' among the triggers:
// It is used to discriminate C#'s #region/#endregion which are indented
// against normal preprocessing statements which aren't indented.
static const QString triggers("}{)/:#n");
static const QString firstTriggers("}{)/:#");
static const QString lastTriggers(":n");
if (triggers.find(c) < 0)
return;
KateView *view = doc->activeView();
int curCol = view->cursorColumnReal() - 1;
KateDocCursor begin(view->cursorLine(), 0, doc);
KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
const QChar curChar = textLine->getChar(curCol);
const int first = textLine->firstChar();
const QChar firstChar = textLine->getChar(first);
#if 0 // nice try
// Only indent on symbols or preprocessing directives -- never on
// anything else
kdDebug() << "curChar " << curChar << " curCol " << curCol << " textlen " << textLine->length() << " a " << textLine->attribute( curCol ) << " sym " << symbolAttrib << " pp " << preprocessorAttrib << endl;
if (!(((curChar == '#' || curChar == 'n')
&& textLine->attribute( curCol ) == preprocessorAttrib)
|| textLine->attribute( curCol ) == symbolAttrib)
)
return;
kdDebug() << "curChar " << curChar << endl;
#endif
if (c == 'n')
{
if (firstChar != '#' || textLine->string(curCol-5, 5) != QString::fromLatin1("regio"))
return;
}
if ( c == '/' )
{
// dominik: if line is "* /", change it to "*/"
if ( textLine->attribute( begin.col() ) == doxyCommentAttrib )
{
// if the first char exists and is a '*', and the next non-space-char
// is already the just typed '/', concatenate it to "*/".
if ( first != -1
&& firstChar == '*'
&& textLine->nextNonSpaceChar( first+1 ) == view->cursorColumnReal()-1 )
doc->removeText( view->cursorLine(), first+1, view->cursorLine(), view->cursorColumnReal()-1);
}
// ls: never have comments change the indentation.
return;
}
// ls: only reindent line if the user actually expects it
// I. e. take action on single braces on line or last colon, but inhibit
// any reindentation if any of those characters appear amidst some section
// of the line
const QChar lastChar = textLine->getChar(textLine->lastChar());
int pos;
if (((c == firstChar && firstTriggers.find(firstChar) >= 0)
|| (c == lastChar && lastTriggers.find(lastChar) >= 0))
&& (c != ':' || colonPermitsReindent(*this, textLine, curCol)))
processLine(begin);
}
uint KateCSmartIndent::calcIndent(KateDocCursor &begin, bool needContinue)
{
KateTextLine::Ptr textLine;
KateDocCursor cur = begin;
uint anchorIndent = 0;
int anchorPos = 0;
int parenCount = 0; // Possibly in a multiline for stmt. Used to skip ';' ...
bool found = false;
bool isSpecial = false;
bool potentialAnchorSeen = false;
bool isArg = false; // ...arg,<newline>
bool parenthesizedArg = false; // ...(arg,<newline>
//kdDebug(13030) << "calcIndent begin line:" << begin.line() << " col:" << begin.col() << endl;
// Find Indent Anchor Point
while (cur.gotoPreviousLine())
{
isSpecial = found = false;
textLine = doc->plainKateTextLine(cur.line());
// Skip comments and handle cases like if (...) { stmt;
int pos = textLine->lastChar();
int openCount = 0;
int otherAnchor = -1;
do
{
if (textLine->attribute(pos) == symbolAttrib)
{
QChar tc = textLine->getChar (pos);
if ((tc == ';' || tc == ':' || tc == ',') && otherAnchor == -1 && parenCount <= 0) {
otherAnchor = pos, potentialAnchorSeen = true;
isArg = tc == ',';
} else if (tc == ')')
parenCount++;
else if (tc == '(')
parenCount--, parenthesizedArg = isArg, potentialAnchorSeen = true;
else if (tc == '}')
openCount--;
else if (tc == '{')
{
openCount++, potentialAnchorSeen = true;
if (openCount == 1)
break;
}
}
} while (--pos >= textLine->firstChar());
if (openCount != 0 || otherAnchor != -1)
{
found = true;
QChar c;
if (openCount > 0)
c = '{';
else if (openCount < 0)
c = '}';
else if (otherAnchor >= 0)
c = textLine->getChar (otherAnchor);
int specialIndent = 0;
if (c == ':' && needContinue)
{
QChar ch;
specialIndent = textLine->firstChar();
if (textLine->stringAtPos(specialIndent, "case"))
ch = textLine->getChar(specialIndent + 4);
else if (textLine->stringAtPos(specialIndent, "default"))
ch = textLine->getChar(specialIndent + 7);
else if (textLine->stringAtPos(specialIndent, "public"))
ch = textLine->getChar(specialIndent + 6);
else if (textLine->stringAtPos(specialIndent, "private"))
ch = textLine->getChar(specialIndent + 7);
else if (textLine->stringAtPos(specialIndent, "protected"))
ch = textLine->getChar(specialIndent + 9);
else if (textLine->stringAtPos(specialIndent, "signals"))
ch = textLine->getChar(specialIndent + 7);
else if (textLine->stringAtPos(specialIndent, "Q_SIGNALS"))
ch = textLine->getChar(specialIndent + 9);
else if (textLine->stringAtPos(specialIndent, "slots"))
ch = textLine->getChar(specialIndent + 5);
else if (textLine->stringAtPos(specialIndent, "Q_SLOTS"))
ch = textLine->getChar(specialIndent + 7);
if (ch.isNull() || (!ch.isSpace() && ch != '(' && ch != ':'))
continue;
KateDocCursor lineBegin = cur;
lineBegin.setCol(specialIndent);
specialIndent = measureIndent(lineBegin);
isSpecial = true;
}
// Move forward past blank lines
KateDocCursor skip = cur;
skip.setCol(textLine->lastChar());
bool result = skipBlanks(skip, begin, true);
anchorPos = skip.col();
anchorIndent = measureIndent(skip);
//kdDebug(13030) << "calcIndent anchorPos:" << anchorPos << " anchorIndent:" << anchorIndent << " at line:" << skip.line() << endl;
// Accept if it's before requested position or if it was special
if (result && skip < begin)
{
cur = skip;
break;
}
else if (isSpecial)
{
anchorIndent = specialIndent;
break;
}
// Are these on a line by themselves? (i.e. both last and first char)
if ((c == '{' || c == '}') && textLine->getChar(textLine->firstChar()) == c)
{
cur.setCol(anchorPos = textLine->firstChar());
anchorIndent = measureIndent (cur);
break;
}
}
}
// treat beginning of document as anchor position
if (cur.line() == 0 && cur.col() == 0 && potentialAnchorSeen)
found = true;
if (!found)
return 0;
uint continueIndent = (needContinue) ? calcContinue (cur, begin) : 0;
//kdDebug(13030) << "calcIndent continueIndent:" << continueIndent << endl;
// Move forward from anchor and determine last known reference character
// Braces take precedance over others ...
textLine = doc->plainKateTextLine(cur.line());
QChar lastChar = textLine->getChar (anchorPos);
int lastLine = cur.line();
if (lastChar == '#' || lastChar == '[')
{
// Never continue if # or [ is encountered at this point here
// A fail-safe really... most likely an #include, #region, or a c# attribute
continueIndent = 0;
}
int openCount = 0;
while (cur.validPosition() && cur < begin)
{
if (!skipBlanks(cur, begin, true))
return isArg && !parenthesizedArg ? begin.col() : 0;
QChar tc = cur.currentChar();
//kdDebug(13030) << " cur.line:" << cur.line() << " cur.col:" << cur.col() << " currentChar '" << tc << "' " << textLine->attribute(cur.col()) << endl;
if (cur == begin || tc.isNull())
break;
if (!tc.isSpace() && cur < begin)
{
uchar attrib = cur.currentAttrib();
if (tc == '{' && attrib == symbolAttrib)
openCount++;
else if (tc == '}' && attrib == symbolAttrib)
openCount--;
lastChar = tc;
lastLine = cur.line();
}
}
if (openCount > 0) // Open braces override
lastChar = '{';
uint indent = 0;
//kdDebug(13030) << "calcIndent lastChar '" << lastChar << "'" << endl;
if (lastChar == '{' || (lastChar == ':' && isSpecial && needContinue))
{
indent = anchorIndent + indentWidth;
}
else if (lastChar == '}')
{
indent = anchorIndent;
}
else if (lastChar == ';')
{
indent = anchorIndent + ((allowSemi && needContinue) ? continueIndent : 0);
}
else if (lastChar == ',' || lastChar == '(')
{
textLine = doc->plainKateTextLine(lastLine);
KateDocCursor start(lastLine, textLine->firstChar(), doc);
KateDocCursor finish(lastLine, textLine->lastChar() + 1, doc);
uint pos = 0;
if (isBalanced(start, finish, QChar('('), QChar(')'), pos) && false)
indent = anchorIndent;
else
{
// TODO: Config option. If we're below 48, go ahead and line them up
indent = ((pos < 48) ? pos : anchorIndent + (indentWidth * 2));
}
}
else if (!lastChar.isNull())
{
if (anchorIndent != 0)
indent = anchorIndent + continueIndent;
else
indent = continueIndent;
}
return indent;
}
uint KateCSmartIndent::calcContinue(KateDocCursor &start, KateDocCursor &end)
{
KateDocCursor cur = start;
bool needsBalanced = true;
bool isFor = false;
allowSemi = false;
KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
// Handle cases such as } while (s ... by skipping the leading symbol
if (textLine->attribute(cur.col()) == symbolAttrib)
{
cur.moveForward(1);
skipBlanks(cur, end, false);
}
if (textLine->getChar(cur.col()) == '}')
{
skipBlanks(cur, end, true);
if (cur.line() != start.line())
textLine = doc->plainKateTextLine(cur.line());
if (textLine->stringAtPos(cur.col(), "else"))
cur.setCol(cur.col() + 4);
else
return indentWidth * 2;
needsBalanced = false;
}
else if (textLine->stringAtPos(cur.col(), "else"))
{
cur.setCol(cur.col() + 4);
needsBalanced = false;
int next = textLine->nextNonSpaceChar(cur.col());
if (next >= 0 && textLine->stringAtPos(next, "if"))
{
cur.setCol(next + 2);
needsBalanced = true;
}
}
else if (textLine->stringAtPos(cur.col(), "if"))
{
cur.setCol(cur.col() + 2);
}
else if (textLine->stringAtPos(cur.col(), "do"))
{
cur.setCol(cur.col() + 2);
needsBalanced = false;
}
else if (textLine->stringAtPos(cur.col(), "for"))
{
cur.setCol(cur.col() + 3);
isFor = true;
}
else if (textLine->stringAtPos(cur.col(), "while"))
{
cur.setCol(cur.col() + 5);
}
else if (textLine->stringAtPos(cur.col(), "switch"))
{
cur.setCol(cur.col() + 6);
}
else if (textLine->stringAtPos(cur.col(), "using"))
{
cur.setCol(cur.col() + 5);
}
else
{
return indentWidth * 2;
}
uint openPos = 0;
if (needsBalanced && !isBalanced (cur, end, QChar('('), QChar(')'), openPos))
{
allowSemi = isFor;
if (openPos > 0)
return (openPos - textLine->firstChar());
else
return indentWidth * 2;
}
// Check if this statement ends a line now
skipBlanks(cur, end, false);
if (cur == end)
return indentWidth;
if (skipBlanks(cur, end, true))
{
if (cur == end)
return indentWidth;
else
return indentWidth + calcContinue(cur, end);
}
return 0;
}
uint KateCSmartIndent::findOpeningBrace(KateDocCursor &start)
{
KateDocCursor cur = start;
int count = 1;
// Move backwards 1 by 1 and find the opening brace
// Return the indent of that line
while (cur.moveBackward(1))
{
if (cur.currentAttrib() == symbolAttrib)
{
QChar ch = cur.currentChar();
if (ch == '{')
count--;
else if (ch == '}')
count++;
if (count == 0)
{
KateDocCursor temp(cur.line(), doc->plainKateTextLine(cur.line())->firstChar(), doc);
return measureIndent(temp);
}
}
}
return 0;
}
bool KateCSmartIndent::firstOpeningBrace(KateDocCursor &start)
{
KateDocCursor cur = start;
// Are we the first opening brace at this level?
while(cur.moveBackward(1))
{
if (cur.currentAttrib() == symbolAttrib)
{
QChar ch = cur.currentChar();
if (ch == '{')
return false;
else if (ch == '}' && cur.col() == 0)
break;
}
}
return true;
}
uint KateCSmartIndent::findOpeningParen(KateDocCursor &start)
{
KateDocCursor cur = start;
int count = 1;
// Move backwards 1 by 1 and find the opening (
// Return the indent of that line
while (cur.moveBackward(1))
{
if (cur.currentAttrib() == symbolAttrib)
{
QChar ch = cur.currentChar();
if (ch == '(')
count--;
else if (ch == ')')
count++;
if (count == 0)
return measureIndent(cur);
}
}
return 0;
}
uint KateCSmartIndent::findOpeningComment(KateDocCursor &start)
{
KateDocCursor cur = start;
// Find the line with the opening /* and return the proper indent
do
{
KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
int pos = textLine->string().find("/*", false);
if (pos >= 0)
{
KateDocCursor temp(cur.line(), pos, doc);
return measureIndent(temp);
}
} while (cur.gotoPreviousLine());
return 0;
}
//END
//BEGIN KatePythonIndent
QRegExp KatePythonIndent::endWithColon = QRegExp( "^[^#]*:\\s*(#.*)?$" );
QRegExp KatePythonIndent::stopStmt = QRegExp( "^\\s*(break|continue|raise|return|pass)\\b.*" );
QRegExp KatePythonIndent::blockBegin = QRegExp( "^\\s*(class|def|if|elif|else|for|while|try)\\b.*" );
KatePythonIndent::KatePythonIndent (KateDocument *doc)
: KateNormalIndent (doc)
{
}
KatePythonIndent::~KatePythonIndent ()
{
}
void KatePythonIndent::processNewline (KateDocCursor &begin, bool /*newline*/)
{
int prevLine = begin.line() - 1;
int prevPos = begin.col();
while ((prevLine > 0) && (prevPos < 0)) // search a not empty text line
prevPos = doc->plainKateTextLine(--prevLine)->firstChar();
int prevBlock = prevLine;
int prevBlockPos = prevPos;
int extraIndent = calcExtra (prevBlock, prevBlockPos, begin);
int indent = doc->plainKateTextLine(prevBlock)->cursorX(prevBlockPos, tabWidth);
if (extraIndent == 0)
{
if (!stopStmt.exactMatch(doc->plainKateTextLine(prevLine)->string()))
{
if (endWithColon.exactMatch(doc->plainKateTextLine(prevLine)->string()))
indent += indentWidth;
else
indent = doc->plainKateTextLine(prevLine)->cursorX(prevPos, tabWidth);
}
}
else
indent += extraIndent;
if (indent > 0)
{
QString filler = tabString (indent);
doc->insertText (begin.line(), 0, filler);
begin.setCol(filler.length());
}
else
begin.setCol(0);
}
int KatePythonIndent::calcExtra (int &prevBlock, int &pos, KateDocCursor &end)
{
int nestLevel = 0;
bool levelFound = false;
while ((prevBlock > 0))
{
if (blockBegin.exactMatch(doc->plainKateTextLine(prevBlock)->string()))
{
if ((!levelFound && nestLevel == 0) || (levelFound && nestLevel - 1 <= 0))
{
pos = doc->plainKateTextLine(prevBlock)->firstChar();
break;
}
nestLevel --;
}
else if (stopStmt.exactMatch(doc->plainKateTextLine(prevBlock)->string()))
{
nestLevel ++;
levelFound = true;
}
--prevBlock;
}
KateDocCursor cur (prevBlock, pos, doc);
QChar c;
int extraIndent = 0;
while (cur.line() < end.line())
{
c = cur.currentChar();
if (c == '(')
extraIndent += indentWidth;
else if (c == ')')
extraIndent -= indentWidth;
else if (c == ':')
break;
else if (c == '\'' || c == '"' )
traverseString( c, cur, end );
if (c.isNull() || c == '#')
cur.gotoNextLine();
else
cur.moveForward(1);
}
return extraIndent;
}
void KatePythonIndent::traverseString( const QChar &stringChar, KateDocCursor &cur, KateDocCursor &end )
{
QChar c;
bool escape = false;
cur.moveForward(1);
c = cur.currentChar();
while ( ( c != stringChar || escape ) && cur.line() < end.line() )
{
if ( escape )
escape = false;
else if ( c == '\\' )
escape = !escape;
cur.moveForward(1);
c = cur.currentChar();
}
}
//END
//BEGIN KateXmlIndent
/* Explanation
The XML indenter simply inherits the indentation of the previous line,
with the first line starting at 0 (of course!). For each element that
is opened on the previous line, the indentation is increased by one
level; for each element that is closed, it is decreased by one.
We also have a special case of opening an element on one line and then
entering attributes on the following lines, in which case we would like
to see the following layout:
<elem attr="..."
blah="..." />
<x><a href="..."
title="..." />
</x>
This is accomplished by checking for lines that contain an unclosed open
tag.
*/
const QRegExp KateXmlIndent::startsWithCloseTag("^[ \t]*</");
const QRegExp KateXmlIndent::unclosedDoctype("<!DOCTYPE[^>]*$");
KateXmlIndent::KateXmlIndent (KateDocument *doc)
: KateNormalIndent (doc)
{
}
KateXmlIndent::~KateXmlIndent ()
{
}
void KateXmlIndent::processNewline (KateDocCursor &begin, bool /*newline*/)
{
begin.setCol(processLine(begin.line()));
}
void KateXmlIndent::processChar (QChar c)
{
if(c != '/') return;
// only alter lines that start with a close element
KateView *view = doc->activeView();
QString text = doc->plainKateTextLine(view->cursorLine())->string();
if(text.find(startsWithCloseTag) == -1) return;
// process it
processLine(view->cursorLine());
}
void KateXmlIndent::processLine (KateDocCursor &line)
{
processLine (line.line());
}
void KateXmlIndent::processSection (const KateDocCursor &start, const KateDocCursor &end)
{
KateDocCursor cur (start);
int endLine = end.line();
do {
processLine(cur.line());
if(!cur.gotoNextLine()) break;
} while(cur.line() < endLine);
}
void KateXmlIndent::getLineInfo (uint line, uint &prevIndent, int &numTags,
uint &attrCol, bool &unclosedTag)
{
prevIndent = 0;
int firstChar;
KateTextLine::Ptr prevLine = 0;
// get the indentation of the first non-empty line
while(true) {
prevLine = doc->plainKateTextLine(line);
if( (firstChar = prevLine->firstChar()) < 0) {
if(!line--) return;
continue;
}
break;
}
prevIndent = prevLine->cursorX(prevLine->firstChar(), tabWidth);
QString text = prevLine->string();
// special case:
// <a>
// </a> <!-- indentation *already* decreased -->
// requires that we discount the </a> from the number of closed tags
if(text.find(startsWithCloseTag) != -1) ++numTags;
// count the number of open and close tags
int lastCh = 0;
uint pos, len = text.length();
bool seenOpen = false;
for(pos = 0; pos < len; ++pos) {
int ch = text.at(pos).unicode();
switch(ch) {
case '<':
seenOpen = true;
unclosedTag = true;
attrCol = pos;
++numTags;
break;
// don't indent because of DOCTYPE, comment, CDATA, etc.
case '!':
if(lastCh == '<') --numTags;
break;
// don't indent because of xml decl or PI
case '?':
if(lastCh == '<') --numTags;
break;
case '>':
if(!seenOpen) {
// we are on a line like the second one here:
// <element attr="val"
// other="val">
// so we need to set prevIndent to the indent of the first line
//
// however, we need to special case "<!DOCTYPE" because
// it's not an open tag
prevIndent = 0;
for(uint backLine = line; backLine; ) {
// find first line with an open tag
KateTextLine::Ptr x = doc->plainKateTextLine(--backLine);
if(x->string().find('<') == -1) continue;
// recalculate the indent
if(x->string().find(unclosedDoctype) != -1) --numTags;
getLineInfo(backLine, prevIndent, numTags, attrCol, unclosedTag);
break;
}
}
if(lastCh == '/') --numTags;
unclosedTag = false;
break;
case '/':
if(lastCh == '<') numTags -= 2; // correct for '<', above
break;
}
lastCh = ch;
}
if(unclosedTag) {
// find the start of the next attribute, so we can align with it
do {
lastCh = text.at(++attrCol).unicode();
}while(lastCh && lastCh != ' ' && lastCh != '\t');
while(lastCh == ' ' || lastCh == '\t') {
lastCh = text.at(++attrCol).unicode();
}
attrCol = prevLine->cursorX(attrCol, tabWidth);
}
}
uint KateXmlIndent::processLine (uint line)
{
KateTextLine::Ptr kateLine = doc->plainKateTextLine(line);
if(!kateLine) return 0; // sanity check
// get details from previous line
uint prevIndent = 0, attrCol = 0;
int numTags = 0;
bool unclosedTag = false; // for aligning attributes
if(line) {
getLineInfo(line - 1, prevIndent, numTags, attrCol, unclosedTag);
}
// compute new indent
int indent = 0;
if(unclosedTag) indent = attrCol;
else indent = prevIndent + numTags * indentWidth;
if(indent < 0) indent = 0;
// unindent lines that start with a close tag
if(kateLine->string().find(startsWithCloseTag) != -1) {
indent -= indentWidth;
}
if(indent < 0) indent = 0;
// apply new indent
doc->removeText(line, 0, line, kateLine->firstChar());
QString filler = tabString(indent);
doc->insertText(line, 0, filler);
return filler.length();
}
//END
//BEGIN KateCSAndSIndent
KateCSAndSIndent::KateCSAndSIndent (KateDocument *doc)
: KateNormalIndent (doc)
{
}
void KateCSAndSIndent::updateIndentString()
{
if( useSpaces )
indentString.fill( ' ', indentWidth );
else
indentString = '\t';
}
KateCSAndSIndent::~KateCSAndSIndent ()
{
}
void KateCSAndSIndent::processLine (KateDocCursor &line)
{
KateTextLine::Ptr textLine = doc->plainKateTextLine(line.line());
if (!textLine)
return;
updateIndentString();
const int oldCol = line.col();
QString whitespace = calcIndent(line);
// strip off existing whitespace
int oldIndent = textLine->firstChar();
if ( oldIndent < 0 )
oldIndent = doc->lineLength( line.line() );
if( oldIndent > 0 )
doc->removeText(line.line(), 0, line.line(), oldIndent);
// add correct amount
doc->insertText(line.line(), 0, whitespace);
// try to preserve the cursor position in the line
if ( int(oldCol + whitespace.length()) >= oldIndent )
line.setCol( oldCol + whitespace.length() - oldIndent );
else
line.setCol( 0 );
}
void KateCSAndSIndent::processSection (const KateDocCursor &begin, const KateDocCursor &end)
{
QTime t; t.start();
for( KateDocCursor cur = begin; cur.line() <= end.line(); )
{
processLine (cur);
if (!cur.gotoNextLine())
break;
}
kdDebug(13030) << "+++ total: " << t.elapsed() << endl;
}
/**
* Returns the first @p chars characters of @p line, converted to whitespace.
* If @p convert is set to false, characters at and after the first non-whitespace
* character are removed, not converted.
*/
static QString initialWhitespace(const KateTextLine::Ptr &line, int chars, bool convert = true)
{
QString text = line->string(0, chars);
if( (int)text.length() < chars )
{
QString filler; filler.fill(' ',chars - text.length());
text += filler;
}
for( uint n = 0; n < text.length(); ++n )
{
if( text[n] != '\t' && text[n] != ' ' )
{
if( !convert )
return text.left( n );
text[n] = ' ';
}
}
return text;
}
QString KateCSAndSIndent::findOpeningCommentIndentation(const KateDocCursor &start)
{
KateDocCursor cur = start;
// Find the line with the opening /* and return the indentation of it
do
{
KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
int pos = textLine->string().findRev("/*");
// FIXME: /* inside /* is possible. This screws up in that case...
if (pos >= 0)
return initialWhitespace(textLine, pos);
} while (cur.gotoPreviousLine());
// should never happen.
kdWarning( 13030 ) << " in a comment, but can't find the start of it" << endl;
return QString::null;
}
bool KateCSAndSIndent::handleDoxygen (KateDocCursor &begin)
{
// Look backwards for a nonempty line
int line = begin.line();
int first = -1;
while ((line > 0) && (first < 0))
first = doc->plainKateTextLine(--line)->firstChar();
// no earlier nonempty line
if (first < 0)
return false;
KateTextLine::Ptr textLine = doc->plainKateTextLine(line);
// if the line doesn't end with a doxygen comment (that's not closed)
// and doesn't start with a doxygen comment (that's not closed), we don't care.
// note that we do need to check the start of the line, or lines ending with, say, @brief aren't
// recognised.
if ( !(textLine->attribute(textLine->lastChar()) == doxyCommentAttrib && !textLine->endingWith("*/")) &&
!(textLine->attribute(textLine->firstChar()) == doxyCommentAttrib && !textLine->string().contains("*/")) )
return false;
// our line is inside a doxygen comment. align the *'s and then maybe insert one too ...
textLine = doc->plainKateTextLine(begin.line());
first = textLine->firstChar();
QString indent = findOpeningCommentIndentation(begin);
bool doxygenAutoInsert = doc->config()->configFlags() & KateDocumentConfig::cfDoxygenAutoTyping;
// starts with *: indent one space more to line up *s
if ( first >= 0 && textLine->stringAtPos(first, "*") )
indent = indent + " ";
// does not start with *: insert one if user wants that
else if ( doxygenAutoInsert )
indent = indent + " * ";
// user doesn't want * inserted automatically: put in spaces?
//else
// indent = indent + " ";
doc->removeText (begin.line(), 0, begin.line(), first);
doc->insertText (begin.line(), 0, indent);
begin.setCol(indent.length());
return true;
}
/**
* @brief User pressed enter. Line has been split; begin is on the new line.
* @param begin Three unrelated variables: the new line number, where the first
* non-whitespace char was on the previous line, and the document.
* @param needContinue Something to do with indenting the current line; always true.
*/
void KateCSAndSIndent::processNewline (KateDocCursor &begin, bool /*needContinue*/)
{
// in a comment, add a * doxygen-style.
if( handleDoxygen(begin) )
return;
// TODO: if the user presses enter in the middle of a label, maybe the first half of the
// label should be indented?
// where the cursor actually is...
int cursorPos = doc->plainKateTextLine( begin.line() )->firstChar();
if ( cursorPos < 0 )
cursorPos = doc->lineLength( begin.line() );
begin.setCol( cursorPos );
processLine( begin );
}
/**
* Does the line @p line start with a label?
* @note May also return @c true if the line starts in a continuation.
*/
bool KateCSAndSIndent::startsWithLabel( int line )
{
// Get the current line.
KateTextLine::Ptr indentLine = doc->plainKateTextLine(line);
const int indentFirst = indentLine->firstChar();
// Not entirely sure what this check does.
int attrib = indentLine->attribute(indentFirst);
if (attrib != 0 && attrib != keywordAttrib && attrib != normalAttrib && attrib != extensionAttrib)
return false;
// Get the line text.
const QString lineContents = indentLine->string();
const int indentLast = indentLine->lastChar();
bool whitespaceFound = false;
for ( int n = indentFirst; n <= indentLast; ++n )
{
// Get the character as latin1. Can't use QChar::isLetterOrNumber()
// as that includes non 0-9 numbers.
char c = lineContents[n].latin1();
if ( c == ':' )
{
// See if the next character is ':' - if so, skip to the character after it.
if ( n < lineContents.length() - 1 )
{
if ( lineContents[n+1].latin1() == ':' )
{
n += 2;
continue;
}
}
// Right this is the relevent ':'.
if ( n == indentFirst)
{
// Just a line with a : on it.
return false;
}
// It is a label of some kind!
return true;
}
if (isspace(c))
{
if (!whitespaceFound)
{
if (lineContents.mid(indentFirst, n - indentFirst) == "case")
return true;
else if (lineContents.mid(indentFirst, n - indentFirst) == "class")
return false;
whitespaceFound = true;
}
}
// All other characters don't indent.
else if ( !isalnum(c) && c != '_' )
{
return false;
}
}
return false;
}
template<class T> T min(T a, T b) { return (a < b) ? a : b; }
int KateCSAndSIndent::lastNonCommentChar( const KateDocCursor &line )
{
KateTextLine::Ptr textLine = doc->plainKateTextLine( line.line() );
QString str = textLine->string();
// find a possible start-of-comment
int p = -2; // so the first find starts at position 0
do p = str.find( "//", p + 2 );
while ( p >= 0 && textLine->attribute(p) != commentAttrib && textLine->attribute(p) != doxyCommentAttrib );
// no // found? use whole string
if ( p < 0 )
p = str.length();
// ignore trailing blanks. p starts one-past-the-end.
while( p > 0 && str[p-1].isSpace() ) --p;
return p - 1;
}
bool KateCSAndSIndent::inForStatement( int line )
{
// does this line end in a for ( ...
// with no closing ) ?
int parens = 0, semicolons = 0;
for ( ; line >= 0; --line )
{
KateTextLine::Ptr textLine = doc->plainKateTextLine(line);
const int first = textLine->firstChar();
const int last = textLine->lastChar();
// look backwards for a symbol: (){};
// match ()s, {...; and }...; => not in a for
// ; ; ; => not in a for
// ( ; and ( ; ; => a for
for ( int curr = last; curr >= first; --curr )
{
if ( textLine->attribute(curr) != symbolAttrib )
continue;
switch( textLine->getChar(curr) )
{
case ';':
if( ++semicolons > 2 )
return false;
break;
case '{': case '}':
return false;
case ')':
++parens;
break;
case '(':
if( --parens < 0 )
return true;
break;
}
}
}
// no useful symbols before the ;?
// not in a for then
return false;
}
// is the start of the line containing 'begin' in a statement?
bool KateCSAndSIndent::inStatement( const KateDocCursor &begin )
{
// if the current line starts with an open brace, it's not a continuation.
// this happens after a function definition (which is treated as a continuation).
KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
const int first = textLine->firstChar();
// note that if we're being called from processChar the attribute has not yet been calculated
// should be reasonably safe to assume that unattributed {s are symbols; if the { is in a comment
// we don't want to touch it anyway.
const int attrib = textLine->attribute(first);
if( first >= 0 && (attrib == 0 || attrib == symbolAttrib) && textLine->getChar(first) == '{' )
return false;
int line;
for ( line = begin.line() - 1; line >= 0; --line )
{
textLine = doc->plainKateTextLine(line);
const int first = textLine->firstChar();
if ( first == -1 )
continue;
// starts with #: in a comment, don't care
// outside a comment: preprocessor, don't care
if ( textLine->getChar( first ) == '#' )
continue;
KateDocCursor currLine = begin;
currLine.setLine( line );
const int last = lastNonCommentChar( currLine );
if ( last < first )
continue;
// HACK: if we see a comment, assume boldly that this isn't a continuation.
// detecting comments (using attributes) is HARD, since they may have
// embedded alerts, or doxygen stuff, or just about anything. this is
// wrong, and needs fixing. note that only multi-line comments and
// single-line comments continued with \ are affected.
const int attrib = textLine->attribute(last);
if ( attrib == commentAttrib || attrib == doxyCommentAttrib )
return false;
char c = textLine->getChar(last);
// brace => not a continuation.
if ( attrib == symbolAttrib && c == '{' || c == '}' )
return false;
// ; => not a continuation, unless in a for (;;)
if ( attrib == symbolAttrib && c == ';' )
return inForStatement( line );
// found something interesting. maybe it's a label?
if ( attrib == symbolAttrib && c == ':' )
{
// the : above isn't necessarily the : in the label, eg in
// case 'x': a = b ? c :
// this will say no continuation incorrectly. but continued statements
// starting on a line with a label at the start is Bad Style (tm).
if( startsWithLabel( line ) )
{
// either starts with a label or a continuation. if the current line
// starts in a continuation, we're still in one. if not, this was
// a label, so we're not in one now. so continue to the next line
// upwards.
continue;
}
}
// any other character => in a continuation
return true;
}
// no non-comment text found before here - not a continuation.
return false;
}
QString KateCSAndSIndent::continuationIndent( const KateDocCursor &begin )
{
if( !inStatement( begin ) )
return QString::null;
return indentString;
}
/**
* Figure out how indented the line containing @p begin should be.
*/
QString KateCSAndSIndent::calcIndent (const KateDocCursor &begin)
{
KateTextLine::Ptr currLine = doc->plainKateTextLine(begin.line());
int currLineFirst = currLine->firstChar();
// if the line starts inside a comment, no change of indentation.
// FIXME: this unnecessarily copies the current indentation over itself.
// FIXME: on newline, this should copy from the previous line.
if ( currLineFirst >= 0 &&
(currLine->attribute(currLineFirst) == commentAttrib ||
currLine->attribute(currLineFirst) == doxyCommentAttrib) )
return currLine->string( 0, currLineFirst );
// if the line starts with # (but isn't a c# region thingy), no indentation at all.
if( currLineFirst >= 0 && currLine->getChar(currLineFirst) == '#' )
{
if( !currLine->stringAtPos( currLineFirst+1, QString::fromLatin1("region") ) &&
!currLine->stringAtPos( currLineFirst+1, QString::fromLatin1("endregion") ) )
return QString::null;
}
/* Strategy:
* Look for an open bracket or brace, or a keyword opening a new scope, whichever comes latest.
* Found a brace: indent one tab in.
* Found a bracket: indent to the first non-white after it.
* Found a keyword: indent one tab in. for try, catch and switch, if newline is set, also add
* an open brace, a newline, and indent two tabs in.
*/
KateDocCursor cur = begin;
int pos, openBraceCount = 0, openParenCount = 0;
bool lookingForScopeKeywords = true;
const char * const scopeKeywords[] = { "for", "do", "while", "if", "else" };
const char * const blockScopeKeywords[] = { "try", "catch", "switch" };
while (cur.gotoPreviousLine())
{
KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
const int lastChar = textLine->lastChar();
const int firstChar = textLine->firstChar();
// look through line backwards for interesting characters
for( pos = lastChar; pos >= firstChar; --pos )
{
if (textLine->attribute(pos) == symbolAttrib)
{
char tc = textLine->getChar (pos);
switch( tc )
{
case '(': case '[':
if( ++openParenCount > 0 )
return calcIndentInBracket( begin, cur, pos );
break;
case ')': case ']': openParenCount--; break;
case '{':
if( ++openBraceCount > 0 )
return calcIndentInBrace( begin, cur, pos );
break;
case '}': openBraceCount--; lookingForScopeKeywords = false; break;
case ';':
if( openParenCount == 0 )
lookingForScopeKeywords = false;
break;
}
}
// if we've not had a close brace or a semicolon yet, and we're at the same parenthesis level
// as the cursor, and we're at the start of a scope keyword, indent from it.
if ( lookingForScopeKeywords && openParenCount == 0 &&
textLine->attribute(pos) == keywordAttrib &&
(pos == 0 || textLine->attribute(pos-1) != keywordAttrib ) )
{
#define ARRLEN( array ) ( sizeof(array)/sizeof(array[0]) )
for( uint n = 0; n < ARRLEN(scopeKeywords); ++n )
if( textLine->stringAtPos(pos, QString::fromLatin1(scopeKeywords[n]) ) )
return calcIndentAfterKeyword( begin, cur, pos, false );
for( uint n = 0; n < ARRLEN(blockScopeKeywords); ++n )
if( textLine->stringAtPos(pos, QString::fromLatin1(blockScopeKeywords[n]) ) )
return calcIndentAfterKeyword( begin, cur, pos, true );
#undef ARRLEN
}
}
}
// no active { in file.
return QString::null;
}
QString KateCSAndSIndent::calcIndentInBracket(const KateDocCursor &indentCursor, const KateDocCursor &bracketCursor, int bracketPos)
{
KateTextLine::Ptr indentLine = doc->plainKateTextLine(indentCursor.line());
KateTextLine::Ptr bracketLine = doc->plainKateTextLine(bracketCursor.line());
// FIXME: hard-coded max indent to bracket width - use a kate variable
// FIXME: expand tabs first...
if ( bracketPos > 48 )
{
// how far to indent? we could look back for a brace or keyword, 2 from that.
// as it is, we just indent one more than the line with the ( on it.
// the potential problem with this is when
// you have code ( which does <-- continuation + start of func call
// something like this ); <-- extra indentation for func call
// then again (
// it works better than (
// the other method for (
// cases like this )));
// consequently, i think this method wins.
return indentString + initialWhitespace( bracketLine, bracketLine->firstChar() );
}
const int indentLineFirst = indentLine->firstChar();
int indentTo;
const int attrib = indentLine->attribute(indentLineFirst);
if( indentLineFirst >= 0 && (attrib == 0 || attrib == symbolAttrib) &&
( indentLine->getChar(indentLineFirst) == ')' || indentLine->getChar(indentLineFirst) == ']' ) )
{
// If the line starts with a close bracket, line it up
indentTo = bracketPos;
}
else
{
// Otherwise, line up with the text after the open bracket
indentTo = bracketLine->nextNonSpaceChar( bracketPos + 1 );
if( indentTo == -1 )
indentTo = bracketPos + 2;
}
return initialWhitespace( bracketLine, indentTo );
}
QString KateCSAndSIndent::calcIndentAfterKeyword(const KateDocCursor &indentCursor, const KateDocCursor &keywordCursor, int keywordPos, bool blockKeyword)
{
KateTextLine::Ptr keywordLine = doc->plainKateTextLine(keywordCursor.line());
KateTextLine::Ptr indentLine = doc->plainKateTextLine(indentCursor.line());
QString whitespaceToKeyword = initialWhitespace( keywordLine, keywordPos, false );
if( blockKeyword ) {
// FIXME: we could add the open brace and subsequent newline here since they're definitely needed.
}
// If the line starts with an open brace, don't indent...
int first = indentLine->firstChar();
// if we're being called from processChar attribute won't be set
const int attrib = indentLine->attribute(first);
if( first >= 0 && (attrib == 0 || attrib == symbolAttrib) && indentLine->getChar(first) == '{' )
return whitespaceToKeyword;
// don't check for a continuation. rules are simple here:
// if we're in a non-compound statement after a scope keyword, we indent all lines
// once. so:
// if ( some stuff
// goes here )
// apples, and <-- continuation here is ignored. but this is Bad Style (tm) anyway.
// oranges too;
return indentString + whitespaceToKeyword;
}
QString KateCSAndSIndent::calcIndentInBrace(const KateDocCursor &indentCursor, const KateDocCursor &braceCursor, int bracePos)
{
KateTextLine::Ptr braceLine = doc->plainKateTextLine(braceCursor.line());
const int braceFirst = braceLine->firstChar();
QString whitespaceToOpenBrace = initialWhitespace( braceLine, bracePos, false );
// if the open brace is the start of a namespace, don't indent...
// FIXME: this is an extremely poor heuristic. it looks on the line with
// the { and the line before to see if they start with a keyword
// beginning 'namespace'. that's 99% of usage, I'd guess.
{
if( braceFirst >= 0 && braceLine->attribute(braceFirst) == keywordAttrib &&
braceLine->stringAtPos( braceFirst, QString::fromLatin1( "namespace" ) ) )
return continuationIndent(indentCursor) + whitespaceToOpenBrace;
if( braceCursor.line() > 0 )
{
KateTextLine::Ptr prevLine = doc->plainKateTextLine(braceCursor.line() - 1);
int firstPrev = prevLine->firstChar();
if( firstPrev >= 0 && prevLine->attribute(firstPrev) == keywordAttrib &&
prevLine->stringAtPos( firstPrev, QString::fromLatin1( "namespace" ) ) )
return continuationIndent(indentCursor) + whitespaceToOpenBrace;
}
}
KateTextLine::Ptr indentLine = doc->plainKateTextLine(indentCursor.line());
const int indentFirst = indentLine->firstChar();
// if the line starts with a close brace, don't indent...
if( indentFirst >= 0 && indentLine->getChar(indentFirst) == '}' )
return whitespaceToOpenBrace;
// if : is the first character (and not followed by another :), this is the start
// of an initialization list, or a continuation of a ?:. either way, indent twice.
if ( indentFirst >= 0 && indentLine->attribute(indentFirst) == symbolAttrib &&
indentLine->getChar(indentFirst) == ':' && indentLine->getChar(indentFirst+1) != ':' )
{
return indentString + indentString + whitespaceToOpenBrace;
}
const bool continuation = inStatement(indentCursor);
// if the current line starts with a label, don't indent...
if( !continuation && startsWithLabel( indentCursor.line() ) )
return whitespaceToOpenBrace;
// the normal case: indent once for the brace, again if it's a continuation
QString continuationIndent = continuation ? indentString : QString::null;
return indentString + continuationIndent + whitespaceToOpenBrace;
}
void KateCSAndSIndent::processChar(QChar c)
{
// 'n' trigger is for c# regions.
static const QString triggers("}{)]/:;#n");
if (triggers.find(c) == -1)
return;
// for historic reasons, processChar doesn't get a cursor
// to work on. so fabricate one.
KateView *view = doc->activeView();
KateDocCursor begin(view->cursorLine(), 0, doc);
KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
if ( c == 'n' )
{
int first = textLine->firstChar();
if( first < 0 || textLine->getChar(first) != '#' )
return;
}
if ( textLine->attribute( begin.col() ) == doxyCommentAttrib )
{
// dominik: if line is "* /", change it to "*/"
if ( c == '/' )
{
int first = textLine->firstChar();
// if the first char exists and is a '*', and the next non-space-char
// is already the just typed '/', concatenate it to "*/".
if ( first != -1
&& textLine->getChar( first ) == '*'
&& textLine->nextNonSpaceChar( first+1 ) == view->cursorColumnReal()-1 )
doc->removeText( view->cursorLine(), first+1, view->cursorLine(), view->cursorColumnReal()-1);
}
// anders: don't change the indent of doxygen lines here.
return;
}
processLine(begin);
}
//END
//BEGIN KateVarIndent
class KateVarIndentPrivate {
public:
QRegExp reIndentAfter, reIndent, reUnindent;
QString triggers;
uint couples;
uchar coupleAttrib;
};
KateVarIndent::KateVarIndent( KateDocument *doc )
: KateNormalIndent( doc )
{
d = new KateVarIndentPrivate;
d->reIndentAfter = QRegExp( doc->variable( "var-indent-indent-after" ) );
d->reIndent = QRegExp( doc->variable( "var-indent-indent" ) );
d->reUnindent = QRegExp( doc->variable( "var-indent-unindent" ) );
d->triggers = doc->variable( "var-indent-triggerchars" );
d->coupleAttrib = 0;
slotVariableChanged( "var-indent-couple-attribute", doc->variable( "var-indent-couple-attribute" ) );
slotVariableChanged( "var-indent-handle-couples", doc->variable( "var-indent-handle-couples" ) );
// update if a setting is changed
connect( doc, SIGNAL(variableChanged( const QString&, const QString&) ),
this, SLOT(slotVariableChanged( const QString&, const QString& )) );
}
KateVarIndent::~KateVarIndent()
{
delete d;
}
void KateVarIndent::processNewline ( KateDocCursor &begin, bool /*needContinue*/ )
{
// process the line left, as well as the one entered
KateDocCursor left( begin.line()-1, 0, doc );
processLine( left );
processLine( begin );
}
void KateVarIndent::processChar ( QChar c )
{
// process line if the c is in our list, and we are not in comment text
if ( d->triggers.contains( c ) )
{
KateTextLine::Ptr ln = doc->plainKateTextLine( doc->activeView()->cursorLine() );
if ( ln->attribute( doc->activeView()->cursorColumn()-1 ) == commentAttrib )
return;
KateView *view = doc->activeView();
KateDocCursor begin( view->cursorLine(), 0, doc );
kdDebug(13030)<<"variable indenter: process char '"<<c<<", line "<<begin.line()<<endl;
processLine( begin );
}
}
void KateVarIndent::processLine ( KateDocCursor &line )
{
QString indent; // store the indent string here
// find the first line with content that is not starting with comment text,
// and take the position from that
int ln = line.line();
int pos = -1;
KateTextLine::Ptr ktl = doc->plainKateTextLine( ln );
if ( ! ktl ) return; // no line!?
// skip blank lines, except for the cursor line
KateView *v = doc->activeView();
if ( (ktl->firstChar() < 0) && (!v || (int)v->cursorLine() != ln ) )
return;
int fc;
if ( ln > 0 )
do
{
ktl = doc->plainKateTextLine( --ln );
fc = ktl->firstChar();
if ( ktl->attribute( fc ) != commentAttrib )
pos = fc;
}
while ( (ln > 0) && (pos < 0) ); // search a not empty text line
if ( pos < 0 )
pos = 0;
else
pos = ktl->cursorX( pos, tabWidth );
int adjustment = 0;
// try 'couples' for an opening on the above line first. since we only adjust by 1 unit,
// we only need 1 match.
if ( d->couples & Parens && coupleBalance( ln, '(', ')' ) > 0 )
adjustment++;
else if ( d->couples & Braces && coupleBalance( ln, '{', '}' ) > 0 )
adjustment++;
else if ( d->couples & Brackets && coupleBalance( ln, '[', ']' ) > 0 )
adjustment++;
// Try 'couples' for a closing on this line first. since we only adjust by 1 unit,
// we only need 1 match. For unindenting, we look for a closing character
// *at the beginning of the line*
// NOTE Assume that a closing brace with the configured attribute on the start
// of the line is closing.
// When acting on processChar, the character isn't highlighted. So I could
// either not check, assuming that the first char *is* meant to close, or do a
// match test if the attrib is 0. How ever, doing that is
// a potentially huge job, if the match is several hundred lines away.
// Currently, the check is done.
{
KateTextLine::Ptr tl = doc->plainKateTextLine( line.line() );
int i = tl->firstChar();
if ( i > -1 )
{
QChar ch = tl->getChar( i );
uchar at = tl->attribute( i );
kdDebug(13030)<<"attrib is "<<at<<endl;
if ( d->couples & Parens && ch == ')'
&& ( at == d->coupleAttrib
|| (! at && hasRelevantOpening( KateDocCursor( line.line(), i, doc ) ))
)
)
adjustment--;
else if ( d->couples & Braces && ch == '}'
&& ( at == d->coupleAttrib
|| (! at && hasRelevantOpening( KateDocCursor( line.line(), i, doc ) ))
)
)
adjustment--;
else if ( d->couples & Brackets && ch == ']'
&& ( at == d->coupleAttrib
|| (! at && hasRelevantOpening( KateDocCursor( line.line(), i, doc ) ))
)
)
adjustment--;
}
}
#define ISCOMMENTATTR(attr) (attr==commentAttrib||attr==doxyCommentAttrib)
#define ISCOMMENT (ISCOMMENTATTR(ktl->attribute(ktl->firstChar()))||ISCOMMENTATTR(ktl->attribute(matchpos)))
// check if we should indent, unless the line starts with comment text,
// or the match is in comment text
kdDebug(13030)<<"variable indenter: starting indent: "<<pos<<endl;
// check if the above line indicates that we shuld add indentation
int matchpos = 0;
if ( ktl && ! d->reIndentAfter.isEmpty()
&& (matchpos = d->reIndentAfter.search( doc->textLine( ln ) )) > -1
&& ! ISCOMMENT )
adjustment++;
// else, check if this line should indent unless ...
ktl = doc->plainKateTextLine( line.line() );
if ( ! d->reIndent.isEmpty()
&& (matchpos = d->reIndent.search( doc->textLine( line.line() ) )) > -1
&& ! ISCOMMENT )
adjustment++;
// else, check if the current line indicates if we should remove indentation unless ...
if ( ! d->reUnindent.isEmpty()
&& (matchpos = d->reUnindent.search( doc->textLine( line.line() ) )) > -1
&& ! ISCOMMENT )
adjustment--;
kdDebug(13030)<<"variable indenter: adjusting by "<<adjustment<<" units"<<endl;
if ( adjustment > 0 )
pos += indentWidth;
else if ( adjustment < 0 )
pos -= indentWidth;
ln = line.line();
fc = doc->plainKateTextLine( ln )->firstChar();
// dont change if there is no change.
// ### should I actually compare the strings?
// FIXME for some odd reason, the document gets marked as changed
// even if we don't change it !?
if ( fc == pos )
return;
if ( fc > 0 )
doc->removeText (ln, 0, ln, fc );
if ( pos > 0 )
indent = tabString( pos );
if ( pos > 0 )
doc->insertText (ln, 0, indent);
// try to restore cursor ?
line.setCol( pos );
}
void KateVarIndent::processSection (const KateDocCursor &begin, const KateDocCursor &end)
{
KateDocCursor cur = begin;
while (cur.line() <= end.line())
{
processLine (cur);
if (!cur.gotoNextLine())
break;
}
}
void KateVarIndent::slotVariableChanged( const QString &var, const QString &val )
{
if ( ! var.startsWith("var-indent") )
return;
if ( var == "var-indent-indent-after" )
d->reIndentAfter.setPattern( val );
else if ( var == "var-indent-indent" )
d->reIndent.setPattern( val );
else if ( var == "var-indent-unindent" )
d->reUnindent.setPattern( val );
else if ( var == "var-indent-triggerchars" )
d->triggers = val;
else if ( var == "var-indent-handle-couples" )
{
d->couples = 0;
QStringList l = QStringList::split( " ", val );
if ( l.contains("parens") ) d->couples |= Parens;
if ( l.contains("braces") ) d->couples |= Braces;
if ( l.contains("brackets") ) d->couples |= Brackets;
}
else if ( var == "var-indent-couple-attribute" )
{
//read a named attribute of the config.
KateHlItemDataList items;
doc->highlight()->getKateHlItemDataListCopy (0, items);
for (uint i=0; i<items.count(); i++)
{
if ( items.at(i)->name.section( ':', 1 ) == val )
{
d->coupleAttrib = i;
break;
}
}
}
}
int KateVarIndent::coupleBalance ( int line, const QChar &open, const QChar &close ) const
{
int r = 0;
KateTextLine::Ptr ln = doc->plainKateTextLine( line );
if ( ! ln || ! ln->length() ) return 0;
for ( uint z=0; z < ln->length(); z++ )
{
QChar c = ln->getChar( z );
if ( ln->attribute(z) == d->coupleAttrib )
{
kdDebug(13030)<<z<<", "<<c<<endl;
if (c == open)
r++;
else if (c == close)
r--;
}
}
return r;
}
bool KateVarIndent::hasRelevantOpening( const KateDocCursor &end ) const
{
KateDocCursor cur = end;
int count = 1;
QChar close = cur.currentChar();
QChar opener;
if ( close == '}' ) opener = '{';
else if ( close = ')' ) opener = '(';
else if (close = ']' ) opener = '[';
else return false;
//Move backwards 1 by 1 and find the opening partner
while (cur.moveBackward(1))
{
if (cur.currentAttrib() == d->coupleAttrib)
{
QChar ch = cur.currentChar();
if (ch == opener)
count--;
else if (ch == close)
count++;
if (count == 0)
return true;
}
}
return false;
}
//END KateVarIndent
//BEGIN KateScriptIndent
KateScriptIndent::KateScriptIndent( KateDocument *doc )
: KateNormalIndent( doc )
{
m_script=KateFactory::self()->indentScript ("script-indent-c1-test");
}
KateScriptIndent::~KateScriptIndent()
{
}
void KateScriptIndent::processNewline( KateDocCursor &begin, bool needContinue )
{
kdDebug(13030) << "processNewline" << endl;
KateView *view = doc->activeView();
if (view)
{
QString errorMsg;
QTime t;
t.start();
kdDebug(13030)<<"calling m_script.processChar"<<endl;
if( !m_script.processNewline( view, begin, needContinue , errorMsg ) )
{
kdDebug(13030) << "Error in script-indent: " << errorMsg << endl;
}
kdDebug(13030) << "ScriptIndent::TIME in ms: " << t.elapsed() << endl;
}
}
void KateScriptIndent::processChar( QChar c )
{
kdDebug(13030) << "processChar" << endl;
KateView *view = doc->activeView();
if (view)
{
QString errorMsg;
QTime t;
t.start();
kdDebug(13030)<<"calling m_script.processChar"<<endl;
if( !m_script.processChar( view, c , errorMsg ) )
{
kdDebug(13030) << "Error in script-indent: " << errorMsg << endl;
}
kdDebug(13030) << "ScriptIndent::TIME in ms: " << t.elapsed() << endl;
}
}
void KateScriptIndent::processLine (KateDocCursor &line)
{
kdDebug(13030) << "processLine" << endl;
KateView *view = doc->activeView();
if (view)
{
QString errorMsg;
QTime t;
t.start();
kdDebug(13030)<<"calling m_script.processLine"<<endl;
if( !m_script.processLine( view, line , errorMsg ) )
{
kdDebug(13030) << "Error in script-indent: " << errorMsg << endl;
}
kdDebug(13030) << "ScriptIndent::TIME in ms: " << t.elapsed() << endl;
}
}
//END KateScriptIndent
//BEGIN ScriptIndentConfigPage, THIS IS ONLY A TEST! :)
#include <qlabel.h>
ScriptIndentConfigPage::ScriptIndentConfigPage ( QWidget *parent, const char *name )
: IndenterConfigPage(parent, name)
{
QLabel* hello = new QLabel("Hello world! Dummy for testing purpose.", this);
hello->show();
}
ScriptIndentConfigPage::~ScriptIndentConfigPage ()
{
}
void ScriptIndentConfigPage::apply ()
{
kdDebug(13030) << "ScriptIndentConfigPagE::apply() was called, save config options now!" << endl;
}
//END ScriptIndentConfigPage
// kate: space-indent on; indent-width 2; replace-tabs on;