1 /*********************************************************************************************
2     Copyright (C) 2003 by Jeroen Wijnhout (Jeroen.Wijnhout@kdemail.net)
3               (C) 2005-2007 by Holger Danielsson (holger.danielsson@versanet.de)
4               (C) 2006-2017 by Michel Ludwig (michel.ludwig@kdemail.net)
5  *********************************************************************************************/
6 
7 /***************************************************************************
8  *                                                                         *
9  *   This program is free software; you can redistribute it and/or modify  *
10  *   it under the terms of the GNU General Public License as published by  *
11  *   the Free Software Foundation; either version 2 of the License, or     *
12  *   (at your option) any later version.                                   *
13  *                                                                         *
14  ***************************************************************************/
15 
16 // 2005-11-02: dani
17 //  - cleaning up source of central function updateStruct()
18 //      - always use 'else if', because all conditions are exclusive or
19 //      - most often used commands are at the top
20 //  - add some new types of elements (and levels) for the structure view
21 //  - new commands, which are passed to the structure listview:
22 //       \includegraphics, \caption
23 //  - all user-defined commands for labels are recognized
24 //  - changed folder name of KileStruct::BibItem to "bibs", so that "refs"
25 //    is still unused and can be used for references (if wanted)
26 //  - \begin, \end to gather all environments. But only figure and table
27 //    environments are passed to the structure view
28 
29 // 2005-11-26: dani
30 //  - add support for \fref, \Fref and \eqref references commands
31 
32 // 2005-12-07: dani
33 //  - add support to enable and disable some structure view items
34 
35 // 2006-01-16 tbraun
36 // - fix #59945 Now we call (through a signal ) project->buildProjectTree so the bib files are correct,
37 //   and therefore the keys in \cite completion
38 
39 // 2006-02-09 tbraun/dani
40 // - fix #106261#4 improved parsing of (optional) command parameters
41 // - all comments are removed
42 
43 //2006-09-09 mludwig
44 // - generalising the different document types
45 
46 //2007-02-15
47 // - signal foundItem() not only sends the cursor position of the parameter,
48 //   but also the real cursor position of the command
49 
50 // 2007-03-12 dani
51 //  - use KileDocument::Extensions
52 
53 // 2007-03-24 dani
54 // - preliminary minimal support for Beamer class
55 
56 // 2007-03-25 dani
57 // - merge labels and sections in document structure view as user configurable option
58 
59 // 2007-04-06 dani
60 // - add TODO/FIXME section to structure view
61 
62 #include "documentinfo.h"
63 
64 #include <config.h>
65 
66 #include <QDateTime>
67 #include <QFileInfo>
68 #include <QInputDialog>
69 #include <QRegExp>
70 
71 #include <KConfig>
72 #include <KIconLoader>
73 #include <KJobWidgets>
74 #include <KIO/StatJob>
75 #include <KLocalizedString>
76 #include <KMessageBox>
77 
78 #include "abbreviationmanager.h"
79 #include "codecompletion.h"
80 #include "configurationmanager.h"
81 #include "editorextension.h"
82 #include "eventfilter.h"
83 #include "kileconfig.h"
84 #include "kiledebug.h"
85 #include "kileviewmanager.h"
86 #include "parser/bibtexparser.h"
87 #include "parser/latexparser.h"
88 #include "parser/parsermanager.h"
89 #include "livepreview.h"
90 #include "utilities.h"
91 
92 namespace KileDocument
93 {
94 
containsInvalidCharacters(const QUrl & url)95 bool Info::containsInvalidCharacters(const QUrl &url)
96 {
97     QString filename = url.fileName();
98     return filename.contains(" ") || filename.contains("~") || filename.contains("$") || filename.contains("#");
99 }
100 
repairInvalidCharacters(const QUrl & url,QWidget * mainWidget,bool checkForFileExistence)101 QUrl Info::repairInvalidCharacters(const QUrl &url, QWidget* mainWidget, bool checkForFileExistence /* = true */)
102 {
103     QUrl ret(url);
104     do {
105         bool isOK;
106         QString newURL = QInputDialog::getText(
107                              mainWidget,
108                              i18n("Invalid Characters"),
109                              i18n("The filename contains invalid characters ($~ #).<br>Please provide "
110                                   "another one, or click \"Cancel\" to save anyway."),
111                              QLineEdit::Normal,
112                              ret.fileName(),
113                              &isOK);
114         if(!isOK) {
115             break;
116         }
117         ret = ret.adjusted(QUrl::RemoveFilename);
118         ret.setPath(ret.path() + newURL);
119     } while(containsInvalidCharacters(ret));
120 
121     return (checkForFileExistence ? renameIfExist(ret, mainWidget) : ret);
122 }
123 
renameIfExist(const QUrl & url,QWidget * mainWidget)124 QUrl Info::renameIfExist(const QUrl &url, QWidget* mainWidget)
125 {
126     QUrl ret(url);
127 
128     auto statJob = KIO::stat(url, KIO::StatJob::SourceSide, 0);
129     KJobWidgets::setWindow(statJob, mainWidget);
130     while (statJob->exec()) { // check for writing possibility
131         bool isOK;
132         QString newURL = QInputDialog::getText(
133                              mainWidget,
134                              i18n("File Already Exists"),
135                              i18n("A file with filename '%1' already exists.<br>Please provide "
136                                   "another one, or click \"Cancel\" to overwrite it.", ret.fileName()),
137                              QLineEdit::Normal,
138                              ret.fileName(),
139                              &isOK);
140         if (!isOK) {
141             break;
142         }
143         ret = ret.adjusted(QUrl::RemoveFilename);
144         ret.setPath(ret.path() + newURL);
145     }
146     return ret;
147 }
148 
repairExtension(const QUrl & url,QWidget * mainWidget,bool checkForFileExistence)149 QUrl Info::repairExtension(const QUrl &url, QWidget *mainWidget, bool checkForFileExistence /* = true */)
150 {
151     QUrl ret(url);
152 
153     QString filename = url.fileName();
154     if(filename.contains(".") && filename[0] != '.') // There already is an extension
155         return ret;
156 
157     if(KMessageBox::Yes == KMessageBox::questionYesNo(Q_NULLPTR,
158             i18n("The given filename has no extension; do you want one to be automatically added?"),
159             i18n("Missing Extension"),
160             KStandardGuiItem::yes(),
161             KStandardGuiItem::no(),
162             "AutomaticallyAddExtension"))
163     {
164         ret = ret.adjusted(QUrl::RemoveFilename);
165         ret.setPath(ret.path() + filename + ".tex");
166     }
167     return (checkForFileExistence ? renameIfExist(ret, mainWidget) : ret);
168 }
169 
makeValidTeXURL(const QUrl & url,QWidget * mainWidget,bool istexfile,bool checkForFileExistence)170 QUrl Info::makeValidTeXURL(const QUrl &url, QWidget *mainWidget, bool istexfile, bool checkForFileExistence)
171 {
172     QUrl newURL(url);
173 
174     //add a .tex extension
175     if(!istexfile) {
176         newURL = repairExtension(newURL, mainWidget, checkForFileExistence);
177     }
178 
179     //remove characters TeX does not accept, make sure the newURL does not exists yet
180     if(containsInvalidCharacters(newURL)) {
181         newURL = repairInvalidCharacters(newURL, mainWidget, checkForFileExistence);
182     }
183 
184     return newURL;
185 }
186 
Info()187 Info::Info() :
188     m_bIsRoot(false),
189     m_dirty(false),
190     m_config(KSharedConfig::openConfig().data()),
191     documentTypePromotionAllowed(true)
192 {
193     updateStructLevelInfo();
194 }
195 
~Info()196 Info::~Info()
197 {
198     KILE_DEBUG_MAIN << "DELETING DOCINFO" << this;
199 }
200 
updateStructLevelInfo()201 void Info::updateStructLevelInfo()
202 {
203     KILE_DEBUG_MAIN << "===void Info::updateStructLevelInfo()===";
204     // read config for structureview items
205     m_showStructureLabels = KileConfig::svShowLabels();
206     m_showStructureReferences = KileConfig::svShowReferences();
207     m_showStructureBibitems = KileConfig::svShowBibitems();
208     m_showStructureGraphics = KileConfig::svShowGraphics();
209     m_showStructureFloats = KileConfig::svShowFloats();
210     m_showStructureInputFiles = KileConfig::svShowInputFiles();
211     m_showStructureTodo = KileConfig::svShowTodo();
212     m_showSectioningLabels = KileConfig::svShowSectioningLabels();
213     m_openStructureLabels = KileConfig::svOpenLabels();
214     m_openStructureReferences = KileConfig::svOpenReferences();
215     m_openStructureBibitems = KileConfig::svOpenBibitems();
216     m_openStructureTodo = KileConfig::svOpenTodo();
217 }
218 
setBaseDirectory(const QUrl & url)219 void Info::setBaseDirectory(const QUrl &url)
220 {
221     KILE_DEBUG_MAIN << "===void Info::setBaseDirectory(const QUrl&" << url << ")===";
222     m_baseDirectory = url;
223 }
224 
getBaseDirectory() const225 const QUrl &Info::getBaseDirectory() const
226 {
227     return m_baseDirectory;
228 }
229 
isTextDocument()230 bool Info::isTextDocument()
231 {
232     return false;
233 }
234 
getType()235 Type Info::getType()
236 {
237     return Undefined;
238 }
239 
getFileFilter() const240 QLinkedList<Extensions::ExtensionType> Info::getFileFilter() const
241 {
242     return {};
243 }
244 
isDocumentTypePromotionAllowed()245 bool Info::isDocumentTypePromotionAllowed()
246 {
247     return documentTypePromotionAllowed;
248 }
249 
setDocumentTypePromotionAllowed(bool b)250 void Info::setDocumentTypePromotionAllowed(bool b)
251 {
252     documentTypePromotionAllowed = b;
253 }
254 
isDirty() const255 bool Info::isDirty() const
256 {
257     return m_dirty;
258 }
259 
setDirty(bool b)260 void Info::setDirty(bool b)
261 {
262     m_dirty = b;
263 }
264 
installParserOutput(KileParser::ParserOutput * parserOutput)265 void Info::installParserOutput(KileParser::ParserOutput *parserOutput)
266 {
267     Q_UNUSED(parserOutput);
268 }
269 
url()270 QUrl Info::url()
271 {
272     return QUrl();
273 }
274 
count(const QString & line,long * stat)275 void Info::count(const QString& line, long *stat)
276 {
277     QChar c;
278     int state = stStandard;
279     bool word = false; // we are in a word
280 
281     int lineLength = line.length();
282     for(int p = 0; p < lineLength; ++p) {
283         c = line[p];
284 
285         switch(state) {
286         case stStandard:
287             if(c == TEX_CAT0) {
288                 state = stControlSequence;
289                 ++stat[1];
290 
291                 //look ahead to avoid counting words like K\"ahler as two words
292                 if( (p+1) < lineLength && ( !line[p+1].isPunct() || line[p+1] == '~' || line[p+1] == '^' )) {
293                     word = false;
294                 }
295             }
296             else if(c == TEX_CAT14) {
297                 state = stComment;
298             }
299             else {
300                 if (c.isLetterOrNumber()) {
301                     //only start new word if first character is a letter (42test is still counted as a word, but 42.2 not)
302                     if (c.isLetter() && !word) {
303                         word = true;
304                         ++stat[3];
305                     }
306                     ++stat[0];
307                 }
308                 else {
309                     ++stat[2];
310                     word = false;
311                 }
312             }
313             break;
314 
315         case stControlSequence :
316             if(c.isLetter()) {
317                 // "\begin{[a-zA-z]+}" is an environment, and you can't define a command like \begin
318                 if(line.mid(p, 5) == "begin") {
319                     ++stat[5];
320                     state = stEnvironment;
321                     stat[1] +=5;
322                     p+=4; // after break p++ is executed
323                 }
324                 else if(line.mid(p, 3) == "end") {
325                     stat[1] +=3;
326                     state = stEnvironment;
327                     p+=2;
328                 } // we don't count \end as new environment, this can give wrong results in selections
329                 else {
330                     ++stat[4];
331                     ++stat[1];
332                     state = stCommand;
333                 }
334             }
335             else {
336                 ++stat[4];
337                 ++stat[1];
338                 state = stStandard;
339             }
340             break;
341 
342         case stCommand :
343             if(c.isLetter()) {
344                 ++stat[1];
345             }
346             else if(c == TEX_CAT0) {
347                 ++stat[1];
348                 state = stControlSequence;
349             }
350             else if(c == TEX_CAT14) {
351                 state = stComment;
352             }
353             else {
354                 ++stat[2];
355                 state = stStandard;
356             }
357             break;
358 
359         case stEnvironment :
360             if(c == TEX_CAT2) { // until we find a closing } we have an environment
361                 ++stat[1];
362                 state = stStandard;
363             }
364             else if(c == TEX_CAT14) {
365                 state = stComment;
366             }
367             else {
368                 ++stat[1];
369             }
370             break;
371 
372         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()
373             if(c == '\n') {
374                 ++stat[2]; // \n was counted as punctuation in the old implementation
375                 state = stStandard;
376                 word = false;
377             }
378             break;
379 
380         default :
381             qWarning() << "Unhandled state in getStatistics " << state;
382             break;
383         }
384     }
385 }
386 
updateStruct()387 void Info::updateStruct()
388 {
389 }
390 
updateBibItems()391 void Info::updateBibItems()
392 {
393 }
394 
slotCompleted()395 void Info::slotCompleted()
396 {
397     setDirty(true);
398     emit completed(this);
399 }
400 
TextInfo(Extensions * extensions,KileAbbreviation::Manager * abbreviationManager,KileParser::Manager * parserManager,const QString & defaultMode)401 TextInfo::TextInfo(Extensions* extensions,
402                    KileAbbreviation::Manager* abbreviationManager,
403                    KileParser::Manager* parserManager,
404                    const QString& defaultMode)
405     : m_doc(Q_NULLPTR),
406       m_defaultMode(defaultMode),
407       m_abbreviationManager(abbreviationManager),
408       m_parserManager(parserManager)
409 {
410     m_arStatistics = new long[SIZE_STAT_ARRAY];
411 
412     m_extensions = extensions;
413     m_abbreviationCodeCompletionModel = new KileCodeCompletion::AbbreviationCompletionModel(this, m_abbreviationManager);
414 }
415 
~TextInfo()416 TextInfo::~TextInfo()
417 {
418     emit(aboutToBeDestroyed(this));
419     detach();
420     delete [] m_arStatistics;
421 }
422 
423 
getDoc() const424 const KTextEditor::Document* TextInfo::getDoc() const
425 {
426     return m_doc;
427 }
428 
getDoc()429 KTextEditor::Document* TextInfo::getDoc()
430 {
431     return m_doc;
432 }
433 
getDocument() const434 const KTextEditor::Document* TextInfo::getDocument() const
435 {
436     return m_doc;
437 }
438 
getDocument()439 KTextEditor::Document* TextInfo::getDocument()
440 {
441     return m_doc;
442 }
443 
setDoc(KTextEditor::Document * doc)444 void TextInfo::setDoc(KTextEditor::Document *doc)
445 {
446     setDocument(doc);
447 }
448 
setDocument(KTextEditor::Document * doc)449 void TextInfo::setDocument(KTextEditor::Document *doc)
450 {
451     KILE_DEBUG_MAIN << "===void TextInfo::setDoc(KTextEditor::Document *doc)===";
452 
453     if(m_doc == doc) {
454         return;
455     }
456 
457     detach();
458     if(doc) {
459         m_doc = doc;
460         m_documentContents.clear();
461         connect(m_doc, SIGNAL(documentNameChanged(KTextEditor::Document*)), this, SLOT(slotFileNameChanged()));
462         connect(m_doc, SIGNAL(documentUrlChanged(KTextEditor::Document*)), this, SLOT(slotFileNameChanged()));
463         connect(m_doc, SIGNAL(completed()), this, SLOT(slotCompleted()));
464         connect(m_doc, SIGNAL(modifiedChanged(KTextEditor::Document*)), this, SLOT(makeDirtyIfModified()));
465         // this could be a KatePart bug, and as "work-around" we manually set the highlighting mode again
466         connect(m_doc, SIGNAL(completed()), this, SLOT(activateDefaultMode()));
467         setMode(m_defaultMode);
468         installEventFilters();
469         registerCodeCompletionModels();
470     }
471 }
472 
detach()473 void TextInfo::detach()
474 {
475     if(m_doc) {
476         m_doc->disconnect(this);
477         removeInstalledEventFilters();
478         removeSignalConnections();
479         unregisterCodeCompletionModels();
480         emit(documentDetached(m_doc));
481     }
482     m_doc = Q_NULLPTR;
483 }
484 
makeDirtyIfModified()485 void TextInfo::makeDirtyIfModified()
486 {
487     if(m_doc && m_doc->isModified()) {
488         setDirty(true);
489     }
490 }
491 
getStatistics(KTextEditor::View * view)492 const long* TextInfo::getStatistics(KTextEditor::View *view)
493 {
494     /* [0] = #c in words, [1] = #c in latex commands and environments,
495        [2] = #c whitespace, [3] = #words, [4] = # latex_commands, [5] = latex_environments */
496     m_arStatistics[0] = m_arStatistics[1] = m_arStatistics[2] = m_arStatistics[3] = m_arStatistics[4] = m_arStatistics[5] = 0;
497 
498     QString line;
499 
500     if(view && view->selection()) {
501         line = view->selectionText();
502         KILE_DEBUG_MAIN << "line: " << line;
503         count(line, m_arStatistics);
504     }
505     else if(m_doc) {
506         for(int l = 0; l < m_doc->lines(); ++l) {
507             line = m_doc->line(l);
508             KILE_DEBUG_MAIN << "line : " << line;
509             count(line, m_arStatistics);
510         }
511     }
512 
513     return m_arStatistics;
514 }
515 
url()516 QUrl TextInfo::url()
517 {
518     if(m_doc) {
519         return m_doc->url();
520     }
521     else {
522         return QUrl();
523     }
524 }
525 
getType()526 Type TextInfo::getType()
527 {
528     return Text;
529 }
530 
isTextDocument()531 bool TextInfo::isTextDocument()
532 {
533     return true;
534 }
535 
setMode(const QString & mode)536 void TextInfo::setMode(const QString &mode)
537 {
538     KILE_DEBUG_MAIN << "==Kile::setMode(" << m_doc->url() << "," << mode << " )==================";
539 
540     if (m_doc && !mode.isEmpty()) {
541         m_doc->setMode(mode);
542     }
543 }
544 
setHighlightingMode(const QString & highlight)545 void TextInfo::setHighlightingMode(const QString& highlight)
546 {
547     KILE_DEBUG_MAIN << "==Kile::setHighlightingMode(" << m_doc->url() << "," << highlight << " )==================";
548 
549     if (m_doc && !highlight.isEmpty()) {
550         m_doc->setHighlightingMode(highlight);
551     }
552 }
553 
setDefaultMode(const QString & string)554 void TextInfo::setDefaultMode(const QString& string)
555 {
556     m_defaultMode = string;
557 }
558 
559 // match a { with the corresponding }
560 // pos is the position of the {
matchBracket(QChar obracket,int & l,int & pos)561 QString TextInfo::matchBracket(QChar obracket, int &l, int &pos)
562 {
563     QChar cbracket;
564     if(obracket == '{') {
565         cbracket = '}';
566     }
567     if(obracket == '[') {
568         cbracket = ']';
569     }
570     if(obracket == '(') {
571         cbracket = ')';
572     }
573 
574     QString line, grab = "";
575     int count=0, len;
576     ++pos;
577 
578     TodoResult todo;
579     while(l <= m_doc->lines()) {
580         line = getTextline(l,todo);
581         len = line.length();
582         for (int i=pos; i < len; ++i) {
583             if(line[i] == '\\' && (line[i+1] == obracket || line[i+1] == cbracket)) {
584                 ++i;
585             }
586             else if(line[i] == obracket) {
587                 ++count;
588             }
589             else if(line[i] == cbracket) {
590                 --count;
591                 if (count < 0) {
592                     pos = i;
593                     return grab;
594                 }
595             }
596 
597             grab += line[i];
598         }
599         ++l;
600         pos = 0;
601     }
602 
603     return QString();
604 }
605 
getTextline(uint line,TodoResult & todo)606 QString TextInfo::getTextline(uint line, TodoResult &todo)
607 {
608     static QRegExp reComments("[^\\\\](%.*$)");
609 
610     todo.type = -1;
611     QString s = m_doc->line(line);
612     if(!s.isEmpty()) {
613         // remove comment lines
614         if(s[0] == '%') {
615             searchTodoComment(s,0,todo);
616             s.clear();
617         }
618         else {
619             //remove escaped \ characters
620             s.replace("\\\\", "  ");
621 
622             //remove comments
623             int pos = s.indexOf(reComments);
624             if(pos != -1) {
625                 searchTodoComment(s, pos,todo);
626                 s = s.left(reComments.pos(1));
627             }
628         }
629     }
630     return s;
631 }
632 
searchTodoComment(const QString & s,uint startpos,TodoResult & todo)633 void TextInfo::searchTodoComment(const QString &s, uint startpos, TodoResult &todo)
634 {
635     static QRegExp reTodoComment("\\b(TODO|FIXME)\\b(:|\\s)?\\s*(.*)");
636 
637     if(s.indexOf(reTodoComment, startpos) != -1) {
638         todo.type = (reTodoComment.cap(1) == "TODO") ? KileStruct::ToDo : KileStruct::FixMe;
639         todo.colTag = reTodoComment.pos(1);
640         todo.colComment = reTodoComment.pos(3);
641         todo.comment = reTodoComment.cap(3).trimmed();
642     }
643 }
644 
createView(QWidget * parent,const char *)645 KTextEditor::View* TextInfo::createView(QWidget *parent, const char* /* name */)
646 {
647     if(!m_doc) {
648         return Q_NULLPTR;
649     }
650     KTextEditor::View *view = m_doc->createView(parent);
651     installEventFilters(view);
652     installSignalConnections(view);
653     registerCodeCompletionModels(view);
654     view->setStatusBarEnabled(false);
655     connect(view, SIGNAL(destroyed(QObject*)), this, SLOT(slotViewDestroyed(QObject*)));
656     return view;
657 }
658 
startAbbreviationCompletion(KTextEditor::View * view)659 void TextInfo::startAbbreviationCompletion(KTextEditor::View *view)
660 {
661     KTextEditor::CodeCompletionInterface* completionInterface = qobject_cast<KTextEditor::CodeCompletionInterface*>(view);
662     if(!completionInterface) {
663         return;
664     }
665     KTextEditor::Range range = m_abbreviationCodeCompletionModel->completionRange(view, view->cursorPosition());
666     if(!range.isValid()) {
667         range = KTextEditor::Range(view->cursorPosition(), view->cursorPosition());
668     }
669     completionInterface->startCompletion(range, m_abbreviationCodeCompletionModel);
670 }
671 
slotFileNameChanged()672 void TextInfo::slotFileNameChanged()
673 {
674     emit urlChanged(this, url());
675 }
676 
installEventFilters(KTextEditor::View * view)677 void TextInfo::installEventFilters(KTextEditor::View *view)
678 {
679     if(m_eventFilterHash.find(view) != m_eventFilterHash.end()) {
680         return;
681     }
682 
683     QList<QObject*> eventFilterList = createEventFilters(view);
684     if(!eventFilterList.isEmpty()) {
685         for(QList<QObject*>::iterator i = eventFilterList.begin(); i != eventFilterList.end(); ++i) {
686             QObject* eventFilter = *i;
687             KileView::Manager::installEventFilter(view, eventFilter);
688         }
689         m_eventFilterHash[view] = eventFilterList;
690     }
691 }
692 
removeInstalledEventFilters(KTextEditor::View * view)693 void TextInfo::removeInstalledEventFilters(KTextEditor::View *view)
694 {
695     QHash<KTextEditor::View*, QList<QObject*> >::iterator i = m_eventFilterHash.find(view);
696     if(i != m_eventFilterHash.end()) {
697         QList<QObject*> eventFilterList = *i;
698         for(QList<QObject*>::iterator i2 = eventFilterList.begin(); i2 != eventFilterList.end(); ++i2) {
699             QObject *eventFilter = *i2;
700             KileView::Manager::removeEventFilter(view, eventFilter);
701             delete(*i2);
702         }
703         m_eventFilterHash.erase(i);
704     }
705 }
706 
createEventFilters(KTextEditor::View *)707 QList<QObject*> TextInfo::createEventFilters(KTextEditor::View* /* view */)
708 {
709     return QList<QObject*>();
710 }
711 
installEventFilters()712 void TextInfo::installEventFilters()
713 {
714     if(!m_doc) {
715         return;
716     }
717     QList<KTextEditor::View*> views = m_doc->views();
718     for(QList<KTextEditor::View*>::iterator i = views.begin(); i != views.end(); ++i) {
719         installEventFilters(*i);
720     }
721 }
722 
removeInstalledEventFilters()723 void TextInfo::removeInstalledEventFilters()
724 {
725     if(!m_doc) {
726         return;
727     }
728     QList<KTextEditor::View*> views = m_doc->views();
729     for(QList<KTextEditor::View*>::iterator i = views.begin(); i != views.end(); ++i) {
730         removeInstalledEventFilters(*i);
731     }
732 }
733 
installSignalConnections(KTextEditor::View *)734 void TextInfo::installSignalConnections(KTextEditor::View *)
735 {
736     /* does nothing */
737 }
738 
removeSignalConnections(KTextEditor::View *)739 void TextInfo::removeSignalConnections(KTextEditor::View *)
740 {
741     /* does nothing */
742 }
743 
installSignalConnections()744 void TextInfo::installSignalConnections()
745 {
746     if(!m_doc) {
747         return;
748     }
749     QList<KTextEditor::View*> views = m_doc->views();
750     for(QList<KTextEditor::View*>::iterator i = views.begin(); i != views.end(); ++i) {
751         installSignalConnections(*i);
752     }
753 }
754 
removeSignalConnections()755 void TextInfo::removeSignalConnections()
756 {
757     if(!m_doc) {
758         return;
759     }
760     QList<KTextEditor::View*> views = m_doc->views();
761     for(QList<KTextEditor::View*>::iterator i = views.begin(); i != views.end(); ++i) {
762         removeSignalConnections(*i);
763     }
764 }
765 
registerCodeCompletionModels(KTextEditor::View * view)766 void TextInfo::registerCodeCompletionModels(KTextEditor::View *view)
767 {
768     KTextEditor::CodeCompletionInterface* completionInterface = qobject_cast<KTextEditor::CodeCompletionInterface*>(view);
769     if(!completionInterface) {
770         return;
771     }
772     completionInterface->registerCompletionModel(m_abbreviationCodeCompletionModel);
773     completionInterface->setAutomaticInvocationEnabled(true);
774 }
775 
unregisterCodeCompletionModels(KTextEditor::View * view)776 void TextInfo::unregisterCodeCompletionModels(KTextEditor::View *view)
777 {
778     KTextEditor::CodeCompletionInterface* completionInterface = qobject_cast<KTextEditor::CodeCompletionInterface*>(view);
779     if(!completionInterface) {
780         return;
781     }
782     completionInterface->unregisterCompletionModel(m_abbreviationCodeCompletionModel);
783 }
784 
registerCodeCompletionModels()785 void TextInfo::registerCodeCompletionModels()
786 {
787     if(!m_doc) {
788         return;
789     }
790     QList<KTextEditor::View*> views = m_doc->views();
791     for(QList<KTextEditor::View*>::iterator i = views.begin(); i != views.end(); ++i) {
792         registerCodeCompletionModels(*i);
793     }
794 }
795 
unregisterCodeCompletionModels()796 void TextInfo::unregisterCodeCompletionModels()
797 {
798     if(!m_doc) {
799         return;
800     }
801     QList<KTextEditor::View*> views = m_doc->views();
802     for(QList<KTextEditor::View*>::iterator i = views.begin(); i != views.end(); ++i) {
803         unregisterCodeCompletionModels(*i);
804     }
805 }
806 
slotViewDestroyed(QObject * object)807 void TextInfo::slotViewDestroyed(QObject *object)
808 {
809     KTextEditor::View* view = dynamic_cast<KTextEditor::View*>(object);
810     if(view) {
811         removeInstalledEventFilters(view);
812         removeSignalConnections(view);
813         unregisterCodeCompletionModels(view);
814         QHash<KTextEditor::View*, QList<QObject*> >::iterator i = m_eventFilterHash.find(view);
815         if(i != m_eventFilterHash.end()) {
816             m_eventFilterHash.erase(i);
817         }
818     }
819 }
820 
activateDefaultMode()821 void TextInfo::activateDefaultMode()
822 {
823     KILE_DEBUG_MAIN << "m_defaultMode = " <<  m_defaultMode << endl;
824 
825     if(m_doc && !m_defaultMode.isEmpty()) {
826         m_doc->setMode(m_defaultMode);
827     }
828 }
829 
documentContents() const830 const QStringList TextInfo::documentContents() const
831 {
832     if (m_doc) {
833         return m_doc->textLines(m_doc->documentRange());
834     }
835     else {
836         return m_documentContents;
837     }
838 }
839 
setDocumentContents(const QStringList & contents)840 void TextInfo::setDocumentContents(const QStringList& contents)
841 {
842     m_documentContents = contents;
843 }
844 
LaTeXInfo(Extensions * extensions,KileAbbreviation::Manager * abbreviationManager,LatexCommands * commands,EditorExtension * editorExtension,KileConfiguration::Manager * manager,KileCodeCompletion::Manager * codeCompletionManager,KileTool::LivePreviewManager * livePreviewManager,KileView::Manager * viewManager,KileParser::Manager * parserManager)845 LaTeXInfo::LaTeXInfo(Extensions* extensions,
846                      KileAbbreviation::Manager* abbreviationManager,
847                      LatexCommands* commands,
848                      EditorExtension* editorExtension,
849                      KileConfiguration::Manager* manager,
850                      KileCodeCompletion::Manager* codeCompletionManager,
851                      KileTool::LivePreviewManager* livePreviewManager,
852                      KileView::Manager *viewManager,
853                      KileParser::Manager* parserManager)
854     : TextInfo(extensions, abbreviationManager, parserManager, "LaTeX"),
855       m_commands(commands),
856       m_editorExtension(editorExtension),
857       m_configurationManager(manager),
858       m_eventFilter(Q_NULLPTR),
859       m_livePreviewManager(livePreviewManager),
860       m_viewManager(viewManager)
861 {
862     documentTypePromotionAllowed = false;
863     updateStructLevelInfo();
864     m_latexCompletionModel = new KileCodeCompletion::LaTeXCompletionModel(this,
865             codeCompletionManager,
866             editorExtension);
867 }
868 
~LaTeXInfo()869 LaTeXInfo::~LaTeXInfo()
870 {
871 }
872 
getType()873 Type LaTeXInfo::getType()
874 {
875     return LaTeX;
876 }
877 
getFileFilter() const878 QLinkedList<Extensions::ExtensionType> LaTeXInfo::getFileFilter() const
879 {
880     return {Extensions::TEX, Extensions::PACKAGES};
881 }
882 
startLaTeXCompletion(KTextEditor::View * view)883 void LaTeXInfo::startLaTeXCompletion(KTextEditor::View *view)
884 {
885     KTextEditor::CodeCompletionInterface* completionInterface = qobject_cast<KTextEditor::CodeCompletionInterface*>(view);
886     if(!completionInterface) {
887         return;
888     }
889     KTextEditor::Range range = m_latexCompletionModel->completionRange(view, view->cursorPosition());
890     if(!range.isValid()) {
891         range = KTextEditor::Range(view->cursorPosition(), view->cursorPosition());
892     }
893     completionInterface->startCompletion(range, m_latexCompletionModel);
894 }
895 
updateStructLevelInfo()896 void LaTeXInfo::updateStructLevelInfo() {
897 
898     KILE_DEBUG_MAIN << "===void LaTeXInfo::updateStructLevelInfo()===";
899 
900     // read config stuff
901     Info::updateStructLevelInfo();
902 
903     // clear all entries
904     m_dictStructLevel.clear();
905 
906     //TODO: make sectioning and bibliography configurable
907 
908     // sectioning
909     m_dictStructLevel["\\part"] = KileStructData(1, KileStruct::Sect, "part");
910     m_dictStructLevel["\\chapter"] = KileStructData(2, KileStruct::Sect, "chapter");
911     m_dictStructLevel["\\section"] = KileStructData(3, KileStruct::Sect, "section");
912     m_dictStructLevel["\\subsection"] = KileStructData(4, KileStruct::Sect, "subsection");
913     m_dictStructLevel["\\subsubsection"] = KileStructData(5, KileStruct::Sect, "subsubsection");
914     m_dictStructLevel["\\paragraph"] = KileStructData(6, KileStruct::Sect, "subsubsection");
915     m_dictStructLevel["\\subparagraph"] = KileStructData(7, KileStruct::Sect, "subsubsection");
916 
917     // hidden commands
918     m_dictStructLevel["\\usepackage"] = KileStructData(KileStruct::Hidden, KileStruct::Package);
919     m_dictStructLevel["\\newcommand"] = KileStructData(KileStruct::Hidden, KileStruct::NewCommand);
920     m_dictStructLevel["\\newlength"] = KileStructData(KileStruct::Hidden, KileStruct::NewCommand);
921     m_dictStructLevel["\\newenvironment"] = KileStructData(KileStruct::Hidden, KileStruct::NewEnvironment);
922     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)
923     m_dictStructLevel["\\DeclareMathOperator"] = KileStructData(KileStruct::Hidden, KileStruct::NewCommand); // amsmath package
924     m_dictStructLevel["\\caption"] = KileStructData(KileStruct::Hidden,KileStruct::Caption);
925 
926     // bibitems
927     if(m_showStructureBibitems) {
928         m_dictStructLevel["\\bibitem"] = KileStructData(KileStruct::NotSpecified, KileStruct::BibItem, QString(), "bibs");
929     }
930 
931     // graphics
932     if(m_showStructureGraphics) {
933         m_dictStructLevel["\\includegraphics"] = KileStructData(KileStruct::Object,KileStruct::Graphics, "graphics");
934     }
935 
936     // float environments
937     if(m_showStructureFloats) {
938         m_dictStructLevel["\\begin"] = KileStructData(KileStruct::Object,KileStruct::BeginEnv);
939         m_dictStructLevel["\\end"] = KileStructData(KileStruct::Hidden,KileStruct::EndEnv);
940 
941         // some entries, which could never be found (but they are set manually)
942         m_dictStructLevel["\\begin{figure}"]=KileStructData(KileStruct::Object,KileStruct::BeginFloat, "figure-env");
943         m_dictStructLevel["\\begin{figure*}"]=KileStructData(KileStruct::Object,KileStruct::BeginFloat, "figure-env");
944         m_dictStructLevel["\\begin{table}"]=KileStructData(KileStruct::Object,KileStruct::BeginFloat, "table-env");
945         m_dictStructLevel["\\begin{table*}"]=KileStructData(KileStruct::Object,KileStruct::BeginFloat, "table-env");
946         m_dictStructLevel["\\begin{asy}"]=KileStructData(KileStruct::Object,KileStruct::BeginFloat, "image-x-generic");
947         m_dictStructLevel["\\end{float}"]=KileStructData(KileStruct::Hidden,KileStruct::EndFloat);
948     }
949 
950     // preliminary minimal beamer support
951     m_dictStructLevel["\\frame"] = KileStructData(KileStruct::Object, KileStruct::BeamerFrame, "beamerframe");
952     m_dictStructLevel["\\frametitle"] = KileStructData(KileStruct::Hidden, KileStruct::BeamerFrametitle);
953     m_dictStructLevel["\\begin{frame}"] = KileStructData(KileStruct::Object, KileStruct::BeamerBeginFrame, "beamerframe");
954     m_dictStructLevel["\\end{frame}"] = KileStructData(KileStruct::Hidden, KileStruct::BeamerEndFrame);
955     m_dictStructLevel["\\begin{block}"] = KileStructData(KileStruct::Object, KileStruct::BeamerBeginBlock, "beamerblock");
956 
957     // add user-defined commands
958 
959     QStringList list;
960     QStringList::ConstIterator it;
961 
962     // labels, we also gather them
963     m_commands->commandList(list,KileDocument::CmdAttrLabel, false);
964     for(it=list.constBegin(); it != list.constEnd(); ++it) {
965         m_dictStructLevel[*it] = KileStructData(KileStruct::NotSpecified, KileStruct::Label, QString(), "labels");
966     }
967 
968     // input files
969     if(m_showStructureInputFiles) {
970         m_commands->commandList(list, KileDocument::CmdAttrIncludes, false);
971         for(it = list.constBegin(); it != list.constEnd(); ++it) {
972             m_dictStructLevel[*it] = KileStructData(KileStruct::File, KileStruct::Input, "input-file");
973         }
974     }
975 
976     // references
977     if(m_showStructureReferences) {
978         m_commands->commandList(list, KileDocument::CmdAttrReference, false);
979         for(it=list.constBegin(); it != list.constEnd(); ++it ) {
980             m_dictStructLevel[*it] = KileStructData(KileStruct::Hidden, KileStruct::Reference);
981         }
982     }
983 
984     //bibliography commands
985     m_commands->commandList(list,KileDocument::CmdAttrBibliographies, false);
986     for(it=list.constBegin(); it != list.constEnd(); ++it) {
987         m_dictStructLevel[*it] = KileStructData(0, KileStruct::Bibliography, "viewbib");
988     }
989 }
990 
createEventFilters(KTextEditor::View * view)991 QList<QObject*> LaTeXInfo::createEventFilters(KTextEditor::View *view)
992 {
993     QList<QObject*> toReturn;
994     QObject *eventFilter = new LaTeXEventFilter(view, m_editorExtension);
995     connect(m_configurationManager, SIGNAL(configChanged()), eventFilter, SLOT(readConfig()));
996     toReturn << eventFilter;
997     return toReturn;
998 }
999 
installSignalConnections(KTextEditor::View * view)1000 void LaTeXInfo::installSignalConnections(KTextEditor::View *view)
1001 {
1002     connect(view, &KTextEditor::View::cursorPositionChanged,
1003             m_viewManager, &KileView::Manager::handleCursorPositionChanged);
1004     connect(view->document(), &KTextEditor::Document::textChanged,
1005             m_livePreviewManager, &KileTool::LivePreviewManager::handleTextChanged, Qt::UniqueConnection);
1006     connect(view->document(), &KTextEditor::Document::documentSavedOrUploaded,
1007             m_livePreviewManager, &KileTool::LivePreviewManager::handleDocumentSavedOrUploaded, Qt::UniqueConnection);
1008 }
1009 
removeSignalConnections(KTextEditor::View * view)1010 void LaTeXInfo::removeSignalConnections(KTextEditor::View *view)
1011 {
1012     disconnect(view, &KTextEditor::View::cursorPositionChanged,
1013                m_viewManager, &KileView::Manager::handleCursorPositionChanged);
1014     disconnect(view->document(), &KTextEditor::Document::textChanged,
1015                m_livePreviewManager, &KileTool::LivePreviewManager::handleTextChanged);
1016     disconnect(view->document(), &KTextEditor::Document::documentSavedOrUploaded,
1017                m_livePreviewManager, &KileTool::LivePreviewManager::handleDocumentSavedOrUploaded);
1018 }
1019 
registerCodeCompletionModels(KTextEditor::View * view)1020 void LaTeXInfo::registerCodeCompletionModels(KTextEditor::View *view)
1021 {
1022     KTextEditor::CodeCompletionInterface* completionInterface = qobject_cast<KTextEditor::CodeCompletionInterface*>(view);
1023     if(!completionInterface) {
1024         return;
1025     }
1026     completionInterface->registerCompletionModel(m_latexCompletionModel);
1027     completionInterface->setAutomaticInvocationEnabled(true);
1028     TextInfo::registerCodeCompletionModels(view);
1029 }
1030 
unregisterCodeCompletionModels(KTextEditor::View * view)1031 void LaTeXInfo::unregisterCodeCompletionModels(KTextEditor::View *view)
1032 {
1033     KTextEditor::CodeCompletionInterface* completionInterface = qobject_cast<KTextEditor::CodeCompletionInterface*>(view);
1034     if(!completionInterface) {
1035         return;
1036     }
1037     completionInterface->unregisterCompletionModel(m_latexCompletionModel);
1038     TextInfo::unregisterCodeCompletionModels(view);
1039 }
1040 
matchBracket(int & l,int & pos)1041 BracketResult LaTeXInfo::matchBracket(int &l, int &pos)
1042 {
1043     BracketResult result;
1044     TodoResult todo;
1045 
1046     if(m_doc->line(l)[pos] == '[') {
1047         result.option = TextInfo::matchBracket('[', l, pos);
1048         int p = 0;
1049         while(l < m_doc->lines()) {
1050             if((p = getTextline(l, todo).indexOf('{', pos)) != -1) {
1051                 pos = p;
1052                 break;
1053             }
1054             else {
1055                 pos = 0;
1056                 ++l;
1057             }
1058         }
1059     }
1060 
1061     if(m_doc->line(l)[pos] == '{') {
1062         result.line = l;
1063         result.col = pos;
1064         result.value  = TextInfo::matchBracket('{', l, pos);
1065     }
1066 
1067     return result;
1068 }
1069 
updateStruct()1070 void LaTeXInfo::updateStruct()
1071 {
1072     KILE_DEBUG_MAIN << "==void TeXInfo::updateStruct: (" << url() << ")=========";
1073 
1074     m_parserManager->parseDocument(this);
1075 }
1076 
checkChangedDeps()1077 void LaTeXInfo::checkChangedDeps()
1078 {
1079     if(m_depsPrev != m_deps) {
1080         KILE_DEBUG_MAIN << "===void LaTeXInfo::checkChangedDeps()===, deps have changed"<< endl;
1081         emit(depChanged());
1082         m_depsPrev = m_deps;
1083     }
1084 }
1085 
installParserOutput(KileParser::ParserOutput * parserOutput)1086 void LaTeXInfo::installParserOutput(KileParser::ParserOutput *parserOutput)
1087 {
1088     KILE_DEBUG_MAIN;
1089     KileParser::LaTeXParserOutput *latexParserOutput = dynamic_cast<KileParser::LaTeXParserOutput*>(parserOutput);
1090     Q_ASSERT(latexParserOutput);
1091     if(!latexParserOutput) {
1092         KILE_DEBUG_MAIN << "wrong type given";
1093         return;
1094     }
1095 
1096     m_labels = latexParserOutput->labels;
1097     m_bibItems = latexParserOutput->bibItems;
1098     m_deps = latexParserOutput->deps;
1099     m_bibliography = latexParserOutput->bibliography;
1100     m_packages = latexParserOutput->packages;
1101     m_newCommands = latexParserOutput->newCommands;
1102     m_asyFigures = latexParserOutput->asyFigures;
1103     m_preamble = latexParserOutput->preamble;
1104     m_bIsRoot = latexParserOutput->bIsRoot;
1105 
1106     checkChangedDeps();
1107     emit(isrootChanged(isLaTeXRoot()));
1108     setDirty(false);
1109     emit(parsingComplete());
1110 }
1111 
BibInfo(Extensions * extensions,KileAbbreviation::Manager * abbreviationManager,KileParser::Manager * parserManager,LatexCommands *)1112 BibInfo::BibInfo(Extensions* extensions,
1113                  KileAbbreviation::Manager* abbreviationManager,
1114                  KileParser::Manager* parserManager,
1115                  LatexCommands* /* commands */)
1116     : TextInfo(extensions, abbreviationManager, parserManager, "BibTeX")
1117 {
1118     documentTypePromotionAllowed = false;
1119 }
1120 
~BibInfo()1121 BibInfo::~BibInfo()
1122 {
1123 }
1124 
isLaTeXRoot()1125 bool BibInfo::isLaTeXRoot()
1126 {
1127     return false;
1128 }
1129 
updateStruct()1130 void BibInfo::updateStruct()
1131 {
1132     m_parserManager->parseDocument(this);
1133 }
1134 
installParserOutput(KileParser::ParserOutput * parserOutput)1135 void BibInfo::installParserOutput(KileParser::ParserOutput *parserOutput)
1136 {
1137     KILE_DEBUG_MAIN;
1138     KileParser::BibTeXParserOutput *bibtexParserOutput = dynamic_cast<KileParser::BibTeXParserOutput*>(parserOutput);
1139     Q_ASSERT(bibtexParserOutput);
1140     if(!bibtexParserOutput) {
1141         KILE_DEBUG_MAIN << "wrong type given";
1142         return;
1143     }
1144 
1145     m_bibItems = bibtexParserOutput->bibItems;
1146 
1147     setDirty(false);
1148     emit(parsingComplete());
1149 }
1150 
getType()1151 Type BibInfo::getType()
1152 {
1153     return BibTeX;
1154 }
1155 
getFileFilter() const1156 QLinkedList<Extensions::ExtensionType> BibInfo::getFileFilter() const
1157 {
1158     return {Extensions::BIB};
1159 }
1160 
ScriptInfo(Extensions * extensions,KileAbbreviation::Manager * abbreviationManager,KileParser::Manager * parserManager)1161 ScriptInfo::ScriptInfo(Extensions* extensions,
1162                        KileAbbreviation::Manager* abbreviationManager,
1163                        KileParser::Manager* parserManager)
1164     : TextInfo(extensions, abbreviationManager, parserManager, "JavaScript")
1165 {
1166     documentTypePromotionAllowed = false;
1167 }
1168 
~ScriptInfo()1169 ScriptInfo::~ScriptInfo()
1170 {
1171 }
1172 
isLaTeXRoot()1173 bool ScriptInfo::isLaTeXRoot()
1174 {
1175     return false;
1176 }
1177 
getType()1178 Type ScriptInfo::getType()
1179 {
1180     return Script;
1181 }
1182 
getFileFilter() const1183 QLinkedList<Extensions::ExtensionType> ScriptInfo::getFileFilter() const
1184 {
1185     return {Extensions::JS};
1186 }
1187 
1188 }
1189 
1190