Class: RubyLsp::Requests::Rename

Inherits:
RubyLsp::Request show all
Includes:
Support::Common
Defined in:
lib/ruby_lsp/requests/rename.rb

Overview

The [rename](microsoft.github.io/language-server-protocol/specification#textDocument_rename) request renames all instances of a symbol in a document.

Defined Under Namespace

Classes: InvalidNameError

Instance Attribute Summary

Attributes inherited from Message

#method, #params

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Support::Common

#categorized_markdown_from_index_entries, #constant_name, #create_code_lens, #each_constant_path_part, #kind_for_entry, #markdown_from_index_entries, #namespace_constant_name, #not_in_dependencies?, #range_from_location, #range_from_node, #self_receiver?

Methods inherited from RubyLsp::Request

register_watched_files, #to_hash

Methods inherited from Message

#to_hash

Constructor Details

#initialize(global_state, store, document, params) ⇒ Rename

: (GlobalState global_state, Store store, (RubyDocument | ERBDocument) document, Hash[Symbol, untyped] params) -> void



22
23
24
25
26
27
28
29
# File 'lib/ruby_lsp/requests/rename.rb', line 22

def initialize(global_state, store, document, params)
  super()
  @global_state = global_state
  @store = store
  @document = document
  @position = params[:position] #: Hash[Symbol, Integer]
  @new_name = params[:newName] #: String
end

Class Method Details

.providerObject

: -> Interface::RenameOptions



16
17
18
# File 'lib/ruby_lsp/requests/rename.rb', line 16

def provider
  Interface::RenameOptions.new(prepare_provider: true)
end

Instance Method Details

#performObject

: -> Interface::WorkspaceEdit?



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
# File 'lib/ruby_lsp/requests/rename.rb', line 33

def perform
  char_position, _ = @document.find_index_by_position(@position)

  node_context = RubyDocument.locate(
    @document.ast,
    char_position,
    node_types: [Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::ConstantPathTargetNode],
    code_units_cache: @document.code_units_cache,
  )
  target = node_context.node
  parent = node_context.parent
  return if !target || target.is_a?(Prism::ProgramNode)

  if target.is_a?(Prism::ConstantReadNode) && parent.is_a?(Prism::ConstantPathNode)
    target = determine_target(
      target,
      parent,
      @position,
    )
  end

  target = target #: as Prism::ConstantReadNode | Prism::ConstantPathNode | Prism::ConstantPathTargetNode

  name = RubyIndexer::Index.constant_name(target)
  return unless name

  entries = @global_state.index.resolve(name, node_context.nesting)
  return unless entries

  if (conflict_entries = @global_state.index.resolve(@new_name, node_context.nesting))
    raise InvalidNameError, "The new name is already in use by #{conflict_entries.first&.name}"
  end

  fully_qualified_name = entries.first #: as !nil
    .name
  reference_target = RubyIndexer::ReferenceFinder::ConstTarget.new(fully_qualified_name)
  changes = collect_text_edits(reference_target, name)

  # If the client doesn't support resource operations, such as renaming files, then we can only return the basic
  # text changes
  unless @global_state.client_capabilities.supports_rename?
    return Interface::WorkspaceEdit.new(changes: changes)
  end

  # Text edits must be applied before any resource operations, such as renaming files. Otherwise, the file is
  # renamed and then the URI associated to the text edit no longer exists, causing it to be dropped
  document_changes = changes.map do |uri, edits|
    Interface::TextDocumentEdit.new(
      text_document: Interface::VersionedTextDocumentIdentifier.new(uri: uri, version: nil),
      edits: edits,
    )
  end

  collect_file_renames(fully_qualified_name, document_changes)
  Interface::WorkspaceEdit.new(document_changes: document_changes)
end