- /* 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 <tdelocale.h>
- #include <kdebug.h>
- #include <tdepopupmenu.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);
- }
-
- TQStringList KateAutoIndent::listModes ()
- {
- TQStringList 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;
- }
-
- TQString KateAutoIndent::modeName (uint mode)
- {
- if (mode == KateDocumentConfig::imNormal)
- return TQString ("normal");
- else if (mode == KateDocumentConfig::imCStyle)
- return TQString ("cstyle");
- else if (mode == KateDocumentConfig::imPythonStyle)
- return TQString ("python");
- else if (mode == KateDocumentConfig::imXmlStyle)
- return TQString ("xml");
- else if (mode == KateDocumentConfig::imCSAndS)
- return TQString ("csands");
- else if ( mode == KateDocumentConfig::imVarIndent )
- return TQString( "varindent" );
- // else if ( mode == KateDocumentConfig::imScriptIndent )
- // return TQString( "scriptindent" );
-
- return TQString ("none");
- }
-
- TQString 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 TQString &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(TQWidget *parent, uint mode)
- {
- // if ( mode == KateDocumentConfig::imScriptIndent )
- // return new ScriptIndentConfigPage(parent, "script_indent_config_page");
-
- return 0;
- }
-
- KateAutoIndent::KateAutoIndent (KateDocument *_doc)
- : TQObject(), doc(_doc)
- {
- }
- KateAutoIndent::~KateAutoIndent ()
- {
- }
-
- //END KateAutoIndent
-
- //BEGIN KateViewIndentAction
- KateViewIndentationAction::KateViewIndentationAction(KateDocument *_doc, const TQString& text, TQObject* parent, const char* name)
- : TDEActionMenu (text, parent, name), doc(_doc)
- {
- connect(popupMenu(),TQT_SIGNAL(aboutToShow()),this,TQT_SLOT(slotAboutToShow()));
- }
-
- void KateViewIndentationAction::slotAboutToShow()
- {
- TQStringList modes = KateAutoIndent::listModes ();
-
- popupMenu()->clear ();
- for (uint z=0; z<modes.size(); ++z)
- popupMenu()->insertItem ( '&' + KateAutoIndent::modeDescription(z).replace('&', "&&"), this, TQT_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, TQT_SIGNAL(hlChanged()), this, TQT_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++)
- {
- TQString 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, TQChar open, TQChar 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)
- {
- TQChar 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 TQString hlFile = doc->highlight()->hlKeyForAttrib( attrib );
-
- if (attrib != commentAttrib && attrib != regionAttrib && attrib != alertAttrib && attrib != preprocessorAttrib && !hlFile.endsWith("doxygen.xml"))
- {
- TQChar 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);
- }
-
- TQString KateNormalIndent::tabString(uint pos) const
- {
- TQString 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)
- {
- TQString 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 ...
- TQChar first = textLine->getChar(firstChar);
- TQChar 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);
- TQString 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;
- TQTime 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);
- TQString 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);
- TQString 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)
- {
- TQString 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,
- TQChar prev1, TQChar 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 TQString 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(TQChar 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 TQString triggers("}{)/:#n");
- static const TQString firstTriggers("}{)/:#");
- static const TQString 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 TQChar curChar = textLine->getChar(curCol);
- const int first = textLine->firstChar();
- const TQChar 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) != TQString::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 TQChar 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)
- {
- TQChar 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;
- TQChar c;
- if (openCount > 0)
- c = '{';
- else if (openCount < 0)
- c = '}';
- else if (otherAnchor >= 0)
- c = textLine->getChar (otherAnchor);
-
- int specialIndent = 0;
- if (c == ':' && needContinue)
- {
- TQChar 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());
- TQChar 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;
-
- TQChar 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, TQChar('('), TQChar(')'), 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, TQChar('('), TQChar(')'), 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)
- {
- TQChar 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)
- {
- TQChar 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)
- {
- TQChar 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
-
- TQRegExp KatePythonIndent::endWithColon = TQRegExp( "^[^#]*:\\s*(#.*)?$" );
- TQRegExp KatePythonIndent::stopStmt = TQRegExp( "^\\s*(break|continue|raise|return|pass)\\b.*" );
- TQRegExp KatePythonIndent::blockBegin = TQRegExp( "^\\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)
- {
- TQString 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);
- TQChar 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 TQChar &stringChar, KateDocCursor &cur, KateDocCursor &end )
- {
- TQChar 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 TQRegExp KateXmlIndent::startsWithCloseTag("^[ \t]*</");
- const TQRegExp 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 (TQChar c)
- {
- if(c != '/') return;
-
- // only alter lines that start with a close element
- KateView *view = doc->activeView();
- TQString 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);
- TQString 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());
- TQString 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();
- TQString 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)
- {
- TQTime 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 TQString initialWhitespace(const KateTextLine::Ptr &line, int chars, bool convert = true)
- {
- TQString text = line->string(0, chars);
- if( (int)text.length() < chars )
- {
- TQString 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;
- }
-
- TQString 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 TQString::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();
- TQString 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 TQString 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 TQChar::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() );
- TQString 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;
- }
-
- TQString KateCSAndSIndent::continuationIndent( const KateDocCursor &begin )
- {
- if( !inStatement( begin ) )
- return TQString::null;
- return indentString;
- }
-
- /**
- * Figure out how indented the line containing @p begin should be.
- */
- TQString 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, TQString::fromLatin1("region") ) &&
- !currLine->stringAtPos( currLineFirst+1, TQString::fromLatin1("endregion") ) )
- return TQString::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, TQString::fromLatin1(scopeKeywords[n]) ) )
- return calcIndentAfterKeyword( begin, cur, pos, false );
- for( uint n = 0; n < ARRLEN(blockScopeKeywords); ++n )
- if( textLine->stringAtPos(pos, TQString::fromLatin1(blockScopeKeywords[n]) ) )
- return calcIndentAfterKeyword( begin, cur, pos, true );
- #undef ARRLEN
- }
- }
- }
-
- // no active { in file.
- return TQString::null;
- }
-
- TQString 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 );
- }
-
- TQString 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());
-
- TQString 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;
- }
-
- TQString KateCSAndSIndent::calcIndentInBrace(const KateDocCursor &indentCursor, const KateDocCursor &braceCursor, int bracePos)
- {
- KateTextLine::Ptr braceLine = doc->plainKateTextLine(braceCursor.line());
- const int braceFirst = braceLine->firstChar();
-
- TQString 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, TQString::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, TQString::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
- TQString continuationIndent = continuation ? indentString : TQString::null;
- return indentString + continuationIndent + whitespaceToOpenBrace;
- }
-
- void KateCSAndSIndent::processChar(TQChar c)
- {
- // 'n' trigger is for c# regions.
- static const TQString 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:
- TQRegExp reIndentAfter, reIndent, reUnindent;
- TQString triggers;
- uint couples;
- uchar coupleAttrib;
- };
-
- KateVarIndent::KateVarIndent( KateDocument *doc )
- : KateNormalIndent( doc )
- {
- d = new KateVarIndentPrivate;
- d->reIndentAfter = TQRegExp( doc->variable( "var-indent-indent-after" ) );
- d->reIndent = TQRegExp( doc->variable( "var-indent-indent" ) );
- d->reUnindent = TQRegExp( 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, TQT_SIGNAL(variableChanged( const TQString&, const TQString&) ),
- this, TQT_SLOT(slotVariableChanged( const TQString&, const TQString& )) );
- }
-
- 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 ( TQChar 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 )
- {
- TQString 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 )
- {
- TQChar 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 TQString &var, const TQString &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;
- TQStringList l = TQStringList::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 TQChar &open, const TQChar &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++ )
- {
- TQChar 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;
-
- TQChar close = cur.currentChar();
- TQChar 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)
- {
- TQChar 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)
- {
- TQString errorMsg;
-
- TQTime 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( TQChar c )
- {
- kdDebug(13030) << "processChar" << endl;
- KateView *view = doc->activeView();
-
- if (view)
- {
- TQString errorMsg;
-
- TQTime 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)
- {
- TQString errorMsg;
-
- TQTime 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 <tqlabel.h>
- ScriptIndentConfigPage::ScriptIndentConfigPage ( TQWidget *parent, const char *name )
- : IndenterConfigPage(parent, name)
- {
- TQLabel* hello = new TQLabel("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;
|