From eceba8b8f2c8b004f6f79ae3fbbc19eceba37136 Mon Sep 17 00:00:00 2001 From: Roy Qu Date: Thu, 10 Nov 2022 13:35:13 +0800 Subject: [PATCH] - enhancement: show completion tips for when define a function that already has a declaration. - clean up code --- NEWS.md | 1 + RedPandaIDE/editor.cpp | 121 ++++++++++++++++--- RedPandaIDE/editor.h | 3 +- RedPandaIDE/project.cpp | 71 +++++------ RedPandaIDE/projectoptions.h | 1 - RedPandaIDE/projecttemplate.cpp | 7 -- RedPandaIDE/widgets/codecompletionpopup.cpp | 123 +++++++++++++++++++- RedPandaIDE/widgets/codecompletionpopup.h | 16 ++- 8 files changed, 268 insertions(+), 75 deletions(-) diff --git a/NEWS.md b/NEWS.md index 14e60beb..d86c3065 100644 --- a/NEWS.md +++ b/NEWS.md @@ -14,6 +14,7 @@ Red Panda C++ Version 2.4 - fix: var assignment not correctly handled in code parser; - fix: function args not correctly handled in code parser; - fix: crash when alt+mouse drag selection + - enhancement: show completion tips for when define a function that already has a declaration. Red Panda C++ Version 2.3 diff --git a/RedPandaIDE/editor.cpp b/RedPandaIDE/editor.cpp index 5e04bc7e..9c75ca40 100644 --- a/RedPandaIDE/editor.cpp +++ b/RedPandaIDE/editor.cpp @@ -731,24 +731,28 @@ void Editor::keyPressEvent(QKeyEvent *event) QString lastWord = getPreviousWordAtPositionForSuggestion(caretXY()); if (mParser && !lastWord.isEmpty()) { if (CppTypeKeywords.contains(lastWord)) { + PStatement currentScope = mParser->findScopeStatement(mFilename,caretY()); + while(currentScope && currentScope->kind==StatementKind::skBlock) { + currentScope = currentScope->parentScope.lock(); + } + if (!currentScope || currentScope->kind == StatementKind::skNamespace) { + //may define a function + commandProcessor(QSynedit::EditCommand::ecChar,ch,nullptr); + showCompletion(lastWord,false,CodeCompletionType::FunctionWithoutDefinition); + handled=true; + return; + } if (lastWord == "long" || lastWord == "short" || lastWord == "signed" || lastWord == "unsigned" ) { commandProcessor(QSynedit::EditCommand::ecChar,ch,nullptr); - showCompletion(lastWord,false); + showCompletion(lastWord,false, CodeCompletionType::TypeKeywordComplex); handled=true; return; } - PStatement currentScope = mParser->findScopeStatement(mFilename,caretY()); - while(currentScope && currentScope->kind==StatementKind::skBlock) { - currentScope = currentScope->parentScope.lock(); - } - if (!currentScope || currentScope->kind == StatementKind::skNamespace) { - return; - } //last word is a type keyword, this is a var or param define, and dont show suggestion return; @@ -762,12 +766,41 @@ void Editor::keyPressEvent(QKeyEvent *event) || kind == StatementKind::skEnumClassType || kind == StatementKind::skEnumType || kind == StatementKind::skTypedef) { + + PStatement currentScope = mParser->findScopeStatement(mFilename,caretY()); + while(currentScope && currentScope->kind==StatementKind::skBlock) { + currentScope = currentScope->parentScope.lock(); + } + if (!currentScope || currentScope->kind == StatementKind::skNamespace) { + //may define a function + commandProcessor(QSynedit::EditCommand::ecChar,ch,nullptr); + showCompletion("",false,CodeCompletionType::FunctionWithoutDefinition); + handled=true; + return; + } + //last word is a typedef/class/struct, this is a var or param define, and dont show suggestion return; } } + qDebug()<<"lalalala"; + lastWord = getPreviousWordAtPositionForCompleteFunctionDefinition(caretXY()); + qDebug()<findScopeStatement(mFilename,caretY()); + while(currentScope && currentScope->kind==StatementKind::skBlock) { + currentScope = currentScope->parentScope.lock(); + } + if (!currentScope || currentScope->kind == StatementKind::skNamespace) { + //may define a function + commandProcessor(QSynedit::EditCommand::ecChar,ch,nullptr); + showCompletion("",false,CodeCompletionType::FunctionWithoutDefinition); + handled=true; + return; + } + } commandProcessor(QSynedit::EditCommand::ecChar,ch,nullptr); - showCompletion("",false); + showCompletion("",false,CodeCompletionType::Normal); handled=true; return; } @@ -779,7 +812,7 @@ void Editor::keyPressEvent(QKeyEvent *event) && pSettings->codeCompletion().showCompletionWhileInput() ) { mLastIdCharPressed++; commandProcessor(QSynedit::EditCommand::ecChar,ch,nullptr); - showCompletion("",false); + showCompletion("",false,CodeCompletionType::Normal); handled=true; return; } @@ -791,7 +824,7 @@ void Editor::keyPressEvent(QKeyEvent *event) && pSettings->codeCompletion().showCompletionWhileInput() ) { mLastIdCharPressed++; commandProcessor(QSynedit::EditCommand::ecChar,ch,nullptr); - showCompletion("",false); + showCompletion("",false,CodeCompletionType::Normal); handled=true; return; } @@ -1269,7 +1302,7 @@ void Editor::inputMethodEvent(QInputMethodEvent *event) return; } } - showCompletion("",false); + showCompletion("",false,CodeCompletionType::Normal); return; } } @@ -2564,20 +2597,20 @@ bool Editor::handleCodeCompletion(QChar key) switch(key.unicode()) { case '.': commandProcessor(QSynedit::EditCommand::ecChar, key); - showCompletion("",false); + showCompletion("",false,CodeCompletionType::Normal); return true; case '>': commandProcessor(QSynedit::EditCommand::ecChar, key); if ((caretX() > 2) && (lineText().length() >= 2) && (lineText()[caretX() - 3] == '-')) - showCompletion("",false); + showCompletion("",false,CodeCompletionType::Normal); return true; case ':': commandProcessor(QSynedit::EditCommand::ecChar,':',nullptr); //setSelText(key); if ((caretX() > 2) && (lineText().length() >= 2) && (lineText()[caretX() - 3] == ':')) - showCompletion("",false); + showCompletion("",false,CodeCompletionType::Normal); return true; case '/': case '\\': @@ -2975,7 +3008,7 @@ void Editor::exportAsHTML(const QString &htmlFilename) exporter.SaveToFile(htmlFilename); } -void Editor::showCompletion(const QString& preWord,bool autoComplete) +void Editor::showCompletion(const QString& preWord,bool autoComplete, CodeCompletionType type) { if (pMainWindow->functionTip()->isVisible()) { pMainWindow->functionTip()->hide(); @@ -3089,6 +3122,7 @@ void Editor::showCompletion(const QString& preWord,bool autoComplete) memberExpression, mFilename, caretY(), + type, keywords); } else { QStringList memberExpression; @@ -3096,7 +3130,7 @@ void Editor::showCompletion(const QString& preWord,bool autoComplete) mCompletionPopup->prepareSearch(preWord, QStringList(), "", - memberExpression, mFilename, caretY(),keywords); + memberExpression, mFilename, caretY(),type,keywords); } // Filter the whole statement list @@ -4390,6 +4424,59 @@ QString Editor::getPreviousWordAtPositionForSuggestion(const QSynedit::BufferCoo return result; } +QString Editor::getPreviousWordAtPositionForCompleteFunctionDefinition(const QSynedit::BufferCoord &p) +{ + QString result; + if ((p.line<1) || (p.line>document()->count())) { + return ""; + } + + QString s = document()->getString(p.line - 1); + int wordBegin; + int wordEnd = p.ch-1; + if (wordEnd >= s.length()) + wordEnd = s.length()-1; + while (wordEnd > 0 && (isIdentChar(s[wordEnd]) || s[wordEnd] == ':')) { + wordEnd--; + } + int bracketLevel=0; + while (wordEnd > 0) { + if (s[wordEnd] == '>' + || s[wordEnd] == ']') { + bracketLevel++; + } else if (s[wordEnd] == '<' + || s[wordEnd] == '[') { + bracketLevel--; + } else if (bracketLevel==0) { + if (s[wordEnd]=='*' || s[wordEnd]=='&' || s[wordEnd]==' ' || s[wordEnd]=='\t') { + //do nothing + } else if (isIdentChar(s[wordEnd])) + break; + else + return ""; //error happened + } + wordEnd--; + } + if (wordEnd<0) + return ""; + +// if (!isIdentChar(s[wordEnd])) +// return ""; + + wordBegin = wordEnd; + while ((wordBegin >= 0) && isIdentChar(s[wordBegin]) ) { + wordBegin--; + } + wordBegin++; + + if (s[wordBegin]>='0' && s[wordBegin]<='9') // not valid word + return ""; + + result = s.mid(wordBegin, wordEnd - wordBegin+1); + + return result; +} + void Editor::reformat(bool doReparse) { if (readOnly()) diff --git a/RedPandaIDE/editor.h b/RedPandaIDE/editor.h index 05f525f0..c9e9e168 100644 --- a/RedPandaIDE/editor.h +++ b/RedPandaIDE/editor.h @@ -195,6 +195,7 @@ public: void modifyBreakpointProperty(int line); void setActiveBreakpointFocus(int Line, bool setFocus=true); QString getPreviousWordAtPositionForSuggestion(const QSynedit::BufferCoord& p); + QString getPreviousWordAtPositionForCompleteFunctionDefinition(const QSynedit::BufferCoord& p); void reformat(bool doReparse=true); void checkSyntaxInBack(); void gotoDeclaration(const QSynedit::BufferCoord& pos); @@ -264,7 +265,7 @@ private: void undoSymbolCompletion(int pos); QuoteStatus getQuoteStatus(); - void showCompletion(const QString& preWord, bool autoComplete); + void showCompletion(const QString& preWord, bool autoComplete, CodeCompletionType type); void showHeaderCompletion(bool autoComplete, bool forceShow=false); bool testInFunc(int x,int y); diff --git a/RedPandaIDE/project.cpp b/RedPandaIDE/project.cpp index d8ae9745..b721e86e 100644 --- a/RedPandaIDE/project.cpp +++ b/RedPandaIDE/project.cpp @@ -987,16 +987,16 @@ bool Project::saveAsTemplate(const QString &templateFolder, } ini->SetLongValue("Project", "Type", static_cast(mOptions.type)); - if (!mOptions.objFiles.isEmpty()) - ini->SetValue("Project", "ObjFiles", mOptions.objFiles.join(";").toUtf8()); if (!mOptions.includeDirs.isEmpty()) - ini->SetValue("Project", "Includes", mOptions.includeDirs.join(";").toUtf8()); + ini->SetValue("Project", "Includes", extractRelativePaths(directory(), mOptions.includeDirs).join(";").toUtf8()); if (!mOptions.resourceIncludes.isEmpty()) - ini->SetValue("Project", "ResourceIncludes", mOptions.resourceIncludes.join(";").toUtf8()); + ini->SetValue("Project", "ResourceIncludes", extractRelativePaths(directory(), mOptions.resourceIncludes).join(";").toUtf8()); + if (!mOptions.makeIncludes.isEmpty()) + ini->SetValue("Project", "MakeIncludes", extractRelativePaths(directory(), mOptions.makeIncludes).join(";").toUtf8()); if (!mOptions.binDirs.isEmpty()) - ini->SetValue("Project", "Bins", mOptions.binDirs.join(";").toUtf8()); + ini->SetValue("Project", "Bins", extractRelativePaths(directory(), mOptions.binDirs).join(";").toUtf8()); if (!mOptions.libDirs.isEmpty()) - ini->SetValue("Project", "Libs", mOptions.libDirs.join(";").toUtf8()); + ini->SetValue("Project", "Libs", extractRelativePaths(directory(), mOptions.libDirs).join(";").toUtf8()); if (!mOptions.compilerCmd.isEmpty()) ini->SetValue("Project", "Compiler", mOptions.compilerCmd.toUtf8()); if (!mOptions.cppCompilerCmd.isEmpty()) @@ -1078,13 +1078,12 @@ void Project::saveOptions() ini.SetValue("Project","Name", toByteArray(mName)); ini.SetLongValue("Project","Type", static_cast(mOptions.type)); ini.SetLongValue("Project","Ver", 3); // Is 3 as of Red Panda C++.0 - ini.SetValue("Project","ObjFiles", toByteArray(mOptions.objFiles.join(";"))); - ini.SetValue("Project","Includes", toByteArray(mOptions.includeDirs.join(";"))); - ini.SetValue("Project","Libs", toByteArray(mOptions.libDirs.join(";"))); - ini.SetValue("Project","Bins", toByteArray(mOptions.binDirs.join(";"))); + ini.SetValue("Project","Includes", toByteArray(extractRelativePaths(directory(),mOptions.includeDirs).join(";"))); + ini.SetValue("Project","Libs", toByteArray(extractRelativePaths(directory(),mOptions.libDirs).join(";"))); + ini.SetValue("Project","Bins", toByteArray(extractRelativePaths(directory(),mOptions.binDirs).join(";"))); ini.SetValue("Project","PrivateResource", toByteArray(mOptions.privateResource)); - ini.SetValue("Project","ResourceIncludes", toByteArray(mOptions.resourceIncludes.join(";"))); - ini.SetValue("Project","MakeIncludes", toByteArray(mOptions.makeIncludes.join(";"))); + ini.SetValue("Project","ResourceIncludes", toByteArray(extractRelativePaths(directory(),mOptions.resourceIncludes).join(";"))); + ini.SetValue("Project","MakeIncludes", toByteArray(extractRelativePaths(directory(),mOptions.makeIncludes).join(";"))); ini.SetValue("Project","Compiler", toByteArray(mOptions.compilerCmd)); ini.SetValue("Project","CppCompiler", toByteArray(mOptions.cppCompilerCmd)); ini.SetValue("Project","Linker", toByteArray(mOptions.linkerCmd)); @@ -1096,11 +1095,11 @@ void Project::saveOptions() ini.SetLongValue("Project","LogOutputEnabled", mOptions.logOutputEnabled); ini.SetLongValue("Project","OverrideOutput", mOptions.overrideOutput); ini.SetValue("Project","OverrideOutputName", toByteArray(mOptions.overridenOutput)); - ini.SetValue("Project","HostApplication", toByteArray(mOptions.hostApplication)); + ini.SetValue("Project","HostApplication", toByteArray(extractRelativePath(directory(), mOptions.hostApplication))); ini.SetLongValue("Project","UseCustomMakefile", mOptions.useCustomMakefile); - ini.SetValue("Project","CustomMakefile", toByteArray(mOptions.customMakefile)); + ini.SetValue("Project","CustomMakefile", toByteArray(extractRelativePath(directory(),mOptions.customMakefile))); ini.SetLongValue("Project","UsePrecompiledHeader", mOptions.usePrecompiledHeader); - ini.SetValue("Project","PrecompiledHeader", toByteArray(mOptions.precompiledHeader)); + ini.SetValue("Project","PrecompiledHeader", toByteArray(extractRelativePath(directory(), mOptions.precompiledHeader))); ini.SetValue("Project","CommandLine", toByteArray(mOptions.cmdLineArgs)); ini.SetValue("Project","Folders", toByteArray(mFolders.join(";"))); ini.SetLongValue("Project","IncludeVersionInfo", mOptions.includeVersionInfo); @@ -1893,49 +1892,42 @@ void Project::loadOptions(SimpleIni& ini) mOptions.compilerCmd = fromByteArray(ini.GetValue("Project", "Compiler", "")); mOptions.cppCompilerCmd = fromByteArray(ini.GetValue("Project", "CppCompiler", "")); mOptions.linkerCmd = fromByteArray(ini.GetValue("Project", "Linker", "")); - mOptions.objFiles = fromByteArray(ini.GetValue("Project", "ObjFiles", "")).split(";", -#if QT_VERSION >= QT_VERSION_CHECK(5,15,0) - Qt::SkipEmptyParts -#else - QString::SkipEmptyParts -#endif - ); - mOptions.binDirs = fromByteArray(ini.GetValue("Project", "Bins", "")).split(";", + mOptions.binDirs = absolutePaths(directory(), fromByteArray(ini.GetValue("Project", "Bins", "")).split(";", #if QT_VERSION >= QT_VERSION_CHECK(5,15,0) Qt::SkipEmptyParts #else QString::SkipEmptyParts #endif - ); - mOptions.libDirs = fromByteArray(ini.GetValue("Project", "Libs", "")).split(";", + )); + mOptions.libDirs = absolutePaths(directory(), fromByteArray(ini.GetValue("Project", "Libs", "")).split(";", #if QT_VERSION >= QT_VERSION_CHECK(5,15,0) Qt::SkipEmptyParts #else QString::SkipEmptyParts #endif - ); - mOptions.includeDirs = fromByteArray(ini.GetValue("Project", "Includes", "")).split(";", + )); + mOptions.includeDirs = absolutePaths(directory(), fromByteArray(ini.GetValue("Project", "Includes", "")).split(";", #if QT_VERSION >= QT_VERSION_CHECK(5,15,0) Qt::SkipEmptyParts #else QString::SkipEmptyParts #endif - ); + )); mOptions.privateResource = fromByteArray(ini.GetValue("Project", "PrivateResource", "")); - mOptions.resourceIncludes = fromByteArray(ini.GetValue("Project", "ResourceIncludes", "")).split(";", + mOptions.resourceIncludes = absolutePaths(directory(), fromByteArray(ini.GetValue("Project", "ResourceIncludes", "")).split(";", #if QT_VERSION >= QT_VERSION_CHECK(5,15,0) Qt::SkipEmptyParts #else QString::SkipEmptyParts #endif - ); - mOptions.makeIncludes = fromByteArray(ini.GetValue("Project", "MakeIncludes", "")).split(";", + )); + mOptions.makeIncludes = absolutePaths(directory(), fromByteArray(ini.GetValue("Project", "MakeIncludes", "")).split(";", #if QT_VERSION >= QT_VERSION_CHECK(5,15,0) Qt::SkipEmptyParts #else QString::SkipEmptyParts #endif - ); + )); mOptions.isCpp = ini.GetBoolValue("Project", "IsCpp", false); mOptions.exeOutput = fromByteArray(ini.GetValue("Project", "ExeOutput", "")); mOptions.objectOutput = fromByteArray(ini.GetValue("Project", "ObjectOutput", "")); @@ -1943,11 +1935,11 @@ void Project::loadOptions(SimpleIni& ini) mOptions.logOutputEnabled = ini.GetBoolValue("Project", "LogOutputEnabled", false); mOptions.overrideOutput = ini.GetBoolValue("Project", "OverrideOutput", false); mOptions.overridenOutput = fromByteArray(ini.GetValue("Project", "OverrideOutputName", "")); - mOptions.hostApplication = fromByteArray(ini.GetValue("Project", "HostApplication", "")); + mOptions.hostApplication = absolutePath(directory(), fromByteArray(ini.GetValue("Project", "HostApplication", ""))); mOptions.useCustomMakefile = ini.GetBoolValue("Project", "UseCustomMakefile", false); - mOptions.customMakefile = fromByteArray(ini.GetValue("Project", "CustomMakefile", "")); + mOptions.customMakefile = absolutePath(directory(),fromByteArray(ini.GetValue("Project", "CustomMakefile", ""))); mOptions.usePrecompiledHeader = ini.GetBoolValue("Project", "UsePrecompiledHeader", false); - mOptions.precompiledHeader = fromByteArray(ini.GetValue("Project", "PrecompiledHeader", "")); + mOptions.precompiledHeader = absolutePath(directory(),fromByteArray(ini.GetValue("Project", "PrecompiledHeader", ""))); mOptions.cmdLineArgs = fromByteArray(ini.GetValue("Project", "CommandLine", "")); mFolders = fromByteArray(ini.GetValue("Project", "Folders", "")).split(";", #if QT_VERSION >= QT_VERSION_CHECK(5,15,0) @@ -2091,13 +2083,6 @@ void Project::loadOptions(SimpleIni& ini) Qt::SkipEmptyParts #else QString::SkipEmptyParts -#endif - ); - mOptions.objFiles = fromByteArray(ini.GetValue("Project", "ObjFiles", "")).split(";", -#if QT_VERSION >= QT_VERSION_CHECK(5,15,0) - Qt::SkipEmptyParts -#else - QString::SkipEmptyParts #endif ); mOptions.includeDirs = fromByteArray(ini.GetValue("Project", "IncludeDirs", "")).split(";", @@ -2113,7 +2098,7 @@ void Project::loadOptions(SimpleIni& ini) mOptions.objectOutput = fromByteArray(ini.GetValue("Project", "ObjectOutput", "")); mOptions.overrideOutput = ini.GetBoolValue("Project", "OverrideOutput", false); mOptions.overridenOutput = fromByteArray(ini.GetValue("Project", "OverrideOutputName", "")); - mOptions.hostApplication = fromByteArray(ini.GetValue("Project", "HostApplication", "")); + mOptions.hostApplication = absolutePath(directory(), fromByteArray(ini.GetValue("Project", "HostApplication", ""))); } } diff --git a/RedPandaIDE/projectoptions.h b/RedPandaIDE/projectoptions.h index 99ef69b8..c3c46e8d 100644 --- a/RedPandaIDE/projectoptions.h +++ b/RedPandaIDE/projectoptions.h @@ -63,7 +63,6 @@ struct ProjectOptions{ explicit ProjectOptions(); ProjectType type; int version; - QStringList objFiles; QString compilerCmd; QString cppCompilerCmd; QString linkerCmd; diff --git a/RedPandaIDE/projecttemplate.cpp b/RedPandaIDE/projecttemplate.cpp index f9408cde..cb8dd738 100644 --- a/RedPandaIDE/projecttemplate.cpp +++ b/RedPandaIDE/projecttemplate.cpp @@ -104,13 +104,6 @@ void ProjectTemplate::readTemplateFile(const QString &fileName) mOptions.icon = mIni->GetValue("Project", "Icon", ""); mOptions.type = static_cast(mIni->GetLongValue("Project", "Type", 0)); // default = gui - mOptions.objFiles = fromByteArray(mIni->GetValue("Project", "ObjFiles", "")).split(";", -#if QT_VERSION >= QT_VERSION_CHECK(5,15,0) - Qt::SkipEmptyParts -#else - QString::SkipEmptyParts -#endif - ); mOptions.includeDirs = fromByteArray(mIni->GetValue("Project", "Includes", "")).split(";", #if QT_VERSION >= QT_VERSION_CHECK(5,15,0) Qt::SkipEmptyParts diff --git a/RedPandaIDE/widgets/codecompletionpopup.cpp b/RedPandaIDE/widgets/codecompletionpopup.cpp index 2bb649b8..d3d4fb70 100644 --- a/RedPandaIDE/widgets/codecompletionpopup.cpp +++ b/RedPandaIDE/widgets/codecompletionpopup.cpp @@ -80,6 +80,7 @@ void CodeCompletionPopup::prepareSearch( const QStringList& memberExpression, const QString &filename, int line, + CodeCompletionType type, const QSet& customKeywords) { QMutexLocker locker(&mMutex); @@ -91,11 +92,14 @@ void CodeCompletionPopup::prepareSearch( mMemberPhrase = memberExpression.join(""); mMemberOperator = memberOperator; - if (preWord.isEmpty()) { + if (type == CodeCompletionType::TypeKeywordComplex) { + getCompletionListForTypeKeywordComplex(preWord); + } else if (type == CodeCompletionType::FunctionWithoutDefinition) { + mIncludedFiles = mParser->getFileIncludes(filename); + getCompletionForFunctionWithoutDefinition(preWord, ownerExpression,memberOperator,memberExpression, filename,line); + } else if (preWord.isEmpty()) { mIncludedFiles = mParser->getFileIncludes(filename); getCompletionFor(ownerExpression,memberOperator,memberExpression, filename,line, customKeywords); - } else { - getCompletionListForPreWord(preWord); } setCursor(oldCursor); @@ -198,6 +202,40 @@ void CodeCompletionPopup::addChildren(PStatement scopeStatement, const QString & } } +void CodeCompletionPopup::addFunctionWithoutDefinitionChildren(PStatement scopeStatement, const QString &fileName, int line) +{ + if (scopeStatement && !isIncluded(scopeStatement->fileName) + && !isIncluded(scopeStatement->definitionFileName)) + return; + const StatementMap& children = mParser->statementList().childrenStatements(scopeStatement); + if (children.isEmpty()) + return; + + for (const PStatement& childStatement: children) { + if (childStatement->inSystemHeader) + continue; + if (childStatement->fileName.isEmpty()) { + // hard defines, do nothing + continue; + } + switch(childStatement->kind) { + case StatementKind::skConstructor: + case StatementKind::skFunction: + case StatementKind::skDestructor: + if (!childStatement->hasDefinition) + addStatement(childStatement,fileName,line); + break; + case StatementKind::skClass: + case StatementKind::skNamespace: + if (isIncluded(childStatement->fileName)) + addStatement(childStatement,fileName,line); + break; + default: + break; + } + } +} + void CodeCompletionPopup::addStatement(PStatement statement, const QString &fileName, int line) { if (mAddedStatements.contains(statement->command)) @@ -766,7 +804,84 @@ void CodeCompletionPopup::getCompletionFor( } } -void CodeCompletionPopup::getCompletionListForPreWord(const QString &preWord) +void CodeCompletionPopup::getCompletionForFunctionWithoutDefinition(const QString& preWord, const QStringList &ownerExpression, const QString &memberOperator, const QStringList &memberExpression, const QString &fileName, int line) +{ + if(!mParser) { + return; + } + if (!mParser->enabled()) + return; + if (memberOperator.isEmpty() && ownerExpression.isEmpty() && memberExpression.isEmpty()) + return; + + if (!mParser->freeze()) + return; + { + auto action = finally([this]{ + mParser->unFreeze(); + }); + + if (memberOperator.isEmpty()) { + getCompletionListForTypeKeywordComplex(preWord); + PStatement scopeStatement = mCurrentScope; + //add members of current scope that not added before + while (scopeStatement && scopeStatement->kind!=StatementKind::skNamespace) { + scopeStatement = scopeStatement->parentScope.lock(); + } + if (scopeStatement) { + //namespace; + PStatementList namespaceStatementsList = + mParser->findNamespace(scopeStatement->fullName); + if (namespaceStatementsList) { + foreach (const PStatement& namespaceStatement, *namespaceStatementsList) { + addFunctionWithoutDefinitionChildren(namespaceStatement, fileName, line); + } + } + } else { + //global + addFunctionWithoutDefinitionChildren(scopeStatement, fileName, line); + } + } else { + if (memberOperator != "::") + return; + //the identifier to be completed is a member of variable/class + if (ownerExpression.isEmpty()) { + // start with '::', we only find in global + // add all global members and not added before + addFunctionWithoutDefinitionChildren(nullptr, fileName, line); + return; + } + if (memberExpression.length()==2 && memberExpression.front()!="~") + return; + if (memberExpression.length()>2) + return; + + PStatement scope = mCurrentScope;//the scope the expression in + PStatement parentTypeStatement; + PEvalStatement ownerStatement = mParser->evalExpression(fileName, + ownerExpression, + scope); + if(!ownerStatement || !ownerStatement->effectiveTypeStatement) { + return; + } + if (ownerStatement->kind==EvalStatementKind::Namespace) { + //there might be many statements corresponding to one namespace; + PStatementList namespaceStatementsList = + mParser->findNamespace(ownerStatement->baseType); + if (namespaceStatementsList) { + foreach (const PStatement& namespaceStatement, *namespaceStatementsList) { + addFunctionWithoutDefinitionChildren(namespaceStatement, fileName, line); + } + } + return; + } else if (ownerStatement->effectiveTypeStatement->kind == StatementKind::skClass) { + addFunctionWithoutDefinitionChildren(ownerStatement->effectiveTypeStatement, fileName, line); + } + } + } +} + +void CodeCompletionPopup::getCompletionListForTypeKeywordComplex(const QString &preWord) { mFullCompletionStatementList.clear(); if (preWord == "long") { diff --git a/RedPandaIDE/widgets/codecompletionpopup.h b/RedPandaIDE/widgets/codecompletionpopup.h index 38d232f5..7d2fadd7 100644 --- a/RedPandaIDE/widgets/codecompletionpopup.h +++ b/RedPandaIDE/widgets/codecompletionpopup.h @@ -39,7 +39,7 @@ private: enum class CodeCompletionType { Normal, - ComplexType, + TypeKeywordComplex, FunctionWithoutDefinition }; @@ -86,6 +86,7 @@ public: const QStringList& memberExpression, const QString& filename, int line, + CodeCompletionType completionType, const QSet& customKeywords); bool search(const QString& memberPhrase, bool autoHideOnSingleResult); @@ -127,6 +128,8 @@ public: private: void addChildren(PStatement scopeStatement, const QString& fileName, int line); + void addFunctionWithoutDefinitionChildren(PStatement scopeStatement, const QString& fileName, + int line); void addStatement(PStatement statement, const QString& fileName, int line); void filterList(const QString& member); void getCompletionFor( @@ -136,7 +139,16 @@ private: const QString& fileName, int line, const QSet& customKeywords); - void getCompletionListForPreWord(const QString& preWord); + + void getCompletionForFunctionWithoutDefinition( + const QString& preWord, + const QStringList& ownerExpression, + const QString& memberOperator, + const QStringList& memberExpression, + const QString& fileName, + int line); + + void getCompletionListForTypeKeywordComplex(const QString& preWord); void addKeyword(const QString& keyword); bool isIncluded(const QString& fileName); private: