diff --git a/NEWS.md b/NEWS.md index 783c2d3a..af11b680 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,19 @@ +Red Panda C++ Version 2.2 + + - enhancement: basic code completion support for C++ lambdas + - enhancement: slightly reduce parsing time + - fix: Wrong charset name returned when saving file + - fix: 'using =' / 'namespace =' not correctly handled + - fix: Pressing '*' at begin of line will crash app + - enhancement: switch header/source in editor's context menu + - enhancement: base class dropdown list in new class dialog now works + - fix: Edting / show context menu when code analysis is turned on may crash app. + - fix: Show context menu when edting non c/c++ file may crash app. + - fix: Project Options Dialog's Files panel will crash app. + - fix: Memory usage of undo system is not correctly calculated + - fix: Set max undo memory usage to 0 don't really remove limit of undo + - fix: Set max undo times to 0 don't really remove limit of undo + Red Panda C++ Version 2.1 - fix: editors that not in the editing panel shouldn't trigger switch breakpoint diff --git a/README.md b/README.md index b26d4b43..623820fe 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Simplified Chinese Website: [https://royqh1979.gitee.io/redpandacpp/](https://ro English Website: [https://sourceforge.net/projects/redpanda-cpp](https://sourceforge.net/projects/redpanda-cpp) New Features (Compared with Red Panda Dev-C++ 6): -* Cross Platform (Windows/Linux) +* Cross Platform (Windows/Linux/MacOS) * Problem Set (run and test program against predefined input / expected output data) * Competitve Companion support ( It's an chrome/firefox extension that can fetch problems from OJ websites) * Find symbol occurrences @@ -36,5 +36,6 @@ Code Intellisense Improvements: * Support C++ 14 using type alias * Support C-Style enum variable definitions * Support MACRO with arguments +* Support C++ lambdas And many other improvements and bug fixes. See NEWS.md for full informantion. \ No newline at end of file diff --git a/RedPandaIDE/RedPandaIDE.pro b/RedPandaIDE/RedPandaIDE.pro index c99aa3c9..7207bfca 100644 --- a/RedPandaIDE/RedPandaIDE.pro +++ b/RedPandaIDE/RedPandaIDE.pro @@ -10,7 +10,7 @@ isEmpty(APP_NAME) { } isEmpty(APP_VERSION) { - APP_VERSION = 2.1 + APP_VERSION = 2.2 } macos: { diff --git a/RedPandaIDE/editor.cpp b/RedPandaIDE/editor.cpp index 09bf0347..e79f03bd 100644 --- a/RedPandaIDE/editor.cpp +++ b/RedPandaIDE/editor.cpp @@ -915,7 +915,7 @@ void Editor::onPreparePaintHighlightToken(int line, int aChar, const QString &to if (token.isEmpty()) return; - if (mParser && mParser->enabled() && highlighter()) { + if (mParser && highlighter()) { QString lineText = document()->getString(line-1); if (mParser->isIncludeLine(lineText)) { if (cursor() == Qt::PointingHandCursor) { @@ -932,7 +932,7 @@ void Editor::onPreparePaintHighlightToken(int line, int aChar, const QString &to } } } - } else if (attr == highlighter()->identifierAttribute()) { + } else if (mParser->enabled() && attr == highlighter()->identifierAttribute()) { QSynedit::BufferCoord p{aChar,line}; // BufferCoord pBeginPos,pEndPos; // QString s= getWordAtPosition(this,p, pBeginPos,pEndPos, WordPurpose::wpInformation); @@ -1057,18 +1057,19 @@ bool Editor::event(QEvent *event) switch (reason) { case TipType::Preprocessor: // When hovering above a preprocessor line, determine if we want to show an include or a identifier hint - s = document()->getString(p.line - 1); - isIncludeNextLine = mParser->isIncludeNextLine(s); - if (!isIncludeNextLine) - isIncludeLine = mParser->isIncludeLine(s); - if (!isIncludeNextLine &&!isIncludeLine) - s = wordAtRowCol(p); + if (mParser) { + s = document()->getString(p.line - 1); + isIncludeNextLine = mParser->isIncludeNextLine(s); + if (!isIncludeNextLine) + isIncludeLine = mParser->isIncludeLine(s); + if (!isIncludeNextLine &&!isIncludeLine) + s = wordAtRowCol(p); + } break; case TipType::Identifier: if (pMainWindow->debugger()->executing() && !pMainWindow->debugger()->inferiorRunning()) s = getWordAtPosition(this,p, pBeginPos,pEndPos, WordPurpose::wpEvaluation); // debugging - else if (//devEditor.ParserHints and - !mCompletionPopup->isVisible() + else if (!mCompletionPopup->isVisible() && !mHeaderCompletionPopup->isVisible()) { expression = getExpressionAtPosition(p); s = expression.join(""); // information during coding @@ -1090,7 +1091,9 @@ bool Editor::event(QEvent *event) s = s.trimmed(); if ((s == mCurrentWord) && (mCurrentTipType == reason)) { - if (qApp->queryKeyboardModifiers() == Qt::ControlModifier) { + if (mParser + && mParser->enabled() + && qApp->queryKeyboardModifiers() == Qt::ControlModifier) { if (!hasFocus()) activate(); setCursor(Qt::PointingHandCursor); @@ -1148,7 +1151,9 @@ bool Editor::event(QEvent *event) if (!hint.isEmpty()) { // QApplication* app = dynamic_cast(QApplication::instance()); // if (app->keyboardModifiers().testFlag(Qt::ControlModifier)) { - if (qApp->queryKeyboardModifiers() == Qt::ControlModifier) { + if (mParser + && mParser->enabled() + && qApp->queryKeyboardModifiers() == Qt::ControlModifier) { if (!hasFocus()) activate(); setCursor(Qt::PointingHandCursor); @@ -1201,7 +1206,7 @@ void Editor::mouseReleaseEvent(QMouseEvent *event) && (event->button() == Qt::LeftButton)) { QSynedit::BufferCoord p; - if (pointToCharLine(event->pos(),p)) { + if (mParser && pointToCharLine(event->pos(),p)) { QString s = document()->getString(p.line - 1); if (mParser->isIncludeNextLine(s)) { QString filename = mParser->getHeaderFileName(mFilename,s, true); @@ -1211,7 +1216,7 @@ void Editor::mouseReleaseEvent(QMouseEvent *event) QString filename = mParser->getHeaderFileName(mFilename,s); pMainWindow->openFile(filename); return; - } else { + } else if (mParser->enabled()) { gotoDefinition(p); return; } @@ -2338,7 +2343,7 @@ bool Editor::handleBracketSkip() bool Editor::handleMultilineCommentCompletion() { - if ((caretX()-2 < lineText().length()) && (lineText()[caretX() - 2] == '/')) { + if ((caretX()-2>=0) && (caretX()-2 < lineText().length()) && (lineText()[caretX() - 2] == '/')) { QString text=selText(); beginUpdate(); beginUndoBlock(); @@ -3457,7 +3462,7 @@ bool Editor::onCompletionInputMethod(QInputMethodEvent *event) Editor::TipType Editor::getTipType(QPoint point, QSynedit::BufferCoord& pos) { // Only allow in the text area... - if (pointToCharLine(point, pos)) { + if (pointToCharLine(point, pos) && highlighter()) { if (!pMainWindow->debugger()->executing() && getSyntaxIssueAtPosition(pos)) { return TipType::Error; @@ -3628,6 +3633,9 @@ void Editor::updateFunctionTip(bool showTip) if (!highlighter()) return; + if (!mParser || !mParser->enabled()) + return; + bool isFunction = false; auto action = finally([&isFunction]{ if (!isFunction) diff --git a/RedPandaIDE/iconsmanager.cpp b/RedPandaIDE/iconsmanager.cpp index 82173d20..a0c7ba35 100644 --- a/RedPandaIDE/iconsmanager.cpp +++ b/RedPandaIDE/iconsmanager.cpp @@ -296,18 +296,18 @@ QPixmap IconsManager::getPixmapForStatement(PStatement statement) case StatementKind::skFunction: case StatementKind::skConstructor: case StatementKind::skDestructor: - if (statement->scope == StatementScope::ssGlobal) + if (statement->scope == StatementScope::Global) return *(pIconsManager->getPixmap(IconsManager::PARSER_GLOBAL_METHOD)); if (statement->isInherited) { - if (statement->classScope == StatementClassScope::scsProtected) { + if (statement->classScope == StatementClassScope::Protected) { return *(pIconsManager->getPixmap(IconsManager::PARSER_INHERITED_PROTECTED_METHOD)); - } else if (statement->classScope == StatementClassScope::scsPublic) { + } else if (statement->classScope == StatementClassScope::Public) { return *(pIconsManager->getPixmap(IconsManager::PARSER_INHERITED_METHOD)); } } else { - if (statement->classScope == StatementClassScope::scsProtected) { + if (statement->classScope == StatementClassScope::Protected) { return *(pIconsManager->getPixmap(IconsManager::PARSER_PROTECTED_METHOD)); - } else if (statement->classScope == StatementClassScope::scsPublic) { + } else if (statement->classScope == StatementClassScope::Public) { return *(pIconsManager->getPixmap(IconsManager::PARSER_PUBLIC_METHOD)); } else { return *(pIconsManager->getPixmap(IconsManager::PARSER_PRIVATE_METHOD)); @@ -320,15 +320,15 @@ QPixmap IconsManager::getPixmapForStatement(PStatement statement) return *(pIconsManager->getPixmap(IconsManager::PARSER_LOCAL_VAR)); case StatementKind::skVariable: if (statement->isInherited) { - if (statement->classScope == StatementClassScope::scsProtected) { + if (statement->classScope == StatementClassScope::Protected) { return *(pIconsManager->getPixmap(IconsManager::PARSER_INHERITED_PROTECTD_VAR)); - } else if (statement->classScope == StatementClassScope::scsPublic) { + } else if (statement->classScope == StatementClassScope::Public) { return *(pIconsManager->getPixmap(IconsManager::PARSER_INHERITED_VAR)); } } else { - if (statement->classScope == StatementClassScope::scsProtected) { + if (statement->classScope == StatementClassScope::Protected) { return *(pIconsManager->getPixmap(IconsManager::PARSER_PROTECTED_VAR)); - } else if (statement->classScope == StatementClassScope::scsPublic) { + } else if (statement->classScope == StatementClassScope::Public) { return *(pIconsManager->getPixmap(IconsManager::PARSER_PUBLIC_VAR)); } else { return *(pIconsManager->getPixmap(IconsManager::PARSER_PRIVATE_VAR)); diff --git a/RedPandaIDE/mainwindow.cpp b/RedPandaIDE/mainwindow.cpp index d174a728..efbbfeb4 100644 --- a/RedPandaIDE/mainwindow.cpp +++ b/RedPandaIDE/mainwindow.cpp @@ -4416,13 +4416,20 @@ void MainWindow::onEditorContextMenu(const QPoint& pos) int line; if (editor->getPositionOfMouse(p)) { line=p.line; + if (!switchHeaderSourceTarget(editor).isEmpty()) { + + menu.addAction(ui->actionSwitchHeaderSource); + menu.addSeparator(); + } //mouse on editing area menu.addAction(ui->actionCompile_Run); menu.addAction(ui->actionDebug); - menu.addSeparator(); - menu.addAction(ui->actionGoto_Declaration); - menu.addAction(ui->actionGoto_Definition); - menu.addAction(ui->actionFind_references); + if (editor->parser() && editor->parser()->enabled()) { + menu.addSeparator(); + menu.addAction(ui->actionGoto_Declaration); + menu.addAction(ui->actionGoto_Definition); + menu.addAction(ui->actionFind_references); + } menu.addSeparator(); menu.addAction(ui->actionOpen_Containing_Folder); @@ -4449,9 +4456,11 @@ void MainWindow::onEditorContextMenu(const QPoint& pos) menu.addAction(ui->actionFile_Properties); //these actions needs parser - ui->actionGoto_Declaration->setEnabled(!editor->parser()->parsing()); - ui->actionGoto_Definition->setEnabled(!editor->parser()->parsing()); - ui->actionFind_references->setEnabled(!editor->parser()->parsing()); + if (editor->parser() && editor->parser()->enabled()) { + ui->actionGoto_Declaration->setEnabled(!editor->parser()->parsing()); + ui->actionGoto_Definition->setEnabled(!editor->parser()->parsing()); + ui->actionFind_references->setEnabled(!editor->parser()->parsing()); + } } else { //mouse on gutter @@ -6781,6 +6790,51 @@ void MainWindow::reparseNonProjectEditors() } } +QString MainWindow::switchHeaderSourceTarget(Editor *editor) +{ + QString filename=editor->filename(); + if (getFileType(filename)==FileType::CHeader + || getFileType(filename)==FileType::CppHeader) { + QStringList lst; + lst.push_back("c"); + lst.push_back("cc"); + lst.push_back("cpp"); + lst.push_back("cxx"); + lst.push_back("C"); + lst.push_back("CC"); + foreach(const QString& suffix,lst) { + QString newFile=changeFileExt(filename,suffix); + if (fileExists(newFile)) { + return newFile; + } + } + } else if (getFileType(filename)==FileType::CSource) { + QStringList lst; + lst.push_back("h"); + foreach(const QString& suffix,lst) { + QString newFile=changeFileExt(filename,suffix); + if (fileExists(newFile)) { + return newFile; + } + } + } else if (getFileType(filename)==FileType::CppSource) { + QStringList lst; + lst.push_back("h"); + lst.push_back("hpp"); + lst.push_back("hxx"); + lst.push_back("HH"); + lst.push_back("H"); + + foreach(const QString& suffix,lst) { + QString newFile=changeFileExt(filename,suffix); + if (fileExists(newFile)) { + return newFile; + } + } + } + return QString(); +} + void MainWindow::onProjectViewNodeRenamed() { updateProjectView(); @@ -7913,7 +7967,7 @@ void MainWindow::on_actionNew_Class_triggered() { if (!mProject) return; - NewClassDialog dialog; + NewClassDialog dialog(mProject->cppParser()); dialog.setPath(mProject->folder()); if (dialog.exec()==QDialog::Accepted) { QDir dir(dialog.path()); @@ -7947,7 +8001,14 @@ void MainWindow::on_actionNew_Class_triggered() header.append(QString("#ifndef %1").arg(header_macro)); header.append(QString("#define %1").arg(header_macro)); header.append(""); - header.append(QString("class %1 {").arg(dialog.className())); + if (dialog.baseClass()) { + header.append(QString("#include \"%1\"").arg(extractRelativePath(mProject->directory(), + dialog.baseClass()->fileName))); + header.append(""); + header.append(QString("class %1 : public %2 {").arg(dialog.className(), + dialog.baseClass()->fullName)); + } else + header.append(QString("class %1 {").arg(dialog.className())); header.append("public:"); header.append(""); header.append("private:"); @@ -8642,7 +8703,7 @@ bool MainWindow::isClosingAll() const void MainWindow::on_actionGoto_block_start_triggered() { - Editor* editor=mEditorList->getEditor(); + Editor *editor=mEditorList->getEditor(); if (editor) editor->gotoBlockStart(); } @@ -8650,8 +8711,18 @@ void MainWindow::on_actionGoto_block_start_triggered() void MainWindow::on_actionGoto_block_end_triggered() { - Editor* editor=mEditorList->getEditor(); + Editor *editor=mEditorList->getEditor(); if (editor) editor->gotoBlockEnd(); } + +void MainWindow::on_actionSwitchHeaderSource_triggered() +{ + Editor *editor=mEditorList->getEditor(); + QString file=switchHeaderSourceTarget(editor); + if (!file.isEmpty()) { + openFile(file); + } +} + diff --git a/RedPandaIDE/mainwindow.h b/RedPandaIDE/mainwindow.h index 3a98a749..889850be 100644 --- a/RedPandaIDE/mainwindow.h +++ b/RedPandaIDE/mainwindow.h @@ -290,6 +290,7 @@ private: void setProjectViewCurrentUnit(std::shared_ptr unit); void reparseNonProjectEditors(); + QString switchHeaderSourceTarget(Editor *editor); private slots: void onProjectViewNodeRenamed(); @@ -732,6 +733,8 @@ private slots: void on_actionGoto_block_end_triggered(); + void on_actionSwitchHeaderSource_triggered(); + private: Ui::MainWindow *ui; EditorList *mEditorList; diff --git a/RedPandaIDE/mainwindow.ui b/RedPandaIDE/mainwindow.ui index 444d3a01..9172f67b 100644 --- a/RedPandaIDE/mainwindow.ui +++ b/RedPandaIDE/mainwindow.ui @@ -3255,6 +3255,14 @@ Ctrl+Alt+Down + + + Switch header/source + + + Switch Header/Source + + diff --git a/RedPandaIDE/parser/cppparser.cpp b/RedPandaIDE/parser/cppparser.cpp index 2c56df50..3992c271 100644 --- a/RedPandaIDE/parser/cppparser.cpp +++ b/RedPandaIDE/parser/cppparser.cpp @@ -904,18 +904,19 @@ void CppParser::parseHardDefines() }); for (const PDefine& define:mPreprocessor.hardDefines()) { addStatement( - PStatement(), // defines don't belong to any scope - "", - "", // define has no type - define->name, - define->value, - define->args, - -1, - StatementKind::skPreprocessor, - StatementScope::ssGlobal, - StatementClassScope::scsNone, - true, - false); + PStatement(), // defines don't belong to any scope + "", + "", // define has no type + define->name, + define->args, + "", + define->value, + -1, + StatementKind::skPreprocessor, + StatementScope::Global, + StatementClassScope::None, + true, + false); } } } @@ -956,7 +957,6 @@ void CppParser::resetParser() mCurrentScope.clear(); mCurrentClassScope.clear(); - mSkipList.clear(); mStatementList.clear(); mProjectFiles.clear(); @@ -991,11 +991,11 @@ bool CppParser::isFileParsed(const QString &filename) QString CppParser::getScopePrefix(const PStatement& statement){ switch (statement->classScope) { - case StatementClassScope::scsPublic: + case StatementClassScope::Public: return "public"; - case StatementClassScope::scsPrivate: + case StatementClassScope::Private: return "private"; - case StatementClassScope::scsProtected: + case StatementClassScope::Protected: return "protected"; default: return ""; @@ -1047,7 +1047,7 @@ QString CppParser::prettyPrintStatement(const PStatement& statement, const QStri case StatementKind::skVariable: case StatementKind::skParameter: case StatementKind::skClass: - if (statement->scope!= StatementScope::ssLocal) + if (statement->scope!= StatementScope::Local) result = getScopePrefix(statement)+ ' '; // public result += statement->type + ' '; // void result += statement->fullName; // A::B::C::Bar @@ -1133,18 +1133,19 @@ PStatement CppParser::addInheritedStatement(const PStatement& derived, const PSt { PStatement statement = addStatement( - derived, - inherit->fileName, - inherit->type, // "Type" is already in use - inherit->command, - inherit->args, - inherit->value, - inherit->line, - inherit->kind, - inherit->scope, - access, - true, - inherit->isStatic); + derived, + inherit->fileName, + inherit->type, // "Type" is already in use + inherit->command, + inherit->args, + inherit->noNameArgs, + inherit->value, + inherit->line, + inherit->kind, + inherit->scope, + access, + true, + inherit->isStatic); statement->isInherited = true; return statement; } @@ -1152,6 +1153,7 @@ PStatement CppParser::addInheritedStatement(const PStatement& derived, const PSt PStatement CppParser::addChildStatement(const PStatement& parent, const QString &fileName, const QString &aType, const QString &command, const QString &args, + const QString& noNameArgs, const QString &value, int line, StatementKind kind, const StatementScope& scope, const StatementClassScope& classScope, bool isDefinition, bool isStatic) @@ -1162,6 +1164,7 @@ PStatement CppParser::addChildStatement(const PStatement& parent, const QString aType, command, args, + noNameArgs, value, line, kind, @@ -1176,6 +1179,7 @@ PStatement CppParser::addStatement(const PStatement& parent, const QString &aType, const QString &command, const QString &args, + const QString &noNameArgs, const QString &value, int line, StatementKind kind, const StatementScope& scope, @@ -1192,13 +1196,11 @@ PStatement CppParser::addStatement(const PStatement& parent, // qDebug()<fullName; // } - QString noNameArgs = ""; if (kind == StatementKind::skConstructor || kind == StatementKind::skFunction || kind == StatementKind::skDestructor || kind == StatementKind::skVariable ) { - noNameArgs = removeArgNames(args); //find if (isDefinition) { PStatement oldStatement = findStatementInScope(newCommand,noNameArgs,kind,parent); @@ -1246,7 +1248,7 @@ PStatement CppParser::addStatement(const PStatement& parent, //result->friends; result->isStatic = isStatic; result->isInherited = false; - if (scope == StatementScope::ssLocal) + if (scope == StatementScope::Local) result->fullName = newCommand; else result->fullName = getFullStatementName(newCommand, parent); @@ -1270,18 +1272,97 @@ PStatement CppParser::addStatement(const PStatement& parent, return result; } +PStatement CppParser::addStatement(const PStatement &parent, const QString &fileName, const QString &aType, const QString &command, int argStart, int argEnd, const QString &value, int line, StatementKind kind, const StatementScope &scope, const StatementClassScope &classScope, bool isDefinition, bool isStatic) +{ + Q_ASSERT(mTokenizer[argStart]->text=='('); + QString args("("); + QString noNameArgs("("); + + int start=argStart+1; + bool typeGetted = false; + int braceLevel=0; + QString word; + for (int i=start;itext[0]; + if (this->isIdentChar(ch)) { + QString spaces=(i>argStart)?" ":""; + args+=spaces; + word += mTokenizer[i]->text[0]; + if (!typeGetted) { + noNameArgs+=spaces+word; + if (mCppTypeKeywords.contains(word) || !isCppKeyword(word)) + typeGetted = true; + } else { + if (isCppKeyword(word)) { + noNameArgs+=spaces+word; + } + } + word=""; + } else if (this->isDigitChar(ch)) { + args+=" "; + } else { + switch(ch.unicode()) { + case ',': + if (braceLevel==0) + typeGetted=false; + break; + case '{': + case '[': + case '<': + case '(': + braceLevel++; + break; + case '}': + case ']': + case '>': + case ')': + braceLevel--; + break; + //todo: * and & processing + case '*': + case '&': + if (braceLevel==0) + word+=ch; + break; + } + noNameArgs+= mTokenizer[i]->text; + } + args+=mTokenizer[i]->text; + } + + args.push_back(")"); + noNameArgs.push_back(")"); + return addStatement( + parent, + fileName, + aType, + command, + args, + noNameArgs, + value, + line, + kind, + scope, + classScope, + isDefinition, + isStatic); +} + void CppParser::setInheritance(int index, const PStatement& classStatement, bool isStruct) { // Clear it. Assume it is assigned - StatementClassScope lastInheritScopeType = StatementClassScope::scsNone; + StatementClassScope lastInheritScopeType = StatementClassScope::None; // Assemble a list of statements in text form we inherit from while (true) { - StatementClassScope inheritScopeType = getClassScope(index); - if (inheritScopeType == StatementClassScope::scsNone) { - if (mTokenizer[index]->text.front()!=',' - && mTokenizer[index]->text.front()!=':' - && mTokenizer[index]->text.front()!='(') { - QString basename = mTokenizer[index]->text; + StatementClassScope inheritScopeType = getClassScope(mTokenizer[index]->text); + QString currentText = mTokenizer[index]->text; + if (currentText=='(') { + //skip to matching ')' + index=mTokenizer[index]->matchIndex; + } else if (inheritScopeType == StatementClassScope::None) { + if (currentText.front()!=',' + && currentText.front()!=':') { + QString basename = currentText; //remove template staff if (basename.endsWith('>')) { int pBegin = basename.indexOf('<'); @@ -1313,11 +1394,20 @@ bool CppParser::isCurrentScope(const QString &command) return false; QString s = command; // remove template staff - int i= command.indexOf('<'); - if (i>=0) { - s.truncate(i); + if (s.endsWith('>')) { + int i= command.indexOf('<'); + if (i>=0) { + s.truncate(i); + } } - return (statement->command == s); + QString s2 = statement->command; + if (s2.endsWith('>')) { + int i= s2.indexOf('<'); + if (i>=0) { + s2.truncate(i); + } + } + return (s2 == s); } void CppParser::addSoloScopeLevel(PStatement& statement, int line, bool shouldResetBlock) @@ -1348,14 +1438,16 @@ void CppParser::addSoloScopeLevel(PStatement& statement, int line, bool shouldRe // Set new scope if (!statement) - mClassScope = StatementClassScope::scsNone; // {}, namespace or class that doesn't exist + mClassScope = StatementClassScope::None; // {}, namespace or class that doesn't exist else if (statement->kind == StatementKind::skNamespace) - mClassScope = StatementClassScope::scsNone; + mClassScope = StatementClassScope::None; else if (statement->type == "class") - mClassScope = StatementClassScope::scsPrivate; // classes are private by default + mClassScope = StatementClassScope::Private; // classes are private by default else - mClassScope = StatementClassScope::scsPublic; // structs are public by default + mClassScope = StatementClassScope::Public; // structs are public by default mCurrentClassScope.append(mClassScope); + //if (mCurrentClassScope.count()==2) +// qDebug()<<"++add scope"<kind == StatementKind::skBlock)) { - if (currentScope->children.isEmpty()) { - // remove no children block - if (fileIncludes) { - fileIncludes->scopes.removeLastScope(); + if (currentScope) { + if (currentScope->kind == StatementKind::skBlock) { + if (currentScope->children.isEmpty()) { + // remove no children block + if (fileIncludes) { + fileIncludes->scopes.removeLastScope(); + } + mStatementList.deleteStatement(currentScope); + } else { + fileIncludes->statements.insert(currentScope->fullName,currentScope); } - mStatementList.deleteStatement(currentScope); - } else { - fileIncludes->statements.insert(currentScope->fullName,currentScope); + } else if (currentScope->kind == StatementKind::skClass) { + mIndex=indexOfNextSemicolon(mIndex); } } mCurrentScope.pop_back(); @@ -1387,55 +1485,18 @@ void CppParser::removeScopeLevel(int line) } if (!currentScope) { - mClassScope = StatementClassScope::scsNone; + mClassScope = StatementClassScope::None; } else { mClassScope = mCurrentClassScope.back(); } } -int CppParser::skipBraces(int startAt) -{ - int i = startAt; - int level = 0; // assume we start on top of { - while (i < mTokenizer.tokenCount()) { - switch(mTokenizer[i]->text.front().unicode()) { - case '{': level++; - break; - case '}': - level--; - if (level==0) - return i; - } - i++; - } - return startAt; -} - -int CppParser::skipBracket(int startAt) -{ - int i = startAt; - int level = 0; // assume we start on top of { - while (i < mTokenizer.tokenCount()) { - switch(mTokenizer[i]->text.front().unicode()) { - case '[': level++; - break; - case ']': - level--; - if (level==0) - return i; - } - i++; - } - return startAt; -} - void CppParser::internalClear() { mCurrentScope.clear(); mCurrentClassScope.clear(); mIndex = 0; - mClassScope = StatementClassScope::scsNone; - mSkipList.clear(); + mClassScope = StatementClassScope::None; mBlockBeginSkips.clear(); mBlockEndSkips.clear(); mInlineNamespaceEndSkips.clear(); @@ -1498,34 +1559,32 @@ QStringList CppParser::sortFilesByIncludeRelations(const QSet &files) return result; } -bool CppParser::checkForCatchBlock() +bool CppParser::checkForKeyword(KeywordType& keywordType) { -// return mIndex < mTokenizer.tokenCount() && -// mTokenizer[mIndex]->text == "catch"; - return mTokenizer[mIndex]->text == "catch"; + keywordType = mCppKeywords.value(mTokenizer[mIndex]->text,KeywordType::NotKeyword); + switch(keywordType) { + case KeywordType::Catch: + case KeywordType::For: + case KeywordType::Public: + case KeywordType::Private: + case KeywordType::Enum: + case KeywordType::Inline: + case KeywordType::Namespace: + case KeywordType::Typedef: + case KeywordType::Using: + case KeywordType::Friend: + case KeywordType::Protected: + case KeywordType::None: + case KeywordType::NotKeyword: + case KeywordType::DeclType: + return false; + default: + return true; + } } -bool CppParser::checkForEnum() -{ -// return mIndex < mTokenizer.tokenCount() && -// mTokenizer[mIndex]->text == "enum"; - return mTokenizer[mIndex]->text == "enum"; -} - -bool CppParser::checkForForBlock() -{ -// return mIndex < mTokenizer.tokenCount() && -// mTokenizer[mIndex]->text == "for"; - return mTokenizer[mIndex]->text == "for"; -} - -bool CppParser::checkForKeyword() -{ - SkipType st = mCppKeywords.value(mTokenizer[mIndex]->text,SkipType::skNone); - return st!=SkipType::skNone; -} - -bool CppParser::checkForMethod(QString &sType, QString &sName, QString &sArgs, bool &isStatic, bool &isFriend) +bool CppParser::checkForMethod(QString &sType, QString &sName, int &argStartIndex, + int &argEndIndex, bool &isStatic, bool &isFriend) { PStatement scope = getCurrentScope(); @@ -1546,7 +1605,6 @@ bool CppParser::checkForMethod(QString &sType, QString &sName, QString &sArgs, b sType = ""; // should contain type "int" sName = ""; // should contain function name "foo::function" - sArgs = ""; // should contain argument list "(int a)" bool bTypeOK = false; bool bNameOK = false; @@ -1558,25 +1616,42 @@ bool CppParser::checkForMethod(QString &sType, QString &sName, QString &sArgs, b // Gather data for the string parts while ((mIndex < mTokenizer.tokenCount()) && !isSeperator(mTokenizer[mIndex]->text[0])) { if ((mIndex + 1 < mTokenizer.tokenCount()) - && (mTokenizer[mIndex + 1]->text[0] == '(')) { // and start of a function - //it's not a function define - if ((mIndex+2 < mTokenizer.tokenCount()) && (mTokenizer[mIndex + 2]->text[0] == ',')) - break; + && (mTokenizer[mIndex + 1]->text == '(')) { // and start of a function + int indexAfter = mTokenizer[mIndex + 1]->matchIndex+1; - if ((mIndex+2 < mTokenizer.tokenCount()) && (mTokenizer[mIndex + 2]->text[0] == ';')) { - if (isNotFuncArgs(mTokenizer[mIndex + 1]->text)) + //it's not a function define + if (indexAfter>=mTokenizer.tokenCount()) + break; + //it's not a function define; + if (isInvalidFunctionArgsSuffixChar(mTokenizer[indexAfter]->text[0])) + break; + //it's not a function define + if (mTokenizer[indexAfter]->text[0] == ';') { + //function can only be defined in global/namespaces/classes + PStatement currentScope=getCurrentScope(); + if (currentScope) { + //in namespace, it might be function or object initilization + if (currentScope->kind == StatementKind::skNamespace + && isNotFuncArgs(mIndex + 1)) { + break; + //not in class, it can't be a valid function definition + } else if (currentScope->kind != StatementKind::skClass) + break; + //variable can't be initialized in class definition, it must be a function + } else if (isNotFuncArgs(mIndex + 1)) break; } sName = mTokenizer[mIndex]->text; - sArgs = mTokenizer[mIndex + 1]->text; + argStartIndex = mIndex+1; + argEndIndex = mTokenizer[mIndex + 1]->matchIndex; bTypeOK = !sType.isEmpty(); bNameOK = !sName.isEmpty(); - bArgsOK = !sArgs.isEmpty(); + bArgsOK = true; // Allow constructor/destructor too if (!bTypeOK) { // Check for constructor/destructor outside class body - int delimPos = sName.indexOf("::"); + int delimPos = sName.lastIndexOf("::"); if (delimPos >= 0) { bTypeOK = true; sType = sName.mid(0, delimPos); @@ -1597,11 +1672,12 @@ bool CppParser::checkForMethod(QString &sType, QString &sName, QString &sArgs, b sType.remove(0,1); bTypeOK = isCurrentScope(sType); // constructor/destructor } + mIndex = argEndIndex+1; break; } else { //if IsValidIdentifier(mTokenizer[mIndex]->text) then // Still walking through type - QString s = expandMacroType(mTokenizer[mIndex]->text); //todo: do we really need expand macro? it should be done in preprocessor + QString s = mTokenizer[mIndex]->text; if (s == "static") isStatic = true; if (s == "friend") @@ -1613,63 +1689,54 @@ bool CppParser::checkForMethod(QString &sType, QString &sName, QString &sArgs, b mIndex++; } - mIndex = indexBackup; - // Correct function, don't jump over if (bTypeOK && bNameOK && bArgsOK) { sType = sType.trimmed(); // should contain type "int" sName = sName.trimmed(); // should contain function name "foo::function" - sArgs = sArgs.trimmed(); // should contain argument list "(int a)" return true; - } else + } else { + mIndex = indexBackup; return false; + } } -bool CppParser::checkForNamespace() +bool CppParser::checkForNamespace(KeywordType keywordType) { - return ((mIndex < mTokenizer.tokenCount()-1) - && (mTokenizer[mIndex]->text == "namespace")) - || ( - (mIndex+1 < mTokenizer.tokenCount()-1) - && (mTokenizer[mIndex]->text == "inline") - && (mTokenizer[mIndex+1]->text == "namespace")); + return (keywordType==KeywordType::Namespace &&(mIndex < mTokenizer.tokenCount()-1)) + || ( + keywordType==KeywordType::Inline + && (mIndex+1 < mTokenizer.tokenCount()-1) + &&mTokenizer[mIndex+1]->text == "namespace" + ); } bool CppParser::checkForPreprocessor() { -// return (mIndex < mTokenizer.tokenCount()) -// && ( "#" == mTokenizer[mIndex]->text); return (mTokenizer[mIndex]->text.startsWith('#')); } -bool CppParser::checkForScope() +//bool CppParser::checkForLambda() +//{ +// return (mIndex+1text.startsWith('[') +// && mTokenizer[mIndex+1]->text=='('); +//} + +bool CppParser::checkForScope(KeywordType keywordType) { - return (mIndex < mTokenizer.tokenCount() - 1) - && (mTokenizer[mIndex + 1]->text == ':') - && ( - (mTokenizer[mIndex]->text == "public") - || (mTokenizer[mIndex]->text == "protected") - || (mTokenizer[mIndex]->text == "private") + return ( (keywordType == KeywordType::Public || keywordType == KeywordType::Protected + || keywordType == KeywordType::Private) + && mIndex+1 < mTokenizer.tokenCount() + && mTokenizer[mIndex + 1]->text == ':' ); } -void CppParser::checkForSkipStatement() -{ - if ((mSkipList.count()>0) && (mIndex == mSkipList.back())) { // skip to next ';' - do { - mIndex++; - } while ((mIndex < mTokenizer.tokenCount()) && (mTokenizer[mIndex]->text[0] != ';')); - mIndex++; //skip ';' - mSkipList.pop_back(); - } -} - -bool CppParser::checkForStructs() +bool CppParser::checkForStructs(KeywordType keywordType) { int dis = 0; - if ((mTokenizer[mIndex]->text == "friend") - || (mTokenizer[mIndex]->text == "public") - || (mTokenizer[mIndex]->text == "private")) + if (keywordType == KeywordType::Friend + || keywordType == KeywordType::Public + || keywordType == KeywordType::Private) dis = 1; if (mIndex >= mTokenizer.tokenCount() - 2 - dis) return false; @@ -1695,9 +1762,12 @@ bool CppParser::checkForStructs() break; switch(ch.unicode()) { case ';': + case '{': case '}': case ',': + case '(': case ')': + case '[': case ']': case '=': case '*': @@ -1715,16 +1785,11 @@ bool CppParser::checkForStructs() return result; } -bool CppParser::checkForTypedef() -{ - return mTokenizer[mIndex]->text == "typedef"; -} - bool CppParser::checkForTypedefEnum() { //we assume that typedef is the current index, so we check the next //should call CheckForTypedef first!!! - return (mIndex < mTokenizer.tokenCount() - 1) && + return (mIndex+1 < mTokenizer.tokenCount() ) && (mTokenizer[mIndex + 1]->text == "enum"); } @@ -1741,74 +1806,203 @@ bool CppParser::checkForTypedefStruct() return (word.length() == keyLen) || isSpaceChar(word[keyLen]) || word[keyLen]=='['; } -bool CppParser::checkForUsing() +bool CppParser::checkForUsing(KeywordType keywordType) { - return (mIndex < mTokenizer.tokenCount()-1) && mTokenizer[mIndex]->text == "using"; + return keywordType==KeywordType::Using && (mIndex < mTokenizer.tokenCount()-1); } -bool CppParser::checkForVar() +void CppParser::checkAndHandleMethodOrVar(KeywordType keywordType) { - // Be pessimistic - bool result = false; - - // Store old index - int indexBackup = mIndex; - - // Use mIndex so we can reuse checking functions - if (mIndex + 1 < mTokenizer.tokenCount()) { - // Check the current and the next token - for (int i = 0; i<=1; i++) { - if (checkForKeyword() - || isInvalidVarPrefixChar(mTokenizer[mIndex]->text.front()) - || (mTokenizer[mIndex]->text.back() == '.') - || ( - (mTokenizer[mIndex]->text.length() > 1) && - (mTokenizer[mIndex]->text[mTokenizer[mIndex]->text.length() - 2] == '-') && - (mTokenizer[mIndex]->text[mTokenizer[mIndex]->text.length() - 1] == '>')) - ) { - // Reset index and fail - mIndex = indexBackup; - return false; - } // Could be a function pointer? - else if (mTokenizer[mIndex]->text.front() == '(') { - // Quick fix: there must be a pointer operator in the first tiken - if ( (mIndex + 1 >= mTokenizer.tokenCount()) - || (mTokenizer[mIndex + 1]->text.front() != '(') - || mTokenizer[mIndex]->text.indexOf('*')<0) { - // Reset index and fail - mIndex = indexBackup; - return false; + if (mIndex+2>=mTokenizer.tokenCount()) { + mIndex+=2; // left's finish; + return; + } + QString currentText=mTokenizer[mIndex]->text; + if (keywordType==KeywordType::DeclType) { + if (mTokenizer[mIndex+1]->text=='(') { + currentText="auto"; + mIndex=mTokenizer[mIndex+1]->matchIndex+1; + } else { + currentText=mTokenizer[mIndex+1]->text; + mIndex+=2; + } + } else + mIndex++; + //next token must be */&/word/(/{ + if (mTokenizer[mIndex]->text=='(') { + int indexAfterParentheis=mTokenizer[mIndex]->matchIndex+1; + if (indexAfterParentheis>=mTokenizer.tokenCount()) { + //error + mIndex=indexAfterParentheis; + } else if (mTokenizer[indexAfterParentheis]->text=='(') { + // operator overloading like (operator int) + if (mTokenizer[mIndex+1]->text=="operator") { + mIndex=indexAfterParentheis; + handleMethod(StatementKind::skFunction,"", + mergeArgs(mIndex+1,mTokenizer[mIndex]->matchIndex-1), + indexAfterParentheis,false,false); + } else if (currentText.endsWith("::operator")) { + mIndex=indexAfterParentheis; + handleMethod(StatementKind::skFunction,"", + mergeArgs(mIndex+1,mTokenizer[mIndex]->matchIndex-1), + indexAfterParentheis,false,false); + } else { + //function pointer var + handleVar(currentText,false,false); + } + } else { + if (currentText.startsWith("operator") + && currentText.length()>8 + && !isIdentChar(currentText[8])) { + // operator overloading + handleMethod(StatementKind::skFunction,"", + mergeArgs(mIndex+1,mTokenizer[mIndex]->matchIndex-1), + indexAfterParentheis,false,false); + return; + } + //check for constructor like Foo::Foo() + QString name; + QString temp,temp2; + QString parentName; + if (splitLastMember(currentText,name,temp)) { + //has '::' + bool isDestructor=false; + if (!splitLastMember(temp,parentName,temp2)) + parentName=temp; + if (name.startsWith('~')) + name=name.mid(1); + if (removeTemplateParams(name)==removeTemplateParams(parentName)) { + handleMethod( (isDestructor?StatementKind::skDestructor:StatementKind::skConstructor), + "", + currentText, + mIndex,false,false); + return; } } - mIndex++; + // check for constructor like: + // class Foo { + // Foo(); + // }; + PStatement scope=getCurrentScope(); + if (scope && scope->kind==StatementKind::skClass + && removeTemplateParams(scope->command) == removeTemplateParams(currentText)) { + handleMethod(StatementKind::skConstructor,"", + currentText, + mIndex,false,false); + return; + } + + // function call, skip it + mIndex=moveToNextBraceOrSkipNextSemicolon(mIndex,true); + } + } else if (mTokenizer[mIndex]->text.startsWith('*') + || mTokenizer[mIndex]->text.startsWith('&') + || tokenIsIdentifier(mTokenizer[mIndex]->text) + ) { + // it should be function/var + + bool isStatic = false; + bool isFriend = false; + bool isExtern = false; + + QString sType = currentText; // should contain type "int" + QString sName = ""; // should contain function name "foo::function" + + + + // Gather data for the string parts + while (mIndex+1 < mTokenizer.tokenCount()) { + if (mTokenizer[mIndex + 1]->text == '(') { + if (mTokenizer[mIndex+2]->text == '*') { + //foo(*blabla), it's a function pointer var + handleVar(sType,isExtern,isStatic); + return; + } + + int indexAfter=mTokenizer[mIndex + 1]->matchIndex+1; + if (indexAfter>=mTokenizer.tokenCount()) { + //error + mIndex=indexAfter; + return; + } + //if it's like: foo(...)(...) + if (mTokenizer[indexAfter]->text=='(') { + if (mTokenizer[mIndex]->text=="operator") { + //operator()() , it's an operator overload for () + handleMethod(StatementKind::skFunction,sType, + "operator()",indexAfter,isStatic,false); + return; + } + if (mTokenizer[mIndex]->text.endsWith("::operator")) { + // operator overloading + handleMethod(StatementKind::skFunction,"", + mTokenizer[mIndex]->text+"()",indexAfter,isStatic,false); + return; + } + //foo(...)(...), it's a function pointer var + handleVar(sType,isExtern,isStatic); + //Won't implement: ignore function decl like int (text)(int x) { }; + return; + } + //it's not a function define + if (mTokenizer[indexAfter]->text[0] == ',') { + // var decl with init + handleVar(sType,isExtern,isStatic); + return; + } + if (mTokenizer[indexAfter]->text[0] == ';') { + //function can only be defined in global/namespaces/classes + PStatement currentScope=getCurrentScope(); + if (currentScope) { + //in namespace, it might be function or object initilization + if (currentScope->kind == StatementKind::skNamespace + && isNotFuncArgs(mIndex + 1)) { + // var decl with init + handleVar(sType,isExtern,isStatic); + return; + //not in class, it can't be a valid function definition + } else if (currentScope->kind != StatementKind::skClass) { + // var decl with init + handleVar(sType,isExtern,isStatic); + return; + } + //variable can't be initialized in class definition, it must be a function + } else if (isNotFuncArgs(mIndex + 1)){ + // var decl with init + handleVar(sType,isExtern,isStatic); + return; + } + } + sName = mTokenizer[mIndex]->text; + mIndex++; + + handleMethod(StatementKind::skFunction,sType, + sName,mIndex,isStatic,isFriend); + + return; + } else if ( + mTokenizer[mIndex + 1]->text == ',' + ||mTokenizer[mIndex + 1]->text == ';' + ||mTokenizer[mIndex + 1]->text == ':' + ||mTokenizer[mIndex + 1]->text == '{' + ||mTokenizer[mIndex + 1]->text == '=') { + handleVar(sType,isExtern,isStatic); + return; + } else { + QString s = mTokenizer[mIndex]->text; + if (s == "static") + isStatic = true; + else if (s == "friend") + isFriend = true; + else if (s == "extern") + isExtern = true; + if (!s.isEmpty() && !(s=="extern")) + sType = sType + ' '+ s; + mIndex++; + } } } - - // Revert to the point we started at - mIndex = indexBackup; - - // Fail if we do not find a comma or a semicolon or a ( (inline constructor) - while (mIndex < mTokenizer.tokenCount()) { - if (mTokenizer[mIndex]->text.front() == '#' - || mTokenizer[mIndex]->text.front() == '}' - || checkForKeyword()) { - break; // fail -// } else if ((mTokenizer[mIndex]->text.length()>1) && (mTokenizer[mIndex]->text[0] == '(') -// && (mTokenizer[mIndex]->text[1] == '(')) { // TODO: is this used to remove __attribute stuff? -// break; - } else if (mTokenizer[mIndex]->text.front() == ',' - || mTokenizer[mIndex]->text.front() == ';' - || mTokenizer[mIndex]->text.front() == '{') { - result = true; - break; - } - mIndex++; - } - - // Revert to the point we started at - mIndex = indexBackup; - return result; } int CppParser::getCurrentBlockEndSkip() @@ -1908,11 +2102,11 @@ StatementScope CppParser::getScope() // Invalid class or namespace/extern if (!currentScope || (currentScope->kind == StatementKind::skNamespace)) - return StatementScope::ssGlobal; + return StatementScope::Global; else if (currentScope->kind == StatementKind::skClass) - return StatementScope::ssClassLocal; + return StatementScope::ClassLocal; else - return StatementScope::ssLocal; + return StatementScope::Local; } QString CppParser::getStatementKey(const QString &sName, const QString &sType, const QString &sNoNameArgs) @@ -1953,45 +2147,42 @@ void CppParser::handleCatchBlock() if (!((mIndex < mTokenizer.tokenCount()) && (mTokenizer[mIndex]->text.startsWith('(')))) return; //skip params - int i2=mIndex+1; + int i2=mTokenizer[mIndex]->matchIndex+1; if (i2>=mTokenizer.tokenCount()) return; if (mTokenizer[i2]->text.startsWith('{')) { mBlockBeginSkips.append(i2); - int i = skipBraces(i2); - if (i==i2) { - mBlockEndSkips.append(mTokenizer.tokenCount()); - } else { - mBlockEndSkips.append(i); - } + int i = indexOfMatchingBrace(i2); +// if (i==i2) { +// mBlockEndSkips.append(mTokenizer.tokenCount()); +// } else { + mBlockEndSkips.append(i); } else { - int i=i2; - while ((itext.startsWith(';')) - i++; + int i=indexOfNextSemicolon(i2); mBlockEndSkips.append(i); } // add a block PStatement block = addStatement( - getCurrentScope(), - mCurrentFile, - "", - "", - "", - "", - startLine, - StatementKind::skBlock, - getScope(), - mClassScope, - true, - false); - addSoloScopeLevel(block,startLine,false); - if (!mTokenizer[mIndex]->text.contains("...")) - scanMethodArgs(block,mTokenizer[mIndex]->text); + getCurrentScope(), + mCurrentFile, + "", + "", + "", + "", + "", + startLine, + StatementKind::skBlock, + getScope(), + mClassScope, + true, + false); + addSoloScopeLevel(block,startLine); + scanMethodArgs(block,mIndex); + mIndex=mTokenizer[mIndex]->matchIndex+1; } -void CppParser::handleEnum() +void CppParser::handleEnum(bool isTypedef) { - //todo : handle enum class QString enumName = ""; bool isEnumClass = false; int startLine = mTokenizer[mIndex]->line; @@ -2003,30 +2194,33 @@ void CppParser::handleEnum() mIndex++; //skip class } + bool isAdhocVar=false; + int endIndex=-1; if ((mIndex< mTokenizer.tokenCount()) && mTokenizer[mIndex]->text.startsWith('{')) { // enum {...} NAME // Skip to the closing brace - int i = skipBraces(mIndex); + int i = indexOfMatchingBrace(mIndex); // Have we found the name? - if ((i + 1 < mTokenizer.tokenCount()) && mTokenizer[i]->text.startsWith('}')) { - if (!mTokenizer[i + 1]->text.startsWith(';')) - enumName = mTokenizer[i + 1]->text.trimmed(); + if (i + 1 < mTokenizer.tokenCount()) { + enumName = mTokenizer[i + 1]->text.trimmed(); + if (!isIdentifierOrPointer(enumName)) { + //not a valid enum, skip to j + mIndex=indexOfNextSemicolon(i+1)+1; + return; + } + if (!isTypedef) { + //it's an ad-hoc enum var define; + if (isEnumClass) { + //Enum class can't add hoc, just skip to ; + mIndex=indexOfNextSemicolon(i+1)+1; + return; + } + enumName = "__enum__"+enumName+"__"; + isAdhocVar=true; + } } + endIndex=i+1; } else if (mIndex+1< mTokenizer.tokenCount() && mTokenizer[mIndex+1]->text.startsWith('{')){ // enum NAME {...}; - if ( (mIndex< mTokenizer.tokenCount()) && mTokenizer[mIndex]->text == "class") { - //enum class {...} NAME - isEnumClass = true; - mIndex++; - } - while ((mIndex < mTokenizer.tokenCount()) && - !(mTokenizer[mIndex]->text.startsWith('{') - || mTokenizer[mIndex]->text.startsWith(';'))) { - enumName += mTokenizer[mIndex]->text + ' '; - mIndex++; - } - enumName = enumName.trimmed(); - // An opening brace must be present after NAME - if ((mIndex >= mTokenizer.tokenCount()) || !mTokenizer[mIndex]->text.startsWith('{')) - return; + enumName = mTokenizer[mIndex]->text; } else { // enum NAME blahblah // it's an old c-style enum variable definition @@ -2035,39 +2229,72 @@ void CppParser::handleEnum() // Add statement for enum name too PStatement enumStatement; - if (!enumName.isEmpty()) { - if (isEnumClass) { - enumStatement=addStatement( - getCurrentScope(), - mCurrentFile, - "enum class", - enumName, - "", - "", - startLine, - StatementKind::skEnumClassType, - getScope(), - mClassScope, - true, - false); - } else { - enumStatement=addStatement( - getCurrentScope(), - mCurrentFile, - "enum", - enumName, - "", - "", - startLine, - StatementKind::skEnumType, - getScope(), - mClassScope, - true, - false); - } + if (isEnumClass) { + enumStatement=addStatement( + getCurrentScope(), + mCurrentFile, + "enum class", + enumName, + "", + "", + "", + startLine, + StatementKind::skEnumClassType, + getScope(), + mClassScope, + true, + false); } else { - enumStatement = getCurrentScope(); + enumStatement=addStatement( + getCurrentScope(), + mCurrentFile, + "enum", + enumName, + "", + "", + "", + startLine, + StatementKind::skEnumType, + getScope(), + mClassScope, + true, + false); } + if (isAdhocVar) { + //Ad-hoc var definition + // Skip to the closing brace + int i = indexOfMatchingBrace(mIndex)+1; + QString typeSuffix=""; + while (itext; + if (isIdentifierOrPointer(name)) { + QString suffix; + QString args; + parseCommandTypeAndArgs(name,suffix,args); + if (!name.isEmpty()) { + addStatement( + getCurrentScope(), + mCurrentFile, + enumName+suffix, + mTokenizer[i]->text, + args, + "", + "", + mTokenizer[i]->line, + StatementKind::skVariable, + getScope(), + mClassScope, + true, + false); + } + } else if (name!=',') { + break; + } + i++; + } + endIndex=indexOfNextSemicolon(i); + } + // Skip opening brace mIndex++; @@ -2078,18 +2305,12 @@ void CppParser::handleEnum() lastType += ' ' + enumName; QString cmd; QString args; - if (!mTokenizer[mIndex]->text.startsWith('}')) { + if (mTokenizer[mIndex]->text!='}') { while ((mIndex < mTokenizer.tokenCount()) && !isblockChar(mTokenizer[mIndex]->text[0])) { if (!mTokenizer[mIndex]->text.startsWith(',')) { - if (mTokenizer[mIndex]->text.endsWith(']')) { //array; break args - int p = mTokenizer[mIndex]->text.indexOf('['); - cmd = mTokenizer[mIndex]->text.mid(0,p); - args = mTokenizer[mIndex]->text.mid(p); - } else { - cmd = mTokenizer[mIndex]->text; - args = ""; - } + cmd = mTokenizer[mIndex]->text; + args = ""; if (isEnumClass) { if (enumStatement) { addStatement( @@ -2099,6 +2320,7 @@ void CppParser::handleEnum() cmd, args, "", + "", mTokenizer[mIndex]->line, StatementKind::skEnum, getScope(), @@ -2115,6 +2337,7 @@ void CppParser::handleEnum() cmd, args, "", + "", mTokenizer[mIndex]->line, StatementKind::skEnum, getScope(), @@ -2123,26 +2346,27 @@ void CppParser::handleEnum() false); } addStatement( - getCurrentScope(), - mCurrentFile, - lastType, - cmd, - args, - "", - mTokenizer[mIndex]->line, - StatementKind::skEnum, - getScope(), - mClassScope, - true, - false); + getCurrentScope(), + mCurrentFile, + lastType, + cmd, + "", + "", + "", + mTokenizer[mIndex]->line, + StatementKind::skEnum, + getScope(), + mClassScope, + true, + false); } } mIndex ++ ; } } - // Step over closing brace - if ((mIndex < mTokenizer.tokenCount()) && mTokenizer[mIndex]->text.startsWith('}')) - mIndex++; + if (mIndextext.startsWith(';')) - i++; - if (i>=mTokenizer.tokenCount()) - return; + int i=indexOfNextSemicolon(mIndex); int i2 = i+1; //skip over ';' (tokenizer have change for(;;) to for(;) if (i2>=mTokenizer.tokenCount()) return; if (mTokenizer[i2]->text.startsWith('{')) { mBlockBeginSkips.append(i2); - i=skipBraces(i2); - if (i==i2) - mBlockEndSkips.append(mTokenizer.tokenCount()); - else - mBlockEndSkips.append(i); + i=indexOfMatchingBrace(i2); +// tokenizer will handle unbalanced braces, no need check here +// if (i==i2) +// mBlockEndSkips.append(mTokenizer.tokenCount()); +// else + mBlockEndSkips.append(i); } else { - i=i2; - while ((itext.startsWith(';')) - i++; + i=indexOfNextSemicolon(i2); mBlockEndSkips.append(i); } // add a block @@ -2180,6 +2399,7 @@ void CppParser::handleForBlock() "", "", "", + "", startLine, StatementKind::skBlock, getScope(), @@ -2190,126 +2410,249 @@ void CppParser::handleForBlock() addSoloScopeLevel(block,startLine); } -void CppParser::handleKeyword() +void CppParser::handleKeyword(KeywordType skipType) { // Skip - SkipType skipType = mCppKeywords.value(mTokenizer[mIndex]->text,SkipType::skNone); switch (skipType) { - case SkipType::skItself: + case KeywordType::SkipItself: // skip it; mIndex++; break; - case SkipType::skToSemicolon: - // Skip to ; - while (mIndex < mTokenizer.tokenCount() && !mTokenizer[mIndex]->text.startsWith(';')) - mIndex++; - mIndex++;// step over + case KeywordType::SkipNextSemicolon: + // Skip to ; and over it + skipNextSemicolon(mIndex); break; - case SkipType::skToColon: - // Skip to : - while (mIndex < mTokenizer.tokenCount() && !mTokenizer[mIndex]->text.startsWith(':')) - mIndex++; + case KeywordType::SkipNextColon: + // Skip to : and over it + mIndex = indexOfNextColon(mIndex)+1; break; - case SkipType::skToRightParenthesis: - // skip to ) - while (mIndex < mTokenizer.tokenCount() && !mTokenizer[mIndex]->text.endsWith(')')) - mIndex++; - mIndex++; // step over + case KeywordType::SkipNextParenthesis: + // skip pass () + skipParenthesis(mIndex); break; - case SkipType::skToLeftBrace: + case KeywordType::MoveToLeftBrace: // Skip to { - while (mIndex < mTokenizer.tokenCount() && !mTokenizer[mIndex]->text.startsWith('{')) - mIndex++; + mIndex = indexOfNextLeftBrace(mIndex); break; - case SkipType::skToRightBrace: - // Skip to } - while (mIndex < mTokenizer.tokenCount() && !mTokenizer[mIndex]->text.startsWith('}')) - mIndex++; - mIndex++; // step over + case KeywordType::MoveToRightBrace: + // Skip pass {} + mIndex = indexPassBraces(mIndex); break; default: break; } } -void CppParser::handleMethod(const QString &sType, const QString &sName, const QString &sArgs, bool isStatic, bool isFriend) +void CppParser::handleLambda(int index, int endIndex) +{ + Q_ASSERT(mTokenizer[index]->text.startsWith('[')); + int startLine=mTokenizer[index]->line; + int argStart=index+1; + if (mTokenizer[argStart]->text!='(') + return; + int argEnd= mTokenizer[argStart]->matchIndex; + //TODO: parse captures + int bodyStart=indexOfNextLeftBrace(argEnd+1); + if (bodyStart>=endIndex) { + return; + } + int bodyEnd = mTokenizer[bodyStart]->matchIndex; + if (bodyEnd>=endIndex) { + return; + } + PStatement lambdaBlock = addStatement( + getCurrentScope(), + mCurrentFile, + "", + "", + "", + "", + "", + startLine, + StatementKind::skBlock, + StatementScope::Local, + StatementClassScope::None, + true, + false); + scanMethodArgs(lambdaBlock,argStart); + addSoloScopeLevel(lambdaBlock,mTokenizer[bodyStart]->line); + int i=bodyStart+1; // start after '{'; + while (i+2text) + && !mTokenizer[i]->text.endsWith('.') + && !mTokenizer[i]->text.endsWith("->") + && (mTokenizer[i+1]->text.startsWith('*') + || mTokenizer[i+1]->text.startsWith('&') + || tokenIsTypeOrNonKeyword(mTokenizer[i+1]->text))) + { + QString sType; + QString sName; + while (i+1text==':' + || mTokenizer[i+1]->text=='(' + || mTokenizer[i+1]->text=='=' + || mTokenizer[i+1]->text==';' + || mTokenizer[i+1]->text==',' + || mTokenizer[i+1]->text=='{' + ) + break; + else { + if (!sType.isEmpty()) + sType+=' '; + sType+=mTokenizer[i]->text; + } + i++; + } + QString tempType; + while (itext.front() == ':') { + while ( (i < mTokenizer.tokenCount()) + && !( + mTokenizer[i]->text==',' + || mTokenizer[i]->text==';' + || mTokenizer[i]->text=='=' + )) + i++; + } else if (mTokenizer[i]->text==';') { + break; + } else if (isWordChar(mTokenizer[i]->text[0])) { + QString cmd=mTokenizer[i]->text; + while (cmd.startsWith('*')) { + cmd=cmd.mid(1); + } + if (cmd=="const") { + tempType="const"; + } else { + QString suffix; + QString args; + cmd=mTokenizer[i]->text; + parseCommandTypeAndArgs(cmd,suffix,args); + if (!cmd.isEmpty()) { + addChildStatement( + lambdaBlock, + mCurrentFile, + (sType+' '+tempType+suffix).trimmed(), + cmd, + args, + "", + "", + mTokenizer[mIndex]->line, + StatementKind::skVariable, + getScope(), + mClassScope, + //True, + false, + false); // TODO: not supported to pass list + tempType=""; + } + } + i++; + } else if (mTokenizer[i]->text=='(') { + i=mTokenizer[i]->matchIndex+1; + } else if (mTokenizer[i]->text=='=') { + i = skipAssignment(i, mTokenizer.tokenCount()); + } else if (mTokenizer[i]->text=='{') { + tempType=""; + i=mTokenizer[i]->matchIndex+1; + } else { + tempType=""; + i++; + } + + } + } + i=moveToNextBraceOrSkipNextSemicolon(i, true, bodyEnd); + if (itext=='{') { + //skip '}' + i=mTokenizer[i]->matchIndex+1; + } + } + removeScopeLevel(mTokenizer[bodyEnd]->line); +} + +void CppParser::handleMethod(StatementKind functionKind,const QString &sType, const QString &sName, int argStart, bool isStatic, bool isFriend) { bool isValid = true; bool isDeclaration = false; // assume it's not a prototype - int i = mIndex; int startLine = mTokenizer[mIndex]->line; - - // Skip over argument list - while ((mIndex < mTokenizer.tokenCount()) && ! ( - isblockChar(mTokenizer[mIndex]->text.front()) - || mTokenizer[mIndex]->text.startsWith(':'))) - mIndex++; + int argEnd = mTokenizer[argStart]->matchIndex; if (mIndex >= mTokenizer.tokenCount()) // not finished define, just skip it; return; - PStatement functionClass = getCurrentScope(); + PStatement scopeStatement = getCurrentScope(); + + //find start of the function body; + bool foundColon=false; + mIndex=argEnd+1; + while ((mIndex < mTokenizer.tokenCount()) && !isblockChar(mTokenizer[mIndex]->text.front())) { + if (mTokenizer[mIndex]->text=='(') { + mIndex=mTokenizer[mIndex]->matchIndex+1; + }else if (mTokenizer[mIndex]->text==':') { + foundColon=true; + break; + } else + mIndex++; + } + if (foundColon) { + mIndex++; + while ((mIndex < mTokenizer.tokenCount()) && !isblockChar(mTokenizer[mIndex]->text.front())) { + if (isWordChar(mTokenizer[mIndex]->text[0]) + && mIndex+1text=='{') { + //skip parent {}intializer + mIndex=mTokenizer[mIndex+1]->matchIndex+1; + } else if (mTokenizer[mIndex]->text=='(') { + mIndex=mTokenizer[mIndex]->matchIndex+1; + } else + mIndex++; + } + + + } + // Check if this is a prototype if (mTokenizer[mIndex]->text.startsWith(';') || mTokenizer[mIndex]->text.startsWith('}')) {// prototype isDeclaration = true; - } else { - // Find the function body start after the inherited constructor - if ((mIndex < mTokenizer.tokenCount()) && mTokenizer[mIndex]->text.startsWith(':')) { - while ((mIndex < mTokenizer.tokenCount()) && !isblockChar(mTokenizer[mIndex]->text.front())) - mIndex++; - } - - // Still a prototype - if ((mIndex < mTokenizer.tokenCount()) && (mTokenizer[mIndex]->text.startsWith(';') - || mTokenizer[mIndex]->text.startsWith('}'))) {// prototype - isDeclaration = true; - } } QString scopelessName; PStatement functionStatement; - if (isFriend && isDeclaration && functionClass) { + if (isFriend && isDeclaration && scopeStatement) { int delimPos = sName.indexOf("::"); if (delimPos >= 0) { scopelessName = sName.mid(delimPos+2); } else scopelessName = sName; //TODO : we should check namespace - functionClass->friends.insert(scopelessName); + scopeStatement->friends.insert(scopelessName); } else if (isValid) { // Use the class the function belongs to as the parent ID if the function is declared outside of the class body - int delimPos = sName.indexOf("::"); QString scopelessName; QString parentClassName; - if (delimPos >= 0) { + if (splitLastMember(sName,scopelessName,parentClassName)) { // Provide Bar instead of Foo::Bar - scopelessName = sName.mid(delimPos+2); - - // Check what class this function belongs to - parentClassName = sName.mid(0, delimPos); - functionClass = getIncompleteClass(parentClassName,getCurrentScope()); + scopeStatement = getIncompleteClass(parentClassName,getCurrentScope()); } else scopelessName = sName; - StatementKind functionKind; - // Determine function type - if (scopelessName == sType) { - functionKind = StatementKind::skConstructor; - } else if (scopelessName == '~' + sType) { - functionKind = StatementKind::skDestructor; - } else { - functionKind = StatementKind::skFunction; - } // For function definitions, the parent class is given. Only use that as a parent if (!isDeclaration) { functionStatement=addStatement( - functionClass, + scopeStatement, mCurrentFile, sType, scopelessName, - sArgs, + argStart, + argEnd, "", //mTokenizer[mIndex - 1]^.Line, startLine, @@ -2318,25 +2661,27 @@ void CppParser::handleMethod(const QString &sType, const QString &sName, const Q mClassScope, true, isStatic); - scanMethodArgs(functionStatement, sArgs); + scanMethodArgs(functionStatement, argStart); // add variable this to the class function - if (functionClass && functionClass->kind == StatementKind::skClass && + if (scopeStatement && scopeStatement->kind == StatementKind::skClass && !isStatic) { //add this to non-static class member function addStatement( functionStatement, mCurrentFile, - functionClass->command, + scopeStatement->command+"*", "this", "", "", + "", startLine, StatementKind::skVariable, - StatementScope::ssLocal, - StatementClassScope::scsNone, + StatementScope::Local, + StatementClassScope::None, true, false); } + // add "__func__ variable" addStatement( functionStatement, @@ -2344,20 +2689,23 @@ void CppParser::handleMethod(const QString &sType, const QString &sName, const Q "static const char ", "__func__", "[]", + "", "\""+scopelessName+"\"", startLine+1, StatementKind::skVariable, - StatementScope::ssLocal, - StatementClassScope::scsNone, + StatementScope::Local, + StatementClassScope::None, true, false); + } else { functionStatement = addStatement( - functionClass, + scopeStatement, mCurrentFile, sType, scopelessName, - sArgs, + argStart, + argEnd, "", //mTokenizer[mIndex - 1]^.Line, startLine, @@ -2370,7 +2718,6 @@ void CppParser::handleMethod(const QString &sType, const QString &sName, const Q } - if ((mIndex < mTokenizer.tokenCount()) && mTokenizer[mIndex]->text.startsWith('{')) { addSoloScopeLevel(functionStatement,startLine); mIndex++; //skip '{' @@ -2382,29 +2729,23 @@ void CppParser::handleMethod(const QString &sType, const QString &sName, const Q removeScopeLevel(startLine+1); mIndex++; } - - if (i == mIndex) { // if not moved ahead, something is wrong but don't get stuck ;) - if ( (mIndex < mTokenizer.tokenCount()) && - ! isBraceChar(mTokenizer[mIndex]->text.front())) { - mIndex++; - } - } } -void CppParser::handleNamespace() +void CppParser::handleNamespace(KeywordType skipType) { bool isInline=false; - if (mTokenizer[mIndex]->text == "inline") { + int startLine = mTokenizer[mIndex]->line; + + if (skipType==KeywordType::Inline) { isInline = true; mIndex++; //skip 'inline' } - int startLine = mTokenizer[mIndex]->line; mIndex++; //skip 'namespace' - if (!isLetterChar(mTokenizer[mIndex]->text.front())) - //wrong namespace define, stop handling - return; +// if (!tokenIsIdentifier(mTokenizer[mIndex]->text)) +// //wrong namespace define, stop handling +// return; QString command = mTokenizer[mIndex]->text; QString fullName = getFullStatementName(command,getCurrentScope()); @@ -2419,7 +2760,7 @@ void CppParser::handleNamespace() if (mIndex>=mTokenizer.tokenCount()) return; QString aliasName; - if ((mIndex+2text.front() == '=')) { + if ((mIndex+2text == '=')) { aliasName=mTokenizer[mIndex+1]->text; //namespace alias addStatement( @@ -2428,6 +2769,7 @@ void CppParser::handleNamespace() aliasName, // name of the alias namespace command, // command "", // args + "", // noname args "", // values //mTokenizer[mIndex]^.Line, startLine, @@ -2441,9 +2783,9 @@ void CppParser::handleNamespace() } else if (isInline) { //inline namespace , just skip it // Skip to '{' - while ((mIndextext.front() != '{')) + while ((mIndextext != '{')) mIndex++; - int i =skipBraces(mIndex); //skip '}' + int i =indexOfMatchingBrace(mIndex); //skip '}' if (i==mIndex) mInlineNamespaceEndSkips.append(mTokenizer.tokenCount()); else @@ -2452,25 +2794,26 @@ void CppParser::handleNamespace() mIndex++; //skip '{' } else { PStatement namespaceStatement = addStatement( - getCurrentScope(), - mCurrentFile, - "", // type - command, // command - "", // args - "", // values - startLine, - StatementKind::skNamespace, - getScope(), - mClassScope, - true, - false); - addSoloScopeLevel(namespaceStatement,startLine); + getCurrentScope(), + mCurrentFile, + "", // type + command, // command + "", // args + "", // noname args + "", // values + startLine, + StatementKind::skNamespace, + getScope(), + mClassScope, + true, + false); - // Skip to '{' - while ((mIndextext.startsWith('{')) - mIndex++; - if (mIndextext=='{') + addSoloScopeLevel(namespaceStatement,startLine); + //skip it + mIndex++; } } @@ -2483,78 +2826,70 @@ void CppParser::handleOtherTypedefs() if (mIndex>=mTokenizer.tokenCount()) return; - if (mTokenizer[mIndex]->text.front() == '(' - || mTokenizer[mIndex]->text.front() == ',' - || mTokenizer[mIndex]->text.front() == ';') { // error typedef - //skip to ; - while ((mIndex< mTokenizer.tokenCount()) && !mTokenizer[mIndex]->text.startsWith(';')) - mIndex++; - //skip ; - if ((mIndex< mTokenizer.tokenCount()) && mTokenizer[mIndex]->text.startsWith(';')) - mIndex++; + if (mTokenizer[mIndex]->text == '(' + || mTokenizer[mIndex]->text == ',' + || mTokenizer[mIndex]->text == ';') { // error typedef + //skip over next ; + mIndex=indexOfNextSemicolon(mIndex)+1; return; } if ((mIndex+1text == ';')) { - //no old type - QString newType = mTokenizer[mIndex]->text.trimmed(); - addStatement( - getCurrentScope(), - mCurrentFile, - "", - newType, - "", - "", - startLine, - StatementKind::skTypedef, - getScope(), - mClassScope, - true, - false); + //no old type, not valid mIndex+=2; //skip ; return; } - QString oldType; + QString oldType; // Walk up to first new word (before first comma or ;) while(true) { oldType += mTokenizer[mIndex]->text + ' '; mIndex++; - if (mIndex+1>=mTokenizer.tokenCount()) + if (mIndex+1>=mTokenizer.tokenCount()) { + //not valid, just exit + return; + } + if (mTokenizer[mIndex]->text=='(') { break; + } if (mTokenizer[mIndex + 1]->text.front() == ',' - || mTokenizer[mIndex + 1]->text.front() == ';') - break; - if ((mIndex + 2 < mTokenizer.tokenCount()) - && (mTokenizer[mIndex + 2]->text.front() == ',' - || mTokenizer[mIndex + 2]->text.front() == ';') - && (mTokenizer[mIndex + 1]->text.front() == '(')) + || mTokenizer[mIndex + 1]->text == ';') break; + //typedef function pointer + } oldType = oldType.trimmed(); - - // Add synonyms for old - if ((mIndex+1 < mTokenizer.tokenCount()) && !oldType.isEmpty()) { - QString newType; - while(true) { - // Support multiword typedefs - if ((mIndex + 2 < mTokenizer.tokenCount()) - && (mTokenizer[mIndex + 2]->text.front() == ',' - || mTokenizer[mIndex + 2]->text.front() == ';') - && (mTokenizer[mIndex + 1]->text.front() == '(')) { - //valid function define - newType = mTokenizer[mIndex]->text.trimmed(); - newType = newType.mid(1,newType.length()-2); //remove '(' and ')'; - newType = newType.trimmed(); - int p = newType.lastIndexOf(' '); - if (p>=0) - newType.truncate(p+1); + if (oldType.isEmpty()) { + //skip over next ; + mIndex=indexOfNextSemicolon(mIndex)+1; + return; + } + QString newType; + while(mIndex+1text == ',' ) { + mIndex++; + } else if (mTokenizer[mIndex]->text == ';' ) { + break; + } else if (mTokenizer[mIndex]->text == '(') { + int paramStart=mTokenizer[mIndex]->matchIndex+1; + QString newType = mTokenizer[mIndex+1]->text; + if (paramStart>=mTokenizer.tokenCount() + || mTokenizer[paramStart]->text!='(') { + //not valid function pointer (no args) + //skip over next ; + mIndex=indexOfNextSemicolon(paramStart)+1; + return; + } + if (newType.startsWith('*')) + newType = newType.mid(1); + if (!newType.isEmpty()) { addStatement( getCurrentScope(), mCurrentFile, oldType, newType, - mTokenizer[mIndex + 1]->text, + mergeArgs(paramStart,mTokenizer[paramStart]->matchIndex), + "", "", startLine, StatementKind::skTypedef, @@ -2562,19 +2897,21 @@ void CppParser::handleOtherTypedefs() mClassScope, true, false); - newType = ""; - //skip to ',' or ';' - mIndex+=2; - } else if (mTokenizer[mIndex+1]->text.front() ==',' - || mTokenizer[mIndex+1]->text.front() ==';' - || mTokenizer[mIndex+1]->text.front() =='(') { + } + mIndex = mTokenizer[paramStart]->matchIndex+1; + } else if (mTokenizer[mIndex+1]->text.front() ==',' + || mTokenizer[mIndex+1]->text.front() ==';') { newType += mTokenizer[mIndex]->text; - newType = newType.trimmed(); + QString suffix; + QString args; + parseCommandTypeAndArgs(newType,suffix,args); + addStatement( getCurrentScope(), mCurrentFile, - oldType, + oldType+suffix, newType, + args, "", "", startLine, @@ -2585,16 +2922,9 @@ void CppParser::handleOtherTypedefs() false); newType = ""; mIndex++; - } else { - newType += mTokenizer[mIndex]->text + ' '; - mIndex++; - } - if (mIndex < mTokenizer.tokenCount() && mTokenizer[mIndex]->text[0] == ',' ) - mIndex++; - if ((mIndex>= mTokenizer.tokenCount()) || (mTokenizer[mIndex]->text[0] == ';')) - break; - if (mIndex+1 >= mTokenizer.tokenCount()) - break; + } else { + newType += mTokenizer[mIndex]->text; + mIndex++; } } @@ -2613,6 +2943,7 @@ void CppParser::handlePreprocessor() goto handlePreprocessorEnd; int delimPos = s.lastIndexOf(':'); if (delimPos>=0) { +// qDebug()<line<line, - StatementKind::skPreprocessor, - StatementScope::ssGlobal, - StatementClassScope::scsNone, - true, - false); + nullptr, // defines don't belong to any scope + mCurrentFile, + "", // define has no type + name, + args, + "",// noname args + value, + mTokenizer[mIndex]->line, + StatementKind::skPreprocessor, + StatementScope::Global, + StatementClassScope::None, + true, + false); } // TODO: undef ( define has limited scope) handlePreprocessorEnd: mIndex++; } -StatementClassScope CppParser::getClassScope(int index) { - if (mTokenizer[index]->text=="public") - return StatementClassScope::scsPublic; - else if (mTokenizer[index]->text=="private") - return StatementClassScope::scsPrivate; - else if (mTokenizer[index]->text=="protected") - return StatementClassScope::scsProtected; - else - return StatementClassScope::scsNone; +StatementClassScope CppParser::getClassScope(const QString& text) { + if (!text.isEmpty() && text[0]=='p') { + if (text=="public") + return StatementClassScope::Public; + else if (text=="private") + return StatementClassScope::Private; + else if (text=="protected") + return StatementClassScope::Protected; + } + return StatementClassScope::None; } -void CppParser::handleScope() +StatementClassScope CppParser::getClassScope(KeywordType keywordType) { - mClassScope = getClassScope(mIndex); + switch(keywordType) { + case KeywordType::Public: + return StatementClassScope::Public; + case KeywordType::Private: + return StatementClassScope::Private; + case KeywordType::Protected: + return StatementClassScope::Protected; + default: + return StatementClassScope::None; + } +} + +void CppParser::handleScope(KeywordType keywordType) +{ + mClassScope = getClassScope(keywordType); mIndex+=2; // the scope is followed by a ':' } bool CppParser::handleStatement() { - QString S1,S2,S3; - bool isStatic, isFriend; + QString funcType,funcName; int idx=getCurrentBlockEndSkip(); int idx2=getCurrentBlockBeginSkip(); int idx3=getCurrentInlineNamespaceEndSkip(); + KeywordType keywordType; + if (mIndex >= idx2) { //skip (previous handled) block begin mBlockBeginSkips.pop_back(); @@ -2701,62 +3049,99 @@ bool CppParser::handleStatement() mIndex++; } else if (mTokenizer[mIndex]->text.startsWith('{')) { PStatement block = addStatement( - getCurrentScope(), - mCurrentFile, - "", - "", - "", - "", - //mTokenizer[mIndex]^.Line, - mTokenizer[mIndex]->line, - StatementKind::skBlock, - getScope(), - mClassScope, - true, - false); - addSoloScopeLevel(block,mTokenizer[mIndex]->line); + getCurrentScope(), + mCurrentFile, + "", + "", + "", + "", + "", + //mTokenizer[mIndex]^.Line, + mTokenizer[mIndex]->line, + StatementKind::skBlock, + getScope(), + mClassScope, + true, + false); + addSoloScopeLevel(block,mTokenizer[mIndex]->line,true); mIndex++; } else if (mTokenizer[mIndex]->text[0] == '}') { removeScopeLevel(mTokenizer[mIndex]->line); mIndex++; } else if (checkForPreprocessor()) { handlePreprocessor(); - } else if (checkForKeyword()) { // includes template now - handleKeyword(); - } else if (checkForForBlock()) { // (for/catch) +// } else if (checkForLambda()) { // is lambda +// handleLambda(); + } else if (mTokenizer[mIndex]->text=='(') { + if (mTokenizer[mIndex]->text=="operator") { + // things like (operator int) + mIndex++; //just skip '(' + } else + skipParenthesis(mIndex); + } else if (mTokenizer[mIndex]->text==')') { + mIndex++; + } else if (mTokenizer[mIndex]->text.startsWith('~')) { + //it should be a destructor + if (mIndex+1text=='(') { + //dont further check to speed up + handleMethod(StatementKind::skDestructor, "", '~'+mTokenizer[mIndex]->text, mIndex+1, false, false); + } else { + //error + mIndex=moveToNextBraceOrSkipNextSemicolon(mIndex,false); + } + } else if (!isIdentChar(mTokenizer[mIndex]->text[0])) { + mIndex=moveToNextBraceOrSkipNextSemicolon(mIndex,false); + } else if (mTokenizer[mIndex]->text.endsWith('.') + || mTokenizer[mIndex]->text.endsWith("->")) { + mIndex=moveToNextBraceOrSkipNextSemicolon(mIndex,true); + } else if (checkForKeyword(keywordType)) { // includes template now + handleKeyword(keywordType); + } else if (keywordType==KeywordType::For) { // (for/catch) handleForBlock(); - } else if (checkForCatchBlock()) { // (for/catch) + } else if (keywordType==KeywordType::Catch) { // (for/catch) handleCatchBlock(); - } else if (checkForScope()) { // public /private/proteced - handleScope(); - } else if (checkForEnum()) { - handleEnum(); - } else if (checkForTypedef()) { + } else if (checkForScope(keywordType)) { // public /private/proteced + handleScope(keywordType); + } else if (keywordType==KeywordType::Enum) { + handleEnum(false); + } else if (keywordType==KeywordType::Typedef) { if (mIndex+1 < mTokenizer.tokenCount()) { if (checkForTypedefStruct()) { // typedef struct something mIndex++; // skip 'typedef' handleStructs(true); } else if (checkForTypedefEnum()) { // typedef enum something mIndex++; // skip 'typedef' - handleEnum(); + handleEnum(true); } else handleOtherTypedefs(); // typedef Foo Bar } else mIndex++; - } else if (checkForNamespace()) { - handleNamespace(); - } else if (checkForUsing()) { + } else if (checkForNamespace(keywordType)) { + handleNamespace(keywordType); + } else if (checkForUsing(keywordType)) { handleUsing(); - } else if (checkForStructs()) { + } else if (checkForStructs(keywordType)) { handleStructs(false); - } else if (checkForMethod(S1, S2, S3, isStatic, isFriend)) { - handleMethod(S1, S2, S3, isStatic, isFriend); // don't recalculate parts - } else if (checkForVar()) { - handleVar(); - } else - mIndex++; + } else { + // it should be method/constructor/var + checkAndHandleMethodOrVar(keywordType); + } + Q_ASSERT(mIndex<999999); - checkForSkipStatement(); +// while (mTokenizer.lambdasCount()>0 && mTokenizer.indexOfFirstLambda()=mTokenizer.tokenCount()) return; - // Do not modifiy index initially - int i = mIndex; - - // Skip until the struct body starts - while ((i < mTokenizer.tokenCount()) && ! ( - mTokenizer[i]->text.front() ==';' - || mTokenizer[i]->text.front() =='{')) - i++; - + // Do not modifiy index + int i=indexOfNextSemicolonOrLeftBrace(mIndex); + if (i >= mTokenizer.tokenCount()) { + //error + mIndex=i; + return; + } // Forward class/struct decl *or* typedef, e.g. typedef struct some_struct synonym1, synonym2; - if ((i < mTokenizer.tokenCount()) && (mTokenizer[i]->text.front() == ';')) { + if (mTokenizer[i]->text.front() == ';') { // typdef struct Foo Bar if (isTypedef) { QString oldType = mTokenizer[mIndex]->text; @@ -2805,8 +3188,9 @@ void CppParser::handleStructs(bool isTypedef) mCurrentFile, oldType, newType, - "", - "", + "", // args + "", // noname args + "", // values startLine, StatementKind::skTypedef, getScope(), @@ -2838,20 +3222,34 @@ void CppParser::handleStructs(bool isTypedef) PStatement firstSynonym; // Add class/struct name BEFORE opening brace if (mTokenizer[mIndex]->text.front() != '{') { - while(true) { - if ((mIndex + 1 < mTokenizer.tokenCount()) + while(mIndex < mTokenizer.tokenCount()) { + if (mTokenizer[mIndex]->text.front() == ':' + || mTokenizer[mIndex]->text.front() == '{' + || mTokenizer[mIndex]->text.front() == ';') { + break; + } else if ((mIndex + 1 < mTokenizer.tokenCount()) && (mTokenizer[mIndex + 1]->text.front() == ',' || mTokenizer[mIndex + 1]->text.front() == ';' || mTokenizer[mIndex + 1]->text.front() == '{' || mTokenizer[mIndex + 1]->text.front() == ':')) { QString command = mTokenizer[mIndex]->text; + + PStatement scopeStatement=getCurrentScope(); + QString scopelessName; + QString parentName; + if (splitLastMember(command,scopelessName,parentName)) { + scopeStatement = getIncompleteClass(parentName,getCurrentScope()); + } else { + scopelessName=command; + } if (!command.isEmpty()) { firstSynonym = addStatement( - getCurrentScope(), + scopeStatement, mCurrentFile, prefix, // type - command, // command + scopelessName, // command "", // args + "", // no name args, "", // values startLine, StatementKind::skClass, @@ -2862,6 +3260,7 @@ void CppParser::handleStructs(bool isTypedef) command = ""; } mIndex++; + break; } else if ((mIndex + 2 < mTokenizer.tokenCount()) && (mTokenizer[mIndex + 1]->text == "final") && (mTokenizer[mIndex + 2]->text.front()==',' @@ -2875,6 +3274,7 @@ void CppParser::handleStructs(bool isTypedef) prefix, // type command, // command "", // args + "", // no name args "", // values startLine, StatementKind::skClass, @@ -2885,14 +3285,9 @@ void CppParser::handleStructs(bool isTypedef) command=""; } mIndex+=2; + break; } else mIndex++; - if (mIndex >= mTokenizer.tokenCount()) - break; - if (mTokenizer[mIndex]->text.front() == ':' - || mTokenizer[mIndex]->text.front() == '{' - || mTokenizer[mIndex]->text.front() == ';') - break; } } @@ -2900,50 +3295,41 @@ void CppParser::handleStructs(bool isTypedef) if ((mIndex < mTokenizer.tokenCount()) && (mTokenizer[mIndex]->text.front() == ':')) { if (firstSynonym) setInheritance(mIndex, firstSynonym, isStruct); // set the _InheritanceList value - while ((mIndex < mTokenizer.tokenCount()) && (mTokenizer[mIndex]->text.front() != '{')) - mIndex++; // skip decl after ':' + mIndex=indexOfNextLeftBrace(mIndex); } // Check for struct synonyms after close brace if (isStruct) { // Walk to closing brace - i = skipBraces(mIndex); // step onto closing brace + i = indexOfMatchingBrace(mIndex); // step onto closing brace if ((i + 1 < mTokenizer.tokenCount()) && !( mTokenizer[i + 1]->text.front() == ';' || mTokenizer[i + 1]->text.front() == '}')) { // When encountering names again after struct body scanning, skip it - mSkipList.append(i+1); // add first name to skip statement so that we can skip it until the next ; QString command = ""; QString args = ""; // Add synonym before opening brace while(true) { i++; - - if (!(mTokenizer[i]->text.front() == '{' - || mTokenizer[i]->text.front() == ',' - || mTokenizer[i]->text.front() == ';')) { -// if ((mTokenizer[i]->text.front() == '_') -// && (mTokenizer[i]->text.back() == '_')) { -// // skip possible gcc attributes -// // start and end with 2 underscores (i.e. __attribute__) -// // so, to avoid slow checks of strings, we just check the first and last letter of the token -// // if both are underscores, we split -// break; -// } else { - if (mTokenizer[i]->text.endsWith(']')) { // cut-off array brackets - int pos = mTokenizer[i]->text.indexOf('['); - command += mTokenizer[i]->text.mid(0,pos) + ' '; - args = mTokenizer[i]->text.mid(pos); - } else if (mTokenizer[i]->text.front() == '*' - || mTokenizer[i]->text.front() == '&') { // do not add spaces after pointer operator - command += mTokenizer[i]->text; - } else { - command += mTokenizer[i]->text + ' '; - } -// } + if (mTokenizer[i]->text=='(' + || mTokenizer[i]->text==')') { + //skip + } else if (!(mTokenizer[i]->text == '{' + || mTokenizer[i]->text == ',' + || mTokenizer[i]->text == ';')) { + if (mTokenizer[i]->text.endsWith(']')) { // cut-off array brackets + int pos = mTokenizer[i]->text.indexOf('['); + command += mTokenizer[i]->text.mid(0,pos) + ' '; + args = mTokenizer[i]->text.mid(pos); + } else if (mTokenizer[i]->text.front() == '*' + || mTokenizer[i]->text.front() == '&') { // do not add spaces after pointer operator + command += mTokenizer[i]->text; + } else { + command += mTokenizer[i]->text + ' '; + } } else { command = command.trimmed(); if (!command.isEmpty() && @@ -2958,6 +3344,7 @@ void CppParser::handleStructs(bool isTypedef) "__"+command, "", "", + "", startLine, StatementKind::skClass, getScope(), @@ -2968,18 +3355,19 @@ void CppParser::handleStructs(bool isTypedef) if (isTypedef) { //typedef addStatement( - getCurrentScope(), - mCurrentFile, - firstSynonym->command, - command, - "", - "", - mTokenizer[mIndex]->line, - StatementKind::skTypedef, - getScope(), - mClassScope, - true, - false); // typedef + getCurrentScope(), + mCurrentFile, + firstSynonym->command, + command, + "", + "", + "", + mTokenizer[mIndex]->line, + StatementKind::skTypedef, + getScope(), + mClassScope, + true, + false); // typedef } else { //variable define addStatement( @@ -2989,6 +3377,7 @@ void CppParser::handleStructs(bool isTypedef) command, args, "", + "", mTokenizer[i]->line, StatementKind::skVariable, getScope(), @@ -3001,8 +3390,8 @@ void CppParser::handleStructs(bool isTypedef) } if (i >= mTokenizer.tokenCount() - 1) break; - if (mTokenizer[i]->text.front()=='{' - || mTokenizer[i]->text.front()== ';') + if (mTokenizer[i]->text=='{' + || mTokenizer[i]->text== ';') break; } @@ -3011,7 +3400,7 @@ void CppParser::handleStructs(bool isTypedef) } } if (!firstSynonym) { - //anonymous union/struct/class, add ast a block + //anonymous union/struct/class, add as a block firstSynonym=addStatement( getCurrentScope(), mCurrentFile, @@ -3019,6 +3408,7 @@ void CppParser::handleStructs(bool isTypedef) "", "", "", + "", startLine, StatementKind::skBlock, getScope(), @@ -3038,10 +3428,8 @@ void CppParser::handleUsing() { int startLine = mTokenizer[mIndex]->line; if (mCurrentFile.isEmpty()) { - //skip to ; - while ((mIndex < mTokenizer.tokenCount()) && (mTokenizer[mIndex]->text!=';')) - mIndex++; - mIndex++; //skip ; + //skip pass next ; + mIndex=indexOfNextSemicolon(mIndex)+1; return; } @@ -3064,6 +3452,7 @@ void CppParser::handleUsing() aliasName, // name of the alias (type) fullName, // command "", // args + "", // noname args "", // values startLine, StatementKind::skTypedef, @@ -3088,6 +3477,7 @@ void CppParser::handleUsing() fullName, // name of the alias (type) usingName, // command "", // args + "", // noname args "", // values startLine, StatementKind::skAlias, @@ -3096,11 +3486,8 @@ void CppParser::handleUsing() true, false); } - //skip to ; - while ((mIndextext!=";")) - mIndex++; - mIndex++; //and skip it + //skip to ; and skip it + mIndex=indexOfNextSemicolon(mIndex)+1; return; } mIndex++; // skip 'namespace' @@ -3127,167 +3514,161 @@ void CppParser::handleUsing() } } -void CppParser::handleVar() +void CppParser::handleVar(const QString& typePrefix,bool isExtern,bool isStatic) { - // Keep going and stop on top of the variable name - QString lastType = ""; - bool isFunctionPointer = false; - bool isExtern = false; - bool isStatic = false; - bool varAdded = false; - while (true) { - if ((mIndex + 2 < mTokenizer.tokenCount()) - && (mTokenizer[mIndex + 1]->text.front() == '(') - && (mTokenizer[mIndex + 2]->text.front() == '(')) { - isFunctionPointer = mTokenizer[mIndex + 1]->text.indexOf('*') >= 0; - if (!isFunctionPointer) - break; // inline constructor - } else if ((mIndex + 1 < mTokenizer.tokenCount()) - && (mTokenizer[mIndex + 1]->text.front()=='(' - || mTokenizer[mIndex + 1]->text.front()==',' - || mTokenizer[mIndex + 1]->text.front()==';' - || mTokenizer[mIndex + 1]->text.front()==':' - || mTokenizer[mIndex + 1]->text.front()=='}' - || mTokenizer[mIndex + 1]->text.front()=='#' - || mTokenizer[mIndex + 1]->text.front()=='{')) { - break; - } - - - // we've made a mistake, this is a typedef , not a variable definition. - if (mTokenizer[mIndex]->text == "typedef") - return; - - // struct/class/union is part of the type signature - // but we dont store it in the type cache, so must trim it to find the type info - if (mTokenizer[mIndex]->text!="struct" - && mTokenizer[mIndex]->text!="class" - && mTokenizer[mIndex]->text!="union") { - if (mTokenizer[mIndex]->text == ':') { - lastType += ':'; - } else { - QString s=expandMacroType(mTokenizer[mIndex]->text); - if (s == "extern") { - isExtern = true; - } else { - if (!s.isEmpty()) - lastType += ' '+s; - if (s == "static") - isStatic = true; - } - } - } - mIndex++; - if(mIndex >= mTokenizer.tokenCount()) - break; - if (isFunctionPointer) - break; + QString lastType; + if (typePrefix=="extern") { + isExtern=true; + } else if (typePrefix=="static") { + isStatic=true; + } else { + lastType=typePrefix; } - lastType = lastType.trimmed(); - // Don't bother entering the scanning loop when we have failed - if (mIndex >= mTokenizer.tokenCount()) - return; - // Find the variable name - while (true) { + //we only check the first token to reduce calculations +// if (mIndex>=mTokenizer.tokenCount() +// || mTokenizer[mIndex]->text.endsWith('.') +// || mTokenizer[mIndex]->text.endsWith("->")) { +// //failed to handle +// skipNextSemicolon(mIndex); +// return ; +// } +// while (mIndex+1text=='(') { +// if ( mTokenizer[mIndex]->matchIndex<=mTokenizer.tokenCount() +// && mTokenizer[mTokenizer[mIndex]->matchIndex]->text=='(') { +// //function pointer +// break; +// } +// //error break; +// mIndex=indexBackup; +// return false; +// } else if (mTokenizer[mIndex + 1]->text=='(' +// || mTokenizer[mIndex + 1]->text==',' +// || mTokenizer[mIndex + 1]->text==';' +// || mTokenizer[mIndex + 1]->text.front()==':' +// || mTokenizer[mIndex + 1]->text=='}' +// || mTokenizer[mIndex + 1]->text.front()=='#' +// || mTokenizer[mIndex + 1]->text=='{') { +// //end of type info +// break; +// } else if (mTokenizer[mIndex]->text!="struct" +// && mTokenizer[mIndex]->text!="class" +// && mTokenizer[mIndex]->text!="union") { +// QString s=mTokenizer[mIndex]->text; +// if (s == "extern") { +// isExtern = true; +// } else if (s == "static") { +// isStatic = true; +// } else +// lastType += ' '+s; +// } +// mIndex++; +// } + +// if (mIndex+1 >= mTokenizer.tokenCount() || lastType.isEmpty() +// || lastType.endsWith(':')) { +// mIndex=indexBackup; +// return false; +// } + + bool varAdded = false; + QString tempType; + while(mIndextext.front() == ':')) { + if (mTokenizer[mIndex]->text.front() == ':') { while ( (mIndex < mTokenizer.tokenCount()) && !( - mTokenizer[mIndex]->text.front() == ',' - || isblockChar(';') + mTokenizer[mIndex]->text==',' + || mTokenizer[mIndex]->text==';' + || mTokenizer[mIndex]->text=='=' )) mIndex++; - } - - // Skip inline constructors, - // e.g.: - // handle - // int a(3) - // as - // int a - if (!isFunctionPointer && - mIndex < mTokenizer.tokenCount() && - mTokenizer[mIndex]->text.front() == '(') { - while ((mIndex < mTokenizer.tokenCount()) - && !( - mTokenizer[mIndex]->text.front() == ',' - || isblockChar(mTokenizer[mIndex]->text.front()) - )) - mIndex++; - } - - // Did we stop on top of the variable name? - if (mIndex < mTokenizer.tokenCount()) { - if (mTokenizer[mIndex]->text.front()!=',' - && mTokenizer[mIndex]->text.front()!=';') { - QString cmd; - QString args; - if (isFunctionPointer && (mIndex + 1 < mTokenizer.tokenCount())) { - QString s = mTokenizer[mIndex]->text; - cmd = s.mid(2,s.length()-3).trimmed(); // (*foo) -> foo - args = mTokenizer[mIndex + 1]->text; // (int a,int b) - lastType += "(*)" + args; // void(int a,int b) - mIndex++; - } else if (mTokenizer[mIndex]->text.back() == ']') { //array; break args - int pos = mTokenizer[mIndex]->text.indexOf('['); - cmd = mTokenizer[mIndex]->text.mid(0,pos); - args = mTokenizer[mIndex]->text.mid(pos); - } else { - cmd = mTokenizer[mIndex]->text; - args = ""; - } - - // Add a statement for every struct we are in - if (!lastType.isEmpty()) { - addChildStatement( - getCurrentScope(), - mCurrentFile, - lastType, - cmd, - args, - "", - mTokenizer[mIndex]->line, - StatementKind::skVariable, - getScope(), - mClassScope, - //True, - !isExtern, - isStatic); // TODO: not supported to pass list - varAdded = true; - } + } else if (mTokenizer[mIndex]->text==';') { + break; + } else if (mTokenizer[mIndex]->text=='(' + && mTokenizer[mIndex]->matchIndex+1matchIndex+1]->text=='(') { + //function pointer + QString cmd=mTokenizer[mIndex]->text; + int argStart=mTokenizer[mIndex]->matchIndex+1; + int argEnd=mTokenizer[argStart]->matchIndex; + if (cmd.startsWith('*')) + cmd=cmd.mid(1); + if (!cmd.isEmpty()) { + addChildStatement( + getCurrentScope(), + mCurrentFile, + lastType, + cmd, + mergeArgs(argStart,argEnd), + "", + "", + mTokenizer[mIndex]->line, + StatementKind::skVariable, + getScope(), + mClassScope, + //True, + !isExtern, + isStatic); // TODO: not supported to pass list + varAdded = true; + tempType=""; } - - // Step over the variable name - if (isblockChar(mTokenizer[mIndex]->text.front())) { - break; + mIndex=argEnd+1; + } else if (isWordChar(mTokenizer[mIndex]->text[0])) { + QString cmd=mTokenizer[mIndex]->text; + while (cmd.startsWith('*')) { + cmd=cmd.mid(1); + } + if (cmd=="const") { + tempType=mTokenizer[mIndex]->text; + } else { + QString suffix; + QString args; + cmd=mTokenizer[mIndex]->text; + parseCommandTypeAndArgs(cmd,suffix,args); + if (!cmd.isEmpty()) { + addChildStatement( + getCurrentScope(), + mCurrentFile, + (lastType+' '+tempType+suffix).trimmed(), + cmd, + args, + "", + "", + mTokenizer[mIndex]->line, + StatementKind::skVariable, + getScope(), + mClassScope, + //True, + !isExtern, + isStatic); // TODO: not supported to pass list + varAdded = true; + tempType=""; + } } mIndex++; + } else if (mTokenizer[mIndex]->text=='(') { + mIndex=mTokenizer[mIndex]->matchIndex+1; + } else if (mTokenizer[mIndex]->text=='=') { + mIndex = skipAssignment(mIndex, mTokenizer.tokenCount()); + } else if (mTokenizer[mIndex]->text=='{') { + tempType=""; + mIndex=mTokenizer[mIndex]->matchIndex+1; + } else { + tempType=""; + mIndex++; } - if (mIndex >= mTokenizer.tokenCount()) - break; - if (isblockChar(mTokenizer[mIndex]->text.front())) - break; } - if (varAdded && (mIndex < mTokenizer.tokenCount()) - && (mTokenizer[mIndex]->text == '{')) { - // skip { } like A x {new A}; - int i=skipBraces(mIndex); - if (i!=mIndex) - mIndex = i+1; - } - // Skip ; and , - if ( (mIndex < mTokenizer.tokenCount()) && - (mTokenizer[mIndex]->text.front() == ';' - || mTokenizer[mIndex]->text.front() == ',')) - mIndex++; + // Skip ; + mIndex++; } void CppParser::internalParse(const QString &fileName) @@ -3318,7 +3699,7 @@ void CppParser::internalParse(const QString &fileName) QStringList preprocessResult = mPreprocessor.result(); #ifdef QT_DEBUG -// stringsToFile(mPreprocessor.result(),QString("r:\\preprocess-%1.txt").arg(extractFileName(fileName))); + stringsToFile(mPreprocessor.result(),QString("r:\\preprocess-%1.txt").arg(extractFileName(fileName))); // mPreprocessor.dumpDefinesTo("r:\\defines.txt"); // mPreprocessor.dumpIncludesListTo("r:\\includes.txt"); #endif @@ -3331,16 +3712,17 @@ void CppParser::internalParse(const QString &fileName) preprocessResult.clear(); if (mTokenizer.tokenCount() == 0) return; - +#ifdef QT_DEBUG + mTokenizer.dumpTokens(QString("r:\\tokens-%1.txt").arg(extractFileName(fileName))); +#endif // Process the token list while(true) { if (!handleStatement()) break; } #ifdef QT_DEBUG -// mTokenizer.dumpTokens(QString("r:\\tokens-%1.txt").arg(extractFileName(fileName))); -// -// mStatementList.dumpAll("r:\\all-stats.txt"); + mStatementList.dumpAll(QString("r:\\all-stats-%1.txt").arg(extractFileName(fileName))); + mStatementList.dump(QString("r:\\stats-%1.txt").arg(extractFileName(fileName))); #endif //reduce memory usage internalClear(); @@ -3352,42 +3734,36 @@ void CppParser::inheritClassStatement(const PStatement& derived, bool isStruct, const PStatement& base, StatementClassScope access) { //differentiate class and struct - if (access == StatementClassScope::scsNone) { + if (access == StatementClassScope::None) { if (isStruct) - access = StatementClassScope::scsPublic; + access = StatementClassScope::Public; else - access = StatementClassScope::scsPrivate; + access = StatementClassScope::Private; } foreach (const PStatement& statement, base->children) { - if (statement->classScope == StatementClassScope::scsPrivate + if (statement->classScope == StatementClassScope::Private || statement->kind == StatementKind::skConstructor || statement->kind == StatementKind::skDestructor) continue; StatementClassScope m_acc; switch(access) { - case StatementClassScope::scsPublic: + case StatementClassScope::Public: m_acc = statement->classScope; break; - case StatementClassScope::scsProtected: - m_acc = StatementClassScope::scsProtected; + case StatementClassScope::Protected: + m_acc = StatementClassScope::Protected; break; - case StatementClassScope::scsPrivate: - m_acc = StatementClassScope::scsPrivate; + case StatementClassScope::Private: + m_acc = StatementClassScope::Private; break; default: - m_acc = StatementClassScope::scsPrivate; + m_acc = StatementClassScope::Private; } //inherit addInheritedStatement(derived,statement,m_acc); } } -QString CppParser::expandMacroType(const QString &name) -{ - //its done in the preprocessor - return name; -} - void CppParser::fillListOfFunctions(const QString& fileName, int line, const PStatement& statement, const PStatement& scopeStatement, QStringList &list) @@ -4361,87 +4737,92 @@ int CppParser::calcKeyLenForStruct(const QString &word) return -1; } -void CppParser::scanMethodArgs(const PStatement& functionStatement, const QString &argStr) +void CppParser::scanMethodArgs(const PStatement& functionStatement, int argStart) { - // Split up argument string by , - int i = 1; // assume it starts with ( and ends with ) - int paramStart = i; - - QString args; - while (i < argStr.length()) { - if ((argStr[i] == ',') || - ((i == argStr.length()-1) && (argStr[i] == ')'))) { - // We've found "int* a" for example - QString s = argStr.mid(paramStart,i-paramStart); - - //remove default value - int assignPos = s.indexOf('='); - if (assignPos >= 0) { - s.truncate(assignPos); - s = s.trimmed(); - } - // we don't support function pointer parameters now, till we can tokenize function parameters -// { -// // Can be a function pointer. If so, scan after last ) -// BracePos := LastPos(')', S); -// if (BracePos > 0) then // it's a function pointer... begin -// SpacePos := LastPos(' ', Copy(S, BracePos, MaxInt)) // start search at brace -// end else begin -// } - //skip [] - int varEndPos = s.length()-1; - int bracketLevel = 0; - while (varEndPos>=0) { - switch(s[varEndPos].unicode()) { - case ']': - bracketLevel++; - break; - case '[': - bracketLevel--; - varEndPos--; - break; - } - if (bracketLevel==0) - break; - varEndPos--; - } - int varStartPos = varEndPos; - if (varEndPos>=0) { - while (varStartPos-1>=0) { - if (!mTokenizer.isIdentChar(s[varStartPos-1])) - break; - varStartPos--; - } - } - if (varStartPos>=0) { - if (varEndPos+1text=='('); + int argEnd=mTokenizer[argStart]->matchIndex; + int paramStart = argStart+1; + int i = paramStart ; // assume it starts with ( and ends with ) + // Keep going and stop on top of the variable name + QString varType = ""; + while (i < argEnd) { + if (mTokenizer[i]->text=='(' + && mTokenizer[i]->matchIndex+1matchIndex+1]->text=='(') { + //function pointer + int argStart=mTokenizer[i]->matchIndex+1; + int argEnd=mTokenizer[argStart]->matchIndex; + QString cmd=mTokenizer[i+1]->text; + if (cmd.startsWith('*')) + cmd=cmd.mid(1); + QString args=mergeArgs(argStart,argEnd); + if (!cmd.isEmpty()) { addStatement( functionStatement, mCurrentFile, - s.mid(0,varStartPos), // 'int*' - s.mid(varStartPos,varEndPos-varStartPos+1), // a + varType, // 'int*' + cmd, // a args, "", - functionStatement->definitionLine, + "", + mTokenizer[i+1]->line, StatementKind::skParameter, - StatementScope::ssLocal, - StatementClassScope::scsNone, + StatementScope::Local, + StatementClassScope::None, true, false); } - if (varStartPos= 0) { - args = s.mid(bracketPos); - s.truncate(bracketPos); + i=argEnd+1; + varType=""; + } else if (mTokenizer[i]->text=='{') { + i=mTokenizer[i]->matchIndex+1; + } else if (mTokenizer[i]->text=='(') { + i=mTokenizer[i]->matchIndex+1; + } else if (mTokenizer[i]->text=='=') { + i=skipAssignment(i,argEnd); + } else if (isWordChar(mTokenizer[i]->text[0])) { + QString cmd=mTokenizer[i]->text; + if (i+1==argEnd || mTokenizer[i+1]->text==',' || mTokenizer[i+1]->text=='=') { + bool noCmd=false; + if (!cmd.startsWith('*') + && !cmd.startsWith('&') + && !cmd.endsWith(']')) { + PStatement statement=findStatementOf(mCurrentFile,cmd,functionStatement,true); + noCmd = (statement && isTypeStatement(statement->kind)); + if (!noCmd) { + QString args,suffix; + parseCommandTypeAndArgs(cmd,suffix,args); + if (!cmd.isEmpty()) { + addStatement( + functionStatement, + mCurrentFile, + varType+suffix, // 'int*' + cmd, // a + args, + "", + "", + mTokenizer[i]->line, + StatementKind::skParameter, + StatementScope::Local, + StatementClassScope::None, + true, + false); + } + } } + } else { + if (!varType.isEmpty()) + varType+=' '; + varType+=cmd; } - paramStart = i + 1; // step over , + i++; + } else { + i++; + varType=""; } - i++; } + + } QString CppParser::splitPhrase(const QString &phrase, QString &sClazz, @@ -4512,160 +4893,128 @@ QString CppParser::splitPhrase(const QString &phrase, QString &sClazz, return result; } +QString CppParser::removeTemplateParams(const QString &phrase) +{ + int pos = phrase.indexOf('<'); + if (pos>=0) { + return phrase.left(pos); + } + return phrase; +} + +bool CppParser::splitLastMember(const QString &token, QString &lastMember, QString &remaining) +{ + int pos = token.length()-1; + int level=0; + bool found=false; + while (pos>=0 && !found) { + switch(token[pos].unicode()) { + case ']': + case '>': + case ')': + level++; + break; + case '[': + case '<': + case '(': + level--; + break; + case ':': + if (level==0 && pos>0 && token[pos-1]==':') { + found=true; + break; + } + } + pos--; + } + if (pos<0) + return false; + lastMember=token.mid(pos+2); + remaining=token.left(pos); + return true; +} + static bool isIdentChar(const QChar& ch) { return ch.isLetter() || ch == '_' || ch.isDigit(); } -static void appendArgWord(QString& args, const QString& word) { - QString s=word.trimmed(); - if (s.isEmpty()) - return; - if (args.isEmpty()) - args.append(s); - else if (isIdentChar(args.back()) && isIdentChar(word.front()) ) { - args+=" "; - args+=s; - } else { - args+=s; - } -} -QString CppParser::removeArgNames(const QString &args) +//static void appendArgWord(QString& args, const QString& word) { +// QString s=word.trimmed(); +// if (s.isEmpty()) +// return; +// if (args.isEmpty()) +// args.append(s); +// else if (isIdentChar(args.back()) && isIdentChar(word.front()) ) { +// args+=" "; +// args+=s; +// } else { +// args+=s; +// } +//} + +bool CppParser::isNotFuncArgs(int startIndex) { - QString result = ""; - int argsLen = args.length(); - if (argsLen < 2) - return ""; - int i=1; // skip start '(' - QString currentArg; - QString word; - int brackLevel = 0; - bool typeGetted = false; - while (i0) { - word+=args[i]; + Q_ASSERT(mTokenizer[startIndex]->text=='('); + int endIndex=mTokenizer[startIndex]->matchIndex; + //no args, it must be a function + if (endIndex-startIndex==1) + return false; + int i=startIndex+1; //skip '(' + int endPos = endIndex; + QString word = ""; + while (itext[0]; + switch(ch.unicode()) { + // args contains a string/char, can't be a func define + case '"': + case '\'': + case '+': + case '-': + case '/': + case '|': + case '!': + case '{': + return true; + case '[': // function args like int f[10] + i=mTokenizer[i]->matchIndex+1; + if (itext=='(' + || mTokenizer[i]->text=='{')) //lambda + return true; + continue; + } + if (isDigitChar(ch)) + return true; + if (isIdentChar(ch)) { + QString currentText=mTokenizer[i]->text; + if (mTokenizer[i]->text.endsWith('.')) + return true; + if (mTokenizer[i]->text.endsWith("->")) + return true; + if (!mCppTypeKeywords.contains(currentText)) { + if (currentText=="true" || currentText=="false" || currentText=="nullptr" || + currentText=="this") + return true; + if (currentText=="const") + return false; + + if (isCppKeyword(currentText)) + return false; + + PStatement statement =findStatementOf(mCurrentFile,word,getCurrentScope(),true); + if (statement && isTypeStatement(statement->kind)) + return false; } else { - if (!typeGetted) { - appendArgWord(currentArg,word); - } else { - if (isCppKeyword(word)) { - appendArgWord(currentArg,word); - } - } - word = ""; - result += currentArg.trimmed() + ','; - currentArg = ""; - typeGetted = false; - } - break; - case '<': - case '[': - case '(': - brackLevel++; - word+=args[i]; - break; - case '>': - case ']': - case ')': - brackLevel--; - word+=args[i]; - break; - case ' ': - case '\t': - if ((brackLevel >0) && !isSpaceChar(args[i-1])) { - word+=args[i]; - } else if (!word.isEmpty()) { - if (!typeGetted) { - appendArgWord(currentArg,word); - if (mCppTypeKeywords.contains(word) || !isCppKeyword(word)) - typeGetted = true; - } else { - if (isCppKeyword(word)) - appendArgWord(currentArg,word); - } - word = ""; - } - break; - case '&': - case '*': - if (!word.isEmpty()) { - if (!typeGetted) { - appendArgWord(currentArg,word); - if (mCppTypeKeywords.contains(word) || !isCppKeyword(word)) - typeGetted = true; - } else { - if (isCppKeyword(word)) - appendArgWord(currentArg,word); - } - word = ""; - } - currentArg+=args[i]; - break; - default: - if (isIdentChar(args[i])) { - word+=args[i]; + return false; } + } i++; } - if (!typeGetted) { - appendArgWord(currentArg,word); - } else { - if (isCppKeyword(word)) { - appendArgWord(currentArg,word); - } - } - result += currentArg.trimmed(); - return result; -} - -bool CppParser::isNotFuncArgs(const QString &args) -{ - int i=1; //skip '(' - int endPos = args.length()-1;//skip ')' - bool lastCharIsId=false; - QString word = ""; - while (iendPos && word.isEmpty()) { - return false; - } - if (isCppKeyword(word)) { - return word == "true" || word == "false" || word == "nullptr"; - } - PStatement statement =findStatementOf(mCurrentFile,word,getCurrentScope(),true); - if (statement && - !isTypeStatement(statement->kind)) - return true; return false; } @@ -4699,6 +5048,224 @@ void CppParser::updateSerialId() mSerialId = QString("%1 %2").arg(mParserId).arg(mSerialCount); } +int CppParser::indexOfNextSemicolon(int index, int endIndex) +{ + if (endIndex<0) + endIndex=mTokenizer.tokenCount(); + while (indextext[0].unicode()) { + case ';': + return index; + case '(': + index = mTokenizer[index]->matchIndex+1; + break; + default: + index++; + } + } + return index; +} + +int CppParser::indexOfNextSemicolonOrLeftBrace(int index) +{ + while (indextext[0].unicode()) { + case ';': + case '{': + return index; + case '(': + index = mTokenizer[index]->matchIndex+1; + break; + default: + index++; + } + } + return index; +} + +int CppParser::indexOfNextColon(int index) +{ + while (indextext[0].unicode()) { + case ':': + return index; + case '(': + index = mTokenizer[index]->matchIndex+1; + break; + default: + index++; + } + } + return index; +} + +int CppParser::indexOfNextLeftBrace(int index) +{ + while (indextext[0].unicode()) { + case '{': + return index; + case '(': + index = mTokenizer[index]->matchIndex+1; + break; + default: + index++; + } + } + return index; +} + +int CppParser::indexPassParenthesis(int index) +{ + while (indextext=='(') { + return mTokenizer[index]->matchIndex+1; + } + index++; + } + return index; +} + +int CppParser::indexPassBraces(int index) +{ + while (indextext[0].unicode()) { + case '{': + return mTokenizer[index]->matchIndex+1; + case '(': + index = mTokenizer[index]->matchIndex+1; + break; + default: + index++; + } + } + return index; +} + +void CppParser::skipNextSemicolon(int index) +{ + mIndex=index; + while (mIndextext[0].unicode()) { + case ';': + mIndex++; + return; + case '{': + mIndex = mTokenizer[mIndex]->matchIndex+1; + break; + case '(': + mIndex = mTokenizer[mIndex]->matchIndex+1; + break; + default: + mIndex++; + } + } +} + +int CppParser::moveToNextBraceOrSkipNextSemicolon(int index, bool checkLambda, int endIndex) +{ + int startIndex=index; + if (endIndex<0) + endIndex=mTokenizer.tokenCount(); + bool stop=false; + while (indextext[0].unicode()) { + case ';': + index++; + stop=true; + break; + case '{': + stop=true; + break; + case '(': + index = mTokenizer[index]->matchIndex+1; + break; + default: + index++; + } + } + if (stop && checkLambda) { + while (mTokenizer.lambdasCount()>0 && mTokenizer.indexOfFirstLambda()=startIndex) { + handleLambda(i,index); + } + } + } + return index; +} + +void CppParser::skipParenthesis(int index) +{ + mIndex=index; + while (mIndextext=='(') { + mIndex=mTokenizer[mIndex]->matchIndex+1; + return; + } + mIndex++; + } +} + +int CppParser::skipAssignment(int index, int endIndex) +{ + int startIndex=index; + bool stop=false; + while (indextext[0].unicode()) { + case ';': + case ',': + case '{': + stop=true; + break; + case '(': + index = mTokenizer[index]->matchIndex+1; + break; + default: + index++; + } + } + if (stop) { + while (mTokenizer.lambdasCount()>0 && mTokenizer.indexOfFirstLambda()=startIndex) { + handleLambda(i,index); + } + } + } + return (indexstartIndex) + result+=' '; + result+=mTokenizer[i]->text; + } + return result; +} + +void CppParser::parseCommandTypeAndArgs(QString &command, QString &typeSuffix, QString &args) +{ + typeSuffix=""; + while (command.startsWith('*') || command.startsWith('&')) { + typeSuffix=command.front(); + command=command.mid(1); + } + int pos=command.indexOf('['); + if (pos>=0) { + args=command.mid(pos); + command=command.left(pos); + } else { + args=""; + } + +} + ParserLanguage CppParser::language() const { return mLanguage; diff --git a/RedPandaIDE/parser/cppparser.h b/RedPandaIDE/parser/cppparser.h index 568745bd..6eb1415d 100644 --- a/RedPandaIDE/parser/cppparser.h +++ b/RedPandaIDE/parser/cppparser.h @@ -160,6 +160,7 @@ private: const QString& aType, // "Type" is already in use const QString& command, const QString& args, + const QString& noNameArgs, const QString& value, int line, StatementKind kind, @@ -173,6 +174,21 @@ private: const QString &aType, // "Type" is already in use const QString &command, const QString &args, + const QString &noNameArgs, + const QString& value, + int line, + StatementKind kind, + const StatementScope& scope, + const StatementClassScope& classScope, + bool isDefinition, + bool isStatic); + PStatement addStatement( + const PStatement& parent, + const QString &fileName, + const QString &aType, // "Type" is already in use + const QString &command, + int argStart, + int argEnd, const QString& value, int line, StatementKind kind, @@ -182,32 +198,30 @@ private: bool isStatic); void setInheritance(int index, const PStatement& classStatement, bool isStruct); bool isCurrentScope(const QString& command); - void addSoloScopeLevel(PStatement& statement, int line, bool shouldResetBlock = true); // adds new solo level + void addSoloScopeLevel(PStatement& statement, int line, bool shouldResetBlock=false); // adds new solo level void removeScopeLevel(int line); // removes level - int skipBraces(int startAt); - int skipBracket(int startAt); + + int indexOfMatchingBrace(int startAt) { + return mTokenizer[startAt]->matchIndex; + } void internalClear(); QStringList sortFilesByIncludeRelations(const QSet &files); - bool checkForCatchBlock(); - bool checkForEnum(); - bool checkForForBlock(); - bool checkForKeyword(); - bool checkForMethod(QString &sType, QString &sName, QString &sArgs, - bool &isStatic, bool &isFriend); // caching of results - bool checkForNamespace(); + bool checkForKeyword(KeywordType &keywordType); + bool checkForMethod(QString &sType, QString &sName, int &argStartIndex, + int &argEndIndex, bool &isStatic, bool &isFriend); // caching of results + bool checkForNamespace(KeywordType keywordType); bool checkForPreprocessor(); - bool checkForScope(); - void checkForSkipStatement(); - bool checkForStructs(); - bool checkForTypedef(); +// bool checkForLambda(); + bool checkForScope(KeywordType keywordType); + bool checkForStructs(KeywordType keywordType); bool checkForTypedefEnum(); bool checkForTypedefStruct(); - bool checkForUsing(); - bool checkForVar(); - QString expandMacroType(const QString& name); + bool checkForUsing(KeywordType keywordType); + + void checkAndHandleMethodOrVar(KeywordType keywordType); void fillListOfFunctions(const QString& fileName, int line, const PStatement& statement, @@ -308,10 +322,23 @@ private: void doSkipInExpression(const QStringList& expression, int&pos, const QString& startSymbol, const QString& endSymbol); bool isIdentifier(const QString& token) const { - return (!token.isEmpty() && isLetterChar(token.front()) + return (!token.isEmpty() && isIdentChar(token.front()) && !token.contains('\"')); } + bool isIdentifierOrPointer(const QString& term) const { + switch(term[0].unicode()) { + case '*': + return true; + case '\"': + case '\'': + return false; + default: + return isIdentChar(term[0]); + } + } + + bool isIntegerLiteral(const QString& token) const { if (token.isEmpty()) return false; @@ -335,6 +362,24 @@ private: return false; return (token.startsWith('\'')); } + + bool isKeyword(const QString& token) const { + return mCppKeywords.contains(token); + } + + bool tokenIsIdentifier(const QString& token) const { + //token won't be empty + return isIdentChar(token[0]); + } + + bool tokenIsTypeOrNonKeyword(const QString& token) const { + return tokenIsIdentifier(token) && + (mCppTypeKeywords.contains(token) + || !mCppKeywords.contains(token) + || token=="const"); + + } + PStatement doParseEvalTypeInfo( const QString& fileName, const PStatement& scope, @@ -343,7 +388,8 @@ private: int& pointerLevel); int getBracketEnd(const QString& s, int startAt); - StatementClassScope getClassScope(int index); + StatementClassScope getClassScope(const QString& text); + StatementClassScope getClassScope(KeywordType keywordType); int getCurrentBlockBeginSkip(); int getCurrentBlockEndSkip(); int getCurrentInlineNamespaceEndSkip(); @@ -370,23 +416,25 @@ private: PStatement getTypeDef(const PStatement& statement, const QString& fileName, const QString& aType); void handleCatchBlock(); - void handleEnum(); + void handleEnum(bool isTypedef); void handleForBlock(); - void handleKeyword(); + void handleKeyword(KeywordType skipType); + void handleLambda(int index, int endIndex); void handleMethod( + StatementKind functionKind, const QString& sType, const QString& sName, - const QString& sArgs, + int argStart, bool isStatic, bool isFriend); - void handleNamespace(); + void handleNamespace(KeywordType skipType); void handleOtherTypedefs(); void handlePreprocessor(); - void handleScope(); + void handleScope(KeywordType keywordType); bool handleStatement(); void handleStructs(bool isTypedef = false); void handleUsing(); - void handleVar(); + void handleVar(const QString& typePrefix,bool isExtern,bool isStatic); void internalParse(const QString& fileName); // function FindMacroDefine(const Command: AnsiString): PStatement; void inheritClassStatement( @@ -410,11 +458,12 @@ private: // } void scanMethodArgs( const PStatement& functionStatement, - const QString& argStr); + int argStart); QString splitPhrase(const QString& phrase, QString& sClazz, QString& sOperator, QString &sMember); + QString removeTemplateParams(const QString& phrase); - QString removeArgNames(const QString& args); + bool splitLastMember(const QString& token, QString& lastMember, QString& remaining); bool isSpaceChar(const QChar& ch) const { return ch==' ' || ch =='\t'; @@ -427,7 +476,14 @@ private: || ch == '&'; } - bool isLetterChar(const QChar& ch) const { + bool isIdentifier(const QChar& ch) const { + return ch.isLetter() + || ch == '_' + || ch == '~' + ; + } + + bool isIdentChar(const QChar& ch) const { return ch.isLetter() || ch == '_'; } @@ -436,6 +492,24 @@ private: return (ch>='0' && ch<='9'); } + bool isInvalidFunctionArgsSuffixChar(const QChar& ch) const { + + // && + switch(ch.unicode()){ + case '.': + case '-': + case '+': + case '/': + case '%': + case '*': + case '|': + case '?': + return true; + default: + return false; + } + } + /*'(', ';', ':', '{', '}', '#' */ bool isSeperator(const QChar& ch) const { switch(ch.unicode()){ @@ -493,7 +567,7 @@ private: return ch=='\n' || ch=='\r'; } - bool isNotFuncArgs(const QString& args); + bool isNotFuncArgs(int startIndex); /** * @brief Test if a statement is a class/struct/union/namespace/function @@ -511,7 +585,20 @@ private: void updateSerialId(); - + int indexOfNextSemicolon(int index, int endIndex=-1); + int indexOfNextSemicolonOrLeftBrace(int index); + int indexOfNextColon(int index); + int indexOfNextLeftBrace(int index); + int indexPassParenthesis(int index); + int indexPassBraces(int index); + int skipAssignment(int index, int endIndex); + void skipNextSemicolon(int index); + int moveToNextBraceOrSkipNextSemicolon(int index, bool checkLambda, int endIndex=-1); + void skipParenthesis(int index); + QString mergeArgs(int startIndex, int endIndex); + void parseCommandTypeAndArgs(QString& command, + QString& typeSuffix, + QString& args); private: int mParserId; ParserLanguage mLanguage; @@ -528,9 +615,6 @@ private: QVector mCurrentScope; QVector mCurrentClassScope; -// the start index in tokens to skip to ; when parsing typedef struct we need to skip -// the names after the closing bracket because we have processed it - QVector mSkipList; // TList StatementClassScope mClassScope; StatementModel mStatementList; //It's used in preprocessor, so we can't use fIncludeList instead @@ -554,7 +638,7 @@ private: QMutex mMutex; GetFileStreamCallBack mOnGetFileStream; - QMap mCppKeywords; + QMap mCppKeywords; QSet mCppTypeKeywords; }; using PCppParser = std::shared_ptr; diff --git a/RedPandaIDE/parser/cpptokenizer.cpp b/RedPandaIDE/parser/cpptokenizer.cpp index 39c6262a..477a568d 100644 --- a/RedPandaIDE/parser/cpptokenizer.cpp +++ b/RedPandaIDE/parser/cpptokenizer.cpp @@ -18,6 +18,7 @@ #include #include +#include CppTokenizer::CppTokenizer() { @@ -30,6 +31,10 @@ void CppTokenizer::clear() mBuffer.clear(); mBufferStr.clear(); mLastToken.clear(); + mUnmatchedBraces.clear(); + mUnmatchedBrackets.clear(); + mUnmatchedParenthesis.clear(); + mLambdas.clear(); } void CppTokenizer::tokenize(const QStringList &buffer) @@ -48,16 +53,26 @@ void CppTokenizer::tokenize(const QStringList &buffer) mCurrent = mStart; mLineCount = mStart; QString s = ""; - bool bSkipBlocks = false; mCurrentLine = 1; + + TokenType tokenType; while (true) { mLastToken = s; - s = getNextToken(true, true, bSkipBlocks); + s = getNextToken(&tokenType, true, false); simplify(s); if (s.isEmpty()) break; else - addToken(s,mCurrentLine); + addToken(s,mCurrentLine,tokenType); + } + while (!mUnmatchedBraces.isEmpty()) { + addToken("}",mCurrentLine,TokenType::RightBrace); + } + while (!mUnmatchedBrackets.isEmpty()) { + addToken("]",mCurrentLine,TokenType::RightBracket); + } + while (!mUnmatchedParenthesis.isEmpty()) { + addToken(")",mCurrentLine,TokenType::RightParenthesis); } } @@ -68,7 +83,7 @@ void CppTokenizer::dumpTokens(const QString &fileName) if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { QTextStream stream(&file); foreach (const PToken& token,mTokenList) { - stream<line).arg(token->text) + stream<line).arg(token->text).arg(token->matchIndex) #if QT_VERSION >= QT_VERSION_CHECK(5,15,0) <(); token->text = sText; token->line = iLine; +#ifdef Q_DEBUG + token->matchIndex = 1000000000; +#endif + switch(tokenType) { + case TokenType::LeftBrace: + token->matchIndex=-1; + mUnmatchedBraces.push_back(mTokenList.count()); + break; + case TokenType::RightBrace: + if (mUnmatchedBraces.isEmpty()) { + token->matchIndex=-1; + } else { + token->matchIndex = mUnmatchedBraces.last(); + mTokenList[token->matchIndex]->matchIndex=mTokenList.count(); + mUnmatchedBraces.pop_back(); + } + break; + case TokenType::LeftBracket: + token->matchIndex=-1; + mUnmatchedBrackets.push_back(mTokenList.count()); + break; + case TokenType::RightBracket: + if (mUnmatchedBrackets.isEmpty()) { + token->matchIndex=-1; + } else { + token->matchIndex = mUnmatchedBrackets.last(); + mTokenList[token->matchIndex]->matchIndex=mTokenList.count(); + mUnmatchedBrackets.pop_back(); + } + break; + case TokenType::LeftParenthesis: + token->matchIndex=-1; + mUnmatchedParenthesis.push_back(mTokenList.count()); + break; + case TokenType::RightParenthesis: + if (mUnmatchedParenthesis.isEmpty()) { + token->matchIndex=-1; + } else { + token->matchIndex = mUnmatchedParenthesis.last(); + mTokenList[token->matchIndex]->matchIndex=mTokenList.count(); + mUnmatchedParenthesis.pop_back(); + } + break; + case TokenType::LambdaCaptures: + mLambdas.push_back(mTokenList.count()); + default: + break; + } mTokenList.append(token); } @@ -110,28 +173,6 @@ void CppTokenizer::countLines() } } -QString CppTokenizer::getArguments() -{ - QChar* offset = mCurrent; - skipPair('(', ')'); - QString result(offset,mCurrent-offset); - simplifyArgs(result); - if ((*mCurrent == '.') || ((*mCurrent == '-') && (*(mCurrent + 1) == '>'))) { - // skip '.' and '->' - while ( !( *mCurrent == 0 - || *mCurrent == '(' - || *mCurrent == ';' - || *mCurrent == '{' - || *mCurrent == '}' - || *mCurrent == ')' - || isLineChar(*mCurrent) - || isSpaceChar(*mCurrent)) ) - mCurrent++; - } - skipToNextToken(); - return result; -} - QString CppTokenizer::getForInit() { QChar* startOffset = mCurrent; @@ -139,12 +180,13 @@ QString CppTokenizer::getForInit() // Step into the init statement mCurrent++; + TokenType tokenType; // Process until ; or end of file while (true) { - QString s = getNextToken(true, true, false); + QString s = getNextToken(&tokenType, true, false); simplify(s); if (!s.isEmpty()) - addToken(s,mCurrentLine); + addToken(s,mCurrentLine,tokenType); if ( (s == "") || (s == ";") || (s==":")) break; // : is used in for-each loop @@ -156,10 +198,12 @@ QString CppTokenizer::getForInit() return ""; } -QString CppTokenizer::getNextToken(bool /* bSkipParenthesis */, bool bSkipArray, bool bSkipBlock) +QString CppTokenizer::getNextToken(TokenType *pTokenType, bool bSkipArray, bool bSkipBlock) { QString result; + int backupIndex; bool done = false; + *pTokenType=TokenType::Normal; while (true) { skipToNextToken(); if (*mCurrent == 0) @@ -179,13 +223,18 @@ QString CppTokenizer::getNextToken(bool /* bSkipParenthesis */, bool bSkipArray, countLines(); result = getForInit(); done = (result != ""); - } else if (isArguments()) { - countLines(); - result = getArguments(); - done = (result != ""); +// } else if (isArguments()) { +// countLines(); +// result = getArguments(); +// done = (result != ""); } else if (isWord()) { countLines(); result = getWord(false, bSkipArray, bSkipBlock); +// if (result=="noexcept" || result == "throw") { +// result=""; +// if (*mCurrent=='(') +// skipPair('(',')'); +// } done = (result != ""); } else if (isNumber()) { countLines(); @@ -196,49 +245,119 @@ QString CppTokenizer::getNextToken(bool /* bSkipParenthesis */, bool bSkipArray, case 0: done = true; break; - case '/': - advance(); - break; case ':': if (*(mCurrent + 1) == ':') { countLines(); mCurrent+=2; + result = "::"; + skipToNextToken(); // Append next token to this one - result = "::"+getWord(true, bSkipArray, bSkipBlock); + if (isIdentChar(*mCurrent)) + result+=getWord(true, bSkipArray, bSkipBlock); done = true; } else { countLines(); result = *mCurrent; - advance(); + mCurrent++; done = true; } break; case '{': + *pTokenType=TokenType::LeftBrace; + countLines(); + result = *mCurrent; + mCurrent++; + done = true; + break; case '}': + *pTokenType=TokenType::RightBrace; + countLines(); + result = *mCurrent; + mCurrent++; + done = true; + break; + case '(': + *pTokenType=TokenType::LeftParenthesis; + countLines(); + result = *mCurrent; + mCurrent++; + done = true; + break; + case '[': + if (*(mCurrent+1)!='[') { + *pTokenType=TokenType::LambdaCaptures; + countLines(); + QChar* backup=mCurrent; + skipPair('[',']'); + result = QString(backup,mCurrent-backup); + done = true; + } else { + skipPair('[',']'); // attribute, skipit + } + break; + case ')': + *pTokenType=TokenType::RightParenthesis; + countLines(); + result = *mCurrent; + mCurrent++; + done = true; + break; case ';': case ',': //just return the brace or the ';' countLines(); result = *mCurrent; - advance(); + mCurrent++; done = true; break; case '>': // keep stream operators if (*(mCurrent + 1) == '>') { countLines(); result = ">>"; - advance(); + mCurrent+=2; done = true; } else - advance(); + mCurrent+=1; break; case '<': if (*(mCurrent + 1) == '<') { countLines(); result = "<<"; - advance(); + mCurrent+=2; done = true; } else - advance(); + mCurrent+=1; + break; + case '=': { + if (*(mCurrent+1)=='=') { + // skip '==' + skipAssignment(); + } else { + countLines(); + mCurrent+=1; + result = "="; + done = true; + } + break; + } + break; + case '!': + if (*(mCurrent+1)=='=') { + skipAssignment(); + } else + mCurrent++; + break; + case '/': + case '%': + case '&': + case '*': + case '|': + case '+': + case '-': + case '~': + if (*(mCurrent + 1) == '=') { + skipAssignment(); + } else + mCurrent++; break; default: advance(); @@ -256,7 +375,8 @@ QString CppTokenizer::getNumber() if (isDigitChar(*mCurrent)) { while (isDigitChar(*mCurrent) || isHexChar(*mCurrent)) { - advance(); + mCurrent++; + //advance(); } } @@ -321,10 +441,11 @@ QString CppTokenizer::getWord(bool bSkipParenthesis, bool bSkipArray, bool bSkip // Skip template contents, but keep template variable types if (*mCurrent == '<') { - offset = mCurrent; //we don't skip - skipTemplateArgs(); + offset = mCurrent; - if (!bFoundTemplate) { + if (bFoundTemplate) { + skipTemplateArgs(); + } else if (skipAngleBracketPair()){ result += QString(offset, mCurrent-offset); skipToNextToken(); } @@ -351,13 +472,16 @@ QString CppTokenizer::getWord(bool bSkipParenthesis, bool bSkipArray, bool bSkip } else if ((*mCurrent == '-') && (*(mCurrent + 1) == '>')) { result+=QString(mCurrent,2); mCurrent+=2; - } else if ((*mCurrent == ':') && (*(mCurrent + 1) == ':')) { + } else if ((*mCurrent == ':') && (*(mCurrent + 1) == ':') ) { if (result != "using") { result+=QString(mCurrent,2); mCurrent+=2; - // Append next token to this one - QString s = getWord(bSkipParenthesis, bSkipArray, bSkipBlock); - result += s; + skipToNextToken(); + if (isIdentChar(*mCurrent)) { + // Append next token to this one + QString s = getWord(bSkipParenthesis, bSkipArray, bSkipBlock); + result += s; + } } } } @@ -480,18 +604,18 @@ void CppTokenizer::skipDoubleQuotes() } } -void CppTokenizer::skipPair(const QChar &cStart, const QChar cEnd, const QSet& failChars) +void CppTokenizer::skipPair(const QChar &cStart, const QChar cEnd) { mCurrent++; while (*mCurrent != 0) { - if ((*mCurrent == '(') && !failChars.contains('(')) { - skipPair('(', ')', failChars); - } else if ((*mCurrent == '[') && !failChars.contains('[')) { - skipPair('[', ']', failChars); - } else if ((*mCurrent == '{') && !failChars.contains('{')) { - skipPair('{', '}', failChars); + if (*mCurrent == '(') { + skipPair('(', ')'); + } else if (*mCurrent == '[') { + skipPair('[', ']'); + } else if (*mCurrent == '{') { + skipPair('{', '}'); } else if (*mCurrent == cStart) { - skipPair(cStart, cEnd, failChars); + skipPair(cStart, cEnd); } else if (*mCurrent == cEnd) { mCurrent++; // skip over end break; @@ -510,14 +634,81 @@ void CppTokenizer::skipPair(const QChar &cStart, const QChar cEnd, const QSet stack; + while (*mCurrent != '\0') { + switch((*mCurrent).unicode()) { + case '<': + case '(': + case '[': + stack.push_back(*mCurrent); + break; + case ')': + while (!stack.isEmpty() && stack.back()!='(') { + stack.pop_back(); + } + //pop up '(' + if (stack.isEmpty()) { + mCurrent=backup; + return false; + } + stack.pop_back(); + break; + case ']': + while (!stack.isEmpty() && stack.back()!='[') + stack.pop_back(); + //pop up '[' + if (stack.isEmpty()) { + mCurrent=backup; + return false; + } + stack.pop_back(); + break; + case '>': + if (stack.back()=='<') + stack.pop_back(); + if (stack.isEmpty()) { + mCurrent++; + return true; + } + break; + case '{': + case '}': + case ';': + case '"': + case '\'': + mCurrent=backup; + return false; + case '-': + if (*(mCurrent+1)=='>') { + mCurrent=backup; + return false; + } + break; + case '.': + if (*(mCurrent+1)!='.') { + mCurrent=backup; + return false; + } + // skip + while (*(mCurrent+1)=='.') + mCurrent++; + break; + } + mCurrent++; + } + mCurrent=backup; + return false; +} + void CppTokenizer::skipRawString() { mCurrent++; //skip R @@ -566,17 +757,8 @@ void CppTokenizer::skipTemplateArgs() { if (*mCurrent != '<') return; - QChar* start = mCurrent; - QSet failSet; - failSet.insert('{'); - failSet.insert('}'); - failSet.insert(';'); - skipPair('<', '>', failSet); - - // if we failed, return to where we came from - if (start!=mCurrent && *(mCurrent - 1) != '>') - mCurrent = start; + skipPair('<', '>'); } void CppTokenizer::skipToEOL() @@ -601,7 +783,7 @@ void CppTokenizer::skipToEOL() void CppTokenizer::skipToNextToken() { while (isSpaceChar(*mCurrent) || isLineChar(*mCurrent)) - advance(); + mCurrent++; } bool CppTokenizer::isIdentChar(const QChar &ch) @@ -609,39 +791,29 @@ bool CppTokenizer::isIdentChar(const QChar &ch) return ch=='_' || ch.isLetter() ; } +int CppTokenizer::lambdasCount() const +{ + return mLambdas.count(); +} + +int CppTokenizer::indexOfFirstLambda() const +{ + return mLambdas.front(); +} + +void CppTokenizer::removeFirstLambda() +{ + mLambdas.pop_front(); +} + void CppTokenizer::advance() { switch(mCurrent->unicode()) { - case '\"': skipDoubleQuotes(); + case '\"': + skipDoubleQuotes(); break; - case '\'': skipSingleQuote(); - break; - case '/': - if (*(mCurrent + 1) == '=') - skipAssignment(); - else - mCurrent++; - break; - case '=': { - if (mTokenList.size()>2 - && mTokenList[mTokenList.size()-2]->text == "using") { - addToken("=",mCurrentLine); - mCurrent++; - } else - skipAssignment(); - break; - } - case '&': - case '*': - case '!': - case '|': - case '+': - case '-': - case '~': - if (*(mCurrent + 1) == '=') - skipAssignment(); - else - mCurrent++; + case '\'': + skipSingleQuote(); break; case '\\': if (isLineChar(*(mCurrent + 1))) @@ -649,11 +821,14 @@ void CppTokenizer::advance() else mCurrent++; break; - default: - if ((*mCurrent == 'R') && (*(mCurrent+1) == '"')) + case 'R': + if (*(mCurrent+1) == '"') skipRawString(); else mCurrent++; + break; + default: + mCurrent++; } } @@ -693,7 +868,7 @@ bool CppTokenizer::isLineChar(const QChar &ch) bool CppTokenizer::isBlankChar(const QChar &ch) { - return (ch<=32); + return (ch<=32) && (ch>0); } bool CppTokenizer::isOperatorChar(const QChar &ch) diff --git a/RedPandaIDE/parser/cpptokenizer.h b/RedPandaIDE/parser/cpptokenizer.h index 56080034..de3d3f5c 100644 --- a/RedPandaIDE/parser/cpptokenizer.h +++ b/RedPandaIDE/parser/cpptokenizer.h @@ -22,10 +22,23 @@ class CppTokenizer { + enum class TokenType { + Normal, + LeftBrace, + RightBrace, + LeftParenthesis, + RightParenthesis, + LeftBracket, + RightBracket, + Assignment, + LambdaCaptures + }; + public: struct Token { QString text; int line; + int matchIndex; }; using PToken = std::shared_ptr; using TokenList = QVector; @@ -38,16 +51,19 @@ public: PToken operator[](int i); int tokenCount(); bool isIdentChar(const QChar& ch); + int lambdasCount() const; + int indexOfFirstLambda() const; + void removeFirstLambda(); + private: - void addToken(const QString& sText, int iLine); + void addToken(const QString& sText, int iLine, TokenType tokenType); void advance(); void countLines(); PToken getToken(int index); - QString getArguments(); QString getForInit(); QString getNextToken( - bool bSkipParenthesis = false, + TokenType *pTokenType, bool bSkipArray = false, bool bSkipBlock = false); QString getNumber(); @@ -65,7 +81,8 @@ private: void simplifyArgs(QString& output); void skipAssignment(); void skipDoubleQuotes(); - void skipPair(const QChar& cStart, const QChar cEnd, const QSet& failChars = QSet()); + void skipPair(const QChar& cStart, const QChar cEnd); + bool skipAngleBracketPair(); void skipRawString(); void skipSingleQuote(); void skipSplitLine(); @@ -92,6 +109,10 @@ private: int mCurrentLine; QString mLastToken; TokenList mTokenList; + QList mLambdas; + QVector mUnmatchedBraces; // stack of indices for unmatched '{' + QVector mUnmatchedBrackets; // stack of indices for unmatched '[' + QVector mUnmatchedParenthesis;// stack of indices for unmatched '(' }; using PCppTokenizer = std::shared_ptr; diff --git a/RedPandaIDE/parser/parserutils.cpp b/RedPandaIDE/parser/parserutils.cpp index 0a31cb6d..fbb2fd98 100644 --- a/RedPandaIDE/parser/parserutils.cpp +++ b/RedPandaIDE/parser/parserutils.cpp @@ -25,7 +25,7 @@ QStringList CppDirectives; QStringList JavadocTags; -QMap CppKeywords; +QMap CppKeywords; QSet CppControlKeyWords; QSet CppTypeKeywords; QSet CKeywords; @@ -55,107 +55,107 @@ void initParser() CppSourceExts->insert("c++"); CppSourceExts->insert("cp"); // skip itself - CppKeywords.insert("and",SkipType::skItself); - CppKeywords.insert("and_eq",SkipType::skItself); - CppKeywords.insert("bitand",SkipType::skItself); - CppKeywords.insert("bitor",SkipType::skItself); - CppKeywords.insert("break",SkipType::skItself); - CppKeywords.insert("compl",SkipType::skItself); - CppKeywords.insert("constexpr",SkipType::skItself); - CppKeywords.insert("const_cast",SkipType::skItself); - CppKeywords.insert("continue",SkipType::skItself); - CppKeywords.insert("dynamic_cast",SkipType::skItself); - CppKeywords.insert("else",SkipType::skItself); - CppKeywords.insert("explicit",SkipType::skItself); - CppKeywords.insert("export",SkipType::skItself); - CppKeywords.insert("false",SkipType::skItself); + CppKeywords.insert("and",KeywordType::SkipItself); + CppKeywords.insert("and_eq",KeywordType::SkipItself); + CppKeywords.insert("bitand",KeywordType::SkipItself); + CppKeywords.insert("bitor",KeywordType::SkipItself); + CppKeywords.insert("break",KeywordType::SkipItself); + CppKeywords.insert("compl",KeywordType::SkipItself); + CppKeywords.insert("constexpr",KeywordType::SkipItself); + CppKeywords.insert("const_cast",KeywordType::SkipItself); + CppKeywords.insert("continue",KeywordType::SkipItself); + CppKeywords.insert("dynamic_cast",KeywordType::SkipItself); + CppKeywords.insert("else",KeywordType::SkipItself); + CppKeywords.insert("explicit",KeywordType::SkipItself); + CppKeywords.insert("export",KeywordType::SkipItself); + CppKeywords.insert("false",KeywordType::SkipItself); //CppKeywords.insert("for",SkipType::skItself); - CppKeywords.insert("mutable",SkipType::skItself); - CppKeywords.insert("noexcept",SkipType::skItself); - CppKeywords.insert("not",SkipType::skItself); - CppKeywords.insert("not_eq",SkipType::skItself); - CppKeywords.insert("nullptr",SkipType::skItself); - CppKeywords.insert("or",SkipType::skItself); - CppKeywords.insert("or_eq",SkipType::skItself); - CppKeywords.insert("register",SkipType::skItself); - CppKeywords.insert("reinterpret_cast",SkipType::skItself); - CppKeywords.insert("static_assert",SkipType::skItself); - CppKeywords.insert("static_cast",SkipType::skItself); - CppKeywords.insert("template",SkipType::skItself); + CppKeywords.insert("mutable",KeywordType::SkipItself); + CppKeywords.insert("noexcept",KeywordType::SkipItself); + CppKeywords.insert("not",KeywordType::SkipItself); + CppKeywords.insert("not_eq",KeywordType::SkipItself); + CppKeywords.insert("nullptr",KeywordType::SkipItself); + CppKeywords.insert("or",KeywordType::SkipItself); + CppKeywords.insert("or_eq",KeywordType::SkipItself); + CppKeywords.insert("register",KeywordType::SkipItself); + CppKeywords.insert("reinterpret_cast",KeywordType::SkipItself); + CppKeywords.insert("static_cast",KeywordType::SkipItself); + CppKeywords.insert("template",KeywordType::SkipItself); //CppKeywords.insert("this",SkipType::skItself); - CppKeywords.insert("thread_local",SkipType::skItself); - CppKeywords.insert("true",SkipType::skItself); - CppKeywords.insert("typename",SkipType::skItself); - CppKeywords.insert("virtual",SkipType::skItself); - CppKeywords.insert("volatile",SkipType::skItself); - CppKeywords.insert("xor",SkipType::skItself); - CppKeywords.insert("xor_eq",SkipType::skItself); + CppKeywords.insert("thread_local",KeywordType::SkipItself); + CppKeywords.insert("true",KeywordType::SkipItself); + CppKeywords.insert("typename",KeywordType::SkipItself); + CppKeywords.insert("virtual",KeywordType::SkipItself); + CppKeywords.insert("volatile",KeywordType::SkipItself); + CppKeywords.insert("xor",KeywordType::SkipItself); + CppKeywords.insert("xor_eq",KeywordType::SkipItself); //CppKeywords.insert("catch",SkipType::skItself); - CppKeywords.insert("do",SkipType::skItself); - CppKeywords.insert("try",SkipType::skItself); + CppKeywords.insert("do",KeywordType::SkipItself); + CppKeywords.insert("try",KeywordType::SkipItself); // Skip to ; - CppKeywords.insert("delete",SkipType::skToSemicolon); - CppKeywords.insert("delete[]",SkipType::skToSemicolon); - CppKeywords.insert("goto",SkipType::skToSemicolon); - CppKeywords.insert("new",SkipType::skToSemicolon); - CppKeywords.insert("return",SkipType::skToSemicolon); - CppKeywords.insert("throw",SkipType::skToSemicolon); + CppKeywords.insert("delete",KeywordType::SkipNextSemicolon); + CppKeywords.insert("delete[]",KeywordType::SkipNextSemicolon); + CppKeywords.insert("goto",KeywordType::SkipNextSemicolon); + CppKeywords.insert("new",KeywordType::SkipNextSemicolon); + CppKeywords.insert("return",KeywordType::SkipNextSemicolon); + CppKeywords.insert("throw",KeywordType::SkipNextSemicolon); // CppKeywords.insert("using",SkipType::skToSemicolon); //won't use it // Skip to : - CppKeywords.insert("case",SkipType::skToColon); - CppKeywords.insert("default",SkipType::skToColon); + CppKeywords.insert("case",KeywordType::SkipNextColon); + CppKeywords.insert("default",KeywordType::SkipNextColon); // Skip to ) - CppKeywords.insert("__attribute__",SkipType::skToRightParenthesis); - CppKeywords.insert("alignas",SkipType::skToRightParenthesis); // not right - CppKeywords.insert("alignof",SkipType::skToRightParenthesis); // not right - CppKeywords.insert("decltype",SkipType::skToRightParenthesis); // not right - CppKeywords.insert("if",SkipType::skToRightParenthesis); - CppKeywords.insert("sizeof",SkipType::skToRightParenthesis); - CppKeywords.insert("switch",SkipType::skToRightParenthesis); - CppKeywords.insert("typeid",SkipType::skToRightParenthesis); - CppKeywords.insert("while",SkipType::skToRightParenthesis); + CppKeywords.insert("__attribute__",KeywordType::SkipNextParenthesis); + CppKeywords.insert("__attribute",KeywordType::SkipNextParenthesis); + CppKeywords.insert("alignas",KeywordType::SkipNextParenthesis); // not right + CppKeywords.insert("alignof",KeywordType::SkipNextParenthesis); // not right + CppKeywords.insert("if",KeywordType::SkipNextParenthesis); + CppKeywords.insert("sizeof",KeywordType::SkipNextParenthesis); + CppKeywords.insert("switch",KeywordType::SkipNextParenthesis); + CppKeywords.insert("typeid",KeywordType::SkipNextParenthesis); + CppKeywords.insert("while",KeywordType::SkipNextParenthesis); + CppKeywords.insert("static_assert",KeywordType::SkipNextParenthesis); // Skip to } - CppKeywords.insert("asm",SkipType::skToRightBrace); - //CppKeywords.insert("namespace",SkipType::skToLeftBrace); // won't process it + CppKeywords.insert("asm",KeywordType::MoveToRightBrace); + CppKeywords.insert("__asm",KeywordType::MoveToRightBrace); // Skip to { // wont handle //Not supported yet - CppKeywords.insert("atomic_cancel",SkipType::skNone); - CppKeywords.insert("atomic_commit",SkipType::skNone); - CppKeywords.insert("atomic_noexcept",SkipType::skNone); - CppKeywords.insert("concept",SkipType::skNone); - CppKeywords.insert("consteval",SkipType::skNone); - CppKeywords.insert("constinit",SkipType::skNone); - CppKeywords.insert("co_wait",SkipType::skNone); - CppKeywords.insert("co_return",SkipType::skNone); - CppKeywords.insert("co_yield",SkipType::skNone); - CppKeywords.insert("reflexpr",SkipType::skNone); - CppKeywords.insert("requires",SkipType::skNone); + CppKeywords.insert("atomic_cancel",KeywordType::None); + CppKeywords.insert("atomic_commit",KeywordType::None); + CppKeywords.insert("atomic_noexcept",KeywordType::None); + CppKeywords.insert("concept",KeywordType::None); + CppKeywords.insert("consteval",KeywordType::None); + CppKeywords.insert("constinit",KeywordType::None); + CppKeywords.insert("co_wait",KeywordType::None); + CppKeywords.insert("co_return",KeywordType::None); + CppKeywords.insert("co_yield",KeywordType::None); + CppKeywords.insert("reflexpr",KeywordType::None); + CppKeywords.insert("requires",KeywordType::None); // its a type - CppKeywords.insert("auto",SkipType::skNone); - CppKeywords.insert("bool",SkipType::skNone); - CppKeywords.insert("char",SkipType::skNone); - CppKeywords.insert("char8_t",SkipType::skNone); - CppKeywords.insert("char16_t",SkipType::skNone); - CppKeywords.insert("char32_t",SkipType::skNone); - CppKeywords.insert("double",SkipType::skNone); - CppKeywords.insert("float",SkipType::skNone); - CppKeywords.insert("int",SkipType::skNone); - CppKeywords.insert("long",SkipType::skNone); - CppKeywords.insert("short",SkipType::skNone); - CppKeywords.insert("signed",SkipType::skNone); - CppKeywords.insert("unsigned",SkipType::skNone); - CppKeywords.insert("void",SkipType::skNone); - CppKeywords.insert("wchar_t",SkipType::skNone); + CppKeywords.insert("auto",KeywordType::None); + CppKeywords.insert("bool",KeywordType::None); + CppKeywords.insert("char",KeywordType::None); + CppKeywords.insert("char8_t",KeywordType::None); + CppKeywords.insert("char16_t",KeywordType::None); + CppKeywords.insert("char32_t",KeywordType::None); + CppKeywords.insert("double",KeywordType::None); + CppKeywords.insert("float",KeywordType::None); + CppKeywords.insert("int",KeywordType::None); + CppKeywords.insert("long",KeywordType::None); + CppKeywords.insert("short",KeywordType::None); + CppKeywords.insert("signed",KeywordType::None); + CppKeywords.insert("unsigned",KeywordType::None); + CppKeywords.insert("void",KeywordType::None); + CppKeywords.insert("wchar_t",KeywordType::None); // type keywords CppTypeKeywords.insert("auto"); @@ -177,33 +177,32 @@ void initParser() CppTypeKeywords.insert("unsigned"); // it's part of type info - CppKeywords.insert("const",SkipType::skNone); - CppKeywords.insert("extern",SkipType::skNone); - CppKeywords.insert("inline",SkipType::skNone); + CppKeywords.insert("const",KeywordType::None); + CppKeywords.insert("extern",KeywordType::None); // handled elsewhere - CppKeywords.insert("class",SkipType::skNone); - CppKeywords.insert("enum",SkipType::skNone); - CppKeywords.insert("friend",SkipType::skNone); - CppKeywords.insert("operator",SkipType::skNone); - CppKeywords.insert("private",SkipType::skNone); - CppKeywords.insert("protected",SkipType::skNone); - CppKeywords.insert("public",SkipType::skNone); - CppKeywords.insert("static",SkipType::skNone); - CppKeywords.insert("struct",SkipType::skNone); - CppKeywords.insert("typedef",SkipType::skNone); - CppKeywords.insert("union",SkipType::skNone); - // namespace - CppKeywords.insert("namespace",SkipType::skNone); - CppKeywords.insert("using",SkipType::skNone); - - CppKeywords.insert("for",SkipType::skNone); - CppKeywords.insert("catch",SkipType::skNone); + CppKeywords.insert("class",KeywordType::None); + CppKeywords.insert("operator",KeywordType::None); + CppKeywords.insert("static",KeywordType::None); + CppKeywords.insert("struct",KeywordType::None); + CppKeywords.insert("union",KeywordType::None); + CppKeywords.insert("for",KeywordType::For); + CppKeywords.insert("catch",KeywordType::Catch); + CppKeywords.insert("private",KeywordType::Private); + CppKeywords.insert("public",KeywordType::Public); + CppKeywords.insert("enum",KeywordType::Enum); + CppKeywords.insert("namespace",KeywordType::Namespace); + CppKeywords.insert("inline",KeywordType::Inline); + CppKeywords.insert("typedef",KeywordType::Typedef); + CppKeywords.insert("using",KeywordType::Using); + CppKeywords.insert("protected",KeywordType::Protected); + CppKeywords.insert("friend",KeywordType::Friend); + CppKeywords.insert("decltype",KeywordType::DeclType); // not right // nullptr is value - CppKeywords.insert("nullptr",SkipType::skNone); + CppKeywords.insert("nullptr",KeywordType::None); //C Keywords CKeywords.insert("auto"); @@ -538,16 +537,16 @@ void CppScopes::clear() MemberOperatorType getOperatorType(const QString &phrase, int index) { if (index>=phrase.length()) - return MemberOperatorType::otOther; + return MemberOperatorType::Other; if (phrase[index] == '.') - return MemberOperatorType::otDot; + return MemberOperatorType::Dot; if (index+1>=phrase.length()) - return MemberOperatorType::otOther; + return MemberOperatorType::Other; if ((phrase[index] == '-') && (phrase[index+1] == '>')) - return MemberOperatorType::otArrow; + return MemberOperatorType::Arrow; if ((phrase[index] == ':') && (phrase[index+1] == ':')) - return MemberOperatorType::otDColon; - return MemberOperatorType::otOther; + return MemberOperatorType::DColon; + return MemberOperatorType::Other; } bool isScopeTypeKind(StatementKind kind) @@ -648,11 +647,13 @@ StatementKind getKindOfStatement(const PStatement& statement) if (statement->kind == StatementKind::skVariable) { if (!statement->parentScope.lock()) { return StatementKind::skGlobalVariable; - } else if (statement->scope == StatementScope::ssLocal) { + } else if (statement->scope == StatementScope::Local) { return StatementKind::skLocalVariable; } else { return StatementKind::skVariable; } + } else if (statement->kind == StatementKind::skParameter) { + return StatementKind::skLocalVariable; } return statement->kind; } diff --git a/RedPandaIDE/parser/parserutils.h b/RedPandaIDE/parser/parserutils.h index 77adaa86..ddce1ff2 100644 --- a/RedPandaIDE/parser/parserutils.h +++ b/RedPandaIDE/parser/parserutils.h @@ -56,17 +56,30 @@ using PDefine = std::shared_ptr; using DefineMap = QHash; using PDefineMap = std::shared_ptr; -enum class SkipType { - skItself, // skip itself - skToSemicolon, // skip to ; - skToColon, // skip to : - skToRightParenthesis, // skip to ) - skToLeftBrace,// Skip to { - skToRightBrace, // skip to } - skNone // It's a keyword but don't process here +enum class KeywordType { + SkipItself, // skip itself + SkipNextSemicolon, // move to ; and skip it + SkipNextColon, // move to : and skip it + SkipNextParenthesis, // move to ) and skip it + MoveToLeftBrace,// move to { + MoveToRightBrace, // move to } + For, //for + Catch, //catch + Public, // public + Private, + Protected, + Friend, + Enum, //enum + Inline, // inline + Namespace, //namespace + Typedef, //typedef + Using, //using + DeclType, // decltype + None, // It's a keyword but don't process here + NotKeyword }; - +//It will be used as hash key. DONT make it enum class!!!!! enum StatementKind { skUnknown, skNamespace, @@ -94,23 +107,23 @@ enum StatementKind { using StatementKindSet = QSet; enum class StatementScope { - ssGlobal, - ssLocal, - ssClassLocal + Global, + Local, + ClassLocal }; enum class StatementClassScope { - scsNone, - scsPrivate, - scsProtected, - scsPublic + None, + Private, + Protected, + Public }; enum class MemberOperatorType { - otArrow, - otDot, - otDColon, - otOther + Arrow, + Dot, + DColon, + Other }; enum class EvalStatementKind { @@ -136,7 +149,7 @@ using StatementMap = QMultiMap; struct Statement { // Statement(); // ~Statement(); - std::weak_ptr parentScope; // parent class/struct/namespace scope, don't use auto pointer to prevent circular reference + std::weak_ptr parentScope; // parent class/struct/namespace scope, use weak pointer to prevent circular reference QString type; // type "int" QString command; // identifier/name of statement "foo" QString args; // args "(int a,float b)" @@ -232,7 +245,7 @@ using PFileIncludes = std::shared_ptr; extern QStringList CppDirectives; extern QStringList JavadocTags; -extern QMap CppKeywords; +extern QMap CppKeywords; extern QSet CppControlKeyWords; extern QSet CKeywords; extern QSet CppTypeKeywords; diff --git a/RedPandaIDE/settingsdialog/projectfileswidget.cpp b/RedPandaIDE/settingsdialog/projectfileswidget.cpp index b3e47e95..ef88d851 100644 --- a/RedPandaIDE/settingsdialog/projectfileswidget.cpp +++ b/RedPandaIDE/settingsdialog/projectfileswidget.cpp @@ -99,6 +99,7 @@ void ProjectFilesWidget::copyUnits() unitCopy->setOverrideBuildCmd(unit->overrideBuildCmd()); unitCopy->setBuildCmd(unit->buildCmd()); unitCopy->setEncoding(unit->encoding()); + unitCopy->setFileName(unit->fileName()); mUnits.append(unitCopy); } } diff --git a/RedPandaIDE/translations/RedPandaIDE_pt_BR.ts b/RedPandaIDE/translations/RedPandaIDE_pt_BR.ts index 22fc9120..9fe5fc49 100644 --- a/RedPandaIDE/translations/RedPandaIDE_pt_BR.ts +++ b/RedPandaIDE/translations/RedPandaIDE_pt_BR.ts @@ -4716,6 +4716,14 @@ Ctrl+Alt+Down + + Switch header/source + + + + Switch Header/Source + + NewClassDialog diff --git a/RedPandaIDE/translations/RedPandaIDE_zh_CN.ts b/RedPandaIDE/translations/RedPandaIDE_zh_CN.ts index 4d41327b..8f4d682e 100644 --- a/RedPandaIDE/translations/RedPandaIDE_zh_CN.ts +++ b/RedPandaIDE/translations/RedPandaIDE_zh_CN.ts @@ -127,7 +127,7 @@ p, li { white-space: pre-wrap; } Microsoft Visual C++ - Microsoft Visual C++ + Microsoft Visual C++ @@ -3920,11 +3920,11 @@ Are you really want to continue? - - - - - + + + + + Issues 编译器 @@ -4362,7 +4362,7 @@ Are you really want to continue? - + New Problem Set 新建试题集 @@ -4381,14 +4381,14 @@ Are you really want to continue? - + Save Problem Set 保存试题集 - + Load Problem Set 载入试题集 @@ -4524,7 +4524,6 @@ Are you really want to continue? 忽略空格 - New Source File 新建源代码文件 @@ -4742,7 +4741,7 @@ Are you really want to continue? - + Clear all breakpoints 删除所有断点 @@ -4922,11 +4921,22 @@ Are you really want to continue? Ctrl+Alt+Down Ctrl+Alt+Down + + + Switch header/source + 切换头文件/源文件 + + + + Switch Header/Source + 切换头文件/源文件 + Save As Template... 保存为模板... + New File 新建文件 @@ -4968,7 +4978,7 @@ Are you really want to continue? - + Rename Symbol 重命名符号 @@ -4989,13 +4999,13 @@ Are you really want to continue? - + Export As RTF 导出为RTF - + Export As HTML 导出为HTML @@ -5369,22 +5379,22 @@ Are you really want to continue? - - + + Wrong Compiler Settings 错误的编译器设置 - - + + Compiler is set not to generate executable. 编译器被设置为不生成可执行文件。 - + We need the executabe to run problem case. 我们需要可执行文件来运行试题案例。 @@ -5452,7 +5462,7 @@ Are you really want to continue? - + Please correct this before start debugging 请在调试前改正设置。 @@ -5510,22 +5520,22 @@ Are you really want to continue? 全部复制 - + Go to Line 跳转到行 - + Line - + Template Exists 模板已存在 - + Template %1 already exists. Do you want to overwrite? 模板%1已存在。是否覆盖? @@ -5551,7 +5561,7 @@ Are you really want to continue? - + Problem Set %1 试题集%1 @@ -5625,15 +5635,15 @@ Are you really want to continue? - - + + Bookmark Description 书签描述 - - + + Description: 描述: @@ -5779,7 +5789,7 @@ Are you really want to continue? - + Delete 删除 @@ -5862,57 +5872,57 @@ Are you really want to continue? 你真的要删除%1个文件吗? - + Save project 保存项目 - + The project '%1' has modifications. 项目'%1'有改动。 - - + + Do you want to save it? 需要保存吗? - - + + File Changed 文件已发生变化 - + New Project File? 新建项目文件? - + Do you want to add the new file to the project? 您是否要将新建的文件加入项目? - - - + + + Save Error 保存失败 - + Change Project Compiler Set 改变项目编译器配置集 - + Change the project's compiler set will lose all custom compiler set options. 改变项目的编译器配置集会导致所有的自定义编译器选项被重置。 - + Do you really want to do that? 你真的想要那么做吗? @@ -5935,104 +5945,104 @@ Are you really want to continue? 无标题%1 - + Modify Watch 修改监视表达式 - + Watch Expression 监视表达式 - + Do you really want to clear all breakpoints in this file? 您真的要清除该文件的所有断点吗? - + New project 新建项目 - + Close %1 and start new project? 关闭'%1'以打开新项目? - + Folder not exist 文件夹不存在 - + Folder '%1' doesn't exist. Create it now? 文件夹'%1'不存在。是否创建? - + Can't create folder 无法创建文件夹 - + Failed to create folder '%1'. 创建文件夹'%1'失败。 - + Save new project as - + Folder %1 is not empty. 文件夹%1不是空的。 - + Do you really want to delete it? 你真的要删除它吗? - + Change working folder 改变工作文件夹 - + File '%1' is not in the current working folder. File '%1' is not in the current working folder 文件'%1'不在当前工作文件夹中。 - + Do you want to change working folder to '%1'? 是否将工作文件夹改设为'%1'? - + Can't Commit 无法提交 - + Git needs user info to commit. Git需要用信息进行提交。 - + Choose Input Data File 选择输入数据文件 - - + + All files (*.*) 所有文件 (*.*) - + Choose Expected Output Data File Choose Expected Input Data File 选择期望输出文件 @@ -6044,59 +6054,59 @@ Are you really want to continue? - + Choose Working Folder 选择工作文件夹 - - + + Header Exists 头文件已存在 - - + + Header file "%1" already exists! 头文件"%1"已存在! - + Source Exists 源文件已存在! - + Source file "%1" already exists! 源文件"%1"已存在! - + Can't commit! 无法提交! - + The following files are in conflicting: 下列文件处于冲突状态,请解决后重新添加和提交: - + Commit Message 提交信息 - + Commit Message: 提交信息: - + Commit Failed 提交失败 - + Commit message shouldn't be empty! 提交信息不能为空! @@ -6105,22 +6115,22 @@ Are you really want to continue? 小熊猫Dev-C++项目文件 (*.dev) - + New project fail 新建项目失败 - + Can't assign project template 无法使用模板创建项目 - + Remove file 删除文件 - + Remove the file from disk? 同时从硬盘上删除文件? @@ -6129,27 +6139,27 @@ Are you really want to continue? 无标题 - + New Project File Name 新的项目文件名 - + File Name: 文件名: - + File Already Exists! 文件已存在! - + File '%1' already exists! 文件'%1'已经存在! - + Add to project 添加到项目 @@ -6164,75 +6174,75 @@ Are you really want to continue? 本操作会删除此试题的所有案例。 - + Red Panda C++ project file (*.dev) 小熊猫C++项目文件(*.dev) - + Rename Error 重命名出错 - + Symbol '%1' is defined in system header. 符号'%1'在系统头文件中定义,无法修改。 - + New Name 新名称 - - + + Replace Error 替换出错 - + Can't open file '%1' for replace! 无法打开文件'%1'进行替换! - + Contents has changed since last search! 内容和上次查找时不一致。 - + Rich Text Format Files (*.rtf) RTF格式文件 (*.rtf) - + HTML Files (*.html) HTML文件 (*.html) - + The current problem set is not empty. 当前的试题集不是空的。 - + Problem %1 试题%1 - - + + Problem Set Files (*.pbs) 试题集文件 (*.pbs) - + Load Error 载入失败 - + Problem Case %1 试题案例%1 @@ -6245,13 +6255,13 @@ Are you really want to continue? - - - - - - - + + + + + + + Error 错误 @@ -6278,79 +6288,79 @@ Are you really want to continue? 版本控制 - + File '%1' was changed. 磁盘文件'%1'已被修改。 - + Reload its content from disk? 是否重新读取它的内容? - + File '%1' was removed. 磁盘文件'%1'已被删除。 - + Keep it open? 是否保持它在小熊猫C++中打开的编辑窗口? - + Open 打开 - + Compile Failed 编译失败 - + Run Failed 运行失败 - - - + + + Confirm Convertion 确认转换 - - - + + + The editing file will be saved using %1 encoding. <br />This operation can't be reverted. <br />Are you sure to continue? 当前编辑器中的文件将会使用%1编码保存。<br />这项操作无法被撤回。<br />你确定要继续吗? - + New Watch Expression 新监视表达式 - + Enter Watch Expression (it is recommended to use 'this->' for class members): 输入监视表达式 - + Parsing file %1 of %2: "%3" (%1/%2)正在解析文件"%3" - - + + Done parsing %1 files in %2 seconds 完成%1个文件的解析,用时%2秒 - + (%1 files per second) (每秒%1个文件) @@ -8677,14 +8687,14 @@ Are you really want to continue? 性能 - + Compiler Set 编译器配置集 - + Compiler @@ -8696,7 +8706,7 @@ Are you really want to continue? 自动链接 - + @@ -8772,15 +8782,15 @@ Are you really want to continue? 杂项 - - + + Program Runner 程序运行 - + Problem Set 试题集 diff --git a/RedPandaIDE/translations/RedPandaIDE_zh_TW.ts b/RedPandaIDE/translations/RedPandaIDE_zh_TW.ts index fe433607..679d190c 100644 --- a/RedPandaIDE/translations/RedPandaIDE_zh_TW.ts +++ b/RedPandaIDE/translations/RedPandaIDE_zh_TW.ts @@ -4557,6 +4557,14 @@ Ctrl+Alt+Down + + Switch header/source + + + + Switch Header/Source + + NewClassDialog diff --git a/RedPandaIDE/widgets/bookmarkmodel.cpp b/RedPandaIDE/widgets/bookmarkmodel.cpp index 5ffe45f6..83f15a0a 100644 --- a/RedPandaIDE/widgets/bookmarkmodel.cpp +++ b/RedPandaIDE/widgets/bookmarkmodel.cpp @@ -431,13 +431,13 @@ void BookmarkModel::sort(int column, Qt::SortOrder order) case 0: if (order == Qt::SortOrder::AscendingOrder) { auto sorter=[](PBookmark b1,PBookmark b2) { - return QString::compare(b1->description,b2->description); + return b1->description < b2->description; }; std::sort(mBookmarks.begin(),mBookmarks.end(),sorter); std::sort(mProjectBookmarks.begin(),mProjectBookmarks.end(),sorter); } else { auto sorter=[](PBookmark b1,PBookmark b2) { - return QString::compare(b2->description,b1->description); + return b2->descriptiondescription; }; std::sort(mBookmarks.begin(),mBookmarks.end(),sorter); std::sort(mProjectBookmarks.begin(),mProjectBookmarks.end(),sorter); @@ -446,13 +446,13 @@ void BookmarkModel::sort(int column, Qt::SortOrder order) case 1: if (order == Qt::SortOrder::AscendingOrder) { auto sorter=[](PBookmark b1,PBookmark b2) { - return QString::compare(b1->filename,b2->filename); + return b1->filenamefilename; }; std::sort(mBookmarks.begin(),mBookmarks.end(),sorter); std::sort(mProjectBookmarks.begin(),mProjectBookmarks.end(),sorter); } else { auto sorter=[](PBookmark b1,PBookmark b2) { - return QString::compare(b2->filename,b1->filename); + return b2->filenamefilename; }; std::sort(mBookmarks.begin(),mBookmarks.end(),sorter); std::sort(mProjectBookmarks.begin(),mProjectBookmarks.end(),sorter); @@ -461,13 +461,13 @@ void BookmarkModel::sort(int column, Qt::SortOrder order) case 2: if (order == Qt::SortOrder::AscendingOrder) { auto sorter=[](PBookmark b1,PBookmark b2) { - return b1->line-b2->line; + return b1->lineline; }; std::sort(mBookmarks.begin(),mBookmarks.end(),sorter); std::sort(mProjectBookmarks.begin(),mProjectBookmarks.end(),sorter); } else { auto sorter=[](PBookmark b1,PBookmark b2) { - return b2->line-b1->line; + return b2->lineline; }; std::sort(mBookmarks.begin(),mBookmarks.end(),sorter); std::sort(mProjectBookmarks.begin(),mProjectBookmarks.end(),sorter); diff --git a/RedPandaIDE/widgets/classbrowser.cpp b/RedPandaIDE/widgets/classbrowser.cpp index cc35833f..325b0a34 100644 --- a/RedPandaIDE/widgets/classbrowser.cpp +++ b/RedPandaIDE/widgets/classbrowser.cpp @@ -338,7 +338,7 @@ void ClassBrowserModel::filterChildren(ClassBrowserNode *node, const StatementMa if (statement == node->statement) // prevent infinite recursion continue; - if (statement->scope == StatementScope::ssLocal) + if (statement->scope == StatementScope::Local) continue; if (pSettings->codeCompletion().hideSymbolsStartsWithTwoUnderLine() diff --git a/RedPandaIDE/widgets/codecompletionpopup.cpp b/RedPandaIDE/widgets/codecompletionpopup.cpp index 07bd62c2..0441ae11 100644 --- a/RedPandaIDE/widgets/codecompletionpopup.cpp +++ b/RedPandaIDE/widgets/codecompletionpopup.cpp @@ -278,11 +278,11 @@ static bool sortByScopeComparator(PStatement statement1,PStatement statement2){ if (statement1->inSystemHeader != statement2->inSystemHeader) return !(statement1->inSystemHeader); // Show local statements first - if (statement1->scope != StatementScope::ssGlobal - && statement2->scope == StatementScope::ssGlobal ) { + if (statement1->scope != StatementScope::Global + && statement2->scope == StatementScope::Global ) { return true; - } else if (statement1->scope == StatementScope::ssGlobal - && statement2->scope != StatementScope::ssGlobal ) { + } else if (statement1->scope == StatementScope::Global + && statement2->scope != StatementScope::Global ) { return false; } else return nameComparator(statement1,statement2); @@ -359,11 +359,11 @@ static bool sortByScopeWithUsageComparator(PStatement statement1,PStatement stat if (statement1->inSystemHeader != statement2->inSystemHeader) return !(statement1->inSystemHeader); // Show local statements first - if (statement1->scope != StatementScope::ssGlobal - && statement2->scope == StatementScope::ssGlobal ) { + if (statement1->scope != StatementScope::Global + && statement2->scope == StatementScope::Global ) { return true; - } else if (statement1->scope == StatementScope::ssGlobal - && statement2->scope != StatementScope::ssGlobal ) { + } else if (statement1->scope == StatementScope::Global + && statement2->scope != StatementScope::Global ) { return false; } else return nameComparator(statement1,statement2); @@ -712,7 +712,7 @@ void CodeCompletionPopup::getCompletionFor( if (children.isEmpty()) return; foreach (const PStatement& childStatement, children) { - if ((childStatement->classScope==StatementClassScope::scsPublic) + if ((childStatement->classScope==StatementClassScope::Public) && !( childStatement->kind == StatementKind::skConstructor || childStatement->kind == StatementKind::skDestructor) @@ -769,7 +769,7 @@ void CodeCompletionPopup::getCompletionFor( || childStatement->kind == StatementKind::skEnumClassType || childStatement->kind == StatementKind::skEnumType )) { - if (childStatement->classScope == StatementClassScope::scsPublic) + if (childStatement->classScope == StatementClassScope::Public) addStatement(childStatement,fileName,-1); } } diff --git a/RedPandaIDE/widgets/newclassdialog.cpp b/RedPandaIDE/widgets/newclassdialog.cpp index f880baa2..6f110896 100644 --- a/RedPandaIDE/widgets/newclassdialog.cpp +++ b/RedPandaIDE/widgets/newclassdialog.cpp @@ -19,10 +19,12 @@ #include "../iconsmanager.h" #include "../settings.h" #include +#include -NewClassDialog::NewClassDialog(QWidget *parent) : +NewClassDialog::NewClassDialog(PCppParser parser, QWidget *parent) : QDialog(parent), - ui(new Ui::NewClassDialog) + ui(new Ui::NewClassDialog), + mModel(parser) { setWindowFlag(Qt::WindowContextHelpButtonHint,false); ui->setupUi(this); @@ -31,6 +33,7 @@ NewClassDialog::NewClassDialog(QWidget *parent) : connect(pIconsManager,&IconsManager::actionIconsUpdated, this, &NewClassDialog::onUpdateIcons); ui->txtClassName->setFocus(); + ui->cbBaseClass->setModel(&mModel); } NewClassDialog::~NewClassDialog() @@ -42,9 +45,9 @@ QString NewClassDialog::className() const return ui->txtClassName->text(); } -QString NewClassDialog::baseClass() const +PStatement NewClassDialog::baseClass() const { - return ui->cbBaseClass->currentText(); + return mModel.getCandidate(ui->cbBaseClass->currentIndex()); } QString NewClassDialog::headerName() const @@ -105,3 +108,86 @@ void NewClassDialog::on_txtClassName_textChanged(const QString &/* arg1 */) ui->txtSourceName->setText(ui->txtClassName->text().toLower()+".cpp"); } +NewClassCandidatesModel::NewClassCandidatesModel(PCppParser parser):QAbstractListModel(), + mParser(parser) +{ + fillClasses(); +} + +PStatement NewClassCandidatesModel::getCandidate(int row) const +{ + if (row<0) + return PStatement(); + if (row==0) + return PStatement(); + return mCandidates[row-1]; +} + + +void NewClassCandidatesModel::fillClasses() +{ + if (!mParser->enabled()) + return; + if (!mParser->freeze()) + return; + foreach( const PStatement& s, mParser->statementList().childrenStatements()) { + if (s->kind==StatementKind::skClass + && s->inProject + && !s->command.startsWith("_") + && !s->command.contains("<") + && !mClassNames.contains(s->fullName)) { + if (getFileType(s->fileName)==FileType::CHeader + || getFileType(s->fileName)==FileType::CppHeader) { + mCandidates.append(s); + mClassNames.insert(s->fullName); + } + } else if (s->kind == StatementKind::skNamespace + && !s->command.startsWith("_") + && !s->command.contains("<")) { + fillClassesInNamespace(s); + } + } + mParser->unFreeze(); + std::sort(mCandidates.begin(),mCandidates.end(),[](const PStatement& s1, const PStatement& s2){ + return s1->fullNamefullName; + }); + +} + +void NewClassCandidatesModel::fillClassesInNamespace(PStatement ns) +{ + foreach( const PStatement& s, mParser->statementList().childrenStatements(ns)) { + if (s->kind==StatementKind::skClass + && s->inProject + && !s->command.startsWith("_") + && !s->command.contains("<") + && !mClassNames.contains(s->fullName)) { + if (getFileType(s->fileName)==FileType::CHeader + || getFileType(s->fileName)==FileType::CppHeader) { + mCandidates.append(s); + mClassNames.insert(s->fullName); + } + } else if (s->kind == StatementKind::skNamespace + && !s->command.startsWith("_") + && !s->command.contains("<")) { + fillClassesInNamespace(s); + } + } +} + +int NewClassCandidatesModel::rowCount(const QModelIndex &/*parent*/) const +{ + return mCandidates.count()+1; +} + +QVariant NewClassCandidatesModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + if (role==Qt::DisplayRole) { + if (index.row()==0) + return ""; + return mCandidates[index.row()-1]->fullName; + } + return QVariant(); +} diff --git a/RedPandaIDE/widgets/newclassdialog.h b/RedPandaIDE/widgets/newclassdialog.h index 6b14110b..6d6d3534 100644 --- a/RedPandaIDE/widgets/newclassdialog.h +++ b/RedPandaIDE/widgets/newclassdialog.h @@ -18,21 +18,42 @@ #define NEWCLASSDIALOG_H #include +#include "../parser/cppparser.h" +#include namespace Ui { class NewClassDialog; } +class NewClassCandidatesModel: public QAbstractListModel { + Q_OBJECT +public: + explicit NewClassCandidatesModel(PCppParser parser); + PStatement getCandidate(int row) const; +private: + void fillClasses(); + void fillClassesInNamespace(PStatement ns); +private: + PCppParser mParser; + QVector mCandidates; + QSet mClassNames; + + // QAbstractItemModel interface +public: + int rowCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; +}; + class NewClassDialog : public QDialog { Q_OBJECT public: - explicit NewClassDialog(QWidget *parent = nullptr); + explicit NewClassDialog(PCppParser parser, QWidget *parent = nullptr); ~NewClassDialog(); QString className() const; - QString baseClass() const; + PStatement baseClass() const; QString headerName() const; QString sourceName() const; QString path() const; @@ -49,7 +70,8 @@ private slots: private: Ui::NewClassDialog *ui; - + QList mClasses; + NewClassCandidatesModel mModel; private: void onUpdateIcons(); diff --git a/RedPandaIDE/widgets/newclassdialog.ui b/RedPandaIDE/widgets/newclassdialog.ui index 819a7e31..759f8b6a 100644 --- a/RedPandaIDE/widgets/newclassdialog.ui +++ b/RedPandaIDE/widgets/newclassdialog.ui @@ -48,7 +48,11 @@ - + + + false + + diff --git a/Red_Panda_CPP.pro b/Red_Panda_CPP.pro index 8df55dc9..52cb979b 100644 --- a/Red_Panda_CPP.pro +++ b/Red_Panda_CPP.pro @@ -33,7 +33,7 @@ RedPandaIDE.depends += redpanda-git-askpass APP_NAME = RedPandaCPP -APP_VERSION = 2.1 +APP_VERSION = 2.2 linux: { isEmpty(PREFIX) { diff --git a/libs/qsynedit/qsynedit/SynEdit.cpp b/libs/qsynedit/qsynedit/SynEdit.cpp index 96d5a727..87c681b6 100644 --- a/libs/qsynedit/qsynedit/SynEdit.cpp +++ b/libs/qsynedit/qsynedit/SynEdit.cpp @@ -6599,16 +6599,16 @@ bool SynEdit::modified() const return mModified; } -void SynEdit::setModified(bool Value) +void SynEdit::setModified(bool value) { - if (Value) { + if (value) { mLastModifyTime = QDateTime::currentDateTime(); emit statusChanged(StatusChange::scModified); } - if (Value != mModified) { - mModified = Value; + if (value != mModified) { + mModified = value; - if (Value) { + if (value) { mUndoList->clear(); mRedoList->clear(); } else { @@ -6649,6 +6649,7 @@ void SynEdit::setUndoLimit(int size) void SynEdit::setUndoMemoryUsage(int size) { mUndoList->setMaxMemoryUsage(size*1024*1024); +// mUndoList->setMaxMemoryUsage(size*1024); } int SynEdit::charsInWindow() const diff --git a/libs/qsynedit/qsynedit/TextBuffer.cpp b/libs/qsynedit/qsynedit/TextBuffer.cpp index fb97aa01..94af03c4 100644 --- a/libs/qsynedit/qsynedit/TextBuffer.cpp +++ b/libs/qsynedit/qsynedit/TextBuffer.cpp @@ -688,7 +688,7 @@ void Document::saveToFile(QFile &file, const QByteArray& encoding, if (allAscii) { realEncoding = ENCODING_ASCII; } else if (encoding == ENCODING_AUTO_DETECT) { - if (codec->name().compare("System",Qt::CaseInsensitive)) { + if (codec->name().compare("System",Qt::CaseInsensitive)==0) { realEncoding = pCharsetInfoManager->getDefaultSystemEncoding(); } else { realEncoding = codec->name(); @@ -989,9 +989,9 @@ PUndoItem UndoList::popItem() // qDebug()<<"popped"<changeNumber()<changeText()<<(int)item->changeReason()<changeNumber() && item->changeReason()!=ChangeReason::GroupBreak) { mBlockCount--; + Q_ASSERT(mBlockCount>=0); // qDebug()<<"pop"<0 && (mBlockCount > mMaxUndoActions || mMemoryUsage>mMaxMemoryUsage)){ + if (mItems.isEmpty()) + return; +// qDebug()<0 && mBlockCount > mMaxUndoActions) + || (mMaxMemoryUsage>0 && mMemoryUsage>mMaxMemoryUsage)){ + PUndoItem lastItem = mItems.back(); mFullUndoImposible = true; - while ((mBlockCount > mMaxUndoActions || mMemoryUsage>mMaxMemoryUsage) + while (((mMaxUndoActions >0 && mBlockCount > mMaxUndoActions) + || (mMaxMemoryUsage>0 && mMemoryUsage>mMaxMemoryUsage)) && !mItems.isEmpty()) { //remove all undo item in block PUndoItem item = mItems.front(); size_t changeNumber = item->changeNumber(); + //we shouldn't drop the newest changes; + if (changeNumber == lastItem->changeNumber()) + break; while (mItems.count()>0) { item = mItems.front(); if (item->changeNumber()!=changeNumber) @@ -1077,6 +1086,7 @@ void UndoList::ensureMaxEntries() mBlockCount--; } } +// qDebug()<* assignmentOpera assignmentOperators->emplace_back(&AS_LS_LS_LS_ASSIGN); assert(assignmentOperators->size() < elements); - sort(assignmentOperators->begin(), assignmentOperators->end(), sortOnLength); + sort(assignmentOperators->begin(), assignmentOperators->end(), sortOnLength); } /** @@ -228,7 +228,7 @@ void ASResource::buildCastOperators(vector* castOperators) castOperators->emplace_back(&AS_STATIC_CAST); assert(castOperators->size() < elements); - sort(castOperators->begin(), castOperators->end(), sortOnName); + sort(castOperators->begin(), castOperators->end(), sortOnName); } /** @@ -295,7 +295,7 @@ void ASResource::buildHeaders(vector* headers, int fileType, bool } assert(headers->size() < elements); - sort(headers->begin(), headers->end(), sortOnName); + sort(headers->begin(), headers->end(), sortOnName); } /** @@ -369,7 +369,7 @@ void ASResource::buildNonAssignmentOperators(vector* nonAssignmen nonAssignmentOperators->emplace_back(&AS_LAMBDA); assert(nonAssignmentOperators->size() < elements); - sort(nonAssignmentOperators->begin(), nonAssignmentOperators->end(), sortOnLength); + sort(nonAssignmentOperators->begin(), nonAssignmentOperators->end(), sortOnLength); } /** @@ -425,7 +425,7 @@ void ASResource::buildNonParenHeaders(vector* nonParenHeaders, in } assert(nonParenHeaders->size() < elements); - sort(nonParenHeaders->begin(), nonParenHeaders->end(), sortOnName); + sort(nonParenHeaders->begin(), nonParenHeaders->end(), sortOnName); } /** @@ -489,7 +489,7 @@ void ASResource::buildOperators(vector* operators, int fileType) } assert(operators->size() < elements); - sort(operators->begin(), operators->end(), sortOnLength); + sort(operators->begin(), operators->end(), sortOnLength); } /** @@ -527,7 +527,7 @@ void ASResource::buildPreBlockStatements(vector* preBlockStatemen } assert(preBlockStatements->size() < elements); - sort(preBlockStatements->begin(), preBlockStatements->end(), sortOnName); + sort(preBlockStatements->begin(), preBlockStatements->end(), sortOnName); } /** @@ -567,7 +567,7 @@ void ASResource::buildPreCommandHeaders(vector* preCommandHeaders } assert(preCommandHeaders->size() < elements); - sort(preCommandHeaders->begin(), preCommandHeaders->end(), sortOnName); + sort(preCommandHeaders->begin(), preCommandHeaders->end(), sortOnName); } /** @@ -604,7 +604,7 @@ void ASResource::buildPreDefinitionHeaders(vector* preDefinitionH } assert(preDefinitionHeaders->size() < elements); - sort(preDefinitionHeaders->begin(), preDefinitionHeaders->end(), sortOnName); + sort(preDefinitionHeaders->begin(), preDefinitionHeaders->end(), sortOnName); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *