Kate
katesyntaxdocument.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org> 00003 Copyright (C) 2000 Scott Manson <sdmanson@alltel.net> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License version 2 as published by the Free Software Foundation. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "katesyntaxdocument.h" 00021 00022 #include <sys/types.h> 00023 #include <sys/stat.h> 00024 #include <unistd.h> 00025 #include <time.h> 00026 #include <sys/time.h> 00027 00028 #include <kdebug.h> 00029 #include <kstandarddirs.h> 00030 #include <klocale.h> 00031 #include <kmessagebox.h> 00032 #include <kconfiggroup.h> 00033 #include <kde_file.h> 00034 00035 #include <QtGui/QApplication> 00036 #include <QtCore/QFile> 00037 00038 // use this to turn on over verbose debug output... 00039 #undef KSD_OVER_VERBOSE 00040 00041 KateSyntaxDocument::KateSyntaxDocument(KConfig *config, bool force) 00042 : QDomDocument() 00043 , m_config (config) 00044 { 00045 // Let's build the Mode List (katesyntaxhighlightingrc) 00046 setupModeList(force); 00047 } 00048 00049 KateSyntaxDocument::~KateSyntaxDocument() 00050 { 00051 for (int i=0; i < myModeList.size(); i++) 00052 delete myModeList[i]; 00053 } 00054 00059 bool KateSyntaxDocument::setIdentifier(const QString& identifier) 00060 { 00061 // if the current file is the same as the new one don't do anything. 00062 if(currentFile != identifier) 00063 { 00064 // let's open the new file 00065 QFile f( identifier ); 00066 00067 if ( f.open(QIODevice::ReadOnly) ) 00068 { 00069 // Let's parse the contets of the xml file 00070 /* The result of this function should be check for robustness, 00071 a false returned means a parse error */ 00072 QString errorMsg; 00073 int line, col; 00074 bool success=setContent(&f,&errorMsg,&line,&col); 00075 00076 // Ok, now the current file is the pretended one (identifier) 00077 currentFile = identifier; 00078 00079 // Close the file, is not longer needed 00080 f.close(); 00081 00082 if (!success) 00083 { 00084 KMessageBox::error(QApplication::activeWindow(),i18n("<qt>The error <b>%4</b><br /> has been detected in the file %1 at %2/%3</qt>", identifier, 00085 line, col, i18nc("QXml",errorMsg.toUtf8()))); 00086 return false; 00087 } 00088 } 00089 else 00090 { 00091 // Oh o, we couldn't open the file. 00092 KMessageBox::error(QApplication::activeWindow(), i18n("Unable to open %1", identifier) ); 00093 return false; 00094 } 00095 } 00096 return true; 00097 } 00098 00102 bool KateSyntaxDocument::nextGroup( KateSyntaxContextData* data) 00103 { 00104 if(!data) 00105 return false; 00106 00107 // No group yet so go to first child 00108 if (data->currentGroup.isNull()) 00109 { 00110 // Skip over non-elements. So far non-elements are just comments 00111 QDomNode node = data->parent.firstChild(); 00112 while (node.isComment()) 00113 node = node.nextSibling(); 00114 00115 data->currentGroup = node.toElement(); 00116 } 00117 else 00118 { 00119 // common case, iterate over siblings, skipping comments as we go 00120 QDomNode node = data->currentGroup.nextSibling(); 00121 while (node.isComment()) 00122 node = node.nextSibling(); 00123 00124 data->currentGroup = node.toElement(); 00125 } 00126 00127 return !data->currentGroup.isNull(); 00128 } 00129 00133 bool KateSyntaxDocument::nextItem( KateSyntaxContextData* data) 00134 { 00135 if(!data) 00136 return false; 00137 00138 if (data->item.isNull()) 00139 { 00140 QDomNode node = data->currentGroup.firstChild(); 00141 while (node.isComment()) 00142 node = node.nextSibling(); 00143 00144 data->item = node.toElement(); 00145 } 00146 else 00147 { 00148 QDomNode node = data->item.nextSibling(); 00149 while (node.isComment()) 00150 node = node.nextSibling(); 00151 00152 data->item = node.toElement(); 00153 } 00154 00155 return !data->item.isNull(); 00156 } 00157 00161 QString KateSyntaxDocument::groupItemData( const KateSyntaxContextData* data, const QString& name){ 00162 if(!data) 00163 return QString(); 00164 00165 // If there's no name just return the tag name of data->item 00166 if ( (!data->item.isNull()) && (name.isEmpty())) 00167 { 00168 return data->item.tagName(); 00169 } 00170 00171 // if name is not empty return the value of the attribute name 00172 if (!data->item.isNull()) 00173 { 00174 return data->item.attribute(name); 00175 } 00176 00177 return QString(); 00178 00179 } 00180 00181 QString KateSyntaxDocument::groupData( const KateSyntaxContextData* data,const QString& name) 00182 { 00183 if(!data) 00184 return QString(); 00185 00186 if (!data->currentGroup.isNull()) 00187 { 00188 return data->currentGroup.attribute(name); 00189 } 00190 else 00191 { 00192 return QString(); 00193 } 00194 } 00195 00196 void KateSyntaxDocument::freeGroupInfo( KateSyntaxContextData* data) 00197 { 00198 delete data; 00199 } 00200 00201 KateSyntaxContextData* KateSyntaxDocument::getSubItems(KateSyntaxContextData* data) 00202 { 00203 KateSyntaxContextData *retval = new KateSyntaxContextData; 00204 00205 if (data != 0) 00206 { 00207 retval->parent = data->currentGroup; 00208 retval->currentGroup = data->item; 00209 } 00210 00211 return retval; 00212 } 00213 00214 bool KateSyntaxDocument::getElement (QDomElement &element, const QString &mainGroupName, const QString &config) 00215 { 00216 #ifdef KSD_OVER_VERBOSE 00217 kDebug(13010) << "Looking for \"" << mainGroupName << "\" -> \"" << config << "\"."; 00218 #endif 00219 00220 QDomNodeList nodes = documentElement().childNodes(); 00221 00222 // Loop over all these child nodes looking for mainGroupName 00223 for (int i=0; i<nodes.count(); i++) 00224 { 00225 QDomElement elem = nodes.item(i).toElement(); 00226 if (elem.tagName() == mainGroupName) 00227 { 00228 // Found mainGroupName ... 00229 QDomNodeList subNodes = elem.childNodes(); 00230 00231 // ... so now loop looking for config 00232 for (int j=0; j<subNodes.count(); j++) 00233 { 00234 QDomElement subElem = subNodes.item(j).toElement(); 00235 if (subElem.tagName() == config) 00236 { 00237 // Found it! 00238 element = subElem; 00239 return true; 00240 } 00241 } 00242 00243 #ifdef KSD_OVER_VERBOSE 00244 kDebug(13010) << "WARNING: \""<< config <<"\" wasn't found!"; 00245 #endif 00246 00247 return false; 00248 } 00249 } 00250 00251 #ifdef KSD_OVER_VERBOSE 00252 kDebug(13010) << "WARNING: \""<< mainGroupName <<"\" wasn't found!"; 00253 #endif 00254 00255 return false; 00256 } 00257 00262 KateSyntaxContextData* KateSyntaxDocument::getConfig(const QString& mainGroupName, const QString &config) 00263 { 00264 QDomElement element; 00265 if (getElement(element, mainGroupName, config)) 00266 { 00267 KateSyntaxContextData *data = new KateSyntaxContextData; 00268 data->item = element; 00269 return data; 00270 } 00271 return 0; 00272 } 00273 00278 KateSyntaxContextData* KateSyntaxDocument::getGroupInfo(const QString& mainGroupName, const QString &group) 00279 { 00280 QDomElement element; 00281 if (getElement(element, mainGroupName, group+'s')) 00282 { 00283 KateSyntaxContextData *data = new KateSyntaxContextData; 00284 data->parent = element; 00285 return data; 00286 } 00287 return 0; 00288 } 00289 00293 QStringList& KateSyntaxDocument::finddata(const QString& mainGroup, const QString& type, bool clearList) 00294 { 00295 #ifdef KSD_OVER_VERBOSE 00296 kDebug(13010)<<"Create a list of keywords \""<<type<<"\" from \""<<mainGroup<<"\"."; 00297 #endif 00298 00299 if (clearList) 00300 m_data.clear(); 00301 00302 for(QDomNode node = documentElement().firstChild(); !node.isNull(); node = node.nextSibling()) 00303 { 00304 QDomElement elem = node.toElement(); 00305 if (elem.tagName() == mainGroup) 00306 { 00307 #ifdef KSD_OVER_VERBOSE 00308 kDebug(13010)<<"\""<<mainGroup<<"\" found."; 00309 #endif 00310 00311 QDomNodeList nodelist1 = elem.elementsByTagName("list"); 00312 00313 for (int l=0; l<nodelist1.count(); l++) 00314 { 00315 if (nodelist1.item(l).toElement().attribute("name") == type) 00316 { 00317 #ifdef KSD_OVER_VERBOSE 00318 kDebug(13010)<<"List with attribute name=\""<<type<<"\" found."; 00319 #endif 00320 00321 QDomNodeList childlist = nodelist1.item(l).toElement().childNodes(); 00322 00323 for (int i=0; i<childlist.count(); i++) 00324 { 00325 QString element = childlist.item(i).toElement().text().trimmed(); 00326 if (element.isEmpty()) 00327 continue; 00328 00329 #ifdef KSD_OVER_VERBOSE 00330 if (i<6) 00331 { 00332 kDebug(13010)<<"\""<<element<<"\" added to the list \""<<type<<"\""; 00333 } 00334 else if(i==6) 00335 { 00336 kDebug(13010)<<"... The list continues ..."; 00337 } 00338 #endif 00339 00340 m_data += element; 00341 } 00342 00343 break; 00344 } 00345 } 00346 break; 00347 } 00348 } 00349 00350 return m_data; 00351 } 00352 00353 // Private 00357 void KateSyntaxDocument::setupModeList (bool force) 00358 { 00359 // If there's something in myModeList the Mode List was already built so, don't do it again 00360 if (!myModeList.isEmpty()) 00361 return; 00362 00363 // We'll store the ModeList in katesyntaxhighlightingrc 00364 KConfigGroup generalConfig(m_config, "General"); 00365 00366 // figure our if the kate install is too new 00367 if (generalConfig.readEntry ("Version",0) > generalConfig.readEntry ("CachedVersion",0)) 00368 { 00369 generalConfig.writeEntry ("CachedVersion", generalConfig.readEntry ("Version",0)); 00370 force = true; 00371 } 00372 00373 // Let's get a list of all the xml files for hl 00374 const QStringList list = KGlobal::dirs()->findAllResources("data","katepart/syntax/*.xml", 00375 KStandardDirs::NoDuplicates); 00376 00377 // Let's iterate through the list and build the Mode List 00378 for ( QStringList::ConstIterator it = list.begin(); it != list.end(); ++it ) 00379 { 00380 // Each file has a group called: 00381 QString Group="Cache "+ *it; 00382 00383 // Let's go to this group 00384 KConfigGroup config(m_config, Group); 00385 00386 // stat the file 00387 KDE_struct_stat sbuf; 00388 memset (&sbuf, 0, sizeof(sbuf)); 00389 KDE::stat(*it, &sbuf); 00390 00391 // If the group exist and we're not forced to read the xml file, let's build myModeList for katesyntax..rc 00392 if (!force && config.exists() && (sbuf.st_mtime == config.readEntry("lastModified",0))) 00393 { 00394 // Let's make a new KateSyntaxModeListItem to instert in myModeList from the information in katesyntax..rc 00395 KateSyntaxModeListItem *mli=new KateSyntaxModeListItem; 00396 mli->name = config.readEntry("name"); 00397 mli->nameTranslated = i18nc("Language",mli->name.toUtf8()); 00398 mli->section = i18nc("Language Section",config.readEntry("section").toUtf8()); 00399 mli->mimetype = config.readEntry("mimetype"); 00400 mli->extension = config.readEntry("extension"); 00401 mli->version = config.readEntry("version"); 00402 mli->priority = config.readEntry("priority"); 00403 mli->style = config.readEntry("style"); 00404 mli->author = config.readEntry("author"); 00405 mli->license = config.readEntry("license"); 00406 mli->indenter = config.readEntry("indenter"); 00407 mli->hidden = config.readEntry("hidden", false); 00408 mli->identifier = *it; 00409 00410 // Apend the item to the list 00411 myModeList.append(mli); 00412 } 00413 else 00414 { 00415 #ifdef KSD_OVER_VERBOSE 00416 kDebug (13010) << "UPDATE hl cache for: " << *it; 00417 #endif 00418 00419 // We're forced to read the xml files or the mode doesn't exist in the katesyntax...rc 00420 QFile f(*it); 00421 00422 if (f.open(QIODevice::ReadOnly)) 00423 { 00424 // Ok we opened the file, let's read the contents and close the file 00425 /* the return of setContent should be checked because a false return shows a parsing error */ 00426 QString errMsg; 00427 int line, col; 00428 00429 bool success = setContent(&f,&errMsg,&line,&col); 00430 00431 f.close(); 00432 00433 if (success) 00434 { 00435 QDomElement root = documentElement(); 00436 00437 if (!root.isNull()) 00438 { 00439 // If the 'first' tag is language, go on 00440 if (root.tagName()=="language") 00441 { 00442 // let's make the mode list item. 00443 KateSyntaxModeListItem *mli = new KateSyntaxModeListItem; 00444 00445 mli->name = root.attribute("name"); 00446 mli->section = root.attribute("section"); 00447 mli->mimetype = root.attribute("mimetype"); 00448 mli->extension = root.attribute("extensions"); 00449 mli->version = root.attribute("version"); 00450 mli->priority = root.attribute("priority"); 00451 mli->style = root.attribute("style"); 00452 mli->author = root.attribute("author"); 00453 mli->license = root.attribute("license"); 00454 mli->indenter = root.attribute("indenter"); 00455 00456 QString hidden = root.attribute("hidden"); 00457 mli->hidden = (hidden == "true" || hidden == "TRUE"); 00458 00459 mli->identifier = *it; 00460 00461 // Now let's write or overwrite (if force==true) the entry in katesyntax...rc 00462 config = KConfigGroup(m_config, Group); 00463 config.writeEntry("name",mli->name); 00464 config.writeEntry("section",mli->section); 00465 config.writeEntry("mimetype",mli->mimetype); 00466 config.writeEntry("extension",mli->extension); 00467 config.writeEntry("version",mli->version); 00468 config.writeEntry("priority",mli->priority); 00469 config.writeEntry("style",mli->style); 00470 config.writeEntry("author",mli->author); 00471 config.writeEntry("license",mli->license); 00472 config.writeEntry("indenter",mli->indenter); 00473 config.writeEntry("hidden",mli->hidden); 00474 00475 // modified time to keep cache in sync 00476 config.writeEntry("lastModified", int(sbuf.st_mtime)); 00477 00478 // Now that the data is in the config file, translate section 00479 mli->section = i18nc("Language Section",mli->section.toUtf8()); 00480 mli->nameTranslated = i18nc("Language",mli->name.toUtf8()); 00481 00482 // Append the new item to the list. 00483 myModeList.append(mli); 00484 } 00485 } 00486 } 00487 else 00488 { 00489 KateSyntaxModeListItem *emli=new KateSyntaxModeListItem; 00490 00491 emli->section=i18n("Errors!"); 00492 emli->mimetype="invalid_file/invalid_file"; 00493 emli->extension="invalid_file.invalid_file"; 00494 emli->version="1."; 00495 emli->name=QString ("Error: %1").arg(*it); // internal 00496 emli->nameTranslated=i18n("Error: %1", *it); // translated 00497 emli->identifier=(*it); 00498 00499 myModeList.append(emli); 00500 } 00501 } 00502 } 00503 } 00504 00505 // Synchronize with the file katesyntax...rc 00506 generalConfig.sync(); 00507 } 00508 00509 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE 4.6 API Reference