Commit 2fcdc2ff authored by Przemyslaw Gorszkowski's avatar Przemyslaw Gorszkowski Committed by Eike Ziller
Browse files

FindInFiles: Support Silver Searcher



Change-Id: I6f0f55224f228fc1d0496c589cb71ede11d90ea5
Reviewed-by: Eike Ziller's avatarEike Ziller <eike.ziller@qt.io>
parent 30a8e05a
......@@ -54,7 +54,8 @@ SUBDIRS = \
winrt \
updateinfo \
scxmleditor \
welcome
welcome \
silversearcher
qtHaveModule(quick) {
SUBDIRS += qmlprofiler
......
......@@ -57,6 +57,7 @@ Project {
"remotelinux/remotelinux.qbs",
"resourceeditor/resourceeditor.qbs",
"scxmleditor/scxmleditor.qbs",
"silversearcher/silversearcher.qbs",
"subversion/subversion.qbs",
"tasklist/tasklist.qbs",
"texteditor/texteditor.qbs",
......
{
\"Name\" : \"SilverSearcher\",
\"Version\" : \"$$QTCREATOR_VERSION\",
\"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
\"Experimental\" : true,
\"Vendor\" : \"Przemyslaw Gorszkowski\",
\"Copyright\" : \"(C) 2017 Przemyslaw Gorszkowski\",
\"License\" : [ \"Commercial Usage\",
\"\",
\"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\",
\"\",
\"GNU General Public License Usage\",
\"\",
\"Alternatively, this plugin 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 plugin. 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.\"
],
\"Description\" : \"Adds possibility to use SilverSearcher tool as an alternative mechanism of 'find in files'\",
\"Url\" : \"http://www.qt.io\",
$$dependencyList
}
/****************************************************************************
**
** Copyright (C) 2017 Przemyslaw Gorszkowski <pgorszkowski@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 "findinfilessilversearcher.h"
#include <aggregation/aggregate.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <texteditor/findinfiles.h>
#include <utils/algorithm.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
#include <utils/runextensions.h>
#include "silversearcheroutputparser.h"
#include <QProcess>
#include <QHBoxLayout>
#include <QLabel>
using namespace Core;
using namespace TextEditor;
using namespace Utils;
namespace {
const QLatin1String silverSearcherName("Silver Searcher");
using FutureInterfaceType = QFutureInterface<FileSearchResultList>;
const QString metacharacters = "+()^$.{}[]|\\";
QString convertWildcardToRegex(const QString &wildcard)
{
QString regex;
const int wildcardSize = wildcard.size();
regex.append('^');
for (int i = 0; i < wildcardSize; ++i) {
const QChar ch = wildcard[i];
if (ch == '*') {
regex.append(".*");
} else if (ch == '?') {
regex.append('.');
} else if (metacharacters.indexOf(ch) != -1) {
regex.append('\\');
regex.append(ch);
} else {
regex.append(ch);
}
}
regex.append('$');
return regex;
}
bool isSilverSearcherAvailable()
{
QProcess silverSearcherProcess;
silverSearcherProcess.start("ag", {"--version"});
if (silverSearcherProcess.waitForFinished()) {
if (silverSearcherProcess.readAll().contains("ag version"))
return true;
}
return false;
}
void runSilverSeacher(FutureInterfaceType &fi, FileFindParameters parameters)
{
ProgressTimer progress(fi, 5);
const QString directory = parameters.additionalParameters.toString();
QStringList arguments = {"--parallel", "--ackmate"};
if (parameters.flags & FindCaseSensitively)
arguments << "-s";
else
arguments << "-i";
if (parameters.flags & FindWholeWords)
arguments << "-w";
if (!(parameters.flags & FindRegularExpression))
arguments << "-Q";
for (const QString &filter : parameters.exclusionFilters)
arguments << "--ignore" << filter;
FileName path = FileName::fromUserInput(FileUtils::normalizePathName(directory));
arguments << parameters.text << path.toString();
QString nameFiltersAsRegex;
for (const QString &filter : parameters.nameFilters)
nameFiltersAsRegex += QString("(%1)|").arg(convertWildcardToRegex(filter));
nameFiltersAsRegex.remove(nameFiltersAsRegex.length() - 1, 1);
arguments << "-G" << nameFiltersAsRegex;
QProcess process;
process.start("ag", arguments);
if (process.waitForFinished()) {
typedef QList<FileSearchResult> FileSearchResultList;
SilverSearcher::SilverSearcherOutputParser parser(process.readAll());
FileSearchResultList items = parser.parse();
if (!items.isEmpty())
fi.reportResult(items);
} else {
fi.reportCanceled();
}
}
} // namespace
namespace SilverSearcher {
FindInFilesSilverSearcher::FindInFilesSilverSearcher()
: m_widget(0),
m_path("ag"),
m_toolName("SilverSearcher")
{
m_widget = new QWidget;
FindInFiles *findInFiles = FindInFiles::instance();
QTC_ASSERT(findInFiles, return);
findInFiles->addSearchEngine(this);
setEnabled(isSilverSearcherAvailable());
if (!isEnabled()) {
auto layout = new QHBoxLayout(m_widget);
layout->setMargin(0);
QLabel *label = new QLabel(tr("SilverSearcher is not available on system"));
label->setStyleSheet("QLabel { color : red; }");
layout->addWidget(label);
}
}
FindInFilesSilverSearcher::~FindInFilesSilverSearcher()
{
}
QVariant FindInFilesSilverSearcher::parameters() const
{
return QVariant();
}
QString FindInFilesSilverSearcher::title() const
{
return silverSearcherName;
}
QString FindInFilesSilverSearcher::toolTip() const
{
return QString();
}
QWidget *FindInFilesSilverSearcher::widget() const
{
return m_widget;
}
void FindInFilesSilverSearcher::writeSettings(QSettings * /*settings*/) const
{
}
QFuture<FileSearchResultList> FindInFilesSilverSearcher::executeSearch(
const FileFindParameters &parameters, BaseFileFind * /*baseFileFind*/)
{
return Utils::runAsync(runSilverSeacher, parameters);
}
IEditor *FindInFilesSilverSearcher::openEditor(const SearchResultItem & /*item*/,
const FileFindParameters & /*parameters*/)
{
return 0;
}
void FindInFilesSilverSearcher::readSettings(QSettings */*settings*/)
{
}
} // namespace SilverSearcher
/****************************************************************************
**
** Copyright (C) 2017 Przemyslaw Gorszkowski <pgorszkowski@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.
**
****************************************************************************/
#pragma once
#include <coreplugin/find/ifindsupport.h>
#include <texteditor/basefilefind.h>
#include <utils/fileutils.h>
#include <QPointer>
namespace SilverSearcher {
class FindInFilesSilverSearcher : public TextEditor::SearchEngine
{
Q_OBJECT
public:
FindInFilesSilverSearcher();
~FindInFilesSilverSearcher() override;
// TextEditor::FileFindExtension
QString title() const override;
QString toolTip() const override;
QWidget *widget() const override;
QVariant parameters() const override;
void readSettings(QSettings *settings) override;
void writeSettings(QSettings *settings) const override;
QFuture<Utils::FileSearchResultList> executeSearch(
const TextEditor::FileFindParameters &parameters, TextEditor::BaseFileFind *) override;
Core::IEditor *openEditor(const Core::SearchResultItem &item,
const TextEditor::FileFindParameters &parameters) override;
private:
QPointer<Core::IFindSupport> m_currentFindSupport;
Utils::FileName m_directorySetting;
QPointer<QWidget> m_widget;
QString m_path;
QString m_toolName;
};
} // namespace SilverSearcher
/****************************************************************************
**
** 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.
**
****************************************************************************/
#include "outputparser_test.h"
#include "silversearcheroutputparser.h"
#include <QtTest>
namespace SilverSearcher {
namespace Internal {
void OutputParserTest::testNoResults()
{
const char parserOutput[] = "\n";
const QByteArray output(parserOutput);
SilverSearcher::SilverSearcherOutputParser ssop(output);
const QList<Utils::FileSearchResult> items = ssop.parse();
QCOMPARE(items.size(), 0);
}
void OutputParserTest::testOneFileWithOneMatch()
{
const char parserOutput[] = ":/file/path/to/filename.h\n"
"1;1 5:match\n";
const QByteArray output(parserOutput);
SilverSearcher::SilverSearcherOutputParser ssop(output);
const QList<Utils::FileSearchResult> items = ssop.parse();
QCOMPARE(items.size(), 1);
QCOMPARE(items[0].fileName, QStringLiteral("/file/path/to/filename.h"));
QCOMPARE(items[0].lineNumber, 1);
QCOMPARE(items[0].matchingLine, QStringLiteral("match"));
QCOMPARE(items[0].matchStart, 1);
QCOMPARE(items[0].matchLength, 5);
}
void OutputParserTest::testMultipleFilesWithOneMatch()
{
const char parserOutput[] = ":/file/path/to/filename1.h\n"
"1;1 5:match\n"
"\n"
":/file/path/to/filename2.h\n"
"2;2 5: match\n"
;
const QByteArray output(parserOutput);
SilverSearcher::SilverSearcherOutputParser ssop(output);
const QList<Utils::FileSearchResult> items = ssop.parse();
QCOMPARE(items.size(), 2);
QCOMPARE(items[0].fileName, QStringLiteral("/file/path/to/filename1.h"));
QCOMPARE(items[0].lineNumber, 1);
QCOMPARE(items[0].matchingLine, QStringLiteral("match"));
QCOMPARE(items[0].matchStart, 1);
QCOMPARE(items[0].matchLength, 5);
QCOMPARE(items[1].fileName, QStringLiteral("/file/path/to/filename2.h"));
QCOMPARE(items[1].lineNumber, 2);
QCOMPARE(items[1].matchingLine, QStringLiteral(" match"));
QCOMPARE(items[1].matchStart, 2);
QCOMPARE(items[1].matchLength, 5);
}
void OutputParserTest::testOneFileWithMultipleMatches()
{
const char parserOutput[] = ":/file/path/to/filename.h\n"
"1;1 5,7 5:match match\n";
const QByteArray output(parserOutput);
SilverSearcher::SilverSearcherOutputParser ssop(output);
const QList<Utils::FileSearchResult> items = ssop.parse();
QCOMPARE(items.size(), 2);
QCOMPARE(items[0].fileName, QStringLiteral("/file/path/to/filename.h"));
QCOMPARE(items[0].lineNumber, 1);
QCOMPARE(items[0].matchingLine, QStringLiteral("match match"));
QCOMPARE(items[0].matchStart, 1);
QCOMPARE(items[0].matchLength, 5);
QCOMPARE(items[1].fileName, QStringLiteral("/file/path/to/filename.h"));
QCOMPARE(items[1].lineNumber, 1);
QCOMPARE(items[1].matchingLine, QStringLiteral("match match"));
QCOMPARE(items[1].matchStart, 7);
QCOMPARE(items[1].matchLength, 5);
}
void OutputParserTest::testMultipleFilesWithMultipleMatches()
{
const char parserOutput[] = ":/file/path/to/filename1.h\n"
"1;1 5,7 5:match match\n"
"\n"
":/file/path/to/filename2.h\n"
"2;2 5,8 5: match match\n";
const QByteArray output(parserOutput);
SilverSearcher::SilverSearcherOutputParser ssop(output);
const QList<Utils::FileSearchResult> items = ssop.parse();
QCOMPARE(items.size(), 4);
QCOMPARE(items[0].fileName, QStringLiteral("/file/path/to/filename1.h"));
QCOMPARE(items[0].lineNumber, 1);
QCOMPARE(items[0].matchingLine, QStringLiteral("match match"));
QCOMPARE(items[0].matchStart, 1);
QCOMPARE(items[0].matchLength, 5);
QCOMPARE(items[1].fileName, QStringLiteral("/file/path/to/filename1.h"));
QCOMPARE(items[1].lineNumber, 1);
QCOMPARE(items[1].matchingLine, QStringLiteral("match match"));
QCOMPARE(items[1].matchStart, 7);
QCOMPARE(items[1].matchLength, 5);
QCOMPARE(items[2].fileName, QStringLiteral("/file/path/to/filename2.h"));
QCOMPARE(items[2].lineNumber, 2);
QCOMPARE(items[2].matchingLine, QStringLiteral(" match match"));
QCOMPARE(items[2].matchStart, 2);
QCOMPARE(items[2].matchLength, 5);
QCOMPARE(items[3].fileName, QStringLiteral("/file/path/to/filename2.h"));
QCOMPARE(items[3].lineNumber, 2);
QCOMPARE(items[3].matchingLine, QStringLiteral(" match match"));
QCOMPARE(items[3].matchStart, 8);
QCOMPARE(items[3].matchLength, 5);
}
} // namespace Internal
} // namespace SilverSearcher
/****************************************************************************
**
** Copyright (C) 2017 Przemyslaw Gorszkowski <pgorszkowski@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 <QObject>
namespace SilverSearcher {
namespace Internal {
class OutputParserTest : public QObject
{
Q_OBJECT
private slots:
void testNoResults();
void testOneFileWithOneMatch();
void testMultipleFilesWithOneMatch();
void testOneFileWithMultipleMatches();
void testMultipleFilesWithMultipleMatches();
};
} // namespace Internal
} // namespace SilverSearcher
include(../../qtcreatorplugin.pri)
SOURCES += \
findinfilessilversearcher.cpp \
silversearcheroutputparser.cpp \
silversearcherplugin.cpp
HEADERS += \
findinfilessilversearcher.h \
silversearcheroutputparser.h \
silversearcherplugin.h
equals(TEST, 1) {
SOURCES += outputparser_test.cpp
HEADERS += outputparser_test.h
}
import qbs 1.0
QtcPlugin {
name: "SilverSearcher"
Depends { name: "Utils" }
Depends { name: "Core" }
Depends { name: "TextEditor" }
files: [
"findinfilessilversearcher.cpp", "findinfilessilversearcher.h",
"silversearcheroutputparser.cpp", "silversearcheroutputparser.h",
"silversearcherplugin.cpp", "silversearcherplugin.h",
]
Group {
name: "Tests"
condition: qtc.testsEnabled
files: [
"outputparser_test.cpp",
"outputparser_test.h",
]
}
}
QTC_PLUGIN_NAME = SilverSearcher
QTC_LIB_DEPENDS += \
utils
QTC_PLUGIN_DEPENDS += \
coreplugin \
texteditor
/****************************************************************************
**
** Copyright (C) 2017 Przemyslaw Gorszkowski <pgorszkowski@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 "silversearcheroutputparser.h"
#include <QString>
namespace SilverSearcher {
SilverSearcherOutputParser::SilverSearcherOutputParser(
const QByteArray &output)
: output(output)
, outputSize(output.size())
{
}
QList<Utils::FileSearchResult> SilverSearcherOutputParser::parse()
{
while (index < outputSize - 1) {
if (output[index] == '\n') {
++index;
continue;
}
parseFilePath();
while (output[index] != '\n') {
parseLineNumber();
if (index >= outputSize - 1)
break;
int matches = parseMatches();
if (index >= outputSize - 1)
break;