reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
//===--- 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 actions that run over the AST and produce edits, messages
// etc 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 "ParsedAST.h"
#include "Path.h"
#include "Protocol.h"
#include "Selection.h"
#include "SourceCode.h"
#include "index/Index.h"
#include "clang/Tooling/Core/Replacement.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include <string>

namespace clang {
namespace clangd {

/// 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:
///     const char* id() const override final; // defined by REGISTER_TWEAK.
///     // implement other methods here.
///   };
///   REGISTER_TWEAK(MyTweak);
class Tweak {
public:
  /// Input to prepare and apply tweaks.
  struct Selection {
    Selection(const SymbolIndex *Index, ParsedAST &AST, unsigned RangeBegin,
              unsigned RangeEnd);
    /// The text of the active document.
    llvm::StringRef Code;
    /// The Index for handling codebase related queries.
    const SymbolIndex *Index = nullptr;
    /// Parsed AST of the active file.
    ParsedAST &AST;
    /// A location of the cursor in the editor.
    // FIXME: Cursor is redundant and should be removed
    SourceLocation Cursor;
    /// The begin offset of the selection
    unsigned SelectionBegin;
    /// The end offset of the selection
    unsigned SelectionEnd;
    /// The AST nodes that were selected.
    SelectionTree ASTSelection;
    // FIXME: provide a way to get sources and ASTs for other files.
  };

  /// Output of a tweak.
  enum Intent {
    /// Apply changes that preserve the behavior of the code.
    Refactor,
    /// Provide information to the user.
    Info,
  };
  struct Effect {
    /// A message to be displayed to the user.
    llvm::Optional<std::string> ShowMessage;
    /// A mapping from file path(the one used for accessing the underlying VFS)
    /// to edits.
    llvm::StringMap<Edit> ApplyEdits;

    static Effect showMessage(StringRef S) {
      Effect E;
      E.ShowMessage = S;
      return E;
    }

    /// Path is the absolute, symlink-resolved path for the file pointed by FID
    /// in SM. Edit is generated from Replacements.
    /// Fails if cannot figure out absolute path for FID.
    static llvm::Expected<std::pair<Path, Edit>>
    fileEdit(const SourceManager &SM, FileID FID,
             tooling::Replacements Replacements);

    /// Creates an effect with an Edit for the main file.
    /// Fails if cannot figure out absolute path for main file.
    static llvm::Expected<Tweak::Effect>
    mainFileEdit(const SourceManager &SM, tooling::Replacements Replacements);
  };

  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 const char *id() const = 0;
  /// Run the first stage of the action. Returns true indicating that the
  /// action is available and should be shown to the user. Returns false 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 effect.
  /// EXPECTS: prepare() was called and returned true.
  virtual Expected<Effect> 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;
  /// Describes what kind of action this is.
  /// EXPECTS: prepare() was called and returned true.
  virtual Intent intent() const = 0;
  /// Is this a 'hidden' tweak, which are off by default.
  virtual bool hidden() const { return false; }
};

// 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=*/"");           \
  const char *Subclass::id() const { return #Subclass; }

/// Calls prepare() on all tweaks that satisfy the filter, returning those that
/// can run on the selection.
std::vector<std::unique_ptr<Tweak>>
prepareTweaks(const Tweak::Selection &S,
              llvm::function_ref<bool(const Tweak &)> Filter);

// 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(StringRef TweakID,
                                                    const Tweak::Selection &S);
} // namespace clangd
} // namespace clang

#endif