Commit 1a95f6f2 authored by con's avatar con

Implement string based search & replace in multiple files.

This uses the same UI as "Rename Symbol".
Moves the actual rename implementation to a static method in
BaseFileFind and uses it for rename symbol and search & replace.
Moves the signal notification for the code model from VCSManager to
the more general FileManager.
Note that as for rename symbol, there's no undo yet.

Task-number: QTCREATORBUG-73
parent f229a761
......@@ -781,6 +781,11 @@ void FileManager::setFileDialogLastVisitedDirectory(const QString &directory)
d->m_lastVisitedDirectory = directory;
}
void FileManager::notifyFilesChangedInternally(const QStringList &files)
{
emit filesChangedInternally(files);
}
// -------------- FileChangeBlocker
FileChangeBlocker::FileChangeBlocker(const QString &fileName)
......
......@@ -106,8 +106,16 @@ public:
QString projectsDirectory() const;
void setProjectsDirectory(const QString &);
public slots:
/* Used to notify e.g. the code model to update the given files. Does *not*
lead to any editors to reload or any other editor manager actions. */
void notifyFilesChangedInternally(const QStringList &files);
signals:
void currentFileChanged(const QString &filePath);
/* Used to notify e.g. the code model to update the given files. Does *not*
lead to any editors to reload or any other editor manager actions. */
void filesChangedInternally(const QStringList &files);
private slots:
void fileDestroyed(QObject *obj);
......
......@@ -29,6 +29,8 @@
#include "vcsmanager.h"
#include "iversioncontrol.h"
#include "icore.h"
#include "filemanager.h"
#include <extensionsystem/pluginmanager.h>
......@@ -74,9 +76,10 @@ VCSManager::~VCSManager()
void VCSManager::extensionsInitialized()
{
// Change signal connections
FileManager *fileManager = ICore::instance()->fileManager();
foreach (IVersionControl *versionControl, allVersionControls()) {
connect(versionControl, SIGNAL(filesChanged(QStringList)),
this, SIGNAL(filesChanged(QStringList)));
fileManager, SIGNAL(filesChangedInternally(QStringList)));
connect(versionControl, SIGNAL(repositoryChanged(QString)),
this, SIGNAL(repositoryChanged(QString)));
}
......
......@@ -78,7 +78,6 @@ public:
signals:
void repositoryChanged(const QString &repository);
void filesChanged(const QStringList &files);
private:
VCSManagerPrivate *m_d;
......
......@@ -32,6 +32,7 @@
#include "cpptoolsconstants.h"
#include <texteditor/basetexteditor.h>
#include <texteditor/basefilefind.h>
#include <find/searchresultwindow.h>
#include <extensionsystem/pluginmanager.h>
#include <utils/filesearch.h>
......@@ -294,63 +295,11 @@ void CppFindReferences::onReplaceButtonClicked(const QString &text,
{
Core::EditorManager::instance()->hideEditorInfoBar(QLatin1String("CppEditor.Rename"));
if (text.isEmpty())
return;
QHash<QString, QList<Find::SearchResultItem> > changes;
foreach (const Find::SearchResultItem &item, items)
changes[item.fileName].append(item);
Core::EditorManager *editorManager = Core::EditorManager::instance();
QHashIterator<QString, QList<Find::SearchResultItem> > it(changes);
while (it.hasNext()) {
it.next();
const QString fileName = it.key();
const QList<Find::SearchResultItem> items = it.value();
const QList<Core::IEditor *> editors = editorManager->editorsForFileName(fileName);
TextEditor::BaseTextEditor *textEditor = 0;
foreach (Core::IEditor *editor, editors) {
textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor->widget());
if (textEditor != 0)
break;
}
if (textEditor != 0) {
QTextCursor tc = textEditor->textCursor();
tc.beginEditBlock();
applyChanges(textEditor->document(), text, items);
tc.endEditBlock();
} else {
QFile file(fileName);
if (file.open(QFile::ReadOnly)) {
QTextStream stream(&file);
// ### set the encoding
const QString plainText = stream.readAll();
file.close();
QTextDocument doc;
doc.setPlainText(plainText);
applyChanges(&doc, text, items);
QFile newFile(fileName);
if (newFile.open(QFile::WriteOnly)) {
QTextStream stream(&newFile);
// ### set the encoding
stream << doc.toPlainText();
}
}
}
const QStringList fileNames = TextEditor::BaseFileFind::replaceAll(text, items);
if (!fileNames.isEmpty()) {
_modelManager->updateSourceFiles(fileNames);
_resultWindow->hide();
}
const QStringList fileNames = changes.keys();
_modelManager->updateSourceFiles(fileNames);
_resultWindow->hide();
}
void CppFindReferences::displayResults(int first, int last)
......
......@@ -48,6 +48,7 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <coreplugin/vcsmanager.h>
#include <coreplugin/filemanager.h>
#include <cppeditor/cppeditorconstants.h>
#include <QtCore/QtConcurrentRun>
......@@ -99,10 +100,11 @@ bool CppToolsPlugin::initialize(const QStringList &arguments, QString *error)
// Objects
m_modelManager = new CppModelManager(this);
Core::VCSManager *vcsManager = core->vcsManager();
Core::FileManager *fileManager = core->fileManager();
connect(vcsManager, SIGNAL(repositoryChanged(QString)),
m_modelManager, SLOT(updateModifiedSourceFiles()));
connect(vcsManager, SIGNAL(filesChanged(QStringList)),
m_modelManager, SLOT(updateModifiedSourceFiles()));
connect(fileManager, SIGNAL(filesChangedInternally(QStringList)),
m_modelManager, SLOT(updateSourceFiles(QStringList)));
addAutoReleasedObject(m_modelManager);
m_completion = new CppCodeCompletion(m_modelManager);
......
......@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>426</width>
<width>454</width>
<height>168</height>
</rect>
</property>
......@@ -117,6 +117,13 @@
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="replaceButton">
<property name="text">
<string>Search &amp;&amp; Replace</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
......@@ -139,6 +146,7 @@
<tabstop>searchTerm</tabstop>
<tabstop>searchButton</tabstop>
<tabstop>closeButton</tabstop>
<tabstop>replaceButton</tabstop>
<tabstop>matchCase</tabstop>
<tabstop>wholeWords</tabstop>
</tabstops>
......
......@@ -46,11 +46,11 @@ FindToolWindow::FindToolWindow(FindPlugin *plugin)
{
m_ui.setupUi(this);
connect(m_ui.closeButton, SIGNAL(clicked()), this, SLOT(reject()));
connect(m_ui.searchButton, SIGNAL(clicked()), this, SLOT(accept()));
connect(m_ui.searchButton, SIGNAL(clicked()), this, SLOT(search()));
connect(m_ui.replaceButton, SIGNAL(clicked()), this, SLOT(replace()));
connect(m_ui.matchCase, SIGNAL(toggled(bool)), m_plugin, SLOT(setCaseSensitive(bool)));
connect(m_ui.wholeWords, SIGNAL(toggled(bool)), m_plugin, SLOT(setWholeWord(bool)));
connect(m_ui.filterList, SIGNAL(activated(int)), this, SLOT(setCurrentFilter(int)));
connect(this, SIGNAL(accepted()), this, SLOT(search()));
m_findCompleter->setModel(m_plugin->findCompletionModel());
m_ui.searchTerm->setCompleter(m_findCompleter);
QVBoxLayout *layout = new QVBoxLayout;
......@@ -108,12 +108,14 @@ void FindToolWindow::setCurrentFilter(int index)
if (!configWidget)
continue;
if (i == index) {
IFindFilter *filter = m_filters.at(i);
m_ui.configWidget->layout()->addWidget(configWidget);
bool enabled = m_filters.at(i)->isEnabled();
bool enabled = filter->isEnabled();
m_ui.matchCase->setEnabled(enabled);
m_ui.wholeWords->setEnabled(enabled);
m_ui.searchTerm->setEnabled(enabled);
m_ui.searchButton->setEnabled(enabled);
m_ui.replaceButton->setEnabled(filter->isReplaceSupported() && enabled);
configWidget->setEnabled(enabled);
} else {
configWidget->setParent(0);
......@@ -122,17 +124,38 @@ void FindToolWindow::setCurrentFilter(int index)
m_currentFilter = m_filters.at(index);
}
void FindToolWindow::search()
void FindToolWindow::acceptAndGetParameters(QString *term, IFindFilter **filter)
{
if (filter)
*filter = 0;
accept();
m_plugin->updateFindCompletion(m_ui.searchTerm->text());
int index = m_ui.filterList->currentIndex();
QString term = m_ui.searchTerm->text();
if (term.isEmpty() || index < 0)
QString searchTerm = m_ui.searchTerm->text();
if (term)
*term = searchTerm;
if (searchTerm.isEmpty() || index < 0)
return;
IFindFilter *filter = m_filters.at(index);
if (filter)
*filter = m_filters.at(index);
}
void FindToolWindow::search()
{
QString term;
IFindFilter *filter;
acceptAndGetParameters(&term, &filter);
filter->findAll(term, m_plugin->findFlags());
}
void FindToolWindow::replace()
{
QString term;
IFindFilter *filter;
acceptAndGetParameters(&term, &filter);
filter->replaceAll(term, m_plugin->findFlags());
}
void FindToolWindow::writeSettings()
{
QSettings *settings = Core::ICore::instance()->settings();
......
......@@ -59,9 +59,12 @@ public:
private slots:
void search();
void replace();
void setCurrentFilter(int index);
private:
void acceptAndGetParameters(QString *term, IFindFilter **filter);
Ui::FindDialog m_ui;
FindPlugin *m_plugin;
QList<IFindFilter *> m_filters;
......
......@@ -53,7 +53,11 @@ public:
virtual QString name() const = 0;
virtual bool isEnabled() const = 0;
virtual QKeySequence defaultShortcut() const = 0;
virtual bool isReplaceSupported() const { return false; }
virtual void findAll(const QString &txt, QTextDocument::FindFlags findFlags) = 0;
virtual void replaceAll(const QString &txt, QTextDocument::FindFlags findFlags)
{ Q_UNUSED(txt) Q_UNUSED(findFlags) }
virtual QWidget *createConfigWidget() { return 0; }
virtual void writeSettings(QSettings *settings) { Q_UNUSED(settings) }
......
......@@ -32,6 +32,7 @@
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/filemanager.h>
#include <find/textfindconstants.h>
#include <find/searchresultwindow.h>
#include <texteditor/itexteditor.h>
......@@ -106,6 +107,41 @@ void BaseFileFind::findAll(const QString &txt, QTextDocument::FindFlags findFlag
connect(progress, SIGNAL(clicked()), m_resultWindow, SLOT(popup()));
}
void BaseFileFind::replaceAll(const QString &txt, QTextDocument::FindFlags findFlags)
{
m_isSearching = true;
emit changed();
if (m_filterCombo)
updateComboEntries(m_filterCombo, false);
m_watcher.setFuture(QFuture<FileSearchResult>());
SearchResult *result = m_resultWindow->startNewSearch(SearchResultWindow::SearchAndReplace);
connect(result, SIGNAL(activated(Find::SearchResultItem)), this, SLOT(openEditor(Find::SearchResultItem)));
connect(result, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>)),
this, SLOT(doReplace(QString,QList<Find::SearchResultItem>)));
m_resultWindow->popup(true);
if (m_useRegExp)
m_watcher.setFuture(Utils::findInFilesRegExp(txt, files(), findFlags, ITextEditor::openedTextEditorsContents()));
else
m_watcher.setFuture(Utils::findInFiles(txt, files(), findFlags, ITextEditor::openedTextEditorsContents()));
Core::FutureProgress *progress =
Core::ICore::instance()->progressManager()->addTask(m_watcher.future(),
"Search",
Constants::TASK_SEARCH);
progress->setWidget(createProgressWidget());
connect(progress, SIGNAL(clicked()), m_resultWindow, SLOT(popup()));
}
void BaseFileFind::doReplace(const QString &text,
const QList<Find::SearchResultItem> &items)
{
QStringList files = replaceAll(text, items);
Core::FileManager *fileManager = Core::ICore::instance()->fileManager();
if (!files.isEmpty()) {
fileManager->notifyFilesChangedInternally(files);
m_resultWindow->hide();
}
}
void BaseFileFind::displayResult(int index) {
Utils::FileSearchResult result = m_watcher.future().resultAt(index);
m_resultWindow->addResult(result.fileName,
......@@ -237,3 +273,96 @@ void BaseFileFind::openEditor(const Find::SearchResultItem &item)
{
TextEditor::BaseTextEditor::openEditorAt(item.fileName, item.lineNumber, item.searchTermStart);
}
#pragma mark Static methods
static void applyChanges(QTextDocument *doc, const QString &text, const QList<Find::SearchResultItem> &items)
{
QList<QTextCursor> cursors;
foreach (const Find::SearchResultItem &item, items) {
const int blockNumber = item.lineNumber - 1;
QTextCursor tc(doc->findBlockByNumber(blockNumber));
const int cursorPosition = tc.position() + item.searchTermStart;
int cursorIndex = 0;
for (; cursorIndex < cursors.size(); ++cursorIndex) {
const QTextCursor &tc = cursors.at(cursorIndex);
if (tc.position() == cursorPosition)
break;
}
if (cursorIndex != cursors.size())
continue; // skip this change.
tc.setPosition(cursorPosition);
tc.setPosition(tc.position() + item.searchTermLength,
QTextCursor::KeepAnchor);
cursors.append(tc);
}
foreach (QTextCursor tc, cursors)
tc.insertText(text);
}
QStringList BaseFileFind::replaceAll(const QString &text,
const QList<Find::SearchResultItem> &items)
{
if (text.isEmpty() || items.isEmpty())
return QStringList();
QHash<QString, QList<Find::SearchResultItem> > changes;
foreach (const Find::SearchResultItem &item, items)
changes[item.fileName].append(item);
Core::EditorManager *editorManager = Core::EditorManager::instance();
QHashIterator<QString, QList<Find::SearchResultItem> > it(changes);
while (it.hasNext()) {
it.next();
const QString fileName = it.key();
const QList<Find::SearchResultItem> items = it.value();
const QList<Core::IEditor *> editors = editorManager->editorsForFileName(fileName);
TextEditor::BaseTextEditor *textEditor = 0;
foreach (Core::IEditor *editor, editors) {
textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor->widget());
if (textEditor != 0)
break;
}
if (textEditor != 0) {
QTextCursor tc = textEditor->textCursor();
tc.beginEditBlock();
applyChanges(textEditor->document(), text, items);
tc.endEditBlock();
} else {
QFile file(fileName);
if (file.open(QFile::ReadOnly)) {
QTextStream stream(&file);
// ### set the encoding
const QString plainText = stream.readAll();
file.close();
QTextDocument doc;
doc.setPlainText(plainText);
applyChanges(&doc, text, items);
QFile newFile(fileName);
if (newFile.open(QFile::WriteOnly)) {
QTextStream stream(&newFile);
// ### set the encoding
stream << doc.toPlainText();
}
}
}
}
return changes.keys();
}
......@@ -62,8 +62,13 @@ public:
explicit BaseFileFind(Find::SearchResultWindow *resultWindow);
bool isEnabled() const;
bool isReplaceSupported() const { return true; }
void findAll(const QString &txt, QTextDocument::FindFlags findFlags);
void replaceAll(const QString &txt, QTextDocument::FindFlags findFlags);
/* returns the list of unique files that were passed in items */
static QStringList replaceAll(const QString &txt,
const QList<Find::SearchResultItem> &items);
protected:
virtual QStringList files() = 0;
void writeCommonSettings(QSettings *settings);
......@@ -79,6 +84,8 @@ private slots:
void searchFinished();
void openEditor(const Find::SearchResultItem &item);
void syncRegExpSetting(bool useRegExp);
void doReplace(const QString &txt,
const QList<Find::SearchResultItem> &items);
private:
QWidget *createProgressWidget();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment