Module: RubyLsp::Refactor::Support::NodeHelpers

Overview

Shared helpers mixed into every listener.

Provides:

- node_to_lsp_range(node)       – Prism location → Interface::Range
- node_covers_cursor?(node)      – overlap check against @node_context.range
- single_edit_workspace_edit(…)  – convenience WorkspaceEdit factory

Instance Method Summary collapse

Instance Method Details

#create_file_operation(uri) ⇒ Interface::CreateFile

Builds a CreateFile operation for use in a multi_file_workspace_edit. The file is created empty; a subsequent TextDocumentEdit writes its content.

Parameters:

  • uri (String)

    file URI for the new file

Returns:

  • (Interface::CreateFile)


138
139
140
141
142
143
144
145
146
147
# File 'lib/ruby_lsp/ruby_lsp_refactor/support/node_helpers.rb', line 138

def create_file_operation(uri)
  Interface::CreateFile.new(
    kind: "create",
    uri: uri,
    options: Interface::CreateFileOptions.new(
      overwrite: false,
      ignore_if_exists: false
    )
  )
end

#delete_line_edit(node) ⇒ Interface::TextEdit

Produces a TextEdit that deletes the full source line of node, including its trailing newline so no blank line is left behind.

Parameters:

  • node (Prism::Node)

Returns:

  • (Interface::TextEdit)


80
81
82
83
84
85
86
87
88
89
# File 'lib/ruby_lsp/ruby_lsp_refactor/support/node_helpers.rb', line 80

def delete_line_edit(node)
  line = node.location.start_line - 1
  Interface::TextEdit.new(
    range: Interface::Range.new(
      start: Interface::Position.new(line: line, character: 0),
      end: Interface::Position.new(line: line + 1, character: 0)
    ),
    new_text: ""
  )
end

#indent_for(node) ⇒ String

Leading whitespace for the line that contains node.

Parameters:

  • node (Prism::Node)

Returns:

  • (String)


95
96
97
# File 'lib/ruby_lsp/ruby_lsp_refactor/support/node_helpers.rb', line 95

def indent_for(node)
  " " * node.location.start_column
end

#multi_edit_workspace_edit(edits) ⇒ Interface::WorkspaceEdit

Builds a WorkspaceEdit from an arbitrary array of TextEdit objects.

Parameters:

  • edits (Array<Interface::TextEdit>)

Returns:

  • (Interface::WorkspaceEdit)


69
70
71
72
73
# File 'lib/ruby_lsp/ruby_lsp_refactor/support/node_helpers.rb', line 69

def multi_edit_workspace_edit(edits)
  Interface::WorkspaceEdit.new(
    changes: { @node_context.uri => edits }
  )
end

#multi_file_workspace_edit(document_changes) ⇒ Interface::WorkspaceEdit

Builds a multi-file WorkspaceEdit using document_changes, which supports both file creation (CreateFile) and text edits across multiple documents (TextDocumentEdit).

document_changes is an ordered array of:

Interface::CreateFile         create a new empty file
Interface::TextDocumentEdit   apply text edits to a file

The LSP spec requires CreateFile operations to appear before any TextDocumentEdit that writes into the newly created file.

Parameters:

  • document_changes (Array<Interface::CreateFile | Interface::TextDocumentEdit>)

Returns:

  • (Interface::WorkspaceEdit)


112
113
114
# File 'lib/ruby_lsp/ruby_lsp_refactor/support/node_helpers.rb', line 112

def multi_file_workspace_edit(document_changes)
  Interface::WorkspaceEdit.new(document_changes: document_changes)
end

#node_covers_cursor?(node) ⇒ Boolean

Returns true when the node’s source range overlaps the cursor/selection range provided by the LSP client via @node_context.

Parameters:

  • node (Prism::Node)

Returns:

  • (Boolean)


36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/ruby_lsp/ruby_lsp_refactor/support/node_helpers.rb', line 36

def node_covers_cursor?(node)
  cursor = @node_context.range
  return true unless cursor

  node_start = node.location.start_line - 1
  node_end   = node.location.end_line   - 1

  cursor_start = cursor.start.line
  cursor_end   = cursor.end.line

  node_start <= cursor_end && node_end >= cursor_start
end

#node_to_lsp_range(node) ⇒ Interface::Range

Converts a Prism node’s location to an LSP Interface::Range.

Parameters:

  • node (Prism::Node)

Returns:

  • (Interface::Range)


17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/ruby_lsp/ruby_lsp_refactor/support/node_helpers.rb', line 17

def node_to_lsp_range(node)
  loc = node.location
  Interface::Range.new(
    start: Interface::Position.new(
      line: loc.start_line - 1,
      character: loc.start_column
    ),
    end: Interface::Position.new(
      line: loc.end_line - 1,
      character: loc.end_column
    )
  )
end

#single_edit_workspace_edit(node, new_text) ⇒ Interface::WorkspaceEdit

Builds a WorkspaceEdit containing a single TextEdit that replaces the entire range of node with new_text.

Parameters:

  • node (Prism::Node)
  • new_text (String)

Returns:

  • (Interface::WorkspaceEdit)


55
56
57
58
59
60
61
62
63
# File 'lib/ruby_lsp/ruby_lsp_refactor/support/node_helpers.rb', line 55

def single_edit_workspace_edit(node, new_text)
  Interface::WorkspaceEdit.new(
    changes: {
      @node_context.uri => [
        Interface::TextEdit.new(range: node_to_lsp_range(node), new_text: new_text)
      ]
    }
  )
end

#text_document_edit(uri, edits) ⇒ Interface::TextDocumentEdit

Builds a TextDocumentEdit for a given URI and array of TextEdits. Used as one entry inside a multi_file_workspace_edit.

Parameters:

  • uri (String)

    file URI, e.g. “file:///project/lib/foo.rb”

  • edits (Array<Interface::TextEdit>)

Returns:

  • (Interface::TextDocumentEdit)


122
123
124
125
126
127
128
129
130
# File 'lib/ruby_lsp/ruby_lsp_refactor/support/node_helpers.rb', line 122

def text_document_edit(uri, edits)
  Interface::TextDocumentEdit.new(
    text_document: Interface::OptionalVersionedTextDocumentIdentifier.new(
      uri: uri,
      version: nil
    ),
    edits: edits
  )
end