diff --git a/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp b/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp index e266e707680..f22cfd5c11e 100644 --- a/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp +++ b/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp @@ -6,7 +6,10 @@ #include "../coreplugintr.h" #include "../messagemanager.h" +#include + #include +#include #include #include #include @@ -233,6 +236,89 @@ SpotlightLocatorFilter::SpotlightLocatorFilter() reset(); } +static void matches(QPromise &promise, const LocatorStorage &storage, + const CommandLine &command) +{ + // If search string contains spaces, treat them as wildcard '*' and search in full path + const QString wildcardInput = QDir::fromNativeSeparators(storage.input()).replace(' ', '*'); + const Link inputLink = Link::fromString(wildcardInput, true); + const QString newInput = inputLink.targetFilePath.toString(); + const QRegularExpression regExp = ILocatorFilter::createRegExp(newInput); + if (!regExp.isValid()) + return; + + const bool hasPathSeparator = newInput.contains('/') || newInput.contains('*'); + LocatorFileCache::MatchedEntries entries = {}; + QEventLoop loop; + QtcProcess process; + process.setCommand(command); + process.setEnvironment(Environment::systemEnvironment()); // TODO: Is it needed? + + QObject::connect(&process, &QtcProcess::readyReadStandardOutput, &process, + [&, entriesPtr = &entries] { + QString output = process.readAllStandardOutput(); + output.replace("\r\n", "\n"); + const QStringList items = output.split('\n'); + const FilePaths filePaths = Utils::transform(items, &FilePath::fromUserInput); + LocatorFileCache::processFilePaths(promise.future(), filePaths, hasPathSeparator, regExp, + inputLink, entriesPtr); + if (promise.isCanceled()) + loop.exit(); + }); + QObject::connect(&process, &QtcProcess::done, &process, [&] { + if (process.result() != ProcessResult::FinishedWithSuccess) { + MessageManager::writeFlashing(Tr::tr("Locator: Error occurred when running \"%1\".") + .arg(command.executable().toUserOutput())); + } + loop.exit(); + }); + QFutureWatcher watcher; + watcher.setFuture(promise.future()); + QObject::connect(&watcher, &QFutureWatcherBase::canceled, &watcher, [&loop] { loop.exit(); }); + if (promise.isCanceled()) + return; + process.start(); + loop.exec(); + + for (auto &entry : entries) { + if (promise.isCanceled()) + return; + if (entry.size() < 1000) + Utils::sort(entry, LocatorFilterEntry::compareLexigraphically); + } + if (promise.isCanceled()) + return; + storage.reportOutput(std::accumulate(std::begin(entries), std::end(entries), + LocatorFilterEntries())); +} + +LocatorMatcherTasks SpotlightLocatorFilter::matchers() +{ + using namespace Tasking; + + TreeStorage storage; + + const auto onSetup = [storage, command = m_command, insensArgs = m_arguments, + sensArgs = m_caseSensitiveArguments](AsyncTask &async) { + const Link link = Link::fromString(storage->input(), true); + const FilePath input = link.targetFilePath; + if (input.isEmpty()) + return TaskAction::StopWithDone; + + // only pass the file name part to allow searches like "somepath/*foo" + const std::unique_ptr expander(createMacroExpander(input.fileName())); + const QString args = caseSensitivity(input.toString()) == Qt::CaseInsensitive + ? insensArgs : sensArgs; + const CommandLine cmd(FilePath::fromString(command), expander->expand(args), + CommandLine::Raw); + async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer()); + async.setConcurrentCallData(matches, *storage, cmd); + return TaskAction::Continue; + }; + + return {{Async(onSetup), storage}}; +} + void SpotlightLocatorFilter::prepareSearch(const QString &entry) { Link link = Utils::Link::fromString(entry, true); diff --git a/src/plugins/coreplugin/locator/spotlightlocatorfilter.h b/src/plugins/coreplugin/locator/spotlightlocatorfilter.h index 174319d4349..80d770dfa15 100644 --- a/src/plugins/coreplugin/locator/spotlightlocatorfilter.h +++ b/src/plugins/coreplugin/locator/spotlightlocatorfilter.h @@ -10,6 +10,7 @@ namespace Core { namespace Internal { +// TODO: Don't derive from BaseFileFilter, flatten the hierarchy class SpotlightLocatorFilter : public BaseFileFilter { Q_OBJECT @@ -26,6 +27,7 @@ protected: void restoreState(const QJsonObject &obj) final; private: + LocatorMatcherTasks matchers() final; void reset(); QString m_command;