- enhancement: replace in files

- enhancement: refactor in project (using search symbol occurence and replace in files)
 - fix: search in files
This commit is contained in:
royqh1979@gmail.com 2021-10-04 12:49:55 +08:00
parent 35b7bdb18c
commit 75a6b75ebb
13 changed files with 640 additions and 385 deletions

View File

@ -10,6 +10,9 @@ Version 0.6.0
- enhancement: don't add encoding options when using clang to compile (clang only support utf-8) - enhancement: don't add encoding options when using clang to compile (clang only support utf-8)
- enhancement: find occurence in project - enhancement: find occurence in project
- implement: refactor in file - implement: refactor in file
- enhancement: replace in files
- enhancement: refactor in project (using search symbol occurence and replace in files)
- fix: search in files
Version 0.5.0 Version 0.5.0
- enhancement: support C++ using type alias; - enhancement: support C++ using type alias;

File diff suppressed because it is too large Load Diff

View File

@ -1272,9 +1272,10 @@ void MainWindow::debug()
} }
} }
void MainWindow::showSearchPanel() void MainWindow::showSearchPanel(bool showReplace)
{ {
openCloseBottomPanel(true); openCloseBottomPanel(true);
showSearchReplacePanel(showReplace);
ui->tabMessages->setCurrentWidget(ui->tabSearch); ui->tabMessages->setCurrentWidget(ui->tabSearch);
} }
@ -3539,8 +3540,7 @@ void MainWindow::on_actionFind_references_triggered()
if (editor && editor->pointToCharLine(mEditorContextMenuPos,pos)) { if (editor && editor->pointToCharLine(mEditorContextMenuPos,pos)) {
CppRefacter refactor; CppRefacter refactor;
refactor.findOccurence(editor,pos); refactor.findOccurence(editor,pos);
ui->tabMessages->setCurrentWidget(ui->tabSearch); showSearchPanel(true);
openCloseBottomPanel(true);
} }
} }
@ -3973,6 +3973,37 @@ void MainWindow::on_actionRename_Symbol_triggered()
return; return;
} }
BufferCoord oldCaretXY = editor->caretXY();
if (editor->inProject() && mProject) {
mProject->cppParser()->parseFileList();
BufferCoord pBeginPos,pEndPos;
QString phrase = getWordAtPosition(editor,oldCaretXY,pBeginPos,pEndPos,Editor::WordPurpose::wpInformation);
// Find it's definition
PStatement oldStatement = editor->parser()->findStatementOf(
editor->filename(),
phrase,
oldCaretXY.Line);
// definition of the symbol not found
if (!oldStatement)
return;
// found but not in this file
if (editor->filename() != oldStatement->fileName
|| editor->filename() != oldStatement->definitionFileName) {
// it's defined in system header, dont rename
if (mProject->cppParser()->isSystemHeaderFile(oldStatement->fileName)) {
QMessageBox::critical(editor,
tr("Rename Error"),
tr("Symbol '%1' is defined in system header.")
.arg(oldStatement->fullName));
return;
}
CppRefacter refactor;
refactor.findOccurence(editor,oldCaretXY);
showSearchPanel(true);
return;
}
}
bool ok; bool ok;
QString newWord = QInputDialog::getText(editor, QString newWord = QInputDialog::getText(editor,
tr("Rename Symbol"), tr("Rename Symbol"),
@ -3985,7 +4016,6 @@ void MainWindow::on_actionRename_Symbol_triggered()
return; return;
PCppParser parser = editor->parser(); PCppParser parser = editor->parser();
BufferCoord oldCaretXY = editor->caretXY();
//here we must reparse the file in sync, or rename may fail //here we must reparse the file in sync, or rename may fail
parser->parseFile(editor->filename(), editor->inProject(), false, false); parser->parseFile(editor->filename(), editor->inProject(), false, false);
CppRefacter refactor; CppRefacter refactor;
@ -3994,3 +4024,53 @@ void MainWindow::on_actionRename_Symbol_triggered()
} }
void MainWindow::showSearchReplacePanel(bool show)
{
ui->replacePanel->setVisible(show);
ui->cbSearchHistory->setDisabled(show);
if (show && mSearchResultModel.currentResults()) {
ui->cbReplaceInHistory->setCurrentText(
mSearchResultModel.currentResults()->keyword);
}
mSearchResultTreeModel->setSelectable(show);
}
void MainWindow::on_btnReplace_clicked()
{
//select all items by default
PSearchResults results = mSearchResultModel.currentResults();
if (!results) {
return;
}
QString newWord = ui->cbReplaceInHistory->currentText();
foreach (const PSearchResultTreeItem& file, results->results) {
QStringList contents;
Editor* editor = mEditorList->getEditorByFilename(file->filename);
if (!editor) {
QMessageBox::critical(this,
tr("Replace Error"),
tr("Can't open file '%1' for replace!").arg(file->filename));
return;
}
contents = editor->lines()->contents();
for (int i=file->results.count()-1;i>=0;i--) {
const PSearchResultTreeItem& item = file->results[i];
QString line = contents[item->line-1];
if (line.mid(item->start-1,results->keyword.length())!=results->keyword) {
QMessageBox::critical(editor,
tr("Replace Error"),
tr("Contents has changed since last search!"));
return;
}
line.remove(item->start-1,results->keyword.length());
line.insert(item->start-1, newWord);
contents[item->line-1] = line;
}
editor->selectAll();
editor->setSelText(contents.join(editor->lineBreak()));
}
showSearchReplacePanel(false);
openCloseBottomPanel(false);
}

