work save

This commit is contained in:
royqh1979@gmail.com 2021-08-25 00:20:07 +08:00
parent ea77b45661
commit 9066bc4897
6 changed files with 509 additions and 30 deletions

View File

@ -205,11 +205,11 @@ PStatement CppParser::findStatementOf(const QString &fileName, const QString &ph
return findStatementOf(fileName,phrase,findAndScanBlockAt(fileName,line));
}
PStatement CppParser::findStatementOf(const QString &fileName, const QString &phrase, PStatement currentClass, PStatement &currentClassType, bool force)
PStatement CppParser::findStatementOf(const QString &fileName, const QString &phrase, PStatement currentScope, PStatement &parentScopeType, bool force)
{
QMutexLocker locker(&mMutex);
PStatement result;
currentClassType = currentClass;
parentScopeType = currentScope;
if (mParsing && !force)
return PStatement();
@ -249,10 +249,10 @@ PStatement CppParser::findStatementOf(const QString &fileName, const QString &ph
return PStatement();
} else {
//unqualified name
currentClassType = currentClass;
parentScopeType = currentScope;
remainder = splitPhrase(remainder,nextScopeWord,operatorToken,memberName);
if (currentClass && (currentClass->kind == StatementKind::skNamespace)) {
PStatementList namespaceList = mNamespaces.value(currentClass->fullName);
if (currentScope && (currentScope->kind == StatementKind::skNamespace)) {
PStatementList namespaceList = mNamespaces.value(currentScope->fullName);
if (!namespaceList || namespaceList->isEmpty())
return PStatement();
for (PStatement currentNamespace:*namespaceList){
@ -261,15 +261,15 @@ PStatement CppParser::findStatementOf(const QString &fileName, const QString &ph
break;
}
} else {
statement = findStatementStartingFrom(fileName,nextScopeWord,currentClassType,force);
statement = findStatementStartingFrom(fileName,nextScopeWord,parentScopeType,force);
}
if (!statement)
return PStatement();
}
currentClassType = currentClass;
parentScopeType = currentScope;
if (!memberName.isEmpty() && (statement->kind == StatementKind::skTypedef)) {
PStatement typeStatement = findTypeDefinitionOf(fileName,statement->type, currentClassType);
PStatement typeStatement = findTypeDefinitionOf(fileName,statement->type, parentScopeType);
if (typeStatement)
statement = typeStatement;
}
@ -278,7 +278,7 @@ PStatement CppParser::findStatementOf(const QString &fileName, const QString &ph
if ((statement->kind == StatementKind::skAlias) &&
(phrase!=statement->type)) {
statement = findStatementOf(fileName, statement->type,
currentClass, currentClassType, force);
currentScope, parentScopeType, force);
if (!statement)
return PStatement();
}
@ -315,7 +315,7 @@ PStatement CppParser::findStatementOf(const QString &fileName, const QString &ph
}
}
if (!isSTLContainerFunctions)
typeStatement = findTypeDefinitionOf(fileName,statement->type, currentClassType);
typeStatement = findTypeDefinitionOf(fileName,statement->type, parentScopeType);
//it's stl smart pointer
if ((typeStatement)
@ -344,10 +344,10 @@ PStatement CppParser::findStatementOf(const QString &fileName, const QString &ph
if (!memberStatement)
return PStatement();
currentClassType=statement;
parentScopeType=statement;
statement = memberStatement;
if (!memberName.isEmpty() && (statement->kind == StatementKind::skTypedef)) {
PStatement typeStatement = findTypeDefinitionOf(fileName,statement->type, currentClassType);
PStatement typeStatement = findTypeDefinitionOf(fileName,statement->type, parentScopeType);
if (typeStatement)
statement = typeStatement;
}

View File

@ -42,8 +42,8 @@ public:
int line);
PStatement findStatementOf(const QString& fileName,
const QString& phrase,
PStatement currentClass,
PStatement& currentClassType,
PStatement currentScope,
PStatement& parentScopeType,
bool force = false);
PStatement findStatementOf(const QString& fileName,
const QString& phrase,

View File

@ -9,6 +9,7 @@ QStringList CppDirectives;
QStringList JavadocTags;
QMap<QString,SkipType> CppKeywords;
QSet<QString> CppTypeKeywords;
QSet<QString> CKeywords;
QSet<QString> STLPointers;
QSet<QString> STLContainers;
QSet<QString> STLElementMethods;
@ -181,7 +182,41 @@ void initParser()
// nullptr is value
CppKeywords.insert("nullptr",SkipType::skNone);
//C Keywords
CKeywords.insert("auto");
CKeywords.insert("break");
CKeywords.insert("case");
CKeywords.insert("char");
CKeywords.insert("const");
CKeywords.insert("continue");
CKeywords.insert("default");
CKeywords.insert("do");
CKeywords.insert("double");
CKeywords.insert("else");
CKeywords.insert("enum");
CKeywords.insert("extern");
CKeywords.insert("float");
CKeywords.insert("for");
CKeywords.insert("goto");
CKeywords.insert("if");
CKeywords.insert("inline");
CKeywords.insert("int");
CKeywords.insert("long");
CKeywords.insert("register");
CKeywords.insert("restrict");
CKeywords.insert("return");
CKeywords.insert("short");
CKeywords.insert("signed");
CKeywords.insert("sizeof");
CKeywords.insert("static");
CKeywords.insert("struct");
CKeywords.insert("switch");
CKeywords.insert("typedef");
CKeywords.insert("union");
CKeywords.insert("unsigned");
CKeywords.insert("void");
CKeywords.insert("volatile");
CKeywords.insert("while");
//STL Containers
STLContainers.insert("std::array");
@ -426,3 +461,33 @@ void CppScopes::clear()
{
mScopes.clear();
}
MemberOperatorType getOperatorType(const QString &phrase, int index)
{
MemberOperatorType result=MemberOperatorType::otOther;
if (index>=phrase.length())
return MemberOperatorType::otOther;
if (phrase[index] == '.')
return MemberOperatorType::otDot;
if (index+1>=phrase.length())
return MemberOperatorType::otOther;
if ((phrase[index] == '-') && (phrase[index+1] == '>'))
return MemberOperatorType::otArrow;
if ((phrase[index] == ':') && (phrase[index+1] == ':'))
return MemberOperatorType::otDColon;
return MemberOperatorType::otOther;
}
bool isScopeTypeKind(StatementKind kind)
{
switch(kind) {
case StatementKind::skClass:
case StatementKind::skNamespace:
case StatementKind::skFunction:
case StatementKind::skConstructor:
case StatementKind::skDestructor:
return true;
default:
return false;
}
}

View File

@ -184,6 +184,7 @@ using PFileIncludes = std::shared_ptr<FileIncludes>;
extern QStringList CppDirectives;
extern QStringList JavadocTags;
extern QMap<QString,SkipType> CppKeywords;
extern QSet<QString> CKeywords;
extern QSet<QString> CppTypeKeywords;
extern QSet<QString> STLPointers;
extern QSet<QString> STLContainers;
@ -201,5 +202,7 @@ bool isSystemHeaderFile(const QString& fileName, const QSet<QString>& includePat
bool isHfile(const QString filename);
bool isCfile(const QString filename);
bool isKeyword(const QString& word);
bool isScopeTypeKind(StatementKind kind);
MemberOperatorType getOperatorType(const QString& phrase, int index);
#endif // PARSER_UTILS_H

View File

@ -12,6 +12,16 @@ CodeCompletionView::CodeCompletionView(QWidget *parent) :
setLayout(new QVBoxLayout());
layout()->addWidget(mListView);
layout()->setMargin(0);
mShowKeywords=true;
mUseCppKeyword=true;
mEnabled = true;
mOnlyGlobals = false;
mShowCount = 1000;
mShowCodeIns = true;
mIgnoreCase = false;
}
CodeCompletionView::~CodeCompletionView()
@ -24,6 +34,78 @@ void CodeCompletionView::setKeypressedCallback(const KeyPressedCallback &newKeyp
mListView->setKeypressedCallback(newKeypressedCallback);
}
void CodeCompletionView::prepareSearch(const QString &phrase, const QString &filename, int line)
{
QMutexLocker locker(&mMutex);
if (!mEnabled)
return;
mPhrase = phrase;
//Screen.Cursor := crHourglass;
QCursor oldCursor = cursor();
setCursor(Qt::CursorShape::WaitCursor);
mIncludedFiles = mParser->getFileIncludes(filename);
getCompletionFor(filename,phrase,line);
//todo: notify model
//CodeComplForm.lbCompletion.Font.Size := FontSize;
//CodeComplForm.lbCompletion.ItemHeight := CodeComplForm.lbCompletion.Canvas.TextHeight('F')+6;
// Round(2 * FontSize);
//CodeComplForm.Update;
setCursor(oldCursor);
}
bool CodeCompletionView::search(const QString &phrase, const QString &filename, bool autoHideOnSingleResult)
{
QMutexLocker locker(&mMutex);
bool result = false;
mPhrase = phrase;
if (phrase.isEmpty()) {
hide();
return false;
}
if (!mEnabled) {
hide();
return false;
}
QCursor oldCursor = cursor();
setCursor(Qt::CursorShape::WaitCursor);
// Sort here by member
int i = mParser->findLastOperator(phrase);
while ((i>=0) && (i<phrase.length()) && (
phrase[i] == '.'
|| phrase[i] == ':'
|| phrase[i] == '-'
|| phrase[i] == '>'))
i++;
QString symbol = phrase.mid(i);
// filter fFullCompletionStatementList to fCompletionStatementList
filterList(symbol);
if (!mCompletionStatementList.isEmpty()) {
//todo:update model
setCursor(oldCursor);
// if only one suggestion, and is exactly the symbol to search, hide the frame (the search is over)
// if only one suggestion and auto hide , don't show the frame
if(mCompletionStatementList.count() == 1)
if (autoHideOnSingleResult
|| (symbol == mCompletionStatementList.front()->command)) {
return true;
}
} else {
//todo:update(clear) the model
setCursor(oldCursor);
hide();
}
}
void CodeCompletionView::addChildren(PStatement scopeStatement, const QString &fileName, int line)
{
if (scopeStatement && !isIncluded(scopeStatement->fileName)
@ -64,6 +146,8 @@ void CodeCompletionView::addChildren(PStatement scopeStatement, const QString &f
void CodeCompletionView::addStatement(PStatement statement, const QString &fileName, int line)
{
if (mAddedStatements.contains(statement->command))
return;
if ((line!=-1)
&& (line < statement->line)
&& (fileName == statement->fileName))
@ -211,24 +295,24 @@ void CodeCompletionView::filterList(const QString &member)
// return;
// }
QList<PStatement> tmpList;
mCompletionStatementList.clear();
if (!member.isEmpty()) { // filter
tmpList.reserve(mFullCompletionStatementList.size());
mCompletionStatementList.reserve(mFullCompletionStatementList.size());
for (PStatement statement:mFullCompletionStatementList) {
Qt::CaseSensitivity cs = (mIgnoreCase?
Qt::CaseInsensitive:
Qt::CaseSensitive);
if (statement->command.startsWith(member, cs))
tmpList.append(statement);
mCompletionStatementList.append(statement);
}
} else
tmpList = mCompletionStatementList;
mCompletionStatementList.append(mFullCompletionStatementList);
if (mRecordUsage) {
int topCount = 0;
int secondCount = 0;
int thirdCount = 0;
int usageCount;
for (PStatement statement:tmpList) {
for (PStatement statement:mCompletionStatementList) {
if (statement->usageCount == 0) {
usageCount = mSymbolUsage.value(statement->fullName,0);
if (usageCount == 0)
@ -251,7 +335,7 @@ void CodeCompletionView::filterList(const QString &member)
thirdCount = usageCount;
}
}
for (PStatement statement:tmpList) {
for (PStatement statement:mCompletionStatementList) {
if (statement->usageCount == 0) {
statement->freqTop = 0;
} else if (statement->usageCount == topCount) {
@ -263,16 +347,338 @@ void CodeCompletionView::filterList(const QString &member)
}
}
if (mSortByScope) {
std::sort(tmpList.begin(),tmpList.end(),sortByScopeWithUsageComparator);
std::sort(mCompletionStatementList.begin(),
mCompletionStatementList.end(),
sortByScopeWithUsageComparator);
} else {
std::sort(tmpList.begin(),tmpList.end(),sortWithUsageComparator);
std::sort(mCompletionStatementList.begin(),
mCompletionStatementList.end(),
sortWithUsageComparator);
}
} else if (mSortByScope) {
std::sort(tmpList.begin(),tmpList.end(),sortByScopeComparator);
std::sort(mCompletionStatementList.begin(),
mCompletionStatementList.end(),
sortByScopeComparator);
} else {
std::sort(tmpList.begin(),tmpList.end(),defaultComparator);
std::sort(mCompletionStatementList.begin(),
mCompletionStatementList.end(),
defaultComparator);
}
// }
// }
}
void CodeCompletionView::getCompletionFor(const QString &fileName, const QString &phrase, int line)
{
if(!mParser)
return;
if (!mParser->enabled())
return;
if (!mParser->freeze())
return;
{
auto action = finally([this]{
mParser->unFreeze();
});
//C++ preprocessor directives
if (phrase.startsWith('#')) {
if (mShowKeywords) {
for (QString keyword:CppDirectives) {
PStatement statement = std::make_shared<Statement>();
statement->command = keyword;
statement->kind = StatementKind::skPreprocessor;
statement->fullName = keyword;
mFullCompletionStatementList.append(statement);
}
}
return;
}
//docstring tags (javadoc style)
if (phrase.startsWith('@')) {
if (mShowKeywords) {
for (QString keyword:JavadocTags) {
PStatement statement = std::make_shared<Statement>();
statement->command = keyword;
statement->kind = StatementKind::skKeyword;
statement->fullName = keyword;
mFullCompletionStatementList.append(statement);
}
}
return;
}
// Pulling off the same trick as in TCppParser.FindStatementOf, but ignore everything after last operator
int i = mParser->findLastOperator(phrase);
if (i < 0 ) { // don't have any scope prefix
if (mShowCodeIns) {
//add custom code templates
for (PCodeIns codeIn:mCodeInsList) {
PStatement statement = std::make_shared<Statement>();
statement->command = codeIn->prefix;
statement->kind = StatementKind::skUserCodeIn;
statement->fullName = codeIn->prefix;
mFullCompletionStatementList.append(statement);
}
}
if (mShowKeywords) {
//add keywords
if (mUseCppKeyword) {
for (QString keyword:CppKeywords.keys()) {
PStatement statement = std::make_shared<Statement>();
statement->command = keyword;
statement->kind = StatementKind::skKeyword;
statement->fullName = keyword;
mFullCompletionStatementList.append(statement);
}
} else {
for (QString keyword:CKeywords) {
PStatement statement = std::make_shared<Statement>();
statement->command = keyword;
statement->kind = StatementKind::skKeyword;
statement->fullName = keyword;
mFullCompletionStatementList.append(statement);
}
}
}
PStatement scopeStatement = mCurrentStatement;
// repeat until reach global
while (scopeStatement) {
//add members of current scope that not added before
if (scopeStatement->kind == StatementKind::skClass) {
addChildren(scopeStatement, fileName, -1);
} else {
addChildren(scopeStatement, fileName, line);
}
// add members of all usings (in current scope ) and not added before
for (QString namespaceName:scopeStatement->usingList) {
PStatementList namespaceStatementsList =
mParser->findNamespace(namespaceName);
if (!namespaceStatementsList)
continue;
for (PStatement namespaceStatement:*namespaceStatementsList) {
addChildren(namespaceStatement, fileName, line);
}
}
scopeStatement=scopeStatement->parentScope.lock();
}
// add all global members and not added before
addChildren(nullptr, fileName, line);
// add members of all fusings
mUsings = mParser->getFileUsings(fileName);
for (QString namespaceName:mUsings) {
PStatementList namespaceStatementsList =
mParser->findNamespace(namespaceName);
if (!namespaceStatementsList)
continue;
for (PStatement namespaceStatement:*namespaceStatementsList) {
addChildren(namespaceStatement, fileName, line);
}
}
} else { //we are in some statement's scope
MemberOperatorType opType=getOperatorType(phrase,i);
QString scopeName = phrase.mid(0,i);
if (opType == MemberOperatorType::otDColon) {
if (scopeName.isEmpty()) {
// start with '::', we only find in global
// add all global members and not added before
addChildren(nullptr, fileName, line);
return;
} else {
//assume the scope its a namespace
PStatementList namespaceStatementsList =
mParser->findNamespace(scopeName);
if (namespaceStatementsList) {
for (PStatement namespaceStatement:*namespaceStatementsList) {
addChildren(namespaceStatement, fileName, line);
}
return;
}
//namespace not found let's go on
}
}
PStatement parentTypeStatement;
PStatement statement = mParser->findStatementOf(
fileName, scopeName,mCurrentStatement,parentTypeStatement);
if (!statement)
return;
// find the most inner scope statement that has a name (not a block)
PStatement scopeTypeStatement = mCurrentStatement;
while (scopeTypeStatement && !isScopeTypeKind(scopeTypeStatement->kind)) {
scopeTypeStatement = scopeTypeStatement->parentScope.lock();
}
if (
(opType == MemberOperatorType::otArrow
|| opType == MemberOperatorType::otDot)
&& (
statement->kind == StatementKind::skVariable
|| statement->kind == StatementKind::skParameter
|| statement->kind == StatementKind::skFunction)
) {
// Get type statement of current (scope) statement
PStatement classTypeStatement;
PStatement parentScope = statement->parentScope.lock();
if ((statement->kind == StatementKind::skFunction)
&& parentScope
&& STLContainers.contains(parentScope->fullName)
&& STLElementMethods.contains(statement->command)){
// it's an element method of STL container
// we must find the type in the template parameter
// get the function's owner variable's definition
int lastI = mParser->findLastOperator(scopeName);
QString lastScopeName = scopeName.mid(0,lastI);
PStatement lastScopeStatement =
mParser->findStatementOf(
fileName, lastScopeName,
mCurrentStatement,parentTypeStatement);
if (!lastScopeStatement)
return;
QString typeName =
mParser->findFirstTemplateParamOf(
fileName,lastScopeStatement->type,
lastScopeStatement->parentScope.lock());
classTypeStatement = mParser->findTypeDefinitionOf(
fileName, typeName,
lastScopeStatement->parentScope.lock());
} else
classTypeStatement=mParser->findTypeDefinitionOf(
fileName, statement->type,parentTypeStatement);
if (!classTypeStatement)
return;
//is a smart pointer
if (STLPointers.contains(classTypeStatement->fullName)
&& (opType == MemberOperatorType::otArrow)) {
QString typeName= mParser->findFirstTemplateParamOf(
fileName,
statement->type,
parentScope);
classTypeStatement = mParser->findTypeDefinitionOf(
fileName,
typeName,
parentScope);
if (!classTypeStatement)
return;
}
//is a stl container operator[]
if (STLContainers.contains(classTypeStatement->fullName)
&& scopeName.endsWith(']')) {
QString typeName= mParser->findFirstTemplateParamOf(
fileName,
statement->type,
parentScope);
classTypeStatement = mParser->findTypeDefinitionOf(
fileName,
typeName,
parentScope);
if (!classTypeStatement)
return;
}
if (!isIncluded(classTypeStatement->fileName) &&
!isIncluded(classTypeStatement->definitionFileName))
return;
if ((classTypeStatement == scopeTypeStatement) || (statement->command == "this")) {
//we can use all members
addChildren(classTypeStatement,fileName,-1);
} else { // we can only use public members
const StatementMap& children = mParser->statementList().childrenStatements(classTypeStatement);
if (children.isEmpty())
return;
for (PStatement childStatement:children) {
if ((childStatement->classScope==StatementClassScope::scsPublic)
&& !(
childStatement->kind == StatementKind::skConstructor
|| childStatement->kind == StatementKind::skDestructor)
&& !mAddedStatements.contains(childStatement->command)) {
addStatement(childStatement,fileName,-1);
}
}
}
//todo friend
} else if ((opType == MemberOperatorType::otDColon)
&& (statement->kind == StatementKind::skEnumType)
&& (statement->kind == StatementKind::skEnumClassType)) {
//we can add all child enum definess
PStatement classTypeStatement = statement;
if (!isIncluded(classTypeStatement->fileName) &&
!isIncluded(classTypeStatement->definitionFileName))
return;
const StatementMap& children =
mParser->statementList().childrenStatements(classTypeStatement);
for (PStatement child:children) {
addStatement(child,fileName,line);
}
} else if ((opType == MemberOperatorType::otDColon)
&& (statement->kind == StatementKind::skClass)) {
PStatement classTypeStatement = statement;
if (!isIncluded(classTypeStatement->fileName) &&
!isIncluded(classTypeStatement->definitionFileName))
return;
if (classTypeStatement == scopeTypeStatement) {
//we can use all static members
const StatementMap& children =
mParser->statementList().childrenStatements(classTypeStatement);
for (PStatement childStatement: children) {
if (
(childStatement->isStatic)
|| (childStatement->kind == StatementKind::skTypedef
|| childStatement->kind == StatementKind::skClass
|| childStatement->kind == StatementKind::skEnum
|| childStatement->kind == StatementKind::skEnumClassType
|| childStatement->kind == StatementKind::skEnumType
)) {
addStatement(childStatement,fileName,-1);
}
}
} else {
// we can only use public static members
const StatementMap& children =
mParser->statementList().childrenStatements(classTypeStatement);
for (PStatement childStatement: children) {
if (
(childStatement->isStatic)
|| (childStatement->kind == StatementKind::skTypedef
|| childStatement->kind == StatementKind::skClass
|| childStatement->kind == StatementKind::skEnum
|| childStatement->kind == StatementKind::skEnumClassType
|| childStatement->kind == StatementKind::skEnumType
)) {
if (childStatement->classScope == StatementClassScope::scsPublic)
addStatement(childStatement,fileName,-1);
}
}
}
//todo friend
}
}
}
}
bool CodeCompletionView::isIncluded(const QString &fileName)
{
return mIncludedFiles.contains(fileName);
}
void CodeCompletionView::hideEvent(QHideEvent *event)
{
QMutexLocker locker(&mMutex);
mCompletionStatementList.clear();
mFullCompletionStatementList.clear();
mIncludedFiles.clear();
mUsings.clear();
mAddedStatements.clear();
QWidget::hideEvent(event);
}
CodeCompletionListView::CodeCompletionListView(QWidget *parent) : QListView(parent)

View File

@ -31,6 +31,10 @@ public:
~CodeCompletionView();
void setKeypressedCallback(const KeyPressedCallback &newKeypressedCallback);
void prepareSearch(const QString& phrase, const QString& filename, int line);
bool search(const QString& phrase, const QString& filename,
bool autoHideOnSingleResult);
private:
void addChildren(PStatement scopeStatement, const QString& fileName,
@ -52,10 +56,7 @@ private:
PStatement mCurrentStatement;
QSet<QString> mIncludedFiles;
QSet<QString> mUsings;
QString mIsIncludedCacheFileName;
bool mIsIncludedCacheResult;
QSet<QString> mAddedStatements;
bool mPreparing;
QString mPhrase;
QHash<QString,int> mSymbolUsage;
bool mRecordUsage;
@ -63,9 +64,13 @@ private:
bool mShowCodeIns;
bool mIgnoreCase;
QRecursiveMutex mMutex;
QString mParserSerialId;
bool mSortByScope;
bool mUseCppKeyword;
// QWidget interface
protected:
void hideEvent(QHideEvent *event) override;
};
#endif // CODECOMPLETIONVIEW_H