1/*
2 * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com)
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17#include "editor.h"
18
19#include <QtCore/QFileInfo>
20#include <QFont>
21#include <QTextCodec>
22#include <QVariant>
23#include <QWheelEvent>
24#include <memory>
25#include "settings.h"
26#include "mainwindow.h"
27#include "systemconsts.h"
28#include <QFileDialog>
29#include <QMessageBox>
30#include <QDebug>
31#include <QMimeData>
32#include "qsynedit/highlighter/cpp.h"
33#include "HighlighterManager.h"
34#include "qsynedit/exporter/synrtfexporter.h"
35#include "qsynedit/exporter/synhtmlexporter.h"
36#include "qsynedit/Constants.h"
37#include <QGuiApplication>
38#include <QClipboard>
39#include <QPainter>
40#include <QToolTip>
41#include <QApplication>
42#include <QInputDialog>
43#include <QPrinter>
44#include <QPrintDialog>
45#include <QTextDocument>
46#include <QTextCodec>
47#include <QScrollBar>
48#include "iconsmanager.h"
49#include "debugger.h"
50#include "editorlist.h"
51#include <QDebug>
52#include "project.h"
53#include <qt_utils/charsetinfo.h>
54
55using namespace std;
56
57SaveException::SaveException(const QString& reason) {
58 mReason = reason;
59 mReasonBuffer = mReason.toLocal8Bit();
60}
61SaveException::SaveException(const QString&& reason) {
62 mReason = reason;
63 mReasonBuffer = mReason.toLocal8Bit();
64}
65const QString& SaveException::reason() const noexcept{
66 return mReason;
67}
68const char* SaveException::what() const noexcept {
69 return mReasonBuffer;
70}
71
72Editor::Editor(QWidget *parent):
73 Editor(parent,QObject::tr("untitled"),ENCODING_AUTO_DETECT,false,true,nullptr)
74{
75}
76
77Editor::Editor(QWidget *parent, const QString& filename,
78 const QByteArray& encoding,
79 bool inProject, bool isNew,
80 QTabWidget* parentPageControl):
81 SynEdit(parent),
82 mEncodingOption(encoding),
83 mFilename(QFileInfo(filename).absoluteFilePath()),
84 mParentPageControl(parentPageControl),
85 mInProject(inProject),
86 mIsNew(isNew),
87 mSyntaxIssues(),
88 mSyntaxErrorColor(Qt::red),
89 mSyntaxWarningColor("orange"),
90 mLineCount(0),
91 mActiveBreakpointLine(-1),
92 mLastIdCharPressed(0),
93 mCurrentWord(),
94 mCurrentTipType(TipType::None),
95 mOldHighlightedWord(),
96 mCurrentHighlightedWord(),
97 mSaving(false),
98 mHoverModifiedLine(-1)
99{
100 mCurrentLineModified = false;
101 mUseCppSyntax = pSettings->editor().defaultFileCpp();
102 if (mFilename.isEmpty()) {
103 mFilename = tr("untitled")+QString("%1").arg(getNewFileNumber());
104 }
105 QFileInfo fileInfo(mFilename);
106 QSynedit::PHighlighter highlighter;
107 if (!isNew) {
108 loadFile();
109 highlighter = highlighterManager.getHighlighter(mFilename);
110 } else {
111 mFileEncoding = ENCODING_ASCII;
112 highlighter=highlighterManager.getCppHighlighter();
113 }
114
115 if (highlighter) {
116 setHighlighter(highlighter);
117 setUseCodeFolding(true);
118 } else {
119 setUseCodeFolding(false);
120 }
121
122 if (inProject) {
123 mParser = pMainWindow->project()->cppParser();
124 } else {
125 initParser();
126 }
127
128 if (pSettings->editor().readOnlySytemHeader()
129 && mParser && (mParser->isSystemHeaderFile(mFilename) || mParser->isProjectHeaderFile(mFilename))) {
130 this->setModified(false);
131 setReadOnly(true);
132 }
133
134 mCompletionPopup = pMainWindow->completionPopup();
135 mHeaderCompletionPopup = pMainWindow->headerCompletionPopup();
136
137 applySettings();
138 applyColorScheme(pSettings->editor().colorScheme());
139
140 //Initialize User Code Template stuff;
141 mXOffsetSince =0;
142 mTabStopY=-1;
143 mTabStopBegin= -1;
144 mTabStopEnd= -1;
145 //mLineBeforeTabStop="";
146 //mLineAfterTabStop = "";
147
148 connect(this,&SynEdit::statusChanged,this,&Editor::onStatusChanged);
149 connect(this,&SynEdit::gutterClicked,this,&Editor::onGutterClicked);
150
151 onStatusChanged(QSynedit::StatusChange::scOpenFile);
152
153 setAttribute(Qt::WA_Hover,true);
154
155 connect(this,&SynEdit::linesDeleted,
156 this, &Editor::onLinesDeleted);
157 connect(this,&SynEdit::linesInserted,
158 this, &Editor::onLinesInserted);
159
160 setContextMenuPolicy(Qt::CustomContextMenu);
161 connect(this, &QWidget::customContextMenuRequested,
162 pMainWindow, &MainWindow::onEditorContextMenu);
163
164 mCanAutoSave = false;
165 if (isNew && parentPageControl!=nullptr) {
166 QString fileTemplate = pMainWindow->codeSnippetManager()->newFileTemplate();
167 if (!fileTemplate.isEmpty()) {
168 insertCodeSnippet(fileTemplate);
169 setCaretPosition(1,1);
170 mCanAutoSave = true;
171 }
172 }
173 if (!isNew && parentPageControl!=nullptr) {
174 resetBookmarks();
175 resetBreakpoints();
176 }
177 mStatementColors = pMainWindow->statementColors();
178 if (mParentPageControl!=nullptr) {
179 mParentPageControl->addTab(this,"");
180 updateCaption();
181 }
182 if (mParentPageControl==nullptr) {
183 setExtraKeystrokes();
184 }
185 connect(&mFunctionTipTimer, &QTimer::timeout,
186 this, &Editor::onFunctionTipsTimer);
187
188 connect(horizontalScrollBar(), &QScrollBar::valueChanged,
189 this, &Editor::onScrollBarValueChanged);
190 connect(verticalScrollBar(), &QScrollBar::valueChanged,
191 this, &Editor::onScrollBarValueChanged);
192}
193
194Editor::~Editor() {
195 if (mParentPageControl) {
196 pMainWindow->fileSystemWatcher()->removePath(mFilename);
197 pMainWindow->caretList().removeEditor(this);
198 pMainWindow->updateCaretActions();
199 int index = mParentPageControl->indexOf(this);
200 mParentPageControl->removeTab(index);
201 this->setParent(nullptr);
202 }
203}
204
205void Editor::loadFile(QString filename) {
206 if (filename.isEmpty()) {
207 this->document()->loadFromFile(mFilename,mEncodingOption,mFileEncoding);
208 } else {
209 filename = QFileInfo(filename).absoluteFilePath();
210 this->document()->loadFromFile(filename,mEncodingOption,mFileEncoding);
211 }
212 //this->setModified(false);
213 updateCaption();
214 pMainWindow->updateForEncodingInfo();
215 switch(getFileType(mFilename)) {
216 case FileType::CppSource:
217 mUseCppSyntax = true;
218 break;
219 case FileType::CSource:
220 mUseCppSyntax = false;
221 break;
222 default:
223 mUseCppSyntax = pSettings->editor().defaultFileCpp();
224 }
225 if (highlighter() && mParser) {
226 reparse();
227 if (pSettings->editor().syntaxCheckWhenLineChanged()) {
228 checkSyntaxInBack();
229 }
230 reparseTodo();
231 }
232 mLastIdCharPressed = 0;
233}
234
235void Editor::saveFile(QString filename) {
236 QFile file(filename);
237 QByteArray encoding = mFileEncoding;
238 if (mEncodingOption!=ENCODING_AUTO_DETECT || mFileEncoding==ENCODING_ASCII)
239 encoding = mEncodingOption;
240 this->document()->saveToFile(file,encoding,
241 pSettings->editor().defaultEncoding(),
242 mFileEncoding);
243 emit fileSaved(filename, mInProject);
244}
245
246void Editor::convertToEncoding(const QByteArray &encoding)
247{
248 mEncodingOption = encoding;
249 setModified(true);
250 save();
251}
252
253bool Editor::save(bool force, bool doReparse) {
254 if (this->mIsNew && !force) {
255 return saveAs();
256 }
257 //is this file writable;
258 pMainWindow->fileSystemWatcher()->removePath(mFilename);
259 try {
260// QFileInfo info(mFilename);
261// if (!force && !info.isWritable()) {
262// QMessageBox::critical(pMainWindow,tr("Error"),
263// tr("File %1 is not writable!").arg(mFilename));
264// return false;
265// }
266 saveFile(mFilename);
267 pMainWindow->fileSystemWatcher()->addPath(mFilename);
268 setModified(false);
269 mIsNew = false;
270 this->updateCaption();
271 } catch (SaveException& exception) {
272 if (!force) {
273 QMessageBox::critical(pMainWindow,tr("Error"),
274 exception.reason());
275 }
276 pMainWindow->fileSystemWatcher()->addPath(mFilename);
277 return false;
278 }
279
280 if (doReparse && mParser) {
281 reparse();
282 }
283 if (doReparse && pSettings->editor().syntaxCheckWhenSave())
284 checkSyntaxInBack();
285 reparseTodo();
286 return true;
287}
288
289bool Editor::saveAs(const QString &name, bool fromProject){
290 QString newName = name;
291 QString oldName = mFilename;
292 bool firstSave = isNew();
293 if (name.isEmpty()) {
294 QString selectedFileFilter;
295 QString defaultExt;
296 if (pSettings->editor().defaultFileCpp()) {
297 selectedFileFilter = pSystemConsts->defaultCPPFileFilter();
298 defaultExt = "cpp";
299 } else {
300 selectedFileFilter = pSystemConsts->defaultCFileFilter();
301 defaultExt = "c";
302 }
303 QFileDialog dialog(this,tr("Save As"),extractFilePath(mFilename),
304 pSystemConsts->defaultFileFilters().join(";;"));
305 dialog.selectNameFilter(selectedFileFilter);
306 dialog.setDefaultSuffix(defaultExt);
307 dialog.selectFile(mFilename);
308 dialog.setFileMode(QFileDialog::AnyFile);
309 dialog.setAcceptMode(QFileDialog::AcceptSave);
310 connect(&dialog, &QFileDialog::filterSelected,
311 [&dialog](const QString &filter){
312 int pos = filter.indexOf("*.");
313 if (pos>=0) {
314 QString suffix;
315 pos+=2;
316 while (pos<filter.length()) {
317 if (filter[pos] == ';' || filter[pos] ==' ' || filter[pos] == ')')
318 break;
319 suffix+=filter[pos];
320 pos++;
321 }
322 dialog.setDefaultSuffix(suffix);
323 }
324 });
325
326 if (dialog.exec()!=QFileDialog::Accepted) {
327 return false;
328 }
329 newName = dialog.selectedFiles()[0];
330 QDir::setCurrent(extractFileDir(newName));
331 }
332
333 if (pMainWindow->editorList()->getOpenedEditorByFilename(newName)) {
334 QMessageBox::critical(pMainWindow,tr("Error"),
335 tr("File %1 already openned!").arg(newName));
336 return false;
337 }
338 // Update project information
339 if (mInProject && pMainWindow->project() && !fromProject) {
340 int unitIndex = pMainWindow->project()->indexInUnits(newName);
341 if (unitIndex<0) {
342 mInProject = false;
343 }
344 }
345
346 clearSyntaxIssues();
347 pMainWindow->fileSystemWatcher()->removePath(mFilename);
348 if (pSettings->codeCompletion().enabled() && mParser) {
349 mParser->invalidateFile(mFilename);
350 }
351 try {
352 mFilename = newName;
353 saveFile(mFilename);
354 mIsNew = false;
355 setModified(false);
356 this->updateCaption();
357 } catch (SaveException& exception) {
358 QMessageBox::critical(pMainWindow,tr("Error"),
359 exception.reason());
360 return false;
361 }
362 if (pMainWindow->project() && !fromProject) {
363 pMainWindow->project()->associateEditor(this);
364 }
365 pMainWindow->fileSystemWatcher()->addPath(mFilename);
366 switch(getFileType(mFilename)) {
367 case FileType::CppSource:
368 mUseCppSyntax = true;
369 break;
370 case FileType::CSource:
371 mUseCppSyntax = false;
372 break;
373 default:
374 mUseCppSyntax = pSettings->editor().defaultFileCpp();
375 }
376
377 //update (reassign highlighter)
378 QSynedit::PHighlighter newHighlighter = HighlighterManager().getHighlighter(mFilename);
379 if (newHighlighter) {
380 setUseCodeFolding(true);
381 } else {
382 setUseCodeFolding(false);
383 }
384 setHighlighter(newHighlighter);
385 if (!newHighlighter || newHighlighter->getName() != SYN_HIGHLIGHTER_CPP) {
386 mSyntaxIssues.clear();
387 }
388 applyColorScheme(pSettings->editor().colorScheme());
389
390 reparse();
391
392 if (pSettings->editor().syntaxCheckWhenSave())
393 pMainWindow->checkSyntaxInBack(this);
394 reparseTodo();
395
396
397 if (pSettings->editor().readOnlySytemHeader()
398 && (!mParser->isSystemHeaderFile(mFilename) && !mParser->isProjectHeaderFile(mFilename))) {
399 setReadOnly(false);
400 updateCaption();
401 }
402
403 emit renamed(oldName, newName , firstSave);
404 return true;
405}
406
407void Editor::activate()
408{
409 if (mParentPageControl!=nullptr)
410 mParentPageControl->setCurrentWidget(this);
411 setFocus();
412}
413
414const QByteArray& Editor::encodingOption() const noexcept{
415 return mEncodingOption;
416}
417void Editor::setEncodingOption(const QByteArray& encoding) noexcept{
418 if (mEncodingOption == encoding)
419 return;
420 mEncodingOption = encoding;
421 if (!isNew())
422 loadFile();
423 else
424 pMainWindow->updateForEncodingInfo();
425 if (mInProject) {
426 std::shared_ptr<Project> project = pMainWindow->project();
427 if (project) {
428 int index = project->indexInUnits(this);
429 if (index>=0) {
430 PProjectUnit unit = project->units()[index];
431 unit->setEncoding(mEncodingOption);
432 }
433 }
434 }
435}
436const QByteArray& Editor::fileEncoding() const noexcept{
437 return mFileEncoding;
438}
439const QString& Editor::filename() const noexcept{
440 return mFilename;
441}
442bool Editor::inProject() const noexcept{
443 return mInProject;
444}
445bool Editor::isNew() const noexcept {
446 return mIsNew;
447}
448
449QTabWidget* Editor::pageControl() noexcept{
450 return mParentPageControl;
451}
452static int findWidgetInPageControl(QTabWidget* pageControl, QWidget* child) {
453 for (int i=0;i<pageControl->count();i++) {
454 if (pageControl->widget(i)==child)
455 return i;
456 }
457 return -1;
458}
459void Editor::setPageControl(QTabWidget *newPageControl)
460{
461 if (mParentPageControl==newPageControl)
462 return;
463 if (mParentPageControl!=nullptr) {
464 int index = findWidgetInPageControl(mParentPageControl,this);
465 if (index>=0)
466 mParentPageControl->removeTab(index);
467 }
468 mParentPageControl= newPageControl;
469 if (newPageControl!=nullptr) {
470 mParentPageControl->addTab(this,"");
471 updateCaption();
472 }
473}
474
475void Editor::undoSymbolCompletion(int pos)
476{
477 QSynedit::PHighlighterAttribute Attr;
478 QString Token;
479 bool tokenFinished;
480 QSynedit::TokenType tokenType;
481
482 if (!highlighter())
483 return;
484 if (!pSettings->editor().removeSymbolPairs())
485 return;
486 if (!getHighlighterAttriAtRowCol(caretXY(), Token, tokenFinished, tokenType, Attr))
487 return;
488 if ((tokenType == QSynedit::TokenType::Comment) && (!tokenFinished))
489 return ;
490 //convert caret x to string index;
491 pos--;
492
493 if (pos<0 || pos+1>=lineText().length())
494 return;
495 QChar DeletedChar = lineText().at(pos);
496 QChar NextChar = lineText().at(pos+1);
497 if ((tokenType == QSynedit::TokenType::Character) && (DeletedChar != '\''))
498 return;
499 if (tokenType == QSynedit::TokenType::StringEscapeSequence)
500 return;
501 if (tokenType == QSynedit::TokenType::String) {
502 if ((DeletedChar!='"') && (DeletedChar!='('))
503 return;
504 if ((DeletedChar=='"') && (Token!="\"\""))
505 return;
506 if ((DeletedChar=='(') && (!Token.startsWith("R\"")))
507 return;
508 }
509 if ((DeletedChar == '\'') && (tokenType == QSynedit::TokenType::Number))
510 return;
511 if ((DeletedChar == '<') &&
512 !(mParser && mParser->isIncludeLine(lineText())))
513 return;
514 if ( (pSettings->editor().completeBracket() && (DeletedChar == '[') && (NextChar == ']')) ||
515 (pSettings->editor().completeParenthese() && (DeletedChar == '(') && (NextChar == ')')) ||
516 (pSettings->editor().completeGlobalInclude() && (DeletedChar == '<') && (NextChar == '>')) ||
517 (pSettings->editor().completeBrace() && (DeletedChar == '{') && (NextChar == '}')) ||
518 (pSettings->editor().completeSingleQuote() && (DeletedChar == '\'') && (NextChar == '\'')) ||
519 (pSettings->editor().completeDoubleQuote() && (DeletedChar == '\"') && (NextChar == '\"'))) {
520 commandProcessor(QSynedit::EditCommand::ecDeleteChar);
521 }
522}
523
524void Editor::wheelEvent(QWheelEvent *event) {
525 if ( (event->modifiers() & Qt::ControlModifier)!=0) {
526 int size = pSettings->editor().fontSize();
527 if (event->angleDelta().y()>0) {
528 size = std::min(99,size+1);
529 pSettings->editor().setFontSize(size);
530 pSettings->editor().save();
531 pMainWindow->updateEditorSettings();
532 event->accept();
533 return;
534 } else if (event->angleDelta().y()<0) {
535 size = std::max(2,size-1);
536 pSettings->editor().setFontSize(size);
537 pSettings->editor().save();
538 pMainWindow->updateEditorSettings();
539 event->accept();
540 return;
541 }
542 }
543 SynEdit::wheelEvent(event);
544}
545
546void Editor::focusInEvent(QFocusEvent *event)
547{
548 SynEdit::focusInEvent(event);
549 pMainWindow->updateAppTitle();
550 pMainWindow->updateEditorActions();
551 pMainWindow->updateForEncodingInfo();
552 pMainWindow->updateStatusbarForLineCol();
553 pMainWindow->updateForStatusbarModeInfo();
554 pMainWindow->updateClassBrowserForEditor(this);
555}
556
557void Editor::focusOutEvent(QFocusEvent *event)
558{
559 SynEdit::focusOutEvent(event);
560 //pMainWindow->updateClassBrowserForEditor(nullptr);
561 pMainWindow->updateForEncodingInfo();
562 pMainWindow->updateStatusbarForLineCol();
563 pMainWindow->updateForStatusbarModeInfo();
564 pMainWindow->functionTip()->hide();
565}
566
567void Editor::keyPressEvent(QKeyEvent *event)
568{
569 bool handled = false;
570 auto action = finally([&,this]{
571 if (!handled) {
572 SynEdit::keyPressEvent(event);
573 } else {
574 event->accept();
575 }
576 });
577 if (readOnly())
578 return;
579
580 switch (event->key()) {
581 case Qt::Key_Return:
582 case Qt::Key_Enter:
583 mLastIdCharPressed = 0;
584 if (mTabStopBegin>=0) { // editing user code template
585 handled = true;
586 mTabStopBegin = -1;
587 invalidateLine(caretY());
588 clearUserCodeInTabStops();
589 } else {
590 QString s = lineText().mid(0,caretX()-1).trimmed();
591 if (s=="/**") { //javadoc style docstring
592 s = lineText().mid(caretX()-1).trimmed();
593 if (s=="*/") {
594 QSynedit::BufferCoord p = caretXY();
595 setBlockBegin(p);
596 p.ch = lineText().length()+1;
597 setBlockEnd(p);
598 setSelText("");
599 }
600 handled = true;
601 QStringList insertString;
602 insertString.append("");
603 PStatement function;
604 if (mParser)
605 function = mParser->findFunctionAt(mFilename,caretY()+1);
606 if (function) {
607 QStringList params;
608 QString funcName = function->command;
609 bool isVoid = (function->type == "void");
610 foreach (const PStatement& child, function->children) {
611 if (child->kind == StatementKind::skParameter)
612 params.append(child->command);
613 }
614 insertString.append(QString(" * @brief ")+USER_CODE_IN_INSERT_POS);
615 if (!params.isEmpty())
616 insertString.append(" * ");
617 foreach (const QString& param, params) {
618 insertString.append(QString(" * @param %1 %2")
619 .arg(param, USER_CODE_IN_INSERT_POS));
620 }
621 if (!isVoid) {
622 insertString.append(" * ");
623 insertString.append(QString(" * @return ")+USER_CODE_IN_INSERT_POS);
624 }
625 insertString.append(" */");
626// } else if (caretY()==1) { /* file header */
627// insertString.append(QString(" * @file %1<SOURCEPATH>%2")
628// .arg(USER_CODE_IN_REPL_POS_BEGIN)
629// .arg(USER_CODE_IN_REPL_POS_END));
630// insertString.append(QString(" * @brief: ")+ USER_CODE_IN_INSERT_POS);
631// insertString.append(QString(" * @version: ")+ USER_CODE_IN_INSERT_POS);
632// insertString.append(QString(" * @copyright: ")+ USER_CODE_IN_INSERT_POS);
633// insertString.append(QString(" * @author: ")+ USER_CODE_IN_INSERT_POS);
634// insertString.append(" * @date: <DATETIME>");
635// insertString.append(" * ");
636// insertString.append(" **/");
637 } else {
638 insertString.append(QString(" * ")+USER_CODE_IN_INSERT_POS);
639 insertString.append(" */");
640 }
641 insertCodeSnippet(linesToText(insertString));
642 } else if (highlighter()
643 && caretY()>=2
644 && highlighter()->isLastLineCommentNotFinished(
645 document()->ranges(caretY()-2).state)) {
646 s=trimLeft(lineText());
647 if (s.startsWith("* ")) {
648 handled = true;
649 int right = document()->getString(caretY()-1).length()-caretX();
650 s=lineBreak()+"* ";
651 insertString(s,false);
652 QSynedit::BufferCoord p = caretXY();
653 p.line++;
654 p.ch = document()->getString(p.line-1).length()+1;
655 if (right>0) {
656 p.ch -=right+1;
657 }
658 setCaretXY(p);
659 }
660 }
661 }
662 return;
663 case Qt::Key_Escape: // Update function tip
664 mLastIdCharPressed = 0;
665 if (mTabStopBegin>=0) {
666 mTabStopBegin = -1;
667 setBlockEnd(caretXY());
668 invalidateLine(caretY());
669 clearUserCodeInTabStops();
670 }
671 pMainWindow->functionTip()->hide();
672 return;
673 case Qt::Key_Tab:
674 handled = true;
675 tab();
676 return;
677 case Qt::Key_Backtab:
678 handled = true;
679 shifttab();
680 return;
681 case Qt::Key_Up:
682 if (pMainWindow->functionTip()->isVisible()) {
683 handled = true;
684 pMainWindow->functionTip()->previousTip();
685 } else {
686 mLastIdCharPressed = 0;
687 clearUserCodeInTabStops();
688 }
689 return;
690 case Qt::Key_Down:
691 if (pMainWindow->functionTip()->isVisible()) {
692 handled = true;
693 pMainWindow->functionTip()->nextTip();
694 } else {
695 mLastIdCharPressed = 0;
696 clearUserCodeInTabStops();
697 }
698 return;
699 case Qt::Key_Delete:
700 // remove completed character
701 mLastIdCharPressed = 0;
702 if (!selAvail()) {
703 undoSymbolCompletion(caretX());
704 }
705 return;
706 case Qt::Key_Backspace:
707 // remove completed character
708 mLastIdCharPressed = 0;
709 if (!selAvail()) {
710 undoSymbolCompletion(caretX()-1);
711 }
712 return;
713 }
714
715 QString t = event->text();
716 if (t.isEmpty())
717 return;
718
719 if (activeSelectionMode()==QSynedit::SelectionMode::Column)
720 return;
721
722 QChar ch = t[0];
723 if (isIdentChar(ch)) {
724 mLastIdCharPressed++;
725 if (pSettings->codeCompletion().enabled()
726 && pSettings->codeCompletion().showCompletionWhileInput() ) {
727 if (mParser && mParser->isIncludeLine(lineText())
728 && mLastIdCharPressed==pSettings->codeCompletion().minCharRequired()) {
729 // is a #include line
730 setSelText(ch);
731 showHeaderCompletion(false);
732 handled=true;
733 return;
734 } else if (mLastIdCharPressed==pSettings->codeCompletion().minCharRequired()){
735 QString lastWord = getPreviousWordAtPositionForSuggestion(caretXY());
736 if (mParser && !lastWord.isEmpty()) {
737 if (CppTypeKeywords.contains(lastWord)) {
738 if (lastWord == "long" ||
739 lastWord == "short" ||
740 lastWord == "signed" ||
741 lastWord == "unsigned"
742 ) {
743 setSelText(ch);
744 showCompletion(lastWord,false);
745 handled=true;
746 return;
747 }
748 //last word is a type keyword, this is a var or param define, and dont show suggestion
749 // if devEditor.UseTabnine then
750 // ShowTabnineCompletion;
751 return;
752 }
753 PStatement statement = mParser->findStatementOf(
754 mFilename,
755 lastWord,
756 caretY());
757 StatementKind kind = getKindOfStatement(statement);
758 if (kind == StatementKind::skClass
759 || kind == StatementKind::skEnumClassType
760 || kind == StatementKind::skEnumType
761 || kind == StatementKind::skTypedef) {
762 //last word is a typedef/class/struct, this is a var or param define, and dont show suggestion
763 // if devEditor.UseTabnine then
764 // ShowTabnineCompletion;
765 return;
766 }
767 }
768 setSelText(ch);
769 showCompletion("",false);
770 handled=true;
771 return;
772 }
773 }
774 } else {
775 //preprocessor ?
776 if (mParser && (mLastIdCharPressed=0) && (ch=='#') && lineText().isEmpty()) {
777 if (pSettings->codeCompletion().enabled()
778 && pSettings->codeCompletion().showCompletionWhileInput() ) {
779 mLastIdCharPressed++;
780 setSelText(ch);
781 showCompletion("",false);
782 handled=true;
783 return;
784 }
785 }
786 //javadoc directive?
787 if (mParser && (mLastIdCharPressed=0) && (ch=='#') &&
788 lineText().trimmed().startsWith('*')) {
789 if (pSettings->codeCompletion().enabled()
790 && pSettings->codeCompletion().showCompletionWhileInput() ) {
791 mLastIdCharPressed++;
792 setSelText(ch);
793 showCompletion("",false);
794 handled=true;
795 return;
796 }
797 }
798 mLastIdCharPressed = 0;
799 switch (ch.unicode()) {
800 case '"':
801 case '\'':
802 case ')':
803 case '{':
804 case '}':
805 case '[':
806 case ']':
807 case '<':
808 case '*':
809 handled = handleSymbolCompletion(ch);
810 return;
811 case '(': {
812 QChar nextCh = nextNonSpaceChar(caretY()-1,caretX()-1);
813 if (!isIdentChar(nextCh) && nextCh!='('
814 && nextCh!='"' && nextCh!='\'' ){
815 handled = handleSymbolCompletion(ch);
816 }
817 return;
818 }
819 case '>':
820 if ((caretX() <= 1) || lineText().isEmpty()
821 || lineText()[caretX() - 2] != '-') {
822 handled = handleSymbolCompletion(ch);
823 return;
824 }
825 break;
826 }
827 }
828
829 // Spawn code completion window if we are allowed to
830 if (pSettings->codeCompletion().enabled())
831 handled = handleCodeCompletion(ch);
832}
833
834void Editor::onGutterPaint(QPainter &painter, int aLine, int X, int Y)
835{
836 IconsManager::PPixmap icon;
837
838 if (mActiveBreakpointLine == aLine) {
839 icon = pIconsManager->getPixmap(IconsManager::GUTTER_ACTIVEBREAKPOINT);
840 } else if (hasBreakpoint(aLine)) {
841 icon = pIconsManager->getPixmap(IconsManager::GUTTER_BREAKPOINT);
842 } else {
843 PSyntaxIssueList lst = getSyntaxIssuesAtLine(aLine);
844 if (lst) {
845 bool hasError=false;
846 for (const PSyntaxIssue& issue : *lst) {
847 if (issue->issueType == CompileIssueType::Error) {
848 hasError = true;
849 break;;
850 }
851 }
852 if (hasError) {
853 icon = pIconsManager->getPixmap(IconsManager::GUTTER_SYNTAX_ERROR);
854 } else {
855 icon = pIconsManager->getPixmap(IconsManager::GUTTER_SYNTAX_WARNING);
856 }
857 } else if (hasBookmark(aLine)) {
858 icon = pIconsManager->getPixmap(IconsManager::GUTTER_BOOKMARK);
859 }
860 }
861 if (icon) {
862 X = 5;
863 Y += (this->textHeight() - icon->height()) / 2;
864 painter.drawPixmap(X,Y,*icon);
865 }
866}
867
868void Editor::onGetEditingAreas(int Line, QSynedit::EditingAreaList &areaList)
869{
870 areaList.clear();
871 if (mTabStopBegin>=0 && mTabStopY == Line) {
872 QSynedit::PEditingArea p = make_shared<QSynedit::EditingArea>();
873 p->type = QSynedit::EditingAreaType::eatRectangleBorder;
874// int spaceCount = leftSpaces(mLineBeforeTabStop);
875// int spaceBefore = mLineBeforeTabStop.length()-TrimLeft(mLineBeforeTabStop).length();
876 p->beginX = mTabStopBegin;
877 p->endX = mTabStopEnd;
878 p->color = highlighter()->stringAttribute()->foreground();
879 areaList.append(p);
880 }
881 PSyntaxIssueList lst = getSyntaxIssuesAtLine(Line);
882 if (lst) {
883 for (const PSyntaxIssue& issue: *lst) {
884 QSynedit::PEditingArea p=std::make_shared<QSynedit::EditingArea>();
885 p->beginX = issue->col;
886 p->endX = issue->endCol;
887 if (issue->issueType == CompileIssueType::Error) {
888 p->color = mSyntaxErrorColor;
889 } else {
890 p->color = mSyntaxWarningColor;
891 }
892 p->type = QSynedit::EditingAreaType::eatWaveUnderLine;
893 areaList.append(p);
894 }
895 }
896}
897
898bool Editor::onGetSpecialLineColors(int Line, QColor &foreground, QColor &backgroundColor)
899{
900 if (Line == mActiveBreakpointLine &&
901 mActiveBreakpointBackgroundColor.isValid()) {
902 if (mActiveBreakpointForegroundColor.isValid())
903 foreground = mActiveBreakpointForegroundColor;
904 backgroundColor = mActiveBreakpointBackgroundColor;
905 return true;
906 } else if (hasBreakpoint(Line) && mBreakpointBackgroundColor.isValid()) {
907 if (mBreakpointForegroundColor.isValid())
908 foreground = mBreakpointForegroundColor;
909 backgroundColor = mBreakpointBackgroundColor;
910 return true;
911 }
912// end else if Line = fErrorLine then begin
913// StrToThemeColor(tc, devEditor.Syntax.Values[cErr]);
914// BG := tc.Background;
915// FG := tc.Foreground;
916// if (BG <> clNone) or (FG<>clNone) then
917// Special := TRUE;
918// end;
919 return false;
920}
921
922void Editor::onPreparePaintHighlightToken(int line, int aChar, const QString &token, QSynedit::PHighlighterAttribute attr, QSynedit::FontStyles &style, QColor &foreground, QColor &background)
923{
924 if (token.isEmpty())
925 return;
926
927 if (mParser && mParser->enabled() && highlighter()) {
928 QString lineText = document()->getString(line-1);
929 if (mParser->isIncludeLine(lineText)) {
930 if (cursor() == Qt::PointingHandCursor) {
931 QSynedit::BufferCoord p;
932 if (pointToCharLine(mapFromGlobal(QCursor::pos()),p)) {
933 if (line==p.line){
934 int pos1=std::max(lineText.indexOf("<"),lineText.indexOf("\""));
935 int pos2=std::max(lineText.lastIndexOf(">"),lineText.lastIndexOf("\""));
936 pos1++;
937 pos2++;
938 if (pos1>0 && pos2>0 && pos1<aChar && aChar < pos2) {
939 style.setFlag(QSynedit::FontStyle::fsUnderline);
940 }
941 }
942 }
943 }
944 } else if (attr == highlighter()->identifierAttribute()) {
945 QSynedit::BufferCoord p{aChar,line};
946 // BufferCoord pBeginPos,pEndPos;
947 // QString s= getWordAtPosition(this,p, pBeginPos,pEndPos, WordPurpose::wpInformation);
948 // qDebug()<<s;
949 // PStatement statement = mParser->findStatementOf(mFilename,
950 // s , p.Line);
951 QStringList expression = getExpressionAtPosition(p);
952 PStatement statement = parser()->findStatementOf(
953 filename(),
954 expression,
955 p.line);
956 StatementKind kind = getKindOfStatement(statement);
957 if (kind == StatementKind::skUnknown) {
958 QSynedit::BufferCoord pBeginPos,pEndPos;
959 QString s= getWordAtPosition(this,p, pBeginPos,pEndPos, WordPurpose::wpInformation);
960 if ((pEndPos.line>=1)
961 && (pEndPos.ch>=0)
962 && (pEndPos.ch+1 < document()->getString(pEndPos.line-1).length())
963 && (document()->getString(pEndPos.line-1)[pEndPos.ch+1] == '(')) {
964 kind = StatementKind::skFunction;
965 } else {
966 kind = StatementKind::skVariable;
967 }
968 }
969 PColorSchemeItem item = mStatementColors->value(kind,PColorSchemeItem());
970
971 if (item) {
972 foreground = item->foreground();
973 //background = item->background();
974 style.setFlag(QSynedit::FontStyle::fsBold,item->bold());
975 style.setFlag(QSynedit::FontStyle::fsItalic,item->italic());
976 style.setFlag(QSynedit::FontStyle::fsUnderline,item->underlined());
977 style.setFlag(QSynedit::FontStyle::fsStrikeOut,item->strikeout());
978 } else {
979 foreground = highlighter()->identifierAttribute()->foreground();
980 }
981 if (cursor() == Qt::PointingHandCursor) {
982 QSynedit::BufferCoord p;
983 if (pointToCharLine(mapFromGlobal(QCursor::pos()),p)) {
984 if (line==p.line && (aChar<=p.ch && p.ch<aChar+token.length())) {
985 style.setFlag(QSynedit::FontStyle::fsUnderline);
986 }
987 }
988 }
989 }
990 }
991
992 //selection
993 if (highlighter() && attr) {
994 if (((attr == highlighter()->identifierAttribute())
995 || (attr == highlighter()->keywordAttribute())
996 || (attr->name() == SYNS_AttrPreprocessor)
997 )
998 && (token == mCurrentHighlightedWord)) {
999 if (mCurrentHighlighWordForeground.isValid())
1000 foreground = mCurrentHighlighWordForeground;
1001 if (mCurrentHighlighWordBackground.isValid())
1002 background = mCurrentHighlighWordBackground;
1003 } else if (!selAvail() && attr->name() == SYNS_AttrSymbol
1004 && pSettings->editor().highlightMathingBraces()) {
1005 // qDebug()<<line<<":"<<aChar<<" - "<<mHighlightCharPos1.Line<<":"<<mHighlightCharPos1.Char<<" - "<<mHighlightCharPos2.Line<<":"<<mHighlightCharPos2.Char;
1006 if ( (line == mHighlightCharPos1.line)
1007 && (aChar == mHighlightCharPos1.ch)) {
1008 if (mCurrentHighlighWordForeground.isValid())
1009 foreground = mCurrentHighlighWordForeground;
1010 if (mCurrentHighlighWordBackground.isValid())
1011 background = mCurrentHighlighWordBackground;
1012 }
1013 if ((line == mHighlightCharPos2.line)
1014 && (aChar == mHighlightCharPos2.ch)) {
1015 if (mCurrentHighlighWordForeground.isValid())
1016 foreground = mCurrentHighlighWordForeground;
1017 if (mCurrentHighlighWordBackground.isValid())
1018 background = mCurrentHighlighWordBackground;
1019 }
1020 }
1021 } else {
1022 if (token == mCurrentHighlightedWord) {
1023 if (mCurrentHighlighWordForeground.isValid())
1024 foreground = mCurrentHighlighWordForeground;
1025 if (mCurrentHighlighWordBackground.isValid())
1026 background = mCurrentHighlighWordBackground;
1027 }
1028 }
1029}
1030
1031bool Editor::event(QEvent *event)
1032{
1033 if ((event->type() == QEvent::HoverEnter || event->type() == QEvent::HoverMove)
1034 && qApp->mouseButtons() == Qt::NoButton
1035 && pSettings->editor().enableTooltips()
1036 && !pMainWindow->completionPopup()->isVisible()
1037 && !pMainWindow->functionTip()->isVisible()
1038 && !pMainWindow->headerCompletionPopup()->isVisible()) {
1039 if(mHoverModifiedLine!=-1) {
1040 invalidateLine(mHoverModifiedLine);
1041 mHoverModifiedLine=-1;
1042 }
1043
1044 QHoverEvent *helpEvent = static_cast<QHoverEvent *>(event);
1045 QSynedit::BufferCoord p;
1046 TipType reason = getTipType(helpEvent->pos(),p);
1047 PSyntaxIssue pError;
1048 int line ;
1049 if (reason == TipType::Error) {
1050 pError = getSyntaxIssueAtPosition(p);
1051 } else if (pointToLine(helpEvent->pos(),line)) {
1052 //issue tips is prefered
1053 PSyntaxIssueList issues = getSyntaxIssuesAtLine(line);
1054 if (issues && !issues->isEmpty()) {
1055 reason = TipType::Error;
1056 pError = issues->front();
1057 }
1058 }
1059
1060 // Get subject
1061 bool isIncludeLine = false;
1062 QSynedit::BufferCoord pBeginPos,pEndPos;
1063 QString s;
1064 QStringList expression;
1065 switch (reason) {
1066 case TipType::Preprocessor:
1067 // When hovering above a preprocessor line, determine if we want to show an include or a identifier hint
1068 s = document()->getString(p.line - 1);
1069 isIncludeLine = mParser->isIncludeLine(s);
1070 if (!isIncludeLine)
1071 s = wordAtRowCol(p);
1072 break;
1073 case TipType::Identifier:
1074 if (pMainWindow->debugger()->executing() && !pMainWindow->debugger()->inferiorRunning())
1075 s = getWordAtPosition(this,p, pBeginPos,pEndPos, WordPurpose::wpEvaluation); // debugging
1076 else if (//devEditor.ParserHints and
1077 !mCompletionPopup->isVisible()
1078 && !mHeaderCompletionPopup->isVisible()) {
1079 expression = getExpressionAtPosition(p);
1080 s = expression.join(""); // information during coding
1081 }
1082 break;
1083 case TipType::Selection:
1084 s = selText(); // when a selection is available, always only use that
1085 break;
1086 case TipType::Error:
1087 s = pError->token;
1088 break;
1089 case TipType::None:
1090 cancelHint();
1091 mCurrentWord = "";
1092 mCurrentTipType = TipType::None;
1093 event->ignore();
1094 return true;
1095 }
1096
1097 s = s.trimmed();
1098 if ((s == mCurrentWord) && (mCurrentTipType == reason)) {
1099 if (qApp->queryKeyboardModifiers() == Qt::ControlModifier) {
1100 if (!hasFocus())
1101 activate();
1102 setCursor(Qt::PointingHandCursor);
1103 } else {
1104 updateMouseCursor();
1105 }
1106 if (pointToLine(helpEvent->pos(),line)) {
1107 invalidateLine(line);
1108 mHoverModifiedLine=line;
1109 }
1110 event->ignore();
1111 return true; // do NOT remove hint when subject stays the same
1112 }
1113
1114 // Remove hint
1115 cancelHint();
1116 mCurrentWord = s;
1117 mCurrentTipType = reason;
1118
1119
1120 // Determine what to do with subject
1121 QString hint = "";
1122 switch (reason) {
1123 case TipType::Preprocessor:
1124 if (isIncludeLine) {
1125 if (pSettings->editor().enableHeaderToolTips())
1126 hint = getFileHint(s);
1127 } else if (//devEditor.ParserHints and
1128 !mCompletionPopup->isVisible()
1129 && !mHeaderCompletionPopup->isVisible()) {
1130 if (pSettings->editor().enableIdentifierToolTips())
1131 hint = getParserHint(QStringList(),s,p.line);
1132 }
1133 break;
1134 case TipType::Identifier:
1135 case TipType::Selection:
1136 if (!mCompletionPopup->isVisible()
1137 && !mHeaderCompletionPopup->isVisible()) {
1138 if (pMainWindow->debugger()->executing()
1139 && (pSettings->editor().enableDebugTooltips())) {
1140 showDebugHint(s,p.line);
1141 } else if (pSettings->editor().enableIdentifierToolTips()) { //if devEditor.ParserHints {
1142 hint = getParserHint(expression, s, p.line);
1143 }
1144 }
1145 break;
1146 case TipType::Error:
1147 if (pSettings->editor().enableIssueToolTips())
1148 hint = getErrorHint(pError);
1149 break;
1150 default:
1151 break;
1152 }
1153// qDebug()<<"hint:"<<hint;
1154 if (!hint.isEmpty()) {
1155 // QApplication* app = dynamic_cast<QApplication *>(QApplication::instance());
1156 // if (app->keyboardModifiers().testFlag(Qt::ControlModifier)) {
1157 if (qApp->queryKeyboardModifiers() == Qt::ControlModifier) {
1158 if (!hasFocus())
1159 activate();
1160 setCursor(Qt::PointingHandCursor);
1161 } else if (cursor() == Qt::PointingHandCursor) {
1162 updateMouseCursor();
1163 }
1164 if (pointToLine(helpEvent->pos(),line)) {
1165 invalidateLine(line);
1166 mHoverModifiedLine=line;
1167 }
1168 if (pMainWindow->functionTip()->isVisible()) {
1169 pMainWindow->functionTip()->hide();
1170 }
1171 QToolTip::showText(mapToGlobal(helpEvent->pos()),hint,this);
1172 event->ignore();
1173 } else {
1174 updateMouseCursor();
1175 event->ignore();
1176 }
1177 return true;
1178 } else if (event->type() == QEvent::HoverLeave) {
1179 cancelHint();
1180 return true;
1181 } else if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease ) {
1182 if (!mCurrentWord.isEmpty()) {
1183 QKeyEvent* keyEvent = dynamic_cast<QKeyEvent*>(event);
1184 if (keyEvent->key() == Qt::Key_Control) {
1185 QApplication* app = dynamic_cast<QApplication *>(QApplication::instance());
1186 QHoverEvent* hoverEvent=new QHoverEvent(QEvent::HoverMove,
1187 mapFromGlobal(QCursor::pos()),
1188 mapFromGlobal(QCursor::pos()),
1189 Qt::ControlModifier
1190 );
1191 app->postEvent(this,hoverEvent);
1192 }
1193 }
1194 }
1195 return SynEdit::event(event);
1196}
1197
1198void Editor::mouseReleaseEvent(QMouseEvent *event)
1199{
1200 if (event->button() | Qt::LeftButton) {
1201 mLastIdCharPressed = 0;
1202 }
1203
1204 // if ctrl+clicked
1205 if ((cursor() == Qt::PointingHandCursor) && (event->modifiers() == Qt::ControlModifier)
1206 && (event->button() == Qt::LeftButton)) {
1207
1208 QSynedit::BufferCoord p;
1209 if (pointToCharLine(event->pos(),p)) {
1210 QString s = document()->getString(p.line - 1);
1211 if (mParser->isIncludeLine(s)) {
1212 QString filename = mParser->getHeaderFileName(mFilename,s);
1213 Editor * e = pMainWindow->editorList()->getEditorByFilename(filename);
1214 if (e) {
1215 e->setCaretPositionAndActivate(1,1);
1216 return;
1217 }
1218 } else {
1219 gotoDefinition(p);
1220 return;
1221 }
1222 }
1223 }
1224 QSynedit::SynEdit::mouseReleaseEvent(event);
1225}
1226
1227void Editor::inputMethodEvent(QInputMethodEvent *event)
1228{
1229 QSynedit::SynEdit::inputMethodEvent(event);
1230 QString s = event->commitString();
1231 if (s.isEmpty())
1232 return;
1233 if (pMainWindow->completionPopup()->isVisible()) {
1234 onCompletionInputMethod(event);
1235 return;
1236 } else {
1237 mLastIdCharPressed+=s.length();
1238 if (pSettings->codeCompletion().enabled()
1239 && pSettings->codeCompletion().showCompletionWhileInput() ) {
1240 if (mLastIdCharPressed>=pSettings->codeCompletion().minCharRequired()) {
1241 QString lastWord = getPreviousWordAtPositionForSuggestion(caretXY());
1242 if (!lastWord.isEmpty()) {
1243 if (CppTypeKeywords.contains(lastWord)) {
1244 return;
1245 }
1246 PStatement statement = mParser->findStatementOf(
1247 mFilename,
1248 lastWord,
1249 caretY());
1250 StatementKind kind = getKindOfStatement(statement);
1251 if (kind == StatementKind::skClass
1252 || kind == StatementKind::skEnumClassType
1253 || kind == StatementKind::skEnumType
1254 || kind == StatementKind::skTypedef) {
1255 //last word is a typedef/class/struct, this is a var or param define, and dont show suggestion
1256 // if devEditor.UseTabnine then
1257 // ShowTabnineCompletion;
1258 return;
1259 }
1260 }
1261 showCompletion("",false);
1262 return;
1263 }
1264 }
1265 }
1266}
1267
1268void Editor::closeEvent(QCloseEvent *)
1269{
1270 if (mHeaderCompletionPopup)
1271 mHeaderCompletionPopup->hide();
1272 if (mCompletionPopup)
1273 mCompletionPopup->hide();
1274 if (pMainWindow->functionTip())
1275 pMainWindow->functionTip()->hide();
1276 pMainWindow->updateForEncodingInfo(true);
1277 pMainWindow->updateStatusbarForLineCol(true);
1278 pMainWindow->updateForStatusbarModeInfo(true);
1279}
1280
1281void Editor::showEvent(QShowEvent */*event*/)
1282{
1283// if (pSettings->codeCompletion().clearWhenEditorHidden()
1284// && !inProject()) {
1285//// initParser();
1286// }
1287 if (mParser && !pMainWindow->isClosingAll()
1288 && !pMainWindow->isQuitting()
1289 && !mParser->isFileParsed(mFilename)
1290 ) {
1291 connect(mParser.get(),
1292 &CppParser::onEndParsing,
1293 this,
1294 &QSynedit::SynEdit::invalidate);
1295 resetCppParser(mParser);
1296 reparse();
1297 }
1298
1299// if (pSettings->codeCompletion().clearWhenEditorHidden()
1300// && !inProject()) {
1301// reparse();
1302// }
1303 reparseTodo();
1304 setHideTime(QDateTime());
1305}
1306
1307void Editor::hideEvent(QHideEvent */*event*/)
1308{
1309 if (mParser) {
1310 disconnect(mParser.get(),
1311 &CppParser::onEndParsing,
1312 this,
1313 &QSynedit::SynEdit::invalidate);
1314 }
1315 if (pSettings->codeCompletion().clearWhenEditorHidden()
1316 && !inProject() && mParser
1317 && !pMainWindow->isMinimized()) {
1318 mParser->reset();
1319 }
1320 setHideTime(QDateTime::currentDateTime());
1321}
1322
1323void Editor::resizeEvent(QResizeEvent *event)
1324{
1325 QSynedit::SynEdit::resizeEvent(event);
1326 pMainWindow->functionTip()->hide();
1327}
1328
1329void Editor::copyToClipboard()
1330{
1331 if (pSettings->editor().copySizeLimit()) {
1332 int startLine = blockBegin().line;
1333 int endLine = blockEnd().line;
1334 if ((endLine-startLine+1) > pSettings->editor().copyLineLimits()) {
1335 QMessageBox::critical(pMainWindow,tr("Error"),
1336 tr("The text to be copied exceeds count limit!"));
1337 return;
1338 }
1339 if ((selText().length()) > pSettings->editor().copyCharLimits() * 1000) {
1340 QMessageBox::critical(pMainWindow,tr("Error"),
1341 tr("The text to be copied exceeds character limit!"));
1342 return;
1343 }
1344 }
1345 switch(pSettings->editor().copyWithFormatAs()) {
1346 case 1: //HTML
1347 copyAsHTML();
1348 break;;
1349 default:
1350 QSynedit::SynEdit::copyToClipboard();
1351 }
1352}
1353
1354void Editor::cutToClipboard()
1355{
1356 if (pSettings->editor().copySizeLimit()) {
1357 int startLine = blockBegin().line;
1358 int endLine = blockEnd().line;
1359 if ((endLine-startLine+1) > pSettings->editor().copyLineLimits()) {
1360 QMessageBox::critical(pMainWindow,tr("Error"),
1361 tr("The text to be cut exceeds count limit!"));
1362 return;
1363 }
1364 if ((selText().length()) > pSettings->editor().copyCharLimits() * 1000) {
1365 QMessageBox::critical(pMainWindow,tr("Error"),
1366 tr("The text to be cut exceeds character limit!"));
1367 return;
1368 }
1369 }
1370 QSynedit::SynEdit::cutToClipboard();
1371}
1372
1373void Editor::copyAsHTML()
1374{
1375 if (!selAvail())
1376 return;
1377 QSynedit::SynHTMLExporter exporter(tabWidth(), pCharsetInfoManager->getDefaultSystemEncoding());
1378
1379 exporter.setTitle(QFileInfo(mFilename).fileName());
1380 exporter.setExportAsText(false);
1381 exporter.setUseBackground(pSettings->editor().copyHTMLUseBackground());
1382 exporter.setFont(font());
1383 QSynedit::PHighlighter hl = highlighter();
1384 if (!pSettings->editor().copyHTMLUseEditorColor()) {
1385 hl = highlighterManager.copyHighlighter(highlighter());
1386 highlighterManager.applyColorScheme(hl,pSettings->editor().copyHTMLColorScheme());
1387 }
1388 exporter.setHighlighter(hl);
1389 exporter.setOnFormatToken(std::bind(&Editor::onExportedFormatToken,
1390 this,
1391 std::placeholders::_1,
1392 std::placeholders::_2,
1393 std::placeholders::_3,
1394 std::placeholders::_4,
1395 std::placeholders::_5
1396 ));
1397 exporter.setCreateHTMLFragment(true);
1398
1399 exporter.ExportRange(document(),blockBegin(),blockEnd());
1400
1401 QMimeData * mimeData = new QMimeData;
1402
1403 //sethtml will convert buffer to QString , which will cause encoding trouble
1404 mimeData->setData(exporter.clipboardFormat(),exporter.buffer());
1405 mimeData->setText(selText());
1406
1407 QGuiApplication::clipboard()->clear();
1408 QGuiApplication::clipboard()->setMimeData(mimeData);
1409}
1410
1411void Editor::setCaretPosition(int line, int aChar)
1412{
1413 this->uncollapseAroundLine(line);
1414 this->setCaretXYCentered(QSynedit::BufferCoord{aChar,line});
1415}
1416
1417void Editor::setCaretPositionAndActivate(int line, int aChar)
1418{
1419 this->uncollapseAroundLine(line);
1420 if (!this->hasFocus())
1421 this->activate();
1422 this->setCaretXYCentered(QSynedit::BufferCoord{aChar,line});
1423}
1424
1425void Editor::addSyntaxIssues(int line, int startChar, int endChar, CompileIssueType errorType, const QString &hint)
1426{
1427 PSyntaxIssue pError;
1428 QSynedit::BufferCoord p;
1429 QString token;
1430 QSynedit::TokenType tokenType;
1431 int tokenKind,start;
1432 QSynedit::PHighlighterAttribute attr;
1433 PSyntaxIssueList lst;
1434 if ((line<1) || (line>document()->count()))
1435 return;
1436 pError = std::make_shared<SyntaxIssue>();
1437 p.ch = startChar;
1438 p.line = line;
1439 if (startChar >= document()->getString(line-1).length()) {
1440 start = 1;
1441 token = document()->getString(line-1);
1442 } else if (endChar < 1) {
1443 if (!getHighlighterAttriAtRowColEx(p,token,tokenType,tokenKind,start,attr))
1444 return;
1445 } else {
1446 start = startChar;
1447 token = document()->getString(line-1).mid(start-1,endChar-startChar);
1448 }
1449 pError->startChar = start;
1450 pError->endChar = start + token.length();
1451// pError->col = charToColumn(line,pError->startChar);
1452// pError->endCol = charToColumn(line,pError->endChar);
1453 pError->col = pError->startChar;
1454 pError->endCol = pError->endChar;
1455 pError->hint = hint;
1456 pError->token = token;
1457 pError->issueType = errorType;
1458 if (mSyntaxIssues.contains(line)) {
1459 lst = mSyntaxIssues[line];
1460 } else {
1461 lst = std::make_shared<SyntaxIssueList>();
1462 mSyntaxIssues[line] = lst;
1463 }
1464 lst->append(pError);
1465}
1466
1467void Editor::clearSyntaxIssues()
1468{
1469 mSyntaxIssues.clear();
1470}
1471
1472void Editor::gotoNextSyntaxIssue()
1473{
1474 auto iter = mSyntaxIssues.find(caretY());
1475 if (iter==mSyntaxIssues.end())
1476 return;
1477 iter++;
1478 if (iter==mSyntaxIssues.end())
1479 return;
1480 QSynedit::BufferCoord p;
1481 p.ch = (*iter)->at(0)->startChar;
1482 p.line = iter.key();
1483 setCaretXY(p);
1484}
1485
1486void Editor::gotoPrevSyntaxIssue()
1487{
1488 auto iter = mSyntaxIssues.find(caretY());
1489 if (iter==mSyntaxIssues.end())
1490 return;
1491 if (iter==mSyntaxIssues.begin())
1492 return;
1493 iter--;
1494 QSynedit::BufferCoord p;
1495 p.ch = (*iter)->at(0)->startChar;
1496 p.line = iter.key();
1497 setCaretXY(p);
1498
1499}
1500
1501bool Editor::hasNextSyntaxIssue() const
1502{
1503 auto iter = mSyntaxIssues.find(caretY());
1504 if (iter==mSyntaxIssues.end())
1505 return false;
1506 iter++;
1507 if (iter==mSyntaxIssues.end())
1508 return false;
1509 return true;
1510}
1511
1512bool Editor::hasPrevSyntaxIssue() const
1513{
1514 auto iter = mSyntaxIssues.find(caretY());
1515 if (iter==mSyntaxIssues.end())
1516 return true;
1517 if (iter==mSyntaxIssues.begin())
1518 return true;
1519 return false;
1520}
1521
1522Editor::PSyntaxIssueList Editor::getSyntaxIssuesAtLine(int line)
1523{
1524 if (mSyntaxIssues.contains(line))
1525 return mSyntaxIssues[line];
1526 return PSyntaxIssueList();
1527}
1528
1529Editor::PSyntaxIssue Editor::getSyntaxIssueAtPosition(const QSynedit::BufferCoord &pos)
1530{
1531 PSyntaxIssueList lst = getSyntaxIssuesAtLine(pos.line);
1532 if (!lst)
1533 return PSyntaxIssue();
1534 foreach (const PSyntaxIssue& issue, *lst) {
1535 if (issue->startChar<=pos.ch && pos.ch<=issue->endChar)
1536 return issue;
1537 }
1538 return PSyntaxIssue();
1539}
1540
1541void Editor::onStatusChanged(QSynedit::StatusChanges changes)
1542{
1543 if ((!changes.testFlag(QSynedit::StatusChange::scReadOnly)
1544 && !changes.testFlag(QSynedit::StatusChange::scInsertMode)
1545 && (document()->count()!=mLineCount)
1546 && (document()->count()!=0) && ((mLineCount>0) || (document()->count()>1)))
1547 ||
1548 (mCurrentLineModified
1549 && !changes.testFlag(QSynedit::StatusChange::scReadOnly)
1550 && changes.testFlag(QSynedit::StatusChange::scCaretY))) {
1551 mCurrentLineModified = false;
1552 if (!changes.testFlag(QSynedit::StatusChange::scOpenFile))
1553 reparse();
1554// if (pSettings->codeCompletion().clearWhenEditorHidden()
1555// && changes.testFlag(SynStatusChange::scOpenFile)) {
1556// } else{
1557// reparse();
1558// }
1559 if (pSettings->editor().syntaxCheckWhenLineChanged())
1560 checkSyntaxInBack();
1561 reparseTodo();
1562 }
1563 mLineCount = document()->count();
1564 if (changes.testFlag(QSynedit::scModifyChanged)) {
1565 updateCaption();
1566 }
1567 if (changes.testFlag(QSynedit::scModified)) {
1568 mCurrentLineModified = true;
1569 if (mParentPageControl!=nullptr)
1570 mCanAutoSave = true;
1571 }
1572
1573 if (changes.testFlag(QSynedit::StatusChange::scCaretX)
1574 || changes.testFlag(QSynedit::StatusChange::scCaretY)) {
1575 if (mTabStopBegin >=0) {
1576 if (mTabStopY==caretY()) {
1577 if (mLineAfterTabStop.isEmpty()) {
1578 if (lineText().startsWith(mLineBeforeTabStop))
1579 mTabStopBegin = mLineBeforeTabStop.length()+1;
1580 mTabStopEnd = lineText().length()+1;
1581 } else {
1582 if (lineText().startsWith(mLineBeforeTabStop)
1583 && lineText().endsWith(mLineAfterTabStop))
1584 mTabStopBegin = mLineBeforeTabStop.length()+1;
1585 mTabStopEnd = lineText().length()
1586 - mLineAfterTabStop.length()+1;
1587 }
1588 mXOffsetSince = mTabStopEnd - caretX();
1589 if (caretX() < mTabStopBegin ||
1590 caretX() > (mTabStopEnd+1)) {
1591 mTabStopBegin = -1;
1592 }
1593 } else {
1594 if (mTabStopBegin>=0) {
1595 invalidateLine(mTabStopY);
1596 mTabStopBegin = -1;
1597 clearUserCodeInTabStops();
1598 }
1599 }
1600 } else if (!selAvail() && highlighter() && pSettings->editor().highlightMathingBraces()){
1601 invalidateLine(mHighlightCharPos1.line);
1602 invalidateLine(mHighlightCharPos2.line);
1603 mHighlightCharPos1 = QSynedit::BufferCoord{0,0};
1604 mHighlightCharPos2 = QSynedit::BufferCoord{0,0};
1605 // Is there a bracket char before us?
1606 int lineLength = lineText().length();
1607 int ch = caretX() - 2;
1608 QSynedit::BufferCoord coord;
1609 if (ch>=0 && ch<lineLength && isBraceChar(lineText()[ch]) ) {
1610 coord.ch = ch+1;
1611 coord.line = caretY();
1612 }
1613 //or after us?
1614 ch = caretX()-1;
1615 if (ch>=0 && ch<lineLength && isBraceChar(lineText()[ch]) ) {
1616 coord.ch = ch+1;
1617 coord.line = caretY();
1618 }
1619 QSynedit::PHighlighterAttribute attr;
1620 QString token;
1621 if (getHighlighterAttriAtRowCol(coord,token,attr)
1622 && attr == highlighter()->symbolAttribute()) {
1623 QSynedit::BufferCoord complementCharPos = getMatchingBracketEx(coord);
1624 if (!foldHidesLine(coord.line)
1625 && !foldHidesLine(complementCharPos.line)) {
1626 mHighlightCharPos1 = coord;
1627 mHighlightCharPos2 = complementCharPos;
1628 invalidateLine(mHighlightCharPos1.line);
1629 invalidateLine(mHighlightCharPos2.line);
1630 }
1631 }
1632
1633 }
1634 }
1635
1636 // scSelection includes anything caret related
1637 if (changes.testFlag(QSynedit::StatusChange::scSelection)) {
1638 if (!selAvail() && pSettings->editor().highlightCurrentWord()) {
1639 mCurrentHighlightedWord = wordAtCursor();
1640 } else if (selAvail() && blockBegin() == wordStart()
1641 && blockEnd() == wordEnd()){
1642 mCurrentHighlightedWord = selText();
1643 } else {
1644 mCurrentHighlightedWord = "";
1645 }
1646
1647 if (mOldHighlightedWord != mCurrentHighlightedWord) {
1648 invalidate();
1649 mOldHighlightedWord = mCurrentHighlightedWord;
1650 }
1651 pMainWindow->updateStatusbarForLineCol();
1652
1653 // Update the function tip
1654 if (pSettings->editor().showFunctionTips()) {
1655 updateFunctionTip(false);
1656 mFunctionTipTimer.stop();
1657 mFunctionTipTimer.start(500);
1658// updateFunctionTip();
1659 }
1660 }
1661
1662
1663
1664 if (changes.testFlag(QSynedit::scInsertMode) | changes.testFlag(QSynedit::scReadOnly))
1665 pMainWindow->updateForStatusbarModeInfo();
1666
1667 pMainWindow->updateEditorActions();
1668
1669 if (changes.testFlag(QSynedit::StatusChange::scCaretY) && mParentPageControl) {
1670 pMainWindow->caretList().addCaret(this,caretY(),caretX());
1671 pMainWindow->updateCaretActions();
1672 }
1673}
1674
1675void Editor::onGutterClicked(Qt::MouseButton button, int , int , int line)
1676{
1677 if (button == Qt::LeftButton) {
1678 toggleBreakpoint(line);
1679 }
1680 mGutterClickedLine = line;
1681}
1682
1683void Editor::onTipEvalValueReady(const QString& value)
1684{
1685 if (mCurrentWord == mCurrentDebugTipWord) {
1686 QString newValue;
1687 if (value.length()>100) {
1688 newValue = value.left(100) + "...";
1689 } else {
1690 newValue = value;
1691 }
1692 QToolTip::showText(QCursor::pos(), mCurrentDebugTipWord + " = " + newValue, this);
1693 }
1694 disconnect(pMainWindow->debugger(), &Debugger::evalValueReady,
1695 this, &Editor::onTipEvalValueReady);
1696}
1697
1698void Editor::onLinesDeleted(int first, int count)
1699{
1700 pMainWindow->caretList().linesDeleted(this,first,count);
1701 pMainWindow->debugger()->breakpointModel()->onFileDeleteLines(mFilename,first,count);
1702 pMainWindow->bookmarkModel()->onFileDeleteLines(mFilename,first,count);
1703 resetBreakpoints();
1704 resetBookmarks();
1705 if (!pSettings->editor().syntaxCheckWhenLineChanged()) {
1706 //todo: update syntax issues
1707 }
1708}
1709
1710void Editor::onLinesInserted(int first, int count)
1711{
1712 pMainWindow->caretList().linesInserted(this,first,count);
1713 pMainWindow->debugger()->breakpointModel()->onFileInsertLines(mFilename,first,count);
1714 pMainWindow->bookmarkModel()->onFileInsertLines(mFilename,first,count);
1715 resetBreakpoints();
1716 resetBookmarks();
1717 if (!pSettings->editor().syntaxCheckWhenLineChanged()) {
1718 //todo: update syntax issues
1719 }
1720}
1721
1722void Editor::onFunctionTipsTimer()
1723{
1724 mFunctionTipTimer.stop();
1725 updateFunctionTip(true);
1726}
1727
1728bool Editor::isBraceChar(QChar ch)
1729{
1730 switch( ch.unicode()) {
1731 case '{':
1732 case '}':
1733 case '[':
1734 case ']':
1735 case '(':
1736 case ')':
1737 return true;
1738 default:
1739 return false;
1740 }
1741}
1742
1743void Editor::resetBookmarks()
1744{
1745 mBookmarkLines=pMainWindow->bookmarkModel()->bookmarksInFile(mFilename);
1746 invalidate();
1747}
1748
1749void Editor::resetBreakpoints()
1750{
1751 mBreakpointLines.clear();
1752 foreach (const PBreakpoint& breakpoint,
1753 pMainWindow->debugger()->breakpointModel()->breakpoints()) {
1754 if (breakpoint->filename == mFilename) {
1755 mBreakpointLines.insert(breakpoint->line);
1756 }
1757 }
1758 invalidate();
1759}
1760
1761bool Editor::notParsed()
1762{
1763 if (!mParser)
1764 return true;
1765 return mParser->findFileIncludes(mFilename)==nullptr;
1766}
1767
1768void Editor::insertLine()
1769{
1770 ExecuteCommand(QSynedit::EditCommand::ecInsertLine,QChar(),nullptr);
1771}
1772
1773void Editor::deleteWord()
1774{
1775 ExecuteCommand(QSynedit::EditCommand::ecDeleteWord,QChar(),nullptr);
1776}
1777
1778void Editor::deleteToWordStart()
1779{
1780 ExecuteCommand(QSynedit::EditCommand::ecDeleteWordStart,QChar(),nullptr);
1781}
1782
1783void Editor::deleteToWordEnd()
1784{
1785 ExecuteCommand(QSynedit::EditCommand::ecDeleteWordEnd,QChar(),nullptr);
1786}
1787
1788void Editor::deleteLine()
1789{
1790 ExecuteCommand(QSynedit::EditCommand::ecDeleteLine,QChar(),nullptr);
1791}
1792
1793void Editor::duplicateLine()
1794{
1795 ExecuteCommand(QSynedit::EditCommand::ecDuplicateLine,QChar(),nullptr);
1796}
1797
1798void Editor::deleteToEOL()
1799{
1800 ExecuteCommand(QSynedit::EditCommand::ecDeleteEOL,QChar(),nullptr);
1801}
1802
1803void Editor::deleteToBOL()
1804{
1805 ExecuteCommand(QSynedit::EditCommand::ecDeleteBOL,QChar(),nullptr);
1806}
1807
1808QStringList Editor::getOwnerExpressionAndMemberAtPositionForCompletion(
1809 const QSynedit::BufferCoord &pos,
1810 QString &memberOperator,
1811 QStringList &memberExpression)
1812{
1813 QStringList expression = getExpressionAtPosition(pos);
1814 return getOwnerExpressionAndMember(expression,memberOperator,memberExpression);
1815}
1816
1817QStringList Editor::getExpressionAtPosition(
1818 const QSynedit::BufferCoord &pos)
1819{
1820 QStringList result;
1821 if (!highlighter())
1822 return result;
1823 int line = pos.line-1;
1824 int ch = pos.ch-1;
1825 int symbolMatchingLevel = 0;
1826 LastSymbolType lastSymbolType=LastSymbolType::None;
1827 QSynedit::PHighlighter highlighter;
1828 if (isNew())
1829 highlighter = highlighterManager.getCppHighlighter();
1830 else
1831 highlighter = highlighterManager.getHighlighter(mFilename);
1832 if (!highlighter)
1833 return result;
1834 while (true) {
1835 if (line>=document()->count() || line<0)
1836 break;
1837 QStringList tokens;
1838 if (line==0) {
1839 highlighter->resetState();
1840 } else {
1841 highlighter->setState(document()->ranges(line-1));
1842 }
1843 QString sLine = document()->getString(line);
1844 highlighter->setLine(sLine,line-1);
1845 while (!highlighter->eol()) {
1846 int start = highlighter->getTokenPos();
1847 QString token = highlighter->getToken();
1848 int endPos = start + token.length()-1;
1849 if (start>ch) {
1850 break;
1851 }
1852 QSynedit::PHighlighterAttribute attr = highlighter->getTokenAttribute();
1853 if ( (line == pos.line-1)
1854 && (start<=ch) && (ch<=endPos)) {
1855 if (attr==highlighter->commentAttribute() || attr == highlighter->stringAttribute()) {
1856 return result;
1857 }
1858 }
1859 if (attr!=highlighter->commentAttribute() && attr!=highlighter->whitespaceAttribute()){
1860 tokens.append(token);
1861 }
1862 highlighter->next();
1863 }
1864 for (int i=tokens.count()-1;i>=0;i--) {
1865 QString token = tokens[i];
1866 switch(lastSymbolType) {
1867 case LastSymbolType::ScopeResolutionOperator: //before '::'
1868 if (token==">") {
1869 lastSymbolType=LastSymbolType::MatchingAngleQuotation;
1870 symbolMatchingLevel=0;
1871 } else if (isIdentChar(token.front())) {
1872 lastSymbolType=LastSymbolType::Identifier;
1873 } else
1874 return result;
1875 break;
1876 case LastSymbolType::ObjectMemberOperator: //before '.'
1877 case LastSymbolType::PointerMemberOperator: //before '->'
1878 case LastSymbolType::PointerToMemberOfObjectOperator: //before '.*'
1879 case LastSymbolType::PointerToMemberOfPointerOperator: //before '->*'
1880 if (token == ")" ) {
1881 lastSymbolType=LastSymbolType::MatchingParenthesis;
1882 symbolMatchingLevel = 0;
1883 } else if (token == "]") {
1884 lastSymbolType=LastSymbolType::MatchingBracket;
1885 symbolMatchingLevel = 0;
1886 } else if (isIdentChar(token.front())) {
1887 lastSymbolType=LastSymbolType::Identifier;
1888 } else
1889 return result;
1890 break;
1891 case LastSymbolType::AsteriskSign: // before '*':
1892 if (token == '*') {
1893
1894 } else
1895 return result;
1896 break;
1897 case LastSymbolType::AmpersandSign: // before '&':
1898 return result;
1899 break;
1900 case LastSymbolType::ParenthesisMatched: //before '()'
1901// if (token == ".") {
1902// lastSymbolType=LastSymbolType::ObjectMemberOperator;
1903// } else if (token=="->") {
1904// lastSymbolType = LastSymbolType::PointerMemberOperator;
1905// } else if (token == ".*") {
1906// lastSymbolType = LastSymbolType::PointerToMemberOfObjectOperator;
1907// } else if (token == "->*"){
1908// lastSymbolType = LastSymbolType::PointerToMemberOfPointerOperator;
1909// } else if (token==">") {
1910// lastSymbolType=LastSymbolType::MatchingAngleQuotation;
1911// symbolMatchingLevel=0;
1912// } else
1913 if (token == ")" ) {
1914 lastSymbolType=LastSymbolType::MatchingParenthesis;
1915 symbolMatchingLevel = 0;
1916 } else if (token == "]") {
1917 lastSymbolType=LastSymbolType::MatchingBracket;
1918 symbolMatchingLevel = 0;
1919 } else if (token == "*") {
1920 lastSymbolType=LastSymbolType::AsteriskSign;
1921 } else if (token == "&") {
1922 lastSymbolType=LastSymbolType::AmpersandSign;
1923 } else if (isIdentChar(token.front())) {
1924 lastSymbolType=LastSymbolType::Identifier;
1925 } else
1926 return result;
1927 break;
1928 case LastSymbolType::BracketMatched: //before '[]'
1929 if (token == ")" ) {
1930 lastSymbolType=LastSymbolType::MatchingParenthesis;
1931 symbolMatchingLevel = 0;
1932 } else if (token == "]") {
1933 lastSymbolType=LastSymbolType::MatchingBracket;
1934 symbolMatchingLevel = 0;
1935 } else if (isIdentChar(token.front())) {
1936 lastSymbolType=LastSymbolType::Identifier;
1937 } else
1938 return result;
1939 break;
1940 case LastSymbolType::AngleQuotationMatched: //before '<>'
1941 if (isIdentChar(token.front())) {
1942 lastSymbolType=LastSymbolType::Identifier;
1943 } else
1944 return result;
1945 break;
1946 case LastSymbolType::None:
1947 if (token =="::") {
1948 lastSymbolType=LastSymbolType::ScopeResolutionOperator;
1949 } else if (token == ".") {
1950 lastSymbolType=LastSymbolType::ObjectMemberOperator;
1951 } else if (token=="->") {
1952 lastSymbolType = LastSymbolType::PointerMemberOperator;
1953 } else if (token == ".*") {
1954 lastSymbolType = LastSymbolType::PointerToMemberOfObjectOperator;
1955 } else if (token == "->*"){
1956 lastSymbolType = LastSymbolType::PointerToMemberOfPointerOperator;
1957 } else if (token == ")" ) {
1958 lastSymbolType=LastSymbolType::MatchingParenthesis;
1959 symbolMatchingLevel = 0;
1960 } else if (token == "]") {
1961 lastSymbolType=LastSymbolType::MatchingBracket;
1962 symbolMatchingLevel = 0;
1963 } else if (isIdentChar(token.front())) {
1964 lastSymbolType=LastSymbolType::Identifier;
1965 } else
1966 return result;
1967 break;
1968 case LastSymbolType::TildeSign:
1969 if (token =="::") {
1970 lastSymbolType=LastSymbolType::ScopeResolutionOperator;
1971 } else {
1972 // "~" must appear after "::"
1973 result.pop_front();
1974 return result;
1975 }
1976 break;;
1977 case LastSymbolType::Identifier:
1978 if (token =="::") {
1979 lastSymbolType=LastSymbolType::ScopeResolutionOperator;
1980 } else if (token == ".") {
1981 lastSymbolType=LastSymbolType::ObjectMemberOperator;
1982 } else if (token=="->") {
1983 lastSymbolType = LastSymbolType::PointerMemberOperator;
1984 } else if (token == ".*") {
1985 lastSymbolType = LastSymbolType::PointerToMemberOfObjectOperator;
1986 } else if (token == "->*"){
1987 lastSymbolType = LastSymbolType::PointerToMemberOfPointerOperator;
1988 } else if (token == "~") {
1989 lastSymbolType=LastSymbolType::TildeSign;
1990 } else if (token == "*") {
1991 lastSymbolType=LastSymbolType::AsteriskSign;
1992 } else if (token == "&") {
1993 lastSymbolType=LastSymbolType::AmpersandSign;
1994 } else
1995 return result; // stop matching;
1996 break;
1997 case LastSymbolType::MatchingParenthesis:
1998 if (token=="(") {
1999 if (symbolMatchingLevel==0) {
2000 lastSymbolType=LastSymbolType::ParenthesisMatched;
2001 } else {
2002 symbolMatchingLevel--;
2003 }
2004 } else if (token==")") {
2005 symbolMatchingLevel++;
2006 }
2007 break;
2008 case LastSymbolType::MatchingBracket:
2009 if (token=="[") {
2010 if (symbolMatchingLevel==0) {
2011 lastSymbolType=LastSymbolType::BracketMatched;
2012 } else {
2013 symbolMatchingLevel--;
2014 }
2015 } else if (token=="]") {
2016 symbolMatchingLevel++;
2017 }
2018 break;
2019 case LastSymbolType::MatchingAngleQuotation:
2020 if (token=="<") {
2021 if (symbolMatchingLevel==0) {
2022 lastSymbolType=LastSymbolType::MatchingAngleQuotation;
2023 } else {
2024 symbolMatchingLevel--;
2025 }
2026 } else if (token==">") {
2027 symbolMatchingLevel++;
2028 }
2029 break;
2030 }
2031 result.push_front(token);
2032 }
2033
2034 line--;
2035 if (line>=0)
2036 ch = document()->getString(line).length()+1;
2037 }
2038 return result;
2039}
2040
2041QString Editor::getWordForCompletionSearch(const QSynedit::BufferCoord &pos,bool permitTilde)
2042{
2043 QString result = "";
2044 QString s;
2045
2046 s = document()->getString(pos.line - 1);
2047 int len = s.length();
2048
2049 int wordBegin = pos.ch - 1 - 1; //BufferCoord::Char starts with 1
2050 int wordEnd = pos.ch - 1 - 1;
2051
2052 while ((wordBegin >= 0) && (wordBegin<len)) {
2053 if (isIdentChar(s[wordBegin])) {
2054 wordBegin--;
2055 } else if (permitTilde && s[wordBegin] == '~') { // allow destructor signs
2056 wordBegin--;
2057 } else
2058 break;
2059 }
2060 // Get end result
2061 return s.mid(wordBegin+1, wordEnd - wordBegin);
2062}
2063
2064QChar Editor::getCurrentChar()
2065{
2066 if (lineText().length()<caretX())
2067 return QChar();
2068 else
2069 return lineText().at(caretX()-1);
2070}
2071
2072bool Editor::handleSymbolCompletion(QChar key)
2073{
2074 if (!pSettings->editor().completeSymbols())
2075 return false;
2076 if (!insertMode())
2077 return false;
2078
2079 //todo: better methods to detect current caret type
2080 if (highlighter()) {
2081 if (caretX() <= 1) {
2082 if (caretY()>1) {
2083 if (highlighter()->isLastLineCommentNotFinished(document()->ranges(caretY() - 2).state))
2084 return false;
2085 if (highlighter()->isLastLineStringNotFinished(document()->ranges(caretY() - 2).state)
2086 && (key!='\"') && (key!='\''))
2087 return false;
2088 }
2089 } else {
2090 QSynedit::BufferCoord HighlightPos = QSynedit::BufferCoord{caretX()-1, caretY()};
2091 // Check if that line is highlighted as comment
2092 QSynedit::PHighlighterAttribute Attr;
2093 QString Token;
2094 bool tokenFinished;
2095 QSynedit::TokenType tokenType;
2096 if (getHighlighterAttriAtRowCol(HighlightPos, Token, tokenFinished, tokenType,Attr)) {
2097 if ((tokenType == QSynedit::TokenType::Comment) && (!tokenFinished))
2098 return false;
2099 if ((tokenType == QSynedit::TokenType::String) && (!tokenFinished)
2100 && (key!='\'') && (key!='\"') && (key!='(') && (key!=')'))
2101 return false;
2102 if (( key=='<' || key =='>') && (mParser && !mParser->isIncludeLine(lineText())))
2103 return false;
2104 if ((key == '\'') && (Attr->name() == "SYNS_AttrNumber"))
2105 return false;
2106 }
2107 }
2108 }
2109
2110 // Check if that line is highlighted as string or character or comment
2111 // if (Attr = fText.Highlighter.StringAttribute) or (Attr = fText.Highlighter.CommentAttribute) or SameStr(Attr.Name,
2112 // 'Character') then
2113 // Exit;
2114
2115 QuoteStatus status;
2116 switch(key.unicode()) {
2117 case '(':
2118 if (pSettings->editor().completeParenthese()) {
2119 return handleParentheseCompletion();
2120 }
2121 return false;
2122 case ')':
2123 if (selAvail())
2124 return false;
2125 if (pSettings->editor().completeParenthese() && pSettings->editor().overwriteSymbols()) {
2126 return handleParentheseSkip();
2127 }
2128 return false;
2129 case '[':
2130 if (pSettings->editor().completeBracket()) {
2131 return handleBracketCompletion();
2132 }
2133 return false;
2134 case ']':
2135 if (selAvail())
2136 return false;
2137 if (pSettings->editor().completeBracket() && pSettings->editor().overwriteSymbols()) {
2138 return handleBracketSkip();
2139 }
2140 return false;
2141 case '*':
2142 status = getQuoteStatus();
2143 if (pSettings->editor().completeComment() && (status == QuoteStatus::NotQuote)) {
2144 return handleMultilineCommentCompletion();
2145 }
2146 return false;
2147 case '{':
2148 if (pSettings->editor().completeBrace()) {
2149 return handleBraceCompletion();
2150 }
2151 return false;
2152 case '}':
2153 if (selAvail())
2154 return false;
2155 if (pSettings->editor().completeBrace() && pSettings->editor().overwriteSymbols()) {
2156 return handleBraceSkip();
2157 }
2158 return false;
2159 case '\'':
2160 if (pSettings->editor().completeSingleQuote()) {
2161 return handleSingleQuoteCompletion();
2162 }
2163 return false;
2164 case '\"':
2165 if (pSettings->editor().completeDoubleQuote()) {
2166 return handleDoubleQuoteCompletion();
2167 }
2168 return false;
2169 case '<':
2170 if (selAvail())
2171 return false;
2172 if (pSettings->editor().completeGlobalInclude()) { // #include <>
2173 return handleGlobalIncludeCompletion();
2174 }
2175 return false;
2176 case '>':
2177 if (selAvail())
2178 return false;
2179 if (pSettings->editor().completeGlobalInclude() && pSettings->editor().overwriteSymbols()) { // #include <>
2180 return handleGlobalIncludeSkip();
2181 }
2182 return false;
2183 }
2184 return false;
2185}
2186
2187bool Editor::handleParentheseCompletion()
2188{
2189 QuoteStatus status = getQuoteStatus();
2190 if (status == QuoteStatus::RawString || status == QuoteStatus::NotQuote) {
2191 if (selAvail() && status == QuoteStatus::NotQuote) {
2192 QString text=selText();
2193 beginUpdate();
2194 beginUndoBlock();
2195 commandProcessor(QSynedit::EditCommand::ecChar,'(');
2196 setSelText(text);
2197 commandProcessor(QSynedit::EditCommand::ecChar,')');
2198 endUndoBlock();
2199 endUpdate();
2200 } else {
2201 beginUpdate();
2202 beginUndoBlock();
2203 commandProcessor(QSynedit::EditCommand::ecChar,'(');
2204 QSynedit::BufferCoord oldCaret = caretXY();
2205 commandProcessor(QSynedit::EditCommand::ecChar,')');
2206 setCaretXY(oldCaret);
2207 endUndoBlock();
2208 endUpdate();
2209 }
2210 return true;
2211 }
2212// if (status == QuoteStatus::NotQuote) && FunctionTipAllowed then
2213 // fFunctionTip.Activated := true;
2214 return false;
2215}
2216
2217bool Editor::handleParentheseSkip()
2218{
2219 if (getCurrentChar() != ')')
2220 return false;
2221 QuoteStatus status = getQuoteStatus();
2222 if (status == QuoteStatus::RawStringNoEscape) {
2223 setCaretXY( QSynedit::BufferCoord{caretX() + 1, caretY()}); // skip over
2224 return true;
2225 }
2226 if (status != QuoteStatus::NotQuote)
2227 return false;
2228
2229 if (document()->count()==0)
2230 return false;
2231 if (highlighter()) {
2232 QSynedit::HighlighterState lastLineState = document()->ranges(document()->count()-1);
2233 if (lastLineState.parenthesisLevel==0) {
2234 setCaretXY( QSynedit::BufferCoord{caretX() + 1, caretY()}); // skip over
2235 return true;
2236 }
2237 } else {
2238 QSynedit::BufferCoord pos = getMatchingBracket();
2239 if (pos.line != 0) {
2240 setCaretXY( QSynedit::BufferCoord{caretX() + 1, caretY()}); // skip over
2241 return true;
2242 }
2243 }
2244 return false;
2245}
2246
2247bool Editor::handleBracketCompletion()
2248{
2249// QuoteStatus status = getQuoteStatus();
2250// if (status == QuoteStatus::RawString || status == QuoteStatus::NotQuote) {
2251 QuoteStatus status = getQuoteStatus();
2252 if (selAvail() && status == QuoteStatus::NotQuote) {
2253 QString text=selText();
2254 beginUpdate();
2255 beginUndoBlock();
2256 commandProcessor(QSynedit::EditCommand::ecChar,'[');
2257 setSelText(text);
2258 commandProcessor(QSynedit::EditCommand::ecChar,']');
2259 endUndoBlock();
2260 endUpdate();
2261 } else {
2262 beginUpdate();
2263 beginUndoBlock();
2264 commandProcessor(QSynedit::EditCommand::ecChar,'[');
2265 QSynedit::BufferCoord oldCaret = caretXY();
2266 commandProcessor(QSynedit::EditCommand::ecChar,']');
2267 setCaretXY(oldCaret);
2268 endUndoBlock();
2269 endUpdate();
2270 }
2271 return true;
2272 // }
2273}
2274
2275bool Editor::handleBracketSkip()
2276{
2277 if (getCurrentChar() != ']')
2278 return false;
2279
2280 if (document()->count()==0)
2281 return false;
2282 if (highlighter()) {
2283 QSynedit::HighlighterState lastLineState = document()->ranges(document()->count()-1);
2284 if (lastLineState.bracketLevel==0) {
2285 setCaretXY( QSynedit::BufferCoord{caretX() + 1, caretY()}); // skip over
2286 return true;
2287 }
2288 } else {
2289 QSynedit::BufferCoord pos = getMatchingBracket();
2290 if (pos.line != 0) {
2291 setCaretXY( QSynedit::BufferCoord{caretX() + 1, caretY()}); // skip over
2292 return true;
2293 }
2294 }
2295 return false;
2296}
2297
2298bool Editor::handleMultilineCommentCompletion()
2299{
2300 if ((caretX()-2 < lineText().length()) && (lineText()[caretX() - 2] == '/')) {
2301 QString text=selText();
2302 beginUpdate();
2303 beginUndoBlock();
2304 commandProcessor(QSynedit::EditCommand::ecChar,'*');
2305 QSynedit::BufferCoord oldCaret;
2306 if (text.isEmpty())
2307 oldCaret = caretXY();
2308 else
2309 setSelText(text);
2310 commandProcessor(QSynedit::EditCommand::ecChar,'*');
2311 commandProcessor(QSynedit::EditCommand::ecChar,'/');
2312 if (text.isEmpty())
2313 setCaretXY(oldCaret);
2314 endUndoBlock();
2315 endUpdate();
2316 return true;
2317 }
2318 return false;
2319}
2320
2321bool Editor::handleBraceCompletion()
2322{
2323 QString s = lineText().trimmed();
2324 int i= caretY()-2;
2325 while ((s.isEmpty()) && (i>=0)) {
2326 s=document()->getString(i);
2327 i--;
2328 }
2329 QString text=selText();
2330 beginUpdate();
2331 beginUndoBlock();
2332 commandProcessor(QSynedit::EditCommand::ecChar,'{');
2333 QSynedit::BufferCoord oldCaret;
2334 if (text.isEmpty()) {
2335 oldCaret = caretXY();
2336 } else {
2337 commandProcessor(QSynedit::EditCommand::ecInsertLine);
2338 setSelText(text);
2339 commandProcessor(QSynedit::EditCommand::ecInsertLine);
2340 }
2341 commandProcessor(QSynedit::EditCommand::ecChar,'}');
2342 if (
2343 ( (s.startsWith("struct")
2344 || s.startsWith("class")
2345 || s.startsWith("union")
2346 || s.startsWith("typedef")
2347 || s.startsWith("public")
2348 || s.startsWith("private")
2349 || s.startsWith("enum") )
2350 && !s.contains(';')
2351 ) || s.endsWith('=')) {
2352 commandProcessor(QSynedit::EditCommand::ecChar,';');
2353 }
2354 if (text.isEmpty())
2355 setCaretXY(oldCaret);
2356 endUndoBlock();
2357 endUpdate();
2358 return true;
2359}
2360
2361bool Editor::handleBraceSkip()
2362{
2363 if (getCurrentChar() != '}')
2364 return false;
2365
2366 if (document()->count()==0)
2367 return false;
2368 if (highlighter()) {
2369 QSynedit::HighlighterState lastLineState = document()->ranges(document()->count()-1);
2370 if (lastLineState.braceLevel==0) {
2371 bool oldInsertMode = insertMode();
2372 setInsertMode(false); //set mode to overwrite
2373 commandProcessor(QSynedit::EditCommand::ecChar,'}');
2374 setInsertMode(oldInsertMode);
2375 return true;
2376 }
2377 } else {
2378 QSynedit::BufferCoord pos = getMatchingBracket();
2379 if (pos.line != 0) {
2380 bool oldInsertMode = insertMode();
2381 setInsertMode(false); //set mode to overwrite
2382 commandProcessor(QSynedit::EditCommand::ecChar,'}');
2383 setInsertMode(oldInsertMode);
2384 return true;
2385 }
2386 }
2387 return false;
2388}
2389
2390bool Editor::handleSingleQuoteCompletion()
2391{
2392 QuoteStatus status = getQuoteStatus();
2393 QChar ch = getCurrentChar();
2394 if (ch == '\'') {
2395 if (status == QuoteStatus::SingleQuote && !selAvail()) {
2396 setCaretXY( QSynedit::BufferCoord{caretX() + 1, caretY()}); // skip over
2397 return true;
2398 }
2399 } else {
2400 if (status == QuoteStatus::NotQuote) {
2401 if (selAvail()) {
2402 QString text=selText();
2403 beginUpdate();
2404 beginUndoBlock();
2405 commandProcessor(QSynedit::EditCommand::ecChar,'\'');
2406 setSelText(text);
2407 commandProcessor(QSynedit::EditCommand::ecChar,'\'');
2408 endUndoBlock();
2409 endUpdate();
2410 return true;
2411 }
2412 if (ch == 0 || highlighter()->isWordBreakChar(ch) || highlighter()->isSpaceChar(ch)) {
2413 // insert ''
2414 beginUpdate();
2415 beginUndoBlock();
2416 commandProcessor(QSynedit::EditCommand::ecChar,'\'');
2417 QSynedit::BufferCoord oldCaret = caretXY();
2418 commandProcessor(QSynedit::EditCommand::ecChar,'\'');
2419 setCaretXY(oldCaret);
2420 endUndoBlock();
2421 endUpdate();
2422 return true;
2423 }
2424 }
2425 }
2426 return false;
2427}
2428
2429bool Editor::handleDoubleQuoteCompletion()
2430{
2431 QuoteStatus status = getQuoteStatus();
2432 QChar ch = getCurrentChar();
2433 if (ch == '"') {
2434 if ((status == QuoteStatus::DoubleQuote || status == QuoteStatus::RawString)
2435 && !selAvail()) {
2436 setCaretXY( QSynedit::BufferCoord{caretX() + 1, caretY()}); // skip over
2437 return true;
2438 }
2439 } else {
2440 if (status == QuoteStatus::NotQuote) {
2441 if (selAvail()) {
2442 QString text=selText();
2443 beginUpdate();
2444 beginUndoBlock();
2445 commandProcessor(QSynedit::EditCommand::ecChar,'"');
2446 setSelText(text);
2447 commandProcessor(QSynedit::EditCommand::ecChar,'"');
2448 endUndoBlock();
2449 endUpdate();
2450 return true;
2451 }
2452 if ((ch == 0) || highlighter()->isWordBreakChar(ch) || highlighter()->isSpaceChar(ch)) {
2453 // insert ""
2454 beginUpdate();
2455 beginUndoBlock();
2456 commandProcessor(QSynedit::EditCommand::ecChar,'"');
2457 QSynedit::BufferCoord oldCaret = caretXY();
2458 commandProcessor(QSynedit::EditCommand::ecChar,'"');
2459 setCaretXY(oldCaret);
2460 endUndoBlock();
2461 endUpdate();
2462 return true;
2463 }
2464 }
2465 }
2466 return false;
2467}
2468
2469bool Editor::handleGlobalIncludeCompletion()
2470{
2471 if (!lineText().startsWith('#'))
2472 return false;
2473 QString s= lineText().mid(1).trimmed();
2474 if (!s.startsWith("include")) //it's not #include
2475 return false;
2476 beginUpdate();
2477 beginUndoBlock();
2478 commandProcessor(QSynedit::EditCommand::ecChar,'<');
2479 QSynedit::BufferCoord oldCaret = caretXY();
2480 commandProcessor(QSynedit::EditCommand::ecChar,'>');
2481 setCaretXY(oldCaret);
2482 endUpdate();
2483 endUndoBlock();
2484 return true;
2485}
2486
2487bool Editor::handleGlobalIncludeSkip()
2488{
2489 if (getCurrentChar()!='>')
2490 return false;
2491 QString s= lineText().mid(1).trimmed();
2492 if (!s.startsWith("include")) //it's not #include
2493 return false;
2494 QSynedit::BufferCoord pos = getMatchingBracket();
2495 if (pos.line != 0) {
2496 setCaretXY(QSynedit::BufferCoord{caretX() + 1, caretY()}); // skip over
2497 return true;
2498 }
2499 return false;
2500}
2501
2502bool Editor::handleCodeCompletion(QChar key)
2503{
2504 if (!mCompletionPopup->isEnabled())
2505 return false;
2506 if (mParser) {
2507 switch(key.unicode()) {
2508 case '.':
2509 setSelText(key);
2510 showCompletion("",false);
2511 return true;
2512 case '>':
2513 setSelText(key);
2514 if ((caretX() > 2) && (lineText().length() >= 2) &&
2515 (lineText()[caretX() - 3] == '-'))
2516 showCompletion("",false);
2517 return true;
2518 case ':':
2519 ExecuteCommand(QSynedit::EditCommand::ecChar,':',nullptr);
2520 //setSelText(key);
2521 if ((caretX() > 2) && (lineText().length() >= 2) &&
2522 (lineText()[caretX() - 3] == ':'))
2523 showCompletion("",false);
2524 return true;
2525 case '/':
2526 case '\\':
2527 setSelText(key);
2528 if (mParser->isIncludeLine(lineText())) {
2529 showHeaderCompletion(false);
2530 }
2531 return true;
2532 default:
2533 return false;
2534 }
2535 }
2536 return false;
2537}
2538
2539void Editor::initParser()
2540{
2541 mParser = std::make_shared<CppParser>();
2542 if (mUseCppSyntax) {
2543 mParser->setLanguage(ParserLanguage::CPlusPlus);
2544 } else {
2545 mParser->setLanguage(ParserLanguage::C);
2546 }
2547 mParser->setOnGetFileStream(
2548 std::bind(
2549 &EditorList::getContentFromOpenedEditor,pMainWindow->editorList(),
2550 std::placeholders::_1, std::placeholders::_2));
2551 resetCppParser(mParser);
2552 mParser->setEnabled(
2553 pSettings->codeCompletion().enabled() &&
2554 (highlighter() && highlighter()->getClass() == QSynedit::HighlighterClass::CppHighlighter));
2555}
2556
2557Editor::QuoteStatus Editor::getQuoteStatus()
2558{
2559 QuoteStatus Result = QuoteStatus::NotQuote;
2560 if (!highlighter())
2561 return Result;
2562 if ((caretY()>1) && highlighter()->isLastLineStringNotFinished(document()->ranges(caretY() - 2).state))
2563 Result = QuoteStatus::DoubleQuote;
2564
2565 QString Line = document()->getString(caretY()-1);
2566 int posX = caretX()-1;
2567 if (posX >= Line.length()) {
2568 posX = Line.length()-1;
2569 }
2570 for (int i=0; i<posX;i++) {
2571 if (i+1<Line.length() && (Line[i] == 'R') && (Line[i+1] == '"') && (Result == QuoteStatus::NotQuote)) {
2572 Result = QuoteStatus::RawString;
2573 i++; // skip R
2574 } else if (Line[i] == '(') {
2575 switch(Result) {
2576 case QuoteStatus::RawString:
2577 Result=QuoteStatus::RawStringNoEscape;
2578 break;
2579 default:
2580 break;
2581 }
2582 } else if (Line[i] == ')') {
2583 switch(Result) {
2584 case QuoteStatus::RawStringNoEscape:
2585 Result=QuoteStatus::RawString;
2586 break;
2587 default:
2588 break;
2589 }
2590 } else if (Line[i] == '"') {
2591 switch(Result) {
2592 case QuoteStatus::NotQuote:
2593 Result = QuoteStatus::DoubleQuote;
2594 break;
2595 case QuoteStatus::SingleQuote:
2596 Result = QuoteStatus::SingleQuote;
2597 break;
2598 case QuoteStatus::SingleQuoteEscape:
2599 Result = QuoteStatus::SingleQuote;
2600 break;
2601 case QuoteStatus::DoubleQuote:
2602 Result = QuoteStatus::NotQuote;
2603 break;
2604 case QuoteStatus::DoubleQuoteEscape:
2605 Result = QuoteStatus::DoubleQuote;
2606 break;
2607 case QuoteStatus::RawString:
2608 Result=QuoteStatus::NotQuote;
2609 break;
2610 default:
2611 break;
2612 }
2613 } else if (Line[i] == '\'') {
2614 switch(Result) {
2615 case QuoteStatus::NotQuote:
2616 Result = QuoteStatus::SingleQuote;
2617 break;
2618 case QuoteStatus::SingleQuote:
2619 Result = QuoteStatus::NotQuote;
2620 break;
2621 case QuoteStatus::SingleQuoteEscape:
2622 Result = QuoteStatus::SingleQuote;
2623 break;
2624 case QuoteStatus::DoubleQuote:
2625 Result = QuoteStatus::DoubleQuote;
2626 break;
2627 case QuoteStatus::DoubleQuoteEscape:
2628 Result = QuoteStatus::DoubleQuote;
2629 break;
2630 default:
2631 break;
2632 }
2633 } else if (Line[i] == '\\') {
2634 switch(Result) {
2635 case QuoteStatus::NotQuote:
2636 Result = QuoteStatus::NotQuote;
2637 break;
2638 case QuoteStatus::SingleQuote:
2639 Result = QuoteStatus::SingleQuoteEscape;
2640 break;
2641 case QuoteStatus::SingleQuoteEscape:
2642 Result = QuoteStatus::SingleQuote;
2643 break;
2644 case QuoteStatus::DoubleQuote:
2645 Result = QuoteStatus::DoubleQuoteEscape;
2646 break;
2647 case QuoteStatus::DoubleQuoteEscape:
2648 Result = QuoteStatus::DoubleQuote;
2649 break;
2650 default:
2651 break;
2652 }
2653 } else {
2654 switch(Result) {
2655 case QuoteStatus::NotQuote:
2656 Result = QuoteStatus::NotQuote;
2657 break;
2658 case QuoteStatus::SingleQuote:
2659 Result = QuoteStatus::SingleQuote;
2660 break;
2661 case QuoteStatus::SingleQuoteEscape:
2662 Result = QuoteStatus::SingleQuote;
2663 break;
2664 case QuoteStatus::DoubleQuote:
2665 Result = QuoteStatus::DoubleQuote;
2666 break;
2667 case QuoteStatus::DoubleQuoteEscape:
2668 Result = QuoteStatus::DoubleQuote;
2669 break;
2670 default:
2671 break;
2672 }
2673 }
2674 }
2675 return Result;
2676}
2677
2678void Editor::reparse()
2679{
2680 if (!highlighter())
2681 return;
2682 if (highlighter()->language() != QSynedit::HighlighterLanguage::Cpp
2683 && highlighter()->language() != QSynedit::HighlighterLanguage::GLSL)
2684 return;
2685 if (mParser)
2686 mParser->setEnabled(pSettings->codeCompletion().enabled());
2687 ParserLanguage language = mUseCppSyntax?ParserLanguage::CPlusPlus:ParserLanguage::C;
2688 if (language!=mParser->language() && !mInProject) {
2689 mParser->setLanguage(language);
2690 resetCppParser(mParser);
2691 }
2692 parseFile(mParser,mFilename,mInProject);
2693}
2694
2695void Editor::reparseTodo()
2696{
2697 if (!highlighter())
2698 return;
2699 pMainWindow->todoParser()->parseFile(mFilename);
2700}
2701
2702void Editor::insertString(const QString &value, bool moveCursor)
2703{
2704 beginUpdate();
2705 auto action = finally([this]{
2706 endUpdate();
2707 });
2708
2709 QSynedit::BufferCoord oldCursorPos = caretXY();
2710 setSelText(value);
2711 if (!moveCursor) {
2712 setCaretXY(oldCursorPos);
2713 }
2714}
2715
2716void Editor::insertCodeSnippet(const QString &code)
2717{
2718 clearUserCodeInTabStops();
2719 mXOffsetSince = 0;
2720 mTabStopBegin = -1;
2721 mTabStopEnd = -1;
2722 mTabStopY =0;
2723 mLineBeforeTabStop = "";
2724 mLineAfterTabStop = "";
2725 // prevent lots of repaints
2726 beginUpdate();
2727 auto action = finally([this]{
2728 endUpdate();
2729 });
2730 if (selAvail())
2731 setSelText("");
2732 QStringList sl = textToLines(parseMacros(code));
2733 int lastI=0;
2734// int spaceCount = GetLeftSpacing(
2735// leftSpaces(lineText()),true).length();
2736 QStringList newSl;
2737 for (int i=0;i<sl.count();i++) {
2738 int lastPos = 0;
2739 QString s = sl[i];
2740 if (i>0)
2741 lastPos = countLeadingWhitespaceChars(s);
2742 while (true) {
2743 int insertPos = s.indexOf(USER_CODE_IN_INSERT_POS);
2744 if (insertPos < 0) // no %INSERT% macro in this line now
2745 break;
2746 PTabStop p = std::make_shared<TabStop>();
2747 s.remove(insertPos, QString(USER_CODE_IN_INSERT_POS).length());
2748 //insertPos--;
2749 p->x = insertPos - lastPos;
2750 p->endX = p->x ;
2751 p->y = i - lastI;
2752 lastPos = insertPos;
2753 lastI = i;
2754 mUserCodeInTabStops.append(p);
2755 }
2756 lastPos = 0;
2757 if (i>0)
2758 lastPos = countLeadingWhitespaceChars(s);
2759 while (true) {
2760 int insertPos = s.indexOf(USER_CODE_IN_REPL_POS_BEGIN);
2761 if (insertPos < 0) // no %INSERT% macro in this line now
2762 break;
2763 PTabStop p = std::make_shared<TabStop>();
2764 s.remove(insertPos, QString(USER_CODE_IN_REPL_POS_BEGIN).length());
2765 //insertPos--;
2766 p->x = insertPos - lastPos;
2767
2768 int insertEndPos = insertPos +
2769 s.mid(insertPos).indexOf(USER_CODE_IN_REPL_POS_END);
2770 if (insertEndPos < insertPos) {
2771 p->endX = s.length();
2772 } else {
2773 s.remove(insertEndPos, QString(USER_CODE_IN_REPL_POS_END).length());
2774 //insertEndPos--;
2775 p->endX = insertEndPos - lastPos;
2776 }
2777 p->y=i-lastI;
2778 lastPos = insertEndPos;
2779 lastI = i;
2780 mUserCodeInTabStops.append(p);
2781 }
2782 newSl.append(s);
2783 }
2784
2785 QSynedit::BufferCoord cursorPos = caretXY();
2786 QString s = linesToText(newSl);
2787// if EndsStr(#13#10,s) then
2788// Delete(s,Length(s)-1,2)
2789// else if EndsStr(#10, s) then
2790// Delete(s,Length(s),1);
2791 setSelText(s);
2792 if (mUserCodeInTabStops.count()>0) {
2793 setCaretXY(cursorPos); //restore cursor pos before insert
2794 mTabStopBegin = caretX();
2795 mTabStopEnd = caretX();
2796 popUserCodeInTabStops();
2797 }
2798 if (!code.isEmpty()) {
2799 mLastIdCharPressed = 0;
2800 }
2801}
2802
2803void Editor::print()
2804{
2805 QPrinter printer;
2806
2807 QPrintDialog dialog(&printer, this);
2808 dialog.setWindowTitle(tr("Print Document"));
2809// if (editor->selAvail())
2810// dialog.addEnabledOption(QAbstractPrintDialog::PrintSelection);
2811 if (dialog.exec() != QDialog::Accepted) {
2812 return;
2813 }
2814 QTextDocument doc;
2815// if (editor->selAvail()) {
2816// doc.setPlainText(editor->selText());
2817// } else {
2818 QStringList lst = contents();
2819 for (int i=0;i<lst.length();i++) {
2820 int columns = 0;
2821 QString line = lst[i];
2822 QString newLine;
2823 for (QChar ch:line) {
2824 if (ch=='\t') {
2825 int charCol = tabWidth() - (columns % tabWidth());
2826 newLine += QString(charCol,' ');
2827 columns += charCol;
2828 } else {
2829 newLine+=ch;
2830 columns+=charColumns(ch);
2831 }
2832 }
2833 lst[i]=newLine;
2834 }
2835 doc.setDefaultFont(font());
2836 doc.setPlainText(lst.join(lineBreak()));
2837 doc.print(&printer);
2838}
2839
2840void Editor::exportAsRTF(const QString &rtfFilename)
2841{
2842 QSynedit::SynRTFExporter exporter(pCharsetInfoManager->getDefaultSystemEncoding());
2843 exporter.setTitle(extractFileName(rtfFilename));
2844 exporter.setExportAsText(true);
2845 exporter.setUseBackground(pSettings->editor().copyRTFUseBackground());
2846 exporter.setFont(font());
2847 QSynedit::PHighlighter hl = highlighter();
2848 if (!pSettings->editor().copyRTFUseEditorColor()) {
2849 hl = highlighterManager.copyHighlighter(highlighter());
2850 highlighterManager.applyColorScheme(hl,pSettings->editor().copyRTFColorScheme());
2851 }
2852 exporter.setHighlighter(hl);
2853 exporter.setOnFormatToken(std::bind(&Editor::onExportedFormatToken,
2854 this,
2855 std::placeholders::_1,
2856 std::placeholders::_2,
2857 std::placeholders::_3,
2858 std::placeholders::_4,
2859 std::placeholders::_5
2860 ));
2861 exporter.ExportAll(document());
2862 exporter.SaveToFile(rtfFilename);
2863}
2864
2865void Editor::exportAsHTML(const QString &htmlFilename)
2866{
2867 QSynedit::SynHTMLExporter exporter(tabWidth(), pCharsetInfoManager->getDefaultSystemEncoding());
2868 exporter.setTitle(extractFileName(htmlFilename));
2869 exporter.setExportAsText(false);
2870 exporter.setUseBackground(pSettings->editor().copyHTMLUseBackground());
2871 exporter.setFont(font());
2872 QSynedit::PHighlighter hl = highlighter();
2873 if (!pSettings->editor().copyHTMLUseEditorColor()) {
2874 hl = highlighterManager.copyHighlighter(highlighter());
2875 highlighterManager.applyColorScheme(hl,pSettings->editor().copyHTMLColorScheme());
2876 }
2877 exporter.setHighlighter(hl);
2878 exporter.setOnFormatToken(std::bind(&Editor::onExportedFormatToken,
2879 this,
2880 std::placeholders::_1,
2881 std::placeholders::_2,
2882 std::placeholders::_3,
2883 std::placeholders::_4,
2884 std::placeholders::_5
2885 ));
2886 exporter.ExportAll(document());
2887 exporter.SaveToFile(htmlFilename);
2888}
2889
2890void Editor::showCompletion(const QString& preWord,bool autoComplete)
2891{
2892 if (pMainWindow->functionTip()->isVisible()) {
2893 pMainWindow->functionTip()->hide();
2894 }
2895 if (!pSettings->codeCompletion().enabled())
2896 return;
2897 if (!mParser || !mParser->enabled())
2898 return;
2899
2900 if (!highlighter())
2901 return;
2902
2903 if (mCompletionPopup->isVisible()) // already in search, don't do it again
2904 return;
2905
2906 QString word="";
2907
2908 QString s;
2909 QSynedit::PHighlighterAttribute attr;
2910 bool tokenFinished;
2911 QSynedit::TokenType tokenType;
2912 QSynedit::BufferCoord pBeginPos, pEndPos;
2913 if (getHighlighterAttriAtRowCol(
2914 QSynedit::BufferCoord{caretX() - 1,
2915 caretY()}, s, tokenFinished,tokenType, attr)) {
2916 if (tokenType == QSynedit::TokenType::PreprocessDirective) {//Preprocessor
2917 word = getWordAtPosition(this,caretXY(),pBeginPos,pEndPos, WordPurpose::wpDirective);
2918 if (!word.startsWith('#')) {
2919 word = "";
2920 }
2921 } else if (tokenType == QSynedit::TokenType::Comment) { //Comment, javadoc tag
2922 word = getWordAtPosition(this,caretXY(),pBeginPos,pEndPos, WordPurpose::wpJavadoc);
2923 if (!word.startsWith('@')) {
2924 return;
2925 }
2926 } else if (
2927 (tokenType != QSynedit::TokenType::Symbol) &&
2928 (tokenType != QSynedit::TokenType::Space) &&
2929 (tokenType != QSynedit::TokenType::Keyword) &&
2930 (tokenType != QSynedit::TokenType::Identifier)
2931 ) {
2932 return;
2933 }
2934 }
2935
2936 // Position it at the top of the next line
2937 QPoint p = rowColumnToPixels(displayXY());
2938 p+=QPoint(0,textHeight()+2);
2939 mCompletionPopup->move(mapToGlobal(p));
2940
2941 mCompletionPopup->setRecordUsage(pSettings->codeCompletion().recordUsage());
2942 mCompletionPopup->setSortByScope(pSettings->codeCompletion().sortByScope());
2943 mCompletionPopup->setShowKeywords(pSettings->codeCompletion().showKeywords());
2944 mCompletionPopup->setShowCodeSnippets(pSettings->codeCompletion().showCodeIns());
2945 mCompletionPopup->setHideSymbolsStartWithUnderline(pSettings->codeCompletion().hideSymbolsStartsWithUnderLine());
2946 mCompletionPopup->setHideSymbolsStartWithTwoUnderline(pSettings->codeCompletion().hideSymbolsStartsWithTwoUnderLine());
2947 if (pSettings->codeCompletion().showCodeIns()) {
2948 mCompletionPopup->setCodeSnippets(pMainWindow->codeSnippetManager()->snippets());
2949 }
2950 mCompletionPopup->setIgnoreCase(pSettings->codeCompletion().ignoreCase());
2951 mCompletionPopup->resize(pSettings->codeCompletion().width(),
2952 pSettings->codeCompletion().height());
2953// fCompletionBox.CodeInsList := dmMain.CodeInserts.ItemList;
2954// fCompletionBox.SymbolUsage := dmMain.SymbolUsage;
2955// fCompletionBox.ShowCount := devCodeCompletion.MaxCount;
2956 //Set Font size;
2957 mCompletionPopup->setFont(font());
2958 // Redirect key presses to completion box if applicable
2959 //todo:
2960 mCompletionPopup->setKeypressedCallback([this](QKeyEvent *event)->bool{
2961 return onCompletionKeyPressed(event);
2962 });
2963 mCompletionPopup->setParser(mParser);
2964 mCompletionPopup->setUseCppKeyword(mUseCppSyntax);
2965 pMainWindow->functionTip()->hide();
2966 mCompletionPopup->show();
2967
2968 // Scan the current function body
2969 mCompletionPopup->setCurrentStatement(
2970 mParser->findAndScanBlockAt(mFilename, caretY())
2971 );
2972
2973 QSet<QString> keywords;
2974 if (highlighter()) {
2975 if (highlighter()->language() != QSynedit::HighlighterLanguage::Cpp ) {
2976 keywords = highlighter()->keywords();
2977 } else if (mUseCppSyntax) {
2978 foreach (const QString& keyword, CppKeywords.keys()) {
2979 keywords.insert(keyword);
2980 }
2981 } else {
2982 keywords = CKeywords;
2983 }
2984 }
2985
2986 if (word.isEmpty()) {
2987 //word=getWordAtPosition(this,caretXY(),pBeginPos,pEndPos, WordPurpose::wpCompletion);
2988 QString memberOperator;
2989 QStringList memberExpression;
2990 QSynedit::BufferCoord pos = caretXY();
2991 pos.ch--;
2992 QStringList ownerExpression = getOwnerExpressionAndMemberAtPositionForCompletion(
2993 pos,
2994 memberOperator,
2995 memberExpression);
2996// qDebug()<<ownerExpression<<memberExpression;
2997 word = memberExpression.join("");
2998 mCompletionPopup->prepareSearch(
2999 preWord,
3000 ownerExpression,
3001 memberOperator,
3002 memberExpression,
3003 mFilename,
3004 caretY(),
3005 keywords);
3006 } else {
3007 QStringList memberExpression;
3008 memberExpression.append(word);
3009 mCompletionPopup->prepareSearch(preWord,
3010 QStringList(),
3011 "",
3012 memberExpression, mFilename, caretY(),keywords);
3013 }
3014
3015 // Filter the whole statement list
3016 if (mCompletionPopup->search(word, autoComplete)) { //only one suggestion and it's not input while typing
3017 completionInsert(pSettings->codeCompletion().appendFunc());
3018 }
3019}
3020
3021void Editor::showHeaderCompletion(bool autoComplete, bool forceShow)
3022{
3023 if (!pSettings->codeCompletion().enabled())
3024 return;
3025// if not devCodeCompletion.Enabled then
3026// Exit;
3027
3028 if (!forceShow && mHeaderCompletionPopup->isVisible()) // already in search, don't do it again
3029 return;
3030
3031 // Position it at the top of the next line
3032 QPoint p = rowColumnToPixels(displayXY());
3033 p.setY(p.y() + textHeight() + 2);
3034 mHeaderCompletionPopup->move(mapToGlobal(p));
3035
3036
3037 mHeaderCompletionPopup->setIgnoreCase(pSettings->codeCompletion().ignoreCase());
3038 mHeaderCompletionPopup->resize(pSettings->codeCompletion().width(),
3039 pSettings->codeCompletion().height());
3040 //Set Font size;
3041 mHeaderCompletionPopup->setFont(font());
3042
3043 // Redirect key presses to completion box if applicable
3044 mHeaderCompletionPopup->setKeypressedCallback([this](QKeyEvent* event)->bool{
3045 return onHeaderCompletionKeyPressed(event);
3046 });
3047 mHeaderCompletionPopup->setParser(mParser);
3048
3049 QSynedit::BufferCoord pBeginPos,pEndPos;
3050 QString word = getWordAtPosition(this,caretXY(),pBeginPos,pEndPos,
3051 WordPurpose::wpHeaderCompletionStart);
3052
3053 if (word.isEmpty())
3054 return;
3055
3056 if (!word.startsWith('"') && !word.startsWith('<'))
3057 return;
3058
3059 if (word.lastIndexOf('"')>0 || word.lastIndexOf('>')>0)
3060 return;
3061
3062 pMainWindow->functionTip()->hide();
3063 mHeaderCompletionPopup->show();
3064 mHeaderCompletionPopup->setSearchLocal(word.startsWith('"'));
3065 word.remove(0,1);
3066
3067 mHeaderCompletionPopup->prepareSearch(word, mFilename);
3068
3069 // Filter the whole statement list
3070 if (mHeaderCompletionPopup->search(word, autoComplete)) //only one suggestion and it's not input while typing
3071 headerCompletionInsert(); // if only have one suggestion, just use it
3072}
3073
3074bool Editor::testInFunc(int x, int y)
3075{
3076 bool result = false;
3077 QString s = document()->getString(y);
3078 int posY = y;
3079 int posX = std::min(x,s.length()-1); // x is started from 1
3080 int bracketLevel=0;
3081 while (true) {
3082 while (posX < 0) {
3083 posY--;
3084 if (posY < 0)
3085 return false;
3086 s = document()->getString(posY);
3087 posX = s.length()-1;
3088 }
3089 if (s[posX] == '>'
3090 || s[posX] == ']') {
3091 bracketLevel++;
3092 } else if (s[posX] == '<'
3093 || s[posX] == '[') {
3094 bracketLevel--;
3095 } else if (bracketLevel==0) {
3096 switch (s[posX].unicode()) {
3097 case '(':
3098 return true;
3099 case ';':
3100 case '{':
3101 return false;
3102 }
3103 if (!(isIdentChar(s[posX])
3104 || s[posX] == ' '
3105 || s[posX] == '\t'
3106 || s[posX] == '*'
3107 || s[posX] == '&'))
3108 break;;
3109 }
3110 posX--;
3111 }
3112 return result;
3113}
3114
3115void Editor::completionInsert(bool appendFunc)
3116{
3117 PStatement statement = mCompletionPopup->selectedStatement();
3118 if (!statement)
3119 return;
3120
3121 if (pSettings->codeCompletion().recordUsage()
3122 && statement->kind != StatementKind::skUserCodeSnippet) {
3123 statement->usageCount+=1;
3124 pMainWindow->symbolUsageManager()->updateUsage(statement->fullName,
3125 statement->usageCount);
3126 }
3127
3128 QString funcAddOn = "";
3129
3130// delete the part of the word that's already been typed ...
3131 QSynedit::BufferCoord p = wordEnd();
3132 QSynedit::BufferCoord pStart = wordStart();
3133 setCaretAndSelection(pStart,pStart,p);
3134
3135 // if we are inserting a function,
3136 if (appendFunc) {
3137 if (statement->kind == StatementKind::skAlias) {
3138 PStatement newStatement = mParser->findAliasedStatement(statement);
3139 if (newStatement)
3140 statement = newStatement;
3141 }
3142 if ( (statement->kind == StatementKind::skFunction
3143 && !IOManipulators.contains(statement->fullName))
3144 || statement->kind == StatementKind::skConstructor
3145 || statement->kind == StatementKind::skDestructor
3146 ||
3147 (statement->kind == StatementKind::skPreprocessor
3148 && !statement->args.isEmpty())) {
3149 QChar nextCh = nextNonSpaceChar(caretY()-1,p.ch-1);
3150 if (nextCh=='(') {
3151 funcAddOn = "";
3152 } else if (isIdentChar(nextCh) || nextCh == '"'
3153 || nextCh == '\'') {
3154 funcAddOn = '(';
3155 } else {
3156 funcAddOn = "()";
3157 }
3158 }
3159 }
3160
3161 // ... by replacing the selection
3162 if (statement->kind == StatementKind::skUserCodeSnippet) { // it's a user code template
3163 // insertUserCodeIn(Statement->value);
3164 //first move caret to the begin of the word to be replaced
3165 insertCodeSnippet(statement->value);
3166 } else {
3167 if (
3168 (statement->kind == StatementKind::skKeyword
3169 || statement->kind == StatementKind::skPreprocessor)
3170 && (statement->command.startsWith('#')
3171 || statement->command.startsWith('@'))
3172 ) {
3173 setSelText(statement->command.mid(1));
3174 } else
3175 setSelText(statement->command + funcAddOn);
3176
3177 if (!funcAddOn.isEmpty())
3178 mLastIdCharPressed = 0;
3179
3180 // Move caret inside the ()'s, only when the user has something to do there...
3181 if (!funcAddOn.isEmpty()
3182 && (statement->args != "()")
3183 && (statement->args != "(void)")) {
3184 setCaretX(caretX() - funcAddOn.length()+1);
3185
3186 //todo: function hint
3187 // immediately activate function hint
3188// if devEditor.ShowFunctionTip and Assigned(fText.Highlighter) then begin
3189// fText.SetFocus;
3190// fFunctionTip.Parser := fParser;
3191// fFunctionTip.FileName := fFileName;
3192// fFunctionTip.Show;
3193// end;
3194 } else {
3195 setCaretX(caretX());
3196 }
3197 }
3198 mCompletionPopup->hide();
3199}
3200
3201void Editor::headerCompletionInsert()
3202{
3203 QString headerName = mHeaderCompletionPopup->selectedFilename(true);
3204 if (headerName.isEmpty()) {
3205 mHeaderCompletionPopup->hide();
3206 return;
3207 }
3208
3209 // delete the part of the word that's already been typed ...
3210 QSynedit::BufferCoord p = caretXY();
3211 int posBegin = p.ch-1;
3212 int posEnd = p.ch-1;
3213 QString sLine = lineText();
3214 while ((posBegin>0) &&
3215 (isIdentChar(sLine[posBegin-1]) || (sLine[posBegin-1]=='.') || (sLine[posBegin-1]=='+')))
3216 posBegin--;
3217
3218 while ((posEnd < sLine.length())
3219 && (isIdentChar(sLine[posEnd]) || (sLine[posEnd]=='.') || (sLine[posBegin-1]=='+')))
3220 posEnd++;
3221 p.ch = posBegin+1;
3222 setBlockBegin(p);
3223 p.ch = posEnd+1;
3224 setBlockEnd(p);
3225
3226 setSelText(headerName);
3227
3228 setCaretX(caretX());
3229
3230 if (headerName.endsWith("/")) {
3231 showHeaderCompletion(false,true);
3232 } else {
3233 mHeaderCompletionPopup->hide();
3234 }
3235}
3236
3237bool Editor::onCompletionKeyPressed(QKeyEvent *event)
3238{
3239 bool processed = false;
3240 if (!mCompletionPopup->isEnabled())
3241 return false;
3242 QString oldPhrase = mCompletionPopup->memberPhrase();
3243 WordPurpose purpose = WordPurpose::wpCompletion;
3244 if (oldPhrase.startsWith('#')) {
3245 purpose = WordPurpose::wpDirective;
3246 } else if (oldPhrase.startsWith('@')) {
3247 purpose = WordPurpose::wpJavadoc;
3248 }
3249 QString phrase;
3250 QSynedit::BufferCoord pBeginPos,pEndPos;
3251 switch (event->key()) {
3252 case Qt::Key_Shift:
3253 case Qt::Key_Control:
3254 case Qt::Key_Meta:
3255 case Qt::Key_Alt:
3256 //ignore it
3257 return true;
3258 case Qt::Key_Backspace:
3259 ExecuteCommand(
3260 QSynedit::EditCommand::ecDeleteLastChar,
3261 QChar(), nullptr); // Simulate backspace in editor
3262 if (purpose == WordPurpose::wpCompletion) {
3263 phrase = getWordForCompletionSearch(caretXY(), mCompletionPopup->memberOperator()=="::");
3264 } else
3265 phrase = getWordAtPosition(this,caretXY(),
3266 pBeginPos,pEndPos,
3267 purpose);
3268 mLastIdCharPressed = phrase.length();
3269 if (phrase.isEmpty()) {
3270 mCompletionPopup->hide();
3271 } else {
3272 mCompletionPopup->search(phrase, false);
3273 }
3274 return true;
3275 case Qt::Key_Escape:
3276 mCompletionPopup->hide();
3277 return true;
3278 case Qt::Key_Return:
3279 case Qt::Key_Enter:
3280 case Qt::Key_Tab:
3281 completionInsert(pSettings->codeCompletion().appendFunc());
3282 return true;
3283 default:
3284 if (event->text().isEmpty()) {
3285 //stop completion
3286 mCompletionPopup->hide();
3287 keyPressEvent(event);
3288 return true;
3289 }
3290 }
3291 QChar ch = event->text().front();
3292 if (isIdentChar(ch)) {
3293 setSelText(ch);
3294 if (purpose == WordPurpose::wpCompletion) {
3295 phrase = getWordForCompletionSearch(caretXY(),mCompletionPopup->memberOperator()=="::");
3296 } else
3297 phrase = getWordAtPosition(this,caretXY(),
3298 pBeginPos,pEndPos,
3299 purpose);
3300 mLastIdCharPressed = phrase.length();
3301 mCompletionPopup->search(phrase, false);
3302 return true;
3303 } else {
3304 //stop completion
3305 mCompletionPopup->hide();
3306 keyPressEvent(event);
3307 return true;
3308 }
3309 return processed;
3310}
3311
3312bool Editor::onHeaderCompletionKeyPressed(QKeyEvent *event)
3313{
3314 bool processed = false;
3315 if (!mHeaderCompletionPopup->isEnabled())
3316 return false;
3317 QString phrase;
3318 QSynedit::BufferCoord pBeginPos,pEndPos;
3319 switch (event->key()) {
3320 case Qt::Key_Backspace:
3321 ExecuteCommand(
3322 QSynedit::EditCommand::ecDeleteLastChar,
3323 QChar(), nullptr); // Simulate backspace in editor
3324 phrase = getWordAtPosition(this,caretXY(),
3325 pBeginPos,pEndPos,
3326 WordPurpose::wpHeaderCompletion);
3327 mLastIdCharPressed = phrase.length();
3328 mHeaderCompletionPopup->search(phrase, false);
3329 return true;
3330 case Qt::Key_Escape:
3331 mHeaderCompletionPopup->hide();
3332 return true;
3333 case Qt::Key_Return:
3334 case Qt::Key_Enter:
3335 case Qt::Key_Tab:
3336 headerCompletionInsert();
3337 //mHeaderCompletionPopup->hide();
3338 return true;
3339 case Qt::Key_Shift:
3340 return false;
3341 default:
3342 if (event->text().isEmpty()) {
3343 //stop completion
3344 mHeaderCompletionPopup->hide();
3345 keyPressEvent(event);
3346 return true;
3347 }
3348 }
3349 QChar ch = event->text().front();
3350
3351 if (isIdentChar(ch) || ch == '.'
3352 || ch =='_' || ch=='+') {
3353 setSelText(ch);
3354 phrase = getWordAtPosition(this,caretXY(),
3355 pBeginPos,pEndPos,
3356 WordPurpose::wpHeaderCompletion);
3357 mLastIdCharPressed = phrase.length();
3358 mHeaderCompletionPopup->search(phrase, false);
3359 return true;
3360 } else {
3361 //stop completion
3362 mHeaderCompletionPopup->hide();
3363 keyPressEvent(event);
3364 return true;
3365 }
3366 return processed;
3367}
3368
3369bool Editor::onCompletionInputMethod(QInputMethodEvent *event)
3370{
3371 bool processed = false;
3372 if (!mCompletionPopup->isVisible())
3373 return processed;
3374 QString s=event->commitString();
3375 if (!s.isEmpty()) {
3376 QString phrase = getWordForCompletionSearch(caretXY(),mCompletionPopup->memberOperator()=="::");
3377 mLastIdCharPressed = phrase.length();
3378 mCompletionPopup->search(phrase, false);
3379 return true;
3380 }
3381 return processed;
3382}
3383
3384Editor::TipType Editor::getTipType(QPoint point, QSynedit::BufferCoord& pos)
3385{
3386 // Only allow in the text area...
3387 if (pointToCharLine(point, pos)) {
3388 if (!pMainWindow->debugger()->executing()
3389 && getSyntaxIssueAtPosition(pos)) {
3390 return TipType::Error;
3391 }
3392
3393 QSynedit::PHighlighterAttribute attr;
3394 QString s;
3395
3396 // Only allow hand tips in highlighted areas
3397 if (getHighlighterAttriAtRowCol(pos,s,attr)) {
3398 // Only allow Identifiers, Preprocessor directives, and selection
3399 if (attr) {
3400 if (selAvail()) {
3401 // do not allow when dragging selection
3402 if (isPointInSelection(pos))
3403 return TipType::Selection;
3404 } else if (mParser && mParser->isIncludeLine(document()->getString(pos.line-1))) {
3405 return TipType::Preprocessor;
3406 }else if (attr == highlighter()->identifierAttribute())
3407 return TipType::Identifier;
3408 }
3409 }
3410 }
3411 return TipType::None;
3412}
3413
3414void Editor::cancelHint()
3415{
3416 if(mHoverModifiedLine!=-1) {
3417 invalidateLine(mHoverModifiedLine);
3418 mHoverModifiedLine=-1;
3419 }
3420
3421 //MainForm.Debugger.OnEvalReady := nil;
3422
3423 // disable editor hint
3424 QToolTip::hideText();
3425 //mCurrentWord = "";
3426 //mCurrentTipType = TipType::None;
3427 updateMouseCursor();
3428}
3429
3430QString Editor::getFileHint(const QString &s)
3431{
3432 QString fileName = mParser->getHeaderFileName(mFilename, s);
3433 if (fileExists(fileName)) {
3434 return fileName + " - " + tr("Ctrl+click for more info");
3435 }
3436 return "";
3437}
3438
3439QString Editor::getParserHint(const QStringList& expression,const QString &/*s*/, int line)
3440{
3441 // This piece of code changes the parser database, possibly making hints and code completion invalid...
3442 QString result;
3443 // Exit early, don't bother creating a stream (which is slow)
3444 PStatement statement = mParser->findStatementOf(
3445 mFilename,expression,
3446 line);
3447 if (!statement)
3448 return result;
3449 if (statement->kind == StatementKind::skFunction
3450 || statement->kind == StatementKind::skConstructor
3451 || statement->kind == StatementKind::skDestructor) {
3452 //PStatement parentScope = statement->parentScope.lock();
3453// if (parentScope && parentScope->kind == StatementKind::skNamespace) {
3454// PStatementList namespaceStatementsList =
3455// mParser->findNamespace(parentScope->command);
3456// if (namespaceStatementsList) {
3457// int counts=0;
3458// foreach (const PStatement& namespaceStatement, *namespaceStatementsList) {
3459// QString hint = getHintForFunction(statement,namespaceStatement,
3460// mFilename,line);
3461// if (!hint.isEmpty()) {
3462// counts++;
3463// if (!result.isEmpty())
3464// result += "\n";
3465// if (counts>4) {
3466// result += "...";
3467// break;
3468// }
3469// result += hint;
3470// }
3471// }
3472// }
3473// } else
3474 result = getHintForFunction(statement,mFilename,line);
3475 } else if (statement->line>0) {
3476 QFileInfo fileInfo(statement->fileName);
3477 result = mParser->prettyPrintStatement(statement,mFilename, line) + " - "
3478 + QString("%1(%2) ").arg(fileInfo.fileName()).arg(statement->line)
3479 + tr("Ctrl+click for more info");
3480 } else { // hard defines
3481 result = mParser->prettyPrintStatement(statement, mFilename);
3482 }
3483// Result := StringReplace(Result, '|', #5, [rfReplaceAll]);
3484 return result;
3485}
3486
3487void Editor::showDebugHint(const QString &s, int line)
3488{
3489 PStatement statement = mParser->findStatementOf(mFilename,s,line);
3490 if (statement) {
3491 if (statement->kind != StatementKind::skVariable
3492 && statement->kind != StatementKind::skGlobalVariable
3493 && statement->kind != StatementKind::skLocalVariable
3494 && statement->kind != StatementKind::skParameter) {
3495 return;
3496 }
3497 }
3498 if (pMainWindow->debugger()->commandRunning())
3499 return;
3500 if (pMainWindow->functionTip()->isVisible()) {
3501 pMainWindow->functionTip()->hide();
3502 }
3503 connect(pMainWindow->debugger(), &Debugger::evalValueReady,
3504 this, &Editor::onTipEvalValueReady);
3505 mCurrentDebugTipWord = s;
3506 pMainWindow->debugger()->sendCommand("-data-evaluate-expression",s);
3507}
3508
3509QString Editor::getErrorHint(const PSyntaxIssue& issue)
3510{
3511 if (issue) {
3512 return issue->hint;
3513 } else {
3514 return "";
3515 }
3516}
3517
3518QString Editor::getHintForFunction(const PStatement &statement, const QString& filename, int line)
3519{
3520 QFileInfo fileInfo(statement->fileName);
3521 QString result;
3522 result = mParser->prettyPrintStatement(statement,filename,line) + " - "
3523 + QString("%1(%2) ").arg(fileInfo.fileName()).arg(statement->line)
3524 + tr("Ctrl+click for more info");
3525// const StatementMap& children = mParser->statementList().childrenStatements(scopeStatement);
3526// foreach (const PStatement& childStatement, children){
3527// if (statement->command == childStatement->command
3528// && statement->kind == childStatement->kind) {
3529// if ((line < childStatement->line) &&
3530// childStatement->fileName == filename)
3531// continue;
3532// if (!result.isEmpty())
3533// result += "\n";
3534// QFileInfo fileInfo(childStatement->fileName);
3535// result = mParser->prettyPrintStatement(childStatement,filename,line) + " - "
3536// + QString("%1(%2) ").arg(fileInfo.fileName()).arg(childStatement->line)
3537// + tr("Ctrl+click for more info");
3538// }
3539// }
3540 return result;
3541}
3542
3543void Editor::updateFunctionTip(bool showTip)
3544{
3545 if (pMainWindow->completionPopup()->isVisible()) {
3546 pMainWindow->functionTip()->hide();
3547 return;
3548 }
3549 if (inputMethodOn()) {
3550 pMainWindow->functionTip()->hide();
3551 return;
3552 }
3553 if (!highlighter())
3554 return;
3555
3556 bool isFunction = false;
3557 auto action = finally([&isFunction]{
3558 if (!isFunction)
3559 pMainWindow->functionTip()->hide();
3560 });
3561 const int maxLines=10;
3562 QSynedit::BufferCoord caretPos = caretXY();
3563 int currentLine = caretPos.line-1;
3564 int currentChar = caretPos.ch-1;
3565 QSynedit::BufferCoord functionNamePos{-1,-1};
3566 bool foundFunctionStart = false;
3567 int parenthesisLevel = 0;
3568 int braceLevel = 0;
3569 int bracketLevel = 0;
3570 int paramsCount = 1;
3571 int currentParamPos = 1;
3572 if (currentLine>=document()->count())
3573 return;
3574 QChar ch=lastNonSpaceChar(currentLine,currentChar);
3575 if (ch!="(" && ch!=",")
3576 return;
3577
3578 while (currentLine>=0) {
3579 QString line = document()->getString(currentLine);
3580 if (currentLine!=caretPos.line-1)
3581 currentChar = line.length();
3582 QStringList tokens;
3583 QList<int> positions;
3584 if (currentLine==0)
3585 highlighter()->resetState();
3586 else
3587 highlighter()->setState(
3588 document()->ranges(currentLine-1));
3589 highlighter()->setLine(line,currentLine);
3590 while(!highlighter()->eol()) {
3591 int start = highlighter()->getTokenPos();
3592 QString token = highlighter()->getToken();
3593 QSynedit::PHighlighterAttribute attr = highlighter()->getTokenAttribute();
3594 if (start>=currentChar)
3595 break;
3596
3597 if (attr != highlighter()->commentAttribute()
3598 && attr!=highlighter()->whitespaceAttribute()) {
3599 if (foundFunctionStart) {
3600 if (attr!=highlighter()->identifierAttribute())
3601 return; // not a function
3602 functionNamePos.line = currentLine+1;
3603 functionNamePos.ch = start+1;
3604 break;
3605 }
3606 tokens.append(token);
3607 positions.append(start);
3608 } else if (attr == highlighter()->commentAttribute()
3609 && currentLine == caretPos.line-1 && start<caretPos.ch
3610 && start+token.length()>=caretPos.ch) {
3611 return; // in comment, do nothing
3612 }
3613 highlighter()->next();
3614 }
3615 if (!foundFunctionStart) {
3616 for (int i=tokens.length()-1;i>=0;i--) {
3617 if (braceLevel>0) {
3618 if (tokens[i]=="{") {
3619 braceLevel--;
3620 } else if (tokens[i]=="}") {
3621 braceLevel++;
3622 }
3623 } else if (bracketLevel>0) {
3624 if (tokens[i]=="[") {
3625 bracketLevel--;
3626 } else if (tokens[i]=="]") {
3627 bracketLevel++;
3628 }
3629 }else if (parenthesisLevel>0){
3630 if (tokens[i]==")") {
3631 parenthesisLevel++;
3632 } else if (tokens[i]=="(") {
3633 parenthesisLevel--;
3634 }
3635 } else {
3636 if (tokens[i]=="(") {
3637 // found start of function
3638 foundFunctionStart = true;
3639 if (i>0) {
3640 functionNamePos.line = currentLine+1;
3641 functionNamePos.ch = positions[i-1]+1;
3642 }
3643 break;
3644 } else if (tokens[i]=="[") {
3645 //we are not in a function call
3646 return;
3647 } else if (tokens[i]=="{") {
3648 //we are not in a function call
3649 return;
3650 } else if (tokens[i]==";") {
3651 //we are not in a function call
3652 return;
3653 } else if (tokens[i]==")") {
3654 parenthesisLevel++;
3655 } else if (tokens[i]=="}") {
3656 braceLevel++;
3657 } else if (tokens[i]=="]") {
3658 bracketLevel++;
3659 } else if (tokens[i]==",") {
3660 paramsCount++;
3661 }
3662 }
3663 }
3664 }
3665 if (functionNamePos.ch>=0)
3666 break;
3667 currentLine--;
3668 if (caretPos.line-currentLine>maxLines)
3669 break;
3670 }
3671 isFunction = functionNamePos.ch>=0;
3672 currentParamPos = paramsCount-1;
3673 if (!isFunction)
3674 return;
3675 QSynedit::BufferCoord pWordBegin, pWordEnd;
3676
3677 QString s = getWordAtPosition(this, functionNamePos, pWordBegin,pWordEnd, WordPurpose::wpInformation);
3678
3679 int x = pWordBegin.ch-1-1;
3680 QString line = document()->getString(pWordBegin.line-1);
3681 bool hasPreviousWord=false;
3682 while (x>=0) {
3683 QChar ch=line[x];
3684 if (ch == ' ' || ch == '\t') {
3685 x--;
3686 continue;
3687 }
3688 if (isIdentChar(ch)) {
3689 hasPreviousWord = true;
3690 break;
3691 }
3692 hasPreviousWord = false;
3693 break;
3694 }
3695
3696 if (x >= 0 && hasPreviousWord) {
3697 QSynedit::BufferCoord pos = pWordBegin;
3698 pos.ch = x+1;
3699 QString previousWord = getPreviousWordAtPositionForSuggestion(pos);
3700
3701 PStatement statement = mParser->findStatementOf(
3702 mFilename,
3703 previousWord,
3704 pos.line);
3705 if (statement) {
3706 PStatement typeStatement = mParser->findTypeDef(statement,mFilename);
3707 if (typeStatement && typeStatement->kind == StatementKind::skClass) {
3708 s = previousWord;
3709 functionNamePos = pos;
3710 }
3711 }
3712 }
3713
3714// qDebug()<<QString("find word at %1:%2 - '%3'")
3715// .arg(FuncStartXY.Line)
3716// .arg(FuncStartXY.Char)
3717// .arg(s);
3718 // Don't bother scanning the database when there's no identifier to scan for
3719
3720 // Only do the cumbersome list filling when showing a new tooltip...
3721
3722 if (s != pMainWindow->functionTip()->functionFullName()
3723 && !mParser->parsing()) {
3724 pMainWindow->functionTip()->clearTips();
3725 QList<PStatement> statements=mParser->getListOfFunctions(mFilename,
3726 s,
3727 functionNamePos.line);
3728
3729 foreach (const PStatement statement, statements) {
3730 pMainWindow->functionTip()->addTip(
3731 statement->command,
3732 statement->fullName,
3733 statement->type,
3734 statement->args,
3735 statement->noNameArgs);
3736 }
3737 }
3738
3739 // If we can't find it in our database, hide
3740 if (pMainWindow->functionTip()->tipCount()<=0) {
3741 pMainWindow->functionTip()->hide();
3742 return;
3743 }
3744 // Position it at the top of the next line
3745 QPoint p = rowColumnToPixels(displayXY());
3746 p+=QPoint(0,textHeight()+2);
3747 pMainWindow->functionTip()->move(mapToGlobal(p));
3748
3749 pMainWindow->functionTip()->setFunctioFullName(s);
3750 pMainWindow->functionTip()->guessFunction(paramsCount-1);
3751 pMainWindow->functionTip()->setParamIndex(
3752 currentParamPos
3753 );
3754 cancelHint();
3755 if (showTip)
3756 pMainWindow->functionTip()->show();
3757}
3758
3759void Editor::clearUserCodeInTabStops()
3760{
3761 mUserCodeInTabStops.clear();
3762}
3763
3764void Editor::popUserCodeInTabStops()
3765{
3766 if (mTabStopBegin < 0) {
3767 clearUserCodeInTabStops();
3768 return;
3769 }
3770 QSynedit::BufferCoord newCursorPos;
3771 int tabStopEnd;
3772 int tabStopBegin;
3773 if (mUserCodeInTabStops.count() > 0) {
3774 PTabStop p = mUserCodeInTabStops.front();
3775 // Update the cursor
3776 if (p->y ==0) {
3777 tabStopBegin = mTabStopEnd + p->x;
3778 tabStopEnd = mTabStopEnd + p->endX;
3779 } else {
3780 int n=countLeadingWhitespaceChars(document()->getString(caretY()-1+p->y));
3781// qDebug()<<line<<n<<p->x;
3782 tabStopBegin = n+p->x+1;
3783 tabStopEnd = n+p->endX+1;
3784 }
3785 mTabStopY = caretY() + p->y;
3786 newCursorPos.line = mTabStopY;
3787 newCursorPos.ch = tabStopBegin;
3788 setCaretXY(newCursorPos);
3789 setBlockBegin(newCursorPos);
3790 newCursorPos.ch = tabStopEnd;
3791 setBlockEnd(newCursorPos);
3792
3793 mTabStopBegin = tabStopBegin;
3794 mTabStopEnd = tabStopEnd;
3795 mLineBeforeTabStop = lineText().mid(0, mTabStopBegin-1) ;
3796 mLineAfterTabStop = lineText().mid(mTabStopEnd-1) ;
3797 mXOffsetSince=0;
3798 mUserCodeInTabStops.pop_front();
3799 }
3800}
3801
3802void Editor::onExportedFormatToken(QSynedit::PHighlighter syntaxHighlighter, int Line, int column, const QString &token, QSynedit::PHighlighterAttribute& attr)
3803{
3804 if (!syntaxHighlighter)
3805 return;
3806 if (token.isEmpty())
3807 return;
3808 //don't do this
3809 if (mCompletionPopup->isVisible() || mHeaderCompletionPopup->isVisible())
3810 return;
3811
3812 if (mParser && (attr == syntaxHighlighter->identifierAttribute())) {
3813 QSynedit::BufferCoord p{column,Line};
3814 QSynedit::BufferCoord pBeginPos,pEndPos;
3815 QString s= getWordAtPosition(this,p, pBeginPos,pEndPos, WordPurpose::wpInformation);
3816// qDebug()<<s;
3817 PStatement statement = mParser->findStatementOf(mFilename,
3818 s , p.line);
3819 StatementKind kind = getKindOfStatement(statement);
3820 if (kind == StatementKind::skUnknown) {
3821 if ((pEndPos.line>=1)
3822 && (pEndPos.ch>=0)
3823 && (pEndPos.ch < document()->getString(pEndPos.line-1).length())
3824 && (document()->getString(pEndPos.line-1)[pEndPos.ch] == '(')) {
3825 kind = StatementKind::skFunction;
3826 } else {
3827 kind = StatementKind::skVariable;
3828 }
3829 }
3830 QSynedit::CppHighlighter* cppHighlighter = dynamic_cast<QSynedit::CppHighlighter*>(syntaxHighlighter.get());
3831 switch(kind) {
3832 case StatementKind::skFunction:
3833 case StatementKind::skConstructor:
3834 case StatementKind::skDestructor:
3835 attr = cppHighlighter->functionAttribute();
3836 break;
3837 case StatementKind::skClass:
3838 case StatementKind::skTypedef:
3839 case StatementKind::skAlias:
3840 attr = cppHighlighter->classAttribute();
3841 break;
3842 case StatementKind::skEnumClassType:
3843 case StatementKind::skEnumType:
3844 break;
3845 case StatementKind::skLocalVariable:
3846 case StatementKind::skParameter:
3847 attr = cppHighlighter->localVarAttribute();
3848 break;
3849 case StatementKind::skVariable:
3850 attr = cppHighlighter->variableAttribute();
3851 break;
3852 case StatementKind::skGlobalVariable:
3853 attr = cppHighlighter->globalVarAttribute();
3854 break;
3855 case StatementKind::skEnum:
3856 case StatementKind::skPreprocessor:
3857 attr = cppHighlighter->preprocessorAttribute();
3858 break;
3859 case StatementKind::skKeyword:
3860 attr = cppHighlighter->keywordAttribute();
3861 break;
3862 case StatementKind::skNamespace:
3863 case StatementKind::skNamespaceAlias:
3864 attr = cppHighlighter->stringAttribute();
3865 break;
3866 default:
3867 break;
3868 }
3869 }
3870}
3871
3872void Editor::onScrollBarValueChanged()
3873{
3874 pMainWindow->functionTip()->hide();
3875}
3876
3877bool Editor::canAutoSave() const
3878{
3879 return mCanAutoSave;
3880}
3881
3882void Editor::setCanAutoSave(bool newCanAutoSave)
3883{
3884 mCanAutoSave = newCanAutoSave;
3885}
3886
3887const QDateTime &Editor::hideTime() const
3888{
3889 return mHideTime;
3890}
3891
3892void Editor::setHideTime(const QDateTime &newHideTime)
3893{
3894 mHideTime = newHideTime;
3895}
3896
3897const std::shared_ptr<QHash<StatementKind, std::shared_ptr<ColorSchemeItem> > > &Editor::statementColors() const
3898{
3899 return mStatementColors;
3900}
3901
3902void Editor::setStatementColors(const std::shared_ptr<QHash<StatementKind, std::shared_ptr<ColorSchemeItem> > > &newStatementColors)
3903{
3904 mStatementColors = newStatementColors;
3905}
3906
3907bool Editor::useCppSyntax() const
3908{
3909 return mUseCppSyntax;
3910}
3911
3912void Editor::setUseCppSyntax(bool newUseCppSyntax)
3913{
3914 mUseCppSyntax = newUseCppSyntax;
3915}
3916
3917void Editor::setInProject(bool newInProject)
3918{
3919 if (mInProject == newInProject)
3920 return;
3921 mInProject = newInProject;
3922 if (mInProject) {
3923 mParser = pMainWindow->project()->cppParser();
3924 if (isVisible()) {
3925 if (mParser && mParser->parsing()) {
3926 connect(mParser.get(),
3927 &CppParser::onEndParsing,
3928 this,
3929 &QSynedit::SynEdit::invalidate);
3930 } else {
3931 invalidate();
3932 }
3933 }
3934 } else {
3935 initParser();
3936 }
3937}
3938
3939void Editor::gotoDeclaration(const QSynedit::BufferCoord &pos)
3940{
3941 if (!parser())
3942 return;
3943 // Exit early, don't bother creating a stream (which is slow)
3944 QStringList expression = getExpressionAtPosition(pos);
3945
3946 // Find it's definition
3947 PStatement statement = parser()->findStatementOf(
3948 filename(),
3949 expression,
3950 pos.line);
3951// QString phrase = getWordAtPosition(this,pos,pBeginPos,pEndPos, WordPurpose::wpInformation);
3952// if (phrase.isEmpty())
3953// return;
3954
3955// PStatement statement = mParser->findStatementOf(
3956// mFilename,phrase,pos.Line);
3957
3958 if (!statement) {
3959// pMainWindow->updateStatusbarMessage(tr("Symbol '%1' not found!").arg(phrase));
3960 return;
3961 }
3962 QString filename;
3963 int line;
3964 if (statement->fileName == mFilename && statement->line == pos.line) {
3965 filename = statement->definitionFileName;
3966 line = statement->definitionLine;
3967 } else {
3968 filename = statement->fileName;
3969 line = statement->line;
3970 }
3971 Editor* e = pMainWindow->editorList()->getEditorByFilename(filename);
3972 if (e) {
3973 e->setCaretPositionAndActivate(line,1);
3974 }
3975}
3976
3977void Editor::gotoDefinition(const QSynedit::BufferCoord &pos)
3978{
3979 QStringList expression = getExpressionAtPosition(pos);
3980
3981 // Find it's definition
3982 PStatement statement = parser()->findStatementOf(
3983 filename(),
3984 expression,
3985 pos.line);
3986
3987 if (!statement) {
3988 // pMainWindow->updateStatusbarMessage(tr("Symbol '%1' not found!").arg(phrase));
3989 return;
3990 }
3991 QString filename;
3992 int line;
3993 if (statement->definitionFileName == mFilename && statement->definitionLine == pos.line) {
3994 filename = statement->fileName;
3995 line = statement->line;
3996 } else {
3997 filename = statement->definitionFileName;
3998 line = statement->definitionLine;
3999 }
4000 Editor* e = pMainWindow->editorList()->getEditorByFilename(filename);
4001 if (e) {
4002 e->setCaretPositionAndActivate(line,1);
4003 }
4004}
4005
4006QString getWordAtPosition(QSynedit::SynEdit *editor, const QSynedit::BufferCoord &p, QSynedit::BufferCoord &pWordBegin, QSynedit::BufferCoord &pWordEnd, Editor::WordPurpose purpose)
4007{
4008 QString result = "";
4009 QString s;
4010 if ((p.line<1) || (p.line>editor->document()->count())) {
4011 pWordBegin = p;
4012 pWordEnd = p;
4013 return "";
4014 }
4015
4016 s = editor->document()->getString(p.line - 1);
4017 int len = s.length();
4018
4019 int wordBegin = p.ch - 1 - 1; //BufferCoord::Char starts with 1
4020 int wordEnd = p.ch - 1 - 1;
4021
4022 // Copy forward until end of word
4023 if (purpose == Editor::WordPurpose::wpEvaluation
4024 || purpose == Editor::WordPurpose::wpInformation) {
4025 while (wordEnd + 1 < len) {
4026 if ((purpose == Editor::WordPurpose::wpEvaluation)
4027 && (s[wordEnd + 1] == '[')) {
4028 if (!findComplement(s, '[', ']', wordEnd, 1))
4029 break;
4030 } else if (editor->isIdentChar(s[wordEnd + 1])) {
4031 wordEnd++;
4032 } else
4033 break;
4034 }
4035 }
4036
4037 // Copy backward until #
4038 if (purpose == Editor::WordPurpose::wpDirective) {
4039 while ((wordBegin >= 0) && (wordBegin < len)) {
4040 if (editor->isIdentChar(s[wordBegin]))
4041 wordBegin--;
4042 else if (s[wordBegin] == '#') {
4043 wordBegin--;
4044 break;
4045 } else
4046 break;
4047 }
4048 }
4049
4050 // Copy backward until @
4051 if (purpose == Editor::WordPurpose::wpJavadoc) {
4052 while ((wordBegin >= 0) && (wordBegin < len)) {
4053 if (editor->isIdentChar(s[wordBegin]))
4054 wordBegin--;
4055 else if (s[wordBegin] == '@') {
4056 wordBegin--;
4057 break;
4058 } else
4059 break;
4060 }
4061 }
4062
4063 // Copy backward until begin of path
4064 if (purpose == Editor::WordPurpose::wpHeaderCompletion) {
4065 while ((wordBegin >= 0) && (wordBegin < len)) {
4066 if (editor->isIdentChar(s[wordBegin])) {
4067 wordBegin--;
4068 } else if (s[wordBegin] == '.'
4069 || s[wordBegin] == '+') {
4070 wordBegin--;
4071 } else if (s[wordBegin] == '/'
4072 || s[wordBegin] == '\\') {
4073 wordBegin--;
4074 break;
4075 } else
4076 break;
4077 }
4078 }
4079
4080 if (purpose == Editor::WordPurpose::wpHeaderCompletionStart) {
4081 while ((wordBegin >= 0) && (wordBegin < len)) {
4082 if (s[wordBegin] == '"'
4083 || s[wordBegin] == '<') {
4084 wordBegin--;
4085 break;
4086 } else if (s[wordBegin] == '/'
4087 || s[wordBegin] == '\\'
4088 || s[wordBegin] == '.') {
4089 wordBegin--;
4090 } else if (editor->isIdentChar(s[wordBegin]))
4091 wordBegin--;
4092 else
4093 break;
4094 }
4095 }
4096
4097// && ( wordBegin < len)
4098 // Copy backward until begin of word
4099 if (purpose == Editor::WordPurpose::wpCompletion
4100 || purpose == Editor::WordPurpose::wpEvaluation
4101 || purpose == Editor::WordPurpose::wpInformation) {
4102 while ((wordBegin >= 0) && (wordBegin<len)) {
4103 if (s[wordBegin] == ']') {
4104 if (!findComplement(s, ']', '[', wordBegin, -1))
4105 break;
4106 else
4107 wordBegin--; // step over mathing [
4108 } else if (editor->isIdentChar(s[wordBegin])) {
4109 wordBegin--;
4110 } else if (s[wordBegin] == '.'
4111 || s[wordBegin] == ':'
4112 || s[wordBegin] == '~') { // allow destructor signs
4113 wordBegin--;
4114 } else if (
4115 (s[wordBegin] == '>')
4116 && (wordBegin+2<len)
4117 && (s[wordBegin+1]==':')
4118 && (s[wordBegin+2]==':')
4119 ) { // allow template
4120 if (!findComplement(s, '>', '<', wordBegin, -1))
4121 break;
4122 else
4123 wordBegin--; // step over >
4124 } else if ((wordBegin-1 >= 0)
4125 && (s[wordBegin - 1] == '-')
4126 && (s[wordBegin] == '>')) {
4127 wordBegin-=2;
4128 } else if ((wordBegin-1 >= 0)
4129 && (s[wordBegin - 1] == ':')
4130 && (s[wordBegin] == ':')) {
4131 wordBegin-=2;
4132 } else if ((wordBegin > 0)
4133 && (s[wordBegin] == ')')) {
4134 if (!findComplement(s, ')', '(', wordBegin, -1))
4135 break;
4136 else
4137 wordBegin--; // step over mathing (
4138 } else
4139 break;
4140 }
4141 }
4142
4143 // Get end result
4144 result = s.mid(wordBegin+1, wordEnd - wordBegin);
4145 pWordBegin.line = p.line;
4146 pWordBegin.ch = wordBegin+1;
4147 pWordEnd.line = p.line;
4148 pWordEnd.ch = wordEnd;
4149
4150 // last line still have part of word
4151 if (!result.isEmpty()
4152 && (
4153 result[0] == '.'
4154 || result[0] == '-')
4155 && (purpose == Editor::WordPurpose::wpCompletion
4156 || purpose == Editor::WordPurpose::wpEvaluation
4157 || purpose == Editor::WordPurpose::wpInformation)) {
4158 int i = wordBegin;
4159 int line=p.line;
4160 while (line>=1) {
4161 while (i>=0) {
4162 if (s[i] == ' '
4163 || s[i] == '\t')
4164 i--;
4165 else
4166 break;
4167 }
4168 if (i<0) {
4169 line--;
4170 if (line>=1) {
4171 s=editor->document()->getString(line-1);
4172 i=s.length();
4173 continue;
4174 } else
4175 break;
4176 } else {
4177 QSynedit::BufferCoord highlightPos;
4178 QSynedit::BufferCoord pDummy;
4179 highlightPos.line = line;
4180 highlightPos.ch = i+1;
4181 result = getWordAtPosition(editor, highlightPos,pWordBegin,pDummy,purpose)+result;
4182 break;
4183 }
4184 }
4185 }
4186
4187 // Strip function parameters
4188 int paramBegin,paramEnd;
4189 while (true) {
4190 paramBegin = result.indexOf('(');
4191 if (paramBegin > 0) {
4192 paramEnd = paramBegin;
4193 if ((paramBegin==0)
4194 && findComplement(result, '(', ')', paramEnd, 1)
4195 && (paramEnd = result.length()-1) ) {
4196 //remove the enclosing parenthese pair
4197 result = result.mid(1,result.length()-2);
4198 continue;
4199 } else {
4200 paramEnd = paramBegin;
4201 if (findComplement(result, '(', ')', paramEnd, 1)) {
4202 result.remove(paramBegin, paramEnd - paramBegin + 1);
4203 } else
4204 break;
4205 }
4206 } else
4207 break;
4208 }
4209
4210 paramBegin = 0;
4211 while ((paramBegin < result.length()) && (result[paramBegin] == '*')) {
4212 paramBegin++;
4213 }
4214 result.remove(0,paramBegin);
4215 return result;
4216}
4217
4218QString Editor::getPreviousWordAtPositionForSuggestion(const QSynedit::BufferCoord &p)
4219{
4220 QString result;
4221 if ((p.line<1) || (p.line>document()->count())) {
4222 return "";
4223 }
4224 bool inFunc = testInFunc(p.ch-1,p.line-1);
4225
4226 QString s = document()->getString(p.line - 1);
4227 int wordBegin;
4228 int wordEnd = p.ch-1;
4229 if (wordEnd >= s.length())
4230 wordEnd = s.length()-1;
4231 while (true) {
4232 int bracketLevel=0;
4233 bool skipNextWord=false;
4234 while (wordEnd > 0) {
4235 if (s[wordEnd] == '>'
4236 || s[wordEnd] == ']') {
4237 bracketLevel++;
4238 } else if (s[wordEnd] == '<'
4239 || s[wordEnd] == '[') {
4240 bracketLevel--;
4241 } else if (bracketLevel==0) {
4242 //we can't differentiate multiple definition and function parameter define here , so we don't handle ','
4243 if (s[wordEnd] == ',') {
4244 if (inFunc) // in func, dont skip ','
4245 break;
4246 else
4247 skipNextWord=true;
4248 } else if (s[wordEnd] != ' '
4249 && s[wordEnd] != '\t') {
4250 break;
4251 }
4252 }
4253 wordEnd--;
4254 }
4255 if (wordEnd<0)
4256 return "";
4257 if (bracketLevel > 0)
4258 return "";
4259 if (!isIdentChar(s[wordEnd]))
4260 return "";
4261
4262 wordBegin = wordEnd;
4263 while ((wordBegin >= 0) && (isIdentChar(s[wordBegin]) || s[wordBegin]==':') ) {
4264 wordBegin--;
4265 }
4266 wordBegin++;
4267
4268 if (s[wordBegin]>='0' && s[wordBegin]<='9') // not valid word
4269 return "";
4270
4271 result = s.mid(wordBegin, wordEnd - wordBegin+1);
4272 if ((result != "const") && !skipNextWord)
4273 break;
4274 wordEnd = wordBegin-1;
4275 }
4276 return result;
4277}
4278
4279void Editor::reformat()
4280{
4281 if (readOnly())
4282 return;
4283#ifndef Q_OS_WIN
4284 if (!fileExists(pSettings->environment().AStylePath())) {
4285 QMessageBox::critical(this,
4286 tr("astyle not found"),
4287 tr("Can't find astyle in \"%1\".").arg(pSettings->environment().AStylePath()));
4288 return;
4289 }
4290#endif
4291 //we must remove all breakpoints and syntax issues
4292 onLinesDeleted(1,document()->count());
4293 QByteArray content = text().toUtf8();
4294 QStringList args = pSettings->codeFormatter().getArguments();
4295 qDebug()<<args;
4296#ifdef Q_OS_WIN
4297 QByteArray newContent = runAndGetOutput("astyle.exe",
4298 pSettings->dirs().appDir(),
4299 args,
4300 content);
4301#else
4302 QByteArray newContent = runAndGetOutput(pSettings->environment().AStylePath(),
4303 extractFileDir(pSettings->environment().AStylePath()),
4304 args,
4305 content);
4306#endif
4307 if (newContent.isEmpty())
4308 return;
4309 int oldTopLine = topLine();
4310 QSynedit::BufferCoord mOldCaret = caretXY();
4311
4312 beginUndoBlock();
4313 addLeftTopToUndo();
4314 addCaretToUndo();
4315
4316 QSynedit::EditorOptions oldOptions = getOptions();
4317 QSynedit::EditorOptions newOptions = oldOptions;
4318 newOptions.setFlag(QSynedit::EditorOption::eoAutoIndent,false);
4319 setOptions(newOptions);
4320 replaceAll(QString::fromUtf8(newContent));
4321 setCaretXY(mOldCaret);
4322 setTopLine(oldTopLine);
4323 setOptions(oldOptions);
4324 endUndoBlock();
4325 reparse();
4326 checkSyntaxInBack();
4327 reparseTodo();
4328 pMainWindow->updateEditorActions();
4329}
4330
4331void Editor::checkSyntaxInBack()
4332{
4333 if (readOnly())
4334 return;
4335 if (!highlighter())
4336 return;
4337 if (highlighter()->language()!=QSynedit::HighlighterLanguage::Cpp)
4338 return;
4339 if(pSettings->editor().syntaxCheck())
4340 pMainWindow->checkSyntaxInBack(this);
4341}
4342
4343const PCppParser &Editor::parser()
4344{
4345 return mParser;
4346}
4347
4348void Editor::tab()
4349{
4350 if (mUserCodeInTabStops.count()>0) {
4351 int oldLine = caretY();
4352 popUserCodeInTabStops();
4353 if (oldLine!=caretY()) {
4354 invalidateLine(oldLine);
4355 }
4356 invalidateLine(caretY());
4357 return;
4358 } else {
4359 if (mTabStopBegin >= 0) {
4360 mTabStopBegin = -1;
4361 setCaretXY(blockEnd());
4362 invalidateLine(caretY());
4363 return;
4364 }
4365 }
4366 SynEdit::tab();
4367}
4368
4369int Editor::gutterClickedLine() const
4370{
4371 return mGutterClickedLine;
4372}
4373
4374void Editor::toggleBreakpoint(int line)
4375{
4376 if (hasBreakpoint(line)) {
4377 mBreakpointLines.remove(line);
4378 pMainWindow->debugger()->removeBreakpoint(line,this);
4379 } else {
4380 mBreakpointLines.insert(line);
4381 pMainWindow->debugger()->addBreakpoint(line,this);
4382 }
4383
4384 invalidateGutterLine(line);
4385 invalidateLine(line);
4386}
4387
4388void Editor::clearBreakpoints()
4389{
4390 pMainWindow->debugger()->deleteBreakpoints(this);
4391 mBreakpointLines.clear();
4392 invalidate();
4393}
4394
4395bool Editor::hasBreakpoint(int line)
4396{
4397 return mBreakpointLines.contains(line);
4398}
4399
4400void Editor::addBookmark(int line, const QString& description)
4401{
4402 mBookmarkLines.insert(line);
4403 pMainWindow->bookmarkModel()->addBookmark(mFilename,line,description);
4404 invalidateGutterLine(line);
4405}
4406
4407void Editor::removeBookmark(int line)
4408{
4409 mBookmarkLines.remove(line);
4410 pMainWindow->bookmarkModel()->removeBookmark(mFilename,line);
4411 invalidateGutterLine(line);
4412}
4413
4414bool Editor::hasBookmark(int line)
4415{
4416 return mBookmarkLines.contains(line);
4417}
4418
4419void Editor::clearBookmarks()
4420{
4421 mBookmarkLines.clear();
4422 pMainWindow->bookmarkModel()->removeBookmarks(mFilename);
4423 invalidateGutter();
4424}
4425
4426void Editor::removeBreakpointFocus()
4427{
4428 if (mActiveBreakpointLine!=-1) {
4429 int oldLine = mActiveBreakpointLine;
4430 mActiveBreakpointLine = -1;
4431 invalidateGutterLine(oldLine);
4432 invalidateLine(oldLine);
4433 }
4434}
4435
4436void Editor::modifyBreakpointProperty(int line)
4437{
4438 int index;
4439 PBreakpoint breakpoint = pMainWindow->debugger()->breakpointAt(line,this,index);
4440 if (!breakpoint)
4441 return;
4442 bool isOk;
4443 QString s=QInputDialog::getText(this,
4444 tr("Break point condition"),
4445 tr("Enter the condition of the breakpoint:"),
4446 QLineEdit::Normal,
4447 breakpoint->condition,&isOk);
4448 if (isOk) {
4449 pMainWindow->debugger()->setBreakPointCondition(index,s);
4450 }
4451}
4452
4453void Editor::setActiveBreakpointFocus(int Line, bool setFocus)
4454{
4455 if (Line != mActiveBreakpointLine) {
4456 removeBreakpointFocus();
4457
4458 // Put the caret at the active breakpoint
4459 mActiveBreakpointLine = Line;
4460
4461 if (setFocus)
4462 setCaretPositionAndActivate(Line,1);
4463 else
4464 setCaretPosition(Line,1);
4465
4466 // Invalidate new active line
4467 invalidateGutterLine(Line);
4468 invalidateLine(Line);
4469 }
4470}
4471
4472void Editor::applySettings()
4473{
4474 QSynedit::EditorOptions options = QSynedit::eoAltSetsColumnMode |
4475 QSynedit::eoDragDropEditing | QSynedit::eoDropFiles | QSynedit::eoKeepCaretX | QSynedit::eoTabsToSpaces |
4476 QSynedit::eoRightMouseMovesCursor | QSynedit::eoScrollByOneLess | QSynedit::eoTabIndent | QSynedit::eoHideShowScrollbars | QSynedit::eoGroupUndo;
4477
4478 //options
4479 options.setFlag(QSynedit::eoAutoIndent,pSettings->editor().autoIndent());
4480 options.setFlag(QSynedit::eoTabsToSpaces,pSettings->editor().tabToSpaces());
4481
4482 options.setFlag(QSynedit::eoLigatureSupport, pSettings->editor().enableLigaturesSupport());
4483
4484 options.setFlag(QSynedit::eoKeepCaretX,pSettings->editor().keepCaretX());
4485 options.setFlag(QSynedit::eoEnhanceHomeKey,pSettings->editor().enhanceHomeKey());
4486 options.setFlag(QSynedit::eoEnhanceEndKey,pSettings->editor().enhanceEndKey());
4487
4488 options.setFlag(QSynedit::eoHideShowScrollbars,pSettings->editor().autoHideScrollbar());
4489 options.setFlag(QSynedit::eoScrollPastEol,pSettings->editor().scrollPastEol());
4490 options.setFlag(QSynedit::eoScrollPastEof,pSettings->editor().scrollPastEof());
4491 options.setFlag(QSynedit::eoScrollByOneLess,pSettings->editor().scrollByOneLess());
4492 options.setFlag(QSynedit::eoHalfPageScroll,pSettings->editor().halfPageScroll());
4493 options.setFlag(QSynedit::eoHalfPageScroll,pSettings->editor().halfPageScroll());
4494 options.setFlag(QSynedit::eoShowRainbowColor, pSettings->editor().rainbowParenthesis());
4495 setOptions(options);
4496
4497 setTabWidth(pSettings->editor().tabWidth());
4498 setInsertCaret(pSettings->editor().caretForInsert());
4499 setOverwriteCaret(pSettings->editor().caretForOverwrite());
4500 setCaretUseTextColor(pSettings->editor().caretUseTextColor());
4501 setCaretColor(pSettings->editor().caretColor());
4502
4503 codeFolding().indentGuides = pSettings->editor().showIndentLines();
4504 codeFolding().indentGuidesColor = pSettings->editor().indentLineColor();
4505 codeFolding().fillIndents = pSettings->editor().fillIndents();
4506
4507 QFont f=QFont(pSettings->editor().fontName());
4508 f.setPixelSize(pointToPixel(pSettings->editor().fontSize()));
4509 f.setStyleStrategy(QFont::PreferAntialias);
4510 setFont(f);
4511 QFont f2=QFont(pSettings->editor().nonAsciiFontName());
4512 f2.setPixelSize(pointToPixel(pSettings->editor().fontSize()));
4513 f2.setStyleStrategy(QFont::PreferAntialias);
4514 setFontForNonAscii(f2);
4515
4516 // Set gutter properties
4517 gutter().setLeftOffset(pointToPixel(pSettings->editor().fontSize()) + pSettings->editor().gutterLeftOffset());
4518 gutter().setRightOffset(pSettings->editor().gutterRightOffset());
4519 gutter().setBorderStyle(QSynedit::GutterBorderStyle::None);
4520 gutter().setUseFontStyle(pSettings->editor().gutterUseCustomFont());
4521 if (pSettings->editor().gutterUseCustomFont()) {
4522 f=QFont(pSettings->editor().gutterFontName());
4523 f.setPixelSize(pointToPixel(pSettings->editor().gutterFontSize()));
4524 } else {
4525 f=QFont(pSettings->editor().fontName());
4526 f.setPixelSize(pointToPixel(pSettings->editor().fontSize()));
4527 }
4528 f.setStyleStrategy(QFont::PreferAntialias);
4529 gutter().setFont(f);
4530 gutter().setDigitCount(pSettings->editor().gutterDigitsCount());
4531 gutter().setVisible(pSettings->editor().gutterVisible());
4532 gutter().setAutoSize(pSettings->editor().gutterAutoSize());
4533 gutter().setShowLineNumbers(pSettings->editor().gutterShowLineNumbers());
4534 gutter().setLeadingZeros(pSettings->editor().gutterAddLeadingZero());
4535 if (pSettings->editor().gutterLineNumbersStartZero())
4536 gutter().setLineNumberStart(0);
4537 else
4538 gutter().setLineNumberStart(1);
4539 //font color
4540
4541 if (pSettings->editor().showRightEdgeLine()) {
4542 setRightEdge(pSettings->editor().rightEdgeWidth());
4543 setRightEdgeColor(pSettings->editor().rightEdgeLineColor());
4544 } else {
4545 setRightEdge(0);
4546 }
4547
4548 this->setUndoLimit(pSettings->editor().undoLimit());
4549
4550 setMouseWheelScrollSpeed(pSettings->editor().mouseWheelScrollSpeed());
4551 setMouseSelectionScrollSpeed(pSettings->editor().mouseSelectionScrollSpeed());
4552 invalidate();
4553}
4554
4555static QSynedit::PHighlighterAttribute createRainbowAttribute(const QString& attrName, const QString& schemeName, const QString& schemeItemName) {
4556 PColorSchemeItem item = pColorManager->getItem(schemeName,schemeItemName);
4557 if (item) {
4558 QSynedit::PHighlighterAttribute attr = std::make_shared<QSynedit::HighlighterAttribute>(attrName);
4559 attr->setForeground(item->foreground());
4560 attr->setBackground(item->background());
4561 return attr;
4562 }
4563 return QSynedit::PHighlighterAttribute();
4564}
4565void Editor::applyColorScheme(const QString& schemeName)
4566{
4567 QSynedit::EditorOptions options = getOptions();
4568 options.setFlag(QSynedit::EditorOption::eoShowRainbowColor, pSettings->editor().rainbowParenthesis());
4569 setOptions(options);
4570 highlighterManager.applyColorScheme(highlighter(),schemeName);
4571 if (pSettings->editor().rainbowParenthesis()) {
4572 QSynedit::PHighlighterAttribute attr0 =createRainbowAttribute(SYNS_AttrSymbol,
4573 schemeName,COLOR_SCHEME_BRACE_1);
4574 QSynedit::PHighlighterAttribute attr1 =createRainbowAttribute(SYNS_AttrSymbol,
4575 schemeName,COLOR_SCHEME_BRACE_2);
4576 QSynedit::PHighlighterAttribute attr2 =createRainbowAttribute(SYNS_AttrSymbol,
4577 schemeName,COLOR_SCHEME_BRACE_3);
4578 QSynedit::PHighlighterAttribute attr3 =createRainbowAttribute(SYNS_AttrSymbol,
4579 schemeName,COLOR_SCHEME_BRACE_4);
4580 setRainbowAttrs(attr0,attr1,attr2,attr3);
4581 }
4582 PColorSchemeItem item = pColorManager->getItem(schemeName,COLOR_SCHEME_ACTIVE_LINE);
4583 if (item) {
4584 setActiveLineColor(item->background());
4585 }
4586 item = pColorManager->getItem(schemeName,COLOR_SCHEME_GUTTER);
4587 if (item) {
4588 gutter().setTextColor(item->foreground());
4589 gutter().setColor(item->background());
4590 }
4591 item = pColorManager->getItem(schemeName,COLOR_SCHEME_GUTTER_ACTIVE_LINE);
4592 if (item) {
4593 gutter().setActiveLineTextColor(item->foreground());
4594 }
4595 item = pColorManager->getItem(schemeName,COLOR_SCHEME_FOLD_LINE);
4596 if (item) {
4597 codeFolding().folderBarLinesColor = item->foreground();
4598 }
4599 item = pColorManager->getItem(schemeName,COLOR_SCHEME_INDENT_GUIDE_LINE);
4600 if (item) {
4601 codeFolding().indentGuidesColor = item->foreground();
4602 }
4603 item = pColorManager->getItem(schemeName,COLOR_SCHEME_ERROR);
4604 if (item) {
4605 this->mSyntaxErrorColor = item->foreground();
4606 }
4607 item = pColorManager->getItem(schemeName,COLOR_SCHEME_WARNING);
4608 if (item) {
4609 this->mSyntaxWarningColor = item->foreground();
4610 }
4611 item = pColorManager->getItem(schemeName,COLOR_SCHEME_SELECTION);
4612 if (item) {
4613 setSelectedForeground(item->foreground());
4614 setSelectedBackground(item->background());
4615 } else {
4616 this->setForegroundColor(palette().color(QPalette::HighlightedText));
4617 this->setBackgroundColor(palette().color(QPalette::Highlight));
4618 }
4619 item = pColorManager->getItem(schemeName,COLOR_SCHEME_ACTIVE_BREAKPOINT);
4620 if (item) {
4621 this->mActiveBreakpointForegroundColor = item->foreground();
4622 this->mActiveBreakpointBackgroundColor = item->background();
4623 }
4624 item = pColorManager->getItem(schemeName,COLOR_SCHEME_BREAKPOINT);
4625 if (item) {
4626 this->mBreakpointForegroundColor = item->foreground();
4627 this->mBreakpointBackgroundColor = item->background();
4628 }
4629 item = pColorManager->getItem(schemeName,COLOR_SCHEME_TEXT);
4630 if (item) {
4631 this->setForegroundColor(item->foreground());
4632 this->setBackgroundColor(item->background());
4633 } else {
4634 this->setForegroundColor(palette().color(QPalette::Text));
4635 this->setBackgroundColor(palette().color(QPalette::Base));
4636 }
4637 item = pColorManager->getItem(schemeName,COLOR_SCHEME_CURRENT_HIGHLIGHTED_WORD);
4638 if (item) {
4639 mCurrentHighlighWordForeground = item->foreground();
4640 mCurrentHighlighWordBackground = item->background();
4641 } else {
4642 mCurrentHighlighWordForeground = selectedForeground();
4643 mCurrentHighlighWordBackground = selectedBackground();
4644 }
4645
4646 this->invalidate();
4647}
4648
4649void Editor::updateCaption(const QString& newCaption) {
4650 if (mParentPageControl==nullptr) {
4651 return;
4652 }
4653 int index = mParentPageControl->indexOf(this);
4654 if (index==-1)
4655 return;
4656 if (newCaption.isEmpty()) {
4657 QString caption = QFileInfo(mFilename).fileName();
4658 if (this->modified()) {
4659 caption.append("[*]");
4660 }
4661 if (this->readOnly()) {
4662 caption.append("["+tr("Readonly")+"]");
4663 }
4664 mParentPageControl->setTabText(index,caption);
4665 } else {
4666 mParentPageControl->setTabText(index,newCaption);
4667 }
4668
4669}
4670