aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/languageclient
diff options
context:
space:
mode:
authorDavid Schulz <david.schulz@qt.io>2019-04-30 11:05:43 +0200
committerDavid Schulz <david.schulz@qt.io>2019-05-14 08:53:34 +0000
commit7ad5313c3b3f16d65ae3a9d836f14e363a1ea413 (patch)
tree6551ba59badb9bd64be5dcf68d54f5877a27b5b8 /src/plugins/languageclient
parent24ac4b9e735abf6435f41d71190dfcede8d89e64 (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.h6
-rw-r--r--src/plugins/languageclient/languageclientmanager.h3
-rw-r--r--src/plugins/languageclient/locatorfilter.cpp179
-rw-r--r--src/plugins/languageclient/locatorfilter.h47
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