diff --git a/RedPandaIDE/RedPandaIDE.pro b/RedPandaIDE/RedPandaIDE.pro index c56ee01b..750dde47 100644 --- a/RedPandaIDE/RedPandaIDE.pro +++ b/RedPandaIDE/RedPandaIDE.pro @@ -139,7 +139,6 @@ HEADERS += \ FORMS += \ settingsdialog/debuggeneralwidget.ui \ - widgets/codecompletionview.ui \ widgets/cpudialog.ui \ mainwindow.ui \ settingsdialog/compilersetdirectorieswidget.ui \ diff --git a/RedPandaIDE/parser/cppparser.cpp b/RedPandaIDE/parser/cppparser.cpp index 5bbc7e70..067bd2c7 100644 --- a/RedPandaIDE/parser/cppparser.cpp +++ b/RedPandaIDE/parser/cppparser.cpp @@ -3525,6 +3525,11 @@ void CppParser::updateSerialId() mSerialId = QString("%1 %2").arg(mParserId).arg(mSerialCount); } +const StatementModel &CppParser::statementList() const +{ + return mStatementList; +} + bool CppParser::parseGlobalHeaders() const { return mParseGlobalHeaders; diff --git a/RedPandaIDE/parser/cppparser.h b/RedPandaIDE/parser/cppparser.h index c60ae112..6544a9b7 100644 --- a/RedPandaIDE/parser/cppparser.h +++ b/RedPandaIDE/parser/cppparser.h @@ -119,6 +119,8 @@ public: bool parseGlobalHeaders() const; void setParseGlobalHeaders(bool newParseGlobalHeaders); + const StatementModel &statementList() const; + signals: void onProgress(const QString& fileName, int total, int current); void onBusy(); diff --git a/RedPandaIDE/parser/parserutils.h b/RedPandaIDE/parser/parserutils.h index 88c951c5..712f44ab 100644 --- a/RedPandaIDE/parser/parserutils.h +++ b/RedPandaIDE/parser/parserutils.h @@ -4,6 +4,17 @@ #include #include #include + +struct CodeIns { + QString caption; //Name + QString prefix; //Prefix used in code suggestion + QString code; //Code body + QString desc; //Description + int section; //Section in the menu +}; + +using PCodeIns = std::shared_ptr; + // preprocess/ macro define struct Define { QString name; @@ -190,4 +201,5 @@ bool isSystemHeaderFile(const QString& fileName, const QSet& includePat bool isHfile(const QString filename); bool isCfile(const QString filename); bool isKeyword(const QString& word); + #endif // PARSER_UTILS_H diff --git a/RedPandaIDE/parser/statementmodel.cpp b/RedPandaIDE/parser/statementmodel.cpp index 42796d99..5b65e6ac 100644 --- a/RedPandaIDE/parser/statementmodel.cpp +++ b/RedPandaIDE/parser/statementmodel.cpp @@ -37,7 +37,7 @@ void StatementModel::deleteStatement(PStatement statement) mCount -= count; } -const StatementMap &StatementModel::childrenStatements(PStatement statement) +const StatementMap &StatementModel::childrenStatements(PStatement statement) const { if (!statement) { return mGlobalStatements; @@ -46,7 +46,7 @@ const StatementMap &StatementModel::childrenStatements(PStatement statement) } } -const StatementMap &StatementModel::childrenStatements(std::weak_ptr statement) +const StatementMap &StatementModel::childrenStatements(std::weak_ptr statement) const { PStatement s = statement.lock(); return childrenStatements(s); diff --git a/RedPandaIDE/parser/statementmodel.h b/RedPandaIDE/parser/statementmodel.h index b2f3e448..555b749d 100644 --- a/RedPandaIDE/parser/statementmodel.h +++ b/RedPandaIDE/parser/statementmodel.h @@ -15,8 +15,8 @@ public: // function DeleteFirst: Integer; // function DeleteLast: Integer; void deleteStatement(PStatement statement); - const StatementMap& childrenStatements(PStatement statement = PStatement()); - const StatementMap& childrenStatements(std::weak_ptr statement); + const StatementMap& childrenStatements(PStatement statement = PStatement()) const; + const StatementMap& childrenStatements(std::weak_ptr statement) const; void clear(); void dump(const QString& logFile); diff --git a/RedPandaIDE/widgets/codecompletionview.cpp b/RedPandaIDE/widgets/codecompletionview.cpp index 30e2a0cb..6404647a 100644 --- a/RedPandaIDE/widgets/codecompletionview.cpp +++ b/RedPandaIDE/widgets/codecompletionview.cpp @@ -1,7 +1,8 @@ #include "codecompletionview.h" -#include "ui_codecompletionview.h" +#include "../utils.h" #include +#include CodeCompletionView::CodeCompletionView(QWidget *parent) : QWidget(parent) @@ -23,6 +24,257 @@ void CodeCompletionView::setKeypressedCallback(const KeyPressedCallback &newKeyp mListView->setKeypressedCallback(newKeypressedCallback); } +void CodeCompletionView::addChildren(PStatement scopeStatement, const QString &fileName, int line) +{ + if (scopeStatement && !isIncluded(scopeStatement->fileName) + && !isIncluded(scopeStatement->definitionFileName)) + return; + const StatementMap& children = mParser->statementList().childrenStatements(scopeStatement); + if (children.isEmpty()) + return; + + if (!scopeStatement) { //Global scope + for (PStatement childStatement: children) { + if (childStatement->fileName.isEmpty()) { + // hard defines + addStatement(childStatement,fileName,-1); + } else if (!( childStatement->kind == StatementKind::skConstructor + || childStatement->kind == StatementKind::skDestructor + || childStatement->kind == StatementKind::skBlock) + && (!mAddedStatements.contains(childStatement->command)) + && ( + isIncluded(childStatement->fileName) + || isIncluded(childStatement->definitionFileName) + ) + ) { + //we must check if the statement is included by the file + addStatement(childStatement,fileName,line); + } + } + } else { + for (PStatement childStatement: children) { + if (!( childStatement->kind == StatementKind::skConstructor + || childStatement->kind == StatementKind::skDestructor + || childStatement->kind == StatementKind::skBlock) + && (!mAddedStatements.contains(childStatement->command))) + addStatement(childStatement,fileName,line); + } + } +} + +void CodeCompletionView::addStatement(PStatement statement, const QString &fileName, int line) +{ + if ((line!=-1) + && (line < statement->line) + && (fileName == statement->fileName)) + return; + mAddedStatements.insert(statement->command); + mFullCompletionStatementList.append(statement); +} + +static bool defaultComparator(PStatement statement1,PStatement statement2) { + // Show user template first + if (statement1->kind == StatementKind::skUserCodeIn) { + if (statement2->kind != StatementKind::skUserCodeIn) + return true; + else + return statement1->command < statement2->command; + } else if (statement2->kind == StatementKind::skUserCodeIn) { + return false; + // show keywords first + } else if ((statement1->kind == StatementKind::skKeyword) + && (statement2->kind != StatementKind::skKeyword)) { + return true; + } else if ((statement1->kind != StatementKind::skKeyword) + && (statement2->kind == StatementKind::skKeyword)) { + return false; + } else + return statement1->command < statement2->command; +} + +static bool sortByScopeComparator(PStatement statement1,PStatement statement2){ + // Show user template first + if (statement1->kind == StatementKind::skUserCodeIn) { + if (statement2->kind != StatementKind::skUserCodeIn) + return true; + else + return statement1->command < statement2->command; + } else if (statement2->kind == StatementKind::skUserCodeIn) { + return false; + // show keywords first + } else if (statement1->kind == StatementKind::skKeyword) { + if (statement2->kind != StatementKind::skKeyword) + return true; + else + return statement1->command < statement2->command; + } else if (statement2->kind == StatementKind::skKeyword) { + return false; + // Show stuff from local headers first + } else if (statement1->inSystemHeader && ! statement2->inSystemHeader) { + return true; + } else if (!statement1->inSystemHeader && statement2->inSystemHeader) { + return false; + // Show local statements first + } else if (statement1->scope != StatementScope::ssGlobal + && statement1->scope == StatementScope::ssGlobal ) { + return true; + } else if (statement1->scope == StatementScope::ssGlobal + && statement1->scope != StatementScope::ssGlobal ) { + return false; + // otherwise, sort by name + } else + return statement1->command < statement2->command; +} + +static bool sortWithUsageComparator(PStatement statement1,PStatement statement2) { + // Show user template first + if (statement1->kind == StatementKind::skUserCodeIn) { + if (statement2->kind != StatementKind::skUserCodeIn) + return true; + else + return statement1->command < statement2->command; + } else if (statement2->kind == StatementKind::skUserCodeIn) { + return false; + //show most freq first + } else if (statement1->freqTop > statement2->freqTop) { + return true; + } else if (statement1->freqTop < statement2->freqTop) { + return false; + // show keywords first + } else if ((statement1->kind == StatementKind::skKeyword) + && (statement2->kind != StatementKind::skKeyword)) { + return true; + } else if ((statement1->kind != StatementKind::skKeyword) + && (statement2->kind == StatementKind::skKeyword)) { + return false; + } else + return statement1->command < statement2->command; +} + +static bool sortByScopeWithUsageComparator(PStatement statement1,PStatement statement2){ + // Show user template first + if (statement1->kind == StatementKind::skUserCodeIn) { + if (statement2->kind != StatementKind::skUserCodeIn) + return true; + else + return statement1->command < statement2->command; + } else if (statement2->kind == StatementKind::skUserCodeIn) { + return false; + //show most freq first + } else if (statement1->freqTop > statement2->freqTop) { + return true; + } else if (statement1->freqTop < statement2->freqTop) { + return false; + // show keywords first + } else if (statement1->kind == StatementKind::skKeyword) { + if (statement2->kind != StatementKind::skKeyword) + return true; + else + return statement1->command < statement2->command; + } else if (statement2->kind == StatementKind::skKeyword) { + return false; + // Show stuff from local headers first + } else if (statement1->inSystemHeader && ! statement2->inSystemHeader) { + return true; + } else if (!statement1->inSystemHeader && statement2->inSystemHeader) { + return false; + // Show local statements first + } else if (statement1->scope != StatementScope::ssGlobal + && statement1->scope == StatementScope::ssGlobal ) { + return true; + } else if (statement1->scope == StatementScope::ssGlobal + && statement1->scope != StatementScope::ssGlobal ) { + return false; + // otherwise, sort by name + } else + return statement1->command < statement2->command; +} + + +void CodeCompletionView::filterList(const QString &member) +{ + QMutexLocker locker(&mMutex); + mCompletionStatementList.clear(); + if (!mParser) + return; + if (!mParser->enabled()) + return; + //we don't need to freeze here since we use smart pointers + // and data have been retrieved from the parser +// if (!mParser->freeze()) +// return; +// { +// auto action = finally([this]{ +// mParser->unFreeze(); +// }); +// if (mParserSerialId!=mParser->serialId()) { +// return; +// } + + QList tmpList; + if (!member.isEmpty()) { // filter + tmpList.reserve(mFullCompletionStatementList.size()); + for (PStatement statement:mFullCompletionStatementList) { + Qt::CaseSensitivity cs = (mIgnoreCase? + Qt::CaseInsensitive: + Qt::CaseSensitive); + if (statement->command.startsWith(member, cs)) + tmpList.append(statement); + } + } else + tmpList = mCompletionStatementList; + if (mRecordUsage) { + int topCount = 0; + int secondCount = 0; + int thirdCount = 0; + int usageCount; + for (PStatement statement:tmpList) { + if (statement->usageCount == 0) { + usageCount = mSymbolUsage.value(statement->fullName,0); + if (usageCount == 0) + continue; + statement->usageCount = usageCount; + } else + usageCount = statement->usageCount; + if (usageCount>topCount) { + thirdCount = secondCount; + secondCount = topCount; + topCount = usageCount; + } else if (usageCount == topCount) { + continue; + } else if (usageCount > secondCount) { + thirdCount = secondCount; + secondCount = usageCount; + } else if (usageCount == secondCount) { + continue; + } else if (usageCount>thirdCount) { + thirdCount = usageCount; + } + } + for (PStatement statement:tmpList) { + if (statement->usageCount == 0) { + statement->freqTop = 0; + } else if (statement->usageCount == topCount) { + statement->freqTop = 30; + } else if (statement->usageCount == secondCount) { + statement->freqTop = 20; + } else if (statement->usageCount == thirdCount) { + statement->freqTop = 10; + } + } + if (mSortByScope) { + std::sort(tmpList.begin(),tmpList.end(),sortByScopeWithUsageComparator); + } else { + std::sort(tmpList.begin(),tmpList.end(),sortWithUsageComparator); + } + } else if (mSortByScope) { + std::sort(tmpList.begin(),tmpList.end(),sortByScopeComparator); + } else { + std::sort(tmpList.begin(),tmpList.end(),defaultComparator); + } +// } +} + CodeCompletionListView::CodeCompletionListView(QWidget *parent) : QListView(parent) { diff --git a/RedPandaIDE/widgets/codecompletionview.h b/RedPandaIDE/widgets/codecompletionview.h index b1af3b36..a1ae33a3 100644 --- a/RedPandaIDE/widgets/codecompletionview.h +++ b/RedPandaIDE/widgets/codecompletionview.h @@ -3,6 +3,7 @@ #include #include +#include "parser/cppparser.h" using KeyPressedCallback = std::function; @@ -31,9 +32,40 @@ public: void setKeypressedCallback(const KeyPressedCallback &newKeypressedCallback); - +private: + void addChildren(PStatement scopeStatement, const QString& fileName, + int line); + void addStatement(PStatement statement, const QString& fileName, int line); + void filterList(const QString& member); + void getCompletionFor(const QString& fileName,const QString& phrase, int line); + bool isIncluded(const QString& fileName); private: CodeCompletionListView * mListView; + QList mCodeInsList; //(Code template list) + //QList mCodeInsStatements; //temporary (user code template) statements created when show code suggestion + PCppParser mParser; + QList mFullCompletionStatementList; + QList mCompletionStatementList; + bool mEnabled; + int mShowCount; + bool mOnlyGlobals; + PStatement mCurrentStatement; + QSet mIncludedFiles; + QSet mUsings; + QString mIsIncludedCacheFileName; + bool mIsIncludedCacheResult; + QSet mAddedStatements; + bool mPreparing; + QString mPhrase; + QHash mSymbolUsage; + bool mRecordUsage; + bool mShowKeywords; + bool mShowCodeIns; + bool mIgnoreCase; + QRecursiveMutex mMutex; + QString mParserSerialId; + bool mSortByScope; + bool mUseCppKeyword; }; #endif // CODECOMPLETIONVIEW_H diff --git a/RedPandaIDE/widgets/codecompletionview.ui b/RedPandaIDE/widgets/codecompletionview.ui deleted file mode 100644 index e84717ca..00000000 --- a/RedPandaIDE/widgets/codecompletionview.ui +++ /dev/null @@ -1,36 +0,0 @@ - - - CodeCompletionView - - - - 0 - 0 - 400 - 300 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - -