diff options
Diffstat (limited to 'clang-tools-extra/clangd/refactor')
-rw-r--r-- | clang-tools-extra/clangd/refactor/Tweak.cpp | 74 | ||||
-rw-r--r-- | clang-tools-extra/clangd/refactor/Tweak.h | 98 | ||||
-rw-r--r-- | clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt | 13 | ||||
-rw-r--r-- | clang-tools-extra/clangd/refactor/tweaks/Dummy.cpp | 9 |
4 files changed, 194 insertions, 0 deletions
diff --git a/clang-tools-extra/clangd/refactor/Tweak.cpp b/clang-tools-extra/clangd/refactor/Tweak.cpp new file mode 100644 index 00000000000..4b316280e6c --- /dev/null +++ b/clang-tools-extra/clangd/refactor/Tweak.cpp @@ -0,0 +1,74 @@ +//===--- Tweak.cpp -----------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +#include "Tweak.h" +#include "Logger.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Registry.h" +#include <functional> +#include <memory> + +LLVM_INSTANTIATE_REGISTRY(llvm::Registry<clang::clangd::Tweak>); + +namespace clang { +namespace clangd { + +/// A handy typedef to save some typing. +typedef llvm::Registry<Tweak> TweakRegistry; + +namespace { +/// Asserts invariants on TweakRegistry. No-op with assertion disabled. +void validateRegistry() { +#ifndef NDEBUG + llvm::StringSet<> Seen; + for (const auto &E : TweakRegistry::entries()) { + // REGISTER_TWEAK ensures E.getName() is equal to the tweak class name. + // We check that id() matches it. + assert(E.instantiate()->id() == E.getName() && + "id should be equal to class name"); + assert(Seen.try_emplace(E.getName()).second && "duplicate check id"); + } +#endif +} +} // namespace + +std::vector<std::unique_ptr<Tweak>> prepareTweaks(const Tweak::Selection &S) { + validateRegistry(); + + std::vector<std::unique_ptr<Tweak>> Available; + for (const auto &E : TweakRegistry::entries()) { + std::unique_ptr<Tweak> T = E.instantiate(); + if (!T->prepare(S)) + continue; + Available.push_back(std::move(T)); + } + // Ensure deterministic order of the results. + llvm::sort(Available, + [](const std::unique_ptr<Tweak> &L, + const std::unique_ptr<Tweak> &R) { return L->id() < R->id(); }); + return Available; +} + +llvm::Expected<std::unique_ptr<Tweak>> prepareTweak(TweakID ID, + const Tweak::Selection &S) { + auto It = llvm::find_if( + TweakRegistry::entries(), + [ID](const TweakRegistry::entry &E) { return E.getName() == ID; }); + if (It == TweakRegistry::end()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "id of the tweak is invalid"); + std::unique_ptr<Tweak> T = It->instantiate(); + if (!T->prepare(S)) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "failed to prepare() a check"); + return T; +} + +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/refactor/Tweak.h b/clang-tools-extra/clangd/refactor/Tweak.h new file mode 100644 index 00000000000..df00bf7fede --- /dev/null +++ b/clang-tools-extra/clangd/refactor/Tweak.h @@ -0,0 +1,98 @@ +//===--- Tweak.h -------------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// Tweaks are small refactoring-like actions that run over the AST and produce +// the set of edits as a result. They are local, i.e. they should take the +// current editor context, e.g. the cursor position and selection into account. +// The actions are executed in two stages: +// - Stage 1 should check whether the action is available in a current +// context. It should be cheap and fast to compute as it is executed for all +// available actions on every client request, which happen quite frequently. +// - Stage 2 is performed after stage 1 and can be more expensive to compute. +// It is performed when the user actually chooses the action. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_REFACTOR_ACTIONS_TWEAK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_REFACTOR_ACTIONS_TWEAK_H + +#include "ClangdUnit.h" +#include "Protocol.h" +#include "clang/Tooling/Core/Replacement.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +namespace clang { +namespace clangd { + +using TweakID = llvm::StringRef; + +/// An interface base for small context-sensitive refactoring actions. +/// To implement a new tweak use the following pattern in a .cpp file: +/// class MyTweak : public Tweak { +/// public: +/// TweakID id() const override final; // definition provided by +/// // REGISTER_TWEAK. +/// // implement other methods here. +/// }; +/// REGISTER_TWEAK(MyTweak); +class Tweak { +public: + /// Input to prepare and apply tweaks. + struct Selection { + /// The text of the active document. + llvm::StringRef Code; + /// Parsed AST of the active file. + ParsedAST &AST; + /// A location of the cursor in the editor. + SourceLocation Cursor; + // FIXME: add selection when there are checks relying on it. + // FIXME: provide a way to get sources and ASTs for other files. + // FIXME: cache some commonly required information (e.g. AST nodes under + // cursor) to avoid redundant AST visit in every action. + }; + virtual ~Tweak() = default; + /// A unique id of the action, it is always equal to the name of the class + /// defining the Tweak. Definition is provided automatically by + /// REGISTER_TWEAK. + virtual TweakID id() const = 0; + /// Run the first stage of the action. The non-None result indicates that the + /// action is available and should be shown to the user. Returns None if the + /// action is not available. + /// This function should be fast, if the action requires non-trivial work it + /// should be moved into 'apply'. + /// Returns true iff the action is available and apply() can be called on it. + virtual bool prepare(const Selection &Sel) = 0; + /// Run the second stage of the action that would produce the actual changes. + /// EXPECTS: prepare() was called and returned true. + virtual Expected<tooling::Replacements> apply(const Selection &Sel) = 0; + /// A one-line title of the action that should be shown to the users in the + /// UI. + /// EXPECTS: prepare() was called and returned true. + virtual std::string title() const = 0; +}; + +// All tweaks must be registered in the .cpp file next to their definition. +#define REGISTER_TWEAK(Subclass) \ + ::llvm::Registry<::clang::clangd::Tweak>::Add<Subclass> \ + TweakRegistrationFor##Subclass(#Subclass, /*Description=*/""); \ + ::clang::clangd::TweakID Subclass::id() const { \ + return llvm::StringLiteral(#Subclass); \ + } + +/// Calls prepare() on all tweaks, returning those that can run on the +/// selection. +std::vector<std::unique_ptr<Tweak>> prepareTweaks(const Tweak::Selection &S); + +// Calls prepare() on the tweak with a given ID. +// If prepare() returns false, returns an error. +// If prepare() returns true, returns the corresponding tweak. +llvm::Expected<std::unique_ptr<Tweak>> prepareTweak(TweakID ID, + const Tweak::Selection &S); + +} // namespace clangd +} // namespace clang + +#endif diff --git a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt new file mode 100644 index 00000000000..630a9d06bfb --- /dev/null +++ b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt @@ -0,0 +1,13 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../..) + +# A target containing all code tweaks (i.e. mini-refactorings) provided by +# clangd. +# Built as an object library to make sure linker does not remove global +# constructors that register individual tweaks in a global registry. +# To enable these tweaks in exectubales or shared libraries, add +# $<TARGET_OBJECTS:obj.clangDaemonTweaks> to a list of sources, see +# clangd/tool/CMakeLists.txt for an example. +add_clang_library(clangDaemonTweaks OBJECT + Dummy.cpp # FIXME: to avoid CMake errors due to empty inputs, remove when a + # first tweak lands. + ) diff --git a/clang-tools-extra/clangd/refactor/tweaks/Dummy.cpp b/clang-tools-extra/clangd/refactor/tweaks/Dummy.cpp new file mode 100644 index 00000000000..f64716ae915 --- /dev/null +++ b/clang-tools-extra/clangd/refactor/tweaks/Dummy.cpp @@ -0,0 +1,9 @@ +//===--- Dummy.cpp -----------------------------------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Does nothing, only here to avoid cmake errors for empty libraries.
\ No newline at end of file |