diff --git a/RedPandaIDE/RedPandaIDE.pro b/RedPandaIDE/RedPandaIDE.pro index 0836043c..6a4391f0 100644 --- a/RedPandaIDE/RedPandaIDE.pro +++ b/RedPandaIDE/RedPandaIDE.pro @@ -25,6 +25,7 @@ SOURCES += \ qsynedit/SearchBase.cpp \ qsynedit/SearchRegex.cpp \ settingsdialog/debuggeneralwidget.cpp \ + widgets/classbrowser.cpp \ widgets/cpudialog.cpp \ debugger.cpp \ editor.cpp \ @@ -87,6 +88,7 @@ HEADERS += \ qsynedit/SearchBase.h \ qsynedit/SearchRegex.h \ settingsdialog/debuggeneralwidget.h \ + widgets/classbrowser.h \ widgets/cpudialog.h \ debugger.h \ editor.h \ diff --git a/RedPandaIDE/parser/cppparser.cpp b/RedPandaIDE/parser/cppparser.cpp index 7b178937..7e4acf25 100644 --- a/RedPandaIDE/parser/cppparser.cpp +++ b/RedPandaIDE/parser/cppparser.cpp @@ -3517,6 +3517,11 @@ void CppParser::updateSerialId() mSerialId = QString("%1 %2").arg(mParserId).arg(mSerialCount); } +const QString &CppParser::serialId() const +{ + return mSerialId; +} + int CppParser::parserId() const { return mParserId; diff --git a/RedPandaIDE/parser/cppparser.h b/RedPandaIDE/parser/cppparser.h index 5b3c1f95..303174ae 100644 --- a/RedPandaIDE/parser/cppparser.h +++ b/RedPandaIDE/parser/cppparser.h @@ -110,6 +110,8 @@ public: int parserId() const; + const QString &serialId() const; + signals: void onProgress(const QString& fileName, int total, int current); void onBusy(); @@ -346,5 +348,5 @@ private: QRecursiveMutex mMutex; GetFileStreamCallBack mOnGetFileStream; }; - +using PCppParser = std::shared_ptr; #endif // CPPPARSER_H diff --git a/RedPandaIDE/widgets/classbrowser.cpp b/RedPandaIDE/widgets/classbrowser.cpp new file mode 100644 index 00000000..562690bc --- /dev/null +++ b/RedPandaIDE/widgets/classbrowser.cpp @@ -0,0 +1,297 @@ +#include "classbrowser.h" +#include "../utils.h" + +ClassBrowserModel::ClassBrowserModel(QObject *parent):QAbstractItemModel(parent) +{ + mRoot = new ClassBrowserNode(); + mRoot->parent = nullptr; + mRoot->statement = PStatement(); + mRoot->childrenFetched = true; + mUpdating = false; + mUpdateCount = 0; + mShowInheritedMembers = false; +} + +ClassBrowserModel::~ClassBrowserModel() +{ + delete mRoot; +} + +QModelIndex ClassBrowserModel::index(int row, int column, const QModelIndex &parent) const +{ + if (!hasIndex(row,column,parent)) + return QModelIndex(); + + ClassBrowserNode *parentNode; + if (!parent.isValid()) { // top level + parentNode = mRoot; + } else { + parentNode = static_cast(parent.internalPointer()); + } + return createIndex(row,column,parentNode->children[row]); +} + +QModelIndex ClassBrowserModel::parent(const QModelIndex &child) const +{ + if (child.isValid()) { + return QModelIndex(); + } + ClassBrowserNode *childNode = static_cast(child.internalPointer()); + ClassBrowserNode *parentNode = childNode->parent; + if (parentNode->parent == nullptr) //it's root node + return QModelIndex(); + + ClassBrowserNode *grandNode = parentNode->parent; + int row = grandNode->children.indexOf(parentNode); + return createIndex(row,0,parentNode); +} + +int ClassBrowserModel::rowCount(const QModelIndex &parent) const +{ + ClassBrowserNode *parentNode; + if (!parent.isValid()) { // top level + parentNode = mRoot; + } else { + parentNode = static_cast(parent.internalPointer()); + } + return parentNode->children.count(); +} + +int ClassBrowserModel::columnCount(const QModelIndex&) const +{ + return 1; +} + +void ClassBrowserModel::fetchMore(const QModelIndex &parent) +{ + if (!parent.isValid()) { // top level + return; + } + + ClassBrowserNode *parentNode = static_cast(parent.internalPointer()); + if (!parentNode->childrenFetched) { + parentNode->childrenFetched = true; + if (parentNode->statement && !parentNode->statement->children.isEmpty()) + filterChildren(parentNode, parentNode->statement->children); + } +} + +bool ClassBrowserModel::canFetchMore(const QModelIndex &parent) const +{ + + if (!parent.isValid()) { // top level + return false; + } + + ClassBrowserNode *parentNode = static_cast(parent.internalPointer()); + if (!parentNode->childrenFetched) { + if (parentNode->statement && !parentNode->statement->children.isEmpty()) + return true; + else + parentNode->childrenFetched = true; + } + return false; +} + +QVariant ClassBrowserModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()){ + return QVariant(); + } + ClassBrowserNode *node = static_cast(index.internalPointer()); + if (!node) + return QVariant(); + if (role == Qt::DisplayRole) { + if (node->statement) { + return node->statement->command; + } + } + return QVariant(); +} + +const PCppParser &ClassBrowserModel::cppParser() const +{ + return mParser; +} + +void ClassBrowserModel::setCppParser(const PCppParser &newCppParser) +{ + if (mParser) { + disconnect(mParser.get(), + &CppParser::onEndParsing, + this, + &ClassBrowserModel::fillStatements); + } + mParser = newCppParser; + if (mParser) { + connect(mParser.get(), + &CppParser::onEndParsing, + this, + &ClassBrowserModel::fillStatements); + if (!mParser->parsing()) + fillStatements(); + } +} + +void ClassBrowserModel::clear() +{ + beginResetModel(); + mRoot->children.clear(); + mNodes.clear(); + mDummyStatements.clear(); + endResetModel(); +} + +void ClassBrowserModel::fillStatements() +{ + QMutexLocker locker(&mMutex); + if (mUpdateCount!=0) + return; + mUpdating = true; + beginResetModel(); + clear(); + { + auto action = finally([this]{ + endResetModel(); + mUpdating = false; + }); + if (!mParser) + return; + if (!mParser->enabled()) + return; + if (!mParser->freeze()) + return; + { + auto action2 = finally([this]{ + mParser->unFreeze(); + }); + QString mParserSerialId = mParser->serialId(); + if (!mCurrentFile.isEmpty()) { + QSet includedFiles = mParser->getFileIncludes(mCurrentFile); + + addMembers(includedFiles); + // Remember selection +// if fLastSelection <> '' then +// ReSelect; + } + + } + } +} + +void ClassBrowserModel::calculateChildrenCounts(const QModelIndex &index) +{ + ClassBrowserNode *parentNode; + if (!index.isValid()) { // top level + parentNode = mRoot; + } else { + parentNode = static_cast(index.internalPointer()); + } + if (!parentNode->childrenFetched && parentNode->statement) { + parentNode->childrenFetched = true; + filterChildren(parentNode, parentNode->statement->children); + } +} + +void ClassBrowserModel::addChild(ClassBrowserNode *node, PStatement statement) +{ + PClassBrowserNode newNode = std::make_shared(); + newNode->parent = node; + newNode->statement = statement; + newNode->childrenInited = false; + node->children.append(newNode.get()); + mNodes.append(newNode); +} + +void ClassBrowserModel::addMembers(const QSet &includedFiles) +{ + // show statements in the file + PFileIncludes p = mParser->findFileIncludes(mCurrentFile); + if (!p) + return; + filterChildren(mRoot,p->statements); +} + +void ClassBrowserModel::filterChildren(ClassBrowserNode *node, const StatementMap &statements) +{ + for (PStatement statement:statements) { + if (statement->kind == StatementKind::skBlock) + continue; + if (statement->isInherited && !mShowInheritedMembers) + continue; + + if (statement == node->statement) // prevent infinite recursion + continue; + + if (statement->scope == StatementScope::ssLocal) + continue; + + +// if (fStatementsType = cbstProject) then begin +// if not Statement^._InProject then +// Continue; +// if Statement^._Static and not SameText(Statement^._FileName,fCurrentFile) +// and not SameText(Statement^._FileName,fCurrentFile) then +// Continue; +// end; + + // we only test and handle orphan statements in the top level (node->statement is null) + PStatement parentScope = statement->parentScope.lock(); + if ((parentScope!=node->statement) && (!node->statement)) { + +// // we only handle orphan statements when type is cbstFile +// if fStatementsType <> cbstFile then +// Continue; + +// //should not happend, just in case of error + if (!parentScope) + continue; + + // Processing the orphan statement + while (statement) { + //the statement's parent is in this file, so it's not a real orphan + if ((parentScope->fileName==mCurrentFile) + ||(parentScope->definitionFileName==mCurrentFile)) + break; + + PStatement dummyParent = mDummyStatements.value(parentScope->fullName,PStatement()); + if (dummyParent) { + dummyParent->children.insert(statement->command,statement); + break; + } + dummyParent = createDummy(parentScope); + dummyParent->children.insert(statement->command,statement); + //we are adding an orphan statement, just add it + statement = dummyParent; + parentScope = statement->parentScope.lock(); + if (!parentScope) { + addChild(node,statement); + + break; + } + } + } else if (statement->kind == StatementKind::skNamespace) { + PStatement dummy = mDummyStatements.value(statement->fullName,PStatement()); + if (dummy) { + for (PStatement child: statement->children) { + dummy->children.insert(child->command,child); + } + continue; + } + dummy = createDummy(statement); + dummy->children = statement->children; + addChild(node,dummy); + } else { + addChild(node,statement); + } + } +// if sortAlphabetically and sortByType then begin +// filtered.Sort(@CompareByAlphaAndType); +// end else if sortAlphabetically then begin +// filtered.Sort(@CompareByAlpha); +// end else if sortByType then begin +// filtered.Sort(@CompareByType); +// end; +} + + diff --git a/RedPandaIDE/widgets/classbrowser.h b/RedPandaIDE/widgets/classbrowser.h new file mode 100644 index 00000000..621cd281 --- /dev/null +++ b/RedPandaIDE/widgets/classbrowser.h @@ -0,0 +1,53 @@ +#ifndef CLASSBROWSER_H +#define CLASSBROWSER_H + +#include +#include "parser/cppparser.h" + +struct ClassBrowserNode { + ClassBrowserNode* parent; + PStatement statement; + QVector children; + bool childrenFetched; +}; + +using PClassBrowserNode = std::shared_ptr; + +class ClassBrowserModel : public QAbstractItemModel{ + Q_OBJECT + // QAbstractItemModel interface +public: + explicit ClassBrowserModel(QObject* parent=nullptr); + ~ClassBrowserModel(); + ClassBrowserModel& operator=(const ClassBrowserModel& model) = delete; + + QModelIndex index(int row, int column, const QModelIndex &parent) const override; + QModelIndex parent(const QModelIndex &child) const override; + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + void fetchMore(const QModelIndex &parent) override; + bool canFetchMore(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + const PCppParser &cppParser() const; + void setCppParser(const PCppParser &newCppParser); + void clear(); +public slots: + void fillStatements(); +private: + void addChild(ClassBrowserNode* node, PStatement statement); + void addMembers(const QSet& includedFiles); + void filterChildren(ClassBrowserNode * node, const StatementMap& statements); +private: + ClassBrowserNode * mRoot; + QHash mDummyStatements; + QVector mNodes; + PCppParser mParser; + bool mUpdating; + int mUpdateCount; + QRecursiveMutex mMutex; + QString mCurrentFile; + bool mShowInheritedMembers; + +}; + +#endif // CLASSBROWSER_H