Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
32 changes: 19 additions & 13 deletions clang-tools-extra/clangd/CodeComplete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,8 +350,7 @@ struct CodeCompletionBuilder {
CodeCompletionContext::Kind ContextKind,
const CodeCompleteOptions &Opts,
bool IsUsingDeclaration, tok::TokenKind NextTokenKind)
: ASTCtx(ASTCtx),
EnableFunctionArgSnippets(Opts.EnableFunctionArgSnippets),
: ASTCtx(ASTCtx), PlaceholderType(Opts.PlaceholderType),
IsUsingDeclaration(IsUsingDeclaration), NextTokenKind(NextTokenKind) {
Completion.Deprecated = true; // cleared by any non-deprecated overload.
add(C, SemaCCS, ContextKind);
Expand Down Expand Up @@ -561,14 +560,22 @@ struct CodeCompletionBuilder {
}

std::string summarizeSnippet() const {
/// localize PlaceholderType for better readability
const bool None = PlaceholderType == CodeCompleteOptions::None;
const bool Open = PlaceholderType == CodeCompleteOptions::OpenDelimiter;
const bool Delim = PlaceholderType == CodeCompleteOptions::Delimiters;
const bool Full =
PlaceholderType == CodeCompleteOptions::FullPlaceholders ||
(!None && !Open && !Delim); // <-- failsafe

if (IsUsingDeclaration)
return "";
auto *Snippet = onlyValue<&BundledEntry::SnippetSuffix>();
if (!Snippet)
// All bundles are function calls.
// FIXME(ibiryukov): sometimes add template arguments to a snippet, e.g.
// we need to complete 'forward<$1>($0)'.
return "($0)";
return None ? "" : (Open ? "(" : "($0)");

if (Snippet->empty())
return "";
Expand Down Expand Up @@ -607,7 +614,7 @@ struct CodeCompletionBuilder {
return "";
}
}
if (EnableFunctionArgSnippets)
if (Full)
return *Snippet;

// Replace argument snippets with a simplified pattern.
Expand All @@ -622,9 +629,9 @@ struct CodeCompletionBuilder {

bool EmptyArgs = llvm::StringRef(*Snippet).ends_with("()");
if (Snippet->front() == '<')
return EmptyArgs ? "<$1>()$0" : "<$1>($0)";
return None ? "" : (Open ? "<" : (EmptyArgs ? "<$1>()$0" : "<$1>($0)"));
if (Snippet->front() == '(')
return EmptyArgs ? "()" : "($0)";
return None ? "" : (Open ? "(" : (EmptyArgs ? "()$0" : "($0)"));
return *Snippet; // Not an arg snippet?
}
// 'CompletionItemKind::Interface' matches template type aliases.
Expand All @@ -638,7 +645,7 @@ struct CodeCompletionBuilder {
// e.g. Foo<${1:class}>.
if (llvm::StringRef(*Snippet).ends_with("<>"))
return "<>"; // can happen with defaulted template arguments.
return "<$0>";
return None ? "" : (Open ? "<" : "<$0>");
}
return *Snippet;
}
Expand All @@ -654,7 +661,7 @@ struct CodeCompletionBuilder {
ASTContext *ASTCtx;
CodeCompletion Completion;
llvm::SmallVector<BundledEntry, 1> Bundled;
bool EnableFunctionArgSnippets;
CodeCompleteOptions::PlaceholderOption PlaceholderType;
// No snippets will be generated for using declarations and when the function
// arguments are already present.
bool IsUsingDeclaration;
Expand Down Expand Up @@ -797,8 +804,8 @@ SpecifiedScope getQueryScopes(CodeCompletionContext &CCContext,
llvm::StringRef SpelledSpecifier = Lexer::getSourceText(
CharSourceRange::getCharRange(SemaSpecifier->getRange()),
CCSema.SourceMgr, clang::LangOptions());
if (SpelledSpecifier.consume_front("::"))
Scopes.QueryScopes = {""};
if (SpelledSpecifier.consume_front("::"))
Scopes.QueryScopes = {""};
Scopes.UnresolvedQualifier = std::string(SpelledSpecifier);
// Sema excludes the trailing "::".
if (!Scopes.UnresolvedQualifier->empty())
Expand Down Expand Up @@ -1591,7 +1598,7 @@ class CodeCompleteFlow {
CompletionPrefix HeuristicPrefix;
std::optional<FuzzyMatcher> Filter; // Initialized once Sema runs.
Range ReplacedRange;
std::vector<std::string> QueryScopes; // Initialized once Sema runs.
std::vector<std::string> QueryScopes; // Initialized once Sema runs.
std::vector<std::string> AccessibleScopes; // Initialized once Sema runs.
// Initialized once QueryScopes is initialized, if there are scopes.
std::optional<ScopeDistance> ScopeProximity;
Expand Down Expand Up @@ -2387,8 +2394,7 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const CodeCompletion &C) {
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
const CodeCompleteResult &R) {
OS << "CodeCompleteResult: " << R.Completions.size() << (R.HasMore ? "+" : "")
<< " (" << getCompletionKindString(R.Context) << ")"
<< " items:\n";
<< " (" << getCompletionKindString(R.Context) << ")" << " items:\n";
for (const auto &C : R.Completions)
OS << C << "\n";
return OS;
Expand Down
18 changes: 15 additions & 3 deletions clang-tools-extra/clangd/CodeComplete.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,21 @@ struct CodeCompleteOptions {
/// '->' on member access etc.
bool IncludeFixIts = false;

/// Whether to generate snippets for function arguments on code-completion.
/// Needs snippets to be enabled as well.
bool EnableFunctionArgSnippets = true;
// requested by community in favour of 'EnableFunctionArgSnippets',
// see here for more info:
// https://github.com/llvm/llvm-project/issues/63565#issuecomment-1975065771
/// Controls how the delimter/argument-list for callables: "()"
/// and for generics: "<>" are handled
enum PlaceholderOption {
/// nothing, no argument list and also NO Delimiters "()" or "<>"
None = 0,
/// open, only opening delimiter "(" or "<"
OpenDelimiter,
/// empty pair of delimiters "()" or "<>" (or [legacy] alias 0)
Delimiters,
/// full name of both type and variable (or [legacy] alias 1)
FullPlaceholders,
} PlaceholderType = PlaceholderOption::FullPlaceholders;

/// Whether to include index symbols that are not defined in the scopes
/// visible from the code completion point. This applies in contexts without
Expand Down
34 changes: 26 additions & 8 deletions clang-tools-extra/clangd/tool/ClangdMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,13 +242,31 @@ opt<std::string> FallbackStyle{
init(clang::format::DefaultFallbackStyle),
};

opt<bool> EnableFunctionArgSnippets{
"function-arg-placeholders",
cat(Features),
desc("When disabled, completions contain only parentheses for "
"function calls. When enabled, completions also contain "
"placeholders for method parameters"),
init(CodeCompleteOptions().EnableFunctionArgSnippets),
opt<CodeCompleteOptions::PlaceholderOption> PlaceholderOption{
"function-arg-placeholders", cat(Features),
desc("Set the way placeholders and delimiters are implemented."),
init(CodeCompleteOptions().PlaceholderType),
values(
clEnumValN(CodeCompleteOptions::PlaceholderOption::None, "None",
"insert nothing, not even the delimiters \"()\" or "
"\"<>\": void foo"),
clEnumValN(CodeCompleteOptions::PlaceholderOption::OpenDelimiter,
"OpenDelimiter",
"Only insert opening delimiter \"(\" or \"<\", no "
"placeholders: void foo("),
clEnumValN(CodeCompleteOptions::PlaceholderOption::Delimiters,
"Delimiters",
"Only insert delimiters \"()\" and \"<>\", no placeholders: "
"void foo()"),
clEnumValN(
CodeCompleteOptions::PlaceholderOption::FullPlaceholders,
"FullPlaceholders",
"[default] Use full type names and value names: void foo(int x)"),
clEnumValN(CodeCompleteOptions::PlaceholderOption::Delimiters, "0",
"[deprecated] use: Delimiters instead"),
clEnumValN(CodeCompleteOptions::PlaceholderOption::FullPlaceholders,
"1", "[deprecated] use: FullPlaceholders instead"))

};

opt<CodeCompleteOptions::IncludeInsertion> HeaderInsertion{
Expand Down Expand Up @@ -916,7 +934,7 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var
Opts.CodeComplete.IncludeIndicator.Insert.clear();
Opts.CodeComplete.IncludeIndicator.NoInsert.clear();
}
Opts.CodeComplete.EnableFunctionArgSnippets = EnableFunctionArgSnippets;
Opts.CodeComplete.PlaceholderType = PlaceholderOption;
Opts.CodeComplete.RunParser = CodeCompletionParse;
Opts.CodeComplete.RankingModel = RankingModel;

Expand Down