From ce34ffdc21ac4928f1b687c36bf11fc4db79b581 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Fri, 8 Oct 2021 10:15:51 +0200 Subject: [PATCH] ClangCodeModel: Do not trigger snippets ... in strings or comments with clangd. Change-Id: Ic16e94051018e91f273fafe2ec90d5395e4cc07a Reviewed-by: David Schulz --- src/plugins/clangcodemodel/clangdclient.cpp | 67 +++++++++++++++------ src/plugins/cppeditor/cppprojectfile.cpp | 14 +++++ src/plugins/cppeditor/cppprojectfile.h | 1 + 3 files changed, 65 insertions(+), 17 deletions(-) diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index 6d4dac1459f..01bd5b6c2a7 100644 --- a/src/plugins/clangcodemodel/clangdclient.cpp +++ b/src/plugins/clangcodemodel/clangdclient.cpp @@ -35,9 +35,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -712,20 +714,12 @@ private: case CustomAssistMode::Preprocessor: static QIcon macroIcon = Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::Macro); for (const QString &completion - : CppEditor::CppCompletionAssistProcessor::preprocessorCompletions()) + : CppEditor::CppCompletionAssistProcessor::preprocessorCompletions()) { 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); model->loadContent(completions); @@ -1057,12 +1051,14 @@ public: ClangdCompletionAssistProvider(ClangdClient *client); private: - IAssistProcessor *createProcessor(const AssistInterface *assistInterface) const override; + IAssistProcessor *createProcessor(const AssistInterface *interface) const override; int activationCharSequenceLength() const override { return 3; } bool isActivationCharSequence(const QString &sequence) const override; bool isContinuationChar(const QChar &c) const override; + bool isInCommentOrString(const AssistInterface *interface) const; + ClangdClient * const m_client; }; @@ -2573,10 +2569,10 @@ ClangdClient::ClangdCompletionAssistProvider::ClangdCompletionAssistProvider(Cla {} IAssistProcessor *ClangdClient::ClangdCompletionAssistProvider::createProcessor( - const AssistInterface *assistInterface) const + const AssistInterface *interface) const { - ClangCompletionContextAnalyzer contextAnalyzer(assistInterface->textDocument(), - assistInterface->position(), false, {}); + ClangCompletionContextAnalyzer contextAnalyzer(interface->textDocument(), + interface->position(), false, {}); contextAnalyzer.analyze(); switch (contextAnalyzer.completionAction()) { case ClangCompletionContextAnalyzer::PassThroughToLibClangAfterLeftParen: @@ -2595,7 +2591,7 @@ IAssistProcessor *ClangdClient::ClangdCompletionAssistProvider::createProcessor( default: break; } - const QString snippetsGroup = contextAnalyzer.addSnippets() + const QString snippetsGroup = contextAnalyzer.addSnippets() && !isInCommentOrString(interface) ? CppEditor::Constants::CPP_SNIPPETS_GROUP_ID : QString(); return new ClangdCompletionAssistProcessor(m_client, snippetsGroup); @@ -2628,6 +2624,43 @@ bool ClangdClient::ClangdCompletionAssistProvider::isContinuationChar(const QCha 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, int /*basePosition*/) const { diff --git a/src/plugins/cppeditor/cppprojectfile.cpp b/src/plugins/cppeditor/cppprojectfile.cpp index fa3bf8c0238..58196521ca1 100644 --- a/src/plugins/cppeditor/cppprojectfile.cpp +++ b/src/plugins/cppeditor/cppprojectfile.cpp @@ -87,6 +87,20 @@ bool ProjectFile::isAmbiguousHeader(const QString &filePath) 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 sourceKind; diff --git a/src/plugins/cppeditor/cppprojectfile.h b/src/plugins/cppeditor/cppprojectfile.h index 078b93cce9e..1bd80fdf65c 100644 --- a/src/plugins/cppeditor/cppprojectfile.h +++ b/src/plugins/cppeditor/cppprojectfile.h @@ -61,6 +61,7 @@ public: static bool isC(Kind kind); static bool isCxx(Kind kind); static bool isAmbiguousHeader(const QString &filePath); + static bool isObjC(const QString &filePath); bool isHeader() const; bool isSource() const;