File indexing completed on 2024-04-14 15:17:28
0001 /********************************************************************************************* 0002 Copyright (C) 2003 by Jeroen Wijnhout (Jeroen.Wijnhout@kdemail.net) 0003 (C) 2005-2007 by Holger Danielsson (holger.danielsson@versanet.de) 0004 (C) 2006-2022 by Michel Ludwig (michel.ludwig@kdemail.net) 0005 *********************************************************************************************/ 0006 0007 /*************************************************************************** 0008 * * 0009 * This program is free software; you can redistribute it and/or modify * 0010 * it under the terms of the GNU General Public License as published by * 0011 * the Free Software Foundation; either version 2 of the License, or * 0012 * (at your option) any later version. * 0013 * * 0014 ***************************************************************************/ 0015 0016 // 2005-11-02: dani 0017 // - cleaning up source of central function updateStruct() 0018 // - always use 'else if', because all conditions are exclusive or 0019 // - most often used commands are at the top 0020 // - add some new types of elements (and levels) for the structure view 0021 // - new commands, which are passed to the structure listview: 0022 // \includegraphics, \caption 0023 // - all user-defined commands for labels are recognized 0024 // - changed folder name of KileStruct::BibItem to "bibs", so that "refs" 0025 // is still unused and can be used for references (if wanted) 0026 // - \begin, \end to gather all environments. But only figure and table 0027 // environments are passed to the structure view 0028 0029 // 2005-11-26: dani 0030 // - add support for \fref, \Fref and \eqref references commands 0031 0032 // 2005-12-07: dani 0033 // - add support to enable and disable some structure view items 0034 0035 // 2006-01-16 tbraun 0036 // - fix #59945 Now we call (through a signal ) project->buildProjectTree so the bib files are correct, 0037 // and therefore the keys in \cite completion 0038 0039 // 2006-02-09 tbraun/dani 0040 // - fix #106261#4 improved parsing of (optional) command parameters 0041 // - all comments are removed 0042 0043 //2006-09-09 mludwig 0044 // - generalising the different document types 0045 0046 //2007-02-15 0047 // - signal foundItem() not only sends the cursor position of the parameter, 0048 // but also the real cursor position of the command 0049 0050 // 2007-03-12 dani 0051 // - use KileDocument::Extensions 0052 0053 // 2007-03-24 dani 0054 // - preliminary minimal support for Beamer class 0055 0056 // 2007-03-25 dani 0057 // - merge labels and sections in document structure view as user configurable option 0058 0059 // 2007-04-06 dani 0060 // - add TODO/FIXME section to structure view 0061 0062 #include "documentinfo.h" 0063 0064 #include <config.h> 0065 0066 #include <QDateTime> 0067 #include <QFileInfo> 0068 #include <QInputDialog> 0069 #include <QRegExp> 0070 0071 #include <KConfig> 0072 #include <KJobWidgets> 0073 #include <KIO/StatJob> 0074 #include <KLocalizedString> 0075 #include <KMessageBox> 0076 0077 #include "abbreviationmanager.h" 0078 #include "codecompletion.h" 0079 #include "configurationmanager.h" 0080 #include "editorextension.h" 0081 #include "eventfilter.h" 0082 #include "kileconfig.h" 0083 #include "kiledebug.h" 0084 #include "kileviewmanager.h" 0085 #include "parser/bibtexparser.h" 0086 #include "parser/latexparser.h" 0087 #include "parser/parsermanager.h" 0088 #include "livepreview.h" 0089 #include "utilities.h" 0090 0091 namespace KileDocument 0092 { 0093 0094 bool Info::containsInvalidCharacters(const QUrl &url) 0095 { 0096 QString filename = url.fileName(); 0097 return filename.contains(" ") || filename.contains("~") || filename.contains("$") || filename.contains("#"); 0098 } 0099 0100 QUrl Info::repairInvalidCharacters(const QUrl &url, QWidget* mainWidget, bool checkForFileExistence /* = true */) 0101 { 0102 QUrl ret(url); 0103 do { 0104 bool isOK; 0105 QString newURL = QInputDialog::getText( 0106 mainWidget, 0107 i18n("Invalid Characters"), 0108 i18n("The filename contains invalid characters ($~ #).<br>Please provide " 0109 "another one, or click \"Cancel\" to save anyway."), 0110 QLineEdit::Normal, 0111 ret.fileName(), 0112 &isOK); 0113 if(!isOK) { 0114 break; 0115 } 0116 ret = ret.adjusted(QUrl::RemoveFilename); 0117 ret.setPath(ret.path() + newURL); 0118 } while(containsInvalidCharacters(ret)); 0119 0120 return (checkForFileExistence ? renameIfExist(ret, mainWidget) : ret); 0121 } 0122 0123 QUrl Info::renameIfExist(const QUrl &url, QWidget* mainWidget) 0124 { 0125 QUrl ret(url); 0126 0127 auto statJob = KIO::statDetails(url, KIO::StatJob::SourceSide, KIO::StatNoDetails); 0128 KJobWidgets::setWindow(statJob, mainWidget); 0129 while (statJob->exec()) { // check for writing possibility 0130 bool isOK; 0131 QString newURL = QInputDialog::getText( 0132 mainWidget, 0133 i18n("File Already Exists"), 0134 i18n("A file with filename '%1' already exists.<br>Please provide " 0135 "another one, or click \"Cancel\" to overwrite it.", ret.fileName()), 0136 QLineEdit::Normal, 0137 ret.fileName(), 0138 &isOK); 0139 if (!isOK) { 0140 break; 0141 } 0142 ret = ret.adjusted(QUrl::RemoveFilename); 0143 ret.setPath(ret.path() + newURL); 0144 } 0145 return ret; 0146 } 0147 0148 QUrl Info::repairExtension(const QUrl &url, QWidget *mainWidget, bool checkForFileExistence /* = true */) 0149 { 0150 QUrl ret(url); 0151 0152 QString filename = url.fileName(); 0153 if(filename.contains(".") && filename[0] != '.') // There already is an extension 0154 return ret; 0155 0156 if(KMessageBox::questionTwoActions(Q_NULLPTR, 0157 i18n("The given filename has no extension; do you want one to be automatically added?"), 0158 i18n("Missing Extension"), 0159 KStandardGuiItem::add(), 0160 KStandardGuiItem::cancel(), 0161 "AutomaticallyAddExtension") == KMessageBox::PrimaryAction) 0162 { 0163 ret = ret.adjusted(QUrl::RemoveFilename); 0164 ret.setPath(ret.path() + filename + ".tex"); 0165 } 0166 return (checkForFileExistence ? renameIfExist(ret, mainWidget) : ret); 0167 } 0168 0169 QUrl Info::makeValidTeXURL(const QUrl &url, QWidget *mainWidget, bool istexfile, bool checkForFileExistence) 0170 { 0171 QUrl newURL(url); 0172 0173 //add a .tex extension 0174 if(!istexfile) { 0175 newURL = repairExtension(newURL, mainWidget, checkForFileExistence); 0176 } 0177 0178 //remove characters TeX does not accept, make sure the newURL does not exists yet 0179 if(containsInvalidCharacters(newURL)) { 0180 newURL = repairInvalidCharacters(newURL, mainWidget, checkForFileExistence); 0181 } 0182 0183 return newURL; 0184 } 0185 0186 Info::Info() : 0187 m_bIsRoot(false), 0188 m_dirty(false), 0189 m_config(KSharedConfig::openConfig().data()), 0190 documentTypePromotionAllowed(true) 0191 { 0192 updateStructLevelInfo(); 0193 } 0194 0195 Info::~Info() 0196 { 0197 KILE_DEBUG_MAIN << "DELETING DOCINFO" << this; 0198 } 0199 0200 void Info::updateStructLevelInfo() 0201 { 0202 KILE_DEBUG_MAIN << "===void Info::updateStructLevelInfo()==="; 0203 // read config for structureview items 0204 m_showStructureLabels = KileConfig::svShowLabels(); 0205 m_showStructureReferences = KileConfig::svShowReferences(); 0206 m_showStructureBibitems = KileConfig::svShowBibitems(); 0207 m_showStructureGraphics = KileConfig::svShowGraphics(); 0208 m_showStructureFloats = KileConfig::svShowFloats(); 0209 m_showStructureInputFiles = KileConfig::svShowInputFiles(); 0210 m_showStructureTodo = KileConfig::svShowTodo(); 0211 m_showSectioningLabels = KileConfig::svShowSectioningLabels(); 0212 m_openStructureLabels = KileConfig::svOpenLabels(); 0213 m_openStructureReferences = KileConfig::svOpenReferences(); 0214 m_openStructureBibitems = KileConfig::svOpenBibitems(); 0215 m_openStructureTodo = KileConfig::svOpenTodo(); 0216 } 0217 0218 void Info::setBaseDirectory(const QUrl &url) 0219 { 0220 KILE_DEBUG_MAIN << "===void Info::setBaseDirectory(const QUrl&" << url << ")==="; 0221 m_baseDirectory = url; 0222 } 0223 0224 const QUrl &Info::getBaseDirectory() const 0225 { 0226 return m_baseDirectory; 0227 } 0228 0229 bool Info::isTextDocument() 0230 { 0231 return false; 0232 } 0233 0234 Type Info::getType() 0235 { 0236 return Undefined; 0237 } 0238 0239 std::list<Extensions::ExtensionType> Info::getFileFilter() const 0240 { 0241 return {}; 0242 } 0243 0244 bool Info::isDocumentTypePromotionAllowed() 0245 { 0246 return documentTypePromotionAllowed; 0247 } 0248 0249 void Info::setDocumentTypePromotionAllowed(bool b) 0250 { 0251 documentTypePromotionAllowed = b; 0252 } 0253 0254 bool Info::isDirty() const 0255 { 0256 return m_dirty; 0257 } 0258 0259 void Info::setDirty(bool b) 0260 { 0261 m_dirty = b; 0262 } 0263 0264 void Info::installParserOutput(KileParser::ParserOutput *parserOutput) 0265 { 0266 Q_UNUSED(parserOutput); 0267 } 0268 0269 QUrl Info::url() 0270 { 0271 return QUrl(); 0272 } 0273 0274 void Info::count(const QString& line, long *stat) 0275 { 0276 QChar c; 0277 int state = stStandard; 0278 bool word = false; // we are in a word 0279 0280 int lineLength = line.length(); 0281 for(int p = 0; p < lineLength; ++p) { 0282 c = line[p]; 0283 0284 switch(state) { 0285 case stStandard: 0286 if(c == TEX_CAT0) { 0287 state = stControlSequence; 0288 ++stat[1]; 0289 0290 //look ahead to avoid counting words like K\"ahler as two words 0291 if( (p+1) < lineLength && ( !line[p+1].isPunct() || line[p+1] == '~' || line[p+1] == '^' )) { 0292 word = false; 0293 } 0294 } 0295 else if(c == TEX_CAT14) { 0296 state = stComment; 0297 } 0298 else { 0299 if (c.isLetterOrNumber()) { 0300 //only start new word if first character is a letter (42test is still counted as a word, but 42.2 not) 0301 if (c.isLetter() && !word) { 0302 word = true; 0303 ++stat[3]; 0304 } 0305 ++stat[0]; 0306 } 0307 else { 0308 ++stat[2]; 0309 word = false; 0310 } 0311 } 0312 break; 0313 0314 case stControlSequence : 0315 if(c.isLetter()) { 0316 // "\begin{[a-zA-z]+}" is an environment, and you can't define a command like \begin 0317 if(line.mid(p, 5) == "begin") { 0318 ++stat[5]; 0319 state = stEnvironment; 0320 stat[1] +=5; 0321 p+=4; // after break p++ is executed 0322 } 0323 else if(line.mid(p, 3) == "end") { 0324 stat[1] +=3; 0325 state = stEnvironment; 0326 p+=2; 0327 } // we don't count \end as new environment, this can give wrong results in selections 0328 else { 0329 ++stat[4]; 0330 ++stat[1]; 0331 state = stCommand; 0332 } 0333 } 0334 else { 0335 ++stat[4]; 0336 ++stat[1]; 0337 state = stStandard; 0338 } 0339 break; 0340 0341 case stCommand : 0342 if(c.isLetter()) { 0343 ++stat[1]; 0344 } 0345 else if(c == TEX_CAT0) { 0346 ++stat[1]; 0347 state = stControlSequence; 0348 } 0349 else if(c == TEX_CAT14) { 0350 state = stComment; 0351 } 0352 else { 0353 ++stat[2]; 0354 state = stStandard; 0355 } 0356 break; 0357 0358 case stEnvironment : 0359 if(c == TEX_CAT2) { // until we find a closing } we have an environment 0360 ++stat[1]; 0361 state = stStandard; 0362 } 0363 else if(c == TEX_CAT14) { 0364 state = stComment; 0365 } 0366 else { 0367 ++stat[1]; 0368 } 0369 break; 0370 0371 case stComment : // if we get a selection the line possibly contains \n and so the comment is only valid till \n and not necessarily till line.length() 0372 if(c == '\n') { 0373 ++stat[2]; // \n was counted as punctuation in the old implementation 0374 state = stStandard; 0375 word = false; 0376 } 0377 break; 0378 0379 default : 0380 qWarning() << "Unhandled state in getStatistics " << state; 0381 break; 0382 } 0383 } 0384 } 0385 0386 void Info::updateStruct() 0387 { 0388 } 0389 0390 void Info::updateBibItems() 0391 { 0392 } 0393 0394 void Info::slotCompleted() 0395 { 0396 setDirty(true); 0397 emit completed(this); 0398 } 0399 0400 TextInfo::TextInfo(Extensions* extensions, 0401 KileAbbreviation::Manager* abbreviationManager, 0402 KileParser::Manager* parserManager, 0403 const QString& defaultMode) 0404 : m_doc(Q_NULLPTR), 0405 m_defaultMode(defaultMode), 0406 m_abbreviationManager(abbreviationManager), 0407 m_parserManager(parserManager) 0408 { 0409 m_arStatistics = new long[SIZE_STAT_ARRAY]; 0410 0411 m_extensions = extensions; 0412 m_abbreviationCodeCompletionModel = new KileCodeCompletion::AbbreviationCompletionModel(this, m_abbreviationManager); 0413 } 0414 0415 TextInfo::~TextInfo() 0416 { 0417 emit(aboutToBeDestroyed(this)); 0418 detach(); 0419 delete [] m_arStatistics; 0420 } 0421 0422 0423 const KTextEditor::Document* TextInfo::getDoc() const 0424 { 0425 return m_doc; 0426 } 0427 0428 KTextEditor::Document* TextInfo::getDoc() 0429 { 0430 return m_doc; 0431 } 0432 0433 const KTextEditor::Document* TextInfo::getDocument() const 0434 { 0435 return m_doc; 0436 } 0437 0438 KTextEditor::Document* TextInfo::getDocument() 0439 { 0440 return m_doc; 0441 } 0442 0443 void TextInfo::setDoc(KTextEditor::Document *doc) 0444 { 0445 setDocument(doc); 0446 } 0447 0448 void TextInfo::setDocument(KTextEditor::Document *doc) 0449 { 0450 KILE_DEBUG_MAIN << "===void TextInfo::setDoc(KTextEditor::Document *doc)==="; 0451 0452 if(m_doc == doc) { 0453 return; 0454 } 0455 0456 detach(); 0457 if(doc) { 0458 m_doc = doc; 0459 m_documentContents.clear(); 0460 connect(m_doc, SIGNAL(documentNameChanged(KTextEditor::Document*)), this, SLOT(slotFileNameChanged())); 0461 connect(m_doc, SIGNAL(documentUrlChanged(KTextEditor::Document*)), this, SLOT(slotFileNameChanged())); 0462 connect(m_doc, SIGNAL(completed()), this, SLOT(slotCompleted())); 0463 connect(m_doc, SIGNAL(modifiedChanged(KTextEditor::Document*)), this, SLOT(makeDirtyIfModified())); 0464 // this could be a KatePart bug, and as "work-around" we manually set the highlighting mode again 0465 connect(m_doc, SIGNAL(completed()), this, SLOT(activateDefaultMode())); 0466 setMode(m_defaultMode); 0467 installEventFilters(); 0468 registerCodeCompletionModels(); 0469 } 0470 } 0471 0472 void TextInfo::detach() 0473 { 0474 if(m_doc) { 0475 m_doc->disconnect(this); 0476 removeInstalledEventFilters(); 0477 removeSignalConnections(); 0478 unregisterCodeCompletionModels(); 0479 emit(documentDetached(m_doc)); 0480 } 0481 m_doc = Q_NULLPTR; 0482 } 0483 0484 void TextInfo::makeDirtyIfModified() 0485 { 0486 if(m_doc && m_doc->isModified()) { 0487 setDirty(true); 0488 } 0489 } 0490 0491 const long* TextInfo::getStatistics(KTextEditor::View *view) 0492 { 0493 /* [0] = #c in words, [1] = #c in latex commands and environments, 0494 [2] = #c whitespace, [3] = #words, [4] = # latex_commands, [5] = latex_environments */ 0495 m_arStatistics[0] = m_arStatistics[1] = m_arStatistics[2] = m_arStatistics[3] = m_arStatistics[4] = m_arStatistics[5] = 0; 0496 0497 QString line; 0498 0499 if(view && view->selection()) { 0500 line = view->selectionText(); 0501 KILE_DEBUG_MAIN << "line: " << line; 0502 count(line, m_arStatistics); 0503 } 0504 else if(m_doc) { 0505 for(int l = 0; l < m_doc->lines(); ++l) { 0506 line = m_doc->line(l); 0507 KILE_DEBUG_MAIN << "line : " << line; 0508 count(line, m_arStatistics); 0509 } 0510 } 0511 0512 return m_arStatistics; 0513 } 0514 0515 QUrl TextInfo::url() 0516 { 0517 if(m_doc) { 0518 return m_doc->url(); 0519 } 0520 else { 0521 return QUrl(); 0522 } 0523 } 0524 0525 Type TextInfo::getType() 0526 { 0527 return Text; 0528 } 0529 0530 bool TextInfo::isTextDocument() 0531 { 0532 return true; 0533 } 0534 0535 void TextInfo::setMode(const QString &mode) 0536 { 0537 KILE_DEBUG_MAIN << "==Kile::setMode(" << (m_doc ? m_doc->url().toString() : "<null doc>") << "," << mode << ")=================="; 0538 0539 if (m_doc && !mode.isEmpty()) { 0540 m_doc->setMode(mode); 0541 } 0542 } 0543 0544 void TextInfo::setHighlightingMode(const QString& highlight) 0545 { 0546 KILE_DEBUG_MAIN << "==Kile::setHighlightingMode(" << (m_doc ? m_doc->url().toString() : "<null doc>") << "," << highlight << " )=================="; 0547 0548 if (m_doc && !highlight.isEmpty()) { 0549 m_doc->setHighlightingMode(highlight); 0550 } 0551 } 0552 0553 void TextInfo::setDefaultMode(const QString& string) 0554 { 0555 m_defaultMode = string; 0556 } 0557 0558 // match a { with the corresponding } 0559 // pos is the position of the { 0560 QString TextInfo::matchBracket(QChar obracket, int &l, int &pos) 0561 { 0562 QChar cbracket; 0563 if(obracket == '{') { 0564 cbracket = '}'; 0565 } 0566 if(obracket == '[') { 0567 cbracket = ']'; 0568 } 0569 if(obracket == '(') { 0570 cbracket = ')'; 0571 } 0572 0573 QString line, grab = ""; 0574 int count=0; 0575 ++pos; 0576 0577 TodoResult todo; 0578 while(l <= m_doc->lines()) { 0579 line = getTextline(l,todo); 0580 int len = line.length(); 0581 for (int i=pos; i < len; ++i) { 0582 if(line[i] == '\\' && (line[i+1] == obracket || line[i+1] == cbracket)) { 0583 ++i; 0584 } 0585 else if(line[i] == obracket) { 0586 ++count; 0587 } 0588 else if(line[i] == cbracket) { 0589 --count; 0590 if (count < 0) { 0591 pos = i; 0592 return grab; 0593 } 0594 } 0595 0596 grab += line[i]; 0597 } 0598 ++l; 0599 pos = 0; 0600 } 0601 0602 return QString(); 0603 } 0604 0605 QString TextInfo::getTextline(uint line, TodoResult &todo) 0606 { 0607 static QRegExp reComments("[^\\\\](%.*$)"); 0608 0609 todo.type = -1; 0610 QString s = m_doc->line(line); 0611 if(!s.isEmpty()) { 0612 // remove comment lines 0613 if(s[0] == '%') { 0614 searchTodoComment(s,0,todo); 0615 s.clear(); 0616 } 0617 else { 0618 //remove escaped \ characters 0619 s.replace("\\\\", " "); 0620 0621 //remove comments 0622 int pos = s.indexOf(reComments); 0623 if(pos != -1) { 0624 searchTodoComment(s, pos,todo); 0625 s = s.left(reComments.pos(1)); 0626 } 0627 } 0628 } 0629 return s; 0630 } 0631 0632 void TextInfo::searchTodoComment(const QString &s, uint startpos, TodoResult &todo) 0633 { 0634 static QRegExp reTodoComment("\\b(TODO|FIXME)\\b(:|\\s)?\\s*(.*)"); 0635 0636 if(s.indexOf(reTodoComment, startpos) != -1) { 0637 todo.type = (reTodoComment.cap(1) == "TODO") ? KileStruct::ToDo : KileStruct::FixMe; 0638 todo.colTag = reTodoComment.pos(1); 0639 todo.colComment = reTodoComment.pos(3); 0640 todo.comment = reTodoComment.cap(3).trimmed(); 0641 } 0642 } 0643 0644 KTextEditor::View* TextInfo::createView(QWidget *parent, const char* /* name */) 0645 { 0646 if(!m_doc) { 0647 return Q_NULLPTR; 0648 } 0649 KTextEditor::View *view = m_doc->createView(parent); 0650 installEventFilters(view); 0651 installSignalConnections(view); 0652 registerCodeCompletionModels(view); 0653 view->setStatusBarEnabled(false); 0654 connect(view, SIGNAL(destroyed(QObject*)), this, SLOT(slotViewDestroyed(QObject*))); 0655 return view; 0656 } 0657 0658 void TextInfo::startAbbreviationCompletion(KTextEditor::View *view) 0659 { 0660 KTextEditor::CodeCompletionInterface* completionInterface = qobject_cast<KTextEditor::CodeCompletionInterface*>(view); 0661 if(!completionInterface) { 0662 return; 0663 } 0664 KTextEditor::Range range = m_abbreviationCodeCompletionModel->completionRange(view, view->cursorPosition()); 0665 if(!range.isValid()) { 0666 range = KTextEditor::Range(view->cursorPosition(), view->cursorPosition()); 0667 } 0668 completionInterface->startCompletion(range, m_abbreviationCodeCompletionModel); 0669 } 0670 0671 void TextInfo::slotFileNameChanged() 0672 { 0673 emit urlChanged(this, url()); 0674 } 0675 0676 void TextInfo::installEventFilters(KTextEditor::View *view) 0677 { 0678 if(m_eventFilterHash.find(view) != m_eventFilterHash.end()) { 0679 return; 0680 } 0681 0682 QList<QObject*> eventFilterList = createEventFilters(view); 0683 if(!eventFilterList.isEmpty()) { 0684 for(QList<QObject*>::iterator i = eventFilterList.begin(); i != eventFilterList.end(); ++i) { 0685 QObject* eventFilter = *i; 0686 KileView::Manager::installEventFilter(view, eventFilter); 0687 } 0688 m_eventFilterHash[view] = eventFilterList; 0689 } 0690 } 0691 0692 void TextInfo::removeInstalledEventFilters(KTextEditor::View *view) 0693 { 0694 QHash<KTextEditor::View*, QList<QObject*> >::iterator i = m_eventFilterHash.find(view); 0695 if(i != m_eventFilterHash.end()) { 0696 QList<QObject*> eventFilterList = *i; 0697 for(QList<QObject*>::iterator i2 = eventFilterList.begin(); i2 != eventFilterList.end(); ++i2) { 0698 QObject *eventFilter = *i2; 0699 KileView::Manager::removeEventFilter(view, eventFilter); 0700 delete(*i2); 0701 } 0702 m_eventFilterHash.erase(i); 0703 } 0704 } 0705 0706 QList<QObject*> TextInfo::createEventFilters(KTextEditor::View* /* view */) 0707 { 0708 return QList<QObject*>(); 0709 } 0710 0711 void TextInfo::installEventFilters() 0712 { 0713 if(!m_doc) { 0714 return; 0715 } 0716 QList<KTextEditor::View*> views = m_doc->views(); 0717 for(QList<KTextEditor::View*>::iterator i = views.begin(); i != views.end(); ++i) { 0718 installEventFilters(*i); 0719 } 0720 } 0721 0722 void TextInfo::removeInstalledEventFilters() 0723 { 0724 if(!m_doc) { 0725 return; 0726 } 0727 QList<KTextEditor::View*> views = m_doc->views(); 0728 for(QList<KTextEditor::View*>::iterator i = views.begin(); i != views.end(); ++i) { 0729 removeInstalledEventFilters(*i); 0730 } 0731 } 0732 0733 void TextInfo::installSignalConnections(KTextEditor::View *) 0734 { 0735 /* does nothing */ 0736 } 0737 0738 void TextInfo::removeSignalConnections(KTextEditor::View *) 0739 { 0740 /* does nothing */ 0741 } 0742 0743 void TextInfo::installSignalConnections() 0744 { 0745 if(!m_doc) { 0746 return; 0747 } 0748 QList<KTextEditor::View*> views = m_doc->views(); 0749 for(QList<KTextEditor::View*>::iterator i = views.begin(); i != views.end(); ++i) { 0750 installSignalConnections(*i); 0751 } 0752 } 0753 0754 void TextInfo::removeSignalConnections() 0755 { 0756 if(!m_doc) { 0757 return; 0758 } 0759 QList<KTextEditor::View*> views = m_doc->views(); 0760 for(QList<KTextEditor::View*>::iterator i = views.begin(); i != views.end(); ++i) { 0761 removeSignalConnections(*i); 0762 } 0763 } 0764 0765 void TextInfo::registerCodeCompletionModels(KTextEditor::View *view) 0766 { 0767 KTextEditor::CodeCompletionInterface* completionInterface = qobject_cast<KTextEditor::CodeCompletionInterface*>(view); 0768 if(!completionInterface) { 0769 return; 0770 } 0771 completionInterface->registerCompletionModel(m_abbreviationCodeCompletionModel); 0772 completionInterface->setAutomaticInvocationEnabled(true); 0773 } 0774 0775 void TextInfo::unregisterCodeCompletionModels(KTextEditor::View *view) 0776 { 0777 KTextEditor::CodeCompletionInterface* completionInterface = qobject_cast<KTextEditor::CodeCompletionInterface*>(view); 0778 if(!completionInterface) { 0779 return; 0780 } 0781 completionInterface->unregisterCompletionModel(m_abbreviationCodeCompletionModel); 0782 } 0783 0784 void TextInfo::registerCodeCompletionModels() 0785 { 0786 if(!m_doc) { 0787 return; 0788 } 0789 QList<KTextEditor::View*> views = m_doc->views(); 0790 for(QList<KTextEditor::View*>::iterator i = views.begin(); i != views.end(); ++i) { 0791 registerCodeCompletionModels(*i); 0792 } 0793 } 0794 0795 void TextInfo::unregisterCodeCompletionModels() 0796 { 0797 if(!m_doc) { 0798 return; 0799 } 0800 QList<KTextEditor::View*> views = m_doc->views(); 0801 for(QList<KTextEditor::View*>::iterator i = views.begin(); i != views.end(); ++i) { 0802 unregisterCodeCompletionModels(*i); 0803 } 0804 } 0805 0806 void TextInfo::slotViewDestroyed(QObject *object) 0807 { 0808 KTextEditor::View* view = dynamic_cast<KTextEditor::View*>(object); 0809 if(view) { 0810 removeInstalledEventFilters(view); 0811 removeSignalConnections(view); 0812 unregisterCodeCompletionModels(view); 0813 QHash<KTextEditor::View*, QList<QObject*> >::iterator i = m_eventFilterHash.find(view); 0814 if(i != m_eventFilterHash.end()) { 0815 m_eventFilterHash.erase(i); 0816 } 0817 } 0818 } 0819 0820 void TextInfo::activateDefaultMode() 0821 { 0822 KILE_DEBUG_MAIN << "m_defaultMode = " << m_defaultMode << Qt::endl; 0823 0824 if(m_doc && !m_defaultMode.isEmpty()) { 0825 m_doc->setMode(m_defaultMode); 0826 } 0827 } 0828 0829 const QStringList TextInfo::documentContents() const 0830 { 0831 if (m_doc) { 0832 return m_doc->textLines(m_doc->documentRange()); 0833 } 0834 else { 0835 return m_documentContents; 0836 } 0837 } 0838 0839 void TextInfo::setDocumentContents(const QStringList& contents) 0840 { 0841 m_documentContents = contents; 0842 } 0843 0844 LaTeXInfo::LaTeXInfo(Extensions* extensions, 0845 KileAbbreviation::Manager* abbreviationManager, 0846 LatexCommands* commands, 0847 EditorExtension* editorExtension, 0848 KileConfiguration::Manager* manager, 0849 KileCodeCompletion::Manager* codeCompletionManager, 0850 KileTool::LivePreviewManager* livePreviewManager, 0851 KileView::Manager *viewManager, 0852 KileParser::Manager* parserManager) 0853 : TextInfo(extensions, abbreviationManager, parserManager, "LaTeX"), 0854 m_commands(commands), 0855 m_editorExtension(editorExtension), 0856 m_configurationManager(manager), 0857 m_eventFilter(Q_NULLPTR), 0858 m_livePreviewManager(livePreviewManager), 0859 m_viewManager(viewManager) 0860 { 0861 documentTypePromotionAllowed = false; 0862 updateStructLevelInfo(); 0863 m_latexCompletionModel = new KileCodeCompletion::LaTeXCompletionModel(this, 0864 codeCompletionManager, 0865 editorExtension); 0866 } 0867 0868 LaTeXInfo::~LaTeXInfo() 0869 { 0870 } 0871 0872 Type LaTeXInfo::getType() 0873 { 0874 return LaTeX; 0875 } 0876 0877 std::list<Extensions::ExtensionType> LaTeXInfo::getFileFilter() const 0878 { 0879 return {Extensions::TEX, Extensions::PACKAGES}; 0880 } 0881 0882 void LaTeXInfo::startLaTeXCompletion(KTextEditor::View *view) 0883 { 0884 KTextEditor::CodeCompletionInterface* completionInterface = qobject_cast<KTextEditor::CodeCompletionInterface*>(view); 0885 if(!completionInterface) { 0886 return; 0887 } 0888 KTextEditor::Range range = m_latexCompletionModel->completionRange(view, view->cursorPosition()); 0889 if(!range.isValid()) { 0890 range = KTextEditor::Range(view->cursorPosition(), view->cursorPosition()); 0891 } 0892 completionInterface->startCompletion(range, m_latexCompletionModel); 0893 } 0894 0895 void LaTeXInfo::updateStructLevelInfo() { 0896 0897 KILE_DEBUG_MAIN << "===void LaTeXInfo::updateStructLevelInfo()==="; 0898 0899 // read config stuff 0900 Info::updateStructLevelInfo(); 0901 0902 // clear all entries 0903 m_dictStructLevel.clear(); 0904 0905 //TODO: make sectioning and bibliography configurable 0906 0907 // sectioning 0908 m_dictStructLevel["\\part"] = KileStructData(1, KileStruct::Sect, "part"); 0909 m_dictStructLevel["\\chapter"] = KileStructData(2, KileStruct::Sect, "chapter"); 0910 m_dictStructLevel["\\section"] = KileStructData(3, KileStruct::Sect, "section"); 0911 m_dictStructLevel["\\subsection"] = KileStructData(4, KileStruct::Sect, "subsection"); 0912 m_dictStructLevel["\\subsubsection"] = KileStructData(5, KileStruct::Sect, "subsubsection"); 0913 m_dictStructLevel["\\paragraph"] = KileStructData(6, KileStruct::Sect, "subsubsection"); 0914 m_dictStructLevel["\\subparagraph"] = KileStructData(7, KileStruct::Sect, "subsubsection"); 0915 0916 // hidden commands 0917 m_dictStructLevel["\\usepackage"] = KileStructData(KileStruct::Hidden, KileStruct::Package); 0918 m_dictStructLevel["\\newcommand"] = KileStructData(KileStruct::Hidden, KileStruct::NewCommand); 0919 m_dictStructLevel["\\newlength"] = KileStructData(KileStruct::Hidden, KileStruct::NewCommand); 0920 m_dictStructLevel["\\newenvironment"] = KileStructData(KileStruct::Hidden, KileStruct::NewEnvironment); 0921 m_dictStructLevel["\\addunit"] = KileStructData(KileStruct::Hidden, KileStruct::NewCommand); // hack to get support for the fancyunits package until we can configure the commands in the gui (tbraun) 0922 m_dictStructLevel["\\DeclareMathOperator"] = KileStructData(KileStruct::Hidden, KileStruct::NewCommand); // amsmath package 0923 m_dictStructLevel["\\caption"] = KileStructData(KileStruct::Hidden,KileStruct::Caption); 0924 0925 // bibitems 0926 if(m_showStructureBibitems) { 0927 m_dictStructLevel["\\bibitem"] = KileStructData(KileStruct::NotSpecified, KileStruct::BibItem, QString(), "bibs"); 0928 } 0929 0930 // graphics 0931 if(m_showStructureGraphics) { 0932 m_dictStructLevel["\\includegraphics"] = KileStructData(KileStruct::Object,KileStruct::Graphics, "graphics"); 0933 } 0934 0935 // float environments 0936 if(m_showStructureFloats) { 0937 m_dictStructLevel["\\begin"] = KileStructData(KileStruct::Object,KileStruct::BeginEnv); 0938 m_dictStructLevel["\\end"] = KileStructData(KileStruct::Hidden,KileStruct::EndEnv); 0939 0940 // some entries, which could never be found (but they are set manually) 0941 m_dictStructLevel["\\begin{figure}"]=KileStructData(KileStruct::Object,KileStruct::BeginFloat, "figure-env"); 0942 m_dictStructLevel["\\begin{figure*}"]=KileStructData(KileStruct::Object,KileStruct::BeginFloat, "figure-env"); 0943 m_dictStructLevel["\\begin{table}"]=KileStructData(KileStruct::Object,KileStruct::BeginFloat, "table-env"); 0944 m_dictStructLevel["\\begin{table*}"]=KileStructData(KileStruct::Object,KileStruct::BeginFloat, "table-env"); 0945 m_dictStructLevel["\\begin{asy}"]=KileStructData(KileStruct::Object,KileStruct::BeginFloat, "image-x-generic"); 0946 m_dictStructLevel["\\end{float}"]=KileStructData(KileStruct::Hidden,KileStruct::EndFloat); 0947 } 0948 0949 // preliminary minimal beamer support 0950 m_dictStructLevel["\\frame"] = KileStructData(KileStruct::Object, KileStruct::BeamerFrame, "beamerframe"); 0951 m_dictStructLevel["\\frametitle"] = KileStructData(KileStruct::Hidden, KileStruct::BeamerFrametitle); 0952 m_dictStructLevel["\\begin{frame}"] = KileStructData(KileStruct::Object, KileStruct::BeamerBeginFrame, "beamerframe"); 0953 m_dictStructLevel["\\end{frame}"] = KileStructData(KileStruct::Hidden, KileStruct::BeamerEndFrame); 0954 m_dictStructLevel["\\begin{block}"] = KileStructData(KileStruct::Object, KileStruct::BeamerBeginBlock, "beamerblock"); 0955 0956 // add user-defined commands 0957 0958 QStringList list; 0959 QStringList::ConstIterator it; 0960 0961 // labels, we also gather them 0962 m_commands->commandList(list,KileDocument::CmdAttrLabel, false); 0963 for(it=list.constBegin(); it != list.constEnd(); ++it) { 0964 m_dictStructLevel[*it] = KileStructData(KileStruct::NotSpecified, KileStruct::Label, QString(), "labels"); 0965 } 0966 0967 // input files 0968 if(m_showStructureInputFiles) { 0969 m_commands->commandList(list, KileDocument::CmdAttrIncludes, false); 0970 for(it = list.constBegin(); it != list.constEnd(); ++it) { 0971 m_dictStructLevel[*it] = KileStructData(KileStruct::File, KileStruct::Input, "input-file"); 0972 } 0973 } 0974 0975 // references 0976 if(m_showStructureReferences) { 0977 m_commands->commandList(list, KileDocument::CmdAttrReference, false); 0978 for(it=list.constBegin(); it != list.constEnd(); ++it ) { 0979 m_dictStructLevel[*it] = KileStructData(KileStruct::Hidden, KileStruct::Reference); 0980 } 0981 } 0982 0983 //bibliography commands 0984 m_commands->commandList(list,KileDocument::CmdAttrBibliographies, false); 0985 for(it=list.constBegin(); it != list.constEnd(); ++it) { 0986 m_dictStructLevel[*it] = KileStructData(0, KileStruct::Bibliography, "viewbib"); 0987 } 0988 } 0989 0990 QList<QObject*> LaTeXInfo::createEventFilters(KTextEditor::View *view) 0991 { 0992 QList<QObject*> toReturn; 0993 QObject *eventFilter = new LaTeXEventFilter(view, m_editorExtension); 0994 connect(m_configurationManager, SIGNAL(configChanged()), eventFilter, SLOT(readConfig())); 0995 toReturn << eventFilter; 0996 return toReturn; 0997 } 0998 0999 void LaTeXInfo::installSignalConnections(KTextEditor::View *view) 1000 { 1001 connect(view, &KTextEditor::View::cursorPositionChanged, 1002 m_viewManager, &KileView::Manager::handleCursorPositionChanged); 1003 connect(view->document(), &KTextEditor::Document::textChanged, 1004 m_livePreviewManager, &KileTool::LivePreviewManager::handleTextChanged, Qt::UniqueConnection); 1005 connect(view->document(), &KTextEditor::Document::documentSavedOrUploaded, 1006 m_livePreviewManager, &KileTool::LivePreviewManager::handleDocumentSavedOrUploaded, Qt::UniqueConnection); 1007 } 1008 1009 void LaTeXInfo::removeSignalConnections(KTextEditor::View *view) 1010 { 1011 disconnect(view, &KTextEditor::View::cursorPositionChanged, 1012 m_viewManager, &KileView::Manager::handleCursorPositionChanged); 1013 disconnect(view->document(), &KTextEditor::Document::textChanged, 1014 m_livePreviewManager, &KileTool::LivePreviewManager::handleTextChanged); 1015 disconnect(view->document(), &KTextEditor::Document::documentSavedOrUploaded, 1016 m_livePreviewManager, &KileTool::LivePreviewManager::handleDocumentSavedOrUploaded); 1017 } 1018 1019 void LaTeXInfo::registerCodeCompletionModels(KTextEditor::View *view) 1020 { 1021 KTextEditor::CodeCompletionInterface* completionInterface = qobject_cast<KTextEditor::CodeCompletionInterface*>(view); 1022 if(!completionInterface) { 1023 return; 1024 } 1025 completionInterface->registerCompletionModel(m_latexCompletionModel); 1026 completionInterface->setAutomaticInvocationEnabled(true); 1027 TextInfo::registerCodeCompletionModels(view); 1028 } 1029 1030 void LaTeXInfo::unregisterCodeCompletionModels(KTextEditor::View *view) 1031 { 1032 KTextEditor::CodeCompletionInterface* completionInterface = qobject_cast<KTextEditor::CodeCompletionInterface*>(view); 1033 if(!completionInterface) { 1034 return; 1035 } 1036 completionInterface->unregisterCompletionModel(m_latexCompletionModel); 1037 TextInfo::unregisterCodeCompletionModels(view); 1038 } 1039 1040 BracketResult LaTeXInfo::matchBracket(int &l, int &pos) 1041 { 1042 BracketResult result; 1043 TodoResult todo; 1044 1045 if(m_doc->line(l)[pos] == '[') { 1046 result.option = TextInfo::matchBracket('[', l, pos); 1047 while(l < m_doc->lines()) { 1048 int p = getTextline(l, todo).indexOf('{', pos); 1049 if(p != -1) { 1050 pos = p; 1051 break; 1052 } 1053 else { 1054 pos = 0; 1055 ++l; 1056 } 1057 } 1058 } 1059 1060 if(m_doc->line(l)[pos] == '{') { 1061 result.line = l; 1062 result.col = pos; 1063 result.value = TextInfo::matchBracket('{', l, pos); 1064 } 1065 1066 return result; 1067 } 1068 1069 void LaTeXInfo::updateStruct() 1070 { 1071 KILE_DEBUG_MAIN << "==void TeXInfo::updateStruct: (" << url() << ")========="; 1072 1073 m_parserManager->parseDocument(this); 1074 } 1075 1076 void LaTeXInfo::checkChangedDeps() 1077 { 1078 if(m_depsPrev != m_deps) { 1079 KILE_DEBUG_MAIN << "===void LaTeXInfo::checkChangedDeps()===, deps have changed"<< Qt::endl; 1080 emit(depChanged()); 1081 m_depsPrev = m_deps; 1082 } 1083 } 1084 1085 void LaTeXInfo::installParserOutput(KileParser::ParserOutput *parserOutput) 1086 { 1087 KILE_DEBUG_MAIN; 1088 KileParser::LaTeXParserOutput *latexParserOutput = dynamic_cast<KileParser::LaTeXParserOutput*>(parserOutput); 1089 Q_ASSERT(latexParserOutput); 1090 if(!latexParserOutput) { 1091 KILE_DEBUG_MAIN << "wrong type given"; 1092 return; 1093 } 1094 1095 m_labels = latexParserOutput->labels; 1096 m_bibItems = latexParserOutput->bibItems; 1097 m_deps = latexParserOutput->deps; 1098 m_bibliography = latexParserOutput->bibliography; 1099 m_packages = latexParserOutput->packages; 1100 m_newCommands = latexParserOutput->newCommands; 1101 m_asyFigures = latexParserOutput->asyFigures; 1102 m_preamble = latexParserOutput->preamble; 1103 m_bIsRoot = latexParserOutput->bIsRoot; 1104 1105 checkChangedDeps(); 1106 emit(isrootChanged(isLaTeXRoot())); 1107 setDirty(false); 1108 emit(parsingComplete()); 1109 } 1110 1111 BibInfo::BibInfo(Extensions* extensions, 1112 KileAbbreviation::Manager* abbreviationManager, 1113 KileParser::Manager* parserManager, 1114 LatexCommands* /* commands */) 1115 : TextInfo(extensions, abbreviationManager, parserManager, "BibTeX") 1116 { 1117 documentTypePromotionAllowed = false; 1118 } 1119 1120 BibInfo::~BibInfo() 1121 { 1122 } 1123 1124 bool BibInfo::isLaTeXRoot() 1125 { 1126 return false; 1127 } 1128 1129 void BibInfo::updateStruct() 1130 { 1131 m_parserManager->parseDocument(this); 1132 } 1133 1134 void BibInfo::installParserOutput(KileParser::ParserOutput *parserOutput) 1135 { 1136 KILE_DEBUG_MAIN; 1137 KileParser::BibTeXParserOutput *bibtexParserOutput = dynamic_cast<KileParser::BibTeXParserOutput*>(parserOutput); 1138 Q_ASSERT(bibtexParserOutput); 1139 if(!bibtexParserOutput) { 1140 KILE_DEBUG_MAIN << "wrong type given"; 1141 return; 1142 } 1143 1144 m_bibItems = bibtexParserOutput->bibItems; 1145 1146 setDirty(false); 1147 emit(parsingComplete()); 1148 } 1149 1150 Type BibInfo::getType() 1151 { 1152 return BibTeX; 1153 } 1154 1155 std::list<Extensions::ExtensionType> BibInfo::getFileFilter() const 1156 { 1157 return {Extensions::BIB}; 1158 } 1159 1160 ScriptInfo::ScriptInfo(Extensions* extensions, 1161 KileAbbreviation::Manager* abbreviationManager, 1162 KileParser::Manager* parserManager) 1163 : TextInfo(extensions, abbreviationManager, parserManager, "JavaScript") 1164 { 1165 documentTypePromotionAllowed = false; 1166 } 1167 1168 ScriptInfo::~ScriptInfo() 1169 { 1170 } 1171 1172 bool ScriptInfo::isLaTeXRoot() 1173 { 1174 return false; 1175 } 1176 1177 Type ScriptInfo::getType() 1178 { 1179 return Script; 1180 } 1181 1182 std::list<Extensions::ExtensionType> ScriptInfo::getFileFilter() const 1183 { 1184 return {Extensions::JS}; 1185 } 1186 1187 } 1188