ClangCodeModel: Do not trigger snippets

... in strings or comments with clangd.

Change-Id: Ic16e94051018e91f273fafe2ec90d5395e4cc07a
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Christian Kandeler
2021-10-08 10:15:51 +02:00
parent 0a2b504663
commit ce34ffdc21
3 changed files with 65 additions and 17 deletions

View File

@@ -35,9 +35,11 @@
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/find/searchresultitem.h> #include <coreplugin/find/searchresultitem.h>
#include <coreplugin/find/searchresultwindow.h> #include <coreplugin/find/searchresultwindow.h>
#include <cplusplus/BackwardsScanner.h>
#include <cplusplus/FindUsages.h> #include <cplusplus/FindUsages.h>
#include <cplusplus/Icons.h> #include <cplusplus/Icons.h>
#include <cplusplus/MatchingText.h> #include <cplusplus/MatchingText.h>
#include <cplusplus/SimpleLexer.h>
#include <cppeditor/cppeditorconstants.h> #include <cppeditor/cppeditorconstants.h>
#include <cppeditor/cppcodemodelsettings.h> #include <cppeditor/cppcodemodelsettings.h>
#include <cppeditor/cppcompletionassistprocessor.h> #include <cppeditor/cppcompletionassistprocessor.h>
@@ -712,20 +714,12 @@ private:
case CustomAssistMode::Preprocessor: case CustomAssistMode::Preprocessor:
static QIcon macroIcon = Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::Macro); static QIcon macroIcon = Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::Macro);
for (const QString &completion for (const QString &completion
: CppEditor::CppCompletionAssistProcessor::preprocessorCompletions()) : CppEditor::CppCompletionAssistProcessor::preprocessorCompletions()) {
completions << createItem(completion, macroIcon); completions << createItem(completion, macroIcon);
const CppEditor::ProjectFile::Kind fileType
= CppEditor::ProjectFile::classify(interface->filePath().toString());
switch (fileType) {
case CppEditor::ProjectFile::ObjCHeader:
case CppEditor::ProjectFile::ObjCXXHeader:
case CppEditor::ProjectFile::ObjCSource:
case CppEditor::ProjectFile::ObjCXXSource:
completions << createItem("import", macroIcon);
break;
default:
break;
} }
if (CppEditor::ProjectFile::isObjC(interface->filePath().toString()))
completions << createItem("import", macroIcon);
break;
} }
GenericProposalModelPtr model(new GenericProposalModel); GenericProposalModelPtr model(new GenericProposalModel);
model->loadContent(completions); model->loadContent(completions);
@@ -1057,12 +1051,14 @@ public:
ClangdCompletionAssistProvider(ClangdClient *client); ClangdCompletionAssistProvider(ClangdClient *client);
private: private:
IAssistProcessor *createProcessor(const AssistInterface *assistInterface) const override; IAssistProcessor *createProcessor(const AssistInterface *interface) const override;
int activationCharSequenceLength() const override { return 3; } int activationCharSequenceLength() const override { return 3; }
bool isActivationCharSequence(const QString &sequence) const override; bool isActivationCharSequence(const QString &sequence) const override;
bool isContinuationChar(const QChar &c) const override; bool isContinuationChar(const QChar &c) const override;
bool isInCommentOrString(const AssistInterface *interface) const;
ClangdClient * const m_client; ClangdClient * const m_client;
}; };
@@ -2573,10 +2569,10 @@ ClangdClient::ClangdCompletionAssistProvider::ClangdCompletionAssistProvider(Cla
{} {}
IAssistProcessor *ClangdClient::ClangdCompletionAssistProvider::createProcessor( IAssistProcessor *ClangdClient::ClangdCompletionAssistProvider::createProcessor(
const AssistInterface *assistInterface) const const AssistInterface *interface) const
{ {
ClangCompletionContextAnalyzer contextAnalyzer(assistInterface->textDocument(), ClangCompletionContextAnalyzer contextAnalyzer(interface->textDocument(),
assistInterface->position(), false, {}); interface->position(), false, {});
contextAnalyzer.analyze(); contextAnalyzer.analyze();
switch (contextAnalyzer.completionAction()) { switch (contextAnalyzer.completionAction()) {
case ClangCompletionContextAnalyzer::PassThroughToLibClangAfterLeftParen: case ClangCompletionContextAnalyzer::PassThroughToLibClangAfterLeftParen:
@@ -2595,7 +2591,7 @@ IAssistProcessor *ClangdClient::ClangdCompletionAssistProvider::createProcessor(
default: default:
break; break;
} }
const QString snippetsGroup = contextAnalyzer.addSnippets() const QString snippetsGroup = contextAnalyzer.addSnippets() && !isInCommentOrString(interface)
? CppEditor::Constants::CPP_SNIPPETS_GROUP_ID ? CppEditor::Constants::CPP_SNIPPETS_GROUP_ID
: QString(); : QString();
return new ClangdCompletionAssistProcessor(m_client, snippetsGroup); return new ClangdCompletionAssistProcessor(m_client, snippetsGroup);
@@ -2628,6 +2624,43 @@ bool ClangdClient::ClangdCompletionAssistProvider::isContinuationChar(const QCha
return CppEditor::isValidIdentifierChar(c); return CppEditor::isValidIdentifierChar(c);
} }
bool ClangdClient::ClangdCompletionAssistProvider::isInCommentOrString(
const AssistInterface *interface) const
{
QTextCursor tc(interface->textDocument());
tc.setPosition(interface->position());
SimpleLexer tokenize;
tokenize.setSkipComments(false);
const Tokens &tokens = tokenize(tc.block().text(),
BackwardsScanner::previousBlockState(tc.block()));
const int tokenIdx = SimpleLexer::tokenBefore(tokens, qMax(0, tc.positionInBlock() - 1));
const Token tk = (tokenIdx == -1) ? Token() : tokens.at(tokenIdx);
if (tk.isComment())
return true;
if (!tk.isLiteral())
return false;
if (tokens.size() == 3 && tokens.at(0).kind() == T_POUND
&& tokens.at(1).kind() == T_IDENTIFIER) {
const QString &line = tc.block().text();
const Token &idToken = tokens.at(1);
QStringView identifier = idToken.utf16charsEnd() > line.size()
? QStringView(line).mid(
idToken.utf16charsBegin())
: QStringView(line)
.mid(idToken.utf16charsBegin(),
idToken.utf16chars());
if (identifier == QLatin1String("include")
|| identifier == QLatin1String("include_next")
|| (CppEditor::ProjectFile::isObjC(interface->filePath().toString())
&& identifier == QLatin1String("import"))) {
return false;
}
}
return true;
}
void ClangdCompletionItem::apply(TextDocumentManipulatorInterface &manipulator, void ClangdCompletionItem::apply(TextDocumentManipulatorInterface &manipulator,
int /*basePosition*/) const int /*basePosition*/) const
{ {

View File

@@ -87,6 +87,20 @@ bool ProjectFile::isAmbiguousHeader(const QString &filePath)
return filePath.endsWith(".h"); return filePath.endsWith(".h");
} }
bool ProjectFile::isObjC(const QString &filePath)
{
const Kind kind = classify(filePath);
switch (kind) {
case CppEditor::ProjectFile::ObjCHeader:
case CppEditor::ProjectFile::ObjCXXHeader:
case CppEditor::ProjectFile::ObjCSource:
case CppEditor::ProjectFile::ObjCXXSource:
return true;
default:
return false;
}
}
ProjectFile::Kind ProjectFile::sourceForHeaderKind(ProjectFile::Kind kind) ProjectFile::Kind ProjectFile::sourceForHeaderKind(ProjectFile::Kind kind)
{ {
ProjectFile::Kind sourceKind; ProjectFile::Kind sourceKind;

View File

@@ -61,6 +61,7 @@ public:
static bool isC(Kind kind); static bool isC(Kind kind);
static bool isCxx(Kind kind); static bool isCxx(Kind kind);
static bool isAmbiguousHeader(const QString &filePath); static bool isAmbiguousHeader(const QString &filePath);
static bool isObjC(const QString &filePath);
bool isHeader() const; bool isHeader() const;
bool isSource() const; bool isSource() const;