Class: RubyLsp::Requests::CodeActionResolve
- Inherits:
-
BaseRequest
- Object
- SyntaxTree::Visitor
- BaseRequest
- RubyLsp::Requests::CodeActionResolve
- Extended by:
- T::Sig
- Defined in:
- lib/ruby_lsp/requests/code_action_resolve.rb
Overview

The code action resolve request is used to to resolve the edit field for a given code action, if it is not already provided in the textDocument/codeAction response. We can use it for scenarios that require more computation such as refactoring.
Example: Extract to variable
# Before:
1 + 1 # Select the text and use Refactor: Extract Variable
# After:
new_variable = 1 + 1
new_variable
Defined Under Namespace
Classes: CodeActionError, Error
Constant Summary collapse
- NEW_VARIABLE_NAME =
"new_variable"
Instance Method Summary collapse
-
#initialize(document, code_action) ⇒ CodeActionResolve
constructor
A new instance of CodeActionResolve.
- #run ⇒ Object
Methods inherited from BaseRequest
#full_constant_name, #locate, #range_from_syntax_tree_node, #visible?, #visit_all
Constructor Details
#initialize(document, code_action) ⇒ CodeActionResolve
Returns a new instance of CodeActionResolve.
38 39 40 41 42 |
# File 'lib/ruby_lsp/requests/code_action_resolve.rb', line 38 def initialize(document, code_action) super(document) @code_action = code_action end |
Instance Method Details
#run ⇒ Object
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 |
# File 'lib/ruby_lsp/requests/code_action_resolve.rb', line 45 def run source_range = @code_action.dig(:data, :range) return Error::EmptySelection if source_range[:start] == source_range[:end] return Error::InvalidTargetRange if @document.syntax_error? scanner = @document.create_scanner start_index = scanner.find_char_position(source_range[:start]) end_index = scanner.find_char_position(source_range[:end]) extracted_source = T.must(@document.source[start_index...end_index]) # Find the closest statements node, so that we place the refactor in a valid position closest_statements = locate(T.must(@document.tree), start_index, node_types: [SyntaxTree::Statements]).first return Error::InvalidTargetRange if closest_statements.nil? # Find the node with the end line closest to the requested position, so that we can place the refactor # immediately after that closest node closest_node = closest_statements.child_nodes.compact.min_by do |node| distance = source_range.dig(:start, :line) - (node.location.end_line - 1) distance <= 0 ? Float::INFINITY : distance end # When trying to extract the first node inside of a statements block, then we can just select one line above it target_line = if closest_node == closest_statements.child_nodes.first closest_node.location.start_line - 1 else closest_node.location.end_line end lines = @document.source.lines indentation = T.must(T.must(lines[target_line - 1])[/\A */]).size target_range = { start: { line: target_line, character: indentation }, end: { line: target_line, character: indentation }, } variable_source = if T.must(lines[target_line]).strip.empty? "\n#{" " * indentation}#{NEW_VARIABLE_NAME} = #{extracted_source}" else "#{NEW_VARIABLE_NAME} = #{extracted_source}\n#{" " * indentation}" end Interface::CodeAction.new( title: "Refactor: Extract Variable", edit: Interface::WorkspaceEdit.new( document_changes: [ Interface::TextDocumentEdit.new( text_document: Interface::OptionalVersionedTextDocumentIdentifier.new( uri: @code_action.dig(:data, :uri), version: nil, ), edits: [ create_text_edit(source_range, NEW_VARIABLE_NAME), create_text_edit(target_range, variable_source), ], ), ], ), ) end |