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 | |
55 | using namespace std; |
56 | |
57 | SaveException::SaveException(const QString& reason) { |
58 | mReason = reason; |
59 | mReasonBuffer = mReason.toLocal8Bit(); |
60 | } |
61 | SaveException::SaveException(const QString&& reason) { |
62 | mReason = reason; |
63 | mReasonBuffer = mReason.toLocal8Bit(); |
64 | } |
65 | const QString& SaveException::reason() const noexcept{ |
66 | return mReason; |
67 | } |
68 | const char* SaveException::what() const noexcept { |
69 | return mReasonBuffer; |
70 | } |
71 | |
72 | Editor::Editor(QWidget *parent): |
73 | Editor(parent,QObject::tr("untitled" ),ENCODING_AUTO_DETECT,false,true,nullptr) |
74 | { |
75 | } |
76 | |
77 | Editor::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 | |
194 | Editor::~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 | |
205 | void 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 | |
235 | void 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 | |
246 | void Editor::convertToEncoding(const QByteArray &encoding) |
247 | { |
248 | mEncodingOption = encoding; |
249 | setModified(true); |
250 | save(); |
251 | } |
252 | |
253 | bool 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 | |
289 | bool 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 | |
407 | void Editor::activate() |
408 | { |
409 | if (mParentPageControl!=nullptr) |
410 | mParentPageControl->setCurrentWidget(this); |
411 | setFocus(); |
412 | } |
413 | |
414 | const QByteArray& Editor::encodingOption() const noexcept{ |
415 | return mEncodingOption; |
416 | } |
417 | void 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 | } |
436 | const QByteArray& Editor::fileEncoding() const noexcept{ |
437 | return mFileEncoding; |
438 | } |
439 | const QString& Editor::filename() const noexcept{ |
440 | return mFilename; |
441 | } |
442 | bool Editor::inProject() const noexcept{ |
443 | return mInProject; |
444 | } |
445 | bool Editor::isNew() const noexcept { |
446 | return mIsNew; |
447 | } |
448 | |
449 | QTabWidget* Editor::pageControl() noexcept{ |
450 | return mParentPageControl; |
451 | } |
452 | static 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 | } |
459 | void 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 | |
475 | void 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 | |
524 | void 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 | |
546 | void 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 | |
557 | void 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 | |
567 | void 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 | |
834 | void 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 | |
868 | void 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 | |
898 | bool 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 | |
922 | void 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 | |
1031 | bool 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 | |
1198 | void 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 | |
1227 | void 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 | |
1268 | void 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 | |
1281 | void 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 | |
1307 | void 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 | |
1323 | void Editor::resizeEvent(QResizeEvent *event) |
1324 | { |
1325 | QSynedit::SynEdit::resizeEvent(event); |
1326 | pMainWindow->functionTip()->hide(); |
1327 | } |
1328 | |
1329 | void 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 | |
1354 | void 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 | |
1373 | void 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 | |
1411 | void Editor::setCaretPosition(int line, int aChar) |
1412 | { |
1413 | this->uncollapseAroundLine(line); |
1414 | this->setCaretXYCentered(QSynedit::BufferCoord{aChar,line}); |
1415 | } |
1416 | |
1417 | void 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 | |
1425 | void 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 | |
1467 | void Editor::clearSyntaxIssues() |
1468 | { |
1469 | mSyntaxIssues.clear(); |
1470 | } |
1471 | |
1472 | void 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 | |
1486 | void 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 | |
1501 | bool 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 | |
1512 | bool 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 | |
1522 | Editor::PSyntaxIssueList Editor::getSyntaxIssuesAtLine(int line) |
1523 | { |
1524 | if (mSyntaxIssues.contains(line)) |
1525 | return mSyntaxIssues[line]; |
1526 | return PSyntaxIssueList(); |
1527 | } |
1528 | |
1529 | Editor::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 | |
1541 | void 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 | |
1675 | void Editor::onGutterClicked(Qt::MouseButton button, int , int , int line) |
1676 | { |
1677 | if (button == Qt::LeftButton) { |
1678 | toggleBreakpoint(line); |
1679 | } |
1680 | mGutterClickedLine = line; |
1681 | } |
1682 | |
1683 | void 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 | |
1698 | void 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 | |
1710 | void 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 | |
1722 | void Editor::onFunctionTipsTimer() |
1723 | { |
1724 | mFunctionTipTimer.stop(); |
1725 | updateFunctionTip(true); |
1726 | } |
1727 | |
1728 | bool 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 | |
1743 | void Editor::resetBookmarks() |
1744 | { |
1745 | mBookmarkLines=pMainWindow->bookmarkModel()->bookmarksInFile(mFilename); |
1746 | invalidate(); |
1747 | } |
1748 | |
1749 | void 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 | |
1761 | bool Editor::notParsed() |
1762 | { |
1763 | if (!mParser) |
1764 | return true; |
1765 | return mParser->findFileIncludes(mFilename)==nullptr; |
1766 | } |
1767 | |
1768 | void Editor::insertLine() |
1769 | { |
1770 | ExecuteCommand(QSynedit::EditCommand::ecInsertLine,QChar(),nullptr); |
1771 | } |
1772 | |
1773 | void Editor::deleteWord() |
1774 | { |
1775 | ExecuteCommand(QSynedit::EditCommand::ecDeleteWord,QChar(),nullptr); |
1776 | } |
1777 | |
1778 | void Editor::deleteToWordStart() |
1779 | { |
1780 | ExecuteCommand(QSynedit::EditCommand::ecDeleteWordStart,QChar(),nullptr); |
1781 | } |
1782 | |
1783 | void Editor::deleteToWordEnd() |
1784 | { |
1785 | ExecuteCommand(QSynedit::EditCommand::ecDeleteWordEnd,QChar(),nullptr); |
1786 | } |
1787 | |
1788 | void Editor::deleteLine() |
1789 | { |
1790 | ExecuteCommand(QSynedit::EditCommand::ecDeleteLine,QChar(),nullptr); |
1791 | } |
1792 | |
1793 | void Editor::duplicateLine() |
1794 | { |
1795 | ExecuteCommand(QSynedit::EditCommand::ecDuplicateLine,QChar(),nullptr); |
1796 | } |
1797 | |
1798 | void Editor::deleteToEOL() |
1799 | { |
1800 | ExecuteCommand(QSynedit::EditCommand::ecDeleteEOL,QChar(),nullptr); |
1801 | } |
1802 | |
1803 | void Editor::deleteToBOL() |
1804 | { |
1805 | ExecuteCommand(QSynedit::EditCommand::ecDeleteBOL,QChar(),nullptr); |
1806 | } |
1807 | |
1808 | QStringList 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 | |
1817 | QStringList 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 | |
2041 | QString 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 | |
2064 | QChar Editor::getCurrentChar() |
2065 | { |
2066 | if (lineText().length()<caretX()) |
2067 | return QChar(); |
2068 | else |
2069 | return lineText().at(caretX()-1); |
2070 | } |
2071 | |
2072 | bool 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 | |
2187 | bool 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 | |
2217 | bool 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 | |
2247 | bool 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 | |
2275 | bool 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 | |
2298 | bool 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 | |
2321 | bool 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 | |
2361 | bool 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 | |
2390 | bool 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 | |
2429 | bool 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 | |
2469 | bool 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 | |
2487 | bool 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 | |
2502 | bool 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 | |
2539 | void 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 | |
2557 | Editor::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 | |
2678 | void 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 | |
2695 | void Editor::reparseTodo() |
2696 | { |
2697 | if (!highlighter()) |
2698 | return; |
2699 | pMainWindow->todoParser()->parseFile(mFilename); |
2700 | } |
2701 | |
2702 | void 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 | |
2716 | void 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 | |
2803 | void 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 | |
2840 | void 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 | |
2865 | void 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 | |
2890 | void 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 | |
3021 | void Editor::(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 | |
3074 | bool 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 | |
3115 | void 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 | |
3201 | void Editor::() |
3202 | { |
3203 | QString = 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 | |
3237 | bool 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 | |
3312 | bool Editor::(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 | |
3369 | bool 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 | |
3384 | Editor::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 | |
3414 | void 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 | |
3430 | QString 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 | |
3439 | QString 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 | |
3487 | void 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 | |
3509 | QString Editor::getErrorHint(const PSyntaxIssue& issue) |
3510 | { |
3511 | if (issue) { |
3512 | return issue->hint; |
3513 | } else { |
3514 | return "" ; |
3515 | } |
3516 | } |
3517 | |
3518 | QString 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 | |
3543 | void 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 | |
3759 | void Editor::clearUserCodeInTabStops() |
3760 | { |
3761 | mUserCodeInTabStops.clear(); |
3762 | } |
3763 | |
3764 | void 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 | |
3802 | void 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 | |
3872 | void Editor::onScrollBarValueChanged() |
3873 | { |
3874 | pMainWindow->functionTip()->hide(); |
3875 | } |
3876 | |
3877 | bool Editor::canAutoSave() const |
3878 | { |
3879 | return mCanAutoSave; |
3880 | } |
3881 | |
3882 | void Editor::setCanAutoSave(bool newCanAutoSave) |
3883 | { |
3884 | mCanAutoSave = newCanAutoSave; |
3885 | } |
3886 | |
3887 | const QDateTime &Editor::hideTime() const |
3888 | { |
3889 | return mHideTime; |
3890 | } |
3891 | |
3892 | void Editor::setHideTime(const QDateTime &newHideTime) |
3893 | { |
3894 | mHideTime = newHideTime; |
3895 | } |
3896 | |
3897 | const std::shared_ptr<QHash<StatementKind, std::shared_ptr<ColorSchemeItem> > > &Editor::statementColors() const |
3898 | { |
3899 | return mStatementColors; |
3900 | } |
3901 | |
3902 | void Editor::setStatementColors(const std::shared_ptr<QHash<StatementKind, std::shared_ptr<ColorSchemeItem> > > &newStatementColors) |
3903 | { |
3904 | mStatementColors = newStatementColors; |
3905 | } |
3906 | |
3907 | bool Editor::useCppSyntax() const |
3908 | { |
3909 | return mUseCppSyntax; |
3910 | } |
3911 | |
3912 | void Editor::setUseCppSyntax(bool newUseCppSyntax) |
3913 | { |
3914 | mUseCppSyntax = newUseCppSyntax; |
3915 | } |
3916 | |
3917 | void 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 | |
3939 | void 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 | |
3977 | void 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 | |
4006 | QString 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 | |
4218 | QString 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 | |
4279 | void 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 | |
4331 | void 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 | |
4343 | const PCppParser &Editor::parser() |
4344 | { |
4345 | return mParser; |
4346 | } |
4347 | |
4348 | void 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 | |
4369 | int Editor::gutterClickedLine() const |
4370 | { |
4371 | return mGutterClickedLine; |
4372 | } |
4373 | |
4374 | void 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 | |
4388 | void Editor::clearBreakpoints() |
4389 | { |
4390 | pMainWindow->debugger()->deleteBreakpoints(this); |
4391 | mBreakpointLines.clear(); |
4392 | invalidate(); |
4393 | } |
4394 | |
4395 | bool Editor::hasBreakpoint(int line) |
4396 | { |
4397 | return mBreakpointLines.contains(line); |
4398 | } |
4399 | |
4400 | void Editor::addBookmark(int line, const QString& description) |
4401 | { |
4402 | mBookmarkLines.insert(line); |
4403 | pMainWindow->bookmarkModel()->addBookmark(mFilename,line,description); |
4404 | invalidateGutterLine(line); |
4405 | } |
4406 | |
4407 | void Editor::removeBookmark(int line) |
4408 | { |
4409 | mBookmarkLines.remove(line); |
4410 | pMainWindow->bookmarkModel()->removeBookmark(mFilename,line); |
4411 | invalidateGutterLine(line); |
4412 | } |
4413 | |
4414 | bool Editor::hasBookmark(int line) |
4415 | { |
4416 | return mBookmarkLines.contains(line); |
4417 | } |
4418 | |
4419 | void Editor::clearBookmarks() |
4420 | { |
4421 | mBookmarkLines.clear(); |
4422 | pMainWindow->bookmarkModel()->removeBookmarks(mFilename); |
4423 | invalidateGutter(); |
4424 | } |
4425 | |
4426 | void Editor::removeBreakpointFocus() |
4427 | { |
4428 | if (mActiveBreakpointLine!=-1) { |
4429 | int oldLine = mActiveBreakpointLine; |
4430 | mActiveBreakpointLine = -1; |
4431 | invalidateGutterLine(oldLine); |
4432 | invalidateLine(oldLine); |
4433 | } |
4434 | } |
4435 | |
4436 | void 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 | |
4453 | void 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 | |
4472 | void 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 | |
4555 | static 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 | } |
4565 | void 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 | |
4649 | void 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 | |