View File

@ -72,7 +72,7 @@ public:
void runExecutable(const QString& exeName, const QString& filename=QString()); void runExecutable(const QString& exeName, const QString& filename=QString());
void runExecutable(); void runExecutable();
void debug(); void debug();
void showSearchPanel(); void showSearchPanel(bool showReplace = false);
void applySettings(); void applySettings();
void applyUISettings(); void applyUISettings();
@ -173,6 +173,8 @@ private:
QKeySequence shortcut=QKeySequence()); QKeySequence shortcut=QKeySequence());
void scanActiveProject(bool parse=false); void scanActiveProject(bool parse=false);
void includeOrSkipDirs(const QStringList& dirs, bool skip); void includeOrSkipDirs(const QStringList& dirs, bool skip);
void showSearchReplacePanel(bool show);
private slots: private slots:
void onAutoSaveTimeout(); void onAutoSaveTimeout();
void onFileChanged(const QString& path); void onFileChanged(const QString& path);
@ -373,6 +375,8 @@ private slots:
void on_actionRename_Symbol_triggered(); void on_actionRename_Symbol_triggered();
void on_btnReplace_clicked();
private: private:
Ui::MainWindow *ui; Ui::MainWindow *ui;
EditorList *mEditorList; EditorList *mEditorList;

View File

@ -283,7 +283,7 @@
<enum>QTabWidget::South</enum> <enum>QTabWidget::South</enum>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>2</number> <number>3</number>
</property> </property>
<widget class="QWidget" name="tabIssues"> <widget class="QWidget" name="tabIssues">
<attribute name="icon"> <attribute name="icon">

View File

