change: redesign code completion logic

This commit is contained in:
Roy Qu 2021-12-04 10:02:07 +08:00
parent f75f885129
commit 6f8096bc27
6 changed files with 197 additions and 481 deletions

View File

@ -1622,7 +1622,57 @@ void Editor::deleteToBOL()
ExecuteCommand(SynEditorCommand::ecDeleteBOL,QChar(),nullptr); ExecuteCommand(SynEditorCommand::ecDeleteBOL,QChar(),nullptr);
} }
QStringList Editor::getExpressionAtPositionForCompletion(const BufferCoord &pos) QStringList Editor::getOwnerExpressionAndMemberAtPositionForCompletion(
const BufferCoord &pos,
QString &memberOperator,
QStringList &memberExpression)
{
QStringList expression = getExpressionAtPositionForCompletion(pos);
//find position of the last member operator
int lastMemberOperatorPos = -1;
int currentMatchingLevel = 0;
QString matchingSignLeft;
QString matchingSignRight;
for (int i=0;i<expression.length();i++) {
QString token = expression[i];
if (currentMatchingLevel == 0) {
if (mParser->isMemberOperator(token)) {
lastMemberOperatorPos = i;
} else if (token == "(") {
matchingSignLeft = "(";
matchingSignRight = ")";
currentMatchingLevel++;
} else if (token == "[") {
matchingSignLeft = "[";
matchingSignRight = "]";
currentMatchingLevel++;
} else if (token == "<") {
matchingSignLeft = "<";
matchingSignRight = ">";
currentMatchingLevel++;
}
} else {
if (token == matchingSignLeft) {
currentMatchingLevel++;
} else if (token == matchingSignRight) {
currentMatchingLevel--;
}
}
}
if (lastMemberOperatorPos<0) {
memberOperator = "";
memberExpression = expression;
return QStringList();
} else {
memberOperator = expression[lastMemberOperatorPos];
memberExpression = expression.mid(lastMemberOperatorPos+1);
return expression.mid(0,lastMemberOperatorPos);
}
}
QStringList Editor::getExpressionAtPositionForCompletion(
const BufferCoord &pos)
{ {
QStringList result; QStringList result;
if (!highlighter()) if (!highlighter())
@ -1767,8 +1817,11 @@ QStringList Editor::getExpressionAtPositionForCompletion(const BufferCoord &pos)
case LastSymbolType::TildeSign: case LastSymbolType::TildeSign:
if (token =="::") { if (token =="::") {
lastSymbolType=LastSymbolType::ScopeResolutionOperator; lastSymbolType=LastSymbolType::ScopeResolutionOperator;
} else } else {
// "~" must appear after "::"
result.pop_front();
return result; return result;
}
break;; break;;
case LastSymbolType::Identifier: case LastSymbolType::Identifier:
if (token =="::") { if (token =="::") {
@ -1834,6 +1887,29 @@ QStringList Editor::getExpressionAtPositionForCompletion(const BufferCoord &pos)
return result; return result;
} }
QString Editor::getWordForCompletionSearch(const BufferCoord &pos,bool permitTilde)
{
QString result = "";
QString s;
s = lines()->getString(pos.Line - 1);
int len = s.length();
int wordBegin = pos.Char - 1 - 1; //BufferCoord::Char starts with 1
int wordEnd = pos.Char - 1 - 1;
while ((wordBegin >= 0) && (wordBegin<len)) {
if (isIdentChar(s[wordBegin])) {
wordBegin--;
} else if (permitTilde && s[wordBegin] == '~') { // allow destructor signs
wordBegin--;
} else
break;
}
// Get end result
return s.mid(wordBegin+1, wordEnd - wordBegin);
}
QChar Editor::getCurrentChar() QChar Editor::getCurrentChar()
{ {
if (lineText().length()<caretX()) if (lineText().length()<caretX())
@ -2592,11 +2668,27 @@ void Editor::showCompletion(const QString& preWord,bool autoComplete)
if (word.isEmpty()) { if (word.isEmpty()) {
//word=getWordAtPosition(this,caretXY(),pBeginPos,pEndPos, WordPurpose::wpCompletion); //word=getWordAtPosition(this,caretXY(),pBeginPos,pEndPos, WordPurpose::wpCompletion);
QStringList expression = getExpressionAtPositionForCompletion(caretXY()); QString memberOperator;
word = expression.join(""); QStringList memberExpression;
mCompletionPopup->prepareSearch(preWord, expression, mFilename, pBeginPos.Line); QStringList ownerExpression = getOwnerExpressionAndMemberAtPositionForCompletion(
caretXY(),
memberOperator,
memberExpression);
word = memberExpression.join("");
mCompletionPopup->prepareSearch(
preWord,
ownerExpression,
memberOperator,
memberExpression,
mFilename,
pBeginPos.Line);
} else { } else {
mCompletionPopup->prepareSearch(preWord, word, mFilename, pBeginPos.Line); QStringList memberExpression;
memberExpression.append(word);
mCompletionPopup->prepareSearch(preWord,
QStringList(),
"",
memberExpression, mFilename, pBeginPos.Line);
} }
// Filter the whole statement list // Filter the whole statement list
@ -2802,7 +2894,7 @@ bool Editor::onCompletionKeyPressed(QKeyEvent *event)
bool processed = false; bool processed = false;
if (!mCompletionPopup->isEnabled()) if (!mCompletionPopup->isEnabled())
return false; return false;
QString oldPhrase = mCompletionPopup->phrase(); QString oldPhrase = mCompletionPopup->memberPhrase();
WordPurpose purpose = WordPurpose::wpCompletion; WordPurpose purpose = WordPurpose::wpCompletion;
if (oldPhrase.startsWith('#')) { if (oldPhrase.startsWith('#')) {
purpose = WordPurpose::wpDirective; purpose = WordPurpose::wpDirective;
@ -2822,11 +2914,18 @@ bool Editor::onCompletionKeyPressed(QKeyEvent *event)
ExecuteCommand( ExecuteCommand(
SynEditorCommand::ecDeleteLastChar, SynEditorCommand::ecDeleteLastChar,
QChar(), nullptr); // Simulate backspace in editor QChar(), nullptr); // Simulate backspace in editor
phrase = getWordAtPosition(this,caretXY(), if (purpose == WordPurpose::wpCompletion) {
pBeginPos,pEndPos, phrase = getWordForCompletionSearch(caretXY(), mCompletionPopup->memberOperator()=="::");
purpose); } else
phrase = getWordAtPosition(this,caretXY(),
pBeginPos,pEndPos,
purpose);
mLastIdCharPressed = phrase.length(); mLastIdCharPressed = phrase.length();
mCompletionPopup->search(phrase, false); if (phrase.isEmpty()) {
mCompletionPopup->hide();
} else {
mCompletionPopup->search(phrase, false);
}
return true; return true;
case Qt::Key_Escape: case Qt::Key_Escape:
mCompletionPopup->hide(); mCompletionPopup->hide();
@ -2848,7 +2947,7 @@ bool Editor::onCompletionKeyPressed(QKeyEvent *event)
if (isIdentChar(ch)) { if (isIdentChar(ch)) {
setSelText(ch); setSelText(ch);
if (purpose == WordPurpose::wpCompletion) { if (purpose == WordPurpose::wpCompletion) {
phrase = getExpressionAtPositionForCompletion(caretXY()).join(""); phrase = getWordForCompletionSearch(caretXY(),mCompletionPopup->memberOperator()=="::");
} else } else
phrase = getWordAtPosition(this,caretXY(), phrase = getWordAtPosition(this,caretXY(),
pBeginPos,pEndPos, pBeginPos,pEndPos,
@ -2929,11 +3028,7 @@ bool Editor::onCompletionInputMethod(QInputMethodEvent *event)
return processed; return processed;
QString s=event->commitString(); QString s=event->commitString();
if (!s.isEmpty()) { if (!s.isEmpty()) {
QString phrase = getExpressionAtPositionForCompletion(caretXY()).join(""); QString phrase = getWordForCompletionSearch(caretXY(),mCompletionPopup->memberOperator()=="::");
// BufferCoord pBeginPos,pEndPos;
// QString phrase = getWordAtPosition(this,caretXY(),
// pBeginPos,pEndPos,
// WordPurpose::wpCompletion);
mLastIdCharPressed = phrase.length(); mLastIdCharPressed = phrase.length();
mCompletionPopup->search(phrase, false); mCompletionPopup->search(phrase, false);
return true; return true;

View File

@ -194,7 +194,11 @@ public:
void deleteToEOL(); void deleteToEOL();
void deleteToBOL(); void deleteToBOL();
QStringList getExpressionAtPositionForCompletion(const BufferCoord& pos); QStringList getOwnerExpressionAndMemberAtPositionForCompletion(
const BufferCoord& pos,
QString& memberOperator,
QStringList& memberExpression);
QString getWordForCompletionSearch(const BufferCoord& pos,bool permitTilde);
const PCppParser &parser(); const PCppParser &parser();
@ -256,6 +260,8 @@ private:
void popUserCodeInTabStops(); void popUserCodeInTabStops();
void onExportedFormatToken(PSynHighlighter syntaxHighlighter, int Line, int column, const QString& token, void onExportedFormatToken(PSynHighlighter syntaxHighlighter, int Line, int column, const QString& token,
PSynHighlighterAttribute &attr); PSynHighlighterAttribute &attr);
QStringList getExpressionAtPositionForCompletion(
const BufferCoord& pos);
private: private:
QByteArray mEncodingOption; // the encoding type set by the user QByteArray mEncodingOption; // the encoding type set by the user
QByteArray mFileEncoding; // the real encoding of the file (auto detected) QByteArray mFileEncoding; // the real encoding of the file (auto detected)

View File

@ -38,6 +38,11 @@ CppParser::CppParser(QObject *parent) : QObject(parent)
//mBlockBeginSkips; //mBlockBeginSkips;
//mBlockEndSkips; //mBlockEndSkips;
//mInlineNamespaceEndSkips; //mInlineNamespaceEndSkips;
mMemberOperators.insert(".");
mMemberOperators.insert("::");
mMemberOperators.insert("->");
mMemberOperators.insert("->*");
mMemberOperators.insert(".*");
} }
CppParser::~CppParser() CppParser::~CppParser()
@ -333,7 +338,7 @@ PStatement CppParser::findStatementOf(const QString &fileName, const QString &ph
if (!statement) if (!statement)
return PStatement(); return PStatement();
} }
qDebug()<<"-----";
PStatement lastScopeStatement; PStatement lastScopeStatement;
QString typeName; QString typeName;
PStatement typeStatement; PStatement typeStatement;
@ -3283,6 +3288,10 @@ PStatement CppParser::findMemberOfStatement(const QString &phrase,
QString s = phrase; QString s = phrase;
//remove [] //remove []
int p = phrase.indexOf('['); int p = phrase.indexOf('[');
if (p>=0)
s.truncate(p);
//remove ()
p = phrase.indexOf('(');
if (p>=0) if (p>=0)
s.truncate(p); s.truncate(p);
@ -3819,6 +3828,11 @@ void CppParser::updateSerialId()
mSerialId = QString("%1 %2").arg(mParserId).arg(mSerialCount); mSerialId = QString("%1 %2").arg(mParserId).arg(mSerialCount);
} }
bool CppParser::isMemberOperator(QString token)
{
return mMemberOperators.contains(token);
}
const StatementModel &CppParser::statementList() const const StatementModel &CppParser::statementList() const
{ {
return mStatementList; return mStatementList;

View File

@ -79,31 +79,15 @@ public:
void unFreeze(); // UnFree/UnLock (reparse while searching) void unFreeze(); // UnFree/UnLock (reparse while searching)
QSet<QString> scannedFiles(); QSet<QString> scannedFiles();
//void getSourcePair(const QString& fName, QString& CFile, QString& HFile);
// int suggestMemberInsertionLine(PStatement parentStatement,
// StatementClassScope Scope,
// bool addScopeStr);
// {
// function GetSystemHeaderFileName(const FileName: AnsiString): AnsiString; // <file.h>
// function GetProjectHeaderFileName(const FileName: AnsiString): AnsiString; // <file.h>
// function GetLocalHeaderFileName(const RelativeTo, FileName: AnsiString): AnsiString; // "file.h"
// }
//QString statementKindStr(StatementKind value);
//QString statementClassScopeStr(StatementClassScope value);
QString prettyPrintStatement(const PStatement& statement, const QString& filename, int line = -1); QString prettyPrintStatement(const PStatement& statement, const QString& filename, int line = -1);
/**
* @brief test if the token is a member/scope operator
* @param token
* @return true if it is, false if not
*/
bool isMemberOperator(QString token);
// StatementKind findKindOfStatementOf(const QString& fileName,
// const QString& phrase,
// int line);
// QString getHintFromStatement(const QString& fileName,
// const QString& phrase,
// int line);
bool enabled() const; bool enabled() const;
void setEnabled(bool newEnabled); void setEnabled(bool newEnabled);
@ -327,8 +311,6 @@ private:
bool isTypeStatement(StatementKind kind); bool isTypeStatement(StatementKind kind);
void updateSerialId(); void updateSerialId();
private: private:
int mParserId; int mParserId;
int mSerialCount; int mSerialCount;
@ -375,6 +357,7 @@ private:
GetFileStreamCallBack mOnGetFileStream; GetFileStreamCallBack mOnGetFileStream;
QMap<QString,SkipType> mCppKeywords; QMap<QString,SkipType> mCppKeywords;
QSet<QString> mCppTypeKeywords; QSet<QString> mCppTypeKeywords;
QSet<QString> mMemberOperators;
}; };
using PCppParser = std::shared_ptr<CppParser>; using PCppParser = std::shared_ptr<CppParser>;

View File

@ -45,12 +45,6 @@ CodeCompletionPopup::CodeCompletionPopup(QWidget *parent) :
mShowCodeSnippets = true; mShowCodeSnippets = true;
mIgnoreCase = false; mIgnoreCase = false;
mMemberOperators.insert(".");
mMemberOperators.insert("::");
mMemberOperators.insert("->");
mMemberOperators.insert("->*");
mMemberOperators.insert(".*");
} }
CodeCompletionPopup::~CodeCompletionPopup() CodeCompletionPopup::~CodeCompletionPopup()
@ -64,33 +58,13 @@ void CodeCompletionPopup::setKeypressedCallback(const KeyPressedCallback &newKey
mListView->setKeypressedCallback(newKeypressedCallback); mListView->setKeypressedCallback(newKeypressedCallback);
} }
void CodeCompletionPopup::prepareSearch(const QString& preWord,const QString &phrase, const QString &filename, int line) void CodeCompletionPopup::prepareSearch(
{ const QString &preWord,
QMutexLocker locker(&mMutex); const QStringList &ownerExpression,
if (!isEnabled()) const QString& memberOperator,
return; const QStringList& memberExpression,
mPhrase = phrase; const QString &filename,
//Screen.Cursor := crHourglass; int line)
QCursor oldCursor = cursor();
setCursor(Qt::CursorShape::WaitCursor);
if (preWord.isEmpty()) {
mIncludedFiles = mParser->getFileIncludes(filename);
getCompletionFor(filename,phrase,line);
if (mFullCompletionStatementList.isEmpty() && phrase.startsWith('~')) {
mPhrase = phrase.mid(1);
getCompletionFor(filename,mPhrase,line);
}
} else {
mPhrase = phrase;
getFullCompletionListFor(preWord);
}
setCursor(oldCursor);
}
void CodeCompletionPopup::prepareSearch(const QString &preWord, const QStringList &expression, const QString &filename, int line)
{ {
QMutexLocker locker(&mMutex); QMutexLocker locker(&mMutex);
if (!isEnabled()) if (!isEnabled())
@ -99,14 +73,11 @@ void CodeCompletionPopup::prepareSearch(const QString &preWord, const QStringLis
QCursor oldCursor = cursor(); QCursor oldCursor = cursor();
setCursor(Qt::CursorShape::WaitCursor); setCursor(Qt::CursorShape::WaitCursor);
mMemberPhrase = memberExpression.join("");
mMemberOperator = memberOperator;
if (preWord.isEmpty()) { if (preWord.isEmpty()) {
mIncludedFiles = mParser->getFileIncludes(filename); mIncludedFiles = mParser->getFileIncludes(filename);
getCompletionFor(expression,filename,line); getCompletionFor(ownerExpression,memberOperator,memberExpression, filename,line);
if (mFullCompletionStatementList.isEmpty() &&
(!expression.isEmpty() && expression.startsWith("~"))) {
getCompletionFor(expression.mid(1),filename,line);
}
} else { } else {
getFullCompletionListFor(preWord); getFullCompletionListFor(preWord);
} }
@ -118,12 +89,8 @@ bool CodeCompletionPopup::search(const QString &phrase, bool autoHideOnSingleRes
{ {
QMutexLocker locker(&mMutex); QMutexLocker locker(&mMutex);
mPhrase = phrase; mMemberPhrase = phrase;
if (phrase.isEmpty()) {
hide();
return false;
}
if (!isEnabled()) { if (!isEnabled()) {
hide(); hide();
return false; return false;
@ -132,24 +99,15 @@ bool CodeCompletionPopup::search(const QString &phrase, bool autoHideOnSingleRes
QCursor oldCursor = cursor(); QCursor oldCursor = cursor();
setCursor(Qt::CursorShape::WaitCursor); setCursor(Qt::CursorShape::WaitCursor);
// Sort here by member QString symbol = phrase;
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 // filter fFullCompletionStatementList to fCompletionStatementList
filterList(symbol); filterList(symbol);
//if can't find a destructor, maybe '~' is only an operator //if can't find a destructor, maybe '~' is only an operator
if (mCompletionStatementList.isEmpty() && phrase.startsWith('~')) { // if (mCompletionStatementList.isEmpty() && phrase.startsWith('~')) {
symbol = phrase.mid(1); // symbol = phrase.mid(1);
filterList(symbol); // filterList(symbol);
} // }
mModel->notifyUpdated(); mModel->notifyUpdated();
setCursor(oldCursor); setCursor(oldCursor);
@ -464,12 +422,19 @@ void CodeCompletionPopup::filterList(const QString &member)
// } // }
} }
void CodeCompletionPopup::getCompletionFor(const QString &fileName, const QString &phrase, int line) void CodeCompletionPopup::getCompletionFor(
const QStringList &ownerExpression,
const QString& memberOperator,
const QStringList& memberExpression,
const QString &fileName,
int line)
{ {
if(!mParser) if(!mParser)
return; return;
if (!mParser->enabled()) if (!mParser->enabled())
return; return;
if (memberOperator.isEmpty() && ownerExpression.isEmpty() && memberExpression.isEmpty())
return;
if (!mParser->freeze()) if (!mParser->freeze())
return; return;
@ -478,332 +443,9 @@ void CodeCompletionPopup::getCompletionFor(const QString &fileName, const QStrin
mParser->unFreeze(); mParser->unFreeze();
}); });
//C++ preprocessor directives if (memberOperator.isEmpty()) {
if (phrase.startsWith('#')) {
if (mShowKeywords) {
foreach (const QString& keyword, CppDirectives) {
addKeyword(keyword);
// PStatement statement = std::make_shared<Statement>();
// statement->command = keyword;
// statement->kind = StatementKind::skKeyword;
// statement->fullName = keyword;
// statement->usageCount = 0;
// statement->freqTop = 0;
// mFullCompletionStatementList.append(statement);
}
}
return;
}
//docstring tags (javadoc style)
if (phrase.startsWith('@')) {
if (mShowKeywords) {
foreach (const QString& keyword,JavadocTags) {
addKeyword(keyword);
// PStatement statement = std::make_shared<Statement>();
// statement->command = keyword;
// statement->kind = StatementKind::skKeyword;
// statement->fullName = keyword;
// statement->usageCount = 0;
// statement->freqTop = 0;
// 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 (mShowCodeSnippets) {
//add custom code templates
foreach (const PCodeSnippet& codeIn,mCodeSnippets) {
if (!codeIn->code.isEmpty()) {
PStatement statement = std::make_shared<Statement>();
statement->command = codeIn->prefix;
statement->value = codeIn->code;
statement->kind = StatementKind::skUserCodeSnippet;
statement->fullName = codeIn->prefix;
statement->usageCount = 0;
statement->freqTop = 0;
mFullCompletionStatementList.append(statement);
}
}
}
if (mShowKeywords) {
//add keywords
if (mUseCppKeyword) {
foreach (const QString& keyword,CppKeywords.keys()) {
addKeyword(keyword);
// PStatement statement = std::make_shared<Statement>();
// statement->command = keyword;
// statement->kind = StatementKind::skKeyword;
// statement->fullName = keyword;
// statement->usageCount = 0;
// statement->freqTop = 0;
// mFullCompletionStatementList.append(statement);
}
} else {
foreach (const QString& keyword,CKeywords) {
addKeyword(keyword);
// PStatement statement = std::make_shared<Statement>();
// statement->command = keyword;
// statement->kind = StatementKind::skKeyword;
// statement->fullName = keyword;
// statement->usageCount = 0;
// statement->freqTop = 0;
// 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
foreach (const QString& namespaceName,scopeStatement->usingList) {
PStatementList namespaceStatementsList =
mParser->findNamespace(namespaceName);
if (!namespaceStatementsList)
continue;
foreach (const 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);
foreach (const QString& namespaceName, mUsings) {
PStatementList namespaceStatementsList =
mParser->findNamespace(namespaceName);
if (!namespaceStatementsList)
continue;
foreach (const 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) {
foreach (const 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;
foreach (const 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);
foreach (const 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);
foreach (const 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);
foreach (const 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
}
}
}
}
void CodeCompletionPopup::getCompletionFor(const QStringList &expression, const QString &fileName, int line)
{
if (expression.isEmpty())
return;
if(!mParser)
return;
if (!mParser->enabled())
return;
if (!mParser->freeze())
return;
{
auto action = finally([this]{
mParser->unFreeze();
});
if (expression.length()==1 ) {
QString phrase = expression.back();
//C++ preprocessor directives //C++ preprocessor directives
if (phrase.startsWith('#')) { if (mMemberPhrase.startsWith('#')) {
if (mShowKeywords) { if (mShowKeywords) {
foreach (const QString& keyword, CppDirectives) { foreach (const QString& keyword, CppDirectives) {
addKeyword(keyword); addKeyword(keyword);
@ -813,7 +455,7 @@ void CodeCompletionPopup::getCompletionFor(const QStringList &expression, const
} }
//docstring tags (javadoc style) //docstring tags (javadoc style)
if (phrase.startsWith('@')) { if (mMemberPhrase.startsWith('@')) {
if (mShowKeywords) { if (mShowKeywords) {
foreach (const QString& keyword,JavadocTags) { foreach (const QString& keyword,JavadocTags) {
addKeyword(keyword); addKeyword(keyword);
@ -821,41 +463,7 @@ void CodeCompletionPopup::getCompletionFor(const QStringList &expression, const
} }
return; return;
} }
}
//find position of the last member operator
int lastMemberOperatorPos = -1;
int currentMatchingLevel = 0;
QString matchingSignLeft;
QString matchingSignRight;
for (int i=0;i<expression.length();i++) {
QString token = expression[i];
if (currentMatchingLevel == 0) {
if (mMemberOperators.contains(token)) {
lastMemberOperatorPos = i;
} else if (token == "(") {
matchingSignLeft = "(";
matchingSignRight = ")";
currentMatchingLevel++;
} else if (token == "[") {
matchingSignLeft = "[";
matchingSignRight = "]";
currentMatchingLevel++;
} else if (token == "<") {
matchingSignLeft = "<";
matchingSignRight = ">";
currentMatchingLevel++;
}
} else {
if (token == matchingSignLeft) {
currentMatchingLevel++;
} else if (token == matchingSignRight) {
currentMatchingLevel--;
}
}
}
if (lastMemberOperatorPos<0) {
//the identifier to be completed is not a member of variable/class //the identifier to be completed is not a member of variable/class
if (mShowCodeSnippets) { if (mShowCodeSnippets) {
//add custom code templates //add custom code templates
@ -926,15 +534,12 @@ void CodeCompletionPopup::getCompletionFor(const QStringList &expression, const
} else { } else {
//the identifier to be completed is a member of variable/class //the identifier to be completed is a member of variable/class
QString memberOperator = expression[lastMemberOperatorPos]; if (memberOperator == "::" && ownerExpression.isEmpty()) {
if (memberOperator == "::" && lastMemberOperatorPos==0) {
// start with '::', we only find in global // start with '::', we only find in global
// add all global members and not added before // add all global members and not added before
addChildren(nullptr, fileName, line); addChildren(nullptr, fileName, line);
return; return;
} }
QStringList ownerExpression = expression.mid(0,lastMemberOperatorPos);
QStringList memberExpression = expression.mid(lastMemberOperatorPos+1);
if (memberExpression.length()==2 && memberExpression.front()!="~") if (memberExpression.length()==2 && memberExpression.front()!="~")
return; return;
if (memberExpression.length()>2) if (memberExpression.length()>2)
@ -947,9 +552,9 @@ void CodeCompletionPopup::getCompletionFor(const QStringList &expression, const
scopeName, scopeName,
mCurrentStatement, mCurrentStatement,
parentTypeStatement); parentTypeStatement);
// qDebug()<<scopeName; qDebug()<<scopeName;
// qDebug()<<memberOperator; qDebug()<<memberOperator;
// qDebug()<<memberExpression; qDebug()<<memberExpression;
if(!ownerStatement ) { if(!ownerStatement ) {
// qDebug()<<"not found!"; // qDebug()<<"not found!";
return; return;
@ -1162,6 +767,11 @@ bool CodeCompletionPopup::isIncluded(const QString &fileName)
return mIncludedFiles.contains(fileName); return mIncludedFiles.contains(fileName);
} }
const QString &CodeCompletionPopup::memberOperator() const
{
return mMemberOperator;
}
const QList<PCodeSnippet> &CodeCompletionPopup::codeSnippets() const const QList<PCodeSnippet> &CodeCompletionPopup::codeSnippets() const
{ {
return mCodeSnippets; return mCodeSnippets;
@ -1177,9 +787,9 @@ void CodeCompletionPopup::setColors(const std::shared_ptr<QHash<StatementKind, s
mColors = newColors; mColors = newColors;
} }
const QString &CodeCompletionPopup::phrase() const const QString &CodeCompletionPopup::memberPhrase() const
{ {
return mPhrase; return mMemberPhrase;
} }
void CodeCompletionPopup::showEvent(QShowEvent *) void CodeCompletionPopup::showEvent(QShowEvent *)

View File

@ -31,8 +31,12 @@ public:
~CodeCompletionPopup(); ~CodeCompletionPopup();
void setKeypressedCallback(const KeyPressedCallback &newKeypressedCallback); void setKeypressedCallback(const KeyPressedCallback &newKeypressedCallback);
void prepareSearch(const QString& preWord, const QString& phrase, const QString& filename, int line); void prepareSearch(const QString& preWord,
void prepareSearch(const QString& preWord, const QStringList & expression, const QString& filename, int line); const QStringList & ownerExpression,
const QString& memberOperator,
const QStringList& memberExpression,
const QString& filename,
int line);
bool search(const QString& phrase, bool autoHideOnSingleResult); bool search(const QString& phrase, bool autoHideOnSingleResult);
PStatement selectedStatement(); PStatement selectedStatement();
@ -68,19 +72,24 @@ public:
void setCurrentStatement(const PStatement &newCurrentStatement); void setCurrentStatement(const PStatement &newCurrentStatement);
const std::shared_ptr<QHash<StatementKind, std::shared_ptr<ColorSchemeItem> > >& colors() const; const std::shared_ptr<QHash<StatementKind, std::shared_ptr<ColorSchemeItem> > >& colors() const;
void setColors(const std::shared_ptr<QHash<StatementKind, std::shared_ptr<ColorSchemeItem> > > &newColors); void setColors(const std::shared_ptr<QHash<StatementKind, std::shared_ptr<ColorSchemeItem> > > &newColors);
const QString &memberPhrase() const;
const QList<PCodeSnippet> &codeSnippets() const;
void setCodeSnippets(const QList<PCodeSnippet> &newCodeSnippets);
private: private:
void addChildren(PStatement scopeStatement, const QString& fileName, void addChildren(PStatement scopeStatement, const QString& fileName,
int line); int line);
void addStatement(PStatement statement, const QString& fileName, int line); void addStatement(PStatement statement, const QString& fileName, int line);
void filterList(const QString& member); void filterList(const QString& member);
void getCompletionFor(const QString& fileName,const QString& phrase, int line); void getCompletionFor(
void getCompletionFor(const QStringList& expression, const QString& fileName,int line); const QStringList& ownerExpression,
const QString& memberOperator,
const QStringList& memberExpression,
const QString& fileName,
int line);
void getFullCompletionListFor(const QString& preWord); void getFullCompletionListFor(const QString& preWord);
void addKeyword(const QString& keyword); void addKeyword(const QString& keyword);
bool isIncluded(const QString& fileName); bool isIncluded(const QString& fileName);
private: private:
QSet<QString> mMemberOperators;
CodeCompletionListView * mListView; CodeCompletionListView * mListView;
CodeCompletionListModel* mModel; CodeCompletionListModel* mModel;
QList<PCodeSnippet> mCodeSnippets; //(Code template list) QList<PCodeSnippet> mCodeSnippets; //(Code template list)
@ -90,7 +99,8 @@ private:
QSet<QString> mIncludedFiles; QSet<QString> mIncludedFiles;
QSet<QString> mUsings; QSet<QString> mUsings;
QSet<QString> mAddedStatements; QSet<QString> mAddedStatements;
QString mPhrase; QString mMemberPhrase;
QString mMemberOperator;
QRecursiveMutex mMutex; QRecursiveMutex mMutex;
std::shared_ptr<QHash<StatementKind, std::shared_ptr<ColorSchemeItem> > > mColors; std::shared_ptr<QHash<StatementKind, std::shared_ptr<ColorSchemeItem> > > mColors;
@ -113,9 +123,7 @@ protected:
// QObject interface // QObject interface
public: public:
bool event(QEvent *event) override; bool event(QEvent *event) override;
const QString &phrase() const; const QString &memberOperator() const;
const QList<PCodeSnippet> &codeSnippets() const;
void setCodeSnippets(const QList<PCodeSnippet> &newCodeSnippets);
}; };
#endif // CODECOMPLETIONPOPUP_H #endif // CODECOMPLETIONPOPUP_H