diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp index ac1e9aa5f0ff1..f1a87dd12d905 100644 --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -458,6 +458,7 @@ void ClangdServer::codeComplete(PathRef File, Position Pos, CodeCompleteOpts.InsertIncludes = Config::current().Completion.HeaderInsertion; CodeCompleteOpts.CodePatterns = Config::current().Completion.CodePatterns; + CodeCompleteOpts.MacroFilter = Config::current().Completion.MacroFilter; // 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( diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp index e4df7581f1315..0358455ff1f75 100644 --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -1435,7 +1435,8 @@ bool semaCodeComplete(std::unique_ptr Consumer, Clang->setCodeCompletionConsumer(Consumer.release()); if (Input.Preamble.RequiredModules) - Input.Preamble.RequiredModules->adjustHeaderSearchOptions(Clang->getHeaderSearchOpts()); + Input.Preamble.RequiredModules->adjustHeaderSearchOptions( + Clang->getHeaderSearchOpts()); SyntaxOnlyAction Action; if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) { @@ -2037,14 +2038,34 @@ class CodeCompleteFlow { } std::optional fuzzyScore(const CompletionCandidate &C) { - // Macros can be very spammy, so we only support prefix completion. - if (((C.SemaResult && + using MacroFilterPolicy = Config::MacroFilterPolicy; + + const auto IsMacroResult = + ((C.SemaResult && C.SemaResult->Kind == CodeCompletionResult::RK_Macro) || (C.IndexResult && - C.IndexResult->SymInfo.Kind == index::SymbolKind::Macro)) && - !C.Name.starts_with_insensitive(Filter->pattern())) - return std::nullopt; - return Filter->match(C.Name); + C.IndexResult->SymInfo.Kind == index::SymbolKind::Macro)); + + if (!IsMacroResult) + return Filter->match(C.Name); + + // macros with leading and trailing underscore are probably spammy + switch (Opts.MacroFilter) { + case MacroFilterPolicy::ExactPrefix: + if (C.Name.starts_with_insensitive(Filter->pattern())) + return Filter->match(C.Name); + else + return std::nullopt; + case MacroFilterPolicy::FuzzyMatch: + if (!C.Name.starts_with_insensitive("_") && + !C.Name.ends_with_insensitive("_")) + return Filter->match(C.Name); + else + return std::nullopt; + } + llvm_unreachable("Unhandled MacroFilter option in fuzzyScore."); + + return std::nullopt; } CodeCompletion::Scores diff --git a/clang-tools-extra/clangd/CodeComplete.h b/clang-tools-extra/clangd/CodeComplete.h index 1cf3b41119043..cde22a8212e6a 100644 --- a/clang-tools-extra/clangd/CodeComplete.h +++ b/clang-tools-extra/clangd/CodeComplete.h @@ -114,6 +114,11 @@ struct CodeCompleteOptions { /// Whether to suggest code patterns & snippets or not in completion Config::CodePatternsPolicy CodePatterns = Config::CodePatternsPolicy::All; + /// Filter macros using an exact prefix, or with a fuzzy match. In both cases, + /// macros with leading or trailing underscores are strictly filtered + Config::MacroFilterPolicy MacroFilter = + Config::MacroFilterPolicy::ExactPrefix; + /// Whether to use the clang parser, or fallback to text-based completion /// (using identifiers in the current file and symbol indexes). enum CodeCompletionParse { diff --git a/clang-tools-extra/clangd/Config.h b/clang-tools-extra/clangd/Config.h index 01997cee08515..bc58c831e4e89 100644 --- a/clang-tools-extra/clangd/Config.h +++ b/clang-tools-extra/clangd/Config.h @@ -157,6 +157,12 @@ struct Config { None // Suggest none of the code patterns and snippets }; + enum class MacroFilterPolicy { + ExactPrefix, // Suggest macros if the prefix matches exactly + FuzzyMatch, // Fuzzy-match macros if they do not have "_" as prefix or + // suffix + }; + /// Configures code completion feature. struct { /// Whether code completion includes results that are not visible in current @@ -168,6 +174,8 @@ struct Config { HeaderInsertionPolicy HeaderInsertion = HeaderInsertionPolicy::IWYU; /// Enables code patterns & snippets suggestions CodePatternsPolicy CodePatterns = CodePatternsPolicy::All; + /// Controls how macros are filtered + MacroFilterPolicy MacroFilter = MacroFilterPolicy::ExactPrefix; } Completion; /// Configures hover feature. diff --git a/clang-tools-extra/clangd/ConfigCompile.cpp b/clang-tools-extra/clangd/ConfigCompile.cpp index 18e31809aa7c7..2b41949d6d05c 100644 --- a/clang-tools-extra/clangd/ConfigCompile.cpp +++ b/clang-tools-extra/clangd/ConfigCompile.cpp @@ -564,10 +564,10 @@ struct FragmentCompiler { auto Fast = isFastTidyCheck(Str); if (!Fast.has_value()) { diag(Warning, - llvm::formatv( - "Latency of clang-tidy check '{0}' is not known. " - "It will only run if ClangTidy.FastCheckFilter is Loose or None", - Str) + llvm::formatv("Latency of clang-tidy check '{0}' is not known. " + "It will only run if ClangTidy.FastCheckFilter is " + "Loose or None", + Str) .str(), Arg.Range); } else if (!*Fast) { @@ -719,6 +719,18 @@ struct FragmentCompiler { C.Completion.CodePatterns = *Val; }); } + + if (F.MacroFilter) { + if (auto Val = + compileEnum("MacroFilter", + *F.MacroFilter) + .map("ExactPrefix", Config::MacroFilterPolicy::ExactPrefix) + .map("FuzzyMatch", Config::MacroFilterPolicy::FuzzyMatch) + .value()) + Out.Apply.push_back([Val](const Params &, Config &C) { + C.Completion.MacroFilter = *Val; + }); + } } void compile(Fragment::HoverBlock &&F) { diff --git a/clang-tools-extra/clangd/ConfigFragment.h b/clang-tools-extra/clangd/ConfigFragment.h index 2afeb36574b21..7604fe4e24c97 100644 --- a/clang-tools-extra/clangd/ConfigFragment.h +++ b/clang-tools-extra/clangd/ConfigFragment.h @@ -354,6 +354,12 @@ struct Fragment { /// All => enable all code patterns and snippets suggestion /// None => disable all code patterns and snippets suggestion std::optional> CodePatterns; + /// How to filter macros before offering them as suggestions + /// Values are Config::MacroFilterPolicy: + /// ExactPrefix: Suggest macros if the prefix matches exactly + /// FuzzyMatch: Fuzzy-match macros if they do not have "_" as prefix or + /// suffix + std::optional> MacroFilter; }; CompletionBlock Completion; diff --git a/clang-tools-extra/clangd/ConfigYAML.cpp b/clang-tools-extra/clangd/ConfigYAML.cpp index 392cf19b05a55..7b6993620fb8c 100644 --- a/clang-tools-extra/clangd/ConfigYAML.cpp +++ b/clang-tools-extra/clangd/ConfigYAML.cpp @@ -255,6 +255,10 @@ class Parser { if (auto CodePatterns = scalarValue(N, "CodePatterns")) F.CodePatterns = *CodePatterns; }); + Dict.handle("MacroFilter", [&](Node &N) { + if (auto MacroFilter = scalarValue(N, "MacroFilter")) + F.MacroFilter = *MacroFilter; + }); Dict.parse(N); } diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp index e2bdb0fe46e37..1bec601ef1d36 100644 --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -4692,6 +4692,35 @@ TEST(CompletionTest, ListExplicitObjectOverloads) { } } +TEST(CompletionTest, FuzzyMatchMacro) { + const auto *const Code = R"cpp( + #define gl_foo() 42 + #define _gl_foo() 42 + int gl_frob(); + + int main() { + int x = glf^ + } + )cpp"; + + { + CodeCompleteOptions Opts{}; + EXPECT_EQ(Opts.MacroFilter, Config::MacroFilterPolicy::ExactPrefix); + + auto Results = completions(Code, {}, Opts); + EXPECT_THAT(Results.Completions, ElementsAre(named("gl_frob"))); + } + + { + CodeCompleteOptions Opts{}; + Opts.MacroFilter = Config::MacroFilterPolicy::FuzzyMatch; + + auto Results = completions(Code, {}, Opts); + EXPECT_THAT(Results.Completions, + ElementsAre(named("gl_frob"), named("gl_foo"))); + } +} + } // namespace } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp b/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp index c332dcc417fe1..264cb453b413c 100644 --- a/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp +++ b/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp @@ -228,6 +228,20 @@ TEST(ParseYAML, CodePatterns) { EXPECT_THAT(Results[0].Completion.CodePatterns, llvm::ValueIs(val("None"))); } +TEST(ParseYAML, MacroFilter) { + CapturedDiags Diags; + Annotations YAML(R"yaml( + Completion: + MacroFilter: FuzzyMatch + )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.MacroFilter, + llvm::ValueIs(val("FuzzyMatch"))); +} + TEST(ParseYAML, Hover) { CapturedDiags Diags; Annotations YAML(R"yaml( diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index a6f80e3721db1..aa0f9f0508a15 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -102,6 +102,10 @@ Hover Code completion ^^^^^^^^^^^^^^^ +- Added a new ``MacroFilter`` configuration option to ``Completion`` to + allow fuzzy-matching with the ``FuzzyMatch`` option when suggesting + macros. ``ExactPrefix`` is the default, which retains previous + behavior of suggesting macros which match the prefix exactly. Code actions ^^^^^^^^^^^^