diff --git a/clang-tools-extra/clangd/CMakeLists.txt b/clang-tools-extra/clangd/CMakeLists.txt --- a/clang-tools-extra/clangd/CMakeLists.txt +++ b/clang-tools-extra/clangd/CMakeLists.txt @@ -36,6 +36,7 @@ CollectMacros.cpp CompileCommands.cpp Compiler.cpp + Config.cpp ConfigYAML.cpp Diagnostics.cpp DraftStore.cpp diff --git a/clang-tools-extra/clangd/CompileCommands.cpp b/clang-tools-extra/clangd/CompileCommands.cpp --- a/clang-tools-extra/clangd/CompileCommands.cpp +++ b/clang-tools-extra/clangd/CompileCommands.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "CompileCommands.h" +#include "Config.h" #include "support/Logger.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Tooling/ArgumentsAdjusters.h" @@ -182,6 +183,10 @@ } void CommandMangler::adjust(std::vector &Cmd) const { + // FIXME: remove const_cast once unique_function is const-compatible. + for (auto &Edit : const_cast(Config::current()).CompileFlags.Edits) + Edit(Cmd); + // Check whether the flag exists, either as -flag or -flag=* auto Has = [&](llvm::StringRef Flag) { for (llvm::StringRef Arg : Cmd) { diff --git a/clang-tools-extra/clangd/Config.h b/clang-tools-extra/clangd/Config.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/Config.h @@ -0,0 +1,63 @@ +//===--- Config.h - User configuration of clangd behavior --------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Various clangd features have configurable behaviour (or can be disabled). +// This file defines "resolved" configuration seen by features within clangd. +// For example, settings may vary per-file, the resolved Config only contains +// settings that apply to the current file. +// +// This is distinct from how the config is specified by the user (Fragment) +// interpreted (CompiledFragment), and combined (Provider). +// ConfigFragment.h describes the steps to add a new configuration option. +// +// Because this structure is shared throughout clangd, it's a potential source +// of layering problems. Config should be expressed in terms of simple +// vocubulary types where possible. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONFIG_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONFIG_H + +#include "support/Context.h" +#include "llvm/ADT/FunctionExtras.h" +#include +#include + +namespace clang { +namespace clangd { + +/// Settings that express user/project preferences and control clangd behavior. +/// +/// Generally, features should consume Config::current() and the caller is +/// responsible for setting it appropriately. In practice these callers are +/// ClangdServer, TUScheduler, and BackgroundQueue. +struct Config { + /// Returns the Config of the current Context, or an empty configuration. + static const Config ¤t(); + /// Context key which can be used to set the current Config. + static clangd::Key Key; + + Config() = default; + Config(const Config &) = delete; + Config &operator=(const Config &) = delete; + Config(Config &&) = default; + Config &operator=(Config &&) = default; + + /// Controls how the compile command for the current file is determined. + struct { + // Edits to apply to the compile command, in sequence. + // FIXME: these functions need to be const-callable. For now, const_cast. + std::vector &)>> Edits; + } CompileFlags; +}; + +} // namespace clangd +} // namespace clang + +#endif diff --git a/clang-tools-extra/clangd/Config.cpp b/clang-tools-extra/clangd/Config.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/Config.cpp @@ -0,0 +1,25 @@ +//===--- Config.cpp - User configuration of clangd behavior ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Config.h" +#include "support/Context.h" + +namespace clang { +namespace clangd { + +Key Config::Key; + +const Config &Config::current() { + if (const Config *C = Context::current().get(Key)) + return *C; + static Config Default; + return Default; +} + +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/unittests/CompileCommandsTests.cpp b/clang-tools-extra/clangd/unittests/CompileCommandsTests.cpp --- a/clang-tools-extra/clangd/unittests/CompileCommandsTests.cpp +++ b/clang-tools-extra/clangd/unittests/CompileCommandsTests.cpp @@ -7,7 +7,9 @@ //===----------------------------------------------------------------------===// #include "CompileCommands.h" +#include "Config.h" #include "TestFS.h" +#include "support/Context.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/StringExtras.h" @@ -185,6 +187,26 @@ } #endif +TEST(CommandMangler, ConfigEdits) { + auto Mangler = CommandMangler::forTests(); + std::vector Cmd = {"clang++", "foo.cc"}; + { + Config Cfg; + Cfg.CompileFlags.Edits.push_back([](std::vector &Argv) { + for (auto &Arg : Argv) + for (char &C : Arg) + C = llvm::toUpper(C); + }); + Cfg.CompileFlags.Edits.push_back( + [](std::vector &Argv) { Argv.push_back("--hello"); }); + WithContextValue WithConfig(Config::Key, std::move(Cfg)); + Mangler.adjust(Cmd); + } + // Edits are applied in given order and before other mangling. + EXPECT_THAT(Cmd, + ElementsAre("CLANG++", "FOO.CC", "--hello", "-fsyntax-only")); +} + } // namespace } // namespace clangd } // namespace clang