diff --git a/RedPandaIDE/editor.cpp b/RedPandaIDE/editor.cpp index 220f4e0d..a5e81bae 100644 --- a/RedPandaIDE/editor.cpp +++ b/RedPandaIDE/editor.cpp @@ -1315,57 +1315,264 @@ void Editor::showCompletion(bool autoComplete) BufferCoord{caretX() - 1, caretY()}, s, tokenFinished,tokenType, attr)) { if (tokenType == SynHighlighterTokenType::PreprocessDirective) {//Preprocessor - word = getWordAtPosition(this, caretXY(),pBeginPos,pEndPos, wpDirective); -if not StartsStr('#',word) then begin - ShowTabnineCompletion; - Exit; -end; - end else if attr = dmMain.Cpp.CommentAttri then begin //Comment, javadoc tag - word:=GetWordAtPosition(fText, fText.CaretXY,pBeginPos,pEndPos, wpJavadoc); - if not StartsStr('@',word) then begin - Exit; - end; - end else if (attr <> fText.Highlighter.SymbolAttribute) and - (attr <> fText.Highlighter.WhitespaceAttribute) and - (attr <> fText.Highlighter.IdentifierAttribute) then begin - Exit; - end; + word = getWordAtPosition(caretXY(),pBeginPos,pEndPos, WordPurpose::wpDirective); + if (!word.startsWith('#')) { + //showTabnineCompletion(); + return; + } + } else if (tokenType == SynHighlighterTokenType::Comment) { //Comment, javadoc tag + word = getWordAtPosition(caretXY(),pBeginPos,pEndPos, WordPurpose::wpJavadoc); + if (!word.startsWith('@')) { + return; + } + } else if ( + (tokenType != SynHighlighterTokenType::Symbol) && + (tokenType != SynHighlighterTokenType::Space) && + (tokenType != SynHighlighterTokenType::Identifier) + ) { + return; + } } // Position it at the top of the next line - P := fText.RowColumnToPixels(fText.DisplayXY); - Inc(P.Y, fText.LineHeight + 2); - fCompletionBox.Position := fText.ClientToScreen(P); + QPoint p = RowColumnToPixels(displayXY()); + p+=QPoint(0,textHeight()+2); + mCompletionPopup->move(mapToGlobal(p)); - fCompletionBox.RecordUsage := devCodeCompletion.RecordUsage; - fCompletionBox.SortByScope := devCodeCompletion.SortByScope; - fCompletionBox.ShowKeywords := devCodeCompletion.ShowKeywords; - fCompletionBox.ShowCodeIns := devCodeCompletion.ShowCodeIns; - fCompletionBox.IgnoreCase := devCodeCompletion.IgnoreCase; - fCompletionBox.CodeInsList := dmMain.CodeInserts.ItemList; - fCompletionBox.SymbolUsage := dmMain.SymbolUsage; - fCompletionBox.ShowCount := devCodeCompletion.MaxCount; +// fCompletionBox.RecordUsage := devCodeCompletion.RecordUsage; +// fCompletionBox.SortByScope := devCodeCompletion.SortByScope; +// fCompletionBox.ShowKeywords := devCodeCompletion.ShowKeywords; +// fCompletionBox.ShowCodeIns := devCodeCompletion.ShowCodeIns; +// fCompletionBox.IgnoreCase := devCodeCompletion.IgnoreCase; +// fCompletionBox.CodeInsList := dmMain.CodeInserts.ItemList; +// fCompletionBox.SymbolUsage := dmMain.SymbolUsage; +// fCompletionBox.ShowCount := devCodeCompletion.MaxCount; //Set Font size; - fCompletionBox.FontSize := fText.Font.Size; + mCompletionPopup->setFont(font()); // Redirect key presses to completion box if applicable - fCompletionBox.OnKeyPress := CompletionKeyPress; - fCompletionBox.OnKeyDown := CompletionKeyDown; - fCompletionBox.Parser := fParser; - fCompletionBox.useCppKeyword := fUseCppSyntax; - fCompletionBox.Show; + //todo: + mCompletionPopup->setKeypressedCallback([this](QKeyEvent *event)->bool{ + return onCompletionKeyPressed(event); + }); + mCompletionPopup->setParser(mParser); + //todo: + //mCompletionPopup->setUseCppKeyword(mUseCppSyntax); + mCompletionPopup->show(); // Scan the current function body - fCompletionBox.CurrentStatement := fParser.FindAndScanBlockAt(fFileName, fText.CaretY); + mCompletionPopup->setCurrentStatement( + mParser->findAndScanBlockAt(mFilename, caretY()) + ); - if word='' then - word:=GetWordAtPosition(fText, fText.CaretXY,pBeginPos,pEndPos, wpCompletion); + if (word.isEmpty()) + word=getWordAtPosition(caretXY(),pBeginPos,pEndPos, WordPurpose::wpCompletion); //if not fCompletionBox.Visible then - fCompletionBox.PrepareSearch(word, fFileName,pBeginPos.Line); + mCompletionPopup->prepareSearch(word, mFilename, pBeginPos.Line); // Filter the whole statement list - if fCompletionBox.Search(word, fFileName, autoComplete) then //only one suggestion and it's not input while typing - CompletionInsert(); // if only have one suggestion, just use it + if (mCompletionPopup->search(word, autoComplete)) { //only one suggestion and it's not input while typing + completionInsert(); // if only have one suggestion, just use it + } +} + +void Editor::showHeaderCompletion(bool autoComplete) +{ + //todo: +} + +QString Editor::getWordAtPosition(const BufferCoord &p, BufferCoord &pWordBegin, BufferCoord &pWordEnd, WordPurpose purpose) +{ + QString result = ""; + QString s; + if ((p.Line >= 1) && (p.Line <= lines()->count())) { + s = lines()->getString(p.Line - 1); + int len = s.length(); + + int wordBegin = p.Char - 1 - 1; //BufferCoord::Char starts with 1 + int wordEnd = p.Char - 1 - 1; + + // Copy forward until end of word + if (purpose == WordPurpose::wpEvaluation + || purpose == WordPurpose::wpInformation) { + while (wordEnd + 1 < len) do { + if ((purpose == WordPurpose::wpEvaluation) + && (s[wordEnd + 1] == '[')) { + if (!findComplement(s, '[', ']', wordEnd, 1)) + break; + } else if (isIdentChar(s[wordEnd + 1])) { + wordEnd++; + } else + break; + } + } + + // Copy backward until # + if (purpose == WordPurpose::wpDirective) { + while ((wordBegin >= 0) && (wordBegin < len)) { + if (isIdentChar(s[wordBegin])) + wordBegin--; + else if (s[wordBegin] == '#') { + wordBegin--; + break; + } else + break; + } + } + + // Copy backward until @ + if (purpose == WordPurpose::wpJavadoc) { + while ((wordBegin >= 0) && (wordBegin < len)) { + if (isIdentChar(s[wordBegin])) + wordBegin--; + else if (s[wordBegin] == '@') { + wordBegin--; + break; + } else + break; + } + } + + // Copy backward until begin of path + if (purpose == wpHeaderCompletion) { + while ((wordBegin >= 0) && (wordBegin < len)) { + if (isIdentChar(s[wordBegin])) + wordBegin--; + else if (s[wordBegin] == '/' + || s[wordBegin] == '\\' + || s[wordBegin] == '.') { + wordBegin--; + break; + } else + break; + } + } + + if (purpose == WordPurpose::wpHeaderCompletionStart) { + while ((wordBegin >= 0) && (wordBegin < len)) { + if (s[wordBegin] == '"' + || s[wordBegin] == '<']) { + wordBegin--; + break; + } else if (s[wordBegin] == '/' + || s[wordBegin] == '\\' + || s[wordBegin] == '.') { + wordBegin--; + } else if (isIdentChar(s[wordBegin])) + wordBegin--; + else + break; + } + } + + // Copy backward until begin of word + if (purpose == WordPurpose::wpCompletion + || purpose == WordPurpose::wpEvaluation + || purpose == WordPurpose::wpInformation) { + while ((wordBegin >= 0) && (wordBegin < len)) { + if (s[wordBegin] == ']') then { + if (!findComplement(s, ']', '[', wordBegin, -1)) + break + else + wordBegin++; // step over [ + } else if (isIdentChar(s[wordBegin])) { + wordBegin--; + } else if (s[wordBegin] == '.' + || s[wordBegin] == ':' + || s[wordBegin] == '~') { // allow destructor signs + wordBegin--; + } else if ( + (s[wordBegin] == '>') + && (wordBegin+2', '<', wordBegin, -1)) + break; + else + wordBegin--; // step over > + } else if ((wordBegin-1 >= 0) + && (s[wordBegin - 1] == '-') + && (s[wordBegin] == '>')) { + wordBegin-=2; + } else if ((wordBegin-1 >= 0) + && (s[wordBegin - 1] == ':') + && (s[wordBegin] == ':')) { + wordBegin-=2; + } else if ((wordBegin > 0) + && (s[wordBegin] == ')')) { + if (!findComplement(s, ')', '(', WordBegin, -1)) + break; + else + wordBegin--; // step over ( + } else + break; + } + } + } + + // Get end result + result = s.mid(wordBegin + 1, wordEnd - wordBegin); + pWordBegin.Line = p.Line; + pWordBegin.Char = wordBegin+1; + pWordEnd.Line = p.Line; + pWordEnd.Char = wordEnd; + if (!result.isEmpty() && ( + result[0] in ['.','-']) + and (Purpose in [wpCompletion, wpEvaluation, wpInformation]) then begin + i:=WordBegin; + line:=p.Line; + while line>=1 do begin + while i>=1 do begin + if S[i] in [' ',#9] then + dec(i) + else + break; + end; + if i<1 then begin + dec(line); + if (line>=1) then begin + S:=editor.Lines[line-1]; + i:=Length(s); + continue; + end else + break; + end else begin + HighlightPos.Line := line; + HighlightPos.Char := i+1; + Result := GetWordAtPosition(editor,highlightPos,pWordBegin,pDummy,Purpose)+Result; + break; + end; + end; + end; + + // Strip function parameters + while true do begin + ParamBegin := Pos('(', Result); + if ParamBegin > 0 then begin + ParamEnd := ParamBegin; + if (ParamBegin=1) and FindComplement(Result, '(', ')', ParamEnd, 1) + and (ParamEnd = Length(Result)) then begin + Delete(Result,ParamEnd,1); + Delete(Result,ParamBegin,1); + continue; + end else begin + ParamEnd := ParamBegin; + if FindComplement(Result, '(', ')', ParamEnd, 1) then begin + Delete(Result, ParamBegin, ParamEnd - ParamBegin + 1); + end else + break; + end; + end else + break; + end; + + ParamBegin := 1; + while (ParamBegin <= Length(Result)) and (Result[ParamBegin] = '*') do begin + inc(ParamBegin); + end; + Delete(Result,1,ParamBegin-1); + } const PCppParser &Editor::parser() const diff --git a/RedPandaIDE/editor.h b/RedPandaIDE/editor.h index b3c03373..9f06cbbe 100644 --- a/RedPandaIDE/editor.h +++ b/RedPandaIDE/editor.h @@ -125,6 +125,10 @@ public: bool hasBreakpoint(int line); void removeBreakpointFocus(); void setActiveBreakpointFocus(int Line, bool setFocus=true); + QString getWordAtPosition(const BufferCoord& p, + BufferCoord& pWordBegin, + BufferCoord& pWordEnd, + WordPurpose purpose); const PCppParser &parser() const; @@ -158,6 +162,7 @@ private: void reparse(); void showCompletion(bool autoComplete); + void showHeaderCompletion(bool autoComplete); private: static int newfileCount; @@ -182,6 +187,7 @@ private: PCppParser mParser; std::shared_ptr mCompletionPopup; int mLastIdCharPressed; + bool mUseCppSyntax; // QWidget interface protected: diff --git a/RedPandaIDE/widgets/codecompletionview.cpp b/RedPandaIDE/widgets/codecompletionview.cpp index 7065ce87..50f0b2f4 100644 --- a/RedPandaIDE/widgets/codecompletionview.cpp +++ b/RedPandaIDE/widgets/codecompletionview.cpp @@ -669,6 +669,106 @@ bool CodeCompletionView::isIncluded(const QString &fileName) return mIncludedFiles.contains(fileName); } +const PStatement &CodeCompletionView::currentStatement() const +{ + return mCurrentStatement; +} + +void CodeCompletionView::setCurrentStatement(const PStatement &newCurrentStatement) +{ + mCurrentStatement = newCurrentStatement; +} + +bool CodeCompletionView::useCppKeyword() const +{ + return mUseCppKeyword; +} + +void CodeCompletionView::setUseCppKeyword(bool newUseCppKeyword) +{ + mUseCppKeyword = newUseCppKeyword; +} + +bool CodeCompletionView::sortByScope() const +{ + return mSortByScope; +} + +void CodeCompletionView::setSortByScope(bool newSortByScope) +{ + mSortByScope = newSortByScope; +} + +bool CodeCompletionView::ignoreCase() const +{ + return mIgnoreCase; +} + +void CodeCompletionView::setIgnoreCase(bool newIgnoreCase) +{ + mIgnoreCase = newIgnoreCase; +} + +bool CodeCompletionView::showCodeIns() const +{ + return mShowCodeIns; +} + +void CodeCompletionView::setShowCodeIns(bool newShowCodeIns) +{ + mShowCodeIns = newShowCodeIns; +} + +bool CodeCompletionView::showKeywords() const +{ + return mShowKeywords; +} + +void CodeCompletionView::setShowKeywords(bool newShowKeywords) +{ + mShowKeywords = newShowKeywords; +} + +bool CodeCompletionView::recordUsage() const +{ + return mRecordUsage; +} + +void CodeCompletionView::setRecordUsage(bool newRecordUsage) +{ + mRecordUsage = newRecordUsage; +} + +bool CodeCompletionView::onlyGlobals() const +{ + return mOnlyGlobals; +} + +void CodeCompletionView::setOnlyGlobals(bool newOnlyGlobals) +{ + mOnlyGlobals = newOnlyGlobals; +} + +int CodeCompletionView::showCount() const +{ + return mShowCount; +} + +void CodeCompletionView::setShowCount(int newShowCount) +{ + mShowCount = newShowCount; +} + +const PCppParser &CodeCompletionView::parser() const +{ + return mParser; +} + +void CodeCompletionView::setParser(const PCppParser &newParser) +{ + mParser = newParser; +} + void CodeCompletionView::hideEvent(QHideEvent *event) { QMutexLocker locker(&mMutex); @@ -710,7 +810,7 @@ CodeCompletionListModel::CodeCompletionListModel(StatementList *statements, QObj } -int CodeCompletionListModel::rowCount(const QModelIndex &parent) const +int CodeCompletionListModel::rowCount(const QModelIndex &) const { return mStatements->count(); } diff --git a/RedPandaIDE/widgets/codecompletionview.h b/RedPandaIDE/widgets/codecompletionview.h index aac7937a..6691ca41 100644 --- a/RedPandaIDE/widgets/codecompletionview.h +++ b/RedPandaIDE/widgets/codecompletionview.h @@ -47,6 +47,36 @@ public: bool search(const QString& phrase, bool autoHideOnSingleResult); + const PCppParser &parser() const; + void setParser(const PCppParser &newParser); + + int showCount() const; + void setShowCount(int newShowCount); + + bool onlyGlobals() const; + void setOnlyGlobals(bool newOnlyGlobals); + + bool recordUsage() const; + void setRecordUsage(bool newRecordUsage); + + bool showKeywords() const; + void setShowKeywords(bool newShowKeywords); + + bool showCodeIns() const; + void setShowCodeIns(bool newShowCodeIns); + + bool ignoreCase() const; + void setIgnoreCase(bool newIgnoreCase); + + bool sortByScope() const; + void setSortByScope(bool newSortByScope); + + bool useCppKeyword() const; + void setUseCppKeyword(bool newUseCppKeyword); + + const PStatement ¤tStatement() const; + void setCurrentStatement(const PStatement &newCurrentStatement); + private: void addChildren(PStatement scopeStatement, const QString& fileName, int line); @@ -59,26 +89,26 @@ private: CodeCompletionListModel* mModel; QList mCodeInsList; //(Code template list) //QList mCodeInsStatements; //temporary (user code template) statements created when show code suggestion - PCppParser mParser; StatementList mFullCompletionStatementList; StatementList mCompletionStatementList; - int mShowCount; - bool mOnlyGlobals; - PStatement mCurrentStatement; QSet mIncludedFiles; QSet mUsings; QSet mAddedStatements; QString mPhrase; QHash mSymbolUsage; + QRecursiveMutex mMutex; + + PCppParser mParser; + PStatement mCurrentStatement; + int mShowCount; + bool mOnlyGlobals; bool mRecordUsage; bool mShowKeywords; bool mShowCodeIns; bool mIgnoreCase; - QRecursiveMutex mMutex; bool mSortByScope; bool mUseCppKeyword; - // QWidget interface protected: void hideEvent(QHideEvent *event) override;