diff options
author | David Schulz <david.schulz@qt.io> | 2019-04-30 11:05:43 +0200 |
---|---|---|
committer | David Schulz <david.schulz@qt.io> | 2019-05-14 08:53:34 +0000 |
commit | 7ad5313c3b3f16d65ae3a9d836f14e363a1ea413 (patch) | |
tree | 6551ba59badb9bd64be5dcf68d54f5877a27b5b8 /src/plugins/languageclient | |
parent | 24ac4b9e735abf6435f41d71190dfcede8d89e64 (diff) |
LanguageClient: add locator filters for symbols in workspace/project
Implement locator filter that is using the workspace/symbol request to
search for symbols in a project. In total three filters were added:
':': searches for all kind of symbols
'c': searches for classes and structs
'm': searches for methods and functions
Fixes: QTCREATORBUG-21915
Change-Id: Id62c9e0b1bcb29112e35b926b1a5cf04357751c4
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Diffstat (limited to 'src/plugins/languageclient')
-rw-r--r-- | src/plugins/languageclient/languageclient_global.h | 6 | ||||
-rw-r--r-- | src/plugins/languageclient/languageclientmanager.h | 3 | ||||
-rw-r--r-- | src/plugins/languageclient/locatorfilter.cpp | 179 | ||||
-rw-r--r-- | src/plugins/languageclient/locatorfilter.h | 47 |
4 files changed, 201 insertions, 34 deletions
diff --git a/src/plugins/languageclient/languageclient_global.h b/src/plugins/languageclient/languageclient_global.h index 7609bdf0844..685379804df 100644 --- a/src/plugins/languageclient/languageclient_global.h +++ b/src/plugins/languageclient/languageclient_global.h @@ -35,6 +35,12 @@ const char LANGUAGECLIENT_SETTINGS_PAGE[] = "LanguageClient.General"; const char LANGUAGECLIENT_SETTINGS_TR[] = QT_TRANSLATE_NOOP("LanguageClient", "Language Client"); const char LANGUAGECLIENT_DOCUMENT_FILTER_ID[] = "Current Document Symbols"; const char LANGUAGECLIENT_DOCUMENT_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("LanguageClient", "Symbols in Current Document"); +const char LANGUAGECLIENT_WORKSPACE_FILTER_ID[] = "Workspace Symbols"; +const char LANGUAGECLIENT_WORKSPACE_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("LanguageClient", "Symbols in Workspace"); +const char LANGUAGECLIENT_WORKSPACE_CLASS_FILTER_ID[] = "Workspace Classes and Structs"; +const char LANGUAGECLIENT_WORKSPACE_CLASS_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("LanguageClient", "Classes and Structs in Workspace"); +const char LANGUAGECLIENT_WORKSPACE_METHOD_FILTER_ID[] = "Workspace Functions and Methods"; +const char LANGUAGECLIENT_WORKSPACE_METHOD_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("LanguageClient", "Functions and Methods in Workspace"); } // namespace Constants } // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclientmanager.h b/src/plugins/languageclient/languageclientmanager.h index 6e1bb87eb00..a39d88f3c3f 100644 --- a/src/plugins/languageclient/languageclientmanager.h +++ b/src/plugins/languageclient/languageclientmanager.h @@ -107,5 +107,8 @@ private: QMap<QString, QVector<Client *>> m_clientsForSetting; QHash<LanguageServerProtocol::MessageId, QList<Client *>> m_exclusiveRequests; DocumentLocatorFilter m_currentDocumentLocatorFilter; + WorkspaceLocatorFilter m_workspaceLocatorFilter; + WorkspaceClassLocatorFilter m_workspaceClassLocatorFilter; + WorkspaceMethodLocatorFilter m_workspaceMethodLocatorFilter; }; } // namespace LanguageClient diff --git a/src/plugins/languageclient/locatorfilter.cpp b/src/plugins/languageclient/locatorfilter.cpp index e3f34eba5ee..92da6a642c0 100644 --- a/src/plugins/languageclient/locatorfilter.cpp +++ b/src/plugins/languageclient/locatorfilter.cpp @@ -30,6 +30,7 @@ #include "languageclientutils.h" #include <coreplugin/editormanager/editormanager.h> +#include <languageserverprotocol/servercapabilities.h> #include <texteditor/textdocument.h> #include <texteditor/texteditor.h> #include <utils/fuzzymatcher.h> @@ -96,44 +97,24 @@ void DocumentLocatorFilter::resetSymbols() m_currentSymbols.reset(); } -template<class T> -QList<Core::LocatorFilterEntry> DocumentLocatorFilter::generateEntries(const QList<T> &list, - const QString &filter) -{ - QList<Core::LocatorFilterEntry> entries; - FuzzyMatcher::CaseSensitivity caseSensitivity - = DocumentLocatorFilter::caseSensitivity(filter) == Qt::CaseSensitive - ? FuzzyMatcher::CaseSensitivity::CaseSensitive - : FuzzyMatcher::CaseSensitivity::CaseInsensitive; - const QRegularExpression regexp = FuzzyMatcher::createRegExp(filter, caseSensitivity); - if (!regexp.isValid()) - return entries; - - for (const T &item : list) { - QRegularExpressionMatch match = regexp.match(item.name()); - if (match.hasMatch()) - entries << generateLocatorEntry(item); - } - return entries; -} - -Core::LocatorFilterEntry DocumentLocatorFilter::generateLocatorEntry(const SymbolInformation &info) +Core::LocatorFilterEntry generateLocatorEntry(const SymbolInformation &info, + Core::ILocatorFilter *filter) { Core::LocatorFilterEntry entry; - entry.filter = this; + entry.filter = filter; entry.displayName = info.name(); if (Utils::optional<QString> container = info.containerName()) entry.extraInfo = container.value_or(QString()); entry.displayIcon = symbolIcon(info.kind()); - const Position &pos = info.location().range().start(); - entry.internalData = qVariantFromValue(Utils::LineColumn(pos.line(), pos.character())); + entry.internalData = qVariantFromValue(info.location().toLink()); return entry; } -Core::LocatorFilterEntry DocumentLocatorFilter::generateLocatorEntry(const DocumentSymbol &info) +Core::LocatorFilterEntry generateLocatorEntry(const DocumentSymbol &info, + Core::ILocatorFilter *filter) { Core::LocatorFilterEntry entry; - entry.filter = this; + entry.filter = filter; entry.displayName = info.name(); if (Utils::optional<QString> detail = info.detail()) entry.extraInfo = detail.value_or(QString()); @@ -143,6 +124,27 @@ Core::LocatorFilterEntry DocumentLocatorFilter::generateLocatorEntry(const Docum return entry; } +template<class T> +QList<Core::LocatorFilterEntry> DocumentLocatorFilter::generateEntries(const QList<T> &list, + const QString &filter) +{ + QList<Core::LocatorFilterEntry> entries; + FuzzyMatcher::CaseSensitivity caseSensitivity + = ILocatorFilter::caseSensitivity(filter) == Qt::CaseSensitive + ? FuzzyMatcher::CaseSensitivity::CaseSensitive + : FuzzyMatcher::CaseSensitivity::CaseInsensitive; + const QRegularExpression regexp = FuzzyMatcher::createRegExp(filter, caseSensitivity); + if (!regexp.isValid()) + return entries; + + for (const T &item : list) { + QRegularExpressionMatch match = regexp.match(item.name()); + if (match.hasMatch()) + entries << generateLocatorEntry(item, this); + } + return entries; +} + void DocumentLocatorFilter::prepareSearch(const QString &/*entry*/) { QMutexLocker locker(&m_mutex); @@ -188,12 +190,127 @@ void DocumentLocatorFilter::accept(Core::LocatorFilterEntry selection, int * /*selectionStart*/, int * /*selectionLength*/) const { - auto lineColumn = qvariant_cast<Utils::LineColumn>(selection.internalData); - Core::EditorManager::openEditorAt(m_currentUri.toFileName().toString(), - lineColumn.line + 1, - lineColumn.column); + if (selection.internalData.canConvert<Utils::LineColumn>()) { + auto lineColumn = qvariant_cast<Utils::LineColumn>(selection.internalData); + Core::EditorManager::openEditorAt(m_currentUri.toFileName().toString(), + lineColumn.line + 1, + lineColumn.column); + } else if (selection.internalData.canConvert<Utils::Link>()) { + auto link = qvariant_cast<Utils::Link>(selection.internalData); + Core::EditorManager::openEditorAt(link.targetFileName, link.targetLine, link.targetColumn); + } } void DocumentLocatorFilter::refresh(QFutureInterface<void> & /*future*/) {} +WorkspaceLocatorFilter::WorkspaceLocatorFilter() + : WorkspaceLocatorFilter(QVector<SymbolKind>()) +{} + +WorkspaceLocatorFilter::WorkspaceLocatorFilter(const QVector<SymbolKind> &filter) + : m_filterKinds(filter) +{ + setId(Constants::LANGUAGECLIENT_WORKSPACE_FILTER_ID); + setDisplayName(Constants::LANGUAGECLIENT_WORKSPACE_FILTER_DISPLAY_NAME); + setShortcutString(":"); + setIncludedByDefault(false); + setPriority(ILocatorFilter::Low); +} + +void WorkspaceLocatorFilter::prepareSearch(const QString &entry) +{ + m_pendingRequests.clear(); + m_results.clear(); + + WorkspaceSymbolParams params; + params.setQuery(entry); + + QMutexLocker locker(&m_mutex); + for (auto client : Utils::filtered(LanguageClientManager::clients(), &Client::reachable)) { + if (client->capabilities().workspaceSymbolProvider().value_or(false)) { + WorkspaceSymbolRequest request(params); + request.setResponseCallback( + [this, client](const WorkspaceSymbolRequest::Response &response) { + handleResponse(client, response); + }); + m_pendingRequests[client] = request.id(); + client->sendContent(request); + } + } +} + +QList<Core::LocatorFilterEntry> WorkspaceLocatorFilter::matchesFor( + QFutureInterface<Core::LocatorFilterEntry> &future, const QString & /*entry*/) +{ + QMutexLocker locker(&m_mutex); + if (!m_pendingRequests.isEmpty()) { + QEventLoop loop; + connect(this, &WorkspaceLocatorFilter::allRequestsFinished, &loop, [&]() { loop.exit(1); }); + QFutureWatcher<Core::LocatorFilterEntry> watcher; + watcher.setFuture(future.future()); + connect(&watcher, + &QFutureWatcher<Core::LocatorFilterEntry>::canceled, + &loop, + &QEventLoop::quit); + locker.unlock(); + if (!loop.exec()) + return {}; + + locker.relock(); + } + + + if (!m_filterKinds.isEmpty()) { + m_results = Utils::filtered(m_results, [&](const SymbolInformation &info) { + return m_filterKinds.contains(SymbolKind(info.kind())); + }); + } + return Utils::transform(m_results, + [this](const SymbolInformation &info) { + return generateLocatorEntry(info, this); + }) + .toList(); +} + +void WorkspaceLocatorFilter::accept(Core::LocatorFilterEntry selection, + QString * /*newText*/, + int * /*selectionStart*/, + int * /*selectionLength*/) const +{ + if (selection.internalData.canConvert<Utils::Link>()) { + auto link = qvariant_cast<Utils::Link>(selection.internalData); + Core::EditorManager::openEditorAt(link.targetFileName, link.targetLine, link.targetColumn); + } +} + +void WorkspaceLocatorFilter::refresh(QFutureInterface<void> & /*future*/) {} + +void WorkspaceLocatorFilter::handleResponse(Client *client, + const WorkspaceSymbolRequest::Response &response) +{ + QMutexLocker locker(&m_mutex); + m_pendingRequests.remove(client); + auto result = response.result().value_or(LanguageClientArray<SymbolInformation>()); + if (!result.isNull()) + m_results.append(result.toList().toVector()); + if (m_pendingRequests.isEmpty()) + emit allRequestsFinished(QPrivateSignal()); +} + +WorkspaceClassLocatorFilter::WorkspaceClassLocatorFilter() + : WorkspaceLocatorFilter({SymbolKind::Class, SymbolKind::Struct}) +{ + setId(Constants::LANGUAGECLIENT_WORKSPACE_CLASS_FILTER_ID); + setDisplayName(Constants::LANGUAGECLIENT_WORKSPACE_CLASS_FILTER_DISPLAY_NAME); + setShortcutString("c"); +} + +WorkspaceMethodLocatorFilter::WorkspaceMethodLocatorFilter() + : WorkspaceLocatorFilter({SymbolKind::Method, SymbolKind::Function, SymbolKind::Constructor}) +{ + setId(Constants::LANGUAGECLIENT_WORKSPACE_METHOD_FILTER_ID); + setDisplayName(Constants::LANGUAGECLIENT_WORKSPACE_METHOD_FILTER_DISPLAY_NAME); + setShortcutString("m"); +} + } // namespace LanguageClient diff --git a/src/plugins/languageclient/locatorfilter.h b/src/plugins/languageclient/locatorfilter.h index 17191eb3a28..a3aa9cd5975 100644 --- a/src/plugins/languageclient/locatorfilter.h +++ b/src/plugins/languageclient/locatorfilter.h @@ -30,6 +30,7 @@ #include <coreplugin/locator/ilocatorfilter.h> #include <languageserverprotocol/lsptypes.h> #include <languageserverprotocol/languagefeatures.h> +#include <languageserverprotocol/workspace.h> namespace Core { class IEditor; } @@ -63,9 +64,6 @@ private: const LanguageServerProtocol::DocumentSymbolsResult &symbols); void resetSymbols(); - Core::LocatorFilterEntry generateLocatorEntry( - const LanguageServerProtocol::SymbolInformation &info); - Core::LocatorFilterEntry generateLocatorEntry(const LanguageServerProtocol::DocumentSymbol &info); template<class T> QList<Core::LocatorFilterEntry> generateEntries(const QList<T> &list, const QString &filter); @@ -75,4 +73,47 @@ private: Utils::optional<LanguageServerProtocol::DocumentSymbolsResult> m_currentSymbols; }; +class WorkspaceLocatorFilter : public Core::ILocatorFilter +{ + Q_OBJECT +public: + WorkspaceLocatorFilter(); + + void prepareSearch(const QString &entry) override; + QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, + const QString &entry) override; + void accept(Core::LocatorFilterEntry selection, + QString *newText, + int *selectionStart, + int *selectionLength) const override; + void refresh(QFutureInterface<void> &future) override; + +signals: + void allRequestsFinished(QPrivateSignal); + +protected: + explicit WorkspaceLocatorFilter(const QVector<LanguageServerProtocol::SymbolKind> &filter); + +private: + void handleResponse(Client *client, + const LanguageServerProtocol::WorkspaceSymbolRequest::Response &response); + + QMutex m_mutex; + QMap<Client *, LanguageServerProtocol::MessageId> m_pendingRequests; + QVector<LanguageServerProtocol::SymbolInformation> m_results; + QVector<LanguageServerProtocol::SymbolKind> m_filterKinds; +}; + +class WorkspaceClassLocatorFilter : public WorkspaceLocatorFilter +{ +public: + WorkspaceClassLocatorFilter(); +}; + +class WorkspaceMethodLocatorFilter : public WorkspaceLocatorFilter +{ +public: + WorkspaceMethodLocatorFilter(); +}; + } // namespace LanguageClient |