diff --git a/src/plugins/coreplugin/CMakeLists.txt b/src/plugins/coreplugin/CMakeLists.txt index 3d3e6f3a289..fdf91cee044 100644 --- a/src/plugins/coreplugin/CMakeLists.txt +++ b/src/plugins/coreplugin/CMakeLists.txt @@ -178,7 +178,7 @@ extend_qtc_plugin(Core DEPENDS ${FWAppKit} SOURCES progressmanager/progressmanager_mac.mm - locator/spotlightlocatorfilter.h locator/spotlightlocatorfilter.mm + locator/spotlightlocatorfilter.h locator/spotlightlocatorfilter.cpp ) extend_qtc_plugin(Core diff --git a/src/plugins/coreplugin/coreplugin.qbs b/src/plugins/coreplugin/coreplugin.qbs index 87878791c0c..3936a1739af 100644 --- a/src/plugins/coreplugin/coreplugin.qbs +++ b/src/plugins/coreplugin/coreplugin.qbs @@ -375,7 +375,7 @@ Project { condition: qbs.targetOS.contains("macos") files: [ "locator/spotlightlocatorfilter.h", - "locator/spotlightlocatorfilter.mm", + "locator/spotlightlocatorfilter.cpp", ] } diff --git a/src/plugins/coreplugin/locator/locator.pri b/src/plugins/coreplugin/locator/locator.pri index fea0082722b..376ba0d261a 100644 --- a/src/plugins/coreplugin/locator/locator.pri +++ b/src/plugins/coreplugin/locator/locator.pri @@ -53,5 +53,5 @@ equals(TEST, 1) { osx { HEADERS += $$PWD/spotlightlocatorfilter.h - OBJECTIVE_SOURCES += $$PWD/spotlightlocatorfilter.mm + SOURCES += $$PWD/spotlightlocatorfilter.cpp } diff --git a/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp b/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp new file mode 100644 index 00000000000..5af7b6a45ba --- /dev/null +++ b/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp @@ -0,0 +1,203 @@ +/**************************************************************************** +** +** 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 "spotlightlocatorfilter.h" + +#include "../messagemanager.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace Utils; + +namespace Core { +namespace Internal { + +// #pragma mark -- SpotlightIterator + +class SpotlightIterator : public BaseFileFilter::Iterator +{ +public: + SpotlightIterator(const QStringList &command); + ~SpotlightIterator() override; + + void toFront() override; + bool hasNext() const override; + Utils::FilePath next() override; + Utils::FilePath filePath() const override; + + void scheduleKillProcess(); + void killProcess(); + +private: + void ensureNext(); + + std::unique_ptr m_process; + QMutex m_mutex; + QWaitCondition m_waitForItems; + QList m_queue; + QList m_filePaths; + int m_index; + bool m_finished; +}; + +SpotlightIterator::SpotlightIterator(const QStringList &command) + : m_index(-1) + , m_finished(false) +{ + QTC_ASSERT(!command.isEmpty(), return ); + m_process.reset(new QProcess); + m_process->setProgram( + Utils::Environment::systemEnvironment().searchInPath(command.first()).toString()); + m_process->setArguments(command.mid(1)); + m_process->setProcessEnvironment(Utils::Environment::systemEnvironment().toProcessEnvironment()); + QObject::connect(m_process.get(), + QOverload::of(&QProcess::finished), + [this] { scheduleKillProcess(); }); + QObject::connect(m_process.get(), &QProcess::errorOccurred, [this, command] { + MessageManager::writeFlashing( + SpotlightLocatorFilter::tr("Locator: Error occurred when running \"%1\".") + .arg(command.first())); + scheduleKillProcess(); + }); + QObject::connect(m_process.get(), &QProcess::readyReadStandardOutput, [this] { + const QStringList items = QString::fromUtf8(m_process->readAllStandardOutput()).split('\n'); + QMutexLocker lock(&m_mutex); + m_queue.append(Utils::transform(items, &FilePath::fromString)); + if (m_filePaths.size() + m_queue.size() > 10000) // limit the amount of data + scheduleKillProcess(); + m_waitForItems.wakeAll(); + }); + m_process->start(QIODevice::ReadOnly); +} + +SpotlightIterator::~SpotlightIterator() +{ + killProcess(); +} + +void SpotlightIterator::toFront() +{ + m_index = -1; +} + +bool SpotlightIterator::hasNext() const +{ + auto that = const_cast(this); + that->ensureNext(); + return (m_index + 1 < m_filePaths.size()); +} + +Utils::FilePath SpotlightIterator::next() +{ + ensureNext(); + ++m_index; + QTC_ASSERT(m_index < m_filePaths.size(), return FilePath()); + return m_filePaths.at(m_index); +} + +Utils::FilePath SpotlightIterator::filePath() const +{ + QTC_ASSERT(m_index < m_filePaths.size(), return FilePath()); + return m_filePaths.at(m_index); +} + +void SpotlightIterator::scheduleKillProcess() +{ + QTimer::singleShot(0, m_process.get(), [this] { killProcess(); }); +} + +void SpotlightIterator::killProcess() +{ + if (!m_process) + return; + m_process->disconnect(); + QMutexLocker lock(&m_mutex); + m_finished = true; + m_waitForItems.wakeAll(); + if (m_process->state() == QProcess::NotRunning) + m_process.reset(); + else + Reaper::reap(m_process.release()); +} + +void SpotlightIterator::ensureNext() +{ + if (m_index + 1 < m_filePaths.size()) // nothing to do + return; + // check if there are items in the queue, otherwise wait for some + m_mutex.lock(); + if (m_queue.isEmpty() && !m_finished) + m_waitForItems.wait(&m_mutex); + m_filePaths.append(m_queue); + m_queue.clear(); + m_mutex.unlock(); +} + +// #pragma mark -- SpotlightLocatorFilter + +SpotlightLocatorFilter::SpotlightLocatorFilter() +{ + setId("SpotlightFileNamesLocatorFilter"); + setDisplayName(tr("Spotlight File Name Index")); + setShortcutString("md"); +} + +void SpotlightLocatorFilter::prepareSearch(const QString &entry) +{ + const EditorManager::FilePathInfo fp = EditorManager::splitLineAndColumnNumber(entry); + if (fp.filePath.isEmpty()) { + setFileIterator(new BaseFileFilter::ListIterator(Utils::FilePaths())); + } else { + // only pass the file name part to spotlight to allow searches like "somepath/*foo" + int lastSlash = fp.filePath.lastIndexOf(QLatin1Char('/')); + QString quoted = fp.filePath.mid(lastSlash + 1); + quoted.replace('\\', "\\\\").replace('\'', "\\\'").replace('\"', "\\\""); + setFileIterator(new SpotlightIterator( + {"mdfind", + QString("kMDItemFSName = '*%1*'%2") + .arg(quoted, + caseSensitivity(fp.filePath) == Qt::CaseInsensitive ? QString("c") + : QString())})); + } + BaseFileFilter::prepareSearch(entry); +} + +void SpotlightLocatorFilter::refresh(QFutureInterface &future) +{ + Q_UNUSED(future) +} + +} // Internal +} // Core diff --git a/src/plugins/coreplugin/locator/spotlightlocatorfilter.h b/src/plugins/coreplugin/locator/spotlightlocatorfilter.h index 1c6dd2876cf..5f22032d42d 100644 --- a/src/plugins/coreplugin/locator/spotlightlocatorfilter.h +++ b/src/plugins/coreplugin/locator/spotlightlocatorfilter.h @@ -34,13 +34,7 @@ class SpotlightLocatorFilter : public BaseFileFilter { Q_OBJECT public: - SpotlightLocatorFilter() - { - // tr() must not be placed in .mm file. - setId("SpotlightFileNamesLocatorFilter"); - setDisplayName(tr("Spotlight File Name Index")); - setShortcutString("md"); - } + SpotlightLocatorFilter(); void prepareSearch(const QString &entry) override; void refresh(QFutureInterface &future) override; diff --git a/src/plugins/coreplugin/locator/spotlightlocatorfilter.mm b/src/plugins/coreplugin/locator/spotlightlocatorfilter.mm deleted file mode 100644 index 870e83646c6..00000000000 --- a/src/plugins/coreplugin/locator/spotlightlocatorfilter.mm +++ /dev/null @@ -1,202 +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. -** -****************************************************************************/ - -#include "spotlightlocatorfilter.h" - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -namespace Core { -namespace Internal { - -// #pragma mark -- SpotlightIterator - -class SpotlightIterator : public BaseFileFilter::Iterator -{ -public: - SpotlightIterator(const QString &expression); - ~SpotlightIterator() override; - - void toFront() override; - bool hasNext() const override; - Utils::FilePath next() override; - Utils::FilePath filePath() const override; - -private: - void ensureNext(); - - NSMetadataQuery *m_query; - id m_progressObserver; - id m_finishObserver; - QMutex m_mutex; - QWaitCondition m_waitForItems; - NSMutableArray *m_queue; - QStringList m_filePaths; - QStringList m_fileNames; - int m_index; - unsigned int m_queueIndex; - bool m_finished; -}; - -SpotlightIterator::SpotlightIterator(const QString &expression) - : m_index(-1), - m_queueIndex(-1), - m_finished(false) -{ - @autoreleasepool { - NSPredicate *predicate = [NSPredicate predicateWithFormat:expression.toNSString()]; - m_query = [[NSMetadataQuery alloc] init]; - m_query.predicate = predicate; - m_query.searchScopes = [NSArray arrayWithObject:NSMetadataQueryLocalComputerScope]; - m_queue = [[NSMutableArray alloc] init]; - m_progressObserver = [[[NSNotificationCenter defaultCenter] - addObserverForName:NSMetadataQueryGatheringProgressNotification - object:m_query - queue:nil - usingBlock:^(NSNotification *note) { - [m_query disableUpdates]; - QMutexLocker lock(&m_mutex); Q_UNUSED(lock) - [m_queue addObjectsFromArray:[note.userInfo objectForKey:(NSString *)kMDQueryUpdateAddedItems]]; - [m_query enableUpdates]; - m_waitForItems.wakeAll(); - }] retain]; - m_finishObserver = [[[NSNotificationCenter defaultCenter] - addObserverForName:NSMetadataQueryDidFinishGatheringNotification - object:m_query - queue:nil - usingBlock:^(NSNotification *) { - QMutexLocker lock(&m_mutex); Q_UNUSED(lock) - m_finished = true; - m_waitForItems.wakeAll(); - }] retain]; - [m_query startQuery]; - } -} - -SpotlightIterator::~SpotlightIterator() -{ - [[NSNotificationCenter defaultCenter] removeObserver:m_progressObserver]; - [m_progressObserver release]; - [[NSNotificationCenter defaultCenter] removeObserver:m_finishObserver]; - [m_finishObserver release]; - [m_query stopQuery]; - [m_query release]; - [m_queue release]; -} - -void SpotlightIterator::toFront() -{ - m_index = -1; -} - -bool SpotlightIterator::hasNext() const -{ - auto that = const_cast(this); - that->ensureNext(); - return (m_index + 1 < m_filePaths.size()); -} - -Utils::FilePath SpotlightIterator::next() -{ - ensureNext(); - ++m_index; - QTC_ASSERT(m_index < m_filePaths.size(), return Utils::FilePath()); - return Utils::FilePath::fromString(m_filePaths.at(m_index)); -} - -Utils::FilePath SpotlightIterator::filePath() const -{ - QTC_ASSERT(m_index < m_filePaths.size(), return Utils::FilePath()); - return Utils::FilePath::fromString(m_filePaths.at(m_index)); -} - -void SpotlightIterator::ensureNext() -{ - if (m_index + 1 < m_filePaths.size()) // nothing to do - return; - if (m_index >= 10000) // limit the amount of data that is passed on - return; - @autoreleasepool { - // check if there are items in the queue, otherwise wait for some - m_mutex.lock(); - bool itemAvailable = (m_queueIndex + 1 < m_queue.count); - if (!itemAvailable && !m_finished) { - m_waitForItems.wait(&m_mutex); - itemAvailable = (m_queueIndex + 1 < m_queue.count); - } - if (itemAvailable) { - ++m_queueIndex; - NSMetadataItem *item = [m_queue objectAtIndex:m_queueIndex]; - m_filePaths.append(QString::fromNSString([item valueForAttribute:NSMetadataItemPathKey])); - m_fileNames.append(QString::fromNSString([item valueForAttribute:NSMetadataItemFSNameKey])); - - } - m_mutex.unlock(); - } -} - -// #pragma mark -- SpotlightLocatorFilter - -void SpotlightLocatorFilter::prepareSearch(const QString &entry) -{ - const EditorManager::FilePathInfo fp = EditorManager::splitLineAndColumnNumber(entry); - if (fp.filePath.isEmpty()) { - setFileIterator(new BaseFileFilter::ListIterator(Utils::FilePaths())); - } else { - // only pass the file name part to spotlight to allow searches like "somepath/*foo" - int lastSlash = fp.filePath.lastIndexOf(QLatin1Char('/')); - QString quoted = fp.filePath.mid(lastSlash + 1); - quoted.replace(QLatin1Char('\\'), QLatin1String("\\\\")) - .replace(QLatin1Char('\''), QLatin1String("\\\'")) - .replace(QLatin1Char('\"'), QLatin1String("\\\"")); - setFileIterator(new SpotlightIterator( - QString::fromLatin1("kMDItemFSName like%1 \"*%2*\"") - .arg(caseSensitivity(fp.filePath) == Qt::CaseInsensitive ? QLatin1String("[c]") - : QString()) - .arg(quoted))); - } - BaseFileFilter::prepareSearch(entry); -} - -void SpotlightLocatorFilter::refresh(QFutureInterface &future) -{ - Q_UNUSED(future) -} - -} // Internal -} // Core