diff --git a/NEWS.md b/NEWS.md index 49c8cab3..f3587685 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,6 +8,8 @@ Version 0.6.0 - add: about dialog - implement: correctly recognize clang (msys2 build) - enhancement: don't add encoding options when using clang to compile (clang only support utf-8) + - enhancement: find occurence in project + - implement: refactor in file Version 0.5.0 - enhancement: support C++ using type alias; diff --git a/RedPandaIDE/cpprefacter.cpp b/RedPandaIDE/cpprefacter.cpp index abd259fc..a48d14aa 100644 --- a/RedPandaIDE/cpprefacter.cpp +++ b/RedPandaIDE/cpprefacter.cpp @@ -4,7 +4,9 @@ #include "editor.h" #include "editorlist.h" #include +#include #include "HighlighterManager.h" +#include "project.h" CppRefacter::CppRefacter(QObject *parent) : QObject(parent) { @@ -30,24 +32,80 @@ bool CppRefacter::findOccurence(Editor *editor, const BufferCoord &pos) if (!statement) return false; - PSearchResults results = pMainWindow->searchResultModel()->addSearchResults( - phrase, - editor->filename(), - pos.Line - ); - - - PSearchResultTreeItem item = findOccurenceInFile( - editor->filename(), - statement, - editor->parser()); - if (item && !(item->results.isEmpty())) { - results->results.append(item); + std::shared_ptr project = pMainWindow->project(); + if (editor->inProject() && project) { + foreach (const PProjectUnit& unit, project->units()) { + if (isCfile(unit->fileName()) || isHfile(unit->fileName())) { + findOccurenceInFile( + phrase, + unit->fileName(), + statement, + pos.Line, + editor->parser()); + } + } + } else { + findOccurenceInFile( + phrase, + editor->filename(), + statement, + pos.Line, + editor->parser()); } pMainWindow->searchResultModel()->notifySearchResultsUpdated(); return true; } +static QString fullParentName(PStatement statement) { + PStatement parent = statement->parentScope.lock(); + if (parent) { + return parent->fullName; + } else { + return ""; + } +} +void CppRefacter::renameSymbol(Editor *editor, const BufferCoord &pos, const QString &word, const QString &newWord) +{ + if (!editor->parser()->freeze()) + return; + auto action = finally([&editor]{ + editor->parser()->unFreeze(); + }); + // get full phrase (such as s.name instead of name) + BufferCoord pBeginPos,pEndPos; + QString phrase = getWordAtPosition(editor,pos,pBeginPos,pEndPos,Editor::WordPurpose::wpInformation); + // Find it's definition + PStatement oldStatement = editor->parser()->findStatementOf( + editor->filename(), + phrase, + pos.Line); + QString oldScope = fullParentName(oldStatement); + // definition of the symbol not found + if (!oldStatement) + return; + // found but not in this file + if (editor->filename() != oldStatement->fileName + || editor->filename() != oldStatement->definitionFileName) { + QMessageBox::critical(editor, + tr("Rename Symbol Error"), + tr("Can't rename symbols not defined in this file.")); + return; + } + + QString newPhrase = phrase.mid(0,phrase.length()-word.length()) + newWord; + PStatement newStatement = editor->parser()->findStatementOf( + editor->filename(), + newPhrase, + pos.Line); + if (newStatement && fullParentName(newStatement) == oldScope) { + QMessageBox::critical(editor, + tr("Rename Symbol Error"), + tr("New symbol already exists!")); + return; + } + renameSymbolInFile(editor->filename(),oldStatement,word,newWord, editor->parser()); +} + PSearchResultTreeItem CppRefacter::findOccurenceInFile( const QString &filename, const PStatement &statement, @@ -114,3 +172,91 @@ PSearchResultTreeItem CppRefacter::findOccurenceInFile( } return parentItem; } + +void CppRefacter::renameSymbolInFile(const QString &filename, const PStatement &statement, const QString &word, const QString &newWord, const PCppParser &parser) +{ + QStringList buffer; + SynEdit editor; + if (pMainWindow->editorList()->getContentFromOpenedEditor( + filename,buffer)){ + editor.lines()->setContents(buffer); + } else { + QByteArray encoding; + editor.lines()->LoadFromFile(filename,ENCODING_AUTO_DETECT,encoding); + } + QStringList newContents; + editor.setHighlighter(HighlighterManager().getCppHighlighter()); + int posY = 0; + while (posY < editor.lines()->count()) { + QString line = editor.lines()->getString(posY); + if (line.isEmpty()) { + posY++; + continue; + } + + if (posY == 0) { + editor.highlighter()->resetState(); + } else { + editor.highlighter()->setState( + editor.lines()->ranges(posY-1)); + } + editor.highlighter()->setLine(line,posY); + QString newLine; + while (!editor.highlighter()->eol()) { + int start = editor.highlighter()->getTokenPos() + 1; + QString token = editor.highlighter()->getToken(); + if (token == statement->command) { + //same name symbol , test if the same statement; + BufferCoord p,pBeginPos,pEndPos; + p.Line = posY+1; + p.Char = start; + QString phrase = getWordAtPosition(&editor, p, pBeginPos,pEndPos, + Editor::WordPurpose::wpInformation); + PStatement tokenStatement = parser->findStatementOf( + filename, + phrase, p.Line); + if (tokenStatement + && (tokenStatement->line == statement->line) + && (tokenStatement->fileName == statement->fileName)) { + token = newWord; + } + } + newLine += token; + editor.highlighter()->next(); + } + newContents.append(newLine); + posY++; + } + + Editor * oldEditor = pMainWindow->editorList()->getOpenedEditorByFilename(filename); + if (oldEditor) { + oldEditor->selectAll(); + oldEditor->setSelText(newContents.join(oldEditor->lineBreak())); + } else { + QByteArray realEncoding; + QFile file(filename); + editor.lines()->SaveToFile(file,ENCODING_AUTO_DETECT, realEncoding); + } +} + +void CppRefacter::findOccurenceInFile( + const QString& phrase, + const QString &filename, + const PStatement &statement, + int line, + const PCppParser &parser) +{ + PSearchResults results = pMainWindow->searchResultModel()->addSearchResults( + phrase, + filename, + line + ); + + PSearchResultTreeItem item = findOccurenceInFile( + filename, + statement, + parser); + if (item && !(item->results.isEmpty())) { + results->results.append(item); + } +} diff --git a/RedPandaIDE/cpprefacter.h b/RedPandaIDE/cpprefacter.h index 81cba0ab..3a2071a4 100644 --- a/RedPandaIDE/cpprefacter.h +++ b/RedPandaIDE/cpprefacter.h @@ -15,13 +15,26 @@ public: explicit CppRefacter(QObject *parent = nullptr); bool findOccurence(Editor * editor, const BufferCoord& pos); + + void renameSymbol(Editor* editor, const BufferCoord& pos, const QString& word, const QString& newWord); signals: private: + void findOccurenceInFile( + const QString& phrase, + const QString& filename, + const PStatement& statement, + int line, + const PCppParser& parser); PSearchResultTreeItem findOccurenceInFile( const QString& filename, const PStatement& statement, const PCppParser& parser); - + void renameSymbolInFile( + const QString& filename, + const PStatement& statement, + const QString& word, + const QString& newWord, + const PCppParser& parser); }; #endif // CPPREFACTER_H diff --git a/RedPandaIDE/mainwindow.cpp b/RedPandaIDE/mainwindow.cpp index 1980c053..753aaddc 100644 --- a/RedPandaIDE/mainwindow.cpp +++ b/RedPandaIDE/mainwindow.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -114,7 +115,7 @@ MainWindow::MainWindow(QWidget *parent) ui->menuCode->insertMenu(ui->actionReformat_Code,mMenuInsertCodeSnippet); ui->menuCode->insertSeparator(ui->actionReformat_Code); connect(mMenuInsertCodeSnippet,&QMenu::aboutToShow, - this, onShowInsertCodeSnippetMenu); + this, &MainWindow::onShowInsertCodeSnippetMenu); mCPUDialog = nullptr; @@ -3945,3 +3946,51 @@ void MainWindow::on_actionAbout_triggered() dialog.exec(); } + +void MainWindow::on_actionRename_Symbol_triggered() +{ + Editor * editor = mEditorList->getEditor(); + if (!editor) + return; + editor->beginUpdate(); +// mClassBrowserModel.beginUpdate(); + QCursor oldCursor = editor->cursor(); + editor->setCursor(Qt::CursorShape::WaitCursor); + auto action = finally([this,oldCursor,editor]{ + editor->endUpdate(); +// mClassBrowserModel.EndTreeUpdate; + editor->setCursor(oldCursor); + }); + QString word = editor->wordAtCursor(); + if (word.isEmpty()) + return; + +// if (!isIdentifier(word)) { +// return; +// } + + if (isKeyword(word)) { + return; + } + + bool ok; + QString newWord = QInputDialog::getText(editor, + tr("Rename Symbol"), + tr("New Name"), + QLineEdit::Normal,word, &ok); + if (!ok) + return; + + if (word == newWord) + return; + + PCppParser parser = editor->parser(); + BufferCoord oldCaretXY = editor->caretXY(); + //here we must reparse the file in sync, or rename may fail + parser->parseFile(editor->filename(), editor->inProject(), false, false); + CppRefacter refactor; + refactor.renameSymbol(editor,oldCaretXY,word,newWord); + editor->reparse(); + +} + diff --git a/RedPandaIDE/mainwindow.h b/RedPandaIDE/mainwindow.h index f2b8c1d2..75e86d15 100644 --- a/RedPandaIDE/mainwindow.h +++ b/RedPandaIDE/mainwindow.h @@ -371,6 +371,8 @@ private slots: void on_actionAbout_triggered(); + void on_actionRename_Symbol_triggered(); + private: Ui::MainWindow *ui; EditorList *mEditorList; diff --git a/RedPandaIDE/mainwindow.ui b/RedPandaIDE/mainwindow.ui index b089dac5..eff80faf 100644 --- a/RedPandaIDE/mainwindow.ui +++ b/RedPandaIDE/mainwindow.ui @@ -949,10 +949,17 @@ + + + Refactor + + + + @@ -1780,6 +1787,14 @@ About + + + Rename Symbol + + + Shift+F6 + + diff --git a/RedPandaIDE/qsynedit/SynEdit.cpp b/RedPandaIDE/qsynedit/SynEdit.cpp index e7c7e8bb..a6e539d5 100644 --- a/RedPandaIDE/qsynedit/SynEdit.cpp +++ b/RedPandaIDE/qsynedit/SynEdit.cpp @@ -2461,6 +2461,9 @@ void SynEdit::doPasteFromClipboard() void SynEdit::incPaintLock() { + if (mPaintLock==0) { + onBeginFirstPaintLock(); + } mPaintLock ++ ; } @@ -2477,6 +2480,7 @@ void SynEdit::decPaintLock() updateCaret(); if (mStatusChanges!=0) doOnStatusChange(mStatusChanges); + onEndFirstPaintLock(); } } @@ -5256,6 +5260,16 @@ void SynEdit::ExecuteCommand(SynEditorCommand Command, QChar AChar, void *pData) } +void SynEdit::onEndFirstPaintLock() +{ + +} + +void SynEdit::onBeginFirstPaintLock() +{ + +} + bool SynEdit::isIdentChar(const QChar &ch) { if (mHighlighter) { diff --git a/RedPandaIDE/qsynedit/SynEdit.h b/RedPandaIDE/qsynedit/SynEdit.h index ed6b5592..0f070eca 100644 --- a/RedPandaIDE/qsynedit/SynEdit.h +++ b/RedPandaIDE/qsynedit/SynEdit.h @@ -411,6 +411,8 @@ protected: virtual void onProcessCommand(SynEditorCommand Command, QChar AChar, void * pData); virtual void onCommandProcessed(SynEditorCommand Command, QChar AChar, void * pData); virtual void ExecuteCommand(SynEditorCommand Command, QChar AChar, void * pData); + virtual void onEndFirstPaintLock(); + virtual void onBeginFirstPaintLock(); private: void clearAreaList(SynEditingAreaList areaList);