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