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