Skip to content

Commit d78e431

Browse files
authored
[clangd] Add option to fuzzy-match macros in code-complete (#169880)
Adds `MacroFilterPolicy` ```yaml Completion: MacroFilter: ExactPrefix (default), FuzzyMatch ``` Fixes clangd/clangd#1480
1 parent 4a1b696 commit d78e431

File tree

10 files changed

+138
-9
lines changed

10 files changed

+138
-9
lines changed

clang-tools-extra/clangd/ClangdServer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,7 @@ void ClangdServer::codeComplete(PathRef File, Position Pos,
458458
CodeCompleteOpts.InsertIncludes =
459459
Config::current().Completion.HeaderInsertion;
460460
CodeCompleteOpts.CodePatterns = Config::current().Completion.CodePatterns;
461+
CodeCompleteOpts.MacroFilter = Config::current().Completion.MacroFilter;
461462
// FIXME(ibiryukov): even if Preamble is non-null, we may want to check
462463
// both the old and the new version in case only one of them matches.
463464
CodeCompleteResult Result = clangd::codeComplete(

clang-tools-extra/clangd/CodeComplete.cpp

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,7 +1435,8 @@ bool semaCodeComplete(std::unique_ptr<CodeCompleteConsumer> Consumer,
14351435
Clang->setCodeCompletionConsumer(Consumer.release());
14361436

14371437
if (Input.Preamble.RequiredModules)
1438-
Input.Preamble.RequiredModules->adjustHeaderSearchOptions(Clang->getHeaderSearchOpts());
1438+
Input.Preamble.RequiredModules->adjustHeaderSearchOptions(
1439+
Clang->getHeaderSearchOpts());
14391440

14401441
SyntaxOnlyAction Action;
14411442
if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) {
@@ -2037,13 +2038,28 @@ class CodeCompleteFlow {
20372038
}
20382039

20392040
std::optional<float> fuzzyScore(const CompletionCandidate &C) {
2040-
// Macros can be very spammy, so we only support prefix completion.
2041-
if (((C.SemaResult &&
2041+
using MacroFilterPolicy = Config::MacroFilterPolicy;
2042+
2043+
const auto IsMacroResult =
2044+
((C.SemaResult &&
20422045
C.SemaResult->Kind == CodeCompletionResult::RK_Macro) ||
20432046
(C.IndexResult &&
2044-
C.IndexResult->SymInfo.Kind == index::SymbolKind::Macro)) &&
2045-
!C.Name.starts_with_insensitive(Filter->pattern()))
2047+
C.IndexResult->SymInfo.Kind == index::SymbolKind::Macro));
2048+
2049+
if (!IsMacroResult)
2050+
return Filter->match(C.Name);
2051+
2052+
// macros with underscores are probably noisy, so don't suggest them
2053+
bool RequireExactPrefix =
2054+
Opts.MacroFilter == MacroFilterPolicy::ExactPrefix ||
2055+
C.Name.starts_with_insensitive("_") ||
2056+
C.Name.ends_with_insensitive("_");
2057+
2058+
if (RequireExactPrefix &&
2059+
!C.Name.starts_with_insensitive(Filter->pattern())) {
20462060
return std::nullopt;
2061+
}
2062+
20472063
return Filter->match(C.Name);
20482064
}
20492065

clang-tools-extra/clangd/CodeComplete.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,11 @@ struct CodeCompleteOptions {
114114
/// Whether to suggest code patterns & snippets or not in completion
115115
Config::CodePatternsPolicy CodePatterns = Config::CodePatternsPolicy::All;
116116

117+
/// Filter macros using an exact prefix, or with a fuzzy match. In both cases,
118+
/// macros with leading or trailing underscores are strictly filtered
119+
Config::MacroFilterPolicy MacroFilter =
120+
Config::MacroFilterPolicy::ExactPrefix;
121+
117122
/// Whether to use the clang parser, or fallback to text-based completion
118123
/// (using identifiers in the current file and symbol indexes).
119124
enum CodeCompletionParse {

clang-tools-extra/clangd/Config.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,12 @@ struct Config {
157157
None // Suggest none of the code patterns and snippets
158158
};
159159

160+
enum class MacroFilterPolicy {
161+
ExactPrefix, // Suggest macros if the prefix matches exactly
162+
FuzzyMatch, // Fuzzy-match macros if they do not have "_" as prefix or
163+
// suffix
164+
};
165+
160166
/// Configures code completion feature.
161167
struct {
162168
/// Whether code completion includes results that are not visible in current
@@ -168,6 +174,8 @@ struct Config {
168174
HeaderInsertionPolicy HeaderInsertion = HeaderInsertionPolicy::IWYU;
169175
/// Enables code patterns & snippets suggestions
170176
CodePatternsPolicy CodePatterns = CodePatternsPolicy::All;
177+
/// Controls how macros are filtered
178+
MacroFilterPolicy MacroFilter = MacroFilterPolicy::ExactPrefix;
171179
} Completion;
172180

173181
/// Configures hover feature.

clang-tools-extra/clangd/ConfigCompile.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -564,10 +564,10 @@ struct FragmentCompiler {
564564
auto Fast = isFastTidyCheck(Str);
565565
if (!Fast.has_value()) {
566566
diag(Warning,
567-
llvm::formatv(
568-
"Latency of clang-tidy check '{0}' is not known. "
569-
"It will only run if ClangTidy.FastCheckFilter is Loose or None",
570-
Str)
567+
llvm::formatv("Latency of clang-tidy check '{0}' is not known. "
568+
"It will only run if ClangTidy.FastCheckFilter is "
569+
"Loose or None",
570+
Str)
571571
.str(),
572572
Arg.Range);
573573
} else if (!*Fast) {
@@ -719,6 +719,18 @@ struct FragmentCompiler {
719719
C.Completion.CodePatterns = *Val;
720720
});
721721
}
722+
723+
if (F.MacroFilter) {
724+
if (auto Val =
725+
compileEnum<Config::MacroFilterPolicy>("MacroFilter",
726+
*F.MacroFilter)
727+
.map("ExactPrefix", Config::MacroFilterPolicy::ExactPrefix)
728+
.map("FuzzyMatch", Config::MacroFilterPolicy::FuzzyMatch)
729+
.value())
730+
Out.Apply.push_back([Val](const Params &, Config &C) {
731+
C.Completion.MacroFilter = *Val;
732+
});
733+
}
722734
}
723735

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

clang-tools-extra/clangd/ConfigFragment.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,12 @@ struct Fragment {
354354
/// All => enable all code patterns and snippets suggestion
355355
/// None => disable all code patterns and snippets suggestion
356356
std::optional<Located<std::string>> CodePatterns;
357+
/// How to filter macros before offering them as suggestions
358+
/// Values are Config::MacroFilterPolicy:
359+
/// ExactPrefix: Suggest macros if the prefix matches exactly
360+
/// FuzzyMatch: Fuzzy-match macros if they do not have "_" as prefix or
361+
/// suffix
362+
std::optional<Located<std::string>> MacroFilter;
357363
};
358364
CompletionBlock Completion;
359365

clang-tools-extra/clangd/ConfigYAML.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,10 @@ class Parser {
255255
if (auto CodePatterns = scalarValue(N, "CodePatterns"))
256256
F.CodePatterns = *CodePatterns;
257257
});
258+
Dict.handle("MacroFilter", [&](Node &N) {
259+
if (auto MacroFilter = scalarValue(N, "MacroFilter"))
260+
F.MacroFilter = *MacroFilter;
261+
});
258262
Dict.parse(N);
259263
}
260264

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

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4692,6 +4692,64 @@ TEST(CompletionTest, ListExplicitObjectOverloads) {
46924692
}
46934693
}
46944694

4695+
TEST(CompletionTest, FuzzyMatchMacro) {
4696+
Annotations Code(R"cpp(
4697+
#define gl_foo() 42
4698+
#define _gl_foo() 42
4699+
#define glfbar() 42
4700+
4701+
int gl_frob();
4702+
int _gl_frob();
4703+
4704+
int main() {
4705+
int y = glf$c1^;
4706+
int y = _gl$c2^;
4707+
}
4708+
)cpp");
4709+
4710+
auto TU = TestTU::withCode(Code.code());
4711+
4712+
// Exact prefix should match macro or symbol
4713+
{
4714+
CodeCompleteOptions Opts{};
4715+
EXPECT_EQ(Opts.MacroFilter, Config::MacroFilterPolicy::ExactPrefix);
4716+
4717+
{
4718+
auto Results = completions(TU, Code.point("c1"), {}, Opts);
4719+
EXPECT_THAT(
4720+
Results.Completions,
4721+
ElementsAre(named("gl_frob"), named("_gl_frob"), named("glfbar")));
4722+
}
4723+
4724+
{
4725+
auto Results = completions(TU, Code.point("c2"), {}, Opts);
4726+
EXPECT_THAT(Results.Completions,
4727+
ElementsAre(named("_gl_frob"), named("_gl_foo")));
4728+
}
4729+
}
4730+
4731+
// but with fuzzy match
4732+
{
4733+
CodeCompleteOptions Opts{};
4734+
Opts.MacroFilter = Config::MacroFilterPolicy::FuzzyMatch;
4735+
4736+
// don't suggest underscore macros in general,
4737+
{
4738+
auto Results = completions(TU, Code.point("c1"), {}, Opts);
4739+
EXPECT_THAT(Results.Completions,
4740+
ElementsAre(named("gl_frob"), named("_gl_frob"),
4741+
named("glfbar"), named("gl_foo")));
4742+
}
4743+
4744+
// but do suggest when macro contains exact prefix
4745+
{
4746+
auto Results = completions(TU, Code.point("c2"), {}, Opts);
4747+
EXPECT_THAT(Results.Completions,
4748+
ElementsAre(named("_gl_frob"), named("_gl_foo")));
4749+
}
4750+
}
4751+
}
4752+
46954753
} // namespace
46964754
} // namespace clangd
46974755
} // namespace clang

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,20 @@ TEST(ParseYAML, CodePatterns) {
228228
EXPECT_THAT(Results[0].Completion.CodePatterns, llvm::ValueIs(val("None")));
229229
}
230230

231+
TEST(ParseYAML, MacroFilter) {
232+
CapturedDiags Diags;
233+
Annotations YAML(R"yaml(
234+
Completion:
235+
MacroFilter: FuzzyMatch
236+
)yaml");
237+
auto Results =
238+
Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());
239+
ASSERT_THAT(Diags.Diagnostics, IsEmpty());
240+
ASSERT_EQ(Results.size(), 1u);
241+
EXPECT_THAT(Results[0].Completion.MacroFilter,
242+
llvm::ValueIs(val("FuzzyMatch")));
243+
}
244+
231245
TEST(ParseYAML, Hover) {
232246
CapturedDiags Diags;
233247
Annotations YAML(R"yaml(

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ Hover
107107
Code completion
108108
^^^^^^^^^^^^^^^
109109

110+
- Added a new ``MacroFilter`` configuration option to ``Completion`` to
111+
allow fuzzy-matching with the ``FuzzyMatch`` option when suggesting
112+
macros. ``ExactPrefix`` is the default, which retains previous
113+
behavior of suggesting macros which match the prefix exactly.
114+
110115
Code actions
111116
^^^^^^^^^^^^
112117

0 commit comments

Comments
 (0)