- 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:
parent
35b7bdb18c
commit
75a6b75ebb
3
NEWS.md
3
NEWS.md
|
@ -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
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>;
|
||||
|
|
Loading…
Reference in New Issue