[clangd] Add CodePatterns config option under Completion (#137613)
Allows enabling/disabling code pattern & snippet suggestions during code completion. Resolves https://github.com/clangd/clangd/discussions/1867
This commit is contained in:
parent
a6c4ca8e93
commit
bfd2ef7659
@ -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(
|
||||
|
@ -926,7 +926,8 @@ struct CompletionRecorder : public CodeCompleteConsumer {
|
||||
// FIXME: in case there is no future sema completion callback after the
|
||||
// recovery mode, we might still want to provide some results (e.g. trivial
|
||||
// identifier-based completion).
|
||||
if (Context.getKind() == CodeCompletionContext::CCC_Recovery) {
|
||||
CodeCompletionContext::Kind ContextKind = Context.getKind();
|
||||
if (ContextKind == CodeCompletionContext::CCC_Recovery) {
|
||||
log("Code complete: Ignoring sema code complete callback with Recovery "
|
||||
"context.");
|
||||
return;
|
||||
@ -950,6 +951,12 @@ 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 &&
|
||||
// keep allowing the include files autocomplete suggestions
|
||||
ContextKind != CodeCompletionContext::CCC_IncludedFile)
|
||||
continue;
|
||||
// Class members that are shadowed by subclasses are usually noise.
|
||||
if (Result.Hidden && Result.Declaration &&
|
||||
Result.Declaration->isCXXClassMember())
|
||||
@ -2153,7 +2160,8 @@ private:
|
||||
|
||||
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
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -349,6 +349,11 @@ struct Fragment {
|
||||
/// symbol is forward-declared
|
||||
/// "Never": Never insert headers
|
||||
std::optional<Located<std::string>> HeaderInsertion;
|
||||
/// Will suggest code patterns & snippets.
|
||||
/// 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;
|
||||
|
||||
|
@ -249,6 +249,10 @@ private:
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -3326,6 +3326,40 @@ 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, CompleteIncludeIfCodePatternsNone) {
|
||||
clangd::CodeCompleteOptions Opts = {};
|
||||
Opts.EnableSnippets = true;
|
||||
Opts.CodePatterns = Config::CodePatternsPolicy::None;
|
||||
|
||||
Annotations Test(R"cpp(#include "^)cpp");
|
||||
auto TU = TestTU::withCode(Test.code());
|
||||
TU.AdditionalFiles["foo/bar.h"] = "";
|
||||
TU.ExtraArgs.push_back("-I" + testPath("foo"));
|
||||
|
||||
auto Results = completions(TU, Test.point(), {}, Opts);
|
||||
EXPECT_THAT(Results.Completions,
|
||||
AllOf(has("foo/", CompletionItemKind::Folder),
|
||||
has("bar.h\"", CompletionItemKind::File)));
|
||||
}
|
||||
|
||||
TEST(CompletionTest, NoQualifierIfShadowed) {
|
||||
clangd::CodeCompleteOptions Opts = {};
|
||||
Opts.AllScopes = true;
|
||||
|
@ -217,6 +217,19 @@ Completion:
|
||||
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(
|
||||
|
Loading…
x
Reference in New Issue
Block a user