- 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: find occurence in project
- 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
- 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);
showSearchReplacePanel(showReplace);
ui->tabMessages->setCurrentWidget(ui->tabSearch);
}
@ -3539,8 +3540,7 @@ void MainWindow::on_actionFind_references_triggered()
if (editor && editor->pointToCharLine(mEditorContextMenuPos,pos)) {
CppRefacter refactor;
refactor.findOccurence(editor,pos);
ui->tabMessages->setCurrentWidget(ui->tabSearch);
openCloseBottomPanel(true);
showSearchPanel(true);
}
}
@ -3973,6 +3973,37 @@ void MainWindow::on_actionRename_Symbol_triggered()
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;
QString newWord = QInputDialog::getText(editor,
tr("Rename Symbol"),
@ -3985,7 +4016,6 @@ void MainWindow::on_actionRename_Symbol_triggered()
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;
@ -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();
void debug();
void showSearchPanel();
void showSearchPanel(bool showReplace = false);
void applySettings();
void applyUISettings();
@ -173,6 +173,8 @@ private:
QKeySequence shortcut=QKeySequence());
void scanActiveProject(bool parse=false);
void includeOrSkipDirs(const QStringList& dirs, bool skip);
void showSearchReplacePanel(bool show);
private slots:
void onAutoSaveTimeout();
void onFileChanged(const QString& path);
@ -373,6 +375,8 @@ private slots:
void on_actionRename_Symbol_triggered();
void on_btnReplace_clicked();
private:
Ui::MainWindow *ui;
EditorList *mEditorList;

View File

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

View File

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

View File

@ -14,7 +14,7 @@ public:
int length(int aIndex) override;
int result(int aIndex) 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;
private:
bool isDelimitChar(QChar ch);

View File

@ -26,7 +26,7 @@ public:
virtual int length(int aIndex) = 0;
virtual int result(int aIndex) = 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;
SynSearchOptions options() const;
virtual void setOptions(const SynSearchOptions &options);

View File

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

View File

@ -15,7 +15,7 @@ public:
int length(int aIndex) override;
int result(int aIndex) 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;
void setPattern(const QString &value) 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 in files"));
mTabBar->addTab(tr("Replace"));
mTabBar->addTab(tr("Replace in files"));
mTabBar->setExpanding(false);
ui->dialogLayout->insertWidget(0,mTabBar);
connect(mTabBar,&QTabBar::currentChanged,this, &SearchDialog::onTabChanged);
@ -101,7 +102,7 @@ void SearchDialog::replace(const QString &sFind, const QString &sReplace)
void SearchDialog::onTabChanged()
{
bool isfind = (mTabBar->currentIndex() == 0);
bool isfindfiles = (mTabBar->currentIndex() == 1);
bool isfindfiles = (mTabBar->currentIndex() == 1 || mTabBar->currentIndex() == 3 );
bool isreplace = (mTabBar->currentIndex() == 2);
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 fileHitted = 0;
QString keyword = ui->cbFind->currentText();
@ -339,7 +340,7 @@ void SearchDialog::on_btnExecute_clicked()
// end;
}
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):
QAbstractItemModel(parent),
mSearchResultModel(model)
mSearchResultModel(model),
mSelectable(false)
{
connect(mSearchResultModel,&SearchResultModel::currentChanged,
this,&SearchResultTreeModel::onResultModelChanged);
@ -213,6 +214,21 @@ QVariant SearchResultTreeModel::data(const QModelIndex &index, int role) const
.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();
}
@ -256,6 +272,69 @@ void SearchResultTreeModel::onResultModelChanged()
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):
QAbstractListModel(parent),
mSearchResultModel(model)

View File

@ -26,6 +26,7 @@ struct SearchResultTreeItem {
QString text;
SearchResultTreeItem* parent;
SearchResultTreeItemList results;
bool selected;
};
struct SearchResults{
@ -98,10 +99,22 @@ public:
QString& filename,
int& line,
int& startChar);
bool selectable() const;
void setSelectable(bool newSelectable);
public slots:
void onResultModelChanged();
private:
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>;