@ -24,7 +24,7 @@ int SynSearch::resultCount()
return mResults.count(); return mResults.count();
} }
int SynSearch::findAll(const QString &keyword) int SynSearch::findAll(const QString &text)
{ {
mResults.clear(); mResults.clear();
if (pattern().isEmpty()) if (pattern().isEmpty())
@ -33,18 +33,18 @@ int SynSearch::findAll(const QString &keyword)
int next=-1; int next=-1;
while (true) { while (true) {
if (options().testFlag(ssoMatchCase)) { if (options().testFlag(ssoMatchCase)) {
next = keyword.indexOf(pattern(),start,Qt::CaseSensitive); next = text.indexOf(pattern(),start,Qt::CaseSensitive);
} else { } else {
next = keyword.indexOf(pattern(),start,Qt::CaseInsensitive); next = text.indexOf(pattern(),start,Qt::CaseInsensitive);
} }
if (next<0) { if (next<0) {
break; break;
} }
start = next + keyword.length(); start = next + pattern().length();
if (options().testFlag(ssoWholeWord)) { if (options().testFlag(ssoWholeWord)) {
if (((next<=0) || isDelimitChar(keyword[next-1])) if (((next<=0) || isDelimitChar(text[next-1]))
&& &&
( (start>=keyword.length()) || isDelimitChar(keyword[start]) ) ( (start>=text.length()) || isDelimitChar(text[start]) )
) { ) {
mResults.append(next); mResults.append(next);
} }

View File

@ -14,7 +14,7 @@ public:
int length(int aIndex) override; int length(int aIndex) override;
int result(int aIndex) override; int result(int aIndex) override;
int resultCount() override; int resultCount() override;
int findAll(const QString &keyword) override; int findAll(const QString &text) override;
QString replace(const QString &aOccurrence, const QString &aReplacement) override; QString replace(const QString &aOccurrence, const QString &aReplacement) override;
private: private:
bool isDelimitChar(QChar ch); bool isDelimitChar(QChar ch);

View File

@ -26,7 +26,7 @@ public:
virtual int length(int aIndex) = 0; virtual int length(int aIndex) = 0;
virtual int result(int aIndex) = 0; virtual int result(int aIndex) = 0;
virtual int resultCount() = 0; virtual int resultCount() = 0;
virtual int findAll(const QString& keyword) = 0; virtual int findAll(const QString& text) = 0;
virtual QString replace(const QString& aOccurrence, const QString& aReplacement) = 0; virtual QString replace(const QString& aOccurrence, const QString& aReplacement) = 0;
SynSearchOptions options() const; SynSearchOptions options() const;
virtual void setOptions(const SynSearchOptions &options); virtual void setOptions(const SynSearchOptions &options);

View File

@ -26,13 +26,13 @@ int SynSearchRegex::resultCount()
return mResults.size(); return mResults.size();
} }
int SynSearchRegex::findAll(const QString &keyword) int SynSearchRegex::findAll(const QString &text)
{ {
if (pattern().isEmpty()) if (pattern().isEmpty())
return 0; return 0;
mResults.clear(); mResults.clear();
mLengths.clear(); mLengths.clear();
QRegularExpressionMatchIterator it = mRegex.globalMatch(keyword); QRegularExpressionMatchIterator it = mRegex.globalMatch(text);
while (it.hasNext()) { while (it.hasNext()) {
QRegularExpressionMatch match = it.next(); QRegularExpressionMatch match = it.next();
mLengths.append(match.capturedLength()); mLengths.append(match.capturedLength());

View File

@ -15,7 +15,7 @@ public:
int length(int aIndex) override; int length(int aIndex) override;
int result(int aIndex) override; int result(int aIndex) override;
int resultCount() override; int resultCount() override;
int findAll(const QString &keyword) override; int findAll(const QString &text) override;
QString replace(const QString &aOccurrence, const QString &aReplacement) override; QString replace(const QString &aOccurrence, const QString &aReplacement) override;
void setPattern(const QString &value) override; void setPattern(const QString &value) override;
void setOptions(const SynSearchOptions &options) override; void setOptions(const SynSearchOptions &options) override;

View File

@ -21,6 +21,7 @@ SearchDialog::SearchDialog(QWidget *parent) :
mTabBar->addTab(tr("Find")); mTabBar->addTab(tr("Find"));
mTabBar->addTab(tr("Find in files")); mTabBar->addTab(tr("Find in files"));
mTabBar->addTab(tr("Replace")); mTabBar->addTab(tr("Replace"));
mTabBar->addTab(tr("Replace in files"));
mTabBar->setExpanding(false); mTabBar->setExpanding(false);
ui->dialogLayout->insertWidget(0,mTabBar); ui->dialogLayout->insertWidget(0,mTabBar);
connect(mTabBar,&QTabBar::currentChanged,this, &SearchDialog::onTabChanged); connect(mTabBar,&QTabBar::currentChanged,this, &SearchDialog::onTabChanged);
@ -101,7 +102,7 @@ void SearchDialog::replace(const QString &sFind, const QString &sReplace)
void SearchDialog::onTabChanged() void SearchDialog::onTabChanged()
{ {
bool isfind = (mTabBar->currentIndex() == 0); bool isfind = (mTabBar->currentIndex() == 0);
bool isfindfiles = (mTabBar->currentIndex() == 1); bool isfindfiles = (mTabBar->currentIndex() == 1 || mTabBar->currentIndex() == 3 );
bool isreplace = (mTabBar->currentIndex() == 2); bool isreplace = (mTabBar->currentIndex() == 2);
ui->lblReplace->setVisible(isreplace); ui->lblReplace->setVisible(isreplace);
@ -246,7 +247,7 @@ void SearchDialog::on_btnExecute_clicked()
}); });
} }
} else if (actionType == SearchAction::FindFiles) { } else if (actionType == SearchAction::FindFiles || actionType == SearchAction::ReplaceFiles) {
int fileSearched = 0; int fileSearched = 0;
int fileHitted = 0; int fileHitted = 0;
QString keyword = ui->cbFind->currentText(); QString keyword = ui->cbFind->currentText();
@ -339,7 +340,7 @@ void SearchDialog::on_btnExecute_clicked()
// end; // end;
} }
if (findCount>0) if (findCount>0)
pMainWindow->showSearchPanel(); pMainWindow->showSearchPanel(actionType == SearchAction::ReplaceFiles);
} }
} }

View File

@ -119,7 +119,8 @@ void SearchResultModel::removeSearchResults(int index)
SearchResultTreeModel::SearchResultTreeModel(SearchResultModel *model, QObject *parent): SearchResultTreeModel::SearchResultTreeModel(SearchResultModel *model, QObject *parent):
QAbstractItemModel(parent), QAbstractItemModel(parent),
mSearchResultModel(model) mSearchResultModel(model),
mSelectable(false)
{ {
connect(mSearchResultModel,&SearchResultModel::currentChanged, connect(mSearchResultModel,&SearchResultModel::currentChanged,
this,&SearchResultTreeModel::onResultModelChanged); this,&SearchResultTreeModel::onResultModelChanged);
@ -213,6 +214,21 @@ QVariant SearchResultTreeModel::data(const QModelIndex &index, int role) const
.arg(item->text); .arg(item->text);
} }
} }
if (role == Qt::CheckStateRole && mSelectable) {
PSearchResults results = mSearchResultModel->currentResults();
if (!results || !index.isValid() ) {
// This is nothing this function is supposed to handle
return QVariant();
}
if (item->parent==nullptr) { //is filename
return QVariant();
} else {
return (item->selected)?Qt::Checked:Qt::Unchecked;
}
}
return QVariant(); return QVariant();
} }
@ -256,6 +272,69 @@ void SearchResultTreeModel::onResultModelChanged()
endResetModel(); endResetModel();
} }
Qt::ItemFlags SearchResultTreeModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags flags=Qt::ItemIsEnabled | Qt::ItemIsSelectable;
if (mSelectable) {
flags.setFlag(Qt::ItemIsUserCheckable);
}
return flags;
}
bool SearchResultTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid()){
return false;
}
SearchResultTreeItem *item = static_cast<SearchResultTreeItem *>(index.internalPointer());
if (!item)
return false;
if (role == Qt::CheckStateRole && mSelectable) {
PSearchResults results = mSearchResultModel->currentResults();
if (!results || !index.isValid() ) {
// This is nothing this function is supposed to handle
return false;
}
if (item->parent==nullptr) { //is filename
return false;
} else {
item->selected = value.toBool();
return true;
}
}
return false;
}
bool SearchResultTreeModel::selectable() const
{
return mSelectable;
}
void SearchResultTreeModel::setSelectable(bool newSelectable)
{
if (newSelectable!=mSelectable) {
beginResetModel();
mSelectable = newSelectable;
if (mSelectable) {
//select all items by default
PSearchResults results = mSearchResultModel->currentResults();
if (results) {
foreach (const PSearchResultTreeItem& file, results->results) {
file->selected = false;
foreach (const PSearchResultTreeItem& item, file->results) {
item->selected = true;
}
}
}
}
endResetModel();
}
}
SearchResultListModel::SearchResultListModel(SearchResultModel *model, QObject *parent): SearchResultListModel::SearchResultListModel(SearchResultModel *model, QObject *parent):
QAbstractListModel(parent), QAbstractListModel(parent),
mSearchResultModel(model) mSearchResultModel(model)

View File

@ -26,6 +26,7 @@ struct SearchResultTreeItem {
QString text; QString text;
SearchResultTreeItem* parent; SearchResultTreeItem* parent;
SearchResultTreeItemList results; SearchResultTreeItemList results;
bool selected;
}; };
struct SearchResults{ struct SearchResults{
@ -98,10 +99,22 @@ public:
QString& filename, QString& filename,
int& line, int& line,
int& startChar); int& startChar);
bool selectable() const;
void setSelectable(bool newSelectable);
public slots: public slots:
void onResultModelChanged(); void onResultModelChanged();
private: private:
SearchResultModel *mSearchResultModel; SearchResultModel *mSearchResultModel;
bool mSelectable;
// QAbstractItemModel interface
public:
Qt::ItemFlags flags(const QModelIndex &index) const override;
// QAbstractItemModel interface
public:
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
}; };
using PSearchResultTreeModel = std::shared_ptr<SearchResultTreeModel>; using PSearchResultTreeModel = std::shared_ptr<SearchResultTreeModel>;