• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

Kate

katehighlight.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002    Copyright (C) 2007 Mirko Stocker <me@misto.ch>
00003    Copyright (C) 2003, 2004 Anders Lund <anders@alweb.dk>
00004    Copyright (C) 2003 Hamish Rodda <rodda@kde.org>
00005    Copyright (C) 2001,2002 Joseph Wenninger <jowenn@kde.org>
00006    Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
00007    Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
00008 
00009    This library is free software; you can redistribute it and/or
00010    modify it under the terms of the GNU Library General Public
00011    License version 2 as published by the Free Software Foundation.
00012 
00013    This library is distributed in the hope that it will be useful,
00014    but WITHOUT ANY WARRANTY; without even the implied warranty of
00015    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016    Library General Public License for more details.
00017 
00018    You should have received a copy of the GNU Library General Public License
00019    along with this library; see the file COPYING.LIB.  If not, write to
00020    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00021    Boston, MA 02110-1301, USA.
00022 */
00023 
00024 //BEGIN INCLUDES
00025 #include "katehighlight.h"
00026 
00027 #include "katehighlighthelpers.h"
00028 #include "katetextline.h"
00029 #include "katedocument.h"
00030 #include "katesyntaxdocument.h"
00031 #include "katerenderer.h"
00032 #include "kateglobal.h"
00033 #include "kateschema.h"
00034 #include "kateconfig.h"
00035 #include "kateextendedattribute.h"
00036 
00037 #include <kconfig.h>
00038 #include <kglobal.h>
00039 #include <kcomponentdata.h>
00040 #include <kmimetype.h>
00041 #include <klocale.h>
00042 #include <kmenu.h>
00043 #include <kglobalsettings.h>
00044 #include <kdebug.h>
00045 #include <kstandarddirs.h>
00046 #include <kmessagebox.h>
00047 #include <kapplication.h>
00048 
00049 #include <QtCore/QSet>
00050 #include <QtGui/QAction>
00051 #include <QtGui/QApplication>
00052 #include <QtCore/QStringList>
00053 #include <QtCore/QTextStream>
00054 //END
00055 
00056 //BEGIN defines
00057 // x is a QString. if x is "true" or "1" this expression returns "true"
00058 #define IS_TRUE(x) x.toLower() == QLatin1String("true") || x.toInt() == 1
00059 //END defines
00060 
00061 //BEGIN STATICS
00062 static const QString stdDeliminator = QString (" \t.():!+,-<=>%&*/;?[]^{|}~\\");
00063 //END
00064 
00065 //BEGIN KateHighlighting
00066 KateHighlighting::KateHighlighting(const KateSyntaxModeListItem *def) : refCount(0)
00067 {
00068   errorsAndWarnings = "";
00069   building=false;
00070   noHl = false;
00071   m_foldingIndentationSensitive = false;
00072   folding=false;
00073 
00074   if (def == 0)
00075   {
00076     noHl = true;
00077     iName = "None"; // not translated internal name (for config and more)
00078     iNameTranslated = i18nc("Syntax highlighting", "None"); // user visible name
00079     iSection = "";
00080     iHidden = false;
00081     m_additionalData.insert( "none", new HighlightPropertyBag );
00082     m_additionalData["none"]->deliminator = stdDeliminator;
00083     m_additionalData["none"]->wordWrapDeliminator = stdDeliminator;
00084     m_hlIndex[0] = "none";
00085   }
00086   else
00087   {
00088     iName = def->name;
00089     iNameTranslated = def->nameTranslated;
00090     iSection = def->section;
00091     iHidden = def->hidden;
00092     identifier = def->identifier;
00093     iVersion=def->version;
00094     iStyle = def->style;
00095     iAuthor=def->author;
00096     iLicense=def->license;
00097   }
00098 
00099    deliminator = stdDeliminator;
00100 }
00101 
00102 KateHighlighting::~KateHighlighting()
00103 {
00104   // cleanup ;)
00105   cleanup ();
00106 
00107   qDeleteAll(m_additionalData);
00108 }
00109 
00110 void KateHighlighting::cleanup ()
00111 {
00112   qDeleteAll (m_contexts);
00113   m_contexts.clear ();
00114 
00115   m_attributeArrays.clear ();
00116 
00117   internalIDList.clear();
00118 }
00119 
00120 KateHlContext *KateHighlighting::generateContextStack (QVector<short> &contextStack,
00121                                                        KateHlContextModification modification,
00122                                                        int &indexLastContextPreviousLine)
00123 {
00124   while (true)
00125   {
00126     switch (modification.type)
00127     {
00132       case KateHlContextModification::doNothing:
00133         return contextNum (contextStack.isEmpty() ? 0 : contextStack.last());
00134 
00139       case KateHlContextModification::doPush:
00140         contextStack.append (modification.newContext);
00141         return contextNum (modification.newContext);
00142 
00146       case KateHlContextModification::doPopsAndPush:
00147         // resize stack
00148         contextStack.resize ((modification.pops >= contextStack.size()) ? 0 : (contextStack.size() - modification.pops));
00149 
00150         // push imediate the new context....
00151         // don't handle the previous line stuff at all....
00152         // ### TODO ### think about this
00153         contextStack.append (modification.newContext);
00154         return contextNum (modification.newContext);
00155 
00159       default:
00160       {
00161         // resize stack
00162         contextStack.resize ((modification.pops >= contextStack.size()) ? 0 : (contextStack.size() - modification.pops));
00163 
00164         // handling of context of previous line....
00165         if (indexLastContextPreviousLine >= (contextStack.size()-1))
00166         {
00167           // set new index, if stack is empty, this is -1, done for eternity...
00168           indexLastContextPreviousLine = contextStack.size() - 1;
00169 
00170           // stack already empty, nothing to do...
00171           if ( contextStack.isEmpty() )
00172             return contextNum (0);
00173 
00174           KateHlContext *c = contextNum(contextStack.last());
00175 
00176           // this must be a valid context, or our context stack is borked....
00177           Q_ASSERT (c);
00178 
00179           // handle line end context as new modificationContext
00180           modification = c->lineEndContext;
00181           continue;
00182         }
00183 
00184         return contextNum (contextStack.isEmpty() ? 0 : contextStack.last());
00185       }
00186     }
00187   }
00188 
00189   // should never be reached
00190   Q_ASSERT (false);
00191 
00192   return contextNum (0);
00193 }
00194 
00198 int KateHighlighting::makeDynamicContext(KateHlContext *model, const QStringList *args)
00199 {
00200   QPair<KateHlContext *, QString> key(model, args->front());
00201   short value;
00202 
00203   if (dynamicCtxs.contains(key))
00204     value = dynamicCtxs[key];
00205   else
00206   {
00207     kDebug(13010) << "new stuff: " << startctx;
00208 
00209     KateHlContext *newctx = model->clone(args);
00210 
00211     m_contexts.push_back (newctx);
00212 
00213     value = startctx++;
00214     dynamicCtxs[key] = value;
00215     KateHlManager::self()->incDynamicCtxs();
00216   }
00217 
00218   // kDebug(13010) << "Dynamic context: using context #" << value << " (for model " << model << " with args " << *args << ")";
00219 
00220   return value;
00221 }
00222 
00227 void KateHighlighting::dropDynamicContexts()
00228 {
00229   for (int i=base_startctx; i < m_contexts.size(); ++i)
00230     delete m_contexts[i];
00231 
00232   m_contexts.resize (base_startctx);
00233 
00234   dynamicCtxs.clear();
00235   startctx = base_startctx;
00236 }
00237 
00246 void KateHighlighting::doHighlight ( KateTextLine *prevLine,
00247                                      KateTextLine *textLine,
00248                                      QVector<int> &foldingList,
00249                                      bool &ctxChanged )
00250 {
00251   if (!textLine)
00252     return;
00253 
00254   // in all cases, remove old hl, or we will grow to infinite ;)
00255   textLine->clearAttributes ();
00256 
00257   // no hl set, nothing to do more than the above cleaning ;)
00258   if (noHl) {
00259     textLine->addAttribute (0, textLine->length(), KateExtendedAttribute::dsNormal);
00260     return;
00261   }
00262 
00263   // duplicate the ctx stack, only once !
00264   QVector<short> ctx (prevLine->ctxArray());
00265 
00266   int previousLine = -1;
00267   KateHlContext *context;
00268 
00269   if (ctx.isEmpty())
00270   {
00271     // If the stack is empty, we assume to be in Context 0 (Normal)
00272     context = contextNum(0);
00273   }
00274   else
00275   {
00276     //kDebug(13010) << "\t\tctxNum = " << ctxNum << " contextList[ctxNum] = " << contextList[ctxNum]; // ellis
00277 
00278     //if (lineContinue)   kDebug(13010)<<QString("The old context should be %1").arg((int)ctxNum);
00279     context = contextNum(ctx.last());
00280 
00281     //kDebug(13010)<<"test1-2-1-text2";
00282 
00283     previousLine = ctx.size()-1; //position of the last context ID of th previous line within the stack
00284 
00285     // hl continue set or not ???
00286     if (prevLine->hlLineContinue())
00287     {
00288       prevLine--;
00289     }
00290     else
00291     {
00292       context = generateContextStack(ctx, context->lineEndContext, previousLine); //get stack ID to use
00293     }
00294 
00295     //kDebug(13010)<<"test1-2-1-text4";
00296 
00297     //if (lineContinue)   kDebug(13010)<<QString("The new context is %1").arg((int)ctxNum);
00298   }
00299 
00300   // text, for programming convenience :)
00301   QChar lastChar = ' ';
00302   const QString& text = textLine->string();
00303   const int len = textLine->length();
00304 
00305   // calc at which char the first char occurs, set it to length of line if never
00306   const int firstChar = textLine->firstChar();
00307   const int startNonSpace = (firstChar == -1) ? len : firstChar;
00308 
00309   // last found item
00310   KateHlItem *item = 0;
00311 
00312   // loop over the line, offset gives current offset
00313   int offset = 0;
00314   while (offset < len)
00315   {
00316     bool anItemMatched = false;
00317     bool standardStartEnableDetermined = false;
00318     bool customStartEnableDetermined = false;
00319 
00320     int index = 0;
00321     for (item = context->items.empty() ? 0 : context->items[0]; item; item = (++index < context->items.size()) ? context->items[index] : 0 )
00322     {
00323       // does we only match if we are firstNonSpace?
00324       if (item->firstNonSpace && (offset > startNonSpace))
00325         continue;
00326 
00327       // have we a column specified? if yes, only match at this column
00328       if ((item->column != -1) && (item->column != offset))
00329         continue;
00330 
00331       if (!item->alwaysStartEnable)
00332       {
00333         if (item->customStartEnable)
00334         {
00335             if (customStartEnableDetermined || m_additionalData[context->hlId]->deliminator.contains(lastChar))
00336             customStartEnableDetermined = true;
00337           else
00338             continue;
00339         }
00340         else
00341         {
00342           if (standardStartEnableDetermined || stdDeliminator.contains(lastChar))
00343             standardStartEnableDetermined = true;
00344           else
00345             continue;
00346         }
00347       }
00348 
00349       int offset2 = item->checkHgl(text, offset, len-offset);
00350 
00351       if (offset2 <= offset)
00352         continue;
00353       // BUG 144599: Ignore a context change that would push the same context
00354       // without eating anything... this would be an infinite loop!
00355       if ( item->lookAhead && ( item->ctx.pops < 2 && item->ctx.newContext == ( ctx.isEmpty() ? 0 : ctx.last() ) ) )
00356         continue;
00357 
00358       if (item->region2)
00359       {
00360         // kDebug(13010)<<QString("Region mark 2 detected: %1").arg(item->region2);
00361         if ( !foldingList.isEmpty() && ((item->region2 < 0) && (int)foldingList[foldingList.size()-2] == -item->region2 ) )
00362         {
00363           foldingList.resize (foldingList.size()-2);
00364         }
00365         else
00366         {
00367           foldingList.resize (foldingList.size()+2);
00368           foldingList[foldingList.size()-2] = (uint)item->region2;
00369           if (item->region2<0) //check not really needed yet
00370             foldingList[foldingList.size()-1] = offset2;
00371           else
00372             foldingList[foldingList.size()-1] = offset;
00373         }
00374 
00375       }
00376 
00377       if (item->region)
00378       {
00379         // kDebug(13010)<<QString("Region mark detected: %1").arg(item->region);
00380 
00381       /* if ( !foldingList->isEmpty() && ((item->region < 0) && (*foldingList)[foldingList->size()-1] == -item->region ) )
00382         {
00383           foldingList->resize (foldingList->size()-1, QGArray::SpeedOptim);
00384         }
00385         else*/
00386         {
00387           foldingList.resize (foldingList.size()+2);
00388           foldingList[foldingList.size()-2] = item->region;
00389           if (item->region<0) //check not really needed yet
00390             foldingList[foldingList.size()-1] = offset2;
00391           else
00392             foldingList[foldingList.size()-1] = offset;
00393         }
00394 
00395       }
00396 
00397       // regenerate context stack if needed
00398       context = generateContextStack (ctx, item->ctx, previousLine);
00399 
00400       // dynamic context: substitute the model with an 'instance'
00401       if (context->dynamic)
00402       {
00403         QStringList *lst = item->capturedTexts();
00404         if (lst != 0)
00405         {
00406           // Replace the top of the stack and the current context
00407           int newctx = makeDynamicContext(context, lst);
00408           if (ctx.size() > 0)
00409             ctx[ctx.size() - 1] = newctx;
00410 
00411           context = contextNum(newctx);
00412         }
00413         delete lst;
00414       }
00415 
00416       // dominik: look ahead w/o changing offset?
00417       if (!item->lookAhead)
00418       {
00419         if (offset2 > len)
00420           offset2 = len;
00421 
00422         // even set attributes ;)
00423         int attribute = item->onlyConsume ? context->attr : item->attr;
00424         if (attribute > 0)
00425           textLine->addAttribute (offset, offset2-offset, attribute);
00426 
00427         offset = offset2;
00428         lastChar = text[offset-1];
00429       }
00430 
00431       anItemMatched = true;
00432       break;
00433     }
00434 
00435     // something matched, continue loop
00436     if (anItemMatched)
00437       continue;
00438 
00439     // nothing found: set attribute of one char
00440     // anders: unless this context does not want that!
00441     if ( context->fallthrough )
00442     {
00443     // set context to context->ftctx.
00444       context=generateContextStack(ctx, context->ftctx, previousLine);  //regenerate context stack
00445 
00446     //kDebug(13010)<<"context num after fallthrough at col "<<z<<": "<<ctxNum;
00447     // the next is necessary, as otherwise keyword (or anything using the std delimitor check)
00448     // immediately after fallthrough fails. Is it bad?
00449     // jowenn, can you come up with a nicer way to do this?
00450     /*  if (offset)
00451         lastChar = text[offset - 1];
00452       else
00453         lastChar = '\\';*/
00454       continue;
00455     }
00456     else
00457     {
00458       // set attribute if any
00459       if (context->attr > 0)
00460         textLine->addAttribute (offset, 1, context->attr);
00461 
00462       lastChar = text[offset];
00463       offset++;
00464     }
00465   }
00466 
00467   // has the context stack changed ?
00468   if (ctx == textLine->ctxArray())
00469   {
00470     ctxChanged = false;
00471   }
00472   else
00473   {
00474     ctxChanged = true;
00475 
00476     // assign ctx stack !
00477     textLine->setContext(ctx);
00478   }
00479 
00480   // write hl continue flag
00481   textLine->setHlLineContinue (item && item->lineContinue());
00482 
00483   if (m_foldingIndentationSensitive) {
00484     bool noindent=false;
00485     for(int i=ctx.size()-1; i>=0; --i) {
00486       if (contextNum(ctx[i])->noIndentationBasedFolding) {
00487         noindent=true;
00488         break;
00489       }
00490     }
00491     textLine->setNoIndentBasedFolding(noindent);
00492   }
00493 
00494   //set the dsNormal attribute if we haven't found anything else
00495   if(textLine->attributesList().empty()) {
00496     textLine->addAttribute (0, textLine->length(), KateExtendedAttribute::dsNormal);
00497   }
00498 }
00499 
00500 void KateHighlighting::getKateExtendedAttributeList (const QString &schema, QList<KateExtendedAttribute::Ptr> &list)
00501 {
00502   KConfigGroup config(KateHlManager::self()->getKConfig(),
00503                       "Highlighting " + iName + " - Schema " + schema);
00504 
00505   list.clear();
00506   createKateExtendedAttribute(list);
00507 
00508   foreach (KateExtendedAttribute::Ptr p, list)
00509   {
00510     Q_ASSERT(p);
00511 
00512     QStringList s = config.readEntry(p->name(), QStringList());
00513 
00514 //    kDebug(13010)<<p->name<<s.count();
00515     if (s.count()>0)
00516     {
00517 
00518       while(s.count()<9) s<<"";
00519       p->clear();
00520 
00521       QString tmp=s[0]; if (!tmp.isEmpty()) p->setDefaultStyleIndex(tmp.toInt());
00522 
00523       QRgb col;
00524 
00525       tmp=s[1]; if (!tmp.isEmpty()) {
00526          col=tmp.toUInt(0,16); p->setForeground(QColor(col)); }
00527 
00528       tmp=s[2]; if (!tmp.isEmpty()) {
00529          col=tmp.toUInt(0,16); p->setSelectedForeground(QColor(col)); }
00530 
00531       tmp=s[3]; if (!tmp.isEmpty()) p->setFontBold(tmp!="0");
00532 
00533       tmp=s[4]; if (!tmp.isEmpty()) p->setFontItalic(tmp!="0");
00534 
00535       tmp=s[5]; if (!tmp.isEmpty()) p->setFontStrikeOut(tmp!="0");
00536 
00537       tmp=s[6]; if (!tmp.isEmpty()) p->setFontUnderline(tmp!="0");
00538 
00539       tmp=s[7]; if (!tmp.isEmpty()) {
00540          col=tmp.toUInt(0,16); p->setBackground(QColor(col)); }
00541 
00542       tmp=s[8]; if (!tmp.isEmpty()) {
00543          col=tmp.toUInt(0,16); p->setSelectedBackground(QColor(col)); }
00544 
00545     }
00546   }
00547 }
00548 
00549 void KateHighlighting::getKateExtendedAttributeListCopy( const QString &schema, QList< KateExtendedAttribute::Ptr >& list )
00550 {
00551   QList<KateExtendedAttribute::Ptr> attributes;
00552   getKateExtendedAttributeList(schema, attributes);
00553 
00554   list.clear();
00555 
00556   foreach (const KateExtendedAttribute::Ptr &attribute, attributes)
00557     list.append(KateExtendedAttribute::Ptr(new KateExtendedAttribute(*attribute.data())));
00558 }
00559 
00560 
00567 void KateHighlighting::setKateExtendedAttributeList(uint schema, QList<KateExtendedAttribute::Ptr> &list)
00568 {
00569   KConfigGroup config(KateHlManager::self()->getKConfig(),
00570                       "Highlighting " + iName + " - Schema "
00571                       + KateGlobal::self()->schemaManager()->name(schema));
00572 
00573   QStringList settings;
00574 
00575   foreach (const KateExtendedAttribute::Ptr& p, list)
00576   {
00577     Q_ASSERT(p);
00578 
00579     settings.clear();
00580     settings<<QString::number(p->defaultStyleIndex(),10);
00581     settings<<(p->hasProperty(QTextFormat::ForegroundBrush)?QString::number(p->foreground().color().rgb(),16):"");
00582     settings<<(p->hasProperty(KTextEditor::Attribute::SelectedForeground)?QString::number(p->selectedForeground().color().rgb(),16):"");
00583     settings<<(p->hasProperty(QTextFormat::FontWeight)?(p->fontBold()?"1":"0"):"");
00584     settings<<(p->hasProperty(QTextFormat::FontItalic)?(p->fontItalic()?"1":"0"):"");
00585     settings<<(p->hasProperty(QTextFormat::FontStrikeOut)?(p->fontStrikeOut()?"1":"0"):"");
00586     settings<<(p->hasProperty(QTextFormat::FontUnderline)?(p->fontUnderline()?"1":"0"):"");
00587     settings<<(p->hasProperty(QTextFormat::BackgroundBrush)?QString::number(p->background().color().rgb(),16):"");
00588     settings<<(p->hasProperty(KTextEditor::Attribute::SelectedBackground)?QString::number(p->selectedBackground().color().rgb(),16):"");
00589     settings<<"---";
00590     config.writeEntry(p->name(),settings);
00591   }
00592 }
00593 
00597 void KateHighlighting::use()
00598 {
00599   if (refCount == 0)
00600     init();
00601 
00602   refCount++;
00603 }
00604 
00608 void KateHighlighting::release()
00609 {
00610   refCount--;
00611 
00612   if (refCount == 0)
00613     done();
00614 }
00615 
00620 void KateHighlighting::init()
00621 {
00622   if (noHl)
00623     return;
00624 
00625   // cu contexts
00626   for (int i=0; i < m_contexts.size(); ++i)
00627     delete m_contexts[i];
00628   m_contexts.clear ();
00629 
00630   makeContextList();
00631 }
00632 
00633 
00638 void KateHighlighting::done()
00639 {
00640   if (noHl)
00641     return;
00642 
00643   cleanup ();
00644 }
00645 
00653 void KateHighlighting::createKateExtendedAttribute(QList<KateExtendedAttribute::Ptr> &list)
00654 {
00655   // If no highlighting is selected we need only one default.
00656   if (noHl)
00657   {
00658     list.append(KateExtendedAttribute::Ptr(new KateExtendedAttribute(i18n("Normal Text"), KateExtendedAttribute::dsNormal)));
00659     return;
00660   }
00661 
00662   // If the internal list isn't already available read the config file
00663   if (internalIDList.isEmpty())
00664     makeContextList();
00665 
00666   list=internalIDList;
00667 }
00668 
00672 void KateHighlighting::addToKateExtendedAttributeList()
00673 {
00674   //Tell the syntax document class which file we want to parse and which data group
00675   KateHlManager::self()->syntax->setIdentifier(buildIdentifier);
00676   KateSyntaxContextData *data = KateHlManager::self()->syntax->getGroupInfo("highlighting","itemData");
00677 
00678   //begin with the real parsing
00679   while (KateHlManager::self()->syntax->nextGroup(data))
00680   {
00681     // read all attributes
00682     QString color = KateHlManager::self()->syntax->groupData(data,QString("color"));
00683     QString selColor = KateHlManager::self()->syntax->groupData(data,QString("selColor"));
00684     QString bold = KateHlManager::self()->syntax->groupData(data,QString("bold"));
00685     QString italic = KateHlManager::self()->syntax->groupData(data,QString("italic"));
00686     QString underline = KateHlManager::self()->syntax->groupData(data,QString("underline"));
00687     QString strikeOut = KateHlManager::self()->syntax->groupData(data,QString("strikeOut"));
00688     QString bgColor = KateHlManager::self()->syntax->groupData(data,QString("backgroundColor"));
00689     QString selBgColor = KateHlManager::self()->syntax->groupData(data,QString("selBackgroundColor"));
00690 
00691     KateExtendedAttribute::Ptr newData(new KateExtendedAttribute(
00692             buildPrefix+KateHlManager::self()->syntax->groupData(data,QString("name")).simplified(),
00693             KateExtendedAttribute::indexForStyleName(KateHlManager::self()->syntax->groupData(data,QString("defStyleNum")))));
00694 
00695     /* here the custom style overrides are specified, if needed */
00696     if (!color.isEmpty()) newData->setForeground(QColor(color));
00697     if (!selColor.isEmpty()) newData->setSelectedForeground(QColor(selColor));
00698     if (!bold.isEmpty()) newData->setFontBold( IS_TRUE(bold) );
00699     if (!italic.isEmpty()) newData->setFontItalic( IS_TRUE(italic) );
00700     // new attributes for the new rendering view
00701     if (!underline.isEmpty()) newData->setFontUnderline( IS_TRUE(underline) );
00702     if (!strikeOut.isEmpty()) newData->setFontStrikeOut( IS_TRUE(strikeOut) );
00703     if (!bgColor.isEmpty()) newData->setBackground(QColor(bgColor));
00704     if (!selBgColor.isEmpty()) newData->setSelectedBackground(QColor(selBgColor));
00705 
00706     internalIDList.append(newData);
00707   }
00708 
00709   //clean up
00710   if (data)
00711     KateHlManager::self()->syntax->freeGroupInfo(data);
00712 }
00713 
00724 int  KateHighlighting::lookupAttrName(const QString& name, QList<KateExtendedAttribute::Ptr> &iDl)
00725 {
00726   for (int i = 0; i < iDl.count(); i++)
00727     if (iDl.at(i)->name() == buildPrefix+name)
00728       return i;
00729 
00730   kDebug(13010)<<"Couldn't resolve itemDataName:"<<name;
00731   return 0;
00732 }
00733 
00747 KateHlItem *KateHighlighting::createKateHlItem(KateSyntaxContextData *data,
00748                                                QList<KateExtendedAttribute::Ptr> &iDl,
00749                                                QStringList *RegionList,
00750                                                QStringList *ContextNameList)
00751 {
00752   // No highlighting -> exit
00753   if (noHl)
00754     return 0;
00755 
00756   // get the (tagname) itemd type
00757   QString dataname=KateHlManager::self()->syntax->groupItemData(data,QString(""));
00758 
00759   // code folding region handling:
00760   QString beginRegionStr=KateHlManager::self()->syntax->groupItemData(data,QString("beginRegion"));
00761   QString endRegionStr=KateHlManager::self()->syntax->groupItemData(data,QString("endRegion"));
00762 
00763   signed char regionId=0;
00764   signed char regionId2=0;
00765 
00766   if (!beginRegionStr.isEmpty())
00767   {
00768     regionId = RegionList->indexOf(beginRegionStr);
00769 
00770     if (regionId==-1) // if the region name doesn't already exist, add it to the list
00771     {
00772       (*RegionList)<<beginRegionStr;
00773       regionId = RegionList->indexOf(beginRegionStr);
00774     }
00775 
00776     regionId++;
00777 
00778     kDebug(13010) << "########### BEG REG: "  << beginRegionStr << " NUM: " << regionId;
00779   }
00780 
00781   if (!endRegionStr.isEmpty())
00782   {
00783     regionId2 = RegionList->indexOf(endRegionStr);
00784 
00785     if (regionId2==-1) // if the region name doesn't already exist, add it to the list
00786     {
00787       (*RegionList)<<endRegionStr;
00788       regionId2 = RegionList->indexOf(endRegionStr);
00789     }
00790 
00791     regionId2 = -regionId2 - 1;
00792 
00793     kDebug(13010) << "########### END REG: "  << endRegionStr << " NUM: " << regionId2;
00794   }
00795 
00796   int attr = 0;
00797   QString tmpAttr=KateHlManager::self()->syntax->groupItemData(data,QString("attribute")).simplified();
00798   bool onlyConsume = tmpAttr.isEmpty();
00799 
00800   // only relevant for non consumer
00801   if (!onlyConsume)
00802   {
00803     if (QString("%1").arg(tmpAttr.toInt())==tmpAttr)
00804     {
00805       errorsAndWarnings+=i18n(
00806           "<b>%1</b>: Deprecated syntax. Attribute (%2) not addressed by symbolic name<br />",
00807       buildIdentifier, tmpAttr);
00808       attr=tmpAttr.toInt();
00809     }
00810     else
00811       attr=lookupAttrName(tmpAttr,iDl);
00812   }
00813 
00814   // Info about context switch
00815   KateHlContextModification context = -1;
00816   QString unresolvedContext;
00817   QString tmpcontext=KateHlManager::self()->syntax->groupItemData(data,QString("context"));
00818   if (!tmpcontext.isEmpty())
00819     context=getContextModificationFromString(ContextNameList, tmpcontext,unresolvedContext);
00820 
00821   // Get the char parameter (eg DetectChar)
00822   char chr;
00823   if (! KateHlManager::self()->syntax->groupItemData(data,QString("char")).isEmpty())
00824     chr= qPrintable((KateHlManager::self()->syntax->groupItemData(data,QString("char"))))[0];
00825   else
00826     chr=0;
00827 
00828   // Get the String parameter (eg. StringDetect)
00829   QString stringdata=KateHlManager::self()->syntax->groupItemData(data,QString("String"));
00830 
00831   // Get a second char parameter (char1) (eg Detect2Chars)
00832   char chr1;
00833   if (! KateHlManager::self()->syntax->groupItemData(data,QString("char1")).isEmpty())
00834     chr1= (KateHlManager::self()->syntax->groupItemData(data,QString("char1")).toLatin1())[0];
00835   else
00836     chr1=0;
00837 
00838   // Will be removed eventually. Atm used for StringDetect, keyword and RegExp
00839   const QString & insensitive_str = KateHlManager::self()->syntax->groupItemData(data,QString("insensitive"));
00840   bool insensitive = IS_TRUE( insensitive_str );
00841 
00842   // for regexp only
00843   bool minimal = IS_TRUE( KateHlManager::self()->syntax->groupItemData(data,QString("minimal")) );
00844 
00845   // dominik: look ahead and do not change offset. so we can change contexts w/o changing offset1.
00846   bool lookAhead = IS_TRUE( KateHlManager::self()->syntax->groupItemData(data,QString("lookAhead")) );
00847 
00848   bool dynamic= IS_TRUE(KateHlManager::self()->syntax->groupItemData(data,QString("dynamic")) );
00849 
00850   bool firstNonSpace = IS_TRUE(KateHlManager::self()->syntax->groupItemData(data,QString("firstNonSpace")) );
00851 
00852   int column = -1;
00853   QString colStr = KateHlManager::self()->syntax->groupItemData(data,QString("column"));
00854   if (!colStr.isEmpty())
00855     column = colStr.toInt();
00856 
00857   //Create the item corresponding to it's type and set it's parameters
00858   KateHlItem *tmpItem;
00859 
00860   if (dataname=="keyword")
00861   {
00862     bool keywordInsensitive = insensitive_str.isEmpty() ? !casesensitive : insensitive;
00863     KateHlKeyword *keyword=new KateHlKeyword(attr,context,regionId,regionId2,keywordInsensitive,
00864                                              m_additionalData[ buildIdentifier ]->deliminator);
00865 
00866     //Get the entries for the keyword lookup list
00867     keyword->addList(KateHlManager::self()->syntax->finddata("highlighting",stringdata));
00868     tmpItem=keyword;
00869   }
00870   else if (dataname=="Float") tmpItem= (new KateHlFloat(attr,context,regionId,regionId2));
00871   else if (dataname=="Int") tmpItem=(new KateHlInt(attr,context,regionId,regionId2));
00872   else if (dataname=="DetectChar") tmpItem=(new KateHlCharDetect(attr,context,regionId,regionId2,chr));
00873   else if (dataname=="Detect2Chars") tmpItem=(new KateHl2CharDetect(attr,context,regionId,regionId2,chr,chr1));
00874   else if (dataname=="RangeDetect") tmpItem=(new KateHlRangeDetect(attr,context,regionId,regionId2, chr, chr1));
00875   else if (dataname=="LineContinue") tmpItem=(new KateHlLineContinue(attr,context,regionId,regionId2));
00876   else if (dataname=="StringDetect") tmpItem=(new KateHlStringDetect(attr,context,regionId,regionId2,stringdata,insensitive));
00877   else if (dataname=="AnyChar") tmpItem=(new KateHlAnyChar(attr,context,regionId,regionId2,stringdata));
00878   else if (dataname=="RegExpr") tmpItem=(new KateHlRegExpr(attr,context,regionId,regionId2,stringdata, insensitive, minimal));
00879   else if (dataname=="HlCChar") tmpItem= ( new KateHlCChar(attr,context,regionId,regionId2));
00880   else if (dataname=="HlCHex") tmpItem= (new KateHlCHex(attr,context,regionId,regionId2));
00881   else if (dataname=="HlCOct") tmpItem= (new KateHlCOct(attr,context,regionId,regionId2));
00882   else if (dataname=="HlCFloat") tmpItem= (new KateHlCFloat(attr,context,regionId,regionId2));
00883   else if (dataname=="HlCStringChar") tmpItem= (new KateHlCStringChar(attr,context,regionId,regionId2));
00884   else if (dataname=="DetectSpaces") tmpItem= (new KateHlDetectSpaces(attr,context,regionId,regionId2));
00885   else if (dataname=="DetectIdentifier") tmpItem= (new KateHlDetectIdentifier(attr,context,regionId,regionId2));
00886   else
00887   {
00888     // oops, unknown type. Perhaps a spelling error in the xml file
00889     return 0;
00890   }
00891 
00892   // set lookAhead & dynamic properties
00893   tmpItem->lookAhead = lookAhead;
00894   tmpItem->dynamic = dynamic;
00895   tmpItem->firstNonSpace = firstNonSpace;
00896   tmpItem->column = column;
00897   tmpItem->onlyConsume = onlyConsume;
00898 
00899   if (!unresolvedContext.isEmpty())
00900   {
00901     unresolvedContextReferences.insert(&(tmpItem->ctx),unresolvedContext);
00902   }
00903 
00904   return tmpItem;
00905 }
00906 
00907 QString KateHighlighting::hlKeyForAttrib( int i ) const
00908 {
00909   // find entry. This is faster than QMap::find. m_hlIndex always has an entry
00910   // for key '0' (it is "none"), so the result is always valid.
00911   int k = 0;
00912   QMap<int,QString>::const_iterator it = m_hlIndex.constEnd();
00913   while ( it != m_hlIndex.constBegin() )
00914   {
00915     --it;
00916     k = it.key();
00917     if ( i >= k )
00918       break;
00919   }
00920   return it.value();
00921 }
00922 
00923 bool KateHighlighting::isInWord( QChar c, int attrib ) const
00924 {
00925   return m_additionalData[ hlKeyForAttrib( attrib ) ]->deliminator.indexOf(c) < 0
00926       && !c.isSpace()
00927       && c != QChar::fromLatin1('"') && c != QChar::fromLatin1('\'');
00928 }
00929 
00930 bool KateHighlighting::canBreakAt( QChar c, int attrib ) const
00931 {
00932   static const QString& sq = KGlobal::staticQString("\"'");
00933   return (m_additionalData[ hlKeyForAttrib( attrib ) ]->wordWrapDeliminator.indexOf(c) != -1) && (sq.indexOf(c) == -1);
00934 }
00935 
00936 QLinkedList<QRegExp> KateHighlighting::emptyLines(int attrib) const
00937 {
00938   kDebug(13010)<<"hlKeyForAttrib: "<<hlKeyForAttrib(attrib);
00939   return m_additionalData[hlKeyForAttrib(attrib)]->emptyLines;
00940 }
00941 
00942 signed char KateHighlighting::commentRegion(int attr) const {
00943   QString commentRegion=m_additionalData[ hlKeyForAttrib( attr ) ]->multiLineRegion;
00944   return (commentRegion.isEmpty()?0:(commentRegion.toShort()));
00945 }
00946 
00947 bool KateHighlighting::canComment( int startAttrib, int endAttrib ) const
00948 {
00949   QString k = hlKeyForAttrib( startAttrib );
00950   return ( k == hlKeyForAttrib( endAttrib ) &&
00951       ( ( !m_additionalData[k]->multiLineCommentStart.isEmpty() && !m_additionalData[k]->multiLineCommentEnd.isEmpty() ) ||
00952        ! m_additionalData[k]->singleLineCommentMarker.isEmpty() ) );
00953 }
00954 
00955 QString KateHighlighting::getCommentStart( int attrib ) const
00956 {
00957   return m_additionalData[ hlKeyForAttrib( attrib) ]->multiLineCommentStart;
00958 }
00959 
00960 QString KateHighlighting::getCommentEnd( int attrib ) const
00961 {
00962   return m_additionalData[ hlKeyForAttrib( attrib ) ]->multiLineCommentEnd;
00963 }
00964 
00965 QString KateHighlighting::getCommentSingleLineStart( int attrib ) const
00966 {
00967   return m_additionalData[ hlKeyForAttrib( attrib) ]->singleLineCommentMarker;
00968 }
00969 
00970 KateHighlighting::CSLPos KateHighlighting::getCommentSingleLinePosition( int attrib ) const
00971 {
00972   return m_additionalData[ hlKeyForAttrib( attrib) ]->singleLineCommentPosition;
00973 }
00974 
00975 
00980 void KateHighlighting::readCommentConfig()
00981 {
00982   KateHlManager::self()->syntax->setIdentifier(buildIdentifier);
00983   KateSyntaxContextData *data=KateHlManager::self()->syntax->getGroupInfo("general","comment");
00984 
00985   QString cmlStart="", cmlEnd="", cmlRegion="", cslStart="";
00986   CSLPos cslPosition=CSLPosColumn0;
00987 
00988   if (data)
00989   {
00990     while  (KateHlManager::self()->syntax->nextGroup(data))
00991     {
00992       if (KateHlManager::self()->syntax->groupData(data,"name")=="singleLine")
00993       {
00994         cslStart=KateHlManager::self()->syntax->groupData(data,"start");
00995         QString cslpos=KateHlManager::self()->syntax->groupData(data,"position");
00996         if (cslpos=="afterwhitespace")
00997           cslPosition=CSLPosAfterWhitespace;
00998         else
00999           cslPosition=CSLPosColumn0;
01000       }
01001       else if (KateHlManager::self()->syntax->groupData(data,"name")=="multiLine")
01002       {
01003         cmlStart=KateHlManager::self()->syntax->groupData(data,"start");
01004         cmlEnd=KateHlManager::self()->syntax->groupData(data,"end");
01005         cmlRegion=KateHlManager::self()->syntax->groupData(data,"region");
01006       }
01007     }
01008 
01009     KateHlManager::self()->syntax->freeGroupInfo(data);
01010   }
01011 
01012   m_additionalData[buildIdentifier]->singleLineCommentMarker = cslStart;
01013   m_additionalData[buildIdentifier]->singleLineCommentPosition = cslPosition;
01014   m_additionalData[buildIdentifier]->multiLineCommentStart = cmlStart;
01015   m_additionalData[buildIdentifier]->multiLineCommentEnd = cmlEnd;
01016   m_additionalData[buildIdentifier]->multiLineRegion = cmlRegion;
01017 }
01018 
01019 
01020 
01021 
01022 void KateHighlighting::readEmptyLineConfig()
01023 {
01024   KateHlManager::self()->syntax->setIdentifier(buildIdentifier);
01025   KateSyntaxContextData *data=KateHlManager::self()->syntax->getGroupInfo("general","emptyLine");
01026 
01027   QLinkedList<QRegExp> exprList;
01028 
01029   if (data)
01030   {
01031     while  (KateHlManager::self()->syntax->nextGroup(data))
01032     {
01033       kDebug(13010)<<"creating an empty line regular expression";
01034       QString regexprline=KateHlManager::self()->syntax->groupData(data,"regexpr");
01035       bool regexprcase=(KateHlManager::self()->syntax->groupData(data,"casesensitive").toUpper().compare("TRUE")==0);
01036       exprList.append(QRegExp(regexprline,regexprcase?Qt::CaseSensitive:Qt::CaseInsensitive));
01037     }
01038       KateHlManager::self()->syntax->freeGroupInfo(data);
01039   }
01040 
01041   m_additionalData[buildIdentifier]->emptyLines = exprList;
01042 }
01043 
01044 
01045 
01046 
01047 
01048 
01054 void KateHighlighting::readGlobalKeywordConfig()
01055 {
01056   deliminator = stdDeliminator;
01057   // Tell the syntax document class which file we want to parse
01058   kDebug(13010)<<"readGlobalKeywordConfig:BEGIN";
01059 
01060   KateHlManager::self()->syntax->setIdentifier(buildIdentifier);
01061   KateSyntaxContextData *data = KateHlManager::self()->syntax->getConfig("general","keywords");
01062 
01063   if (data)
01064   {
01065     kDebug(13010)<<"Found global keyword config";
01066 
01067     if ( IS_TRUE( KateHlManager::self()->syntax->groupItemData(data,QString("casesensitive")) ) )
01068       casesensitive=true;
01069     else
01070       casesensitive=false;
01071 
01072     //get the weak deliminators
01073     weakDeliminator=(KateHlManager::self()->syntax->groupItemData(data,QString("weakDeliminator")));
01074 
01075     kDebug(13010)<<"weak delimiters are: "<<weakDeliminator;
01076 
01077     // remove any weakDelimitars (if any) from the default list and store this list.
01078     for (int s=0; s < weakDeliminator.length(); s++)
01079     {
01080       int f = deliminator.indexOf (weakDeliminator[s]);
01081 
01082       if (f > -1)
01083         deliminator.remove (f, 1);
01084     }
01085 
01086     QString addDelim = (KateHlManager::self()->syntax->groupItemData(data,QString("additionalDeliminator")));
01087 
01088     if (!addDelim.isEmpty())
01089       deliminator=deliminator+addDelim;
01090 
01091     KateHlManager::self()->syntax->freeGroupInfo(data);
01092   }
01093   else
01094   {
01095     //Default values
01096     casesensitive=true;
01097     weakDeliminator=QString("");
01098   }
01099 
01100   kDebug(13010)<<"readGlobalKeywordConfig:END";
01101 
01102   kDebug(13010)<<"delimiterCharacters are: "<<deliminator;
01103 
01104   m_additionalData[buildIdentifier]->deliminator = deliminator;
01105 }
01106 
01117 void KateHighlighting::readWordWrapConfig()
01118 {
01119   // Tell the syntax document class which file we want to parse
01120   kDebug(13010)<<"readWordWrapConfig:BEGIN";
01121 
01122   KateHlManager::self()->syntax->setIdentifier(buildIdentifier);
01123   KateSyntaxContextData *data = KateHlManager::self()->syntax->getConfig("general","keywords");
01124 
01125   QString wordWrapDeliminator = stdDeliminator;
01126   if (data)
01127   {
01128     kDebug(13010)<<"Found global keyword config";
01129 
01130     wordWrapDeliminator = (KateHlManager::self()->syntax->groupItemData(data,QString("wordWrapDeliminator")));
01131     //when no wordWrapDeliminator is defined use the deliminator list
01132     if ( wordWrapDeliminator.length() == 0 ) wordWrapDeliminator = deliminator;
01133 
01134     kDebug(13010) << "word wrap deliminators are " << wordWrapDeliminator;
01135 
01136     KateHlManager::self()->syntax->freeGroupInfo(data);
01137   }
01138 
01139   kDebug(13010)<<"readWordWrapConfig:END";
01140 
01141   m_additionalData[buildIdentifier]->wordWrapDeliminator = wordWrapDeliminator;
01142 }
01143 
01144 void KateHighlighting::readIndentationConfig()
01145 {
01146   m_indentation = "";
01147 
01148   KateHlManager::self()->syntax->setIdentifier(buildIdentifier);
01149   KateSyntaxContextData *data = KateHlManager::self()->syntax->getConfig("general","indentation");
01150 
01151   if (data)
01152   {
01153     m_indentation = (KateHlManager::self()->syntax->groupItemData(data,QString("mode")));
01154 
01155     KateHlManager::self()->syntax->freeGroupInfo(data);
01156   }
01157 }
01158 
01159 void KateHighlighting::readFoldingConfig()
01160 {
01161   // Tell the syntax document class which file we want to parse
01162   kDebug(13010)<<"readfoldignConfig:BEGIN";
01163 
01164   KateHlManager::self()->syntax->setIdentifier(buildIdentifier);
01165   KateSyntaxContextData *data = KateHlManager::self()->syntax->getConfig("general","folding");
01166 
01167   if (data)
01168   {
01169     kDebug(13010)<<"Found global keyword config";
01170 
01171     if ( IS_TRUE( KateHlManager::self()->syntax->groupItemData(data,QString("indentationsensitive")) ) )
01172       m_foldingIndentationSensitive=true;
01173     else
01174       m_foldingIndentationSensitive=false;
01175 
01176     KateHlManager::self()->syntax->freeGroupInfo(data);
01177   }
01178   else
01179   {
01180     //Default values
01181     m_foldingIndentationSensitive = false;
01182   }
01183 
01184   kDebug(13010)<<"readfoldingConfig:END";
01185 
01186   kDebug(13010)<<"############################ use indent for fold are: "<<m_foldingIndentationSensitive;
01187 }
01188 
01189 void  KateHighlighting::createContextNameList(QStringList *ContextNameList,int ctx0)
01190 {
01191   kDebug(13010)<<"creatingContextNameList:BEGIN";
01192 
01193   if (ctx0 == 0)
01194       ContextNameList->clear();
01195 
01196   KateHlManager::self()->syntax->setIdentifier(buildIdentifier);
01197 
01198   KateSyntaxContextData *data=KateHlManager::self()->syntax->getGroupInfo("highlighting","context");
01199 
01200   int id=ctx0;
01201 
01202   if (data)
01203   {
01204      while (KateHlManager::self()->syntax->nextGroup(data))
01205      {
01206           QString tmpAttr=KateHlManager::self()->syntax->groupData(data,QString("name")).simplified();
01207     if (tmpAttr.isEmpty())
01208     {
01209      tmpAttr=QString("!KATE_INTERNAL_DUMMY! %1").arg(id);
01210      errorsAndWarnings +=i18n("<b>%1</b>: Deprecated syntax. Context %2 has no symbolic name<br />", buildIdentifier, id-ctx0);
01211     }
01212           else tmpAttr=buildPrefix+tmpAttr;
01213     (*ContextNameList)<<tmpAttr;
01214           id++;
01215      }
01216      KateHlManager::self()->syntax->freeGroupInfo(data);
01217   }
01218   kDebug(13010)<<"creatingContextNameList:END";
01219 
01220 }
01221 
01222 KateHlContextModification KateHighlighting::getContextModificationFromString(QStringList *ContextNameList, QString tmpLineEndContext, /*NO CONST*/ QString &unres)
01223 {
01224   // nothing unresolved
01225   unres = "";
01226 
01227   // context to push on stack
01228   int context = -1;
01229 
01230   // number of contexts to pop
01231   int pops = 0;
01232 
01233   // we allow arbitrary #stay and #pop at the start
01234   bool anyFound = false;
01235   while (tmpLineEndContext.startsWith("#stay") || tmpLineEndContext.startsWith("#pop"))
01236   {
01237     // ignore stay
01238     if (tmpLineEndContext.startsWith("#stay"))
01239     {
01240       tmpLineEndContext.remove (0, 5);
01241     }
01242     else // count the pops
01243     {
01244       ++pops;
01245       tmpLineEndContext.remove (0, 4);
01246     }
01247 
01248     anyFound = true;
01249   }
01250 
01254   if (anyFound && !tmpLineEndContext.isEmpty())
01255   {
01256     if (tmpLineEndContext.startsWith('!'))
01257       tmpLineEndContext.remove (0, 1);
01258   }
01259 
01263   if (tmpLineEndContext.isEmpty())
01264     return KateHlContextModification (context, pops);
01265 
01270   if ( tmpLineEndContext.contains("##"))
01271   {
01272     int o = tmpLineEndContext.indexOf("##");
01273     // FIXME at least with 'foo##bar'-style contexts the rules are picked up
01274     // but the default attribute is not
01275     QString tmp=tmpLineEndContext.mid(o+2);
01276     if (!embeddedHls.contains(tmp))  embeddedHls.insert(tmp,KateEmbeddedHlInfo());
01277     unres=tmp+':'+tmpLineEndContext.left(o);
01278     kDebug(13010) << "unres = " << unres;
01279     context=0;
01280   }
01281 
01282   else
01283   {
01284     context=ContextNameList->indexOf(buildPrefix+tmpLineEndContext);
01285     if (context==-1)
01286     {
01287       context=tmpLineEndContext.toInt();
01288       errorsAndWarnings+=i18n(
01289         "<B>%1</B>:Deprecated syntax. Context %2 not addressed by a symbolic name"
01290         , buildIdentifier, tmpLineEndContext);
01291     }
01292 //#warning restructure this the name list storage.
01293 //    context=context+buildContext0Offset;
01294   }
01295 
01296   return KateHlContextModification (context, pops);
01297 }
01298 
01304 void KateHighlighting::makeContextList()
01305 {
01306   if (noHl)  // if this a highlighting for "normal texts" only, tere is no need for a context list creation
01307     return;
01308 
01309   embeddedHls.clear();
01310   unresolvedContextReferences.clear();
01311   RegionList.clear();
01312   ContextNameList.clear();
01313 
01314   // prepare list creation. To reuse as much code as possible handle this
01315   // highlighting the same way as embedded onces
01316   embeddedHls.insert(iName,KateEmbeddedHlInfo());
01317 
01318   bool something_changed;
01319   // the context "0" id is 0 for this hl, all embedded context "0"s have offsets
01320   startctx=base_startctx=0;
01321   // inform everybody that we are building the highlighting contexts and itemlists
01322   building=true;
01323 
01324   do
01325   {
01326     kDebug(13010)<<"**************** Outer loop in make ContextList";
01327     kDebug(13010)<<"**************** Hl List count:"<<embeddedHls.count();
01328     something_changed=false; //assume all "embedded" hls have already been loaded
01329     for (KateEmbeddedHlInfos::const_iterator it=embeddedHls.begin(); it!=embeddedHls.end();++it)
01330     {
01331       if (!it.value().loaded)  // we found one, we still have to load
01332       {
01333         kDebug(13010)<<"**************** Inner loop in make ContextList";
01334         QString identifierToUse;
01335         kDebug(13010)<<"Trying to open highlighting definition file: "<< it.key();
01336         if (iName==it.key()) // the own identifier is known
01337           identifierToUse=identifier;
01338         else                 // all others have to be looked up
01339           identifierToUse=KateHlManager::self()->identifierForName(it.key());
01340 
01341         kDebug(13010)<<"Location is:"<< identifierToUse;
01342 
01343         buildPrefix=it.key()+':';  // attribute names get prefixed by the names
01344                                    // of the highlighting definitions they belong to
01345 
01346         if (identifierToUse.isEmpty() )
01347           kDebug(13010)<<"OHOH, unknown highlighting description referenced";
01348 
01349         kDebug(13010)<<"setting ("<<it.key()<<") to loaded";
01350 
01351         //mark hl as loaded
01352         it=embeddedHls.insert(it.key(),KateEmbeddedHlInfo(true,startctx));
01353         //set class member for context 0 offset, so we don't need to pass it around
01354         buildContext0Offset=startctx;
01355         //parse one hl definition file
01356         startctx=addToContextList(identifierToUse,startctx);
01357 
01358         if (noHl) return;  // an error occurred
01359 
01360         base_startctx = startctx;
01361         something_changed=true; // something has been loaded
01362       }
01363     }
01364   } while (something_changed);  // as long as there has been another file parsed
01365                   // repeat everything, there could be newly added embedded hls.
01366 
01367 
01368   // at this point all needed highlighing (sub)definitions are loaded. It's time
01369   // to resolve cross file  references (if there are any)
01370   kDebug(13010)<<"Unresolved contexts, which need attention: "<<unresolvedContextReferences.count();
01371 
01372   //optimize this a littlebit
01373   for (KateHlUnresolvedCtxRefs::iterator unresIt=unresolvedContextReferences.begin();
01374        unresIt != unresolvedContextReferences.end();
01375        ++unresIt)
01376   {
01377     QString incCtx = unresIt.value();
01378     kDebug(13010) << "Context " <<incCtx << " is unresolved";
01379 
01380     // only resolve '##Name' contexts here; handleKateHlIncludeRules() can figure
01381     // out 'Name##Name'-style inclusions, but we screw it up
01382     if (incCtx.endsWith(':')) {
01383       kDebug(13010)<<"Looking up context0 for ruleset "<<incCtx;
01384       incCtx = incCtx.left(incCtx.length()-1);
01385       //try to find the context0 id for a given unresolvedReference
01386       KateEmbeddedHlInfos::const_iterator hlIt=embeddedHls.find(incCtx);
01387       if (hlIt!=embeddedHls.end())
01388         *(unresIt.key())=hlIt.value().context0;
01389     }
01390   }
01391 
01392   // eventually handle KateHlIncludeRules items, if they exist.
01393   // This has to be done after the cross file references, because it is allowed
01394   // to include the context0 from a different definition, than the one the rule
01395   // belongs to
01396   handleKateHlIncludeRules();
01397 
01398   embeddedHls.clear(); //save some memory.
01399   unresolvedContextReferences.clear(); //save some memory
01400   RegionList.clear();  // I think you get the idea ;)
01401   ContextNameList.clear();
01402 
01403 
01404   // if there have been errors show them
01405   if (!errorsAndWarnings.isEmpty())
01406   KMessageBox::detailedSorry(QApplication::activeWindow(),i18n(
01407         "There were warning(s) and/or error(s) while parsing the syntax "
01408         "highlighting configuration."),
01409         errorsAndWarnings, i18n("Kate Syntax Highlighting Parser"));
01410 
01411   // we have finished
01412   building=false;
01413 }
01414 
01415 void KateHighlighting::handleKateHlIncludeRules()
01416 {
01417   // if there are noe include rules to take care of, just return
01418   kDebug(13010)<<"KateHlIncludeRules, which need attention: " <<includeRules.size();
01419   if (includeRules.isEmpty()) return;
01420 
01421   buildPrefix="";
01422   QString dummy;
01423 
01424   // By now the context0 references are resolved, now more or less only inner
01425   // file references are resolved. If we decide that arbitrary inclusion is
01426   // needed, this doesn't need to be changed, only the addToContextList
01427   // method.
01428 
01429   //resolove context names
01430   for (KateHlIncludeRules::iterator it=includeRules.begin(); it!=includeRules.end(); )
01431   {
01432     if ((*it)->incCtx.newContext==-1) // context unresolved ?
01433     {
01434 
01435       if ((*it)->incCtxN.isEmpty())
01436       {
01437         // no context name given, and no valid context id set, so this item is
01438         // going to be removed
01439         KateHlIncludeRules::iterator it1=it;
01440         ++it1;
01441         delete (*it);
01442         includeRules.erase(it);
01443         it=it1;
01444       }
01445       else
01446       {
01447         // resolve name to id
01448         (*it)->incCtx=getContextModificationFromString(&ContextNameList,(*it)->incCtxN,dummy).newContext;
01449         kDebug(13010)<<"Resolved "<<(*it)->incCtxN<< " to "<<(*it)->incCtx.newContext<<" for include rule";
01450         // It would be good to look here somehow, if the result is valid
01451       }
01452     }
01453     else ++it; //nothing to do, already resolved (by the cross defintion reference resolver)
01454   }
01455 
01456   // now that all KateHlIncludeRule items should be valid and completely resolved,
01457   // do the real inclusion of the rules.
01458   // recursiveness is needed, because context 0 could include context 1, which
01459   // itself includes context 2 and so on.
01460   //  In that case we have to handle context 2 first, then 1, 0
01461   //TODO: catch circular references: eg 0->1->2->3->1
01462   while (!includeRules.isEmpty())
01463     handleKateHlIncludeRulesRecursive(0, &includeRules);
01464 }
01465 
01466 void KateHighlighting::handleKateHlIncludeRulesRecursive(int index, KateHlIncludeRules *list)
01467 {
01468   if (index < 0 || index >= list->count()) return;  //invalid iterator, shouldn't happen, but better have a rule prepared ;)
01469 
01470   int index1 = index;
01471   int ctx = list->at(index1)->ctx;
01472 
01473   // find the last entry for the given context in the KateHlIncludeRules list
01474   // this is need if one context includes more than one. This saves us from
01475   // updating all insert positions:
01476   // eg: context 0:
01477   // pos 3 - include context 2
01478   // pos 5 - include context 3
01479   // During the building of the includeRules list the items are inserted in
01480   // ascending order, now we need it descending to make our life easier.
01481   while (index < list->count() && list->at(index)->ctx == ctx)
01482   {
01483     index1 = index;
01484     ++index;
01485   }
01486 
01487   // iterate over each include rule for the context the function has been called for.
01488   while (index1 >= 0 && index1 < list->count() && list->at(index1)->ctx == ctx)
01489   {
01490     KateHlContextModification ctx1 = list->at(index1)->incCtx;
01491 
01492     //let's see, if the the included context includes other contexts
01493     for (int index2 = 0; index2 < list->count(); ++index2)
01494     {
01495       if (list->at(index2)->ctx == ctx1.newContext)
01496       {
01497         //yes it does, so first handle that include rules, since we want to
01498         // include those subincludes too
01499         handleKateHlIncludeRulesRecursive(index2, list);
01500         break;
01501       }
01502     }
01503 
01504     // if the context we want to include had sub includes, they are already inserted there.
01505     KateHlContext *dest=m_contexts[ctx];
01506     KateHlContext *src=m_contexts[ctx1.newContext];
01507 //     kDebug(3010)<<"linking included rules from "<<ctx<<" to "<<ctx1;
01508 
01509     // If so desired, change the dest attribute to the one of the src.
01510     // Required to make commenting work, if text matched by the included context
01511     // is a different highlight than the host context.
01512     if ( list->at(index1)->includeAttrib )
01513       dest->attr = src->attr;
01514 
01515     // insert the included context's rules starting at position p
01516     int p = list->at(index1)->pos;
01517 
01518     // remember some stuff
01519     int oldLen = dest->items.size();
01520     uint itemsToInsert = src->items.size();
01521 
01522     // resize target
01523     dest->items.resize (oldLen + itemsToInsert);
01524 
01525     // move old elements
01526     for (int i=oldLen-1; i >= p; --i)
01527       dest->items[i+itemsToInsert] = dest->items[i];
01528 
01529     // insert new stuff
01530     for (uint i=0; i < itemsToInsert; ++i  )
01531       dest->items[p+i] = src->items[i];
01532 
01533     index = index1; //backup the iterator
01534     --index1;  //move to the next entry, which has to be take care of
01535     delete list->takeAt(index); //free + remove the already handled data structure
01536   }
01537 }
01538 
01544 int KateHighlighting::addToContextList(const QString &ident, int ctx0)
01545 {
01546   kDebug(13010)<<"=== Adding hl with ident '"<<ident<<"'";
01547 
01548   buildIdentifier=ident;
01549   KateSyntaxContextData *data, *datasub;
01550   KateHlItem *c;
01551 
01552   QString dummy;
01553 
01554   // Let the syntax document class know, which file we'd like to parse
01555   if (!KateHlManager::self()->syntax->setIdentifier(ident))
01556   {
01557     noHl=true;
01558     KMessageBox::information(QApplication::activeWindow(),i18n(
01559         "Since there has been an error parsing the highlighting description, "
01560         "this highlighting will be disabled"));
01561     return 0;
01562   }
01563 
01564   // only read for the own stuff
01565   if (identifier == ident)
01566   {
01567     readIndentationConfig ();
01568   }
01569 
01570   RegionList<<"!KateInternal_TopLevel!";
01571 
01572   m_hlIndex[internalIDList.count()] = ident;
01573   m_additionalData.insert( ident, new HighlightPropertyBag );
01574 
01575   // fill out the propertybag
01576   readCommentConfig();
01577   readEmptyLineConfig();
01578   readGlobalKeywordConfig();
01579   readWordWrapConfig();
01580 
01581   readFoldingConfig ();
01582 
01583   QString ctxName;
01584 
01585   // This list is needed for the translation of the attribute parameter,
01586   // if the itemData name is given instead of the index
01587   addToKateExtendedAttributeList();
01588   QList<KateExtendedAttribute::Ptr> iDl = internalIDList;
01589 
01590   createContextNameList(&ContextNameList,ctx0);
01591 
01592 
01593   kDebug(13010)<<"Parsing Context structure";
01594   //start the real work
01595   data=KateHlManager::self()->syntax->getGroupInfo("highlighting","context");
01596   uint i=buildContext0Offset;
01597   if (data)
01598   {
01599     while (KateHlManager::self()->syntax->nextGroup(data))
01600     {
01601       kDebug(13010)<<"Found a context in file, building structure now";
01602       //BEGIN - Translation of the attribute parameter
01603       QString tmpAttr=KateHlManager::self()->syntax->groupData(data,QString("attribute")).simplified();
01604       int attr;
01605       if (QString("%1").arg(tmpAttr.toInt())==tmpAttr)
01606         attr=tmpAttr.toInt();
01607       else
01608         attr=lookupAttrName(tmpAttr,iDl);
01609       //END - Translation of the attribute parameter
01610 
01611       ctxName=buildPrefix+KateHlManager::self()->syntax->groupData(data,QString("lineEndContext")).simplified();
01612 
01613       QString tmpLineEndContext=KateHlManager::self()->syntax->groupData(data,QString("lineEndContext")).simplified();
01614       KateHlContextModification context;
01615 
01616       context=getContextModificationFromString(&ContextNameList, tmpLineEndContext,dummy);
01617 
01618       QString tmpNIBF = KateHlManager::self()->syntax->groupData(data, QString("noIndentationBasedFolding") );
01619       bool noIndentationBasedFolding=IS_TRUE(tmpNIBF);
01620 
01621       //BEGIN get fallthrough props
01622       bool ft = false;
01623       KateHlContextModification ftc = 0; // fallthrough context
01624       if ( i > 0 )  // fallthrough is not smart in context 0
01625       {
01626         QString tmpFt = KateHlManager::self()->syntax->groupData(data, QString("fallthrough") );
01627         if ( IS_TRUE(tmpFt) )
01628           ft = true;
01629         if ( ft )
01630         {
01631           QString tmpFtc = KateHlManager::self()->syntax->groupData( data, QString("fallthroughContext") );
01632 
01633           ftc=getContextModificationFromString(&ContextNameList, tmpFtc,dummy);
01634 
01635           // stay is not allowed, we need to #pop or push some context...
01636           if (ftc.type == KateHlContextModification::doNothing) ftc = 0;
01637 
01638           kDebug(13010)<<"Setting fall through context (context "<<i<<"): "<<ftc.newContext;
01639         }
01640       }
01641       //END falltrhough props
01642 
01643       bool dynamic = false;
01644       QString tmpDynamic = KateHlManager::self()->syntax->groupData(data, QString("dynamic") );
01645       if ( tmpDynamic.toLower() == "true" ||  tmpDynamic.toInt() == 1 )
01646         dynamic = true;
01647 
01648       KateHlContext *ctxNew = new KateHlContext (
01649         ident,
01650         attr,
01651         context,
01652         (KateHlManager::self()->syntax->groupData(data,QString("lineBeginContext"))).isEmpty()?-1:
01653         (KateHlManager::self()->syntax->groupData(data,QString("lineBeginContext"))).toInt(),
01654         ft, ftc, dynamic,noIndentationBasedFolding);
01655 
01656       m_contexts.push_back (ctxNew);
01657 
01658       kDebug(13010) << "INDEX: " << i << " LENGTH " << m_contexts.size()-1;
01659 
01660       //Let's create all items for the context
01661       while (KateHlManager::self()->syntax->nextItem(data))
01662       {
01663 //    kDebug(13010)<< "In make Contextlist: Item:";
01664 
01665       // KateHlIncludeRules : add a pointer to each item in that context
01666         // TODO add a attrib includeAttrib
01667       QString tag = KateHlManager::self()->syntax->groupItemData(data,QString(""));
01668       if ( tag == "IncludeRules" ) //if the new item is an Include rule, we have to take special care
01669       {
01670         QString incCtx = KateHlManager::self()->syntax->groupItemData( data, QString("context"));
01671         QString incAttrib = KateHlManager::self()->syntax->groupItemData( data, QString("includeAttrib"));
01672         bool includeAttrib = IS_TRUE( incAttrib );
01673 
01674         // only context refernces of type Name, ##Name, and Subname##Name are allowed
01675         if (incCtx.startsWith("##") || (!incCtx.startsWith('#')))
01676         {
01677            int incCtxi = incCtx.indexOf ("##");
01678            //#stay, #pop is not interesting here
01679            if (incCtxi >= 0)
01680            {
01681              QString incSet = incCtx.mid(incCtxi + 2);
01682              QString incCtxN = incSet + ':' + incCtx.left(incCtxi);
01683 
01684              //a cross highlighting reference
01685              kDebug(13010)<<"Cross highlight reference <IncludeRules>, context "<<incCtxN;
01686              KateHlIncludeRule *ir=new KateHlIncludeRule(i,m_contexts[i]->items.count(),incCtxN,includeAttrib);
01687 
01688              //use the same way to determine cross hl file references as other items do
01689              if (!embeddedHls.contains(incSet))
01690                embeddedHls.insert(incSet,KateEmbeddedHlInfo());
01691              else
01692                kDebug(13010)<<"Skipping embeddedHls.insert for "<<incCtxN;
01693 
01694             unresolvedContextReferences.insert(&(ir->incCtx), incCtxN);
01695 
01696             includeRules.append(ir);
01697           }
01698           else
01699           {
01700             // a local reference -> just initialize the include rule structure
01701             incCtx=buildPrefix+incCtx.simplified ();
01702             includeRules.append(new KateHlIncludeRule(i,m_contexts[i]->items.count(),incCtx, includeAttrib));
01703           }
01704         }
01705 
01706         continue;
01707       }
01708       // TODO -- can we remove the block below??
01709 #if 0
01710                 QString tag = KateHlManager::self()->syntax->groupKateExtendedAttribute(data,QString(""));
01711                 if ( tag == "IncludeRules" ) {
01712                   // attrib context: the index (jowenn, i think using names here
01713                   // would be a cool feat, goes for mentioning the context in
01714                   // any item. a map or dict?)
01715                   int ctxId = getIdFromString(&ContextNameList,
01716                                                KateHlManager::self()->syntax->groupKateExtendedAttribute( data, QString("context")),dummy); // the index is *required*
01717                   if ( ctxId > -1) { // we can even reuse rules of 0 if we want to:)
01718                     kDebug(13010)<<"makeContextList["<<i<<"]: including all items of context "<<ctxId;
01719                     if ( ctxId < (int) i ) { // must be defined
01720                       for ( c = m_contexts[ctxId]->items.first(); c; c = m_contexts[ctxId]->items.next() )
01721                         m_contexts[i]->items.append(c);
01722                     }
01723                     else
01724                       kDebug(13010)<<"Context "<<ctxId<<"not defined. You can not include the rules of an undefined context";
01725                   }
01726                   continue; // while nextItem
01727                 }
01728 #endif
01729       c=createKateHlItem(data,iDl,&RegionList,&ContextNameList);
01730       if (c)
01731       {
01732         m_contexts[i]->items.append(c);
01733 
01734         // Not supported completely atm and only one level. Subitems.(all have
01735         // to be matched to at once)
01736         datasub=KateHlManager::self()->syntax->getSubItems(data);
01737         for (bool tmpbool=KateHlManager::self()->syntax->nextItem(datasub);
01738              tmpbool;
01739              tmpbool=KateHlManager::self()->syntax->nextItem(datasub))
01740         {
01741           c->subItems.resize (c->subItems.size()+1);
01742           c->subItems[c->subItems.size()-1] = createKateHlItem(datasub,iDl,&RegionList,&ContextNameList);
01743         }
01744         KateHlManager::self()->syntax->freeGroupInfo(datasub);
01745       }
01746       }
01747       i++;
01748     }
01749   }
01750 
01751   KateHlManager::self()->syntax->freeGroupInfo(data);
01752 
01753   if (RegionList.count()!=1)
01754     folding=true;
01755 
01756   folding = folding || m_foldingIndentationSensitive;
01757 
01758   //BEGIN Resolve multiline region if possible
01759   if (!m_additionalData[ ident ]->multiLineRegion.isEmpty()) {
01760     long commentregionid=RegionList.indexOf( m_additionalData[ ident ]->multiLineRegion );
01761     if (-1==commentregionid) {
01762       errorsAndWarnings+=i18n(
01763           "<b>%1</b>: Specified multiline comment region (%2) could not be resolved<br />"
01764                              , buildIdentifier,  m_additionalData[ ident ]->multiLineRegion );
01765       m_additionalData[ ident ]->multiLineRegion.clear();
01766       kDebug(13010)<<"ERROR comment region attribute could not be resolved";
01767 
01768     } else {
01769       m_additionalData[ ident ]->multiLineRegion=QString::number(commentregionid+1);
01770       kDebug(13010)<<"comment region resolved to:"<<m_additionalData[ ident ]->multiLineRegion;
01771     }
01772   }
01773   //END Resolve multiline region if possible
01774   return i;
01775 }
01776 
01777 void KateHighlighting::clearAttributeArrays ()
01778 {
01779   QMutableHashIterator< QString, QList<KTextEditor::Attribute::Ptr> > it = m_attributeArrays;
01780   while (it.hasNext())
01781   {
01782     it.next();
01783 
01784     // k, schema correct, let create the data
01785     KateAttributeList defaultStyleList;
01786 
01787     KateHlManager::self()->getDefaults(it.key(), defaultStyleList);
01788 
01789     QList<KateExtendedAttribute::Ptr> itemDataList;
01790     getKateExtendedAttributeList(it.key(), itemDataList);
01791 
01792     uint nAttribs = itemDataList.count();
01793     QList<KTextEditor::Attribute::Ptr>& array = it.value();
01794     array.clear();
01795 
01796     for (uint z = 0; z < nAttribs; z++)
01797     {
01798       KateExtendedAttribute::Ptr itemData = itemDataList.at(z);
01799       KTextEditor::Attribute::Ptr newAttribute( new KTextEditor::Attribute(*defaultStyleList.at(itemData->defaultStyleIndex())) );
01800 
01801       if (itemData && itemData->hasAnyProperty())
01802         *newAttribute += *itemData;
01803 
01804       array.append(newAttribute);
01805     }
01806   }
01807 }
01808 
01809 QList<KTextEditor::Attribute::Ptr> KateHighlighting::attributes (const QString &schema)
01810 {
01811   // found it, already floating around
01812   if (m_attributeArrays.contains(schema))
01813     return m_attributeArrays[schema];
01814 
01815   // k, schema correct, let create the data
01816   QList<KTextEditor::Attribute::Ptr> array;
01817   KateAttributeList defaultStyleList;
01818 
01819   KateHlManager::self()->getDefaults(schema, defaultStyleList);
01820 
01821   QList<KateExtendedAttribute::Ptr> itemDataList;
01822   getKateExtendedAttributeList(schema, itemDataList);
01823 
01824   uint nAttribs = itemDataList.count();
01825   for (uint z = 0; z < nAttribs; z++)
01826   {
01827     KateExtendedAttribute::Ptr itemData = itemDataList.at(z);
01828     KTextEditor::Attribute::Ptr newAttribute( new KTextEditor::Attribute(*defaultStyleList.at(itemData->defaultStyleIndex())) );
01829 
01830     if (itemData && itemData->hasAnyProperty())
01831       *newAttribute += *itemData;
01832 
01833     array.append(newAttribute);
01834   }
01835 
01836   m_attributeArrays.insert(schema, array);
01837 
01838   return array;
01839 }
01840 
01841 //END
01842 
01843 // kate: space-indent on; indent-width 2; replace-tabs on;

Kate

Skip menu "Kate"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal