diff --git a/src/plugins/git/git.pro b/src/plugins/git/git.pro index 9f8846e7b069e79154300ce8f8263ade1aa13759..953181e371c74c38f88fc438f1b116d821dfcc62 100644 --- a/src/plugins/git/git.pro +++ b/src/plugins/git/git.pro @@ -21,7 +21,8 @@ HEADERS += gitplugin.h \ logchangedialog.h \ mergetool.h \ branchcheckoutdialog.h \ - githighlighters.h + githighlighters.h \ + gitgrep.h SOURCES += gitplugin.cpp \ gitclient.cpp \ @@ -44,7 +45,8 @@ SOURCES += gitplugin.cpp \ logchangedialog.cpp \ mergetool.cpp \ branchcheckoutdialog.cpp \ - githighlighters.cpp + githighlighters.cpp \ + gitgrep.cpp FORMS += changeselectiondialog.ui \ settingspage.ui \ diff --git a/src/plugins/git/git.qbs b/src/plugins/git/git.qbs index 5b1180033c24da406c47ee8548aeb06b7ab342f4..dad838db6b26d8db460efb3e7d21024c80124115 100644 --- a/src/plugins/git/git.qbs +++ b/src/plugins/git/git.qbs @@ -36,6 +36,8 @@ QtcPlugin { "gitconstants.h", "giteditor.cpp", "giteditor.h", + "gitgrep.cpp", + "gitgrep.h", "githighlighters.cpp", "githighlighters.h", "gitplugin.cpp", diff --git a/src/plugins/git/gitgrep.cpp b/src/plugins/git/gitgrep.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d1cdce949d51dc526eebb1b51f31e06034041190 --- /dev/null +++ b/src/plugins/git/gitgrep.cpp @@ -0,0 +1,302 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Orgad Shaneh <orgads@gmail.com>. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "gitgrep.h" +#include "gitclient.h" +#include "gitplugin.h" + +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/find/findplugin.h> +#include <coreplugin/vcsmanager.h> +#include <vcsbase/vcsbaseconstants.h> + +#include <utils/filesearch.h> +#include <utils/fileutils.h> +#include <utils/pathchooser.h> +#include <utils/qtcassert.h> +#include <utils/qtcprocess.h> +#include <utils/runextensions.h> + +#include <QDir> +#include <QEventLoop> +#include <QFuture> +#include <QFutureWatcher> +#include <QGridLayout> +#include <QLabel> +#include <QSettings> + +using namespace Utils; + +namespace Git { +namespace Internal { + +using namespace Core; + +QString GitGrep::id() const +{ + return QLatin1String("Git Grep"); +} + +QString GitGrep::displayName() const +{ + return tr("Git Grep"); +} + +namespace { + +class GitGrepRunner : public QObject +{ + using FutureInterfaceType = QFutureInterface<FileSearchResultList>; + +public: + GitGrepRunner(FutureInterfaceType &fi, + const TextEditor::FileFindParameters ¶meters) : + m_fi(fi), + m_parameters(parameters) + { + m_directory = parameters.additionalParameters.toString(); + } + + void processLine(const QByteArray &line, FileSearchResultList *resultList) const + { + if (line.isEmpty()) + return; + static const char boldRed[] = "\x1b[1;31m"; + static const char resetColor[] = "\x1b[m"; + FileSearchResult single; + const int lineSeparator = line.indexOf('\0'); + single.fileName = m_directory + QLatin1Char('/') + + QString::fromLocal8Bit(line.left(lineSeparator)); + const int textSeparator = line.indexOf('\0', lineSeparator + 1); + single.lineNumber = line.mid(lineSeparator + 1, textSeparator - lineSeparator - 1).toInt(); + QByteArray text = line.mid(textSeparator + 1); + QVector<QPair<int, int>> matches; + for (;;) { + const int matchStart = text.indexOf(boldRed); + if (matchStart == -1) + break; + const int matchTextStart = matchStart + int(sizeof(boldRed)) - 1; + const int matchEnd = text.indexOf(resetColor, matchTextStart); + QTC_ASSERT(matchEnd != -1, break); + const int matchLength = matchEnd - matchTextStart; + matches.append(qMakePair(matchStart, matchLength)); + text = text.left(matchStart) + text.mid(matchTextStart, matchLength) + + text.mid(matchEnd + int(sizeof(resetColor)) - 1); + } + single.matchingLine = QString::fromLocal8Bit(text); + foreach (auto match, matches) { + single.matchStart = match.first; + single.matchLength = match.second; + resultList->append(single); + } + } + + void read() + { + FileSearchResultList resultList; + while (m_process.canReadLine() && !m_fi.isCanceled()) + processLine(m_process.readLine().trimmed(), &resultList); + if (!resultList.isEmpty()) + m_fi.reportResult(resultList); + } + + void finish() + { + read(); + m_fi.setProgressValue(1); + m_fi.reportFinished(); + } + + void exec() + { + m_fi.setProgressRange(0, 1); + m_fi.setProgressValue(0); + + QStringList arguments; + arguments << QLatin1String("-c") << QLatin1String("color.grep.match=bold red") + << QLatin1String("grep") << QLatin1String("-zn") + << QLatin1String("--color=always"); + if (!(m_parameters.flags & FindCaseSensitively)) + arguments << QLatin1String("-i"); + if (m_parameters.flags & FindWholeWords) + arguments << QLatin1String("-w"); + if (m_parameters.flags & FindRegularExpression) + arguments << QLatin1String("-P"); + else + arguments << QLatin1String("-F"); + arguments << m_parameters.text; + arguments << QLatin1String("--") << m_parameters.nameFilters; + QString args; + m_process.addArgs(&args, arguments); + m_process.setWorkingDirectory(m_directory); + m_process.setCommand(GitPlugin::instance()->client()->vcsBinary().toString(), args); + QFutureWatcher<FileSearchResultList> watcher; + watcher.setFuture(m_fi.future()); + connect(&watcher, &QFutureWatcher<FileSearchResultList>::canceled, + &m_process, &QtcProcess::kill); + connect(&m_process, &QProcess::readyRead, + this, &GitGrepRunner::read); + connect(&m_process, static_cast<void(QProcess::*)(int)>(&QProcess::finished), + this, &GitGrepRunner::finish); + QEventLoop eventLoop; + connect(&watcher, &QFutureWatcher<FileSearchResultList>::finished, + &eventLoop, &QEventLoop::quit); + m_process.start(); + if (!m_process.waitForStarted()) + return; + m_fi.reportStarted(); + eventLoop.exec(); + } + + static void run(QFutureInterface<FileSearchResultList> &fi, + TextEditor::FileFindParameters parameters) + { + GitGrepRunner runner(fi, parameters); + runner.exec(); + } + +private: + QtcProcess m_process; + FutureInterfaceType m_fi; + QString m_directory; + const TextEditor::FileFindParameters &m_parameters; +}; + +} // namespace + +QFuture<FileSearchResultList> GitGrep::executeSearch( + const TextEditor::FileFindParameters ¶meters) +{ + return Utils::runAsync<FileSearchResultList>(GitGrepRunner::run, parameters); +} + +FileIterator *GitGrep::files(const QStringList &, const QVariant &) const +{ + QTC_ASSERT(false, return 0); +} + +QVariant GitGrep::additionalParameters() const +{ + return qVariantFromValue(path().toString()); +} + +QString GitGrep::label() const +{ + const QChar slash = QLatin1Char('/'); + const QStringList &nonEmptyComponents = path().toFileInfo().absoluteFilePath() + .split(slash, QString::SkipEmptyParts); + return tr("Git Grep \"%1\":").arg(nonEmptyComponents.isEmpty() ? QString(slash) + : nonEmptyComponents.last()); +} + +QString GitGrep::toolTip() const +{ + //: %3 is filled by BaseFileFind::runNewSearch + return tr("Path: %1\nFilter: %2\n%3") + .arg(path().toUserOutput(), fileNameFilters().join(QLatin1Char(','))); +} + +bool GitGrep::validateDirectory(FancyLineEdit *edit, QString *errorMessage) const +{ + static IVersionControl *gitVc = + VcsManager::versionControl(VcsBase::Constants::VCS_ID_GIT); + QTC_ASSERT(gitVc, return false); + if (!m_directory->defaultValidationFunction()(edit, errorMessage)) + return false; + const QString path = m_directory->path(); + IVersionControl *vc = VcsManager::findVersionControlForDirectory(path, 0); + if (vc == gitVc) + return true; + if (errorMessage) + *errorMessage = tr("The path \"%1\" is not managed by Git").arg(path); + return false; +} + +QWidget *GitGrep::createConfigWidget() +{ + if (!m_configWidget) { + m_configWidget = new QWidget; + QGridLayout * const gridLayout = new QGridLayout(m_configWidget); + gridLayout->setMargin(0); + m_configWidget->setLayout(gridLayout); + + QLabel *dirLabel = new QLabel(tr("Director&y:")); + gridLayout->addWidget(dirLabel, 0, 0, Qt::AlignRight); + m_directory = new PathChooser; + m_directory->setExpectedKind(PathChooser::ExistingDirectory); + m_directory->setHistoryCompleter(QLatin1String("Git.Grep.History"), true); + m_directory->setPromptDialogTitle(tr("Directory to search")); + m_directory->setValidationFunction([this](FancyLineEdit *edit, QString *errorMessage) { + return validateDirectory(edit, errorMessage); + }); + connect(m_directory.data(), &PathChooser::validChanged, + this, &GitGrep::enabledChanged); + dirLabel->setBuddy(m_directory); + gridLayout->addWidget(m_directory, 0, 1, 1, 2); + + QLabel * const filePatternLabel = new QLabel(tr("Fi&le pattern:")); + filePatternLabel->setMinimumWidth(80); + filePatternLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + filePatternLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + QWidget *patternWidget = createPatternWidget(); + filePatternLabel->setBuddy(patternWidget); + gridLayout->addWidget(filePatternLabel, 1, 0); + gridLayout->addWidget(patternWidget, 1, 1, 1, 2); + m_configWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + } + return m_configWidget; +} + +FileName GitGrep::path() const +{ + return m_directory->fileName(); +} + +void GitGrep::writeSettings(QSettings *settings) +{ + settings->beginGroup(QLatin1String("GitGrep")); + writeCommonSettings(settings); + settings->endGroup(); +} + +void GitGrep::readSettings(QSettings *settings) +{ + settings->beginGroup(QLatin1String("GitGrep")); + readCommonSettings(settings, QString()); + settings->endGroup(); +} + +bool GitGrep::isValid() const +{ + return m_directory->isValid(); +} + +void GitGrep::setDirectory(const FileName &directory) +{ + m_directory->setFileName(directory); +} + +} // Internal +} // Git diff --git a/src/plugins/git/gitgrep.h b/src/plugins/git/gitgrep.h new file mode 100644 index 0000000000000000000000000000000000000000..d919cfad8d60597545d1288d5558abcef0db3c62 --- /dev/null +++ b/src/plugins/git/gitgrep.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Orgad Shaneh <orgads@gmail.com>. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#ifndef GITGREP_H +#define GITGREP_H + +#include <texteditor/basefilefind.h> + +#include <utils/fileutils.h> + +#include <QPointer> +#include <QStringListModel> + +namespace Utils { +class FancyLineEdit; +class PathChooser; +} + +namespace Git { +namespace Internal { + +class GitGrep : public TextEditor::BaseFileFind +{ + Q_OBJECT + +public: + QString id() const override; + QString displayName() const override; + QFuture<Utils::FileSearchResultList> executeSearch( + const TextEditor::FileFindParameters ¶meters) override; + QWidget *createConfigWidget() override; + void writeSettings(QSettings *settings) override; + void readSettings(QSettings *settings) override; + bool isValid() const override; + + void setDirectory(const Utils::FileName &directory); + +protected: + Utils::FileIterator *files(const QStringList &nameFilters, + const QVariant &additionalParameters) const override; + QVariant additionalParameters() const override; + QString label() const override; + QString toolTip() const override; + +private: + Utils::FileName path() const; + bool validateDirectory(Utils::FancyLineEdit *edit, QString *errorMessage) const; + + QPointer<QWidget> m_configWidget; + QPointer<Utils::PathChooser> m_directory; +}; + +} // namespace Internal +} // namespace Git + +#endif // FINDINFILES_H diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp index 461afa0f2e0d04c742ef49e796df8d2865674442..0069fa90e1147db34331a5702b77e469311699ab 100644 --- a/src/plugins/git/gitplugin.cpp +++ b/src/plugins/git/gitplugin.cpp @@ -39,6 +39,7 @@ #include "logchangedialog.h" #include "mergetool.h" #include "gitutils.h" +#include "gitgrep.h" #include "gerrit/gerritplugin.h" @@ -283,6 +284,7 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage) // Create the settings Page addAutoReleasedObject(new SettingsPage(versionControl())); + addAutoReleasedObject(new GitGrep); static const char *describeSlot = SLOT(show(QString,QString)); const int editorCount = sizeof(editorParameters) / sizeof(editorParameters[0]); diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp index a8c995f76b1cf6493e572e5a352cce557b191101..b5077262edf8d25088bba0fe065ed61912acceab 100644 --- a/src/plugins/texteditor/basefilefind.cpp +++ b/src/plugins/texteditor/basefilefind.cpp @@ -24,7 +24,6 @@ ****************************************************************************/ #include "basefilefind.h" -#include "basefilefind_p.h" #include "textdocument.h" #include <aggregation/aggregate.h> @@ -59,6 +58,13 @@ using namespace Core; namespace TextEditor { namespace Internal { +class CountingLabel : public QLabel +{ +public: + CountingLabel(); + void updateCount(int count); +}; + class BaseFileFindPrivate { public: @@ -168,17 +174,7 @@ void BaseFileFind::runSearch(SearchResult *search) watcher->setPendingResultsLimit(1); connect(watcher, &QFutureWatcherBase::resultReadyAt, this, &BaseFileFind::displayResult); connect(watcher, &QFutureWatcherBase::finished, this, &BaseFileFind::searchFinished); - if (parameters.flags & FindRegularExpression) { - watcher->setFuture(Utils::findInFilesRegExp(parameters.text, - files(parameters.nameFilters, parameters.additionalParameters), - textDocumentFlagsForFindFlags(parameters.flags), - TextDocument::openedTextDocumentContents())); - } else { - watcher->setFuture(Utils::findInFiles(parameters.text, - files(parameters.nameFilters, parameters.additionalParameters), - textDocumentFlagsForFindFlags(parameters.flags), - TextDocument::openedTextDocumentContents())); - } + watcher->setFuture(executeSearch(parameters)); FutureProgress *progress = ProgressManager::addTask(watcher->future(), tr("Searching"), Constants::TASK_SEARCH); progress->setWidget(label); @@ -427,6 +423,20 @@ QVariant BaseFileFind::getAdditionalParameters(SearchResult *search) return search->userData().value<FileFindParameters>().additionalParameters; } +QFuture<FileSearchResultList> BaseFileFind::executeSearch(const FileFindParameters ¶meters) +{ + auto func = parameters.flags & FindRegularExpression + ? Utils::findInFilesRegExp + : Utils::findInFiles; + + return func(parameters.text, + files(parameters.nameFilters, parameters.additionalParameters), + textDocumentFlagsForFindFlags(parameters.flags), + TextDocument::openedTextDocumentContents()); +} + +namespace Internal { + CountingLabel::CountingLabel() { setAlignment(Qt::AlignCenter); @@ -444,4 +454,5 @@ void CountingLabel::updateCount(int count) setText(tr("%1 found").arg(count)); } +} // namespace Internal } // namespace TextEditor diff --git a/src/plugins/texteditor/basefilefind.h b/src/plugins/texteditor/basefilefind.h index 6200902ac09502182aaa348429ad865d4de8f4c0..31f56ed368f993d2f09db79dd89ff4a4024498a2 100644 --- a/src/plugins/texteditor/basefilefind.h +++ b/src/plugins/texteditor/basefilefind.h @@ -27,10 +27,13 @@ #define BASEFILEFIND_H #include "texteditor_global.h" +#include "utils/filesearch.h" #include <coreplugin/find/ifindfilter.h> #include <coreplugin/find/searchresultwindow.h> +#include <QFuture> + QT_BEGIN_NAMESPACE class QLabel; class QComboBox; @@ -44,8 +47,18 @@ class IFindSupport; } // namespace Core namespace TextEditor { + namespace Internal { class BaseFileFindPrivate; } +class TEXTEDITOR_EXPORT FileFindParameters +{ +public: + QString text; + Core::FindFlags flags; + QStringList nameFilters; + QVariant additionalParameters; +}; + class TEXTEDITOR_EXPORT BaseFileFind : public Core::IFindFilter { Q_OBJECT @@ -72,6 +85,8 @@ protected: virtual QString label() const = 0; // see Core::SearchResultWindow::startNewSearch virtual QString toolTip() const = 0; // see Core::SearchResultWindow::startNewSearch, // add %1 placeholder where the find flags should be put + virtual QFuture<Utils::FileSearchResultList> executeSearch( + const FileFindParameters ¶meters); void writeCommonSettings(QSettings *settings); void readCommonSettings(QSettings *settings, const QString &defaultFilter); @@ -102,4 +117,6 @@ private: } // namespace TextEditor +Q_DECLARE_METATYPE(TextEditor::FileFindParameters) + #endif // BASEFILEFIND_H diff --git a/src/plugins/texteditor/basefilefind_p.h b/src/plugins/texteditor/basefilefind_p.h deleted file mode 100644 index 1d689f212f8f3a722317a3a2d03be7d94bc46e1c..0000000000000000000000000000000000000000 --- a/src/plugins/texteditor/basefilefind_p.h +++ /dev/null @@ -1,61 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#ifndef BASEFILEFIND_P_H -#define BASEFILEFIND_P_H - -#include <coreplugin/find/ifindfilter.h> - -#include <QVariant> -#include <QLabel> - -namespace TextEditor { -namespace Internal { - -class CountingLabel : public QLabel -{ - Q_OBJECT -public: - CountingLabel(); - -public slots: - void updateCount(int count); -}; - -class FileFindParameters -{ -public: - QString text; - Core::FindFlags flags; - QStringList nameFilters; - QVariant additionalParameters; -}; - -} // namespace Internal -} // namespace TextEditor - -Q_DECLARE_METATYPE(TextEditor::Internal::FileFindParameters) - -#endif // BASEFILEFIND_P_H diff --git a/src/plugins/texteditor/texteditor.pro b/src/plugins/texteditor/texteditor.pro index 243f08b271c674f351acf860982f420e631e6b46..f763a8863cf0a3e55e1b14dede0e35122d2b024a 100644 --- a/src/plugins/texteditor/texteditor.pro +++ b/src/plugins/texteditor/texteditor.pro @@ -206,7 +206,6 @@ HEADERS += texteditorplugin.h \ icodestylepreferences.h \ codestylepool.h \ codestyleeditor.h \ - basefilefind_p.h \ circularclipboard.h \ circularclipboardassist.h \ textmark.h \ diff --git a/src/plugins/texteditor/texteditor.qbs b/src/plugins/texteditor/texteditor.qbs index 3799fc8fbf6a196c4ee447cade4938d86ab27e2f..e9edede675ec818ae7914fd530b9b0d5bd0d47a9 100644 --- a/src/plugins/texteditor/texteditor.qbs +++ b/src/plugins/texteditor/texteditor.qbs @@ -16,7 +16,6 @@ QtcPlugin { "autocompleter.h", "basefilefind.cpp", "basefilefind.h", - "basefilefind_p.h", "basehoverhandler.cpp", "basehoverhandler.h", "behaviorsettings.cpp",