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 "codecompletionpopup.h"
18#include "../utils.h"
19#include "../mainwindow.h"
20#include "../editor.h"
21#include "../editorlist.h"
22#include "../symbolusagemanager.h"
23#include "../colorscheme.h"
24#include "../iconsmanager.h"
25
26#include <QKeyEvent>
27#include <QVBoxLayout>
28#include <QDebug>
29#include <QApplication>
30#include <QPainter>
31
32CodeCompletionPopup::CodeCompletionPopup(QWidget *parent) :
33 QWidget(parent),
34 mMutex(QMutex::Recursive)
35{
36 setWindowFlags(Qt::Popup);
37 mListView = new CodeCompletionListView(this);
38 mModel=new CodeCompletionListModel(&mCompletionStatementList);
39 mDelegate = new CodeCompletionListItemDelegate(mModel,this);
40 mListView->setModel(mModel);
41 mListView->setItemDelegate(mDelegate);
42 setLayout(new QVBoxLayout());
43 layout()->addWidget(mListView);
44 layout()->setMargin(0);
45
46 mShowKeywords=true;
47 mUseCppKeyword=true;
48 mRecordUsage = false;
49 mSortByScope = true;
50
51 mOnlyGlobals = false;
52 mShowCount = 1000;
53 mShowCodeSnippets = true;
54
55 mIgnoreCase = false;
56
57 mHideSymbolsStartWithTwoUnderline = false;
58 mHideSymbolsStartWithUnderline = false;
59}
60
61CodeCompletionPopup::~CodeCompletionPopup()
62{
63 delete mListView;
64 delete mModel;
65}
66
67void CodeCompletionPopup::setKeypressedCallback(const KeyPressedCallback &newKeypressedCallback)
68{
69 mListView->setKeypressedCallback(newKeypressedCallback);
70}
71
72void CodeCompletionPopup::prepareSearch(
73 const QString &preWord,
74 const QStringList &ownerExpression,
75 const QString& memberOperator,
76 const QStringList& memberExpression,
77 const QString &filename,
78 int line,
79 const QSet<QString>& customKeywords)
80{
81 QMutexLocker locker(&mMutex);
82 if (!isEnabled())
83 return;
84 //Screen.Cursor := crHourglass;
85 QCursor oldCursor = cursor();
86 setCursor(Qt::CursorShape::WaitCursor);
87
88 mMemberPhrase = memberExpression.join("");
89 mMemberOperator = memberOperator;
90 if (preWord.isEmpty()) {
91 mIncludedFiles = mParser->getFileIncludes(filename);
92 getCompletionFor(ownerExpression,memberOperator,memberExpression, filename,line, customKeywords);
93 } else {
94 getCompletionListForPreWord(preWord);
95 }
96
97 setCursor(oldCursor);
98}
99
100bool CodeCompletionPopup::search(const QString &memberPhrase, bool autoHideOnSingleResult)
101{
102 QMutexLocker locker(&mMutex);
103
104 mMemberPhrase = memberPhrase;
105
106 if (!isEnabled()) {
107 hide();
108 return false;
109 }
110
111 QCursor oldCursor = cursor();
112 setCursor(Qt::CursorShape::WaitCursor);
113
114 // filter fFullCompletionStatementList to fCompletionStatementList
115 filterList(memberPhrase);
116
117 //if can't find a destructor, maybe '~' is only an operator
118// if (mCompletionStatementList.isEmpty() && phrase.startsWith('~')) {
119// symbol = phrase.mid(1);
120// filterList(symbol);
121// }
122
123 mModel->notifyUpdated();
124 setCursor(oldCursor);
125
126 if (!mCompletionStatementList.isEmpty()) {
127 PColorSchemeItem item = mColors->value(StatementKind::skUnknown,PColorSchemeItem());
128 if (item)
129 mDelegate->setNormalColor(item->foreground());
130 else
131 mDelegate->setNormalColor(palette().color(QPalette::Text));
132 item = mColors->value(StatementKind::skKeyword,PColorSchemeItem());
133 if (item)
134 mDelegate->setMatchedColor(item->foreground());
135 else
136 mDelegate->setMatchedColor(palette().color(QPalette::HighlightedText));
137 mListView->setCurrentIndex(mModel->index(0,0));
138 // if only one suggestion, and is exactly the symbol to search, hide the frame (the search is over)
139 // if only one suggestion and auto hide , don't show the frame
140 if(mCompletionStatementList.count() == 1)
141 if (autoHideOnSingleResult
142 || (memberPhrase == mCompletionStatementList.front()->command)) {
143 return true;
144 }
145 } else {
146 hide();
147 }
148 return false;
149}
150
151PStatement CodeCompletionPopup::selectedStatement()
152{
153 if (isEnabled()) {
154 int index = mListView->currentIndex().row();
155 if (mListView->currentIndex().isValid()
156 && (index<mCompletionStatementList.count()) ) {
157 return mCompletionStatementList[index];
158 } else {
159 if (!mCompletionStatementList.isEmpty())
160 return mCompletionStatementList.front();
161 else
162 return PStatement();
163 }
164 } else
165 return PStatement();
166}
167
168void CodeCompletionPopup::addChildren(PStatement scopeStatement, const QString &fileName, int line)
169{
170 if (scopeStatement && !isIncluded(scopeStatement->fileName)
171 && !isIncluded(scopeStatement->definitionFileName))
172 return;
173 const StatementMap& children = mParser->statementList().childrenStatements(scopeStatement);
174 if (children.isEmpty())
175 return;
176
177 if (!scopeStatement) { //Global scope
178 for (const PStatement& childStatement: children) {
179 if (childStatement->fileName.isEmpty()) {
180 // hard defines
181 addStatement(childStatement,fileName,-1);
182 } else if (!( childStatement->kind == StatementKind::skConstructor
183 || childStatement->kind == StatementKind::skDestructor
184 || childStatement->kind == StatementKind::skBlock)
185 && (!mAddedStatements.contains(childStatement->command))
186 && (
187 isIncluded(childStatement->fileName)
188 || isIncluded(childStatement->definitionFileName)
189 )
190 ) {
191 //we must check if the statement is included by the file
192 addStatement(childStatement,fileName,line);
193 }
194 }
195 } else {
196 for (const PStatement& childStatement: children) {
197 if (!( childStatement->kind == StatementKind::skConstructor
198 || childStatement->kind == StatementKind::skDestructor
199 || childStatement->kind == StatementKind::skBlock)
200 && (!mAddedStatements.contains(childStatement->command)))
201 addStatement(childStatement,fileName,line);
202 }
203 }
204}
205
206void CodeCompletionPopup::addStatement(PStatement statement, const QString &fileName, int line)
207{
208 if (mAddedStatements.contains(statement->command))
209 return;
210 if ((line!=-1)
211 && (line < statement->line)
212 && (fileName == statement->fileName))
213 return;
214 mAddedStatements.insert(statement->command);
215 mFullCompletionStatementList.append(statement);
216}
217
218static bool nameComparator(PStatement statement1,PStatement statement2) {
219 return statement1->command < statement2->command;
220}
221
222static bool defaultComparator(PStatement statement1,PStatement statement2) {
223 if (statement1->matchPosSpan!=statement2->matchPosSpan)
224 return statement1->matchPosSpan < statement2->matchPosSpan;
225 if (statement1->firstMatchLength != statement2->firstMatchLength)
226 return statement1->firstMatchLength > statement2->firstMatchLength;
227 if (statement1->matchPosTotal != statement2->matchPosTotal)
228 return statement1->matchPosTotal < statement2->matchPosTotal;
229 if (statement1->caseMatched != statement2->caseMatched)
230 return statement1->caseMatched > statement2->caseMatched;
231 // Show user template first
232 if (statement1->kind == StatementKind::skUserCodeSnippet) {
233 if (statement2->kind != StatementKind::skUserCodeSnippet)
234 return true;
235 else
236 return statement1->command < statement2->command;
237 } else if (statement2->kind == StatementKind::skUserCodeSnippet) {
238 return false;
239 // show keywords first
240 } else if ((statement1->kind == StatementKind::skKeyword)
241 && (statement2->kind != StatementKind::skKeyword)) {
242 return true;
243 } else if ((statement1->kind != StatementKind::skKeyword)
244 && (statement2->kind == StatementKind::skKeyword)) {
245 return false;
246 } else
247 return nameComparator(statement1,statement2);
248}
249
250static bool sortByScopeComparator(PStatement statement1,PStatement statement2){
251 if (statement1->matchPosSpan!=statement2->matchPosSpan)
252 return statement1->matchPosSpan < statement2->matchPosSpan;
253 if (statement1->firstMatchLength != statement2->firstMatchLength)
254 return statement1->firstMatchLength > statement2->firstMatchLength;
255 if (statement1->matchPosTotal != statement2->matchPosTotal)
256 return statement1->matchPosTotal < statement2->matchPosTotal;
257 if (statement1->caseMatched != statement2->caseMatched)
258 return statement1->caseMatched > statement2->caseMatched;
259 // Show user template first
260 if (statement1->kind == StatementKind::skUserCodeSnippet) {
261 if (statement2->kind != StatementKind::skUserCodeSnippet)
262 return true;
263 else
264 return statement1->command < statement2->command;
265 } else if (statement2->kind == StatementKind::skUserCodeSnippet) {
266 return false;
267 // show non-system defines before keyword
268 } else if (statement1->kind == StatementKind::skKeyword) {
269 if (statement2->kind != StatementKind::skKeyword) {
270 //s1 keyword / s2 system defines, s1 < s2, should return true
271 //s1 keyword / s2 not system defines, s2 < s1, should return false;
272 return statement2->inSystemHeader;
273 } else
274 return statement1->command < statement2->command;
275 } else if (statement2->kind == StatementKind::skKeyword) {
276 //s1 system defines / s2 keyword, s2 < s1, should return false;
277 //s1 not system defines / s2 keyword, s1 < s2, should return true;
278 return (!statement1->inSystemHeader);
279 }
280 // Show stuff from local headers first
281 if (statement1->inSystemHeader != statement2->inSystemHeader)
282 return !(statement1->inSystemHeader);
283 // Show local statements first
284 if (statement1->scope != StatementScope::ssGlobal
285 && statement2->scope == StatementScope::ssGlobal ) {
286 return true;
287 } else if (statement1->scope == StatementScope::ssGlobal
288 && statement2->scope != StatementScope::ssGlobal ) {
289 return false;
290 } else
291 return nameComparator(statement1,statement2);
292}
293
294static bool sortWithUsageComparator(PStatement statement1,PStatement statement2) {
295 if (statement1->matchPosSpan!=statement2->matchPosSpan)
296 return statement1->matchPosSpan < statement2->matchPosSpan;
297 if (statement1->firstMatchLength != statement2->firstMatchLength)
298 return statement1->firstMatchLength > statement2->firstMatchLength;
299 if (statement1->matchPosTotal != statement2->matchPosTotal)
300 return statement1->matchPosTotal < statement2->matchPosTotal;
301 if (statement1->caseMatched != statement2->caseMatched)
302 return statement1->caseMatched > statement2->caseMatched;
303 // Show user template first
304 if (statement1->kind == StatementKind::skUserCodeSnippet) {
305 if (statement2->kind != StatementKind::skUserCodeSnippet)
306 return true;
307 else
308 return statement1->command < statement2->command;
309 } else if (statement2->kind == StatementKind::skUserCodeSnippet) {
310 return false;
311 //show most freq first
312 }
313 if (statement1->usageCount != statement2->usageCount)
314 return statement1->usageCount > statement2->usageCount;
315
316 if ((statement1->kind != StatementKind::skKeyword)
317 && (statement2->kind == StatementKind::skKeyword)) {
318 return true;
319 } else if ((statement1->kind == StatementKind::skKeyword)
320 && (statement2->kind != StatementKind::skKeyword)) {
321 return false;
322 } else
323 return nameComparator(statement1,statement2);
324}
325
326static bool sortByScopeWithUsageComparator(PStatement statement1,PStatement statement2){
327 if (statement1->matchPosSpan!=statement2->matchPosSpan)
328 return statement1->matchPosSpan < statement2->matchPosSpan;
329 if (statement1->firstMatchLength != statement2->firstMatchLength)
330 return statement1->firstMatchLength > statement2->firstMatchLength;
331 if (statement1->matchPosTotal != statement2->matchPosTotal)
332 return statement1->matchPosTotal < statement2->matchPosTotal;
333 if (statement1->caseMatched != statement2->caseMatched)
334 return statement1->caseMatched > statement2->caseMatched;
335 // Show user template first
336 if (statement1->kind == StatementKind::skUserCodeSnippet) {
337 if (statement2->kind != StatementKind::skUserCodeSnippet)
338 return true;
339 else
340 return statement1->command < statement2->command;
341 } else if (statement2->kind == StatementKind::skUserCodeSnippet) {
342 return false;
343 //show most freq first
344 }
345 if (statement1->usageCount != statement2->usageCount)
346 return statement1->usageCount > statement2->usageCount;
347
348 // show non-system defines before keyword
349 if (statement1->kind == StatementKind::skKeyword) {
350 if (statement2->kind != StatementKind::skKeyword) {
351 //s1 keyword / s2 system defines, s1 < s2, should return true
352 //s1 keyword / s2 not system defines, s2 < s1, should return false;
353 return statement2->inSystemHeader;
354 } else
355 return statement1->command < statement2->command;
356 } else if (statement2->kind == StatementKind::skKeyword) {
357 //s1 system defines / s2 keyword, s2 < s1, should return false;
358 //s1 not system defines / s2 keyword, s1 < s2, should return true;
359 return (!statement1->inSystemHeader);
360 }
361 // Show stuff from local headers first
362 if (statement1->inSystemHeader != statement2->inSystemHeader)
363 return !(statement1->inSystemHeader);
364 // Show local statements first
365 if (statement1->scope != StatementScope::ssGlobal
366 && statement2->scope == StatementScope::ssGlobal ) {
367 return true;
368 } else if (statement1->scope == StatementScope::ssGlobal
369 && statement2->scope != StatementScope::ssGlobal ) {
370 return false;
371 } else
372 return nameComparator(statement1,statement2);
373}
374
375void CodeCompletionPopup::filterList(const QString &member)
376{
377 QMutexLocker locker(&mMutex);
378 mCompletionStatementList.clear();
379 if (!mParser)
380 return;
381 if (!mParser->enabled())
382 return;
383 //we don't need to freeze here since we use smart pointers
384 // and data have been retrieved from the parser
385// if (!mParser->freeze())
386// return;
387// {
388// auto action = finally([this]{
389// mParser->unFreeze();
390// });
391// if (mParserSerialId!=mParser->serialId()) {
392// return;
393// }
394
395 mCompletionStatementList.clear();
396 mCompletionStatementList.reserve(mFullCompletionStatementList.size());
397 bool hideSymbolsTwoUnderline = mHideSymbolsStartWithTwoUnderline && !member.startsWith("__") ;
398 bool hideSymbolsUnderline = mHideSymbolsStartWithUnderline && !member.startsWith("_") ;
399 int len = member.length();
400 foreach (const PStatement& statement, mFullCompletionStatementList) {
401
402 int matched = 0;
403 int caseMatched = 0;
404 QString command = statement->command;
405 int pos = 0;
406 int lastPos = -10;
407 int totalPos = 0;
408 statement->matchPositions.clear();
409 if (hideSymbolsTwoUnderline && statement->command.startsWith("__")) {
410
411 } else if (hideSymbolsUnderline && statement->command.startsWith("_")) {
412
413 } else {
414 foreach (const QChar& ch, member) {
415 if (mIgnoreCase)
416 pos = command.indexOf(ch,pos,Qt::CaseInsensitive);
417 else
418 pos = command.indexOf(ch,pos,Qt::CaseSensitive);
419 if (pos<0) {
420 break;
421 }
422 if (pos == lastPos+1) {
423 statement->matchPositions.last()->end++;
424 } else {
425 PStatementMathPosition matchPosition=std::make_shared<StatementMatchPosition>();
426 matchPosition->start = pos;
427 matchPosition->end = pos+1;
428 statement->matchPositions.append(matchPosition);
429 }
430 if (ch==command[pos])
431 caseMatched++;
432 matched++;
433 totalPos += pos;
434 lastPos = pos;
435 pos+=1;
436 }
437 }
438
439 if (mIgnoreCase && matched== len) {
440 statement->caseMatched = caseMatched;
441 statement->matchPosTotal = totalPos;
442 if (member.length()>0) {
443 statement->firstMatchLength = statement->matchPositions.front()->end - statement->matchPositions.front()->start;
444 statement->matchPosSpan = statement->matchPositions.last()->end - statement->matchPositions.front()->start;
445 } else
446 statement->firstMatchLength = 0;
447 mCompletionStatementList.append(statement);
448 } else if (caseMatched == len) {
449 statement->caseMatched = caseMatched;
450 statement->matchPosTotal = totalPos;
451 if (member.length()>0) {
452 statement->firstMatchLength = statement->matchPositions.front()->end - statement->matchPositions.front()->start;
453 statement->matchPosSpan = statement->matchPositions.last()->end - statement->matchPositions.front()->start;
454 } else
455 statement->firstMatchLength = 0;
456 mCompletionStatementList.append(statement);
457 } else {
458 statement->matchPositions.clear();
459 statement->caseMatched = 0;
460 statement->matchPosTotal = 0;
461 statement->firstMatchLength = 0;
462 statement->matchPosSpan = 0;
463 }
464 }
465 if (mRecordUsage) {
466 int usageCount;
467 foreach (const PStatement& statement,mCompletionStatementList) {
468 if (statement->usageCount == -1) {
469 PSymbolUsage usage = pMainWindow->symbolUsageManager()->findUsage(statement->fullName);
470 if (usage) {
471 usageCount = usage->count;
472 } else {
473 usageCount = 0;
474 }
475 statement->usageCount = usageCount;
476 }
477 }
478 if (mSortByScope) {
479 std::sort(mCompletionStatementList.begin(),
480 mCompletionStatementList.end(),
481 sortByScopeWithUsageComparator);
482 } else {
483 std::sort(mCompletionStatementList.begin(),
484 mCompletionStatementList.end(),
485 sortWithUsageComparator);
486 }
487 } else if (mSortByScope) {
488 std::sort(mCompletionStatementList.begin(),
489 mCompletionStatementList.end(),
490 sortByScopeComparator);
491 } else {
492 std::sort(mCompletionStatementList.begin(),
493 mCompletionStatementList.end(),
494 defaultComparator);
495 }
496 // }
497}
498
499void CodeCompletionPopup::getCompletionFor(
500 const QStringList &ownerExpression,
501 const QString& memberOperator,
502 const QStringList& memberExpression,
503 const QString &fileName,
504 int line,
505 const QSet<QString>& customKeywords)
506{
507 if(!mParser) {
508 if (mShowKeywords) {
509 //add keywords
510 if (!customKeywords.isEmpty()) {
511 foreach (const QString& keyword,customKeywords) {
512 addKeyword(keyword);
513 }
514 } else if (mUseCppKeyword) {
515 foreach (const QString& keyword,CppKeywords.keys()) {
516 addKeyword(keyword);
517 }
518 } else {
519 foreach (const QString& keyword,CKeywords) {
520 addKeyword(keyword);
521 }
522 }
523 }
524 return;
525 }
526 if (!mParser->enabled())
527 return;
528 if (memberOperator.isEmpty() && ownerExpression.isEmpty() && memberExpression.isEmpty())
529 return;
530
531 if (!mParser->freeze())
532 return;
533 {
534 auto action = finally([this]{
535 mParser->unFreeze();
536 });
537
538 if (memberOperator.isEmpty()) {
539 //C++ preprocessor directives
540 if (mMemberPhrase.startsWith('#')) {
541 if (mShowKeywords) {
542 foreach (const QString& keyword, CppDirectives) {
543 addKeyword(keyword);
544 }
545 }
546 return;
547 }
548
549 //docstring tags (javadoc style)
550 if (mMemberPhrase.startsWith('@')) {
551 if (mShowKeywords) {
552 foreach (const QString& keyword,JavadocTags) {
553 addKeyword(keyword);
554 }
555 }
556 return;
557 }
558
559 //the identifier to be completed is not a member of variable/class
560 if (mShowCodeSnippets) {
561 //add custom code templates
562 foreach (const PCodeSnippet& codeIn,mCodeSnippets) {
563 if (!codeIn->code.isEmpty()) {
564 PStatement statement = std::make_shared<Statement>();
565 statement->command = codeIn->prefix;
566 statement->value = codeIn->code;
567 statement->kind = StatementKind::skUserCodeSnippet;
568 statement->fullName = codeIn->prefix;
569 statement->usageCount = 0;
570 mFullCompletionStatementList.append(statement);
571 }
572 }
573 }
574
575 if (mShowKeywords) {
576 //add keywords
577 if (!customKeywords.isEmpty()) {
578 foreach (const QString& keyword,customKeywords) {
579 addKeyword(keyword);
580 }
581 } else if (mUseCppKeyword) {
582 foreach (const QString& keyword,CppKeywords.keys()) {
583 addKeyword(keyword);
584 }
585 } else {
586 foreach (const QString& keyword,CKeywords) {
587 addKeyword(keyword);
588 }
589 }
590 }
591
592 PStatement scopeStatement = mCurrentStatement;
593 // repeat until reach global
594 while (scopeStatement) {
595 //add members of current scope that not added before
596 if (scopeStatement->kind == StatementKind::skClass) {
597 addChildren(scopeStatement, fileName, -1);
598 } else {
599 addChildren(scopeStatement, fileName, line);
600 }
601
602 // add members of all usings (in current scope ) and not added before
603 foreach (const QString& namespaceName,scopeStatement->usingList) {
604 PStatementList namespaceStatementsList =
605 mParser->findNamespace(namespaceName);
606 if (!namespaceStatementsList)
607 continue;
608 foreach (const PStatement& namespaceStatement,*namespaceStatementsList) {
609 addChildren(namespaceStatement, fileName, line);
610 }
611 }
612 scopeStatement=scopeStatement->parentScope.lock();
613 }
614
615 // add all global members and not added before
616 addChildren(nullptr, fileName, line);
617
618 // add members of all fusings
619 mUsings = mParser->getFileUsings(fileName);
620 foreach (const QString& namespaceName, mUsings) {
621 PStatementList namespaceStatementsList =
622 mParser->findNamespace(namespaceName);
623 if (!namespaceStatementsList)
624 continue;
625 foreach (const PStatement& namespaceStatement, *namespaceStatementsList) {
626 addChildren(namespaceStatement, fileName, line);
627 }
628 }
629
630 } else {
631 //the identifier to be completed is a member of variable/class
632 if (memberOperator == "::" && ownerExpression.isEmpty()) {
633 // start with '::', we only find in global
634 // add all global members and not added before
635 addChildren(nullptr, fileName, line);
636 return;
637 }
638 if (memberExpression.length()==2 && memberExpression.front()!="~")
639 return;
640 if (memberExpression.length()>2)
641 return;
642
643 PStatement scope = mCurrentStatement;//the scope the expression in
644 PStatement parentTypeStatement;
645// QString scopeName = ownerExpression.join("");
646// PStatement ownerStatement = mParser->findStatementOf(
647// fileName,
648// scopeName,
649// mCurrentStatement,
650// parentTypeStatement);
651 PEvalStatement ownerStatement = mParser->evalExpression(fileName,
652 ownerExpression,
653 scope);
654// qDebug()<<scopeName;
655// qDebug()<<memberOperator;
656// qDebug()<<memberExpression;
657 if(!ownerStatement || !ownerStatement->effectiveTypeStatement) {
658// qDebug()<<"statement not found!";
659 return;
660 }
661// qDebug()<<"found: "<<ownerStatement->fullName;
662 if (memberOperator == "::") {
663 if (ownerStatement->kind==EvalStatementKind::Namespace) {
664 //there might be many statements corresponding to one namespace;
665 PStatementList namespaceStatementsList =
666 mParser->findNamespace(ownerStatement->baseType);
667 if (namespaceStatementsList) {
668 foreach (const PStatement& namespaceStatement, *namespaceStatementsList) {
669 addChildren(namespaceStatement, fileName, line);
670 }
671 }
672 return;
673 }
674 }
675
676 // find the most inner scope statement that has a name (not a block)
677 PStatement scopeTypeStatement = mCurrentStatement;
678 while (scopeTypeStatement && !isScopeTypeKind(scopeTypeStatement->kind)) {
679 scopeTypeStatement = scopeTypeStatement->parentScope.lock();
680 }
681 if (
682 (memberOperator != "::")
683 && (
684 ownerStatement->kind == EvalStatementKind::Variable)
685 ) {
686 // Get type statement of current (scope) statement
687 PStatement classTypeStatement = ownerStatement->effectiveTypeStatement;
688
689 if (!classTypeStatement)
690 return;
691 //is a smart pointer
692 if (STLPointers.contains(classTypeStatement->fullName)
693 && (memberOperator == "->"
694 || memberOperator == "->*")
695 && ownerStatement->baseStatement) {
696 QString typeName= mParser->findFirstTemplateParamOf(
697 fileName,
698 ownerStatement->baseStatement->type,
699 scope);
700 classTypeStatement = mParser->findTypeDefinitionOf(
701 fileName,
702 typeName,
703 scope);
704 if (!classTypeStatement)
705 return;
706 }
707 if (!isIncluded(classTypeStatement->fileName) &&
708 !isIncluded(classTypeStatement->definitionFileName))
709 return;
710 if ((classTypeStatement == scopeTypeStatement) || (ownerStatement->effectiveTypeStatement->command == "this")) {
711 //we can use all members
712 addChildren(classTypeStatement,fileName,-1);
713 } else { // we can only use public members
714 const StatementMap& children = mParser->statementList().childrenStatements(classTypeStatement);
715 if (children.isEmpty())
716 return;
717 foreach (const PStatement& childStatement, children) {
718 if ((childStatement->classScope==StatementClassScope::scsPublic)
719 && !(
720 childStatement->kind == StatementKind::skConstructor
721 || childStatement->kind == StatementKind::skDestructor)
722 && !mAddedStatements.contains(childStatement->command)) {
723 addStatement(childStatement,fileName,-1);
724 }
725 }
726 }
727 //todo friend
728 } else if ((memberOperator == "::")
729 && (ownerStatement->kind == EvalStatementKind::Type)) {
730 //we can add all child enum definess
731 PStatement classTypeStatement = ownerStatement->effectiveTypeStatement;
732 if (!classTypeStatement)
733 return;
734 if (!isIncluded(classTypeStatement->fileName) &&
735 !isIncluded(classTypeStatement->definitionFileName))
736 return;
737 if (classTypeStatement->kind == StatementKind::skEnumType
738 || classTypeStatement->kind == StatementKind::skEnumClassType) {
739 const StatementMap& children =
740 mParser->statementList().childrenStatements(classTypeStatement);
741 foreach (const PStatement& child,children) {
742 addStatement(child,fileName,line);
743 }
744 } else {
745 //class
746 if (classTypeStatement == scopeTypeStatement) {
747 //we can use all static members
748 const StatementMap& children =
749 mParser->statementList().childrenStatements(classTypeStatement);
750 foreach (const PStatement& childStatement, children) {
751 if (
752 (childStatement->isStatic)
753 || (childStatement->kind == StatementKind::skTypedef
754 || childStatement->kind == StatementKind::skClass
755 || childStatement->kind == StatementKind::skEnum
756 || childStatement->kind == StatementKind::skEnumClassType
757 || childStatement->kind == StatementKind::skEnumType
758 )) {
759 addStatement(childStatement,fileName,-1);
760 }
761 }
762 } else {
763 // we can only use public static members
764 const StatementMap& children =
765 mParser->statementList().childrenStatements(classTypeStatement);
766 foreach (const PStatement& childStatement,children) {
767 if (
768 (childStatement->isStatic)
769 || (childStatement->kind == StatementKind::skTypedef
770 || childStatement->kind == StatementKind::skClass
771 || childStatement->kind == StatementKind::skEnum
772 || childStatement->kind == StatementKind::skEnumClassType
773 || childStatement->kind == StatementKind::skEnumType
774 )) {
775 if (childStatement->classScope == StatementClassScope::scsPublic)
776 addStatement(childStatement,fileName,-1);
777 }
778 }
779 }
780 }
781 }
782 }
783 }
784}
785
786void CodeCompletionPopup::getCompletionListForPreWord(const QString &preWord)
787{
788 mFullCompletionStatementList.clear();
789 if (preWord == "long") {
790 addKeyword("long");
791 addKeyword("double");
792 addKeyword("int");
793 } else if (preWord == "short") {
794 addKeyword("int");
795 } else if (preWord == "signed") {
796 addKeyword("long");
797 addKeyword("short");
798 addKeyword("int");
799 addKeyword("char");
800 } else if (preWord == "unsigned") {
801 addKeyword("long");
802 addKeyword("short");
803 addKeyword("int");
804 addKeyword("char");
805 }
806}
807
808void CodeCompletionPopup::addKeyword(const QString &keyword)
809{
810 PStatement statement = std::make_shared<Statement>();
811 statement->command = keyword;
812 statement->kind = StatementKind::skKeyword;
813 statement->fullName = keyword;
814 statement->usageCount = 0;
815 mFullCompletionStatementList.append(statement);
816}
817
818bool CodeCompletionPopup::isIncluded(const QString &fileName)
819{
820 return mIncludedFiles.contains(fileName);
821}
822
823void CodeCompletionPopup::setHideSymbolsStartWithTwoUnderline(bool newHideSymbolsStartWithTwoUnderline)
824{
825 mHideSymbolsStartWithTwoUnderline = newHideSymbolsStartWithTwoUnderline;
826}
827
828bool CodeCompletionPopup::hideSymbolsStartWithTwoUnderline() const
829{
830 return mHideSymbolsStartWithTwoUnderline;
831}
832
833bool CodeCompletionPopup::hideSymbolsStartWithUnderline() const
834{
835 return mHideSymbolsStartWithUnderline;
836}
837
838void CodeCompletionPopup::setHideSymbolsStartWithUnderline(bool newHideSymbolsStartWithUnderline)
839{
840 mHideSymbolsStartWithUnderline = newHideSymbolsStartWithUnderline;
841}
842
843const QString &CodeCompletionPopup::memberOperator() const
844{
845 return mMemberOperator;
846}
847
848const QList<PCodeSnippet> &CodeCompletionPopup::codeSnippets() const
849{
850 return mCodeSnippets;
851}
852
853void CodeCompletionPopup::setCodeSnippets(const QList<PCodeSnippet> &newCodeSnippets)
854{
855 mCodeSnippets = newCodeSnippets;
856}
857
858void CodeCompletionPopup::setColors(const std::shared_ptr<QHash<StatementKind, std::shared_ptr<ColorSchemeItem> > > &newColors)
859{
860 mColors = newColors;
861}
862
863const QString &CodeCompletionPopup::memberPhrase() const
864{
865 return mMemberPhrase;
866}
867
868void CodeCompletionPopup::showEvent(QShowEvent *)
869{
870 mListView->setFocus();
871}
872
873const PStatement &CodeCompletionPopup::currentStatement() const
874{
875 return mCurrentStatement;
876}
877
878void CodeCompletionPopup::setCurrentStatement(const PStatement &newCurrentStatement)
879{
880 mCurrentStatement = newCurrentStatement;
881}
882
883const std::shared_ptr<QHash<StatementKind, std::shared_ptr<ColorSchemeItem> > >& CodeCompletionPopup::colors() const
884{
885 return mColors;
886}
887
888bool CodeCompletionPopup::useCppKeyword() const
889{
890 return mUseCppKeyword;
891}
892
893void CodeCompletionPopup::setUseCppKeyword(bool newUseCppKeyword)
894{
895 mUseCppKeyword = newUseCppKeyword;
896}
897
898bool CodeCompletionPopup::sortByScope() const
899{
900 return mSortByScope;
901}
902
903void CodeCompletionPopup::setSortByScope(bool newSortByScope)
904{
905 mSortByScope = newSortByScope;
906}
907
908bool CodeCompletionPopup::ignoreCase() const
909{
910 return mIgnoreCase;
911}
912
913void CodeCompletionPopup::setIgnoreCase(bool newIgnoreCase)
914{
915 mIgnoreCase = newIgnoreCase;
916}
917
918bool CodeCompletionPopup::showCodeSnippets() const
919{
920 return mShowCodeSnippets;
921}
922
923void CodeCompletionPopup::setShowCodeSnippets(bool newShowCodeIns)
924{
925 mShowCodeSnippets = newShowCodeIns;
926}
927
928bool CodeCompletionPopup::showKeywords() const
929{
930 return mShowKeywords;
931}
932
933void CodeCompletionPopup::setShowKeywords(bool newShowKeywords)
934{
935 mShowKeywords = newShowKeywords;
936}
937
938bool CodeCompletionPopup::recordUsage() const
939{
940 return mRecordUsage;
941}
942
943void CodeCompletionPopup::setRecordUsage(bool newRecordUsage)
944{
945 mRecordUsage = newRecordUsage;
946}
947
948bool CodeCompletionPopup::onlyGlobals() const
949{
950 return mOnlyGlobals;
951}
952
953void CodeCompletionPopup::setOnlyGlobals(bool newOnlyGlobals)
954{
955 mOnlyGlobals = newOnlyGlobals;
956}
957
958int CodeCompletionPopup::showCount() const
959{
960 return mShowCount;
961}
962
963void CodeCompletionPopup::setShowCount(int newShowCount)
964{
965 mShowCount = newShowCount;
966}
967
968const PCppParser &CodeCompletionPopup::parser() const
969{
970 return mParser;
971}
972
973void CodeCompletionPopup::setParser(const PCppParser &newParser)
974{
975 mParser = newParser;
976}
977
978void CodeCompletionPopup::hideEvent(QHideEvent *event)
979{
980 QMutexLocker locker(&mMutex);
981 mListView->setKeypressedCallback(nullptr);
982 mCompletionStatementList.clear();
983// foreach (PStatement statement, mFullCompletionStatementList) {
984// statement->matchPositions.clear();
985// }
986 mFullCompletionStatementList.clear();
987 mIncludedFiles.clear();
988 mUsings.clear();
989 mAddedStatements.clear();
990 mCurrentStatement = nullptr;
991 mParser = nullptr;
992 QWidget::hideEvent(event);
993}
994
995bool CodeCompletionPopup::event(QEvent *event)
996{
997 bool result = QWidget::event(event);
998 if (event->type() == QEvent::FontChange) {
999 mListView->setFont(font());
1000 mDelegate->setFont(font());
1001 }
1002 return result;
1003}
1004
1005CodeCompletionListModel::CodeCompletionListModel(const StatementList *statements, QObject *parent):
1006 QAbstractListModel(parent),
1007 mStatements(statements)
1008{
1009
1010}
1011
1012int CodeCompletionListModel::rowCount(const QModelIndex &) const
1013{
1014 return mStatements->count();
1015}
1016
1017QVariant CodeCompletionListModel::data(const QModelIndex &index, int role) const
1018{
1019 if (!index.isValid())
1020 return QVariant();
1021 if (index.row()>=mStatements->count())
1022 return QVariant();
1023
1024 switch(role) {
1025 case Qt::DisplayRole: {
1026 PStatement statement = mStatements->at(index.row());
1027 return statement->command;
1028 }
1029 case Qt::DecorationRole:
1030 PStatement statement = mStatements->at(index.row());
1031 return pIconsManager->getPixmapForStatement(statement);
1032 }
1033 return QVariant();
1034}
1035
1036PStatement CodeCompletionListModel::statement(const QModelIndex &index) const
1037{
1038 if (!index.isValid())
1039 return PStatement();
1040 if (index.row()>=mStatements->count())
1041 return PStatement();
1042 return mStatements->at(index.row());
1043}
1044
1045QPixmap CodeCompletionListModel::statementIcon(const QModelIndex &index) const
1046{
1047 if (!index.isValid())
1048 return QPixmap();
1049 if (index.row()>=mStatements->count())
1050 return QPixmap();
1051 PStatement statement = mStatements->at(index.row());
1052 return pIconsManager->getPixmapForStatement(statement);
1053}
1054
1055void CodeCompletionListModel::notifyUpdated()
1056{
1057 beginResetModel();
1058 endResetModel();
1059}
1060
1061void CodeCompletionListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
1062{
1063 PStatement statement;
1064 if (mModel && (statement = mModel->statement(index)) ) {
1065 painter->save();
1066 painter->setFont(font());
1067 QColor normalColor = mNormalColor;
1068 if (option.state & QStyle::State_Selected) {
1069 painter->fillRect(option.rect, option.palette.highlight());
1070 normalColor = option.palette.color(QPalette::HighlightedText);
1071 }
1072 QPixmap icon = mModel->statementIcon(index);
1073 int x=option.rect.left();
1074 if (!icon.isNull()) {
1075 painter->drawPixmap(x+(option.rect.height()-icon.width())/2,option.rect.top()+(option.rect.height()-icon.height())/2,icon);
1076 x+=option.rect.height();
1077 }
1078 QString text = statement->command;
1079 int pos=0;
1080 int y=option.rect.bottom()-painter->fontMetrics().descent();
1081 foreach (const PStatementMathPosition& matchPosition, statement->matchPositions) {
1082 if (pos<matchPosition->start) {
1083 QString t = text.mid(pos,matchPosition->start-pos);
1084 painter->setPen(normalColor);
1085 painter->drawText(x,y,t);
1086 x+=painter->fontMetrics().horizontalAdvance(t);
1087 }
1088 QString t = text.mid(matchPosition->start, matchPosition->end-matchPosition->start);
1089 painter->setPen(mMatchedColor);
1090 painter->drawText(x,y,t);
1091 x+=painter->fontMetrics().horizontalAdvance(t);
1092 pos=matchPosition->end;
1093 }
1094 if (pos<text.length()) {
1095 QString t = text.mid(pos,text.length()-pos);
1096 painter->setPen(normalColor);
1097 painter->drawText(x,y,t);
1098 x+=painter->fontMetrics().horizontalAdvance(t);
1099 }
1100 painter->restore();
1101 } else {
1102 QStyledItemDelegate::paint(painter, option, index);
1103 }
1104}
1105
1106CodeCompletionListModel *CodeCompletionListItemDelegate::model() const
1107{
1108 return mModel;
1109}
1110
1111void CodeCompletionListItemDelegate::setModel(CodeCompletionListModel *newModel)
1112{
1113 mModel = newModel;
1114}
1115
1116const QColor &CodeCompletionListItemDelegate::normalColor() const
1117{
1118 return mNormalColor;
1119}
1120
1121void CodeCompletionListItemDelegate::setNormalColor(const QColor &newNormalColor)
1122{
1123 mNormalColor = newNormalColor;
1124}
1125
1126const QColor &CodeCompletionListItemDelegate::matchedColor() const
1127{
1128 return mMatchedColor;
1129}
1130
1131void CodeCompletionListItemDelegate::setMatchedColor(const QColor &newMatchedColor)
1132{
1133 mMatchedColor = newMatchedColor;
1134}
1135
1136const QFont &CodeCompletionListItemDelegate::font() const
1137{
1138 return mFont;
1139}
1140
1141void CodeCompletionListItemDelegate::setFont(const QFont &newFont)
1142{
1143 mFont = newFont;
1144}
1145
1146CodeCompletionListItemDelegate::CodeCompletionListItemDelegate(CodeCompletionListModel *model, QWidget *parent) : QStyledItemDelegate(parent),
1147 mModel(model)
1148{
1149 mNormalColor = qApp->palette().color(QPalette::Text);
1150 mMatchedColor = qApp->palette().color(QPalette::BrightText);
1151}
1152