Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/ClangdServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
11 changes: 9 additions & 2 deletions clang-tools-extra/clangd/CodeComplete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -950,6 +951,11 @@ 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 &&
ContextKind != CodeCompletionContext::CCC_IncludedFile)
continue;
// Class members that are shadowed by subclasses are usually noise.
if (Result.Hidden && Result.Declaration &&
Result.Declaration->isCXXClassMember())
Expand Down Expand Up @@ -2153,7 +2159,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
Expand Down
3 changes: 3 additions & 0 deletions clang-tools-extra/clangd/CodeComplete.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
7 changes: 7 additions & 0 deletions clang-tools-extra/clangd/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down
11 changes: 11 additions & 0 deletions clang-tools-extra/clangd/ConfigCompile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
6 changes: 6 additions & 0 deletions clang-tools-extra/clangd/ConfigFragment.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
4 changes: 4 additions & 0 deletions clang-tools-extra/clangd/ConfigYAML.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
19 changes: 19 additions & 0 deletions clang-tools-extra/clangd/tool/ClangdMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
34 changes: 34 additions & 0 deletions clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
13 changes: 13 additions & 0 deletions clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Loading