diff options
author | David Schulz <david.schulz@qt.io> | 2019-04-05 10:05:25 +0200 |
---|---|---|
committer | David Schulz <david.schulz@qt.io> | 2019-04-30 05:19:31 +0000 |
commit | 69e880c050bd5afa7930600cab30370c202c11fd (patch) | |
tree | 75143aac9a0a964897f58009314016b649cb02b3 /src/plugins/languageclient | |
parent | ac91f6f4528c3f2ac34d3caeb0d65abb8167db3b (diff) | |
download | qt-creator-69e880c050bd5afa7930600cab30370c202c11fd.tar.gz |
LanguageClient: add current document locator filter
Change-Id: I20442a254e4e016088b2da845d87af6e9f836731
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Diffstat (limited to 'src/plugins/languageclient')
-rw-r--r-- | src/plugins/languageclient/client.cpp | 2 | ||||
-rw-r--r-- | src/plugins/languageclient/languageclient.pro | 6 | ||||
-rw-r--r-- | src/plugins/languageclient/languageclient.qbs | 2 | ||||
-rw-r--r-- | src/plugins/languageclient/languageclient_global.h | 2 | ||||
-rw-r--r-- | src/plugins/languageclient/languageclientmanager.cpp | 19 | ||||
-rw-r--r-- | src/plugins/languageclient/languageclientmanager.h | 3 | ||||
-rw-r--r-- | src/plugins/languageclient/languageclientoutline.cpp | 41 | ||||
-rw-r--r-- | src/plugins/languageclient/languageclientutils.cpp | 41 | ||||
-rw-r--r-- | src/plugins/languageclient/languageclientutils.h | 1 | ||||
-rw-r--r-- | src/plugins/languageclient/locatorfilter.cpp | 197 | ||||
-rw-r--r-- | src/plugins/languageclient/locatorfilter.h | 78 |
11 files changed, 349 insertions, 43 deletions
diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index c84bc04866..b588c35563 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -1077,13 +1077,13 @@ void Client::intializeCallback(const InitializeRequest::Response &initResponse) qCDebug(LOGLSPCLIENT) << "language server " << m_displayName << " initialized"; m_state = Initialized; sendContent(InitializeNotification()); - emit initialized(m_serverCapabilities); for (auto openedDocument : Core::DocumentModel::openedDocuments()) { if (openDocument(openedDocument)) { for (Core::IEditor *editor : Core::DocumentModel::editorsForDocument(openedDocument)) updateEditorToolBar(editor); } } + emit initialized(m_serverCapabilities); } void Client::shutDownCallback(const ShutdownRequest::Response &shutdownResponse) diff --git a/src/plugins/languageclient/languageclient.pro b/src/plugins/languageclient/languageclient.pro index 912873134b..7ff9bf3574 100644 --- a/src/plugins/languageclient/languageclient.pro +++ b/src/plugins/languageclient/languageclient.pro @@ -14,7 +14,8 @@ HEADERS += \ languageclientplugin.h \ languageclientquickfix.h \ languageclientsettings.h \ - languageclientutils.h + languageclientutils.h \ + locatorfilter.h SOURCES += \ @@ -28,7 +29,8 @@ SOURCES += \ languageclientplugin.cpp \ languageclientquickfix.cpp \ languageclientsettings.cpp \ - languageclientutils.cpp + languageclientutils.cpp \ + locatorfilter.cpp RESOURCES += \ languageclient.qrc diff --git a/src/plugins/languageclient/languageclient.qbs b/src/plugins/languageclient/languageclient.qbs index b2520fba34..b743675c09 100644 --- a/src/plugins/languageclient/languageclient.qbs +++ b/src/plugins/languageclient/languageclient.qbs @@ -38,5 +38,7 @@ QtcPlugin { "languageclientsettings.h", "languageclientutils.cpp", "languageclientutils.h", + "locatorfilter.cpp", + "locatorfilter.h", ] } diff --git a/src/plugins/languageclient/languageclient_global.h b/src/plugins/languageclient/languageclient_global.h index afb91fb78d..7609bdf084 100644 --- a/src/plugins/languageclient/languageclient_global.h +++ b/src/plugins/languageclient/languageclient_global.h @@ -33,6 +33,8 @@ namespace Constants { const char LANGUAGECLIENT_SETTINGS_CATEGORY[] = "ZY.LanguageClient"; 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"); } // namespace Constants } // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclientmanager.cpp b/src/plugins/languageclient/languageclientmanager.cpp index 75fae98545..789e5758f5 100644 --- a/src/plugins/languageclient/languageclientmanager.cpp +++ b/src/plugins/languageclient/languageclientmanager.cpp @@ -110,6 +110,11 @@ void LanguageClientManager::startClient(Client *client) client->initialize(); else managerInstance->clientFinished(client); + + connect(client, + &Client::initialized, + &managerInstance->m_currentDocumentLocatorFilter, + &DocumentLocatorFilter::updateCurrentClient); } void LanguageClientManager::startClient(BaseSettings *setting, ProjectExplorer::Project *project) @@ -275,6 +280,20 @@ const BaseSettings *LanguageClientManager::settingForClient(Client *client) return nullptr; } +Client *LanguageClientManager::clientForEditor(Core::IEditor *iEditor) +{ + QTC_ASSERT(managerInstance, return nullptr); + + auto editor = qobject_cast<TextEditor::BaseTextEditor *>(iEditor); + if (!editor) + return nullptr; + + return Utils::findOrDefault(managerInstance->reachableClients(), + [doc = editor->textDocument()](Client *client) { + return client->documentOpen(doc); + }); +} + QVector<Client *> LanguageClientManager::reachableClients() { return Utils::filtered(m_clients, &Client::reachable); diff --git a/src/plugins/languageclient/languageclientmanager.h b/src/plugins/languageclient/languageclientmanager.h index 0252c996ee..fe24b03af7 100644 --- a/src/plugins/languageclient/languageclientmanager.h +++ b/src/plugins/languageclient/languageclientmanager.h @@ -27,6 +27,7 @@ #include "client.h" #include "languageclientsettings.h" +#include "locatorfilter.h" #include <coreplugin/id.h> @@ -74,6 +75,7 @@ public: static QList<BaseSettings *> currentSettings(); static QVector<QPointer<Client> > clientForSetting(const BaseSettings *setting); static const BaseSettings *settingForClient(Client *setting); + static Client *clientForEditor(Core::IEditor *editor); signals: void shutdownFinished(); @@ -103,5 +105,6 @@ private: QList<BaseSettings *> m_currentSettings; // owned QMap<QString, QVector<QPointer<Client>>> m_clientsForSetting; QHash<LanguageServerProtocol::MessageId, QList<Client *>> m_exclusiveRequests; + DocumentLocatorFilter m_currentDocumentLocatorFilter; }; } // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclientoutline.cpp b/src/plugins/languageclient/languageclientoutline.cpp index e08587c1d3..53e771bfd1 100644 --- a/src/plugins/languageclient/languageclientoutline.cpp +++ b/src/plugins/languageclient/languageclientoutline.cpp @@ -26,6 +26,7 @@ #include "languageclientoutline.h" #include "languageclientmanager.h" +#include "languageclientutils.h" #include <coreplugin/find/itemviewfind.h> #include <coreplugin/editormanager/ieditor.h> @@ -43,46 +44,6 @@ using namespace LanguageServerProtocol; namespace LanguageClient { -static const QIcon symbolIcon(int type) -{ - using namespace Utils::CodeModelIcon; - static QMap<SymbolKind, QIcon> icons; - if (type < int(SymbolKind::FirstSymbolKind) || type > int(SymbolKind::LastSymbolKind)) - return {}; - auto kind = static_cast<SymbolKind>(type); - if (icons.contains(kind)) { - switch (kind) { - case SymbolKind::File: icons[kind] = Utils::Icons::NEWFILE.icon(); break; - case SymbolKind::Module: icons[kind] = iconForType(Namespace); break; - case SymbolKind::Namespace: icons[kind] = iconForType(Namespace); break; - case SymbolKind::Package: icons[kind] = iconForType(Namespace); break; - case SymbolKind::Class: icons[kind] = iconForType(Class); break; - case SymbolKind::Method: icons[kind] = iconForType(FuncPublic); break; - case SymbolKind::Property: icons[kind] = iconForType(Property); break; - case SymbolKind::Field: icons[kind] = iconForType(VarPublic); break; - case SymbolKind::Constructor: icons[kind] = iconForType(Class); break; - case SymbolKind::Enum: icons[kind] = iconForType(Enum); break; - case SymbolKind::Interface: icons[kind] = iconForType(Class); break; - case SymbolKind::Function: icons[kind] = iconForType(FuncPublic); break; - case SymbolKind::Variable: icons[kind] = iconForType(VarPublic); break; - case SymbolKind::Constant: icons[kind] = iconForType(VarPublic); break; - case SymbolKind::String: icons[kind] = iconForType(VarPublic); break; - case SymbolKind::Number: icons[kind] = iconForType(VarPublic); break; - case SymbolKind::Boolean: icons[kind] = iconForType(VarPublic); break; - case SymbolKind::Array: icons[kind] = iconForType(VarPublic); break; - case SymbolKind::Object: icons[kind] = iconForType(Class); break; - case SymbolKind::Key: icons[kind] = iconForType(Keyword); break; - case SymbolKind::Null: icons[kind] = iconForType(Keyword); break; - case SymbolKind::EnumMember: icons[kind] = iconForType(Enumerator); break; - case SymbolKind::Struct: icons[kind] = iconForType(Struct); break; - case SymbolKind::Event: icons[kind] = iconForType(FuncPublic); break; - case SymbolKind::Operator: icons[kind] = iconForType(FuncPublic); break; - case SymbolKind::TypeParameter: icons[kind] = iconForType(VarPublic); break; - } - } - return icons[kind]; -} - class LanguageClientOutlineItem : public Utils::TypedTreeItem<LanguageClientOutlineItem> { public: diff --git a/src/plugins/languageclient/languageclientutils.cpp b/src/plugins/languageclient/languageclientutils.cpp index 8b37ab54e5..e9889e1ef0 100644 --- a/src/plugins/languageclient/languageclientutils.cpp +++ b/src/plugins/languageclient/languageclientutils.cpp @@ -37,6 +37,7 @@ #include <texteditor/textdocument.h> #include <texteditor/texteditor.h> #include <utils/textutils.h> +#include <utils/utilsicons.h> #include <QFile> #include <QTextDocument> @@ -230,4 +231,44 @@ void updateEditorToolBar(Core::IEditor *editor) } } +const QIcon symbolIcon(int type) +{ + using namespace Utils::CodeModelIcon; + static QMap<SymbolKind, QIcon> icons; + if (type < int(SymbolKind::FirstSymbolKind) || type > int(SymbolKind::LastSymbolKind)) + return {}; + auto kind = static_cast<SymbolKind>(type); + if (icons.contains(kind)) { + switch (kind) { + case SymbolKind::File: icons[kind] = Utils::Icons::NEWFILE.icon(); break; + case SymbolKind::Module: icons[kind] = iconForType(Namespace); break; + case SymbolKind::Namespace: icons[kind] = iconForType(Namespace); break; + case SymbolKind::Package: icons[kind] = iconForType(Namespace); break; + case SymbolKind::Class: icons[kind] = iconForType(Class); break; + case SymbolKind::Method: icons[kind] = iconForType(FuncPublic); break; + case SymbolKind::Property: icons[kind] = iconForType(Property); break; + case SymbolKind::Field: icons[kind] = iconForType(VarPublic); break; + case SymbolKind::Constructor: icons[kind] = iconForType(Class); break; + case SymbolKind::Enum: icons[kind] = iconForType(Enum); break; + case SymbolKind::Interface: icons[kind] = iconForType(Class); break; + case SymbolKind::Function: icons[kind] = iconForType(FuncPublic); break; + case SymbolKind::Variable: icons[kind] = iconForType(VarPublic); break; + case SymbolKind::Constant: icons[kind] = iconForType(VarPublic); break; + case SymbolKind::String: icons[kind] = iconForType(VarPublic); break; + case SymbolKind::Number: icons[kind] = iconForType(VarPublic); break; + case SymbolKind::Boolean: icons[kind] = iconForType(VarPublic); break; + case SymbolKind::Array: icons[kind] = iconForType(VarPublic); break; + case SymbolKind::Object: icons[kind] = iconForType(Class); break; + case SymbolKind::Key: icons[kind] = iconForType(Keyword); break; + case SymbolKind::Null: icons[kind] = iconForType(Keyword); break; + case SymbolKind::EnumMember: icons[kind] = iconForType(Enumerator); break; + case SymbolKind::Struct: icons[kind] = iconForType(Struct); break; + case SymbolKind::Event: icons[kind] = iconForType(FuncPublic); break; + case SymbolKind::Operator: icons[kind] = iconForType(FuncPublic); break; + case SymbolKind::TypeParameter: icons[kind] = iconForType(VarPublic); break; + } + } + return icons[kind]; +} + } // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclientutils.h b/src/plugins/languageclient/languageclientutils.h index 99831cb497..04925e6bf1 100644 --- a/src/plugins/languageclient/languageclientutils.h +++ b/src/plugins/languageclient/languageclientutils.h @@ -51,5 +51,6 @@ void updateCodeActionRefactoringMarker(Client *client, const LanguageServerProtocol::CodeAction &action, const LanguageServerProtocol::DocumentUri &uri); void updateEditorToolBar(Core::IEditor *editor); +const QIcon symbolIcon(int type); } // namespace LanguageClient diff --git a/src/plugins/languageclient/locatorfilter.cpp b/src/plugins/languageclient/locatorfilter.cpp new file mode 100644 index 0000000000..3c0c79fe48 --- /dev/null +++ b/src/plugins/languageclient/locatorfilter.cpp @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 "locatorfilter.h" + +#include "languageclient_global.h" +#include "languageclientmanager.h" +#include "languageclientutils.h" + +#include <coreplugin/editormanager/editormanager.h> +#include <texteditor/textdocument.h> +#include <texteditor/texteditor.h> +#include <utils/fuzzymatcher.h> +#include <utils/linecolumn.h> + +#include <QFutureWatcher> +#include <QRegularExpression> + +using namespace LanguageServerProtocol; + +namespace LanguageClient { + +DocumentLocatorFilter::DocumentLocatorFilter() +{ + setId(Constants::LANGUAGECLIENT_DOCUMENT_FILTER_ID); + setDisplayName(Constants::LANGUAGECLIENT_DOCUMENT_FILTER_DISPLAY_NAME); + setShortcutString("."); + setIncludedByDefault(true); + setPriority(ILocatorFilter::Low); + connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged, + this, &DocumentLocatorFilter::updateCurrentClient); +} + +void DocumentLocatorFilter::updateCurrentClient() +{ + Core::IEditor *editor = Core::EditorManager::currentEditor(); + resetSymbols(); + disconnect(m_resetSymbolsConnection); + + if (Client *client = LanguageClientManager::clientForEditor(editor)) { + if (m_symbolCache != client->documentSymbolCache()) { + disconnect(m_updateSymbolsConnection); + m_symbolCache = client->documentSymbolCache(); + m_updateSymbolsConnection = connect(m_symbolCache, + &DocumentSymbolCache::gotSymbols, + this, + &DocumentLocatorFilter::updateSymbols); + } + m_resetSymbolsConnection = connect(editor->document(), + &Core::IDocument::contentsChanged, + this, + &DocumentLocatorFilter::resetSymbols); + m_currentUri = DocumentUri::fromFileName(editor->document()->filePath()); + } else { + disconnect(m_updateSymbolsConnection); + m_symbolCache.clear(); + m_currentUri.clear(); + } +} + +void DocumentLocatorFilter::updateSymbols(const DocumentUri &uri, + const DocumentSymbolsResult &symbols) +{ + if (uri != m_currentUri) + return; + QMutexLocker locker(&m_mutex); + m_currentSymbols = symbols; + emit symbolsUpToDate({}); +} + +void DocumentLocatorFilter::resetSymbols() +{ + QMutexLocker locker(&m_mutex); + 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 entry; + entry.filter = this; + 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())); + return entry; +} + +Core::LocatorFilterEntry DocumentLocatorFilter::generateLocatorEntry(const DocumentSymbol &info) +{ + Core::LocatorFilterEntry entry; + entry.filter = this; + entry.displayName = info.name(); + if (Utils::optional<QString> detail = info.detail()) + entry.extraInfo = detail.value_or(QString()); + entry.displayIcon = symbolIcon(info.kind()); + const Position &pos = info.range().start(); + entry.internalData = qVariantFromValue(Utils::LineColumn(pos.line(), pos.character())); + return entry; +} + +void DocumentLocatorFilter::prepareSearch(const QString &/*entry*/) +{ + QMutexLocker locker(&m_mutex); + if (m_symbolCache && !m_currentSymbols.has_value()) + m_symbolCache->requestSymbols(m_currentUri); +} + +QList<Core::LocatorFilterEntry> DocumentLocatorFilter::matchesFor( + QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry) +{ + if (!m_symbolCache) + return {}; + QMutexLocker locker(&m_mutex); + if (!m_currentSymbols.has_value()) { + QEventLoop loop; + connect(this, &DocumentLocatorFilter::symbolsUpToDate, &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(); + } + + QTC_ASSERT(m_currentSymbols.has_value(), return {}); + + if (auto list = Utils::get_if<QList<DocumentSymbol>>(&m_currentSymbols.value())) + return generateEntries(*list, entry); + else if (auto list = Utils::get_if<QList<SymbolInformation>>(&m_currentSymbols.value())) + return generateEntries(*list, entry); + + return {}; +} + +void DocumentLocatorFilter::accept(Core::LocatorFilterEntry selection, + QString * /*newText*/, + 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); +} + +void DocumentLocatorFilter::refresh(QFutureInterface<void> & /*future*/) {} + +} // namespace LanguageClient diff --git a/src/plugins/languageclient/locatorfilter.h b/src/plugins/languageclient/locatorfilter.h new file mode 100644 index 0000000000..17191eb3a2 --- /dev/null +++ b/src/plugins/languageclient/locatorfilter.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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. +** +****************************************************************************/ + +#pragma once + +#include "client.h" + +#include <coreplugin/locator/ilocatorfilter.h> +#include <languageserverprotocol/lsptypes.h> +#include <languageserverprotocol/languagefeatures.h> + +namespace Core { class IEditor; } + +namespace LanguageClient { + +class DocumentLocatorFilter : public Core::ILocatorFilter +{ + Q_OBJECT +public: + DocumentLocatorFilter(); + + void updateCurrentClient(); + 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 symbolsUpToDate(QPrivateSignal); + +protected: + QPointer<DocumentSymbolCache> m_symbolCache; + LanguageServerProtocol::DocumentUri m_currentUri; + +private: + void updateSymbols(const LanguageServerProtocol::DocumentUri &uri, + 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); + + QMutex m_mutex; + QMetaObject::Connection m_updateSymbolsConnection; + QMetaObject::Connection m_resetSymbolsConnection; + Utils::optional<LanguageServerProtocol::DocumentSymbolsResult> m_currentSymbols; +}; + +} // namespace LanguageClient |