Skip to content

Commit b0c222e

Browse files
MK-AliasHighCommander4
MK-Alias
authored andcommitted
[clangd] Add ArgumentLists config option under Completion
The new config option is a more flexible version of --function-arg-placeholders, allowing users more detailed control of what is inserted in argument list position when clangd completes the name of a function in a function call context. Fixes llvm#63565
1 parent 8c3b94f commit b0c222e

File tree

9 files changed

+101
-19
lines changed

9 files changed

+101
-19
lines changed

clang-tools-extra/clangd/ClangdServer.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,7 @@ void ClangdServer::codeComplete(PathRef File, Position Pos,
451451

452452
CodeCompleteOpts.MainFileSignals = IP->Signals;
453453
CodeCompleteOpts.AllScopes = Config::current().Completion.AllScopes;
454+
CodeCompleteOpts.ArgumentLists = Config::current().Completion.ArgumentLists;
454455
// FIXME(ibiryukov): even if Preamble is non-null, we may want to check
455456
// both the old and the new version in case only one of them matches.
456457
CodeCompleteResult Result = clangd::codeComplete(

clang-tools-extra/clangd/CodeComplete.cpp

+18-8
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "AST.h"
2222
#include "CodeCompletionStrings.h"
2323
#include "Compiler.h"
24+
#include "Config.h"
2425
#include "ExpectedTypes.h"
2526
#include "Feature.h"
2627
#include "FileDistance.h"
@@ -350,8 +351,7 @@ struct CodeCompletionBuilder {
350351
CodeCompletionContext::Kind ContextKind,
351352
const CodeCompleteOptions &Opts,
352353
bool IsUsingDeclaration, tok::TokenKind NextTokenKind)
353-
: ASTCtx(ASTCtx),
354-
EnableFunctionArgSnippets(Opts.EnableFunctionArgSnippets),
354+
: ASTCtx(ASTCtx), ArgumentLists(Opts.ArgumentLists),
355355
IsUsingDeclaration(IsUsingDeclaration), NextTokenKind(NextTokenKind) {
356356
Completion.Deprecated = true; // cleared by any non-deprecated overload.
357357
add(C, SemaCCS, ContextKind);
@@ -561,14 +561,23 @@ struct CodeCompletionBuilder {
561561
}
562562

563563
std::string summarizeSnippet() const {
564+
/// localize ArgumentLists tests for better readability
565+
const bool None = ArgumentLists == Config::ArgumentListsPolicy::None;
566+
const bool Open =
567+
ArgumentLists == Config::ArgumentListsPolicy::OpenDelimiter;
568+
const bool Delim = ArgumentLists == Config::ArgumentListsPolicy::Delimiters;
569+
const bool Full =
570+
ArgumentLists == Config::ArgumentListsPolicy::FullPlaceholders ||
571+
(!None && !Open && !Delim); // <-- failsafe: Full is default
572+
564573
if (IsUsingDeclaration)
565574
return "";
566575
auto *Snippet = onlyValue<&BundledEntry::SnippetSuffix>();
567576
if (!Snippet)
568577
// All bundles are function calls.
569578
// FIXME(ibiryukov): sometimes add template arguments to a snippet, e.g.
570579
// we need to complete 'forward<$1>($0)'.
571-
return "($0)";
580+
return None ? "" : (Open ? "(" : "($0)");
572581

573582
if (Snippet->empty())
574583
return "";
@@ -607,7 +616,7 @@ struct CodeCompletionBuilder {
607616
return "";
608617
}
609618
}
610-
if (EnableFunctionArgSnippets)
619+
if (Full)
611620
return *Snippet;
612621

613622
// Replace argument snippets with a simplified pattern.
@@ -622,9 +631,9 @@ struct CodeCompletionBuilder {
622631

623632
bool EmptyArgs = llvm::StringRef(*Snippet).ends_with("()");
624633
if (Snippet->front() == '<')
625-
return EmptyArgs ? "<$1>()$0" : "<$1>($0)";
634+
return None ? "" : (Open ? "<" : (EmptyArgs ? "<$1>()$0" : "<$1>($0)"));
626635
if (Snippet->front() == '(')
627-
return EmptyArgs ? "()" : "($0)";
636+
return None ? "" : (Open ? "(" : (EmptyArgs ? "()" : "($0)"));
628637
return *Snippet; // Not an arg snippet?
629638
}
630639
// 'CompletionItemKind::Interface' matches template type aliases.
@@ -638,7 +647,7 @@ struct CodeCompletionBuilder {
638647
// e.g. Foo<${1:class}>.
639648
if (llvm::StringRef(*Snippet).ends_with("<>"))
640649
return "<>"; // can happen with defaulted template arguments.
641-
return "<$0>";
650+
return None ? "" : (Open ? "<" : "<$0>");
642651
}
643652
return *Snippet;
644653
}
@@ -654,7 +663,8 @@ struct CodeCompletionBuilder {
654663
ASTContext *ASTCtx;
655664
CodeCompletion Completion;
656665
llvm::SmallVector<BundledEntry, 1> Bundled;
657-
bool EnableFunctionArgSnippets;
666+
/// the way argument lists are handled.
667+
Config::ArgumentListsPolicy ArgumentLists;
658668
// No snippets will be generated for using declarations and when the function
659669
// arguments are already present.
660670
bool IsUsingDeclaration;

clang-tools-extra/clangd/CodeComplete.h

+5-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "ASTSignals.h"
1919
#include "Compiler.h"
20+
#include "Config.h"
2021
#include "Protocol.h"
2122
#include "Quality.h"
2223
#include "index/Index.h"
@@ -96,17 +97,17 @@ struct CodeCompleteOptions {
9697
/// '->' on member access etc.
9798
bool IncludeFixIts = false;
9899

99-
/// Whether to generate snippets for function arguments on code-completion.
100-
/// Needs snippets to be enabled as well.
101-
bool EnableFunctionArgSnippets = true;
102-
103100
/// Whether to include index symbols that are not defined in the scopes
104101
/// visible from the code completion point. This applies in contexts without
105102
/// explicit scope qualifiers.
106103
///
107104
/// Such completions can insert scope qualifiers.
108105
bool AllScopes = false;
109106

107+
/// The way argument list on calls '()' and generics '<>' are handled.
108+
Config::ArgumentListsPolicy ArgumentLists =
109+
Config::ArgumentListsPolicy::FullPlaceholders;
110+
110111
/// Whether to use the clang parser, or fallback to text-based completion
111112
/// (using identifiers in the current file and symbol indexes).
112113
enum CodeCompletionParse {

clang-tools-extra/clangd/Config.h

+14
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,25 @@ struct Config {
126126
std::vector<std::string> FullyQualifiedNamespaces;
127127
} Style;
128128

129+
/// controls the completion options for argument lists.
130+
enum class ArgumentListsPolicy {
131+
/// nothing, no argument list and also NO Delimiters "()" or "<>".
132+
None,
133+
/// open, only opening delimiter "(" or "<".
134+
OpenDelimiter,
135+
/// empty pair of delimiters "()" or "<>".
136+
Delimiters,
137+
/// full name of both type and variable.
138+
FullPlaceholders,
139+
};
140+
129141
/// Configures code completion feature.
130142
struct {
131143
/// Whether code completion includes results that are not visible in current
132144
/// scopes.
133145
bool AllScopes = true;
146+
/// controls the completion options for argument lists.
147+
ArgumentListsPolicy ArgumentLists = ArgumentListsPolicy::FullPlaceholders;
134148
} Completion;
135149

136150
/// Configures hover feature.

clang-tools-extra/clangd/ConfigCompile.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,21 @@ struct FragmentCompiler {
622622
C.Completion.AllScopes = AllScopes;
623623
});
624624
}
625+
if (F.ArgumentLists) {
626+
if (auto Val =
627+
compileEnum<Config::ArgumentListsPolicy>("ArgumentLists",
628+
*F.ArgumentLists)
629+
.map("None", Config::ArgumentListsPolicy::None)
630+
.map("OpenDelimiter",
631+
Config::ArgumentListsPolicy::OpenDelimiter)
632+
.map("Delimiters", Config::ArgumentListsPolicy::Delimiters)
633+
.map("FullPlaceholders",
634+
Config::ArgumentListsPolicy::FullPlaceholders)
635+
.value())
636+
Out.Apply.push_back([Val](const Params &, Config &C) {
637+
C.Completion.ArgumentLists = *Val;
638+
});
639+
}
625640
}
626641

627642
void compile(Fragment::HoverBlock &&F) {

clang-tools-extra/clangd/ConfigFragment.h

+8
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONFIGFRAGMENT_H
3333
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONFIGFRAGMENT_H
3434

35+
#include "Config.h"
3536
#include "ConfigProvider.h"
3637
#include "llvm/Support/SMLoc.h"
3738
#include "llvm/Support/SourceMgr.h"
@@ -308,6 +309,13 @@ struct Fragment {
308309
/// Whether code completion should include suggestions from scopes that are
309310
/// not visible. The required scope prefix will be inserted.
310311
std::optional<Located<bool>> AllScopes;
312+
/// How to present the argument list between '()' and '<>':
313+
/// valid values are enum Config::ArgumentListsPolicy values:
314+
/// None: Nothing at all
315+
/// OpenDelimiter: only opening delimiter "(" or "<"
316+
/// Delimiters: empty pair of delimiters "()" or "<>"
317+
/// FullPlaceholders: full name of both type and parameter
318+
std::optional<Located<std::string>> ArgumentLists;
311319
};
312320
CompletionBlock Completion;
313321

clang-tools-extra/clangd/ConfigYAML.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ class Parser {
230230
if (auto AllScopes = boolValue(N, "AllScopes"))
231231
F.AllScopes = *AllScopes;
232232
});
233+
Dict.handle("ArgumentLists", [&](Node &N) {
234+
if (auto ArgumentLists = scalarValue(N, "ArgumentLists"))
235+
F.ArgumentLists = *ArgumentLists;
236+
});
233237
Dict.parse(N);
234238
}
235239

clang-tools-extra/clangd/tool/ClangdMain.cpp

+13-5
Original file line numberDiff line numberDiff line change
@@ -242,13 +242,13 @@ opt<std::string> FallbackStyle{
242242
init(clang::format::DefaultFallbackStyle),
243243
};
244244

245-
opt<bool> EnableFunctionArgSnippets{
245+
opt<int> EnableFunctionArgSnippets{
246246
"function-arg-placeholders",
247247
cat(Features),
248-
desc("When disabled, completions contain only parentheses for "
249-
"function calls. When enabled, completions also contain "
248+
desc("When disabled (0), completions contain only parentheses for "
249+
"function calls. When enabled (1), completions also contain "
250250
"placeholders for method parameters"),
251-
init(CodeCompleteOptions().EnableFunctionArgSnippets),
251+
init(-1),
252252
};
253253

254254
opt<CodeCompleteOptions::IncludeInsertion> HeaderInsertion{
@@ -650,6 +650,7 @@ class FlagsConfigProvider : public config::Provider {
650650
std::optional<Config::CDBSearchSpec> CDBSearch;
651651
std::optional<Config::ExternalIndexSpec> IndexSpec;
652652
std::optional<Config::BackgroundPolicy> BGPolicy;
653+
std::optional<Config::ArgumentListsPolicy> ArgumentLists;
653654

654655
// If --compile-commands-dir arg was invoked, check value and override
655656
// default path.
@@ -694,13 +695,21 @@ class FlagsConfigProvider : public config::Provider {
694695
BGPolicy = Config::BackgroundPolicy::Skip;
695696
}
696697

698+
if (EnableFunctionArgSnippets >= 0) {
699+
ArgumentLists = EnableFunctionArgSnippets
700+
? Config::ArgumentListsPolicy::FullPlaceholders
701+
: Config::ArgumentListsPolicy::Delimiters;
702+
}
703+
697704
Frag = [=](const config::Params &, Config &C) {
698705
if (CDBSearch)
699706
C.CompileFlags.CDBSearch = *CDBSearch;
700707
if (IndexSpec)
701708
C.Index.External = *IndexSpec;
702709
if (BGPolicy)
703710
C.Index.Background = *BGPolicy;
711+
if (ArgumentLists)
712+
C.Completion.ArgumentLists = *ArgumentLists;
704713
if (AllScopesCompletion.getNumOccurrences())
705714
C.Completion.AllScopes = AllScopesCompletion;
706715

@@ -916,7 +925,6 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var
916925
Opts.CodeComplete.IncludeIndicator.Insert.clear();
917926
Opts.CodeComplete.IncludeIndicator.NoInsert.clear();
918927
}
919-
Opts.CodeComplete.EnableFunctionArgSnippets = EnableFunctionArgSnippets;
920928
Opts.CodeComplete.RunParser = CodeCompletionParse;
921929
Opts.CodeComplete.RankingModel = RankingModel;
922930

clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp

+23-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "ClangdServer.h"
1212
#include "CodeComplete.h"
1313
#include "Compiler.h"
14+
#include "Config.h"
1415
#include "Feature.h"
1516
#include "Matchers.h"
1617
#include "Protocol.h"
@@ -2595,10 +2596,10 @@ TEST(SignatureHelpTest, DynamicIndexDocumentation) {
25952596
ElementsAre(AllOf(sig("foo() -> int"), sigDoc("Member doc"))));
25962597
}
25972598

2598-
TEST(CompletionTest, CompletionFunctionArgsDisabled) {
2599+
TEST(CompletionTest, ArgumentListsPolicy) {
25992600
CodeCompleteOptions Opts;
26002601
Opts.EnableSnippets = true;
2601-
Opts.EnableFunctionArgSnippets = false;
2602+
Opts.ArgumentLists = Config::ArgumentListsPolicy::Delimiters;
26022603

26032604
{
26042605
auto Results = completions(
@@ -2670,6 +2671,26 @@ TEST(CompletionTest, CompletionFunctionArgsDisabled) {
26702671
EXPECT_THAT(Results.Completions, UnorderedElementsAre(AllOf(
26712672
named("FOO"), snippetSuffix("($0)"))));
26722673
}
2674+
{
2675+
Opts.ArgumentLists = Config::ArgumentListsPolicy::None;
2676+
auto Results = completions(
2677+
R"cpp(
2678+
void xfoo(int x, int y);
2679+
void f() { xfo^ })cpp",
2680+
{}, Opts);
2681+
EXPECT_THAT(Results.Completions,
2682+
UnorderedElementsAre(AllOf(named("xfoo"), snippetSuffix(""))));
2683+
}
2684+
{
2685+
Opts.ArgumentLists = Config::ArgumentListsPolicy::OpenDelimiter;
2686+
auto Results = completions(
2687+
R"cpp(
2688+
void xfoo(int x, int y);
2689+
void f() { xfo^ })cpp",
2690+
{}, Opts);
2691+
EXPECT_THAT(Results.Completions,
2692+
UnorderedElementsAre(AllOf(named("xfoo"), snippetSuffix("("))));
2693+
}
26732694
}
26742695

26752696
TEST(CompletionTest, SuggestOverrides) {

0 commit comments

Comments
 (0)