summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clangd/refactor
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/clangd/refactor')
-rw-r--r--clang-tools-extra/clangd/refactor/Tweak.cpp74
-rw-r--r--clang-tools-extra/clangd/refactor/Tweak.h98
-rw-r--r--clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt13
-rw-r--r--clang-tools-extra/clangd/refactor/tweaks/Dummy.cpp9
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
OpenPOWER on IntegriCloud