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>::operator=;
|
||||
|
||||
LanguageClientArray() {}
|
||||
|
||||
LanguageClientArray(const QList<T> &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_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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,13 +97,40 @@ void DocumentLocatorFilter::resetSymbols()
|
||||
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>
|
||||
QList<Core::LocatorFilterEntry> DocumentLocatorFilter::generateEntries(const QList<T> &list,
|
||||
const QString &filter)
|
||||
{
|
||||
QList<Core::LocatorFilterEntry> entries;
|
||||
FuzzyMatcher::CaseSensitivity caseSensitivity
|
||||
= DocumentLocatorFilter::caseSensitivity(filter) == Qt::CaseSensitive
|
||||
= ILocatorFilter::caseSensitivity(filter) == Qt::CaseSensitive
|
||||
? FuzzyMatcher::CaseSensitivity::CaseSensitive
|
||||
: FuzzyMatcher::CaseSensitivity::CaseInsensitive;
|
||||
const QRegularExpression regexp = FuzzyMatcher::createRegExp(filter, caseSensitivity);
|
||||
@@ -112,37 +140,11 @@ QList<Core::LocatorFilterEntry> DocumentLocatorFilter::generateEntries(const QLi
|
||||
for (const T &item : list) {
|
||||
QRegularExpressionMatch match = regexp.match(item.name());
|
||||
if (match.hasMatch())
|
||||
entries << generateLocatorEntry(item);
|
||||
entries << generateLocatorEntry(item, this);
|
||||
}
|
||||
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);
|
||||
@@ -188,12 +190,127 @@ void DocumentLocatorFilter::accept(Core::LocatorFilterEntry selection,
|
||||
int * /*selectionStart*/,
|
||||
int * /*selectionLength*/) const
|
||||
{
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user