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 | |
32 | 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 | |
61 | CodeCompletionPopup::() |
62 | { |
63 | delete mListView; |
64 | delete mModel; |
65 | } |
66 | |
67 | void CodeCompletionPopup::(const KeyPressedCallback &newKeypressedCallback) |
68 | { |
69 | mListView->setKeypressedCallback(newKeypressedCallback); |
70 | } |
71 | |
72 | void CodeCompletionPopup::( |
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 | |
100 | bool CodeCompletionPopup::(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 | |
151 | PStatement CodeCompletionPopup::() |
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 | |
168 | void CodeCompletionPopup::(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 | |
206 | void CodeCompletionPopup::(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 | |
218 | static bool nameComparator(PStatement statement1,PStatement statement2) { |
219 | return statement1->command < statement2->command; |
220 | } |
221 | |
222 | static 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 | |
250 | static 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 | |
294 | static 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 | |
326 | static 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 | |
375 | void CodeCompletionPopup::(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 | |
499 | void CodeCompletionPopup::( |
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 | |
786 | void CodeCompletionPopup::(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 | |
808 | void CodeCompletionPopup::(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 | |
818 | bool CodeCompletionPopup::(const QString &fileName) |
819 | { |
820 | return mIncludedFiles.contains(fileName); |
821 | } |
822 | |
823 | void CodeCompletionPopup::(bool newHideSymbolsStartWithTwoUnderline) |
824 | { |
825 | mHideSymbolsStartWithTwoUnderline = newHideSymbolsStartWithTwoUnderline; |
826 | } |
827 | |
828 | bool CodeCompletionPopup::() const |
829 | { |
830 | return mHideSymbolsStartWithTwoUnderline; |
831 | } |
832 | |
833 | bool CodeCompletionPopup::() const |
834 | { |
835 | return mHideSymbolsStartWithUnderline; |
836 | } |
837 | |
838 | void CodeCompletionPopup::(bool newHideSymbolsStartWithUnderline) |
839 | { |
840 | mHideSymbolsStartWithUnderline = newHideSymbolsStartWithUnderline; |
841 | } |
842 | |
843 | const QString &CodeCompletionPopup::() const |
844 | { |
845 | return mMemberOperator; |
846 | } |
847 | |
848 | const QList<PCodeSnippet> &CodeCompletionPopup::() const |
849 | { |
850 | return mCodeSnippets; |
851 | } |
852 | |
853 | void CodeCompletionPopup::(const QList<PCodeSnippet> &newCodeSnippets) |
854 | { |
855 | mCodeSnippets = newCodeSnippets; |
856 | } |
857 | |
858 | void CodeCompletionPopup::(const std::shared_ptr<QHash<StatementKind, std::shared_ptr<ColorSchemeItem> > > &newColors) |
859 | { |
860 | mColors = newColors; |
861 | } |
862 | |
863 | const QString &CodeCompletionPopup::() const |
864 | { |
865 | return mMemberPhrase; |
866 | } |
867 | |
868 | void CodeCompletionPopup::(QShowEvent *) |
869 | { |
870 | mListView->setFocus(); |
871 | } |
872 | |
873 | const PStatement &CodeCompletionPopup::() const |
874 | { |
875 | return mCurrentStatement; |
876 | } |
877 | |
878 | void CodeCompletionPopup::(const PStatement &newCurrentStatement) |
879 | { |
880 | mCurrentStatement = newCurrentStatement; |
881 | } |
882 | |
883 | const std::shared_ptr<QHash<StatementKind, std::shared_ptr<ColorSchemeItem> > >& CodeCompletionPopup::() const |
884 | { |
885 | return mColors; |
886 | } |
887 | |
888 | bool CodeCompletionPopup::() const |
889 | { |
890 | return mUseCppKeyword; |
891 | } |
892 | |
893 | void CodeCompletionPopup::(bool newUseCppKeyword) |
894 | { |
895 | mUseCppKeyword = newUseCppKeyword; |
896 | } |
897 | |
898 | bool CodeCompletionPopup::() const |
899 | { |
900 | return mSortByScope; |
901 | } |
902 | |
903 | void CodeCompletionPopup::(bool newSortByScope) |
904 | { |
905 | mSortByScope = newSortByScope; |
906 | } |
907 | |
908 | bool CodeCompletionPopup::() const |
909 | { |
910 | return mIgnoreCase; |
911 | } |
912 | |
913 | void CodeCompletionPopup::(bool newIgnoreCase) |
914 | { |
915 | mIgnoreCase = newIgnoreCase; |
916 | } |
917 | |
918 | bool CodeCompletionPopup::() const |
919 | { |
920 | return mShowCodeSnippets; |
921 | } |
922 | |
923 | void CodeCompletionPopup::(bool newShowCodeIns) |
924 | { |
925 | mShowCodeSnippets = newShowCodeIns; |
926 | } |
927 | |
928 | bool CodeCompletionPopup::() const |
929 | { |
930 | return mShowKeywords; |
931 | } |
932 | |
933 | void CodeCompletionPopup::(bool newShowKeywords) |
934 | { |
935 | mShowKeywords = newShowKeywords; |
936 | } |
937 | |
938 | bool CodeCompletionPopup::() const |
939 | { |
940 | return mRecordUsage; |
941 | } |
942 | |
943 | void CodeCompletionPopup::(bool newRecordUsage) |
944 | { |
945 | mRecordUsage = newRecordUsage; |
946 | } |
947 | |
948 | bool CodeCompletionPopup::() const |
949 | { |
950 | return mOnlyGlobals; |
951 | } |
952 | |
953 | void CodeCompletionPopup::(bool newOnlyGlobals) |
954 | { |
955 | mOnlyGlobals = newOnlyGlobals; |
956 | } |
957 | |
958 | int CodeCompletionPopup::() const |
959 | { |
960 | return mShowCount; |
961 | } |
962 | |
963 | void CodeCompletionPopup::(int newShowCount) |
964 | { |
965 | mShowCount = newShowCount; |
966 | } |
967 | |
968 | const PCppParser &CodeCompletionPopup::() const |
969 | { |
970 | return mParser; |
971 | } |
972 | |
973 | void CodeCompletionPopup::(const PCppParser &newParser) |
974 | { |
975 | mParser = newParser; |
976 | } |
977 | |
978 | void CodeCompletionPopup::(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 | |
995 | bool CodeCompletionPopup::(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 | |
1005 | CodeCompletionListModel::CodeCompletionListModel(const StatementList *statements, QObject *parent): |
1006 | QAbstractListModel(parent), |
1007 | mStatements(statements) |
1008 | { |
1009 | |
1010 | } |
1011 | |
1012 | int CodeCompletionListModel::rowCount(const QModelIndex &) const |
1013 | { |
1014 | return mStatements->count(); |
1015 | } |
1016 | |
1017 | QVariant 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 | |
1036 | PStatement 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 | |
1045 | QPixmap 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 | |
1055 | void CodeCompletionListModel::notifyUpdated() |
1056 | { |
1057 | beginResetModel(); |
1058 | endResetModel(); |
1059 | } |
1060 | |
1061 | void 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 | |
1106 | CodeCompletionListModel *CodeCompletionListItemDelegate::model() const |
1107 | { |
1108 | return mModel; |
1109 | } |
1110 | |
1111 | void CodeCompletionListItemDelegate::setModel(CodeCompletionListModel *newModel) |
1112 | { |
1113 | mModel = newModel; |
1114 | } |
1115 | |
1116 | const QColor &CodeCompletionListItemDelegate::normalColor() const |
1117 | { |
1118 | return mNormalColor; |
1119 | } |
1120 | |
1121 | void CodeCompletionListItemDelegate::setNormalColor(const QColor &newNormalColor) |
1122 | { |
1123 | mNormalColor = newNormalColor; |
1124 | } |
1125 | |
1126 | const QColor &CodeCompletionListItemDelegate::matchedColor() const |
1127 | { |
1128 | return mMatchedColor; |
1129 | } |
1130 | |
1131 | void CodeCompletionListItemDelegate::setMatchedColor(const QColor &newMatchedColor) |
1132 | { |
1133 | mMatchedColor = newMatchedColor; |
1134 | } |
1135 | |
1136 | const QFont &CodeCompletionListItemDelegate::font() const |
1137 | { |
1138 | return mFont; |
1139 | } |
1140 | |
1141 | void CodeCompletionListItemDelegate::setFont(const QFont &newFont) |
1142 | { |
1143 | mFont = newFont; |
1144 | } |
1145 | |
1146 | CodeCompletionListItemDelegate::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 | |