forked from qt-creator/qt-creator
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>
This commit is contained in:
@@ -67,6 +67,8 @@ public:
|
|||||||
using Utils::variant<QList<T>, std::nullptr_t>::variant;
|
using Utils::variant<QList<T>, std::nullptr_t>::variant;
|
||||||
using Utils::variant<QList<T>, std::nullptr_t>::operator=;
|
using Utils::variant<QList<T>, std::nullptr_t>::operator=;
|
||||||
|
|
||||||
|
LanguageClientArray() {}
|
||||||
|
|
||||||
LanguageClientArray(const QList<T> &list)
|
LanguageClientArray(const QList<T> &list)
|
||||||
{ *this = list; }
|
{ *this = list; }
|
||||||
|
|
||||||
|
|||||||
@@ -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_SETTINGS_TR[] = QT_TRANSLATE_NOOP("LanguageClient", "Language Client");
|
||||||
const char LANGUAGECLIENT_DOCUMENT_FILTER_ID[] = "Current Document Symbols";
|
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_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 Constants
|
||||||
} // namespace LanguageClient
|
} // namespace LanguageClient
|
||||||
|
|||||||
@@ -107,5 +107,8 @@ private:
|
|||||||
QMap<QString, QVector<Client *>> m_clientsForSetting;
|
QMap<QString, QVector<Client *>> m_clientsForSetting;
|
||||||
QHash<LanguageServerProtocol::MessageId, QList<Client *>> m_exclusiveRequests;
|
QHash<LanguageServerProtocol::MessageId, QList<Client *>> m_exclusiveRequests;
|
||||||
DocumentLocatorFilter m_currentDocumentLocatorFilter;
|
DocumentLocatorFilter m_currentDocumentLocatorFilter;
|
||||||
|
WorkspaceLocatorFilter m_workspaceLocatorFilter;
|
||||||
|
WorkspaceClassLocatorFilter m_workspaceClassLocatorFilter;
|
||||||
|
WorkspaceMethodLocatorFilter m_workspaceMethodLocatorFilter;
|
||||||
};
|
};
|
||||||
} // namespace LanguageClient
|
} // namespace LanguageClient
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include "languageclientutils.h"
|
#include "languageclientutils.h"
|
||||||
|
|
||||||
#include <coreplugin/editormanager/editormanager.h>
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
|
#include <languageserverprotocol/servercapabilities.h>
|
||||||
#include <texteditor/textdocument.h>
|
#include <texteditor/textdocument.h>
|
||||||
#include <texteditor/texteditor.h>
|
#include <texteditor/texteditor.h>
|
||||||
#include <utils/fuzzymatcher.h>
|
#include <utils/fuzzymatcher.h>
|
||||||
@@ -96,13 +97,40 @@ void DocumentLocatorFilter::resetSymbols()
|
|||||||
m_currentSymbols.reset();
|
m_currentSymbols.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Core::LocatorFilterEntry generateLocatorEntry(const SymbolInformation &info,
|
||||||
|
Core::ILocatorFilter *filter)
|
||||||
|
{
|
||||||
|
Core::LocatorFilterEntry entry;
|
||||||
|
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());
|
||||||
|
entry.internalData = qVariantFromValue(info.location().toLink());
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::LocatorFilterEntry generateLocatorEntry(const DocumentSymbol &info,
|
||||||
|
Core::ILocatorFilter *filter)
|
||||||
|
{
|
||||||
|
Core::LocatorFilterEntry entry;
|
||||||
|
entry.filter = filter;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
QList<Core::LocatorFilterEntry> DocumentLocatorFilter::generateEntries(const QList<T> &list,
|
QList<Core::LocatorFilterEntry> DocumentLocatorFilter::generateEntries(const QList<T> &list,
|
||||||
const QString &filter)
|
const QString &filter)
|
||||||
{
|
{
|
||||||
QList<Core::LocatorFilterEntry> entries;
|
QList<Core::LocatorFilterEntry> entries;
|
||||||
FuzzyMatcher::CaseSensitivity caseSensitivity
|
FuzzyMatcher::CaseSensitivity caseSensitivity
|
||||||
= DocumentLocatorFilter::caseSensitivity(filter) == Qt::CaseSensitive
|
= ILocatorFilter::caseSensitivity(filter) == Qt::CaseSensitive
|
||||||
? FuzzyMatcher::CaseSensitivity::CaseSensitive
|
? FuzzyMatcher::CaseSensitivity::CaseSensitive
|
||||||
: FuzzyMatcher::CaseSensitivity::CaseInsensitive;
|
: FuzzyMatcher::CaseSensitivity::CaseInsensitive;
|
||||||
const QRegularExpression regexp = FuzzyMatcher::createRegExp(filter, caseSensitivity);
|
const QRegularExpression regexp = FuzzyMatcher::createRegExp(filter, caseSensitivity);
|
||||||
@@ -112,37 +140,11 @@ QList<Core::LocatorFilterEntry> DocumentLocatorFilter::generateEntries(const QLi
|
|||||||
for (const T &item : list) {
|
for (const T &item : list) {
|
||||||
QRegularExpressionMatch match = regexp.match(item.name());
|
QRegularExpressionMatch match = regexp.match(item.name());
|
||||||
if (match.hasMatch())
|
if (match.hasMatch())
|
||||||
entries << generateLocatorEntry(item);
|
entries << generateLocatorEntry(item, this);
|
||||||
}
|
}
|
||||||
return entries;
|
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*/)
|
void DocumentLocatorFilter::prepareSearch(const QString &/*entry*/)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&m_mutex);
|
QMutexLocker locker(&m_mutex);
|
||||||
@@ -188,12 +190,127 @@ void DocumentLocatorFilter::accept(Core::LocatorFilterEntry selection,
|
|||||||
int * /*selectionStart*/,
|
int * /*selectionStart*/,
|
||||||
int * /*selectionLength*/) const
|
int * /*selectionLength*/) const
|
||||||
{
|
{
|
||||||
auto lineColumn = qvariant_cast<Utils::LineColumn>(selection.internalData);
|
if (selection.internalData.canConvert<Utils::LineColumn>()) {
|
||||||
Core::EditorManager::openEditorAt(m_currentUri.toFileName().toString(),
|
auto lineColumn = qvariant_cast<Utils::LineColumn>(selection.internalData);
|
||||||
lineColumn.line + 1,
|
Core::EditorManager::openEditorAt(m_currentUri.toFileName().toString(),
|
||||||
lineColumn.column);
|
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*/) {}
|
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
|
} // namespace LanguageClient
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include <coreplugin/locator/ilocatorfilter.h>
|
#include <coreplugin/locator/ilocatorfilter.h>
|
||||||
#include <languageserverprotocol/lsptypes.h>
|
#include <languageserverprotocol/lsptypes.h>
|
||||||
#include <languageserverprotocol/languagefeatures.h>
|
#include <languageserverprotocol/languagefeatures.h>
|
||||||
|
#include <languageserverprotocol/workspace.h>
|
||||||
|
|
||||||
namespace Core { class IEditor; }
|
namespace Core { class IEditor; }
|
||||||
|
|
||||||
@@ -63,9 +64,6 @@ private:
|
|||||||
const LanguageServerProtocol::DocumentSymbolsResult &symbols);
|
const LanguageServerProtocol::DocumentSymbolsResult &symbols);
|
||||||
void resetSymbols();
|
void resetSymbols();
|
||||||
|
|
||||||
Core::LocatorFilterEntry generateLocatorEntry(
|
|
||||||
const LanguageServerProtocol::SymbolInformation &info);
|
|
||||||
Core::LocatorFilterEntry generateLocatorEntry(const LanguageServerProtocol::DocumentSymbol &info);
|
|
||||||
template<class T>
|
template<class T>
|
||||||
QList<Core::LocatorFilterEntry> generateEntries(const QList<T> &list, const QString &filter);
|
QList<Core::LocatorFilterEntry> generateEntries(const QList<T> &list, const QString &filter);
|
||||||
|
|
||||||
@@ -75,4 +73,47 @@ private:
|
|||||||
Utils::optional<LanguageServerProtocol::DocumentSymbolsResult> m_currentSymbols;
|
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
|
} // namespace LanguageClient
|
||||||
|
|||||||
Reference in New Issue
Block a user