diff --git a/kate/part/katehighlight.cpp b/kate/part/katehighlight.cpp index 65ac6ee73f..4c39da6633 100644 --- a/kate/part/katehighlight.cpp +++ b/kate/part/katehighlight.cpp @@ -1,3376 +1,3363 @@ /* This file is part of the KDE libraries Copyright (C) 2003, 2004 Anders Lund Copyright (C) 2003 Hamish Rodda Copyright (C) 2001,2002 Joseph Wenninger Copyright (C) 2001 Christoph Cullmann Copyright (C) 1999 Jochen Wilhelmy 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ //BEGIN INCLUDES #include "katehighlight.h" #include "katehighlight.moc" #include "katetextline.h" #include "katedocument.h" #include "katesyntaxdocument.h" #include "katerenderer.h" #include "katefactory.h" #include "kateschema.h" #include "kateconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //END //BEGIN defines // same as in kmimemagic, no need to feed more data #define KATE_HL_HOWMANY 1024 // min. x seconds between two dynamic contexts reset static const int KATE_DYNAMIC_CONTEXTS_RESET_DELAY = 30 * 1000; // x is a QString. if x is "true" or "1" this expression returns "true" #define IS_TRUE(x) x.lower() == QString("true") || x.toInt() == 1 //END defines //BEGIN Prviate HL classes inline bool kateInsideString (const QString &str, QChar ch) { for (uint i=0; i < str.length(); i++) if (*(str.unicode()+i) == ch) return true; return false; } class KateHlItem { public: KateHlItem(int attribute, int context,signed char regionId, signed char regionId2); virtual ~KateHlItem(); public: // caller must keep in mind: LEN > 0 is a must !!!!!!!!!!!!!!!!!!!!!1 // Now, the function returns the offset detected, or 0 if no match is found. // bool linestart isn't needed, this is equivalent to offset == 0. virtual int checkHgl(const QString& text, int offset, int len) = 0; virtual bool lineContinue(){return false;} virtual QStringList *capturedTexts() {return 0;} virtual KateHlItem *clone(const QStringList *) {return this;} static void dynamicSubstitute(QString& str, const QStringList *args); QMemArray subItems; int attr; int ctx; signed char region; signed char region2; bool lookAhead; bool dynamic; bool dynamicChild; bool firstNonSpace; bool onlyConsume; int column; // start enable flags, nicer than the virtual methodes // saves function calls bool alwaysStartEnable; bool customStartEnable; }; class KateHlContext { public: - KateHlContext(int attribute, int lineEndContext,int _lineBeginContext, + KateHlContext(const QString &_hlId, int attribute, int lineEndContext,int _lineBeginContext, bool _fallthrough, int _fallthroughContext, bool _dynamic); virtual ~KateHlContext(); KateHlContext *clone(const QStringList *args); QValueVector items; + QString hlId; ///< A unique highlight identifier. Used to look up correct properties. int attr; int ctx; int lineBeginContext; /** @internal anders: possible escape if no rules matches. false unless 'fallthrough="1|true"' (insensitive) if true, go to ftcxt w/o eating of string. ftctx is "fallthroughContext" in xml files, valid values are int or #pop[..] see in KateHighlighting::doHighlight */ bool fallthrough; int ftctx; // where to go after no rules matched bool dynamic; bool dynamicChild; }; class KateEmbeddedHlInfo { public: KateEmbeddedHlInfo() {loaded=false;context0=-1;} KateEmbeddedHlInfo(bool l, int ctx0) {loaded=l;context0=ctx0;} public: bool loaded; int context0; }; class KateHlIncludeRule { public: KateHlIncludeRule(int ctx_=0, uint pos_=0, const QString &incCtxN_="", bool incAttrib=false) : ctx(ctx_) , pos( pos_) , incCtxN( incCtxN_ ) , includeAttrib( incAttrib ) { incCtx=-1; } //KateHlIncludeRule(int ctx_, uint pos_, bool incAttrib) {ctx=ctx_;pos=pos_;incCtx=-1;incCtxN="";includeAttrib=incAttrib} public: int ctx; uint pos; int incCtx; QString incCtxN; bool includeAttrib; }; class KateHlCharDetect : public KateHlItem { public: KateHlCharDetect(int attribute, int context,signed char regionId,signed char regionId2, QChar); virtual int checkHgl(const QString& text, int offset, int len); virtual KateHlItem *clone(const QStringList *args); private: QChar sChar; }; class KateHl2CharDetect : public KateHlItem { public: KateHl2CharDetect(int attribute, int context, signed char regionId,signed char regionId2, QChar ch1, QChar ch2); KateHl2CharDetect(int attribute, int context,signed char regionId,signed char regionId2, const QChar *ch); virtual int checkHgl(const QString& text, int offset, int len); virtual KateHlItem *clone(const QStringList *args); private: QChar sChar1; QChar sChar2; }; class KateHlStringDetect : public KateHlItem { public: KateHlStringDetect(int attribute, int context, signed char regionId,signed char regionId2, const QString &, bool inSensitive=false); virtual int checkHgl(const QString& text, int offset, int len); virtual KateHlItem *clone(const QStringList *args); private: const QString str; const int strLen; const bool _inSensitive; }; class KateHlRangeDetect : public KateHlItem { public: KateHlRangeDetect(int attribute, int context, signed char regionId,signed char regionId2, QChar ch1, QChar ch2); virtual int checkHgl(const QString& text, int offset, int len); private: QChar sChar1; QChar sChar2; }; class KateHlKeyword : public KateHlItem { public: KateHlKeyword(int attribute, int context,signed char regionId,signed char regionId2, bool casesensitive, const QString& delims); virtual ~KateHlKeyword (); void addList(const QStringList &); virtual int checkHgl(const QString& text, int offset, int len); private: QMemArray< QDict* > dict; bool _caseSensitive; const QString& deliminators; int minLen; int maxLen; }; class KateHlInt : public KateHlItem { public: KateHlInt(int attribute, int context, signed char regionId,signed char regionId2); virtual int checkHgl(const QString& text, int offset, int len); }; class KateHlFloat : public KateHlItem { public: KateHlFloat(int attribute, int context, signed char regionId,signed char regionId2); virtual ~KateHlFloat () {} virtual int checkHgl(const QString& text, int offset, int len); }; class KateHlCFloat : public KateHlFloat { public: KateHlCFloat(int attribute, int context, signed char regionId,signed char regionId2); virtual int checkHgl(const QString& text, int offset, int len); int checkIntHgl(const QString& text, int offset, int len); }; class KateHlCOct : public KateHlItem { public: KateHlCOct(int attribute, int context, signed char regionId,signed char regionId2); virtual int checkHgl(const QString& text, int offset, int len); }; class KateHlCHex : public KateHlItem { public: KateHlCHex(int attribute, int context, signed char regionId,signed char regionId2); virtual int checkHgl(const QString& text, int offset, int len); }; class KateHlLineContinue : public KateHlItem { public: KateHlLineContinue(int attribute, int context, signed char regionId,signed char regionId2); virtual bool endEnable(QChar c) {return c == '\0';} virtual int checkHgl(const QString& text, int offset, int len); virtual bool lineContinue(){return true;} }; class KateHlCStringChar : public KateHlItem { public: KateHlCStringChar(int attribute, int context, signed char regionId,signed char regionId2); virtual int checkHgl(const QString& text, int offset, int len); }; class KateHlCChar : public KateHlItem { public: KateHlCChar(int attribute, int context,signed char regionId,signed char regionId2); virtual int checkHgl(const QString& text, int offset, int len); }; class KateHlAnyChar : public KateHlItem { public: KateHlAnyChar(int attribute, int context, signed char regionId,signed char regionId2, const QString& charList); virtual int checkHgl(const QString& text, int offset, int len); private: const QString _charList; }; class KateHlRegExpr : public KateHlItem { public: KateHlRegExpr(int attribute, int context,signed char regionId,signed char regionId2 ,QString expr, bool insensitive, bool minimal); ~KateHlRegExpr() { delete Expr; }; virtual int checkHgl(const QString& text, int offset, int len); virtual QStringList *capturedTexts(); virtual KateHlItem *clone(const QStringList *args); private: QRegExp *Expr; bool handlesLinestart; QString _regexp; bool _insensitive; bool _minimal; }; class KateHlDetectSpaces : public KateHlItem { public: KateHlDetectSpaces (int attribute, int context,signed char regionId,signed char regionId2) : KateHlItem(attribute,context,regionId,regionId2) {} virtual int checkHgl(const QString& text, int offset, int len) { int len2 = offset + len; while ((offset < len2) && text[offset].isSpace()) offset++; return offset; } }; class KateHlDetectIdentifier : public KateHlItem { public: KateHlDetectIdentifier (int attribute, int context,signed char regionId,signed char regionId2) : KateHlItem(attribute,context,regionId,regionId2) { alwaysStartEnable = false; } virtual int checkHgl(const QString& text, int offset, int len) { if (text[offset++].isLetter() || (text[offset] == QChar ('_'))) { int len2 = offset-1+len; while ((offset < len2) && (text[offset].isLetterOrNumber() || (text[offset] == QChar ('_')))) offset++; return offset; } return 0; } }; //END //BEGIN STATICS KateHlManager *KateHlManager::s_self = 0; static const bool trueBool = true; static const QString stdDeliminator = QString (" \t.():!+,-<=>%&*/;?[]^{|}~\\"); //END //BEGIN NON MEMBER FUNCTIONS static KateHlItemData::ItemStyles getDefStyleNum(QString name) { if (name=="dsNormal") return KateHlItemData::dsNormal; else if (name=="dsKeyword") return KateHlItemData::dsKeyword; else if (name=="dsDataType") return KateHlItemData::dsDataType; else if (name=="dsDecVal") return KateHlItemData::dsDecVal; else if (name=="dsBaseN") return KateHlItemData::dsBaseN; else if (name=="dsFloat") return KateHlItemData::dsFloat; else if (name=="dsChar") return KateHlItemData::dsChar; else if (name=="dsString") return KateHlItemData::dsString; else if (name=="dsComment") return KateHlItemData::dsComment; else if (name=="dsOthers") return KateHlItemData::dsOthers; else if (name=="dsAlert") return KateHlItemData::dsAlert; else if (name=="dsFunction") return KateHlItemData::dsFunction; else if (name=="dsRegionMarker") return KateHlItemData::dsRegionMarker; else if (name=="dsError") return KateHlItemData::dsError; return KateHlItemData::dsNormal; } //END //BEGIN KateHlItem KateHlItem::KateHlItem(int attribute, int context,signed char regionId,signed char regionId2) : attr(attribute), ctx(context), region(regionId), region2(regionId2), lookAhead(false), dynamic(false), dynamicChild(false), firstNonSpace(false), onlyConsume(false), column (-1), alwaysStartEnable (true), customStartEnable (false) { } KateHlItem::~KateHlItem() { //kdDebug(13010)<<"In hlItem::~KateHlItem()"<= '0' && c <= '9') { if ((uint)(c - '0') < args->size()) { str.replace(i, 2, (*args)[c - '0']); i += ((*args)[c - '0']).length() - 1; } else { str.replace(i, 2, ""); --i; } } } } } //END //BEGIN KateHlCharDetect KateHlCharDetect::KateHlCharDetect(int attribute, int context, signed char regionId,signed char regionId2, QChar c) : KateHlItem(attribute,context,regionId,regionId2) , sChar(c) { } -int KateHlCharDetect::checkHgl(const QString& text, int offset, int len) +int KateHlCharDetect::checkHgl(const QString& text, int offset, int /*len*/) { if (text[offset] == sChar) return offset + 1; return 0; } KateHlItem *KateHlCharDetect::clone(const QStringList *args) { char c = sChar.latin1(); if (c < '0' || c > '9' || (unsigned)(c - '0') >= args->size()) return this; KateHlCharDetect *ret = new KateHlCharDetect(attr, ctx, region, region2, (*args)[c - '0'][0]); ret->dynamicChild = true; return ret; } //END //BEGIN KateHl2CharDetect KateHl2CharDetect::KateHl2CharDetect(int attribute, int context, signed char regionId,signed char regionId2, QChar ch1, QChar ch2) : KateHlItem(attribute,context,regionId,regionId2) , sChar1 (ch1) , sChar2 (ch2) { } int KateHl2CharDetect::checkHgl(const QString& text, int offset, int len) { if ((len >= 2) && text[offset++] == sChar1 && text[offset++] == sChar2) return offset; return 0; } KateHlItem *KateHl2CharDetect::clone(const QStringList *args) { char c1 = sChar1.latin1(); char c2 = sChar2.latin1(); if (c1 < '0' || c1 > '9' || (unsigned)(c1 - '0') >= args->size()) return this; if (c2 < '0' || c2 > '9' || (unsigned)(c2 - '0') >= args->size()) return this; KateHl2CharDetect *ret = new KateHl2CharDetect(attr, ctx, region, region2, (*args)[c1 - '0'][0], (*args)[c2 - '0'][0]); ret->dynamicChild = true; return ret; } //END //BEGIN KateHlStringDetect KateHlStringDetect::KateHlStringDetect(int attribute, int context, signed char regionId,signed char regionId2,const QString &s, bool inSensitive) : KateHlItem(attribute, context,regionId,regionId2) , str(inSensitive ? s.upper() : s) , strLen (str.length()) , _inSensitive(inSensitive) { } int KateHlStringDetect::checkHgl(const QString& text, int offset, int len) { if (len < strLen) return 0; if (_inSensitive) { for (int i=0; i < strLen; i++) if (text[offset++].upper() != str[i]) return 0; return offset; } else { for (int i=0; i < strLen; i++) if (text[offset++] != str[i]) return 0; return offset; } return 0; } KateHlItem *KateHlStringDetect::clone(const QStringList *args) { QString newstr = str; dynamicSubstitute(newstr, args); if (newstr == str) return this; KateHlStringDetect *ret = new KateHlStringDetect(attr, ctx, region, region2, newstr, _inSensitive); ret->dynamicChild = true; return ret; } //END //BEGIN KateHlRangeDetect KateHlRangeDetect::KateHlRangeDetect(int attribute, int context, signed char regionId,signed char regionId2, QChar ch1, QChar ch2) : KateHlItem(attribute,context,regionId,regionId2) , sChar1 (ch1) , sChar2 (ch2) { } int KateHlRangeDetect::checkHgl(const QString& text, int offset, int len) { if (text[offset] == sChar1) { do { offset++; len--; if (len < 1) return 0; } while (text[offset] != sChar2); return offset + 1; } return 0; } //END //BEGIN KateHlKeyword KateHlKeyword::KateHlKeyword (int attribute, int context, signed char regionId,signed char regionId2, bool casesensitive, const QString& delims) : KateHlItem(attribute,context,regionId,regionId2) , _caseSensitive(casesensitive) , deliminators(delims) , minLen (0xFFFFFF) , maxLen (0) { alwaysStartEnable = false; customStartEnable = true; } KateHlKeyword::~KateHlKeyword () { for (uint i=0; i < dict.size(); ++i) delete dict[i]; } void KateHlKeyword::addList(const QStringList& list) { for(uint i=0; i < list.count(); ++i) { int len = list[i].length(); if (minLen > len) minLen = len; if (maxLen < len) maxLen = len; if ((uint)len >= dict.size()) { uint oldSize = dict.size(); dict.resize (len+1); for (uint m=oldSize; m < dict.size(); ++m) dict[m] = 0; } if (!dict[len]) dict[len] = new QDict (17, _caseSensitive); dict[len]->insert(list[i], &trueBool); } } int KateHlKeyword::checkHgl(const QString& text, int offset, int len) { int offset2 = offset; int wordLen = 0; while ((len > wordLen) && !kateInsideString (deliminators, text[offset2])) { offset2++; wordLen++; if (wordLen > maxLen) return 0; } if (wordLen < minLen) return 0; if ( dict[wordLen] && dict[wordLen]->find(QConstString(text.unicode() + offset, wordLen).string()) ) return offset2; return 0; } //END //BEGIN KateHlInt KateHlInt::KateHlInt(int attribute, int context, signed char regionId,signed char regionId2) : KateHlItem(attribute,context,regionId,regionId2) { alwaysStartEnable = false; } int KateHlInt::checkHgl(const QString& text, int offset, int len) { int offset2 = offset; while ((len > 0) && text[offset2].isDigit()) { offset2++; len--; } if (offset2 > offset) { if (len > 0) { for (uint i=0; i < subItems.size(); i++) { if ( (offset = subItems[i]->checkHgl(text, offset2, len)) ) return offset; } } return offset2; } return 0; } //END //BEGIN KateHlFloat KateHlFloat::KateHlFloat(int attribute, int context, signed char regionId,signed char regionId2) : KateHlItem(attribute,context, regionId,regionId2) { alwaysStartEnable = false; } int KateHlFloat::checkHgl(const QString& text, int offset, int len) { bool b = false; bool p = false; while ((len > 0) && text[offset].isDigit()) { offset++; len--; b = true; } if ((len > 0) && (p = (text[offset] == '.'))) { offset++; len--; while ((len > 0) && text[offset].isDigit()) { offset++; len--; b = true; } } if (!b) return 0; if ((len > 0) && ((text[offset] & 0xdf) == 'E')) { offset++; len--; } else { if (!p) return 0; else { if (len > 0) { for (uint i=0; i < subItems.size(); i++) { int offset2 = subItems[i]->checkHgl(text, offset, len); if (offset2) return offset2; } } return offset; } } if ((len > 0) && (text[offset] == '-' || text[offset] =='+')) { offset++; len--; } b = false; while ((len > 0) && text[offset].isDigit()) { offset++; len--; b = true; } if (b) { if (len > 0) { for (uint i=0; i < subItems.size(); i++) { int offset2 = subItems[i]->checkHgl(text, offset, len); if (offset2) return offset2; } } return offset; } return 0; } //END //BEGIN KateHlCOct KateHlCOct::KateHlCOct(int attribute, int context, signed char regionId,signed char regionId2) : KateHlItem(attribute,context,regionId,regionId2) { alwaysStartEnable = false; } int KateHlCOct::checkHgl(const QString& text, int offset, int len) { if (text[offset] == '0') { offset++; len--; int offset2 = offset; while ((len > 0) && (text[offset2] >= '0' && text[offset2] <= '7')) { offset2++; len--; } if (offset2 > offset) { if ((len > 0) && ((text[offset2] & 0xdf) == 'L' || (text[offset] & 0xdf) == 'U' )) offset2++; return offset2; } } return 0; } //END //BEGIN KateHlCHex KateHlCHex::KateHlCHex(int attribute, int context,signed char regionId,signed char regionId2) : KateHlItem(attribute,context,regionId,regionId2) { alwaysStartEnable = false; } int KateHlCHex::checkHgl(const QString& text, int offset, int len) { if ((len > 1) && (text[offset++] == '0') && ((text[offset++] & 0xdf) == 'X' )) { len -= 2; int offset2 = offset; while ((len > 0) && (text[offset2].isDigit() || ((text[offset2] & 0xdf) >= 'A' && (text[offset2] & 0xdf) <= 'F'))) { offset2++; len--; } if (offset2 > offset) { if ((len > 0) && ((text[offset2] & 0xdf) == 'L' || (text[offset2] & 0xdf) == 'U' )) offset2++; return offset2; } } return 0; } //END //BEGIN KateHlCFloat KateHlCFloat::KateHlCFloat(int attribute, int context, signed char regionId,signed char regionId2) : KateHlFloat(attribute,context,regionId,regionId2) { alwaysStartEnable = false; } int KateHlCFloat::checkIntHgl(const QString& text, int offset, int len) { int offset2 = offset; while ((len > 0) && text[offset].isDigit()) { offset2++; len--; } if (offset2 > offset) return offset2; return 0; } int KateHlCFloat::checkHgl(const QString& text, int offset, int len) { int offset2 = KateHlFloat::checkHgl(text, offset, len); if (offset2) { if ((text[offset2] & 0xdf) == 'F' ) offset2++; return offset2; } else { offset2 = checkIntHgl(text, offset, len); if (offset2 && ((text[offset2] & 0xdf) == 'F' )) return ++offset2; else return 0; } } //END //BEGIN KateHlAnyChar KateHlAnyChar::KateHlAnyChar(int attribute, int context, signed char regionId,signed char regionId2, const QString& charList) : KateHlItem(attribute, context,regionId,regionId2) , _charList(charList) { } int KateHlAnyChar::checkHgl(const QString& text, int offset, int) { if (kateInsideString (_charList, text[offset])) return ++offset; return 0; } //END //BEGIN KateHlRegExpr KateHlRegExpr::KateHlRegExpr( int attribute, int context, signed char regionId,signed char regionId2, QString regexp, bool insensitive, bool minimal) : KateHlItem(attribute, context, regionId,regionId2) , handlesLinestart (regexp.startsWith("^")) , _regexp(regexp) , _insensitive(insensitive) , _minimal(minimal) { if (!handlesLinestart) regexp.prepend("^"); Expr = new QRegExp(regexp, !_insensitive); Expr->setMinimal(_minimal); } int KateHlRegExpr::checkHgl(const QString& text, int offset, int /*len*/) { if (offset && handlesLinestart) return 0; int offset2 = Expr->search( text, offset, QRegExp::CaretAtOffset ); if (offset2 == -1) return 0; return (offset + Expr->matchedLength()); } QStringList *KateHlRegExpr::capturedTexts() { return new QStringList(Expr->capturedTexts()); } KateHlItem *KateHlRegExpr::clone(const QStringList *args) { QString regexp = _regexp; QStringList escArgs = *args; for (QStringList::Iterator it = escArgs.begin(); it != escArgs.end(); ++it) { (*it).replace(QRegExp("(\\W)"), "\\\\1"); } dynamicSubstitute(regexp, &escArgs); if (regexp == _regexp) return this; // kdDebug (13010) << "clone regexp: " << regexp << endl; KateHlRegExpr *ret = new KateHlRegExpr(attr, ctx, region, region2, regexp, _insensitive, _minimal); ret->dynamicChild = true; return ret; } //END //BEGIN KateHlLineContinue KateHlLineContinue::KateHlLineContinue(int attribute, int context, signed char regionId,signed char regionId2) : KateHlItem(attribute,context,regionId,regionId2) { } int KateHlLineContinue::checkHgl(const QString& text, int offset, int len) { if ((len == 1) && (text[offset] == '\\')) return ++offset; return 0; } //END //BEGIN KateHlCStringChar KateHlCStringChar::KateHlCStringChar(int attribute, int context,signed char regionId,signed char regionId2) : KateHlItem(attribute,context,regionId,regionId2) { } // checks for C escaped chars \n and escaped hex/octal chars static int checkEscapedChar(const QString& text, int offset, int& len) { int i; if (text[offset] == '\\' && len > 1) { offset++; len--; switch(text[offset]) { case 'a': // checks for control chars case 'b': // we want to fall through case 'e': case 'f': case 'n': case 'r': case 't': case 'v': case '\'': case '\"': case '?' : // added ? ANSI C classifies this as an escaped char case '\\': offset++; len--; break; case 'x': // if it's like \xff offset++; // eat the x len--; // these for loops can probably be // replaced with something else but // for right now they work // check for hexdigits for (i = 0; (len > 0) && (i < 2) && (text[offset] >= '0' && text[offset] <= '9' || (text[offset] & 0xdf) >= 'A' && (text[offset] & 0xdf) <= 'F'); i++) { offset++; len--; } if (i == 0) return 0; // takes care of case '\x' break; case '0': case '1': case '2': case '3' : case '4': case '5': case '6': case '7' : for (i = 0; (len > 0) && (i < 3) && (text[offset] >='0'&& text[offset] <='7'); i++) { offset++; len--; } break; default: return 0; } return offset; } return 0; } int KateHlCStringChar::checkHgl(const QString& text, int offset, int len) { return checkEscapedChar(text, offset, len); } //END //BEGIN KateHlCChar KateHlCChar::KateHlCChar(int attribute, int context,signed char regionId,signed char regionId2) : KateHlItem(attribute,context,regionId,regionId2) { } int KateHlCChar::checkHgl(const QString& text, int offset, int len) { if ((len > 1) && (text[offset] == '\'') && (text[offset+1] != '\'')) { int oldl; oldl = len; len--; int offset2 = checkEscapedChar(text, offset + 1, len); if (!offset2) { if (oldl > 2) { offset2 = offset + 2; len = oldl - 2; } else { return 0; } } if ((len > 0) && (text[offset2] == '\'')) return ++offset2; } return 0; } //END //BEGIN KateHl2CharDetect KateHl2CharDetect::KateHl2CharDetect(int attribute, int context, signed char regionId,signed char regionId2, const QChar *s) : KateHlItem(attribute,context,regionId,regionId2) { sChar1 = s[0]; sChar2 = s[1]; } //END KateHl2CharDetect KateHlItemData::KateHlItemData(const QString name, int defStyleNum) : name(name), defStyleNum(defStyleNum) { } KateHlData::KateHlData(const QString &wildcards, const QString &mimetypes, const QString &identifier, int priority) : wildcards(wildcards), mimetypes(mimetypes), identifier(identifier), priority(priority) { } //BEGIN KateHlContext -KateHlContext::KateHlContext (int attribute, int lineEndContext, int _lineBeginContext, bool _fallthrough, int _fallthroughContext, bool _dynamic) +KateHlContext::KateHlContext (const QString &_hlId, int attribute, int lineEndContext, int _lineBeginContext, bool _fallthrough, int _fallthroughContext, bool _dynamic) { + hlId = _hlId; attr = attribute; ctx = lineEndContext; lineBeginContext = _lineBeginContext; fallthrough = _fallthrough; ftctx = _fallthroughContext; dynamic = _dynamic; dynamicChild = false; } KateHlContext *KateHlContext::clone(const QStringList *args) { - KateHlContext *ret = new KateHlContext(attr, ctx, lineBeginContext, fallthrough, ftctx, false); + KateHlContext *ret = new KateHlContext(hlId, attr, ctx, lineBeginContext, fallthrough, ftctx, false); for (uint n=0; n < items.size(); ++n) { KateHlItem *item = items[n]; KateHlItem *i = (item->dynamic ? item->clone(args) : item); ret->items.append(i); } ret->dynamicChild = true; return ret; } KateHlContext::~KateHlContext() { if (dynamicChild) { for (uint n=0; n < items.size(); ++n) { if (items[n]->dynamicChild) delete items[n]; } } } //END //BEGIN KateHighlighting KateHighlighting::KateHighlighting(const KateSyntaxModeListItem *def) : refCount(0) { m_attributeArrays.setAutoDelete (true); errorsAndWarnings = ""; building=false; noHl = false; m_foldingIndentationSensitive = false; folding=false; internalIDList.setAutoDelete(true); if (def == 0) { noHl = true; iName = "None"; // not translated internal name (for config and more) iNameTranslated = i18n("None"); // user visible name iSection = ""; m_priority = 0; iHidden = false; + m_additionalData.insert( "none", new HighlightPropertyBag ); + m_additionalData["none"]->deliminator = stdDeliminator; + m_additionalData["none"]->wordWrapDeliminator = stdDeliminator; + m_hlIndex[0] = "none"; } else { iName = def->name; iNameTranslated = def->nameTranslated; iSection = def->section; iHidden = def->hidden; iWildcards = def->extension; iMimetypes = def->mimetype; identifier = def->identifier; iVersion=def->version; iAuthor=def->author; iLicense=def->license; m_priority=def->priority.toInt(); } deliminator = stdDeliminator; } KateHighlighting::~KateHighlighting() { for (uint i=0; i < m_contexts.size(); ++i) delete m_contexts[i]; } void KateHighlighting::generateContextStack(int *ctxNum, int ctx, QMemArray* ctxs, int *prevLine) { //kdDebug(13010)<= 0) { (*ctxNum) = ctx; ctxs->resize (ctxs->size()+1, QGArray::SpeedOptim); (*ctxs)[ctxs->size()-1]=(*ctxNum); return; } else { if (ctx == -1) { (*ctxNum)=( (ctxs->isEmpty() ) ? 0 : (*ctxs)[ctxs->size()-1]); } else { int size = ctxs->size() + ctx + 1; if (size > 0) { ctxs->resize (size, QGArray::SpeedOptim); (*ctxNum)=(*ctxs)[size-1]; } else { ctxs->resize (0, QGArray::SpeedOptim); (*ctxNum)=0; } ctx = 0; if ((*prevLine) >= (int)(ctxs->size()-1)) { *prevLine=ctxs->size()-1; if ( ctxs->isEmpty() ) return; KateHlContext *c = contextNum((*ctxs)[ctxs->size()-1]); if (c && (c->ctx != -1)) { //kdDebug(13010)<<"PrevLine > size()-1 and ctx!=-1)"<ctx; continue; } } } return; } } } /** * Creates a new dynamic context or reuse an old one if it has already been created. */ int KateHighlighting::makeDynamicContext(KateHlContext *model, const QStringList *args) { QPair key(model, args->front()); short value; if (dynamicCtxs.contains(key)) value = dynamicCtxs[key]; else { kdDebug () << "new stuff: " << startctx << endl; KateHlContext *newctx = model->clone(args); m_contexts.push_back (newctx); value = startctx++; dynamicCtxs[key] = value; KateHlManager::self()->incDynamicCtxs(); } // kdDebug(13010) << "Dynamic context: using context #" << value << " (for model " << model << " with args " << *args << ")" << endl; return value; } /** * Drop all dynamic contexts. Shall be called with extreme care, and shall be immediatly * followed by a full HL invalidation. */ void KateHighlighting::dropDynamicContexts() { for (uint i=base_startctx; i < m_contexts.size(); ++i) delete m_contexts[i]; m_contexts.resize (base_startctx); dynamicCtxs.clear(); startctx = base_startctx; } /** * Parse the text and fill in the context array and folding list array * * @param prevLine The previous line, the context array is picked up from that if present. * @param textLine The text line to parse * @param foldinglist will be filled * @param ctxChanged will be set to reflect if the context changed */ void KateHighlighting::doHighlight ( KateTextLine *prevLine, KateTextLine *textLine, QMemArray* foldingList, bool *ctxChanged ) { if (!textLine) return; if (noHl) { if (textLine->length() > 0) memset (textLine->attributes(), 0, textLine->length()); return; } // duplicate the ctx stack, only once ! QMemArray ctx; ctx.duplicate (prevLine->ctxArray()); int ctxNum = 0; int previousLine = -1; KateHlContext *context; if (ctx.isEmpty()) { // If the stack is empty, we assume to be in Context 0 (Normal) context = contextNum(ctxNum); } else { // There does an old context stack exist -> find the context at the line start ctxNum = ctx[ctx.size()-1]; //context ID of the last character in the previous line //kdDebug(13010) << "\t\tctxNum = " << ctxNum << " contextList[ctxNum] = " << contextList[ctxNum] << endl; // ellis //if (lineContinue) kdDebug(13010)<hlLineContinue()) { prevLine--; } else { generateContextStack(&ctxNum, context->ctx, &ctx, &previousLine); //get stack ID to use if (!(context = contextNum(ctxNum))) context = contextNum(0); } //kdDebug(13010)<<"test1-2-1-text4"<string(); const int len = textLine->length(); // calc at which char the first char occurs, set it to lenght of line if never const int firstChar = textLine->firstChar(); const int startNonSpace = (firstChar == -1) ? len : firstChar; // last found item KateHlItem *item = 0; // loop over the line, offset gives current offset int offset = 0; while (offset < len) { bool anItemMatched = false; bool standardStartEnableDetermined = false; bool customStartEnableDetermined = false; uint index = 0; for (item = context->items.empty() ? 0 : context->items[0]; item; item = (++index < context->items.size()) ? context->items[index] : 0 ) { // does we only match if we are firstNonSpace? if (item->firstNonSpace && (offset > startNonSpace)) continue; // have we a column specified? if yes, only match at this column if ((item->column != -1) && (item->column != offset)) continue; if (!item->alwaysStartEnable) { if (item->customStartEnable) { - if (customStartEnableDetermined || kateInsideString (m_additionalData[hlKeyForContext( ctxNum )][Deliminator], lastChar)) + if (customStartEnableDetermined || kateInsideString (m_additionalData[context->hlId]->deliminator, lastChar)) customStartEnableDetermined = true; else continue; } else { if (standardStartEnableDetermined || kateInsideString (stdDeliminator, lastChar)) standardStartEnableDetermined = true; else continue; } } int offset2 = item->checkHgl(text, offset, len-offset); if (offset2 <= offset) continue; if (item->region2) { // kdDebug(13010)<region2)<isEmpty() && ((item->region2 < 0) && (*foldingList)[foldingList->size()-2] == -item->region2 ) ) { foldingList->resize (foldingList->size()-2, QGArray::SpeedOptim); } else { foldingList->resize (foldingList->size()+2, QGArray::SpeedOptim); (*foldingList)[foldingList->size()-2] = (uint)item->region2; if (item->region2<0) //check not really needed yet (*foldingList)[foldingList->size()-1] = offset2; else (*foldingList)[foldingList->size()-1] = offset; } } if (item->region) { // kdDebug(13010)<region)<isEmpty() && ((item->region < 0) && (*foldingList)[foldingList->size()-1] == -item->region ) ) { foldingList->resize (foldingList->size()-1, QGArray::SpeedOptim); } else*/ { foldingList->resize (foldingList->size()+2, QGArray::SpeedOptim); (*foldingList)[foldingList->size()-2] = item->region; if (item->region<0) //check not really needed yet (*foldingList)[foldingList->size()-1] = offset2; else (*foldingList)[foldingList->size()-1] = offset; } } // regenerate context stack if needed if (item->ctx != -1) { generateContextStack (&ctxNum, item->ctx, &ctx, &previousLine); context = contextNum(ctxNum); } // dynamic context: substitute the model with an 'instance' if (context->dynamic) { QStringList *lst = item->capturedTexts(); if (lst != 0) { // Replace the top of the stack and the current context int newctx = makeDynamicContext(context, lst); if (ctx.size() > 0) ctx[ctx.size() - 1] = newctx; ctxNum = newctx; context = contextNum(ctxNum); } delete lst; } // dominik: look ahead w/o changing offset? if (!item->lookAhead) { if (offset2 > len) offset2 = len; // even set attributes ;) memset ( textLine->attributes()+offset , item->onlyConsume ? context->attr : item->attr , len-offset); offset = offset2; lastChar = text[offset-1]; } anItemMatched = true; break; } // something matched, continue loop if (anItemMatched) continue; // nothing found: set attribute of one char // anders: unless this context does not want that! if ( context->fallthrough ) { // set context to context->ftctx. generateContextStack(&ctxNum, context->ftctx, &ctx, &previousLine); //regenerate context stack context=contextNum(ctxNum); //kdDebug(13010)<<"context num after fallthrough at col "<attributes() + offset) = context->attr; lastChar = text[offset]; offset++; } } // has the context stack changed ? if (ctx == textLine->ctxArray()) { if (ctxChanged) (*ctxChanged) = false; } else { if (ctxChanged) (*ctxChanged) = true; // assign ctx stack ! textLine->setContext(ctx); } // write hl continue flag textLine->setHlLineContinue (item && item->lineContinue()); } void KateHighlighting::loadWildcards() { KConfig *config = KateHlManager::self()->getKConfig(); config->setGroup("Highlighting " + iName); QString extensionString = config->readEntry("Wildcards", iWildcards); if (extensionSource != extensionString) { regexpExtensions.clear(); plainExtensions.clear(); extensionSource = extensionString; static QRegExp sep("\\s*;\\s*"); QStringList l = QStringList::split( sep, extensionSource ); static QRegExp boringExpression("\\*\\.[\\d\\w]+"); for( QStringList::Iterator it = l.begin(); it != l.end(); ++it ) if (boringExpression.exactMatch(*it)) plainExtensions.append((*it).mid(1)); else regexpExtensions.append(QRegExp((*it), true, true)); } } QValueList& KateHighlighting::getRegexpExtensions() { return regexpExtensions; } QStringList& KateHighlighting::getPlainExtensions() { return plainExtensions; } QString KateHighlighting::getMimetypes() { KConfig *config = KateHlManager::self()->getKConfig(); config->setGroup("Highlighting " + iName); return config->readEntry("Mimetypes", iMimetypes); } int KateHighlighting::priority() { KConfig *config = KateHlManager::self()->getKConfig(); config->setGroup("Highlighting " + iName); return config->readNumEntry("Priority", m_priority); } KateHlData *KateHighlighting::getData() { KConfig *config = KateHlManager::self()->getKConfig(); config->setGroup("Highlighting " + iName); KateHlData *hlData = new KateHlData( config->readEntry("Wildcards", iWildcards), config->readEntry("Mimetypes", iMimetypes), config->readEntry("Identifier", identifier), config->readNumEntry("Priority", m_priority)); return hlData; } void KateHighlighting::setData(KateHlData *hlData) { KConfig *config = KateHlManager::self()->getKConfig(); config->setGroup("Highlighting " + iName); config->writeEntry("Wildcards",hlData->wildcards); config->writeEntry("Mimetypes",hlData->mimetypes); config->writeEntry("Priority",hlData->priority); } void KateHighlighting::getKateHlItemDataList (uint schema, KateHlItemDataList &list) { KConfig *config = KateHlManager::self()->getKConfig(); config->setGroup("Highlighting " + iName + " - Schema " + KateFactory::self()->schemaManager()->name(schema)); list.clear(); createKateHlItemData(list); for (KateHlItemData *p = list.first(); p != 0L; p = list.next()) { QStringList s = config->readListEntry(p->name); // kdDebug(13010)<name<0) { while(s.count()<9) s<<""; p->clear(); QString tmp=s[0]; if (!tmp.isEmpty()) p->defStyleNum=tmp.toInt(); QRgb col; tmp=s[1]; if (!tmp.isEmpty()) { col=tmp.toUInt(0,16); p->setTextColor(col); } tmp=s[2]; if (!tmp.isEmpty()) { col=tmp.toUInt(0,16); p->setSelectedTextColor(col); } tmp=s[3]; if (!tmp.isEmpty()) p->setBold(tmp!="0"); tmp=s[4]; if (!tmp.isEmpty()) p->setItalic(tmp!="0"); tmp=s[5]; if (!tmp.isEmpty()) p->setStrikeOut(tmp!="0"); tmp=s[6]; if (!tmp.isEmpty()) p->setUnderline(tmp!="0"); tmp=s[7]; if (!tmp.isEmpty()) { col=tmp.toUInt(0,16); p->setBGColor(col); } tmp=s[8]; if (!tmp.isEmpty()) { col=tmp.toUInt(0,16); p->setSelectedBGColor(col); } } } } /** * Saves the KateHlData attribute definitions to the config file. * * @param schema The id of the schema group to save * @param list KateHlItemDataList containing the data to be used */ void KateHighlighting::setKateHlItemDataList(uint schema, KateHlItemDataList &list) { KConfig *config = KateHlManager::self()->getKConfig(); config->setGroup("Highlighting " + iName + " - Schema " + KateFactory::self()->schemaManager()->name(schema)); QStringList settings; for (KateHlItemData *p = list.first(); p != 0L; p = list.next()) { settings.clear(); settings<defStyleNum,10); settings<<(p->itemSet(KateAttribute::TextColor)?QString::number(p->textColor().rgb(),16):""); settings<<(p->itemSet(KateAttribute::SelectedTextColor)?QString::number(p->selectedTextColor().rgb(),16):""); settings<<(p->itemSet(KateAttribute::Weight)?(p->bold()?"1":"0"):""); settings<<(p->itemSet(KateAttribute::Italic)?(p->italic()?"1":"0"):""); settings<<(p->itemSet(KateAttribute::StrikeOut)?(p->strikeOut()?"1":"0"):""); settings<<(p->itemSet(KateAttribute::Underline)?(p->underline()?"1":"0"):""); settings<<(p->itemSet(KateAttribute::BGColor)?QString::number(p->bgColor().rgb(),16):""); settings<<(p->itemSet(KateAttribute::SelectedBGColor)?QString::number(p->selectedBGColor().rgb(),16):""); settings<<"---"; config->writeEntry(p->name,settings); } } /** * Increase the usage count, and trigger initialization if needed. */ void KateHighlighting::use() { if (refCount == 0) init(); refCount++; } /** * Decrease the usage count, and trigger cleanup if needed. */ void KateHighlighting::release() { refCount--; if (refCount == 0) done(); } /** * Initialize a context for the first time. */ void KateHighlighting::init() { if (noHl) return; m_contexts.clear (); makeContextList(); } /** * If the there is no document using the highlighting style free the complete * context structure. */ void KateHighlighting::done() { if (noHl) return; m_contexts.clear (); internalIDList.clear(); } /** * KateHighlighting - createKateHlItemData * This function reads the itemData entries from the config file, which specifies the * default attribute styles for matched items/contexts. * * @param list A reference to the internal list containing the parsed default config */ void KateHighlighting::createKateHlItemData(KateHlItemDataList &list) { // If no highlighting is selected we need only one default. if (noHl) { list.append(new KateHlItemData(i18n("Normal Text"), KateHlItemData::dsNormal)); return; } // If the internal list isn't already available read the config file if (internalIDList.isEmpty()) makeContextList(); list=internalIDList; } /** * Adds the styles of the currently parsed highlight to the itemdata list */ void KateHighlighting::addToKateHlItemDataList() { //Tell the syntax document class which file we want to parse and which data group KateHlManager::self()->syntax->setIdentifier(buildIdentifier); KateSyntaxContextData *data = KateHlManager::self()->syntax->getGroupInfo("highlighting","itemData"); //begin with the real parsing while (KateHlManager::self()->syntax->nextGroup(data)) { // read all attributes QString color = KateHlManager::self()->syntax->groupData(data,QString("color")); QString selColor = KateHlManager::self()->syntax->groupData(data,QString("selColor")); QString bold = KateHlManager::self()->syntax->groupData(data,QString("bold")); QString italic = KateHlManager::self()->syntax->groupData(data,QString("italic")); QString underline = KateHlManager::self()->syntax->groupData(data,QString("underline")); QString strikeOut = KateHlManager::self()->syntax->groupData(data,QString("strikeOut")); QString bgColor = KateHlManager::self()->syntax->groupData(data,QString("backgroundColor")); QString selBgColor = KateHlManager::self()->syntax->groupData(data,QString("selBackgroundColor")); KateHlItemData* newData = new KateHlItemData( buildPrefix+KateHlManager::self()->syntax->groupData(data,QString("name")).simplifyWhiteSpace(), getDefStyleNum(KateHlManager::self()->syntax->groupData(data,QString("defStyleNum")))); /* here the custom style overrides are specified, if needed */ if (!color.isEmpty()) newData->setTextColor(QColor(color)); if (!selColor.isEmpty()) newData->setSelectedTextColor(QColor(selColor)); if (!bold.isEmpty()) newData->setBold( IS_TRUE(bold) ); if (!italic.isEmpty()) newData->setItalic( IS_TRUE(italic) ); // new attributes for the new rendering view if (!underline.isEmpty()) newData->setUnderline( IS_TRUE(underline) ); if (!strikeOut.isEmpty()) newData->setStrikeOut( IS_TRUE(strikeOut) ); if (!bgColor.isEmpty()) newData->setBGColor(QColor(bgColor)); if (!selBgColor.isEmpty()) newData->setSelectedBGColor(QColor(selBgColor)); internalIDList.append(newData); } //clean up if (data) KateHlManager::self()->syntax->freeGroupInfo(data); } /** * KateHighlighting - lookupAttrName * This function is a helper for makeContextList and createKateHlItem. It looks the given * attribute name in the itemData list up and returns it's index * * @param name the attribute name to lookup * @param iDl the list containing all available attributes * * @return The index of the attribute, or 0 if the attribute isn't found */ int KateHighlighting::lookupAttrName(const QString& name, KateHlItemDataList &iDl) { for (uint i = 0; i < iDl.count(); i++) if (iDl.at(i)->name == buildPrefix+name) return i; kdDebug(13010)<<"Couldn't resolve itemDataName:"<index translation * @param RegionList list of code folding region names * @param ContextNameList list of context names * * @return A pointer to the newly created item object */ KateHlItem *KateHighlighting::createKateHlItem(KateSyntaxContextData *data, KateHlItemDataList &iDl,QStringList *RegionList, QStringList *ContextNameList) { // No highlighting -> exit if (noHl) return 0; // get the (tagname) itemd type QString dataname=KateHlManager::self()->syntax->groupItemData(data,QString("")); // code folding region handling: QString beginRegionStr=KateHlManager::self()->syntax->groupItemData(data,QString("beginRegion")); QString endRegionStr=KateHlManager::self()->syntax->groupItemData(data,QString("endRegion")); signed char regionId=0; signed char regionId2=0; if (!beginRegionStr.isEmpty()) { regionId = RegionList->findIndex(beginRegionStr); if (regionId==-1) // if the region name doesn't already exist, add it to the list { (*RegionList)<findIndex(beginRegionStr); } regionId++; kdDebug () << "########### BEG REG: " << beginRegionStr << " NUM: " << regionId << endl; } if (!endRegionStr.isEmpty()) { regionId2 = RegionList->findIndex(endRegionStr); if (regionId2==-1) // if the region name doesn't already exist, add it to the list { (*RegionList)<findIndex(endRegionStr); } regionId2 = -regionId2 - 1; kdDebug () << "########### END REG: " << endRegionStr << " NUM: " << regionId2 << endl; } int attr = 0; QString tmpAttr=KateHlManager::self()->syntax->groupItemData(data,QString("attribute")).simplifyWhiteSpace(); bool onlyConsume = tmpAttr.isEmpty(); // only relevant for non consumer if (!onlyConsume) { if (QString("%1").arg(tmpAttr.toInt())==tmpAttr) { errorsAndWarnings+=i18n("%1: Deprecated syntax. Attribute (%2) not addressed by symbolic name
"). arg(buildIdentifier).arg(tmpAttr); attr=tmpAttr.toInt(); } else attr=lookupAttrName(tmpAttr,iDl); } // Info about context switch int context = -1; QString unresolvedContext; QString tmpcontext=KateHlManager::self()->syntax->groupItemData(data,QString("context")); if (!tmpcontext.isEmpty()) context=getIdFromString(ContextNameList, tmpcontext,unresolvedContext); // Get the char parameter (eg DetectChar) char chr; if (! KateHlManager::self()->syntax->groupItemData(data,QString("char")).isEmpty()) chr= (KateHlManager::self()->syntax->groupItemData(data,QString("char")).latin1())[0]; else chr=0; // Get the String parameter (eg. StringDetect) QString stringdata=KateHlManager::self()->syntax->groupItemData(data,QString("String")); // Get a second char parameter (char1) (eg Detect2Chars) char chr1; if (! KateHlManager::self()->syntax->groupItemData(data,QString("char1")).isEmpty()) chr1= (KateHlManager::self()->syntax->groupItemData(data,QString("char1")).latin1())[0]; else chr1=0; // Will be removed eventuall. Atm used for StringDetect and RegExp bool insensitive = IS_TRUE( KateHlManager::self()->syntax->groupItemData(data,QString("insensitive")) ); // for regexp only bool minimal = IS_TRUE( KateHlManager::self()->syntax->groupItemData(data,QString("minimal")) ); // dominik: look ahead and do not change offset. so we can change contexts w/o changing offset1. bool lookAhead = IS_TRUE( KateHlManager::self()->syntax->groupItemData(data,QString("lookAhead")) ); bool dynamic= IS_TRUE(KateHlManager::self()->syntax->groupItemData(data,QString("dynamic")) ); bool firstNonSpace = IS_TRUE(KateHlManager::self()->syntax->groupItemData(data,QString("firstNonSpace")) ); int column = -1; QString colStr = KateHlManager::self()->syntax->groupItemData(data,QString("column")); if (!colStr.isEmpty()) column = colStr.toInt(); //Create the item corresponding to it's type and set it's parameters KateHlItem *tmpItem; if (dataname=="keyword") { KateHlKeyword *keyword=new KateHlKeyword(attr,context,regionId,regionId2,casesensitive, deliminator); //Get the entries for the keyword lookup list keyword->addList(KateHlManager::self()->syntax->finddata("highlighting",stringdata)); tmpItem=keyword; } else if (dataname=="Float") tmpItem= (new KateHlFloat(attr,context,regionId,regionId2)); else if (dataname=="Int") tmpItem=(new KateHlInt(attr,context,regionId,regionId2)); else if (dataname=="DetectChar") tmpItem=(new KateHlCharDetect(attr,context,regionId,regionId2,chr)); else if (dataname=="Detect2Chars") tmpItem=(new KateHl2CharDetect(attr,context,regionId,regionId2,chr,chr1)); else if (dataname=="RangeDetect") tmpItem=(new KateHlRangeDetect(attr,context,regionId,regionId2, chr, chr1)); else if (dataname=="LineContinue") tmpItem=(new KateHlLineContinue(attr,context,regionId,regionId2)); else if (dataname=="StringDetect") tmpItem=(new KateHlStringDetect(attr,context,regionId,regionId2,stringdata,insensitive)); else if (dataname=="AnyChar") tmpItem=(new KateHlAnyChar(attr,context,regionId,regionId2,stringdata)); else if (dataname=="RegExpr") tmpItem=(new KateHlRegExpr(attr,context,regionId,regionId2,stringdata, insensitive, minimal)); else if (dataname=="HlCChar") tmpItem= ( new KateHlCChar(attr,context,regionId,regionId2)); else if (dataname=="HlCHex") tmpItem= (new KateHlCHex(attr,context,regionId,regionId2)); else if (dataname=="HlCOct") tmpItem= (new KateHlCOct(attr,context,regionId,regionId2)); else if (dataname=="HlCFloat") tmpItem= (new KateHlCFloat(attr,context,regionId,regionId2)); else if (dataname=="HlCStringChar") tmpItem= (new KateHlCStringChar(attr,context,regionId,regionId2)); else if (dataname=="DetectSpaces") tmpItem= (new KateHlDetectSpaces(attr,context,regionId,regionId2)); else if (dataname=="DetectIdentifier") tmpItem= (new KateHlDetectIdentifier(attr,context,regionId,regionId2)); else { // oops, unknown type. Perhaps a spelling error in the xml file return 0; } // set lookAhead & dynamic properties tmpItem->lookAhead = lookAhead; tmpItem->dynamic = dynamic; tmpItem->firstNonSpace = firstNonSpace; tmpItem->column = column; tmpItem->onlyConsume = onlyConsume; if (!unresolvedContext.isEmpty()) { unresolvedContextReferences.insert(&(tmpItem->ctx),unresolvedContext); } return tmpItem; } -int KateHighlighting::hlKeyForList( const IntList *list, int attrib ) const +QString KateHighlighting::hlKeyForAttrib( int i ) const { int k = 0; - IntList::const_iterator it = list->constEnd(); - while ( it != list->constBegin() ) + QMap::const_iterator it = m_hlIndex.constEnd(); + while ( it != m_hlIndex.constBegin() ) { --it; - k = (*it); - if ( attrib >= k ) + k = it.key(); + if ( i >= k ) break; } - return k; + return it.data(); } bool KateHighlighting::isInWord( QChar c, int attrib ) const { static const QString& sq = KGlobal::staticQString(" \"'"); - return getCommentString(4, attrib).find(c) < 0 && sq.find(c) < 0; + return m_additionalData[ hlKeyForAttrib( attrib ) ]->deliminator.find(c) < 0 && sq.find(c) < 0; } bool KateHighlighting::canBreakAt( QChar c, int attrib ) const { static const QString& sq = KGlobal::staticQString("\"'"); - return (getCommentString(5, attrib).find(c) != -1) && (sq.find(c) == -1); + return (m_additionalData[ hlKeyForAttrib( attrib ) ]->wordWrapDeliminator.find(c) != -1) && (sq.find(c) == -1); } signed char KateHighlighting::commentRegion(int attr) const { - int k = hlKeyForAttrib( attr ); - QString commentRegion=m_additionalData[k][MultiLineRegion]; + QString commentRegion=m_additionalData[ hlKeyForAttrib( attr ) ]->multiLineRegion; return (commentRegion.isEmpty()?0:(commentRegion.toShort())); } bool KateHighlighting::canComment( int startAttrib, int endAttrib ) const { - int k = hlKeyForAttrib( startAttrib ); + QString k = hlKeyForAttrib( startAttrib ); return ( k == hlKeyForAttrib( endAttrib ) && - ( ( !m_additionalData[k][Start].isEmpty() && !m_additionalData[k][End].isEmpty() ) || - ! m_additionalData[k][SingleLine].isEmpty() ) ); -} - -QString KateHighlighting::getCommentString( int which, int attrib ) const -{ - if ( noHl ) - return which == 4 ? stdDeliminator : ""; - - int k = hlKeyForAttrib( attrib ); - const QStringList& lst = m_additionalData[k]; - return lst.isEmpty() ? QString::null : lst[which]; + ( ( !m_additionalData[k]->multiLineCommentStart.isEmpty() && !m_additionalData[k]->multiLineCommentEnd.isEmpty() ) || + ! m_additionalData[k]->singleLineCommentMarker.isEmpty() ) ); } QString KateHighlighting::getCommentStart( int attrib ) const { - return getCommentString( Start, attrib ); + return m_additionalData[ hlKeyForAttrib( attrib) ]->multiLineCommentStart; } QString KateHighlighting::getCommentEnd( int attrib ) const { - return getCommentString( End, attrib ); + return m_additionalData[ hlKeyForAttrib( attrib ) ]->multiLineCommentEnd; } QString KateHighlighting::getCommentSingleLineStart( int attrib ) const { - return getCommentString( SingleLine, attrib ); + return m_additionalData[ hlKeyForAttrib( attrib) ]->singleLineCommentMarker; } /** * Helper for makeContextList. It parses the xml file for * information, how single or multi line comments are marked - * - * @return a stringlist containing the comment marker strings in the order - * multilineCommentStart, multilineCommentEnd, singlelineCommentMarker */ -QStringList KateHighlighting::readCommentConfig() +void KateHighlighting::readCommentConfig() { KateHlManager::self()->syntax->setIdentifier(buildIdentifier); KateSyntaxContextData *data=KateHlManager::self()->syntax->getGroupInfo("general","comment"); QString cmlStart, cmlEnd, cmlRegion, cslStart ; if (data) { while (KateHlManager::self()->syntax->nextGroup(data)) { if (KateHlManager::self()->syntax->groupData(data,"name")=="singleLine") cslStart=KateHlManager::self()->syntax->groupData(data,"start"); if (KateHlManager::self()->syntax->groupData(data,"name")=="multiLine") { cmlStart=KateHlManager::self()->syntax->groupData(data,"start"); cmlEnd=KateHlManager::self()->syntax->groupData(data,"end"); cmlRegion=KateHlManager::self()->syntax->groupData(data,"region"); } } KateHlManager::self()->syntax->freeGroupInfo(data); } else { cslStart = ""; cmlStart = ""; cmlEnd = ""; cmlRegion = ""; } - QStringList res; - res << cmlStart << cmlEnd <singleLineCommentMarker = cslStart; + m_additionalData[buildIdentifier]->multiLineCommentStart = cmlStart; + m_additionalData[buildIdentifier]->multiLineCommentEnd = cmlEnd; + m_additionalData[buildIdentifier]->multiLineRegion = cmlRegion; } /** * Helper for makeContextList. It parses the xml file for information, * if keywords should be treated case(in)sensitive and creates the keyword * delimiter list. Which is the default list, without any given weak deliminiators - * - * @return the computed delimiter string. */ -QString KateHighlighting::readGlobalKeywordConfig() +void KateHighlighting::readGlobalKeywordConfig() { deliminator = stdDeliminator; // Tell the syntax document class which file we want to parse kdDebug(13010)<<"readGlobalKeywordConfig:BEGIN"<syntax->setIdentifier(buildIdentifier); KateSyntaxContextData *data = KateHlManager::self()->syntax->getConfig("general","keywords"); if (data) { kdDebug(13010)<<"Found global keyword config"<syntax->groupItemData(data,QString("casesensitive"))!="0") casesensitive=true; else casesensitive=false; //get the weak deliminators weakDeliminator=(KateHlManager::self()->syntax->groupItemData(data,QString("weakDeliminator"))); kdDebug(13010)<<"weak delimiters are: "< -1) deliminator.remove (f, 1); } QString addDelim = (KateHlManager::self()->syntax->groupItemData(data,QString("additionalDeliminator"))); if (!addDelim.isEmpty()) deliminator=deliminator+addDelim; KateHlManager::self()->syntax->freeGroupInfo(data); } else { //Default values casesensitive=true; weakDeliminator=QString(""); } kdDebug(13010)<<"readGlobalKeywordConfig:END"<deliminator = deliminator; } /** * Helper for makeContextList. It parses the xml file for any wordwrap deliminators, characters * at which line can be broken. In case no keyword tag is found in the xml file, * the wordwrap deliminators list defaults to the standard denominators. In case a keyword tag * is defined, but no wordWrapDeliminator attribute is specified, the deliminator list as computed * in readGlobalKeywordConfig is used. * * @return the computed delimiter string. */ -QString KateHighlighting::readWordWrapConfig() +void KateHighlighting::readWordWrapConfig() { // Tell the syntax document class which file we want to parse kdDebug(13010)<<"readWordWrapConfig:BEGIN"<syntax->setIdentifier(buildIdentifier); KateSyntaxContextData *data = KateHlManager::self()->syntax->getConfig("general","keywords"); QString wordWrapDeliminator = stdDeliminator; if (data) { kdDebug(13010)<<"Found global keyword config"<syntax->groupItemData(data,QString("wordWrapDeliminator"))); //when no wordWrapDeliminator is defined use the deliminator list if ( wordWrapDeliminator.length() == 0 ) wordWrapDeliminator = deliminator; kdDebug(13010) << "word wrap deliminators are " << wordWrapDeliminator << endl; KateHlManager::self()->syntax->freeGroupInfo(data); } kdDebug(13010)<<"readWordWrapConfig:END"<wordWrapDeliminator = wordWrapDeliminator; } void KateHighlighting::readIndentationConfig() { m_indentation = ""; KateHlManager::self()->syntax->setIdentifier(buildIdentifier); KateSyntaxContextData *data = KateHlManager::self()->syntax->getConfig("general","indentation"); if (data) { m_indentation = (KateHlManager::self()->syntax->groupItemData(data,QString("mode"))); KateHlManager::self()->syntax->freeGroupInfo(data); } } void KateHighlighting::readFoldingConfig() { // Tell the syntax document class which file we want to parse kdDebug(13010)<<"readfoldignConfig:BEGIN"<syntax->setIdentifier(buildIdentifier); KateSyntaxContextData *data = KateHlManager::self()->syntax->getConfig("general","folding"); if (data) { kdDebug(13010)<<"Found global keyword config"<syntax->groupItemData(data,QString("indentationsensitive"))!="1") m_foldingIndentationSensitive=false; else m_foldingIndentationSensitive=true; KateHlManager::self()->syntax->freeGroupInfo(data); } else { //Default values m_foldingIndentationSensitive = false; } kdDebug(13010)<<"readfoldingConfig:END"<clear(); KateHlManager::self()->syntax->setIdentifier(buildIdentifier); KateSyntaxContextData *data=KateHlManager::self()->syntax->getGroupInfo("highlighting","context"); int id=ctx0; if (data) { while (KateHlManager::self()->syntax->nextGroup(data)) { QString tmpAttr=KateHlManager::self()->syntax->groupData(data,QString("name")).simplifyWhiteSpace(); if (tmpAttr.isEmpty()) { tmpAttr=QString("!KATE_INTERNAL_DUMMY! %1").arg(id); errorsAndWarnings +=i18n("%1: Deprecated syntax. Context %2 has no symbolic name
").arg(buildIdentifier).arg(id-ctx0); } else tmpAttr=buildPrefix+tmpAttr; (*ContextNameList)<syntax->freeGroupInfo(data); } kdDebug(13010)<<"creatingContextNameList:END"<findIndex(buildPrefix+tmpLineEndContext); if (context==-1) { context=tmpLineEndContext.toInt(); errorsAndWarnings+=i18n( "%1:Deprecated syntax. Context %2 not addressed by a symbolic name" ).arg(buildIdentifier).arg(tmpLineEndContext); } //#warning restructure this the name list storage. // context=context+buildContext0Offset; } return context; } /** * The most important initialization function for each highlighting. It's called * each time a document gets a highlighting style assigned. parses the xml file * and creates a corresponding internal structure */ void KateHighlighting::makeContextList() { if (noHl) // if this a highlighting for "normal texts" only, tere is no need for a context list creation return; embeddedHls.clear(); unresolvedContextReferences.clear(); RegionList.clear(); ContextNameList.clear(); // prepare list creation. To reuse as much code as possible handle this // highlighting the same way as embedded onces embeddedHls.insert(iName,KateEmbeddedHlInfo()); bool something_changed; // the context "0" id is 0 for this hl, all embedded context "0"s have offsets startctx=base_startctx=0; // inform everybody that we are building the highlighting contexts and itemlists building=true; do { kdDebug(13010)<<"**************** Outter loop in make ContextList"<identifierForName(it.key()); // all others have to be looked up kdDebug(13010)<<"Location is:"<< identifierToUse<incCtx==-1) // context unresolved ? { if ((*it)->incCtxN.isEmpty()) { // no context name given, and no valid context id set, so this item is going to be removed KateHlIncludeRules::iterator it1=it; ++it1; delete (*it); includeRules.remove(it); it=it1; } else { // resolve name to id (*it)->incCtx=getIdFromString(&ContextNameList,(*it)->incCtxN,dummy); kdDebug(13010)<<"Resolved "<<(*it)->incCtxN<< " to "<<(*it)->incCtx<<" for include rule"<1->2->3->1 while (!includeRules.isEmpty()) handleKateHlIncludeRulesRecursive(includeRules.begin(),&includeRules); } void KateHighlighting::handleKateHlIncludeRulesRecursive(KateHlIncludeRules::iterator it, KateHlIncludeRules *list) { if (it==list->end()) return; //invalid iterator, shouldn't happen, but better have a rule prepared ;) KateHlIncludeRules::iterator it1=it; int ctx=(*it1)->ctx; // find the last entry for the given context in the KateHlIncludeRules list // this is need if one context includes more than one. This saves us from // updating all insert positions: // eg: context 0: // pos 3 - include context 2 // pos 5 - include context 3 // During the building of the includeRules list the items are inserted in // ascending order, now we need it descending to make our life easier. while ((it!=list->end()) && ((*it)->ctx==ctx)) { it1=it; ++it; } // iterate over each include rule for the context the function has been called for. while ((it1!=list->end()) && ((*it1)->ctx==ctx)) { int ctx1=(*it1)->incCtx; //let's see, if the the included context includes other contexts for (KateHlIncludeRules::iterator it2=list->begin();it2!=list->end();++it2) { if ((*it2)->ctx==ctx1) { //yes it does, so first handle that include rules, since we want to // include those subincludes too handleKateHlIncludeRulesRecursive(it2,list); break; } } // if the context we want to include had sub includes, they are already inserted there. KateHlContext *dest=m_contexts[ctx]; KateHlContext *src=m_contexts[ctx1]; // kdDebug(3010)<<"linking included rules from "<includeAttrib ) dest->attr = src->attr; // insert the included context's rules starting at position p int p=(*it1)->pos; // remember some stuff int oldLen = dest->items.size(); uint itemsToInsert = src->items.size(); // resize target dest->items.resize (oldLen + itemsToInsert); // move old elements for (int i=oldLen-1; i >= p; --i) dest->items[i+itemsToInsert] = dest->items[i]; // insert new stuff for (uint i=0; i < itemsToInsert; ++i ) dest->items[p+i] = src->items[i]; it=it1; //backup the iterator --it1; //move to the next entry, which has to be take care of delete (*it); //free the already handled data structure list->remove(it); // remove it from the list } } /** * Add one highlight to the contextlist. * * @return the number of contexts after this is added. */ int KateHighlighting::addToContextList(const QString &ident, int ctx0) { + kdDebug()<<"=== Adding hl with ident '"<syntax->setIdentifier(ident)) { noHl=true; KMessageBox::information(0L,i18n("Since there has been an error parsing the highlighting description, this highlighting will be disabled")); return 0; } // only read for the own stuff if (identifier == ident) { readIndentationConfig (); } RegionList<<"!KateInternal_TopLevel!"; - // Now save the comment and delimitor data. We associate it with the - // length of internalDataList, so when we look it up for an attrib, - // all the attribs added in a moment will be in the correct range - uint additionalDataIndex=internalIDList.count(); - m_hlIndex.append( additionalDataIndex ); - m_ctxIndex.append(ctx0); + m_hlIndex[internalIDList.count()] = ident; + m_additionalData.insert( ident, new HighlightPropertyBag ); - QStringList additionaldata = readCommentConfig(); - additionaldata << readGlobalKeywordConfig(); - additionaldata << readWordWrapConfig(); + // fill out the propertybag + readCommentConfig(); + readGlobalKeywordConfig(); + readWordWrapConfig(); readFoldingConfig (); - m_additionalData.insert( additionalDataIndex, additionaldata ); - QString ctxName; // This list is needed for the translation of the attribute parameter, // if the itemData name is given instead of the index addToKateHlItemDataList(); KateHlItemDataList iDl = internalIDList; createContextNameList(&ContextNameList,ctx0); kdDebug(13010)<<"Parsing Context structure"<syntax->getGroupInfo("highlighting","context"); uint i=buildContext0Offset; if (data) { while (KateHlManager::self()->syntax->nextGroup(data)) { kdDebug(13010)<<"Found a context in file, building structure now"<syntax->groupData(data,QString("attribute")).simplifyWhiteSpace(); int attr; if (QString("%1").arg(tmpAttr.toInt())==tmpAttr) attr=tmpAttr.toInt(); else attr=lookupAttrName(tmpAttr,iDl); //END - Translation of the attribute parameter ctxName=buildPrefix+KateHlManager::self()->syntax->groupData(data,QString("lineEndContext")).simplifyWhiteSpace(); QString tmpLineEndContext=KateHlManager::self()->syntax->groupData(data,QString("lineEndContext")).simplifyWhiteSpace(); int context; context=getIdFromString(&ContextNameList, tmpLineEndContext,dummy); //BEGIN get fallthrough props bool ft = false; int ftc = 0; // fallthrough context if ( i > 0 ) // fallthrough is not smart in context 0 { QString tmpFt = KateHlManager::self()->syntax->groupData(data, QString("fallthrough") ); if ( IS_TRUE(tmpFt) ) ft = true; if ( ft ) { QString tmpFtc = KateHlManager::self()->syntax->groupData( data, QString("fallthroughContext") ); ftc=getIdFromString(&ContextNameList, tmpFtc,dummy); if (ftc == -1) ftc =0; kdDebug(13010)<<"Setting fall through context (context "<syntax->groupData(data, QString("dynamic") ); if ( tmpDynamic.lower() == "true" || tmpDynamic.toInt() == 1 ) dynamic = true; KateHlContext *ctxNew = new KateHlContext ( + ident, attr, context, (KateHlManager::self()->syntax->groupData(data,QString("lineBeginContext"))).isEmpty()?-1: (KateHlManager::self()->syntax->groupData(data,QString("lineBeginContext"))).toInt(), ft, ftc, dynamic); m_contexts.push_back (ctxNew); kdDebug () << "INDEX: " << i << " LENGTH " << m_contexts.size()-1 << endl; //Let's create all items for the context while (KateHlManager::self()->syntax->nextItem(data)) { // kdDebug(13010)<< "In make Contextlist: Item:"<syntax->groupItemData(data,QString("")); if ( tag == "IncludeRules" ) //if the new item is an Include rule, we have to take special care { QString incCtx = KateHlManager::self()->syntax->groupItemData( data, QString("context")); QString incAttrib = KateHlManager::self()->syntax->groupItemData( data, QString("includeAttrib")); bool includeAttrib = ( incAttrib.lower() == "true" || incAttrib.toInt() == 1 ); // only context refernces of type NAME and ##Name are allowed if (incCtx.startsWith("##") || (!incCtx.startsWith("#"))) { //#stay, #pop is not interesting here if (!incCtx.startsWith("#")) { // a local reference -> just initialize the include rule structure incCtx=buildPrefix+incCtx.simplifyWhiteSpace(); includeRules.append(new KateHlIncludeRule(i,m_contexts[i]->items.count(),incCtx, includeAttrib)); } else { //a cross highlighting reference kdDebug(13010)<<"Cross highlight reference "<items.count(),"",includeAttrib); //use the same way to determine cross hl file references as other items do if (!embeddedHls.contains(incCtx.right(incCtx.length()-2))) embeddedHls.insert(incCtx.right(incCtx.length()-2),KateEmbeddedHlInfo()); unresolvedContextReferences.insert(&(ir->incCtx), incCtx.right(incCtx.length()-2)); includeRules.append(ir); } } continue; } // TODO -- can we remove the block below?? #if 0 QString tag = KateHlManager::self()->syntax->groupKateHlItemData(data,QString("")); if ( tag == "IncludeRules" ) { // attrib context: the index (jowenn, i think using names here would be a cool feat, goes for mentioning the context in any item. a map or dict?) int ctxId = getIdFromString(&ContextNameList, KateHlManager::self()->syntax->groupKateHlItemData( data, QString("context")),dummy); // the index is *required* if ( ctxId > -1) { // we can even reuse rules of 0 if we want to:) kdDebug(13010)<<"makeContextList["<items.first(); c; c = m_contexts[ctxId]->items.next() ) m_contexts[i]->items.append(c); } else kdDebug(13010)<<"Context "<items.append(c); // Not supported completely atm and only one level. Subitems.(all have to be matched to at once) datasub=KateHlManager::self()->syntax->getSubItems(data); bool tmpbool; if (tmpbool=KateHlManager::self()->syntax->nextItem(datasub)) { for (;tmpbool;tmpbool=KateHlManager::self()->syntax->nextItem(datasub)) { c->subItems.resize (c->subItems.size()+1); c->subItems[c->subItems.size()-1] = createKateHlItem(datasub,iDl,&RegionList,&ContextNameList); } } KateHlManager::self()->syntax->freeGroupInfo(datasub); // end of sublevel } } i++; } } KateHlManager::self()->syntax->freeGroupInfo(data); if (RegionList.count()!=1) folding=true; folding = folding || m_foldingIndentationSensitive; //BEGIN Resolve multiline region if possible - QStringList& commentData=m_additionalData[additionalDataIndex]; - if (!commentData[MultiLineRegion].isEmpty()) { - long commentregionid=RegionList.findIndex(commentData[MultiLineRegion]); + if (!m_additionalData[ ident ]->multiLineRegion.isEmpty()) { + long commentregionid=RegionList.findIndex( m_additionalData[ ident ]->multiLineRegion ); if (-1==commentregionid) { - errorsAndWarnings+=i18n("%1: Specified multiline comment region (%2) could not be resolved
").arg(buildIdentifier).arg(commentData[MultiLineRegion]); - commentData[MultiLineRegion]=QString(); + errorsAndWarnings+=i18n("%1: Specified multiline comment region (%2) could not be resolved
").arg(buildIdentifier).arg( m_additionalData[ ident ]->multiLineRegion ); + m_additionalData[ ident ]->multiLineRegion = QString(); kdDebug()<<"ERROR comment region attribute could not be resolved"<multiLineRegion=QString::number(commentregionid+1); + kdDebug()<<"comment region resolved to:"<multiLineRegion< > it( m_attributeArrays ); it.current(); ++it ) { // k, schema correct, let create the data KateAttributeList defaultStyleList; defaultStyleList.setAutoDelete(true); KateHlManager::self()->getDefaults(it.currentKey(), defaultStyleList); KateHlItemDataList itemDataList; getKateHlItemDataList(it.currentKey(), itemDataList); uint nAttribs = itemDataList.count(); QMemArray *array = it.current(); array->resize (nAttribs); for (uint z = 0; z < nAttribs; z++) { KateHlItemData *itemData = itemDataList.at(z); KateAttribute n = *defaultStyleList.at(itemData->defStyleNum); if (itemData && itemData->isSomethingSet()) n += *itemData; array->at(z) = n; } } } QMemArray *KateHighlighting::attributes (uint schema) { QMemArray *array; // found it, allready floating around if ((array = m_attributeArrays[schema])) return array; // ohh, not found, check if valid schema number if (!KateFactory::self()->schemaManager()->validSchema(schema)) { // uhh, not valid :/, stick with normal default schema, it's always there ! return attributes (0); } // k, schema correct, let create the data KateAttributeList defaultStyleList; defaultStyleList.setAutoDelete(true); KateHlManager::self()->getDefaults(schema, defaultStyleList); KateHlItemDataList itemDataList; getKateHlItemDataList(schema, itemDataList); uint nAttribs = itemDataList.count(); array = new QMemArray (nAttribs); for (uint z = 0; z < nAttribs; z++) { KateHlItemData *itemData = itemDataList.at(z); KateAttribute n = *defaultStyleList.at(itemData->defStyleNum); if (itemData && itemData->isSomethingSet()) n += *itemData; array->at(z) = n; } m_attributeArrays.insert(schema, array); return array; } void KateHighlighting::getKateHlItemDataListCopy (uint schema, KateHlItemDataList &outlist) { KateHlItemDataList itemDataList; getKateHlItemDataList(schema, itemDataList); outlist.clear (); outlist.setAutoDelete (true); for (uint z=0; z < itemDataList.count(); z++) outlist.append (new KateHlItemData (*itemDataList.at(z))); } //END //BEGIN KateHlManager KateHlManager::KateHlManager() : QObject() , m_config ("katesyntaxhighlightingrc", false, false) , commonSuffixes (QStringList::split(";", ".orig;.new;~;.bak;.BAK")) , syntax (new KateSyntaxDocument()) , dynamicCtxsCount(0) , forceNoDCReset(false) { hlList.setAutoDelete(true); hlDict.setAutoDelete(false); KateSyntaxModeList modeList = syntax->modeList(); for (uint i=0; i < modeList.count(); i++) { KateHighlighting *hl = new KateHighlighting(modeList[i]); uint insert = 0; for (; insert <= hlList.count(); insert++) { if (insert == hlList.count()) break; if ( QString(hlList.at(insert)->section() + hlList.at(insert)->nameTranslated()).lower() > QString(hl->section() + hl->nameTranslated()).lower() ) break; } hlList.insert (insert, hl); hlDict.insert (hl->name(), hl); } // Normal HL KateHighlighting *hl = new KateHighlighting(0); hlList.prepend (hl); hlDict.insert (hl->name(), hl); lastCtxsReset.start(); } KateHlManager::~KateHlManager() { delete syntax; } static KStaticDeleter sdHlMan; KateHlManager *KateHlManager::self() { if ( !s_self ) sdHlMan.setObject(s_self, new KateHlManager ()); return s_self; } KateHighlighting *KateHlManager::getHl(int n) { if (n < 0 || n >= (int) hlList.count()) n = 0; return hlList.at(n); } int KateHlManager::nameFind(const QString &name) { int z (hlList.count() - 1); for (; z > 0; z--) if (hlList.at(z)->name() == name) return z; return z; } int KateHlManager::detectHighlighting (KateDocument *doc) { int hl = wildcardFind( doc->url().filename() ); if ( hl < 0 ) hl = mimeFind ( doc ); return hl; } int KateHlManager::wildcardFind(const QString &fileName) { int result = -1; if ((result = realWildcardFind(fileName)) != -1) return result; int length = fileName.length(); QString backupSuffix = KateDocumentConfig::global()->backupSuffix(); if (fileName.endsWith(backupSuffix)) { if ((result = realWildcardFind(fileName.left(length - backupSuffix.length()))) != -1) return result; } for (QStringList::Iterator it = commonSuffixes.begin(); it != commonSuffixes.end(); ++it) { if (*it != backupSuffix && fileName.endsWith(*it)) { if ((result = realWildcardFind(fileName.left(length - (*it).length()))) != -1) return result; } } return -1; } int KateHlManager::realWildcardFind(const QString &fileName) { static QRegExp sep("\\s*;\\s*"); QPtrList highlights; for (KateHighlighting *highlight = hlList.first(); highlight != 0L; highlight = hlList.next()) { highlight->loadWildcards(); for (QStringList::Iterator it = highlight->getPlainExtensions().begin(); it != highlight->getPlainExtensions().end(); ++it) if (fileName.endsWith((*it))) highlights.append(highlight); for (int i = 0; i < (int)highlight->getRegexpExtensions().count(); i++) { QRegExp re = highlight->getRegexpExtensions()[i]; if (re.exactMatch(fileName)) highlights.append(highlight); } } if ( !highlights.isEmpty() ) { int pri = -1; int hl = -1; for (KateHighlighting *highlight = highlights.first(); highlight != 0L; highlight = highlights.next()) { if (highlight->priority() > pri) { pri = highlight->priority(); hl = hlList.findRef (highlight); } } return hl; } return -1; } int KateHlManager::mimeFind( KateDocument *doc ) { static QRegExp sep("\\s*;\\s*"); KMimeType::Ptr mt = doc->mimeTypeForContent(); QPtrList highlights; for (KateHighlighting *highlight = hlList.first(); highlight != 0L; highlight = hlList.next()) { QStringList l = QStringList::split( sep, highlight->getMimetypes() ); for( QStringList::Iterator it = l.begin(); it != l.end(); ++it ) { if ( *it == mt->name() ) // faster than a regexp i guess? highlights.append (highlight); } } if ( !highlights.isEmpty() ) { int pri = -1; int hl = -1; for (KateHighlighting *highlight = highlights.first(); highlight != 0L; highlight = highlights.next()) { if (highlight->priority() > pri) { pri = highlight->priority(); hl = hlList.findRef (highlight); } } return hl; } return -1; } uint KateHlManager::defaultStyles() { return 14; } QString KateHlManager::defaultStyleName(int n, bool translateNames) { static QStringList names; static QStringList translatedNames; if (names.isEmpty()) { names << "Normal"; names << "Keyword"; names << "Data Type"; names << "Decimal/Value"; names << "Base-N Integer"; names << "Floating Point"; names << "Character"; names << "String"; names << "Comment"; names << "Others"; names << "Alert"; names << "Function"; // this next one is for denoting the beginning/end of a user defined folding region names << "Region Marker"; // this one is for marking invalid input names << "Error"; translatedNames << i18n("Normal"); translatedNames << i18n("Keyword"); translatedNames << i18n("Data Type"); translatedNames << i18n("Decimal/Value"); translatedNames << i18n("Base-N Integer"); translatedNames << i18n("Floating Point"); translatedNames << i18n("Character"); translatedNames << i18n("String"); translatedNames << i18n("Comment"); translatedNames << i18n("Others"); translatedNames << i18n("Alert"); translatedNames << i18n("Function"); // this next one is for denoting the beginning/end of a user defined folding region translatedNames << i18n("Region Marker"); // this one is for marking invalid input translatedNames << i18n("Error"); } return translateNames ? translatedNames[n] : names[n]; } void KateHlManager::getDefaults(uint schema, KateAttributeList &list) { list.setAutoDelete(true); KateAttribute* normal = new KateAttribute(); normal->setTextColor(Qt::black); normal->setSelectedTextColor(Qt::white); list.append(normal); KateAttribute* keyword = new KateAttribute(); keyword->setTextColor(Qt::black); keyword->setSelectedTextColor(Qt::white); keyword->setBold(true); list.append(keyword); KateAttribute* dataType = new KateAttribute(); dataType->setTextColor(Qt::darkRed); dataType->setSelectedTextColor(Qt::white); list.append(dataType); KateAttribute* decimal = new KateAttribute(); decimal->setTextColor(Qt::blue); decimal->setSelectedTextColor(Qt::cyan); list.append(decimal); KateAttribute* basen = new KateAttribute(); basen->setTextColor(Qt::darkCyan); basen->setSelectedTextColor(Qt::cyan); list.append(basen); KateAttribute* floatAttribute = new KateAttribute(); floatAttribute->setTextColor(Qt::darkMagenta); floatAttribute->setSelectedTextColor(Qt::cyan); list.append(floatAttribute); KateAttribute* charAttribute = new KateAttribute(); charAttribute->setTextColor(Qt::magenta); charAttribute->setSelectedTextColor(Qt::magenta); list.append(charAttribute); KateAttribute* string = new KateAttribute(); string->setTextColor(QColor::QColor("#D00")); string->setSelectedTextColor(Qt::red); list.append(string); KateAttribute* comment = new KateAttribute(); comment->setTextColor(Qt::darkGray); comment->setSelectedTextColor(Qt::gray); comment->setItalic(true); list.append(comment); KateAttribute* others = new KateAttribute(); others->setTextColor(Qt::darkGreen); others->setSelectedTextColor(Qt::green); list.append(others); KateAttribute* alert = new KateAttribute(); alert->setTextColor(Qt::white); alert->setSelectedTextColor( QColor::QColor("#FCC") ); alert->setBold(true); alert->setBGColor( QColor::QColor("#FCC") ); list.append(alert); KateAttribute* functionAttribute = new KateAttribute(); functionAttribute->setTextColor(Qt::darkBlue); functionAttribute->setSelectedTextColor(Qt::white); list.append(functionAttribute); KateAttribute* regionmarker = new KateAttribute(); regionmarker->setTextColor(Qt::white); regionmarker->setBGColor(Qt::gray); regionmarker->setSelectedTextColor(Qt::gray); list.append(regionmarker); KateAttribute* error = new KateAttribute(); error->setTextColor(Qt::red); error->setUnderline(true); error->setSelectedTextColor(Qt::red); list.append(error); KConfig *config = KateHlManager::self()->self()->getKConfig(); config->setGroup("Default Item Styles - Schema " + KateFactory::self()->schemaManager()->name(schema)); for (uint z = 0; z < defaultStyles(); z++) { KateAttribute *i = list.at(z); QStringList s = config->readListEntry(defaultStyleName(z)); if (!s.isEmpty()) { while( s.count()<8) s << ""; QString tmp; QRgb col; tmp=s[0]; if (!tmp.isEmpty()) { col=tmp.toUInt(0,16); i->setTextColor(col); } tmp=s[1]; if (!tmp.isEmpty()) { col=tmp.toUInt(0,16); i->setSelectedTextColor(col); } tmp=s[2]; if (!tmp.isEmpty()) i->setBold(tmp!="0"); tmp=s[3]; if (!tmp.isEmpty()) i->setItalic(tmp!="0"); tmp=s[4]; if (!tmp.isEmpty()) i->setStrikeOut(tmp!="0"); tmp=s[5]; if (!tmp.isEmpty()) i->setUnderline(tmp!="0"); tmp=s[6]; if (!tmp.isEmpty()) { col=tmp.toUInt(0,16); i->setBGColor(col); } tmp=s[7]; if (!tmp.isEmpty()) { col=tmp.toUInt(0,16); i->setSelectedBGColor(col); } } } } void KateHlManager::setDefaults(uint schema, KateAttributeList &list) { KConfig *config = KateHlManager::self()->self()->getKConfig(); config->setGroup("Default Item Styles - Schema " + KateFactory::self()->schemaManager()->name(schema)); for (uint z = 0; z < defaultStyles(); z++) { QStringList settings; KateAttribute *i = list.at(z); settings<<(i->itemSet(KateAttribute::TextColor)?QString::number(i->textColor().rgb(),16):""); settings<<(i->itemSet(KateAttribute::SelectedTextColor)?QString::number(i->selectedTextColor().rgb(),16):""); settings<<(i->itemSet(KateAttribute::Weight)?(i->bold()?"1":"0"):""); settings<<(i->itemSet(KateAttribute::Italic)?(i->italic()?"1":"0"):""); settings<<(i->itemSet(KateAttribute::StrikeOut)?(i->strikeOut()?"1":"0"):""); settings<<(i->itemSet(KateAttribute::Underline)?(i->underline()?"1":"0"):""); settings<<(i->itemSet(KateAttribute::BGColor)?QString::number(i->bgColor().rgb(),16):""); settings<<(i->itemSet(KateAttribute::SelectedBGColor)?QString::number(i->selectedBGColor().rgb(),16):""); settings<<"---"; config->writeEntry(defaultStyleName(z),settings); } emit changed(); } int KateHlManager::highlights() { return (int) hlList.count(); } QString KateHlManager::hlName(int n) { return hlList.at(n)->name(); } QString KateHlManager::hlNameTranslated(int n) { return hlList.at(n)->nameTranslated(); } QString KateHlManager::hlSection(int n) { return hlList.at(n)->section(); } bool KateHlManager::hlHidden(int n) { return hlList.at(n)->hidden(); } QString KateHlManager::identifierForName(const QString& name) { KateHighlighting *hl = 0; if ((hl = hlDict[name])) return hl->getIdentifier (); return QString(); } bool KateHlManager::resetDynamicCtxs() { if (forceNoDCReset) return false; if (lastCtxsReset.elapsed() < KATE_DYNAMIC_CONTEXTS_RESET_DELAY) return false; KateHighlighting *hl; for (hl = hlList.first(); hl; hl = hlList.next()) hl->dropDynamicContexts(); dynamicCtxsCount = 0; lastCtxsReset.start(); return true; } //END //BEGIN KateHighlightAction void KateViewHighlightAction::init() { m_doc = 0; subMenus.setAutoDelete( true ); connect(popupMenu(),SIGNAL(aboutToShow()),this,SLOT(slotAboutToShow())); } void KateViewHighlightAction::updateMenu (Kate::Document *doc) { m_doc = doc; } void KateViewHighlightAction::slotAboutToShow() { Kate::Document *doc=m_doc; int count = KateHlManager::self()->highlights(); for (int z=0; zhlNameTranslated (z); QString hlSection = KateHlManager::self()->hlSection (z); if (!KateHlManager::self()->hlHidden(z)) { if ( !hlSection.isEmpty() && (names.contains(hlName) < 1) ) { if (subMenusName.contains(hlSection) < 1) { subMenusName << hlSection; QPopupMenu *menu = new QPopupMenu (); subMenus.append(menu); popupMenu()->insertItem ( '&' + hlSection, menu); } int m = subMenusName.findIndex (hlSection); names << hlName; subMenus.at(m)->insertItem ( '&' + hlName, this, SLOT(setHl(int)), 0, z); } else if (names.contains(hlName) < 1) { names << hlName; popupMenu()->insertItem ( '&' + hlName, this, SLOT(setHl(int)), 0, z); } } } if (!doc) return; for (uint i=0;icount();i2++) { subMenus.at(i)->setItemChecked(subMenus.at(i)->idAt(i2),false); } } popupMenu()->setItemChecked (0, false); int i = subMenusName.findIndex (KateHlManager::self()->hlSection(doc->hlMode())); if (i >= 0 && subMenus.at(i)) subMenus.at(i)->setItemChecked (doc->hlMode(), true); else popupMenu()->setItemChecked (0, true); } void KateViewHighlightAction::setHl (int mode) { Kate::Document *doc=m_doc; if (doc) doc->setHlMode((uint)mode); } //END KateViewHighlightAction // kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/katehighlight.h b/kate/part/katehighlight.h index a0c656b9c9..b5e40c3e47 100644 --- a/kate/part/katehighlight.h +++ b/kate/part/katehighlight.h @@ -1,435 +1,423 @@ /* This file is part of the KDE libraries Copyright (C) 2001,2002 Joseph Wenninger Copyright (C) 2001 Christoph Cullmann Copyright (C) 1999 Jochen Wilhelmy 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __KATE_HIGHLIGHT_H__ #define __KATE_HIGHLIGHT_H__ #include "katetextline.h" #include "kateattribute.h" #include "../interfaces/document.h" #include #include #include #include #include #include #include #include #include #include #include #include class KateHlContext; class KateHlItem; class KateHlItemData; class KateHlData; class KateEmbeddedHlInfo; class KateHlIncludeRule; class KateSyntaxDocument; class KateTextLine; class KateSyntaxModeListItem; class KateSyntaxContextData; class QPopupMenu; // some typedefs typedef QPtrList KateAttributeList; typedef QValueList KateHlIncludeRules; typedef QPtrList KateHlItemDataList; typedef QPtrList KateHlDataList; typedef QMap KateEmbeddedHlInfos; typedef QMap KateHlUnresolvedCtxRefs; typedef QValueList IntList; //Item Properties: name, Item Style, Item Font class KateHlItemData : public KateAttribute { public: KateHlItemData(const QString name, int defStyleNum); enum ItemStyles { dsNormal, dsKeyword, dsDataType, dsDecVal, dsBaseN, dsFloat, dsChar, dsString, dsComment, dsOthers, dsAlert, dsFunction, dsRegionMarker, dsError }; public: const QString name; int defStyleNum; }; class KateHlData { public: KateHlData(const QString &wildcards, const QString &mimetypes,const QString &identifier, int priority); public: QString wildcards; QString mimetypes; QString identifier; int priority; }; class KateHighlighting { public: KateHighlighting(const KateSyntaxModeListItem *def); ~KateHighlighting(); public: void doHighlight ( KateTextLine *prevLine, KateTextLine *textLine, QMemArray *foldingList, bool *ctxChanged ); void loadWildcards(); QValueList& getRegexpExtensions(); QStringList& getPlainExtensions(); QString getMimetypes(); // this pointer needs to be deleted !!!!!!!!!! KateHlData *getData(); void setData(KateHlData *); void setKateHlItemDataList(uint schema, KateHlItemDataList &); // both methodes return hard copies of the internal lists // the lists are cleared first + autodelete is set ! // keep track that you delete them, or mem will be lost void getKateHlItemDataListCopy (uint schema, KateHlItemDataList &); const QString &name() const {return iName;} const QString &nameTranslated() const {return iNameTranslated;} const QString §ion() const {return iSection;} bool hidden() const {return iHidden;} const QString &version() const {return iVersion;} const QString &author () const { return iAuthor; } const QString &license () const { return iLicense; } int priority(); const QString &getIdentifier() const {return identifier;} void use(); void release(); /** * @return true if the character @p c is not a deliminator character * for the corresponding highlight. */ bool isInWord( QChar c, int attrib=0 ) const; /** * @return true if the character @p c is a wordwrap deliminator as specified * in the general keyword section of the xml file. */ bool canBreakAt( QChar c, int attrib=0 ) const; /** * @return true if @p beginAttr and @p endAttr are members of the same * highlight, and there are comment markers of either type in that. */ bool canComment( int startAttr, int endAttr ) const; /** * @return 0 if highlighting which attr is a member of does not * define a comment region, otherwise the region is returned */ signed char commentRegion(int attr) const; - /** - * Defines positions in the additional data list. - * FIXME this (m_additionaldata) is now well designed, since anyone can mainpulate - * the stringlist from anywhere. It allready broke once, and it will break - * again. So we need a class instead of the stringlist. - */ - enum additionalDataType { - Start=0, - End, - MultiLineRegion, - SingleLine, - Deliminator, - WordWrapDeliminator - }; - - /** - * @return the comment marker @p which for the highlight corresponding to - * @p attrib. - */ - QString getCommentString( int which, int attrib ) const; - /** * @return the mulitiline comment start marker for the highlight * corresponding to @p attrib. */ QString getCommentStart( int attrib=0 ) const; /** * @return the muiltiline comment end marker for the highlight corresponding * to @p attrib. */ QString getCommentEnd( int attrib=0 ) const; /** * @return the single comment marker for the highlight corresponding * to @p attrib. */ QString getCommentSingleLineStart( int attrib=0 ) const; /** * @return the attribute for @p context. */ int attribute( int context ) const; void clearAttributeArrays (); QMemArray *attributes (uint schema); inline bool noHighlighting () const { return noHl; }; // be carefull: all documents hl should be invalidated after calling this method! void dropDynamicContexts(); QString indentation () { return m_indentation; } private: // make this private, nobody should play with the internal data pointers void getKateHlItemDataList(uint schema, KateHlItemDataList &); void init(); void done(); void makeContextList (); int makeDynamicContext(KateHlContext *model, const QStringList *args); void handleKateHlIncludeRules (); void handleKateHlIncludeRulesRecursive(KateHlIncludeRules::iterator it, KateHlIncludeRules *list); int addToContextList(const QString &ident, int ctx0); void addToKateHlItemDataList(); void createKateHlItemData (KateHlItemDataList &list); - QString readGlobalKeywordConfig(); - QString readWordWrapConfig(); - QStringList readCommentConfig(); + void readGlobalKeywordConfig(); + void readWordWrapConfig(); + void readCommentConfig(); void readIndentationConfig (); void readFoldingConfig (); // manipulates the ctxs array directly ;) void generateContextStack(int *ctxNum, int ctx, QMemArray *ctxs, int *posPrevLine); KateHlItem *createKateHlItem(KateSyntaxContextData *data, KateHlItemDataList &iDl, QStringList *RegionList, QStringList *ContextList); int lookupAttrName(const QString& name, KateHlItemDataList &iDl); void createContextNameList(QStringList *ContextNameList, int ctx0); int getIdFromString(QStringList *ContextNameList, QString tmpLineEndContext,/*NO CONST*/ QString &unres); /** * @return the key to use for @p attrib in m_additionalData. */ - int hlKeyForAttrib( int attrib ) const { return hlKeyForList( &m_hlIndex, attrib ); } - /** - * @return the key to use for @p context in m_additionalData. - */ - int hlKeyForContext( int context ) const { return hlKeyForList( &m_ctxIndex, context ); } - /** - * Does the work for hlKeyForAttrib and hlKeyForContext - */ - int hlKeyForList( const IntList *, int ) const; + QString hlKeyForAttrib( int attrib ) const; KateHlItemDataList internalIDList; QValueVector m_contexts; inline KateHlContext *contextNum (uint n) { if (n < m_contexts.size()) return m_contexts[n]; return 0; } QMap< QPair, short> dynamicCtxs; // make them pointers perhaps KateEmbeddedHlInfos embeddedHls; KateHlUnresolvedCtxRefs unresolvedContextReferences; QStringList RegionList; QStringList ContextNameList; bool noHl; bool folding; bool casesensitive; QString weakDeliminator; QString deliminator; QString iName; QString iNameTranslated; QString iSection; bool iHidden; QString iWildcards; QString iMimetypes; QString identifier; QString iVersion; QString iAuthor; QString iLicense; QString m_indentation; int m_priority; int refCount; int startctx, base_startctx; QString errorsAndWarnings; QString buildIdentifier; QString buildPrefix; bool building; uint itemData0; uint buildContext0Offset; KateHlIncludeRules includeRules; bool m_foldingIndentationSensitive; QIntDict< QMemArray > m_attributeArrays; /** - * This contains a list of comment data, the deliminator string and - * wordwrap deliminator pr highlight. - * The key is the highlights entry position in internalIDList. - * This is used to look up the correct comment and delimitor strings - * based on the attrtibute. + * This class holds the additional properties for one highlight + * definition, such as comment strings, deliminators etc. + * + * When a highlight is added, a instance of this class is appended to + * m_additionalData, and the current position in the attrib and context + * arrays are stored in the indexes for look up. You can then use + * hlKeyForAttrib or hlKeyForContext to find the relevant instance of this + * class from m_additionalData. + * + * If you need to add a property to a highlight, add it here. */ - QMap m_additionalData; + class HighlightPropertyBag { + public: + QString singleLineCommentMarker; + QString multiLineCommentStart; + QString multiLineCommentEnd; + QString multiLineRegion; + QString deliminator; + QString wordWrapDeliminator; + }; /** - * fast lookup of hl properties, based on attribute index + * Highlight properties for each included highlight definition. + * The key is the identifier */ - IntList m_hlIndex; + QDict m_additionalData; /** - * fast lookup of hl properties, based on context index + * Fast lookup of hl properties, based on attribute index + * The key is the starting index in the attribute array for each file. + * @see hlKeyForAttrib */ - IntList m_ctxIndex; + QMap m_hlIndex; + QString extensionSource; QValueList regexpExtensions; QStringList plainExtensions; public: inline bool foldingIndentationSensitive () { return m_foldingIndentationSensitive; } inline bool allowsFolding(){return folding;} }; class KateHlManager : public QObject { Q_OBJECT private: KateHlManager(); public: ~KateHlManager(); static KateHlManager *self(); inline KConfig *getKConfig() { return &m_config; }; KateHighlighting *getHl(int n); int nameFind(const QString &name); int detectHighlighting (class KateDocument *doc); int findHl(KateHighlighting *h) {return hlList.find(h);} QString identifierForName(const QString&); // methodes to get the default style count + names static uint defaultStyles(); static QString defaultStyleName(int n, bool translateNames = false); void getDefaults(uint schema, KateAttributeList &); void setDefaults(uint schema, KateAttributeList &); int highlights(); QString hlName(int n); QString hlNameTranslated (int n); QString hlSection(int n); bool hlHidden(int n); void incDynamicCtxs() { ++dynamicCtxsCount; }; uint countDynamicCtxs() { return dynamicCtxsCount; }; void setForceNoDCReset(bool b) { forceNoDCReset = b; }; // be carefull: all documents hl should be invalidated after having successfully called this method! bool resetDynamicCtxs(); signals: void changed(); private: int wildcardFind(const QString &fileName); int mimeFind(KateDocument *); int realWildcardFind(const QString &fileName); private: friend class KateHighlighting; QPtrList hlList; QDict hlDict; static KateHlManager *s_self; KConfig m_config; QStringList commonSuffixes; KateSyntaxDocument *syntax; uint dynamicCtxsCount; QTime lastCtxsReset; bool forceNoDCReset; }; class KateViewHighlightAction: public Kate::ActionMenu { Q_OBJECT public: KateViewHighlightAction(const QString& text, QObject* parent = 0, const char* name = 0) : Kate::ActionMenu(text, parent, name) { init(); }; ~KateViewHighlightAction(){;}; void updateMenu (Kate::Document *doc); private: void init(); QGuardedPtr m_doc; QStringList subMenusName; QStringList names; QPtrList subMenus; public slots: void slotAboutToShow(); private slots: void setHl (int mode); }; #endif // kate: space-indent on; indent-width 2; replace-tabs on;