forked from qt-creator/qt-creator
clangd: introduce ClangdCompletionItem
allowing to overwrite apply and get rid of the apply helper in the LanguageClient completion provider and processor. Change-Id: I066fe10b116d638bd1b7a81d4488840bec5f0b63 Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
@@ -807,45 +807,19 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
QList<LanguageServerProtocol::CompletionItem> completionItemsTransformer(
|
class ClangdCompletionItem : public LanguageClientCompletionItem
|
||||||
const Utils::FilePath &filePath,
|
|
||||||
const QString &content,
|
|
||||||
int pos,
|
|
||||||
const QList<CompletionItem> &items)
|
|
||||||
{
|
{
|
||||||
qCDebug(clangdLog) << "received" << items.count() << "completions";
|
public:
|
||||||
|
using LanguageClientCompletionItem::LanguageClientCompletionItem;
|
||||||
// If there are signals among the candidates, we employ the built-in code model to find out
|
void apply(TextDocumentManipulatorInterface &manipulator,
|
||||||
// whether the cursor was on the second argument of a (dis)connect() call.
|
int basePosition) const override;
|
||||||
// If so, we offer only signals, as nothing else makes sense in that context.
|
|
||||||
static const auto criterion = [](const CompletionItem &ci) {
|
|
||||||
const Utils::optional<MarkupOrString> doc = ci.documentation();
|
|
||||||
if (!doc)
|
|
||||||
return false;
|
|
||||||
QString docText;
|
|
||||||
if (Utils::holds_alternative<QString>(*doc))
|
|
||||||
docText = Utils::get<QString>(*doc);
|
|
||||||
else if (Utils::holds_alternative<MarkupContent>(*doc))
|
|
||||||
docText = Utils::get<MarkupContent>(*doc).content();
|
|
||||||
return docText.contains("Annotation: qt_signal");
|
|
||||||
};
|
|
||||||
if (pos != -1 && Utils::anyOf(items, criterion)
|
|
||||||
&& CppEditor::CppModelManager::instance()->positionRequiresSignal(filePath.toString(),
|
|
||||||
content.toUtf8(),
|
|
||||||
pos)) {
|
|
||||||
return Utils::filtered(items, criterion);
|
|
||||||
}
|
|
||||||
return items;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ClangdClient::ClangdCompletionAssistProcessor : public LanguageClientCompletionAssistProcessor
|
class ClangdClient::ClangdCompletionAssistProcessor : public LanguageClientCompletionAssistProcessor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ClangdCompletionAssistProcessor(ClangdClient *client, const QString &snippetsGroup)
|
ClangdCompletionAssistProcessor(ClangdClient *client, const QString &snippetsGroup)
|
||||||
: LanguageClientCompletionAssistProcessor(client,
|
: LanguageClientCompletionAssistProcessor(client, snippetsGroup)
|
||||||
&completionItemsTransformer,
|
|
||||||
&applyCompletionItem,
|
|
||||||
snippetsGroup)
|
|
||||||
, m_client(client)
|
, m_client(client)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -861,13 +835,53 @@ private:
|
|||||||
return LanguageClientCompletionAssistProcessor::perform(interface);
|
return LanguageClientCompletionAssistProcessor::perform(interface);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void applyCompletionItem(const CompletionItem &item,
|
QList<AssistProposalItemInterface *> generateCompletionItems(
|
||||||
TextDocumentManipulatorInterface &manipulator,
|
const QList<LanguageServerProtocol::CompletionItem> &items) const override;
|
||||||
QChar typedChar);
|
|
||||||
|
|
||||||
ClangdClient * const m_client;
|
ClangdClient * const m_client;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
QList<AssistProposalItemInterface *>
|
||||||
|
ClangdClient::ClangdCompletionAssistProcessor::generateCompletionItems(
|
||||||
|
const QList<LanguageServerProtocol::CompletionItem> &items) const
|
||||||
|
{
|
||||||
|
qCDebug(clangdLog) << "received" << items.count() << "completions";
|
||||||
|
|
||||||
|
auto itemGenerator = [](const QList<LanguageServerProtocol::CompletionItem> &items) {
|
||||||
|
return Utils::transform<QList<AssistProposalItemInterface *>>(
|
||||||
|
items, [](const LanguageServerProtocol::CompletionItem &item) {
|
||||||
|
return new ClangdCompletionItem(item);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// If there are signals among the candidates, we employ the built-in code model to find out
|
||||||
|
// whether the cursor was on the second argument of a (dis)connect() call.
|
||||||
|
// If so, we offer only signals, as nothing else makes sense in that context.
|
||||||
|
static const auto criterion = [](const CompletionItem &ci) {
|
||||||
|
const Utils::optional<MarkupOrString> doc = ci.documentation();
|
||||||
|
if (!doc)
|
||||||
|
return false;
|
||||||
|
QString docText;
|
||||||
|
if (Utils::holds_alternative<QString>(*doc))
|
||||||
|
docText = Utils::get<QString>(*doc);
|
||||||
|
else if (Utils::holds_alternative<MarkupContent>(*doc))
|
||||||
|
docText = Utils::get<MarkupContent>(*doc).content();
|
||||||
|
return docText.contains("Annotation: qt_signal");
|
||||||
|
};
|
||||||
|
const QTextDocument *doc = document();
|
||||||
|
const int pos = basePos();
|
||||||
|
if (!doc || pos < 0 || !Utils::anyOf(items, criterion))
|
||||||
|
return itemGenerator(items);
|
||||||
|
const QString content = doc->toPlainText();
|
||||||
|
const bool requiresSignal = CppEditor::CppModelManager::instance()
|
||||||
|
->positionRequiresSignal(filePath().toString(),
|
||||||
|
content.toUtf8(),
|
||||||
|
pos);
|
||||||
|
if (requiresSignal)
|
||||||
|
return itemGenerator(Utils::filtered(items, criterion));
|
||||||
|
return itemGenerator(items);
|
||||||
|
}
|
||||||
|
|
||||||
class ClangdClient::ClangdCompletionAssistProvider : public LanguageClientCompletionAssistProvider
|
class ClangdClient::ClangdCompletionAssistProvider : public LanguageClientCompletionAssistProvider
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -2837,9 +2851,11 @@ bool ClangdClient::ClangdCompletionAssistProvider::isContinuationChar(const QCha
|
|||||||
return CppEditor::isValidIdentifierChar(c);
|
return CppEditor::isValidIdentifierChar(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangdClient::ClangdCompletionAssistProcessor::applyCompletionItem(const CompletionItem &item,
|
void ClangdCompletionItem::apply(TextDocumentManipulatorInterface &manipulator,
|
||||||
TextDocumentManipulatorInterface &manipulator, QChar typedChar)
|
int /*basePosition*/) const
|
||||||
{
|
{
|
||||||
|
const LanguageServerProtocol::CompletionItem item = this->item();
|
||||||
|
QChar typedChar = triggeredCommitCharacter();
|
||||||
const auto edit = item.textEdit();
|
const auto edit = item.textEdit();
|
||||||
if (!edit)
|
if (!edit)
|
||||||
return;
|
return;
|
||||||
|
@@ -54,9 +54,8 @@ using namespace TextEditor;
|
|||||||
|
|
||||||
namespace LanguageClient {
|
namespace LanguageClient {
|
||||||
|
|
||||||
LanguageClientCompletionItem::LanguageClientCompletionItem(CompletionItem item,
|
LanguageClientCompletionItem::LanguageClientCompletionItem(CompletionItem item)
|
||||||
const CompletionApplyHelper &applyHelper)
|
: m_item(std::move(item))
|
||||||
: m_item(std::move(item)), m_applyHelper(applyHelper)
|
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
QString LanguageClientCompletionItem::text() const
|
QString LanguageClientCompletionItem::text() const
|
||||||
@@ -77,11 +76,6 @@ bool LanguageClientCompletionItem::prematurelyApplies(const QChar &typedCharacte
|
|||||||
void LanguageClientCompletionItem::apply(TextDocumentManipulatorInterface &manipulator,
|
void LanguageClientCompletionItem::apply(TextDocumentManipulatorInterface &manipulator,
|
||||||
int /*basePosition*/) const
|
int /*basePosition*/) const
|
||||||
{
|
{
|
||||||
if (m_applyHelper) {
|
|
||||||
m_applyHelper(m_item, manipulator, m_triggeredCommitCharacter);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto edit = m_item.textEdit()) {
|
if (auto edit = m_item.textEdit()) {
|
||||||
applyTextEdit(manipulator, *edit, isSnippet());
|
applyTextEdit(manipulator, *edit, isSnippet());
|
||||||
} else {
|
} else {
|
||||||
@@ -176,6 +170,16 @@ quint64 LanguageClientCompletionItem::hash() const
|
|||||||
return qHash(m_item.label()); // TODO: naaaa
|
return qHash(m_item.label()); // TODO: naaaa
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CompletionItem LanguageClientCompletionItem::item() const
|
||||||
|
{
|
||||||
|
return m_item;
|
||||||
|
}
|
||||||
|
|
||||||
|
QChar LanguageClientCompletionItem::triggeredCommitCharacter() const
|
||||||
|
{
|
||||||
|
return m_triggeredCommitCharacter;
|
||||||
|
}
|
||||||
|
|
||||||
const QString &LanguageClientCompletionItem::sortText() const
|
const QString &LanguageClientCompletionItem::sortText() const
|
||||||
{
|
{
|
||||||
if (m_sortText.isEmpty())
|
if (m_sortText.isEmpty())
|
||||||
@@ -289,12 +293,8 @@ public:
|
|||||||
|
|
||||||
LanguageClientCompletionAssistProcessor::LanguageClientCompletionAssistProcessor(
|
LanguageClientCompletionAssistProcessor::LanguageClientCompletionAssistProcessor(
|
||||||
Client *client,
|
Client *client,
|
||||||
const CompletionItemsTransformer &itemsTransformer,
|
|
||||||
const CompletionApplyHelper &applyHelper,
|
|
||||||
const QString &snippetsGroup)
|
const QString &snippetsGroup)
|
||||||
: m_client(client)
|
: m_client(client)
|
||||||
, m_itemsTransformer(itemsTransformer)
|
|
||||||
, m_applyHelper(applyHelper)
|
|
||||||
, m_snippetsGroup(snippetsGroup)
|
, m_snippetsGroup(snippetsGroup)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@@ -303,6 +303,18 @@ LanguageClientCompletionAssistProcessor::~LanguageClientCompletionAssistProcesso
|
|||||||
QTC_ASSERT(!running(), cancel());
|
QTC_ASSERT(!running(), cancel());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QTextDocument *LanguageClientCompletionAssistProcessor::document() const
|
||||||
|
{
|
||||||
|
return m_document;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<AssistProposalItemInterface *> LanguageClientCompletionAssistProcessor::generateCompletionItems(
|
||||||
|
const QList<LanguageServerProtocol::CompletionItem> &items) const
|
||||||
|
{
|
||||||
|
return Utils::transform<QList<AssistProposalItemInterface *>>(
|
||||||
|
items, [](const CompletionItem &item) { return new LanguageClientCompletionItem(item); });
|
||||||
|
}
|
||||||
|
|
||||||
static QString assistReasonString(AssistReason reason)
|
static QString assistReasonString(AssistReason reason)
|
||||||
{
|
{
|
||||||
switch (reason) {
|
switch (reason) {
|
||||||
@@ -412,17 +424,12 @@ void LanguageClientCompletionAssistProcessor::handleCompletionResponse(
|
|||||||
} else if (Utils::holds_alternative<QList<CompletionItem>>(*result)) {
|
} else if (Utils::holds_alternative<QList<CompletionItem>>(*result)) {
|
||||||
items = Utils::get<QList<CompletionItem>>(*result);
|
items = Utils::get<QList<CompletionItem>>(*result);
|
||||||
}
|
}
|
||||||
if (m_itemsTransformer && m_document)
|
auto proposalItems = generateCompletionItems(items);
|
||||||
items = m_itemsTransformer(m_filePath, m_document->toPlainText(), m_basePos, items);
|
|
||||||
auto model = new LanguageClientCompletionModel();
|
|
||||||
auto proposalItems = Utils::transform<QList<AssistProposalItemInterface *>>(items,
|
|
||||||
[this](const CompletionItem &item) {
|
|
||||||
return new LanguageClientCompletionItem(item, m_applyHelper);
|
|
||||||
});
|
|
||||||
if (!m_snippetsGroup.isEmpty()) {
|
if (!m_snippetsGroup.isEmpty()) {
|
||||||
proposalItems << TextEditor::SnippetAssistCollector(
|
proposalItems << TextEditor::SnippetAssistCollector(
|
||||||
m_snippetsGroup, QIcon(":/texteditor/images/snippet.png")).collect();
|
m_snippetsGroup, QIcon(":/texteditor/images/snippet.png")).collect();
|
||||||
}
|
}
|
||||||
|
auto model = new LanguageClientCompletionModel();
|
||||||
model->loadContent(proposalItems);
|
model->loadContent(proposalItems);
|
||||||
LanguageClientCompletionProposal *proposal = new LanguageClientCompletionProposal(m_basePos,
|
LanguageClientCompletionProposal *proposal = new LanguageClientCompletionProposal(m_basePos,
|
||||||
model);
|
model);
|
||||||
@@ -445,8 +452,6 @@ IAssistProcessor *LanguageClientCompletionAssistProvider::createProcessor(
|
|||||||
const AssistInterface *) const
|
const AssistInterface *) const
|
||||||
{
|
{
|
||||||
return new LanguageClientCompletionAssistProcessor(m_client,
|
return new LanguageClientCompletionAssistProcessor(m_client,
|
||||||
m_itemsTransformer,
|
|
||||||
m_applyHelper,
|
|
||||||
m_snippetsGroup);
|
m_snippetsGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -478,16 +483,4 @@ void LanguageClientCompletionAssistProvider::setTriggerCharacters(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LanguageClientCompletionAssistProvider::setItemsTransformer(
|
|
||||||
const CompletionItemsTransformer &transformer)
|
|
||||||
{
|
|
||||||
m_itemsTransformer = transformer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LanguageClientCompletionAssistProvider::setApplyHelper(
|
|
||||||
const CompletionApplyHelper &applyHelper)
|
|
||||||
{
|
|
||||||
m_applyHelper = applyHelper;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace LanguageClient
|
} // namespace LanguageClient
|
||||||
|
@@ -47,13 +47,6 @@ namespace LanguageClient {
|
|||||||
|
|
||||||
class Client;
|
class Client;
|
||||||
|
|
||||||
using CompletionItemsTransformer = std::function<QList<LanguageServerProtocol::CompletionItem>(
|
|
||||||
const Utils::FilePath &, const QString &, int,
|
|
||||||
const QList<LanguageServerProtocol::CompletionItem> &)>;
|
|
||||||
using CompletionApplyHelper = std::function<void(
|
|
||||||
const LanguageServerProtocol::CompletionItem &,
|
|
||||||
TextEditor::TextDocumentManipulatorInterface &, QChar)>;
|
|
||||||
|
|
||||||
class LANGUAGECLIENT_EXPORT LanguageClientCompletionAssistProvider
|
class LANGUAGECLIENT_EXPORT LanguageClientCompletionAssistProvider
|
||||||
: public TextEditor::CompletionAssistProvider
|
: public TextEditor::CompletionAssistProvider
|
||||||
{
|
{
|
||||||
@@ -74,14 +67,10 @@ public:
|
|||||||
void setSnippetsGroup(const QString &group) { m_snippetsGroup = group; }
|
void setSnippetsGroup(const QString &group) { m_snippetsGroup = group; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void setItemsTransformer(const CompletionItemsTransformer &transformer);
|
|
||||||
void setApplyHelper(const CompletionApplyHelper &applyHelper);
|
|
||||||
Client *client() const { return m_client; }
|
Client *client() const { return m_client; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<QString> m_triggerChars;
|
QList<QString> m_triggerChars;
|
||||||
CompletionItemsTransformer m_itemsTransformer;
|
|
||||||
CompletionApplyHelper m_applyHelper;
|
|
||||||
QString m_snippetsGroup;
|
QString m_snippetsGroup;
|
||||||
int m_activationCharSequenceLength = 0;
|
int m_activationCharSequenceLength = 0;
|
||||||
Client *m_client = nullptr; // not owned
|
Client *m_client = nullptr; // not owned
|
||||||
@@ -91,16 +80,20 @@ class LANGUAGECLIENT_EXPORT LanguageClientCompletionAssistProcessor
|
|||||||
: public TextEditor::IAssistProcessor
|
: public TextEditor::IAssistProcessor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LanguageClientCompletionAssistProcessor(Client *client,
|
LanguageClientCompletionAssistProcessor(Client *client, const QString &snippetsGroup);
|
||||||
const CompletionItemsTransformer &itemsTransformer,
|
|
||||||
const CompletionApplyHelper &applyHelper,
|
|
||||||
const QString &snippetsGroup);
|
|
||||||
~LanguageClientCompletionAssistProcessor() override;
|
~LanguageClientCompletionAssistProcessor() override;
|
||||||
TextEditor::IAssistProposal *perform(const TextEditor::AssistInterface *interface) override;
|
TextEditor::IAssistProposal *perform(const TextEditor::AssistInterface *interface) override;
|
||||||
bool running() override;
|
bool running() override;
|
||||||
bool needsRestart() const override { return true; }
|
bool needsRestart() const override { return true; }
|
||||||
void cancel() override;
|
void cancel() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QTextDocument *document() const;
|
||||||
|
Utils::FilePath filePath() const { return m_filePath; }
|
||||||
|
int basePos() const { return m_basePos; }
|
||||||
|
virtual QList<TextEditor::AssistProposalItemInterface *> generateCompletionItems(
|
||||||
|
const QList<LanguageServerProtocol::CompletionItem> &items) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void handleCompletionResponse(const LanguageServerProtocol::CompletionRequest::Response &response);
|
void handleCompletionResponse(const LanguageServerProtocol::CompletionRequest::Response &response);
|
||||||
|
|
||||||
@@ -109,8 +102,6 @@ private:
|
|||||||
QPointer<Client> m_client;
|
QPointer<Client> m_client;
|
||||||
Utils::optional<LanguageServerProtocol::MessageId> m_currentRequest;
|
Utils::optional<LanguageServerProtocol::MessageId> m_currentRequest;
|
||||||
QMetaObject::Connection m_postponedUpdateConnection;
|
QMetaObject::Connection m_postponedUpdateConnection;
|
||||||
const CompletionItemsTransformer m_itemsTransformer;
|
|
||||||
const CompletionApplyHelper m_applyHelper;
|
|
||||||
const QString m_snippetsGroup;
|
const QString m_snippetsGroup;
|
||||||
int m_pos = -1;
|
int m_pos = -1;
|
||||||
int m_basePos = -1;
|
int m_basePos = -1;
|
||||||
@@ -120,8 +111,7 @@ class LANGUAGECLIENT_EXPORT LanguageClientCompletionItem
|
|||||||
: public TextEditor::AssistProposalItemInterface
|
: public TextEditor::AssistProposalItemInterface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LanguageClientCompletionItem(LanguageServerProtocol::CompletionItem item,
|
LanguageClientCompletionItem(LanguageServerProtocol::CompletionItem item);
|
||||||
const CompletionApplyHelper &applyHelper);
|
|
||||||
|
|
||||||
// AssistProposalItemInterface interface
|
// AssistProposalItemInterface interface
|
||||||
QString text() const override;
|
QString text() const override;
|
||||||
@@ -136,6 +126,9 @@ public:
|
|||||||
bool isValid() const override;
|
bool isValid() const override;
|
||||||
quint64 hash() const override;
|
quint64 hash() const override;
|
||||||
|
|
||||||
|
LanguageServerProtocol::CompletionItem item() const;
|
||||||
|
QChar triggeredCommitCharacter() const;
|
||||||
|
|
||||||
const QString &sortText() const;
|
const QString &sortText() const;
|
||||||
bool hasSortText() const;
|
bool hasSortText() const;
|
||||||
|
|
||||||
@@ -145,7 +138,6 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
LanguageServerProtocol::CompletionItem m_item;
|
LanguageServerProtocol::CompletionItem m_item;
|
||||||
const CompletionApplyHelper m_applyHelper;
|
|
||||||
mutable QChar m_triggeredCommitCharacter;
|
mutable QChar m_triggeredCommitCharacter;
|
||||||
mutable QString m_sortText;
|
mutable QString m_sortText;
|
||||||
mutable QString m_filterText;
|
mutable QString m_filterText;
|
||||||
|
Reference in New Issue
Block a user