[clang-tools-extra] [clangd] Add CodePatterns config option under Completion (PR #137613)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 28 03:49:57 PDT 2025
https://github.com/Noustaa created https://github.com/llvm/llvm-project/pull/137613
Allows enabling/disabling code patterns & snippets suggestion from completion.
This can be done either with YAML config or CLI with --code-patterns
You can refer to this discussion for more context: https://github.com/clangd/clangd/discussions/1867
>From 57cee192600aaaa72db6bbabb1cf7d52cf6da6c3 Mon Sep 17 00:00:00 2001
From: Noustaa <noustaa at gmail.com>
Date: Mon, 28 Apr 2025 12:38:36 +0200
Subject: [PATCH] [clangd] Add CodePatterns config option under Completion
Allows enabling/disabling code patterns & snippets suggestion from
completion.
This can be done either with YAML config or CLI with --code-patterns
---
clang-tools-extra/clangd/ClangdServer.cpp | 1 +
clang-tools-extra/clangd/CodeComplete.cpp | 7 ++++++-
clang-tools-extra/clangd/CodeComplete.h | 3 +++
clang-tools-extra/clangd/Config.h | 7 +++++++
clang-tools-extra/clangd/ConfigCompile.cpp | 11 +++++++++++
clang-tools-extra/clangd/ConfigFragment.h | 6 ++++++
clang-tools-extra/clangd/ConfigYAML.cpp | 4 ++++
clang-tools-extra/clangd/tool/ClangdMain.cpp | 19 +++++++++++++++++++
.../clangd/unittests/CodeCompleteTests.cpp | 17 +++++++++++++++++
.../clangd/unittests/ConfigYAMLTests.cpp | 13 +++++++++++++
10 files changed, 87 insertions(+), 1 deletion(-)
diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp
index 52844129834c3..b499683621f53 100644
--- a/clang-tools-extra/clangd/ClangdServer.cpp
+++ b/clang-tools-extra/clangd/ClangdServer.cpp
@@ -457,6 +457,7 @@ void ClangdServer::codeComplete(PathRef File, Position Pos,
CodeCompleteOpts.ArgumentLists = Config::current().Completion.ArgumentLists;
CodeCompleteOpts.InsertIncludes =
Config::current().Completion.HeaderInsertion;
+ CodeCompleteOpts.CodePatterns = Config::current().Completion.CodePatterns;
// FIXME(ibiryukov): even if Preamble is non-null, we may want to check
// both the old and the new version in case only one of them matches.
CodeCompleteResult Result = clangd::codeComplete(
diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp
index 0eb196fbad46a..171690b6e78b4 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -950,6 +950,10 @@ struct CompletionRecorder : public CodeCompleteConsumer {
// Retain the results we might want.
for (unsigned I = 0; I < NumResults; ++I) {
auto &Result = InResults[I];
+ if (Config::current().Completion.CodePatterns ==
+ Config::CodePatternsPolicy::None &&
+ Result.Kind == CodeCompletionResult::RK_Pattern)
+ continue;
// Class members that are shadowed by subclasses are usually noise.
if (Result.Hidden && Result.Declaration &&
Result.Declaration->isCXXClassMember())
@@ -2153,7 +2157,8 @@ class CodeCompleteFlow {
clang::CodeCompleteOptions CodeCompleteOptions::getClangCompleteOpts() const {
clang::CodeCompleteOptions Result;
- Result.IncludeCodePatterns = EnableSnippets;
+ Result.IncludeCodePatterns =
+ EnableSnippets && (CodePatterns != Config::CodePatternsPolicy::None);
Result.IncludeMacros = true;
Result.IncludeGlobals = true;
// We choose to include full comments and not do doxygen parsing in
diff --git a/clang-tools-extra/clangd/CodeComplete.h b/clang-tools-extra/clangd/CodeComplete.h
index 83d347054119e..1cf3b41119043 100644
--- a/clang-tools-extra/clangd/CodeComplete.h
+++ b/clang-tools-extra/clangd/CodeComplete.h
@@ -111,6 +111,9 @@ struct CodeCompleteOptions {
Config::ArgumentListsPolicy ArgumentLists =
Config::ArgumentListsPolicy::FullPlaceholders;
+ /// Whether to suggest code patterns & snippets or not in completion
+ Config::CodePatternsPolicy CodePatterns = Config::CodePatternsPolicy::All;
+
/// Whether to use the clang parser, or fallback to text-based completion
/// (using identifiers in the current file and symbol indexes).
enum CodeCompletionParse {
diff --git a/clang-tools-extra/clangd/Config.h b/clang-tools-extra/clangd/Config.h
index 2891a6d1e77b0..83e0fce847271 100644
--- a/clang-tools-extra/clangd/Config.h
+++ b/clang-tools-extra/clangd/Config.h
@@ -152,6 +152,11 @@ struct Config {
NeverInsert // Never insert headers as part of code completion
};
+ enum class CodePatternsPolicy {
+ All, // Suggest all code patterns and snippets
+ None // Suggest none of the code patterns and snippets
+ };
+
/// Configures code completion feature.
struct {
/// Whether code completion includes results that are not visible in current
@@ -161,6 +166,8 @@ struct Config {
ArgumentListsPolicy ArgumentLists = ArgumentListsPolicy::FullPlaceholders;
/// Controls if headers should be inserted when completions are accepted
HeaderInsertionPolicy HeaderInsertion = HeaderInsertionPolicy::IWYU;
+ /// Enables code patterns & snippets suggestions
+ CodePatternsPolicy CodePatterns = CodePatternsPolicy::All;
} Completion;
/// Configures hover feature.
diff --git a/clang-tools-extra/clangd/ConfigCompile.cpp b/clang-tools-extra/clangd/ConfigCompile.cpp
index 13c2405e76df7..35d35f747a868 100644
--- a/clang-tools-extra/clangd/ConfigCompile.cpp
+++ b/clang-tools-extra/clangd/ConfigCompile.cpp
@@ -707,6 +707,17 @@ struct FragmentCompiler {
C.Completion.HeaderInsertion = *Val;
});
}
+
+ if (F.CodePatterns) {
+ if (auto Val = compileEnum<Config::CodePatternsPolicy>("CodePatterns",
+ *F.CodePatterns)
+ .map("All", Config::CodePatternsPolicy::All)
+ .map("None", Config::CodePatternsPolicy::None)
+ .value())
+ Out.Apply.push_back([Val](const Params &, Config &C) {
+ C.Completion.CodePatterns = *Val;
+ });
+ }
}
void compile(Fragment::HoverBlock &&F) {
diff --git a/clang-tools-extra/clangd/ConfigFragment.h b/clang-tools-extra/clangd/ConfigFragment.h
index 2363b483ab96d..c5d95ef125dee 100644
--- a/clang-tools-extra/clangd/ConfigFragment.h
+++ b/clang-tools-extra/clangd/ConfigFragment.h
@@ -349,6 +349,12 @@ struct Fragment {
/// symbol is forward-declared
/// "Never": Never insert headers
std::optional<Located<std::string>> HeaderInsertion;
+ /// Will suggest code patterns & snippets.
+ /// CLI option available '--code-patterns':
+ /// Values are Config::CodePatternsPolicy:
+ /// all => enable all code patterns and snippets suggestion
+ /// none => disable all code patterns and snippets suggestion
+ std::optional<Located<std::string>> CodePatterns;
};
CompletionBlock Completion;
diff --git a/clang-tools-extra/clangd/ConfigYAML.cpp b/clang-tools-extra/clangd/ConfigYAML.cpp
index 47c6e1cd0f7e7..ff457d8701307 100644
--- a/clang-tools-extra/clangd/ConfigYAML.cpp
+++ b/clang-tools-extra/clangd/ConfigYAML.cpp
@@ -249,6 +249,10 @@ class Parser {
if (auto HeaderInsertion = scalarValue(N, "HeaderInsertion"))
F.HeaderInsertion = *HeaderInsertion;
});
+ Dict.handle("CodePatterns", [&](Node &N) {
+ if (auto CodePatterns = scalarValue(N, "CodePatterns"))
+ F.CodePatterns = *CodePatterns;
+ });
Dict.parse(N);
}
diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp
index 4bd256d6be22b..688b4d4fee17b 100644
--- a/clang-tools-extra/clangd/tool/ClangdMain.cpp
+++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp
@@ -267,6 +267,17 @@ opt<Config::HeaderInsertionPolicy> HeaderInsertion{
"Never insert #include directives as part of code completion")),
};
+opt<Config::CodePatternsPolicy> CodePatterns{
+ "code-patterns",
+ cat(Features),
+ desc("Code completion menu will suggest code patterns and snippets."),
+ init(CodeCompleteOptions().CodePatterns),
+ values(clEnumValN(Config::CodePatternsPolicy::All, "all",
+ "Enable all code patterns and snippets."),
+ clEnumValN(Config::CodePatternsPolicy::None, "none",
+ "Disable all code patterns and snippets.")),
+};
+
opt<bool> ImportInsertions{
"import-insertions",
cat(Features),
@@ -669,6 +680,7 @@ class FlagsConfigProvider : public config::Provider {
std::optional<Config::BackgroundPolicy> BGPolicy;
std::optional<Config::ArgumentListsPolicy> ArgumentLists;
std::optional<Config::HeaderInsertionPolicy> HeaderInsertionPolicy;
+ std::optional<Config::CodePatternsPolicy> CodePatternsPolicy;
// If --compile-commands-dir arg was invoked, check value and override
// default path.
@@ -723,6 +735,10 @@ class FlagsConfigProvider : public config::Provider {
: Config::ArgumentListsPolicy::Delimiters;
}
+ if (CodePatterns == Config::CodePatternsPolicy::None) {
+ CodePatternsPolicy = Config::CodePatternsPolicy::None;
+ }
+
Frag = [=](const config::Params &, Config &C) {
if (CDBSearch)
C.CompileFlags.CDBSearch = *CDBSearch;
@@ -736,6 +752,8 @@ class FlagsConfigProvider : public config::Provider {
C.Completion.HeaderInsertion = *HeaderInsertionPolicy;
if (AllScopesCompletion.getNumOccurrences())
C.Completion.AllScopes = AllScopesCompletion;
+ if (CodePatternsPolicy)
+ C.Completion.CodePatterns = *CodePatternsPolicy;
if (Test)
C.Index.StandardLibrary = false;
@@ -949,6 +967,7 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var
Opts.CodeComplete.BundleOverloads = CompletionStyle != Detailed;
Opts.CodeComplete.ShowOrigins = ShowOrigins;
Opts.CodeComplete.InsertIncludes = HeaderInsertion;
+ Opts.CodeComplete.CodePatterns = CodePatterns;
Opts.CodeComplete.ImportInsertions = ImportInsertions;
if (!HeaderInsertionDecorators) {
Opts.CodeComplete.IncludeIndicator.Insert.clear();
diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index 718bee2e40b11..9077d5fa1999e 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -3326,6 +3326,23 @@ TEST(CompletionTest, AllScopesCompletion) {
kind(CompletionItemKind::EnumMember))));
}
+TEST(CompletionTest, NoCodePatternsIfDisabled) {
+ clangd::CodeCompleteOptions Opts = {};
+ Opts.EnableSnippets = true;
+ Opts.CodePatterns = Config::CodePatternsPolicy::None;
+
+ auto Results = completions(R"cpp(
+ void function() {
+ /// Trying to trigger "for (init-statement; condition; inc-expression)
+ /// {statements}~" code pattern
+ for^
+ }
+ )cpp", {}, Opts);
+
+ EXPECT_THAT(Results.Completions,
+ Not(Contains(kind(CompletionItemKind::Snippet))));
+}
+
TEST(CompletionTest, NoQualifierIfShadowed) {
clangd::CodeCompleteOptions Opts = {};
Opts.AllScopes = true;
diff --git a/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp b/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp
index 979d725461fd0..d71b8d5f9302a 100644
--- a/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp
+++ b/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp
@@ -217,6 +217,19 @@ TEST(ParseYAML, AllScopesWarn) {
EXPECT_THAT(Results[0].Completion.AllScopes, testing::Eq(std::nullopt));
}
+TEST(ParseYAML, CodePatterns) {
+ CapturedDiags Diags;
+ Annotations YAML(R"yaml(
+ Completion:
+ CodePatterns: None
+ )yaml");
+ auto Results =
+ Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());
+ ASSERT_THAT(Diags.Diagnostics, IsEmpty());
+ ASSERT_EQ(Results.size(), 1u);
+ EXPECT_THAT(Results[0].Completion.CodePatterns, llvm::ValueIs(val("None")));
+}
+
TEST(ParseYAML, ShowAKA) {
CapturedDiags Diags;
Annotations YAML(R"yaml(
More information about the cfe-commits
mailing list