Skip to content

[clangd] Add CodePatterns config option under Completion #137613

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 12, 2025

Conversation

Noustaa
Copy link
Contributor

@Noustaa Noustaa commented Apr 28, 2025

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: clangd/clangd#1867

Copy link

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot
Copy link
Member

llvmbot commented Apr 28, 2025

@llvm/pr-subscribers-clang-tools-extra

Author: Noustaa (Noustaa)

Changes

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: clangd/clangd#1867


Full diff: https://github.com/llvm/llvm-project/pull/137613.diff

10 Files Affected:

  • (modified) clang-tools-extra/clangd/ClangdServer.cpp (+1)
  • (modified) clang-tools-extra/clangd/CodeComplete.cpp (+6-1)
  • (modified) clang-tools-extra/clangd/CodeComplete.h (+3)
  • (modified) clang-tools-extra/clangd/Config.h (+7)
  • (modified) clang-tools-extra/clangd/ConfigCompile.cpp (+11)
  • (modified) clang-tools-extra/clangd/ConfigFragment.h (+6)
  • (modified) clang-tools-extra/clangd/ConfigYAML.cpp (+4)
  • (modified) clang-tools-extra/clangd/tool/ClangdMain.cpp (+19)
  • (modified) clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp (+17)
  • (modified) clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp (+13)
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(

@llvmbot
Copy link
Member

llvmbot commented Apr 28, 2025

@llvm/pr-subscribers-clangd

Author: Noustaa (Noustaa)

Changes

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: clangd/clangd#1867


Full diff: https://github.com/llvm/llvm-project/pull/137613.diff

10 Files Affected:

  • (modified) clang-tools-extra/clangd/ClangdServer.cpp (+1)
  • (modified) clang-tools-extra/clangd/CodeComplete.cpp (+6-1)
  • (modified) clang-tools-extra/clangd/CodeComplete.h (+3)
  • (modified) clang-tools-extra/clangd/Config.h (+7)
  • (modified) clang-tools-extra/clangd/ConfigCompile.cpp (+11)
  • (modified) clang-tools-extra/clangd/ConfigFragment.h (+6)
  • (modified) clang-tools-extra/clangd/ConfigYAML.cpp (+4)
  • (modified) clang-tools-extra/clangd/tool/ClangdMain.cpp (+19)
  • (modified) clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp (+17)
  • (modified) clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp (+13)
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(

Copy link

github-actions bot commented Apr 28, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

Allows enabling/disabling code patterns & snippets suggestion from
completion.
This can be done either with YAML config or CLI with --code-patterns
@Noustaa Noustaa force-pushed the noustaa_feat_code_pattern_policy branch from d3bf294 to 7e71544 Compare April 29, 2025 06:07
@Noustaa
Copy link
Contributor Author

Noustaa commented Apr 30, 2025

Code autocomplete works just fine with CodePatterns::None but include autocomplete does not work anymore.
Gonna investigate when i have some time.

@HighCommander4
Copy link
Collaborator

Code autocomplete works just fine with CodePatterns::None but include autocomplete does not work anymore. Gonna investigate when i have some time.

I guess the preprocessor completions are all RK_Pattern (added here, and the CodeCompletionResult constructor which only takes a CodeCompletionString sets the kind to RK_Pattern).

@Noustaa
Copy link
Contributor Author

Noustaa commented May 5, 2025

There is 2 different features involved here.

  1. The fact that if i type "inc" it will suggest me both includes snippets
    Like the below image the ones tagged LSP on the right side
    image

  2. The fact that if i type "#include <str" it is supposed to search my library path to suggest "string" as autocompletion for headers.

In the feature i'm trying to implement, number 1 should be OFF if CodePatterns is set to None but number 2 should still be working.
Are these 2 features (num 1 & 2) coupled or can they be managed separately as the current state ?

@HighCommander4
Copy link
Collaborator

HighCommander4 commented May 5, 2025

There is 2 different features involved here.

[...]

In the feature i'm trying to implement, number 1 should be OFF if CodePatterns is set to None but number 2 should still be working.

That makes sense to me, as a user expectation.

The code I linked to in my previous comment is for (1). The completions for scenario (2) come from here, but that's also RK_Pattern.

Are these 2 features (num 1 & 2) coupled or can they be managed separately as the current state ?

It looks like we can achieve this by allowing RK_Pattern completions if Context.getKind() == CodeCompletionContext::CCC_IncludedFile.

Fix the #include preprocessor not autocompleting when CodePatterns is
set to None
@Noustaa
Copy link
Contributor Author

Noustaa commented May 5, 2025

It looks like we can achieve this by allowing RK_Pattern completions if Context.getKind() == CodeCompletionContext::CCC_IncludedFile.

Great ! That worked :)
I updated the PR and also added a unit test for this specific case

@HighCommander4 HighCommander4 self-requested a review May 12, 2025 03:58
Copy link
Collaborator

@HighCommander4 HighCommander4 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, the patch and tests look pretty good. I think we should leave out the command-line arg, but otherwise should be good to go.

Noustaa and others added 3 commits May 12, 2025 08:41
This last condition is here to allow user to get autocomplete suggestion
when inside an include expression
to match the actual values the parser accepts
Copy link
Collaborator

@HighCommander4 HighCommander4 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, LGTM!

(I made one small tweak, to ConfigFragment.h, to refer to the config option values All and None with correct capitalization.)

@HighCommander4 HighCommander4 merged commit bfd2ef7 into llvm:main May 12, 2025
4 of 5 checks passed
Copy link

@Noustaa Congratulations on having your first Pull Request (PR) merged into the LLVM Project!

Your changes will be combined with recent changes from other authors, then tested by our build bots. If there is a problem with a build, you may receive a report in an email or a comment on this PR.

Please check whether problems have been caused by your change specifically, as the builds can include changes from many authors. It is not uncommon for your change to be included in a build that fails due to someone else's changes, or infrastructure issues.

How to do this, and the rest of the post-merge process, is covered in detail here.

If your change does cause a problem, it may be reverted, or you can revert it yourself. This is a normal part of LLVM development. You can fix your changes and open a new PR to merge them again.

If you don't get any reports, no action is required from you. Your changes are working as expected, well done!

@HighCommander4
Copy link
Collaborator

@Noustaa if you'd like to also submit a documentation patch for https://clangd.llvm.org/config.html (source at https://github.com/llvm/clangd-www/blob/main/config.md), that would be appreciated!

@Noustaa
Copy link
Contributor Author

Noustaa commented May 13, 2025

@Noustaa if you'd like to also submit a documentation patch for https://clangd.llvm.org/config.html (source at https://github.com/llvm/clangd-www/blob/main/config.md), that would be appreciated!

Sure, i'm gonna do it tomorrow 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants