/* This file is part of the KDE libraries Copyright (C) 2007 Mirko Stocker Copyright (C) 2007 Matthew Woehlke 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ //BEGIN INCLUDES #include "katehighlight.h" #include "katehighlighthelpers.h" #include "katetextline.h" #include "katedocument.h" #include "katesyntaxdocument.h" #include "katerenderer.h" #include "kateglobal.h" #include "kateschema.h" #include "kateconfig.h" #include "kateextendedattribute.h" #include "katedefaultcolors.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //END //BEGIN defines // x is a QString. if x is "true" or "1" this expression returns "true" #define IS_TRUE(x) x.toLower() == QLatin1String("true") || x.toInt() == 1 //END defines //BEGIN STATICS namespace { const QString stdDeliminator = QString (" \t.():!+,-<=>%&*/;?[]^{|}~\\"); QColor toColor(const QString& configEntry) { // note: color is stored in hex format in the config files, i.e.: ffafafae // it's not using the leading hash though so the QString ctor taking a QStirng // fails. return QColor(QRgb(configEntry.toUInt(0, 16))); } } //END //BEGIN KateHighlighting KateHighlighting::KateHighlighting(const KateSyntaxModeListItem *def) : refCount(0) { errorsAndWarnings = ""; building=false; noHl = false; m_foldingIndentationSensitive = false; folding=false; if (def == 0) { noHl = true; iName = "None"; // not translated internal name (for config and more) iNameTranslated = i18nc("Syntax highlighting", "None"); // user visible name iSection = ""; makeNoneContext(); } else { iName = def->name; iNameTranslated = def->nameTranslated; iSection = def->section; iHidden = def->hidden; identifier = def->identifier; iVersion=def->version; iStyle = def->style; iAuthor=def->author; iLicense=def->license; } deliminator = stdDeliminator; } KateHighlighting::~KateHighlighting() { // cleanup ;) cleanup (); qDeleteAll(m_additionalData); } void KateHighlighting::makeNoneContext() { iHidden = false; m_additionalData.insert( "none", new HighlightPropertyBag ); m_additionalData["none"]->deliminator = stdDeliminator; m_additionalData["none"]->wordWrapDeliminator = stdDeliminator; m_hlIndex[0] = "none"; m_ctxIndex[0]= "none"; m_contexts.push_back(new KateHlContext("None", 0, KateHlContextModification(), false, KateHlContextModification(), false, false, false, KateHlContextModification())); } void KateHighlighting::cleanup () { qDeleteAll (m_contexts); m_contexts.clear (); qDeleteAll (m_hlItemCleanupList); m_hlItemCleanupList.clear (); m_attributeArrays.clear (); internalIDList.clear(); } KateHlContext *KateHighlighting::generateContextStack (Kate::TextLineData::ContextStack &contextStack, KateHlContextModification modification, int &indexLastContextPreviousLine) { while (true) { switch (modification.type) { /** * stay, do nothing, just return the last context * in the stack or 0 */ case KateHlContextModification::doNothing: return contextNum (contextStack.isEmpty() ? 0 : contextStack.last()); /** * just add a new context to the stack * and return this one */ case KateHlContextModification::doPush: contextStack.append (modification.newContext); return contextNum (modification.newContext); /** * pop some contexts + add a new one afterwards, immediate.... */ case KateHlContextModification::doPopsAndPush: // resize stack contextStack.resize ((modification.pops >= contextStack.size()) ? 0 : (contextStack.size() - modification.pops)); // push imediate the new context.... // don't handle the previous line stuff at all.... // ### TODO ### think about this contextStack.append (modification.newContext); return contextNum (modification.newContext); /** * do only pops... */ default: { // resize stack contextStack.resize ((modification.pops >= contextStack.size()) ? 0 : (contextStack.size() - modification.pops)); // handling of context of previous line.... if (indexLastContextPreviousLine >= (contextStack.size()-1)) { // set new index, if stack is empty, this is -1, done for eternity... indexLastContextPreviousLine = contextStack.size() - 1; // stack already empty, nothing to do... if ( contextStack.isEmpty() ) return contextNum (0); KateHlContext *c = contextNum(contextStack.last()); // this must be a valid context, or our context stack is borked.... Q_ASSERT (c); // handle line end context as new modificationContext modification = c->lineEndContext; continue; } return contextNum (contextStack.isEmpty() ? 0 : contextStack.last()); } } } // should never be reached Q_ASSERT (false); return contextNum (0); } /** * 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 { #ifdef HIGHLIGHTING_DEBUG kDebug(13010) << "new stuff: " << startctx; #endif KateHlContext *newctx = model->clone(args); m_contexts.push_back (newctx); value = startctx++; dynamicCtxs[key] = value; KateHlManager::self()->incDynamicCtxs(); } // kDebug(13010) << "Dynamic context: using context #" << value << " (for model " << model << " with args " << *args << ")"; return value; } /** * Drop all dynamic contexts. Shall be called with extreme care, and shall be immediately * followed by a full HL invalidation. */ void KateHighlighting::dropDynamicContexts() { if (refCount == 0) // unused highlighting - nothing to drop return; if (noHl) // "normal texts" highlighting - no context list return; qDeleteAll(m_contexts.begin()+base_startctx, m_contexts.end()); // delete dynamic contexts (after base_startctx) m_contexts.resize (base_startctx); dynamicCtxs.clear(); startctx = base_startctx; } void KateHighlighting::doHighlight ( const Kate::TextLineData *_prevLine, Kate::TextLineData *textLine, const Kate::TextLineData *nextLine, bool &ctxChanged, int tabWidth, QVector* contextChanges) { if (!textLine) return; // in all cases, remove old hl, or we will grow to infinite ;) textLine->clearAttributes (); // reset folding start textLine->clearMarkedAsFoldingStart (); // no hl set, nothing to do more than the above cleaning ;) if (noHl) return; const bool firstLine = (_prevLine == 0); static const Kate::TextLineData dummyData; const Kate::TextLineData * prevLine = firstLine ? &dummyData : _prevLine; int previousLine = -1; KateHlContext *context; // duplicate the ctx stack, only once ! Kate::TextLineData::ContextStack ctx (prevLine->contextStack()); if (ctx.isEmpty()) { // If the stack is empty, we assume to be in Context 0 (Normal) if (firstLine) { context = contextNum(0); } else { context = generateContextStack(ctx, contextNum(0)->lineEndContext, previousLine); //get stack ID to use } } else { //kDebug(13010) << "\t\tctxNum = " << ctxNum << " contextList[ctxNum] = " << contextList[ctxNum]; // ellis //if (lineContinue) kDebug(13010)<hlLineContinue()) previousLine--; else context = generateContextStack(ctx, context->lineEndContext, previousLine); //get stack ID to use //kDebug(13010)<<"test1-2-1-text4"; //if (lineContinue) kDebug(13010)<string(); const int len = textLine->length(); // calc at which char the first char occurs, set it to length 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; KateHighlighting::HighlightPropertyBag* additionalData = m_additionalData[context->hlId]; KateHlContext* oldContext = context; // optimization: list of highlighting items that need their cache reset static std::vector cachingItems; // catch empty lines if (len == 0) { // regenerate context stack if needed if (context->emptyLineContext) context = generateContextStack (ctx, context->emptyLineContextModification, previousLine); } else { /** * check if the folding begin/ends are balanced! * constructed on demand! */ QHash *foldingStartToCount = 0; /** * loop over line content! */ QChar lastDelimChar = 0; KateHlContext* previous = context; while (offset < len) { // If requested (happens from completion), return where context changes occur. if ( contextChanges && ( offset == 0 || context != previous ) ) { previous = context; const ContextChange change = {context, offset}; contextChanges->append(change); } bool anItemMatched = false; bool customStartEnableDetermined = false; foreach (KateHlItem *it, context->items) { item = it; // 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 ( oldContext != context ) { oldContext = context; additionalData = m_additionalData[oldContext->hlId]; } if (customStartEnableDetermined || additionalData->deliminator.contains(lastChar)) customStartEnableDetermined = true; else continue; } else { if (lastDelimChar == lastChar) { } else if ( stdDeliminator.contains(lastChar) ) { lastDelimChar = lastChar; } else { continue; } } } int offset2 = item->checkHgl(text, offset, len-offset); if ( item->haveCache && !item->cachingHandled ) { cachingItems.push_back(item); item->cachingHandled = true; } if (offset2 <= offset) continue; // dominik: on lookAhead, do not preocess any data by fixing offset2 if (item->lookAhead) { offset2 = offset; } else { // make sure the rule does not violate the text line length if (offset2 > len) offset2 = len; } // BUG 144599: Ignore a context change that would push the same context // without eating anything... this would be an infinite loop! if ( item->lookAhead && ( item->ctx.pops < 2 && item->ctx.newContext == ( ctx.isEmpty() ? 0 : ctx.last() ) ) ) continue; // regenerate context stack if needed context = generateContextStack (ctx, item->ctx, previousLine); // dynamic context: substitute the model with an 'instance' if (context->dynamic) { // try to retrieve captures from regexp QStringList captures; item->capturedTexts (captures); if (!captures.empty()) { // Replace the top of the stack and the current context int newctx = makeDynamicContext(context, &captures); if (ctx.size() > 0) ctx[ctx.size() - 1] = newctx; context = contextNum(newctx); } } // handle folding end or begin if (item->region || item->region2) { /** * for each end region, decrement counter for that type, erase if count reaches 0! */ if (item->region2 && foldingStartToCount) { QHash::iterator end = foldingStartToCount->find (-item->region2); if (end != foldingStartToCount->end()) { if (end.value() > 1) --(end.value()); else foldingStartToCount->erase (end); } } /** * increment counter for each begin region! */ if (item->region) { // construct on demand! if (!foldingStartToCount) foldingStartToCount = new QHash (); ++(*foldingStartToCount)[item->region]; } } // even set attributes or end of region! ;) int attribute = item->onlyConsume ? context->attr : item->attr; if ((attribute > 0 && !item->lookAhead) || item->region2) textLine->addAttribute (Kate::TextLineData::Attribute (offset, offset2-offset, attribute, item->region2)); // create 0 length attribute for begin of region, if any! if (item->region) textLine->addAttribute (Kate::TextLineData::Attribute (offset2, 0, attribute, item->region)); // only process, if lookAhead is false if (!item->lookAhead) { offset = offset2; lastChar = text[offset-1]; } anItemMatched = true; break; } // something matched, continue loop if (anItemMatched) continue; item = 0; // nothing found: set attribute of one char // anders: unless this context does not want that! if ( context->fallthrough ) { // set context to context->ftctx. context=generateContextStack(ctx, context->ftctx, previousLine); //regenerate context stack //kDebug(13010)<<"context num after fallthrough at col "<attr > 0) textLine->addAttribute (Kate::TextLineData::Attribute (offset, 1, context->attr, 0)); lastChar = text[offset]; offset++; } } /** * check if folding is not balanced and we have more starts then ends * then this line is a possible folding start! */ if (foldingStartToCount) { /** * possible folding start, if imbalanced, aka hash not empty! */ if (!foldingStartToCount->isEmpty()) textLine->markAsFoldingStartAttribute (); /** * kill hash */ delete foldingStartToCount; foldingStartToCount = 0; } } /** * has the context stack changed? */ if ((ctxChanged = (ctx != textLine->contextStack()))) { /** * try to share the simple stack that contains only 0 */ static const Kate::TextLineData::ContextStack onlyDefaulContext (1, 0); if (ctx == onlyDefaulContext) textLine->setContextStack(onlyDefaulContext); /** * next try: try to share data with last line */ else if (ctx == prevLine->contextStack()) textLine->setContextStack(prevLine->contextStack()); /** * ok, really use newly constructed stack! */ else textLine->setContextStack(ctx); } // write hl continue flag textLine->setHlLineContinue (item && item->lineContinue()); // check for indentation based folding if (m_foldingIndentationSensitive && (tabWidth > 0) && !textLine->markedAsFoldingStartAttribute ()) { bool skipIndentationBasedFolding = false; for(int i = ctx.size() - 1; i >= 0; --i) { if (contextNum(ctx[i])->noIndentationBasedFolding) { skipIndentationBasedFolding = true; break; } } /** * compute if we increase indentation in next line */ if (!skipIndentationBasedFolding && !isEmptyLine (textLine) && !isEmptyLine (nextLine) && (textLine->indentDepth (tabWidth) < nextLine->indentDepth (tabWidth))) textLine->markAsFoldingStartIndentation (); } // invalidate caches for ( int i = 0; i < cachingItems.size(); ++i) { cachingItems[i]->cachingHandled = false; cachingItems[i]->haveCache = false; } cachingItems.clear(); cachingItems.shrink_to_fit(); } void KateHighlighting::getKateExtendedAttributeList (const QString &schema, QList &list, KConfig* cfg) { KConfigGroup config(cfg?cfg:KateHlManager::self()->getKConfig(), "Highlighting " + iName + " - Schema " + schema); list.clear(); createKateExtendedAttribute(list); foreach (KateExtendedAttribute::Ptr p, list) { Q_ASSERT(p); QStringList s = config.readEntry(p->name(), QStringList()); // kDebug(13010)<name<0) { while(s.count()<10) s<<""; QString name = p->name(); bool spellCheck = p->performSpellchecking(); p->clear(); p->setName(name); p->setPerformSpellchecking(spellCheck); QString tmp=s[0]; if (!tmp.isEmpty()) p->setDefaultStyleIndex(tmp.toInt()); tmp=s[1]; if (!tmp.isEmpty()) p->setForeground(toColor(tmp)); tmp=s[2]; if (!tmp.isEmpty()) p->setSelectedForeground(toColor(tmp)); tmp=s[3]; if (!tmp.isEmpty()) p->setFontBold(tmp!="0"); tmp=s[4]; if (!tmp.isEmpty()) p->setFontItalic(tmp!="0"); tmp=s[5]; if (!tmp.isEmpty()) p->setFontStrikeOut(tmp!="0"); tmp=s[6]; if (!tmp.isEmpty()) p->setFontUnderline(tmp!="0"); tmp=s[7]; if (!tmp.isEmpty()) p->setBackground(toColor(tmp)); tmp=s[8]; if (!tmp.isEmpty()) p->setSelectedBackground(toColor(tmp)); tmp=s[9]; if (!tmp.isEmpty() && tmp!=QLatin1String("---")) p->setFontFamily(tmp); } } } void KateHighlighting::getKateExtendedAttributeListCopy( const QString &schema, QList< KateExtendedAttribute::Ptr >& list, KConfig* cfg ) { QList attributes; getKateExtendedAttributeList(schema, attributes,cfg); list.clear(); foreach (const KateExtendedAttribute::Ptr &attribute, attributes) list.append(KateExtendedAttribute::Ptr(new KateExtendedAttribute(*attribute.data()))); } /** * Saves the attribute definitions to the config file. * * @param schema The id of the schema group to save * @param list QList containing the data to be used */ void KateHighlighting::setKateExtendedAttributeList(const QString &schema, QList &list, KConfig *cfg, bool writeDefaultsToo) { KConfigGroup config(cfg?cfg:KateHlManager::self()->getKConfig(), "Highlighting " + iName + " - Schema "+ schema); QStringList settings; KateAttributeList defList; KateHlManager::self()->getDefaults(schema, defList); foreach (const KateExtendedAttribute::Ptr& p, list) { Q_ASSERT(p); settings.clear(); uint defStyle=p->defaultStyleIndex(); KTextEditor::Attribute::Ptr a(defList[defStyle]); settings<defaultStyleIndex(),10); settings<<(p->hasProperty(QTextFormat::ForegroundBrush)?QString::number(p->foreground().color().rgb(),16):(writeDefaultsToo?QString::number(a->foreground().color().rgb(),16):"")); settings<<(p->hasProperty(KTextEditor::Attribute::SelectedForeground)?QString::number(p->selectedForeground().color().rgb(),16):(writeDefaultsToo?QString::number(a->selectedForeground().color().rgb(),16):"")); settings<<(p->hasProperty(QTextFormat::FontWeight)?(p->fontBold()?"1":"0"):(writeDefaultsToo?(a->fontBold()?"1":"0"):"")); settings<<(p->hasProperty(QTextFormat::FontItalic)?(p->fontItalic()?"1":"0"):(writeDefaultsToo?(a->fontItalic()?"1":"0"):"")); settings<<(p->hasProperty(QTextFormat::FontStrikeOut)?(p->fontStrikeOut()?"1":"0"):(writeDefaultsToo?(a->fontStrikeOut()?"1":"0"):"")); settings<<(p->hasProperty(QTextFormat::FontUnderline)?(p->fontUnderline()?"1":"0"):(writeDefaultsToo?(a->fontUnderline()?"1":"0"):"")); settings<<(p->hasProperty(QTextFormat::BackgroundBrush)?QString::number(p->background().color().rgb(),16):((writeDefaultsToo && a->hasProperty(QTextFormat::BackgroundBrush))?QString::number(a->background().color().rgb(),16):"")); settings<<(p->hasProperty(KTextEditor::Attribute::SelectedBackground)?QString::number(p->selectedBackground().color().rgb(),16):((writeDefaultsToo&& a->hasProperty(KTextEditor::Attribute::SelectedBackground))?QString::number(a->selectedBackground().color().rgb(),16):"")); settings<<(p->hasProperty(QTextFormat::FontFamily)?(p->fontFamily()):(writeDefaultsToo?a->fontFamily():QString())); settings<<"---"; config.writeEntry(p->name(),settings); } } const QHash& KateHighlighting::getCharacterEncodings( int attrib ) const { return m_additionalData[ hlKeyForAttrib( attrib ) ]->characterEncodings; } const KatePrefixStore& KateHighlighting::getCharacterEncodingsPrefixStore( int attrib ) const { return m_additionalData[ hlKeyForAttrib( attrib ) ]->characterEncodingsPrefixStore; } const QHash& KateHighlighting::getReverseCharacterEncodings( int attrib ) const { return m_additionalData[ hlKeyForAttrib( attrib ) ]->reverseCharacterEncodings; } int KateHighlighting::getEncodedCharactersInsertionPolicy( int attrib ) const { return m_additionalData[ hlKeyForAttrib( attrib ) ]->encodedCharactersInsertionPolicy; } void KateHighlighting::addCharacterEncoding( const QString& key, const QString& encoding, const QChar& c ) { m_additionalData[ key ]->characterEncodingsPrefixStore.addPrefix(encoding); m_additionalData[ key ]->characterEncodings[ encoding ] = c; m_additionalData[ key ]->reverseCharacterEncodings[ c ] = encoding; } /** * 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; qDeleteAll(m_contexts); m_contexts.clear (); makeContextList(); if (noHl) // something went wrong, fill something in makeNoneContext(); } /** * If there is no document using the highlighting style free the complete * context structure. */ void KateHighlighting::done() { if (noHl) return; cleanup (); } /** * KateHighlighting - createKateExtendedAttribute * 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::createKateExtendedAttribute(QList &list) { // If the internal list isn't already available read the config file if (!noHl) { if (internalIDList.isEmpty()) makeContextList(); list=internalIDList; } // If no highlighting is selected or we have no attributes we need only one default. if (noHl || list.isEmpty()) list.append(KateExtendedAttribute::Ptr(new KateExtendedAttribute(i18n("Normal Text"), KTextEditor::HighlightInterface::dsNormal))); } /** * Adds the styles of the currently parsed highlight to the itemdata list */ void KateHighlighting::addToKateExtendedAttributeList() { //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"); KateDefaultColors colors; //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")); QString spellChecking = KateHlManager::self()->syntax->groupData(data,QString("spellChecking")); QString fontFamily = KateHlManager::self()->syntax->groupData(data,QString("fontFamily")); KateExtendedAttribute::Ptr newData(new KateExtendedAttribute( buildPrefix+KateHlManager::self()->syntax->groupData(data,QString("name")).simplified(), KateExtendedAttribute::indexForStyleName(KateHlManager::self()->syntax->groupData(data,QString("defStyleNum"))))); /* here the custom style overrides are specified, if needed */ if (!color.isEmpty()) newData->setForeground(colors.adaptToScheme(QColor(color), KateDefaultColors::ForegroundColor)); if (!selColor.isEmpty()) newData->setSelectedForeground(colors.adaptToScheme(QColor(selColor), KateDefaultColors::ForegroundColor)); if (!bold.isEmpty()) newData->setFontBold( IS_TRUE(bold) ); if (!italic.isEmpty()) newData->setFontItalic( IS_TRUE(italic) ); // new attributes for the new rendering view if (!underline.isEmpty()) newData->setFontUnderline( IS_TRUE(underline) ); if (!strikeOut.isEmpty()) newData->setFontStrikeOut( IS_TRUE(strikeOut) ); if (!bgColor.isEmpty()) newData->setBackground(colors.adaptToScheme(QColor(bgColor), KateDefaultColors::BackgroundColor)); if (!selBgColor.isEmpty()) newData->setSelectedBackground(colors.adaptToScheme(QColor(selBgColor), KateDefaultColors::BackgroundColor)); // is spellchecking desired? if (!spellChecking.isEmpty()) newData->setPerformSpellchecking( IS_TRUE(spellChecking) ); if (!fontFamily.isEmpty()) newData->setFontFamily(fontFamily); 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 its 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, QList &iDl) { const QString needle = buildPrefix + name; for (int i = 0; i < iDl.count(); i++) if (iDl.at(i)->name() == needle) return i; #ifdef HIGHLIGHTING_DEBUG kDebug(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, QList &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->indexOf(beginRegionStr); if (regionId==-1) // if the region name doesn't already exist, add it to the list { (*RegionList)<indexOf(beginRegionStr); } regionId++; #ifdef HIGHLIGHTING_DEBUG kDebug(13010) << "########### BEG REG: " << beginRegionStr << " NUM: " << regionId; #endif } if (!endRegionStr.isEmpty()) { regionId2 = RegionList->indexOf(endRegionStr); if (regionId2==-1) // if the region name doesn't already exist, add it to the list { (*RegionList)<indexOf(endRegionStr); } regionId2 = -regionId2 - 1; #ifdef HIGHLIGHTING_DEBUG kDebug(13010) << "########### END REG: " << endRegionStr << " NUM: " << regionId2; #endif } int attr = 0; QString tmpAttr=KateHlManager::self()->syntax->groupItemData(data,QString("attribute")).simplified(); 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
", buildIdentifier, tmpAttr); attr=tmpAttr.toInt(); } else attr=lookupAttrName(tmpAttr,iDl); } // Info about context switch KateHlContextModification context = -1; QString unresolvedContext; QString tmpcontext=KateHlManager::self()->syntax->groupItemData(data,QString("context")); if (!tmpcontext.isEmpty()) context=getContextModificationFromString(ContextNameList, tmpcontext,unresolvedContext); // Get the char parameter (eg DetectChar) QChar chr; if (! KateHlManager::self()->syntax->groupItemData(data,QString("char")).isEmpty()) { chr= (KateHlManager::self()->syntax->groupItemData(data,QString("char")))[0]; } // Get the String parameter (eg. StringDetect) QString stringdata=KateHlManager::self()->syntax->groupItemData(data,QString("String")); // Get a second char parameter (char1) (eg Detect2Chars) QChar chr1; if (! KateHlManager::self()->syntax->groupItemData(data,QString("char1")).isEmpty()) { chr1= (KateHlManager::self()->syntax->groupItemData(data,QString("char1")))[0]; } // Will be removed eventually. Atm used for StringDetect, WordDetect, keyword and RegExp const QString & insensitive_str = KateHlManager::self()->syntax->groupItemData(data,QString("insensitive")); bool insensitive = IS_TRUE( insensitive_str ); // 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 its type and set its parameters KateHlItem *tmpItem; if (dataname=="keyword") { bool keywordInsensitive = insensitive_str.isEmpty() ? !casesensitive : insensitive; KateHlKeyword *keyword=new KateHlKeyword(attr,context,regionId,regionId2,keywordInsensitive, m_additionalData[ buildIdentifier ]->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, chr)); else if (dataname=="StringDetect") tmpItem=(new KateHlStringDetect(attr,context,regionId,regionId2,stringdata,insensitive)); else if (dataname=="WordDetect") tmpItem=(new KateHlWordDetect(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); } // remember all to delete them m_hlItemCleanupList.append (tmpItem); return tmpItem; } int KateHighlighting::attribute(int ctx) const { return m_contexts[ctx]->attr; } bool KateHighlighting::attributeRequiresSpellchecking( int attr ) { QList attributeList = attributes(""); if(attr < attributeList.length() && attributeList[attr]->hasProperty(KateExtendedAttribute::Spellchecking)) { return attributeList[attr]->boolProperty(KateExtendedAttribute::Spellchecking); } return true; } QString KateHighlighting::hlKeyForContext(int i) const { int k = 0; QMap::const_iterator it = m_ctxIndex.constEnd(); while ( it != m_ctxIndex.constBegin() ) { --it; k = it.key(); if ( i >= k ) break; } return it.value(); } QString KateHighlighting::hlKeyForAttrib( int i ) const { // find entry. This is faster than QMap::find. m_hlIndex always has an entry // for key '0' (it is "none"), so the result is always valid. int k = 0; QMap::const_iterator it = m_hlIndex.constEnd(); while ( it != m_hlIndex.constBegin() ) { --it; k = it.key(); if ( i >= k ) break; } return it.value(); } bool KateHighlighting::isInWord( QChar c, int attrib ) const { return m_additionalData[ hlKeyForAttrib( attrib ) ]->deliminator.indexOf(c) < 0 && !c.isSpace() && c != QChar::fromAscii('"') && c != QChar::fromAscii('\'') && c != QChar::fromAscii('`'); } bool KateHighlighting::canBreakAt( QChar c, int attrib ) const { return (m_additionalData[ hlKeyForAttrib( attrib ) ]->wordWrapDeliminator.indexOf(c) != -1) && (c != QChar::fromAscii('"') && c != QChar::fromAscii('\'')); } QList KateHighlighting::emptyLines(int attrib) const { #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"hlKeyForAttrib: "<emptyLines; } signed char KateHighlighting::commentRegion(int attr) const { QString commentRegion=m_additionalData[ hlKeyForAttrib( attr ) ]->multiLineRegion; return (commentRegion.isEmpty()?0:(commentRegion.toShort())); } bool KateHighlighting::canComment( int startAttrib, int endAttrib ) const { QString k = hlKeyForAttrib( startAttrib ); return ( k == hlKeyForAttrib( endAttrib ) && ( ( !m_additionalData[k]->multiLineCommentStart.isEmpty() && !m_additionalData[k]->multiLineCommentEnd.isEmpty() ) || ! m_additionalData[k]->singleLineCommentMarker.isEmpty() ) ); } QString KateHighlighting::getCommentStart( int attrib ) const { return m_additionalData[ hlKeyForAttrib( attrib) ]->multiLineCommentStart; } QString KateHighlighting::getCommentEnd( int attrib ) const { return m_additionalData[ hlKeyForAttrib( attrib ) ]->multiLineCommentEnd; } QString KateHighlighting::getCommentSingleLineStart( int attrib ) const { return m_additionalData[ hlKeyForAttrib( attrib) ]->singleLineCommentMarker; } KateHighlighting::CSLPos KateHighlighting::getCommentSingleLinePosition( int attrib ) const { return m_additionalData[ hlKeyForAttrib( attrib) ]->singleLineCommentPosition; } const QHash& KateHighlighting::characterEncodings( int attrib ) const { return m_additionalData[ hlKeyForAttrib( attrib) ]->characterEncodings; } /** * Helper for makeContextList. It parses the xml file for * information, how single or multi line comments are marked */ void KateHighlighting::readCommentConfig() { KateHlManager::self()->syntax->setIdentifier(buildIdentifier); KateSyntaxContextData *data=KateHlManager::self()->syntax->getGroupInfo("general","comment"); QString cmlStart="", cmlEnd="", cmlRegion="", cslStart=""; CSLPos cslPosition=CSLPosColumn0; if (data) { while (KateHlManager::self()->syntax->nextGroup(data)) { if (KateHlManager::self()->syntax->groupData(data,"name")=="singleLine") { cslStart=KateHlManager::self()->syntax->groupData(data,"start"); QString cslpos=KateHlManager::self()->syntax->groupData(data,"position"); if (cslpos=="afterwhitespace") cslPosition=CSLPosAfterWhitespace; else cslPosition=CSLPosColumn0; } else 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); } m_additionalData[buildIdentifier]->singleLineCommentMarker = cslStart; m_additionalData[buildIdentifier]->singleLineCommentPosition = cslPosition; m_additionalData[buildIdentifier]->multiLineCommentStart = cmlStart; m_additionalData[buildIdentifier]->multiLineCommentEnd = cmlEnd; m_additionalData[buildIdentifier]->multiLineRegion = cmlRegion; } void KateHighlighting::readEmptyLineConfig() { KateHlManager::self()->syntax->setIdentifier(buildIdentifier); KateSyntaxContextData *data=KateHlManager::self()->syntax->getGroupInfo("general","emptyLine"); QList exprList; if (data) { while (KateHlManager::self()->syntax->nextGroup(data)) { #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"creating an empty line regular expression"; #endif QString regexprline=KateHlManager::self()->syntax->groupData(data,"regexpr"); bool regexprcase=(KateHlManager::self()->syntax->groupData(data,"casesensitive").toUpper().compare("TRUE")==0); exprList.append(QRegExp(regexprline,regexprcase?Qt::CaseSensitive:Qt::CaseInsensitive)); } KateHlManager::self()->syntax->freeGroupInfo(data); } m_additionalData[buildIdentifier]->emptyLines = exprList; } /** * 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 */ void KateHighlighting::readGlobalKeywordConfig() { deliminator = stdDeliminator; #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"readGlobalKeywordConfig:BEGIN"; #endif // Tell the syntax document class which file we want to parse KateHlManager::self()->syntax->setIdentifier(buildIdentifier); KateSyntaxContextData *data = KateHlManager::self()->syntax->getConfig("general","keywords"); if (data) { #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"Found global keyword config"; #endif casesensitive = IS_TRUE( KateHlManager::self()->syntax->groupItemData(data,QString("casesensitive")) ); //get the weak deliminators weakDeliminator=(KateHlManager::self()->syntax->groupItemData(data,QString("weakDeliminator"))); #ifdef HIGHLIGHTING_DEBUG kDebug(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(""); } #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"readGlobalKeywordConfig:END"; kDebug(13010)<<"delimiterCharacters are: "<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. */ void KateHighlighting::readWordWrapConfig() { #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"readWordWrapConfig:BEGIN"; #endif // Tell the syntax document class which file we want to parse KateHlManager::self()->syntax->setIdentifier(buildIdentifier); KateSyntaxContextData *data = KateHlManager::self()->syntax->getConfig("general","keywords"); QString wordWrapDeliminator = stdDeliminator; if (data) { #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"Found global keyword config"; #endif wordWrapDeliminator = (KateHlManager::self()->syntax->groupItemData(data,QString("wordWrapDeliminator"))); //when no wordWrapDeliminator is defined use the deliminator list if ( wordWrapDeliminator.length() == 0 ) wordWrapDeliminator = deliminator; #ifdef HIGHLIGHTING_DEBUG kDebug(13010) << "word wrap deliminators are " << wordWrapDeliminator; #endif KateHlManager::self()->syntax->freeGroupInfo(data); } #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"readWordWrapConfig:END"; #endif m_additionalData[buildIdentifier]->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() { #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"readfoldignConfig:BEGIN"; #endif // Tell the syntax document class which file we want to parse KateHlManager::self()->syntax->setIdentifier(buildIdentifier); KateSyntaxContextData *data = KateHlManager::self()->syntax->getConfig("general","folding"); if (data) { #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"Found global keyword config"; #endif m_foldingIndentationSensitive = IS_TRUE( KateHlManager::self()->syntax->groupItemData(data, QString("indentationsensitive")) ); KateHlManager::self()->syntax->freeGroupInfo(data); } else { //Default values m_foldingIndentationSensitive = false; } #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"readfoldingConfig:END"; kDebug(13010)<<"############################ use indent for fold are: "<syntax->setIdentifier(buildIdentifier); KateSyntaxContextData *data=KateHlManager::self()->syntax->getGroupInfo("spellchecking","encoding"); if (data) { while (KateHlManager::self()->syntax->nextGroup(data)) { QString encoding = KateHlManager::self()->syntax->groupData(data,"string"); QString character = KateHlManager::self()->syntax->groupData(data,"char"); QString ignored = KateHlManager::self()->syntax->groupData(data,"ignored"); const bool ignoredIsTrue = IS_TRUE(ignored); if(encoding.isEmpty() || (character.isEmpty() && !ignoredIsTrue)) { continue; } QRegExp newLineRegExp("\\r|\\n"); if(encoding.indexOf(newLineRegExp) >= 0) { encoding.replace(newLineRegExp, "<\\n|\\r>"); #ifdef HIGHLIGHTING_DEBUG kDebug() << "Encoding" << encoding << "contains new-line characters. Ignored."; #endif } QChar c = (character.isEmpty() || ignoredIsTrue) ? QChar() : character[0]; addCharacterEncoding(buildIdentifier, encoding, c); } KateHlManager::self()->syntax->freeGroupInfo(data); } data=KateHlManager::self()->syntax->getConfig("spellchecking","configuration"); if (data) { QString policy = KateHlManager::self()->syntax->groupItemData(data,"encodingReplacementPolicy"); QString policyLowerCase = policy.toLower(); int p; if(policyLowerCase == "encodewhenpresent") { p = KateDocument::EncodeWhenPresent; } else if(policyLowerCase == "encodealways") { p = KateDocument::EncodeAlways; } else { p = KateDocument::EncodeNever; } m_additionalData[buildIdentifier]->encodedCharactersInsertionPolicy = p; KateHlManager::self()->syntax->freeGroupInfo(data); } } void KateHighlighting::createContextNameList(QStringList *ContextNameList,int ctx0) { #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"creatingContextNameList:BEGIN"; #endif if (ctx0 == 0) ContextNameList->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")).simplified(); if (tmpAttr.isEmpty()) { tmpAttr = QString("!KATE_INTERNAL_DUMMY! %1").arg(id); errorsAndWarnings += i18n("%1: Deprecated syntax. Context %2 has no symbolic name
", buildIdentifier, id-ctx0); } else tmpAttr = buildPrefix + tmpAttr; (*ContextNameList) << tmpAttr; id++; } KateHlManager::self()->syntax->freeGroupInfo(data); } #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"creatingContextNameList:END"; #endif } KateHlContextModification KateHighlighting::getContextModificationFromString(QStringList *ContextNameList, QString tmpLineEndContext, /*NO CONST*/ QString &unres) { // nothing unresolved unres = ""; // context to push on stack int context = -1; // number of contexts to pop int pops = 0; // we allow arbitrary #stay and #pop at the start bool anyFound = false; while (tmpLineEndContext.startsWith(QLatin1String("#stay")) || tmpLineEndContext.startsWith(QLatin1String("#pop"))) { // ignore stay if (tmpLineEndContext.startsWith(QLatin1String("#stay"))) { tmpLineEndContext.remove (0, 5); } else // count the pops { ++pops; tmpLineEndContext.remove (0, 4); } anyFound = true; } /** * we want a ! if we have found any pop or push and still have stuff in the string... */ if (anyFound && !tmpLineEndContext.isEmpty()) { if (tmpLineEndContext.startsWith('!')) tmpLineEndContext.remove (0, 1); } /** * empty string, done */ if (tmpLineEndContext.isEmpty()) return KateHlContextModification (context, pops); /** * handle the remaining string, this might be a ##contextname * or a normal contextname.... */ if ( tmpLineEndContext.contains("##")) { int o = tmpLineEndContext.indexOf("##"); // FIXME at least with 'foo##bar'-style contexts the rules are picked up // but the default attribute is not QString tmp=tmpLineEndContext.mid(o+2); if (!embeddedHls.contains(tmp)) embeddedHls.insert(tmp,KateEmbeddedHlInfo()); unres=tmp+':'+tmpLineEndContext.left(o); #ifdef HIGHLIGHTING_DEBUG kDebug(13010) << "unres = " << unres; #endif context=0; } else { context=ContextNameList->indexOf(buildPrefix+tmpLineEndContext); if (context==-1) { context=tmpLineEndContext.toInt(); errorsAndWarnings+=i18n( "%1:Deprecated syntax. Context %2 not addressed by a symbolic name" , buildIdentifier, tmpLineEndContext); } //#warning restructure this the name list storage. // context=context+buildContext0Offset; } return KateHlContextModification (context, pops); } /** * 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(); embeddedHighlightingModes.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 { #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"**************** Outer loop in make ContextList"; kDebug(13010)<<"**************** Hl List count:"<identifierForName(it.key()); #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"Location is:"<< identifierToUse; #endif if (identifierToUse.isEmpty() ) kWarning(13010)<<"Unknown highlighting description referenced:" << it.key() << "in" << identifier; buildPrefix=it.key()+':'; // attribute names get prefixed by the names // of the highlighting definitions they belong to #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"setting ("< 0); } void KateHighlighting::handleKateHlIncludeRules() { // if there are noe include rules to take care of, just return #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"KateHlIncludeRules, which need attention: " <incCtx.newContext==-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.erase(it); it=it1; } else { // resolve name to id (*it)->incCtx=getContextModificationFromString(&ContextNameList,(*it)->incCtxN,dummy).newContext; #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"Resolved "<<(*it)->incCtxN<< " to "<<(*it)->incCtx.newContext<<" for include rule"; #endif // It would be good to look here somehow, if the result is valid } } else ++it; //nothing to do, already resolved (by the cross definition reference resolver) } // now that all KateHlIncludeRule items should be valid and completely resolved, // do the real inclusion of the rules. // recursiveness is needed, because context 0 could include context 1, which // itself includes context 2 and so on. // In that case we have to handle context 2 first, then 1, 0 //TODO: catch circular references: eg 0->1->2->3->1 for (int i=0; i= list->count()) return; //invalid iterator, shouldn't happen, but better have a rule prepared ;) int index1 = index; int ctx = list->at(index1)->ctx; if (ctx == -1) return; // skip already processed entries // 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 (index < list->count() && list->at(index)->ctx == ctx) { index1 = index; ++index; } // iterate over each include rule for the context the function has been called for. while (index1 >= 0 && index1 < list->count() && list->at(index1)->ctx == ctx) { KateHlContextModification ctx1 = list->at(index1)->incCtx; //let's see, if the included context includes other contexts for (int index2 = 0; index2 < list->count(); ++index2) { if (list->at(index2)->ctx == ctx1.newContext) { if (index2 == index1) { // prevent accidental infinite recursion kWarning() << "infinite recursion in IncludeRules in language file for " << iName << "in context" << list->at(index1)->incCtxN; continue; } //yes it does, so first handle that include rules, since we want to // include those subincludes too handleKateHlIncludeRulesRecursive(index2, 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.newContext]; // kDebug(3010)<<"linking included rules from "<at(index1)->includeAttrib ) dest->attr = src->attr; // insert the included context's rules starting at position p int p = list->at(index1)->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]; index = index1; //backup the iterator --index1; //move to the next entry, which has to be take care of list->at(index)->ctx = -1; // set ctx to -1 to mark already processed entries } } /** * Add one highlight to the contextlist. * * @return the number of contexts after this is added. */ int KateHighlighting::addToContextList(const QString &ident, int ctx0) { //kDebug(13010)<<"=== Adding hl with ident '"< iDl = internalIDList; createContextNameList(&ContextNameList,ctx0); #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"Parsing Context structure"; #endif //start the real work uint i=buildContext0Offset; KateSyntaxContextData * data = KateHlManager::self()->syntax->getGroupInfo("highlighting", "context"); if (data) { while (KateHlManager::self()->syntax->nextGroup(data)) { #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"Found a context in file, building structure now"; #endif //BEGIN - Translation of the attribute parameter QString tmpAttr=KateHlManager::self()->syntax->groupData(data,QString("attribute")).simplified(); int attr; if (QString("%1").arg(tmpAttr.toInt())==tmpAttr) attr=tmpAttr.toInt(); else attr=lookupAttrName(tmpAttr,iDl); //END - Translation of the attribute parameter QString tmpLineEndContext=KateHlManager::self()->syntax->groupData(data,QString("lineEndContext")).simplified(); KateHlContextModification context; context=getContextModificationFromString(&ContextNameList, tmpLineEndContext,dummy); QString tmpNIBF = KateHlManager::self()->syntax->groupData(data, QString("noIndentationBasedFolding") ); bool noIndentationBasedFolding=IS_TRUE(tmpNIBF); //BEGIN get fallthrough props KateHlContextModification ftc = 0; // fallthrough context QString tmpFt = KateHlManager::self()->syntax->groupData(data, QString("fallthrough") ); const bool ft = IS_TRUE(tmpFt); if ( ft ) { QString tmpFtc = KateHlManager::self()->syntax->groupData( data, QString("fallthroughContext") ); ftc=getContextModificationFromString(&ContextNameList, tmpFtc,dummy); // stay is not allowed, we need to #pop or push some context... if (ftc.type == KateHlContextModification::doNothing) ftc = 0; #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"Setting fall through context (context "<syntax->groupData( data, QString("lineEmptyContext") ); KateHlContextModification emptyLineContextModification; if (!emptyLineContext.isEmpty()) emptyLineContextModification = getContextModificationFromString(&ContextNameList, emptyLineContext, dummy); bool dynamic = false; QString tmpDynamic = KateHlManager::self()->syntax->groupData(data, QString("dynamic") ); if ( tmpDynamic.toLower() == "true" || tmpDynamic.toInt() == 1 ) dynamic = true; KateHlContext *ctxNew = new KateHlContext ( ident, attr, context, ft, ftc, dynamic, noIndentationBasedFolding, !emptyLineContext.isEmpty(), emptyLineContextModification); m_contexts.push_back (ctxNew); #ifdef HIGHLIGHTING_DEBUG kDebug(13010) << "INDEX: " << i << " LENGTH " << m_contexts.size()-1; #endif //Let's create all items for the context while (KateHlManager::self()->syntax->nextItem(data)) { // kDebug(13010)<< "In make Contextlist: Item:"; // KateHlIncludeRules : add a pointer to each item in that context // TODO add a attrib includeAttrib QString tag = KateHlManager::self()->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 = IS_TRUE( incAttrib ); // only context refernces of type Name, ##Name, and Subname##Name are allowed if (incCtx.startsWith("##") || (!incCtx.startsWith('#'))) { int incCtxi = incCtx.indexOf ("##"); //#stay, #pop is not interesting here if (incCtxi >= 0) { QString incSet = incCtx.mid(incCtxi + 2); QString incCtxN = incSet + ':' + incCtx.left(incCtxi); //a cross highlighting reference #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"Cross highlight reference , context "<items.count(),incCtxN,includeAttrib); //use the same way to determine cross hl file references as other items do if (!embeddedHls.contains(incSet)) embeddedHls.insert(incSet,KateEmbeddedHlInfo()); #ifdef HIGHLIGHTING_DEBUG else kDebug(13010)<<"Skipping embeddedHls.insert for "<incCtx), incCtxN); includeRules.append(ir); } else { // a local reference -> just initialize the include rule structure incCtx=buildPrefix+incCtx.simplified (); includeRules.append(new KateHlIncludeRule(i,m_contexts[i]->items.count(),incCtx, includeAttrib)); } } continue; } // TODO -- can we remove the block below?? #if 0 QString tag = KateHlManager::self()->syntax->groupKateExtendedAttribute(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->groupKateExtendedAttribute( data, QString("context")),dummy); // the index is *required* if ( ctxId > -1) { // we can even reuse rules of 0 if we want to:) kDebug(13010)<<"makeContextList["<items.first(); c; c = m_contexts[ctxId]->items.next() ) m_contexts[i]->items.append(c); } else kDebug(13010)<<"Context "<items.append(c); // Not supported completely atm and only one level. Subitems.(all have // to be matched to at once) KateSyntaxContextData * datasub = KateHlManager::self()->syntax->getSubItems(data); for (bool tmpbool=KateHlManager::self()->syntax->nextItem(datasub); 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); } } i++; } KateHlManager::self()->syntax->freeGroupInfo(data); } else { // error handling: no "context" element at all in the xml file noHl = true; kWarning() << "There is no \"context\" in the highlighting file:" << buildIdentifier; } if (RegionList.count()!=1) folding=true; folding = folding || m_foldingIndentationSensitive; //BEGIN Resolve multiline region if possible if (!m_additionalData[ ident ]->multiLineRegion.isEmpty()) { long commentregionid=RegionList.indexOf( m_additionalData[ ident ]->multiLineRegion ); if (-1==commentregionid) { errorsAndWarnings+=i18n( "%1: Specified multiline comment region (%2) could not be resolved
" , buildIdentifier, m_additionalData[ ident ]->multiLineRegion ); m_additionalData[ ident ]->multiLineRegion.clear(); #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"ERROR comment region attribute could not be resolved"; #endif } else { m_additionalData[ ident ]->multiLineRegion=QString::number(commentregionid+1); #ifdef HIGHLIGHTING_DEBUG kDebug(13010)<<"comment region resolved to:"<multiLineRegion; #endif } } //END Resolve multiline region if possible return i; } void KateHighlighting::clearAttributeArrays () { QMutableHashIterator< QString, QList > it = m_attributeArrays; while (it.hasNext()) { it.next(); // k, schema correct, let create the data KateAttributeList defaultStyleList; KateHlManager::self()->getDefaults(it.key(), defaultStyleList); QList itemDataList; getKateExtendedAttributeList(it.key(), itemDataList); uint nAttribs = itemDataList.count(); QList& array = it.value(); array.clear(); for (uint z = 0; z < nAttribs; z++) { KateExtendedAttribute::Ptr itemData = itemDataList.at(z); KTextEditor::Attribute::Ptr newAttribute( new KTextEditor::Attribute(*defaultStyleList.at(itemData->defaultStyleIndex())) ); if (itemData && itemData->hasAnyProperty()) *newAttribute += *itemData; array.append(newAttribute); } } } QList KateHighlighting::attributes (const QString &schema) { // found it, already floating around if (m_attributeArrays.contains(schema)) return m_attributeArrays[schema]; // k, schema correct, let create the data QList array; KateAttributeList defaultStyleList; KateHlManager::self()->getDefaults(schema, defaultStyleList); QList itemDataList; getKateExtendedAttributeList(schema, itemDataList); uint nAttribs = itemDataList.count(); for (uint z = 0; z < nAttribs; z++) { KateExtendedAttribute::Ptr itemData = itemDataList.at(z); KTextEditor::Attribute::Ptr newAttribute( new KTextEditor::Attribute(*defaultStyleList.at(itemData->defaultStyleIndex())) ); if (itemData && itemData->hasAnyProperty()) *newAttribute += *itemData; array.append(newAttribute); } m_attributeArrays.insert(schema, array); return array; } QStringList KateHighlighting::getEmbeddedHighlightingModes() const { return embeddedHighlightingModes; } bool KateHighlighting::isEmptyLine(const Kate::TextLineData *textline) const { const QString &txt=textline->string(); if (txt.isEmpty()) return true; QList l; l=emptyLines(textline->attribute(0)); if (l.isEmpty()) return false; foreach(const QRegExp &re,l) { if (re.exactMatch(txt)) return true; } return false; } //END // kate: space-indent on; indent-width 2; replace-tabs on;