ruby-lsp 0.4.2 → 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +29 -35
  3. data/VERSION +1 -1
  4. data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +62 -0
  5. data/lib/ruby_lsp/document.rb +74 -6
  6. data/lib/ruby_lsp/event_emitter.rb +52 -0
  7. data/lib/ruby_lsp/executor.rb +60 -14
  8. data/lib/ruby_lsp/internal.rb +2 -0
  9. data/lib/ruby_lsp/listener.rb +39 -0
  10. data/lib/ruby_lsp/requests/base_request.rb +2 -85
  11. data/lib/ruby_lsp/requests/code_action_resolve.rb +46 -19
  12. data/lib/ruby_lsp/requests/code_actions.rb +5 -4
  13. data/lib/ruby_lsp/requests/code_lens.rb +135 -0
  14. data/lib/ruby_lsp/requests/diagnostics.rb +3 -3
  15. data/lib/ruby_lsp/requests/document_highlight.rb +7 -9
  16. data/lib/ruby_lsp/requests/document_link.rb +7 -7
  17. data/lib/ruby_lsp/requests/document_symbol.rb +13 -10
  18. data/lib/ruby_lsp/requests/folding_ranges.rb +13 -9
  19. data/lib/ruby_lsp/requests/formatting.rb +5 -4
  20. data/lib/ruby_lsp/requests/hover.rb +28 -32
  21. data/lib/ruby_lsp/requests/inlay_hints.rb +5 -4
  22. data/lib/ruby_lsp/requests/path_completion.rb +16 -10
  23. data/lib/ruby_lsp/requests/selection_ranges.rb +3 -3
  24. data/lib/ruby_lsp/requests/semantic_highlighting.rb +29 -15
  25. data/lib/ruby_lsp/requests/support/common.rb +55 -0
  26. data/lib/ruby_lsp/requests/support/highlight_target.rb +5 -4
  27. data/lib/ruby_lsp/requests/support/rails_document_client.rb +7 -6
  28. data/lib/ruby_lsp/requests/support/selection_range.rb +1 -1
  29. data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +2 -2
  30. data/lib/ruby_lsp/requests/support/sorbet.rb +5 -15
  31. data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +39 -0
  32. data/lib/ruby_lsp/requests.rb +3 -0
  33. data/lib/ruby_lsp/server.rb +4 -1
  34. data/lib/ruby_lsp/store.rb +10 -10
  35. metadata +11 -5
@@ -7,6 +7,7 @@ module RubyLsp
7
7
  class BaseRequest < SyntaxTree::Visitor
8
8
  extend T::Sig
9
9
  extend T::Helpers
10
+ include Support::Common
10
11
 
11
12
  abstract!
12
13
 
@@ -31,94 +32,10 @@ module RubyLsp
31
32
  # Syntax Tree implements `visit_all` using `map` instead of `each` for users who want to use the pattern
32
33
  # `result = visitor.visit(tree)`. However, we don't use that pattern and should avoid producing a new array for
33
34
  # every single node visited
34
- sig { params(nodes: T::Array[SyntaxTree::Node]).void }
35
+ sig { params(nodes: T::Array[T.nilable(SyntaxTree::Node)]).void }
35
36
  def visit_all(nodes)
36
37
  nodes.each { |node| visit(node) }
37
38
  end
38
-
39
- sig { params(node: SyntaxTree::Node).returns(LanguageServer::Protocol::Interface::Range) }
40
- def range_from_syntax_tree_node(node)
41
- loc = node.location
42
-
43
- LanguageServer::Protocol::Interface::Range.new(
44
- start: LanguageServer::Protocol::Interface::Position.new(
45
- line: loc.start_line - 1,
46
- character: loc.start_column,
47
- ),
48
- end: LanguageServer::Protocol::Interface::Position.new(line: loc.end_line - 1, character: loc.end_column),
49
- )
50
- end
51
-
52
- sig do
53
- params(node: T.any(SyntaxTree::ConstPathRef, SyntaxTree::ConstRef, SyntaxTree::TopConstRef)).returns(String)
54
- end
55
- def full_constant_name(node)
56
- name = +node.constant.value
57
- constant = T.let(node, SyntaxTree::Node)
58
-
59
- while constant.is_a?(SyntaxTree::ConstPathRef)
60
- constant = constant.parent
61
-
62
- case constant
63
- when SyntaxTree::ConstPathRef
64
- name.prepend("#{constant.constant.value}::")
65
- when SyntaxTree::VarRef
66
- name.prepend("#{constant.value.value}::")
67
- end
68
- end
69
-
70
- name
71
- end
72
-
73
- sig do
74
- params(
75
- node: SyntaxTree::Node,
76
- position: Integer,
77
- node_types: T::Array[T.class_of(SyntaxTree::Node)],
78
- ).returns([T.nilable(SyntaxTree::Node), T.nilable(SyntaxTree::Node)])
79
- end
80
- def locate(node, position, node_types: [])
81
- queue = T.let(node.child_nodes.compact, T::Array[T.nilable(SyntaxTree::Node)])
82
- closest = node
83
-
84
- until queue.empty?
85
- candidate = queue.shift
86
-
87
- # Skip nil child nodes
88
- next if candidate.nil?
89
-
90
- # Add the next child_nodes to the queue to be processed
91
- queue.concat(candidate.child_nodes)
92
-
93
- # Skip if the current node doesn't cover the desired position
94
- loc = candidate.location
95
- next unless (loc.start_char...loc.end_char).cover?(position)
96
-
97
- # If the node's start character is already past the position, then we should've found the closest node already
98
- break if position < loc.start_char
99
-
100
- # If there are node types to filter by, and the current node is not one of those types, then skip it
101
- next if node_types.any? && node_types.none? { |type| candidate.is_a?(type) }
102
-
103
- # If the current node is narrower than or equal to the previous closest node, then it is more precise
104
- closest_loc = closest.location
105
- if loc.end_char - loc.start_char <= closest_loc.end_char - closest_loc.start_char
106
- parent = T.let(closest, SyntaxTree::Node)
107
- closest = candidate
108
- end
109
- end
110
-
111
- [closest, parent]
112
- end
113
-
114
- sig { params(node: T.nilable(SyntaxTree::Node), range: T.nilable(T::Range[Integer])).returns(T::Boolean) }
115
- def visible?(node, range)
116
- return true if range.nil?
117
- return false if node.nil?
118
-
119
- loc = node.location
120
- range.cover?(loc.start_line - 1) && range.cover?(loc.end_line - 1)
121
- end
122
39
  end
123
40
  end
124
41
  end
@@ -54,7 +54,8 @@ module RubyLsp
54
54
  extracted_source = T.must(@document.source[start_index...end_index])
55
55
 
56
56
  # Find the closest statements node, so that we place the refactor in a valid position
57
- closest_statements = locate(T.must(@document.tree), start_index, node_types: [SyntaxTree::Statements]).first
57
+ closest_statements, parent_statements = @document
58
+ .locate(T.must(@document.tree), start_index, node_types: [SyntaxTree::Statements])
58
59
  return Error::InvalidTargetRange if closest_statements.nil?
59
60
 
60
61
  # Find the node with the end line closest to the requested position, so that we can place the refactor
@@ -64,25 +65,51 @@ module RubyLsp
64
65
  distance <= 0 ? Float::INFINITY : distance
65
66
  end
66
67
 
67
- # When trying to extract the first node inside of a statements block, then we can just select one line above it
68
- target_line = if closest_node == closest_statements.child_nodes.first
69
- closest_node.location.start_line - 1
68
+ # Find the parent expression of the closest node
69
+ parent_expression = parent_statements.child_nodes.compact.find do |node|
70
+ loc = node.location
71
+ loc.start_line - 1 <= source_range.dig(:start, :line) && loc.end_line - 1 >= source_range.dig(:end, :line)
72
+ end if parent_statements
73
+
74
+ closest_node_loc = closest_node.location
75
+ # If the parent expression is a single line block, then we have to extract it inside of the oneline block
76
+ if parent_expression.is_a?(SyntaxTree::MethodAddBlock) &&
77
+ parent_expression.location.start_line == parent_expression.location.end_line
78
+
79
+ variable_source = " #{NEW_VARIABLE_NAME} = #{extracted_source};"
80
+ character = source_range.dig(:start, :character) - 1
81
+ target_range = {
82
+ start: { line: closest_node_loc.end_line - 1, character: character },
83
+ end: { line: closest_node_loc.end_line - 1, character: character },
84
+ }
70
85
  else
71
- closest_node.location.end_line
72
- end
73
-
74
- lines = @document.source.lines
75
- indentation = T.must(T.must(lines[target_line - 1])[/\A */]).size
76
-
77
- target_range = {
78
- start: { line: target_line, character: indentation },
79
- end: { line: target_line, character: indentation },
80
- }
81
-
82
- variable_source = if T.must(lines[target_line]).strip.empty?
83
- "\n#{" " * indentation}#{NEW_VARIABLE_NAME} = #{extracted_source}"
84
- else
85
- "#{NEW_VARIABLE_NAME} = #{extracted_source}\n#{" " * indentation}"
86
+ # If the closest node covers the requested location, then we're extracting a statement nested inside of it. In
87
+ # that case, we want to place the extraction at the start of the closest node (one line above). Otherwise, we
88
+ # want to place the extract right below the closest node
89
+ if closest_node_loc.start_line - 1 <= source_range.dig(
90
+ :start,
91
+ :line,
92
+ ) && closest_node_loc.end_line - 1 >= source_range.dig(:end, :line)
93
+ indentation_line = closest_node_loc.start_line - 1
94
+ target_line = indentation_line
95
+ else
96
+ target_line = closest_node_loc.end_line
97
+ indentation_line = closest_node_loc.end_line - 1
98
+ end
99
+
100
+ lines = @document.source.lines
101
+ indentation = T.must(T.must(lines[indentation_line])[/\A */]).size
102
+
103
+ target_range = {
104
+ start: { line: target_line, character: indentation },
105
+ end: { line: target_line, character: indentation },
106
+ }
107
+
108
+ variable_source = if T.must(lines[target_line]).strip.empty?
109
+ "\n#{" " * indentation}#{NEW_VARIABLE_NAME} = #{extracted_source}"
110
+ else
111
+ "#{NEW_VARIABLE_NAME} = #{extracted_source}\n#{" " * indentation}"
112
+ end
86
113
  end
87
114
 
88
115
  Interface::CodeAction.new(
@@ -21,16 +21,15 @@ module RubyLsp
21
21
 
22
22
  sig do
23
23
  params(
24
- uri: String,
25
24
  document: Document,
26
25
  range: Document::RangeShape,
27
26
  context: T::Hash[Symbol, T.untyped],
28
27
  ).void
29
28
  end
30
- def initialize(uri, document, range, context)
29
+ def initialize(document, range, context)
31
30
  super(document)
32
31
 
33
- @uri = uri
32
+ @uri = T.let(document.uri, String)
34
33
  @range = range
35
34
  @context = context
36
35
  end
@@ -49,7 +48,9 @@ module RubyLsp
49
48
  code_action if diagnostic.dig(:data, :correctable) && cover?(range)
50
49
  end
51
50
 
52
- code_actions << refactor_code_action(@range, @uri)
51
+ # Only add refactor actions if there's a non empty selection in the editor
52
+ code_actions << refactor_code_action(@range, @uri) unless @range.dig(:start) == @range.dig(:end)
53
+ code_actions
53
54
  end
54
55
 
55
56
  private
@@ -0,0 +1,135 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module Requests
6
+ # ![Code lens demo](../../misc/code_lens.gif)
7
+ #
8
+ # This feature is currently experimental. Clients will need to pass `experimentalFeaturesEnabled`
9
+ # in the initialization options to enable it.
10
+ #
11
+ # The
12
+ # [code lens](https://microsoft.github.io/language-server-protocol/specification#textDocument_codeLens)
13
+ # request informs the editor of runnable commands such as tests
14
+ #
15
+ # # Example
16
+ #
17
+ # ```ruby
18
+ # # Run
19
+ # class Test < Minitest::Test
20
+ # end
21
+ # ```
22
+
23
+ class CodeLens < BaseRequest
24
+ BASE_COMMAND = T.let((File.exist?("Gemfile.lock") ? "bundle exec ruby" : "ruby") + " -Itest ", String)
25
+ ACCESS_MODIFIERS = T.let(["public", "private", "protected"], T::Array[String])
26
+
27
+ sig do
28
+ params(
29
+ document: Document,
30
+ ).void
31
+ end
32
+ def initialize(document)
33
+ super(document)
34
+ @results = T.let([], T::Array[Interface::CodeLens])
35
+ @path = T.let(document.uri.delete_prefix("file://"), String)
36
+ @modifier = T.let("public", String)
37
+ end
38
+
39
+ sig { override.returns(T.all(T::Array[Interface::CodeLens], Object)) }
40
+ def run
41
+ visit(@document.tree) if @document.parsed?
42
+ @results
43
+ end
44
+
45
+ sig { override.params(node: SyntaxTree::ClassDeclaration).void }
46
+ def visit_class(node)
47
+ class_name = node.constant.constant.value
48
+ if class_name.end_with?("Test")
49
+ add_code_lens(node, name: class_name, command: BASE_COMMAND + @path)
50
+ end
51
+ visit(node.bodystmt)
52
+ end
53
+
54
+ sig { override.params(node: SyntaxTree::DefNode).void }
55
+ def visit_def(node)
56
+ if @modifier == "public"
57
+ method_name = node.name.value
58
+ if method_name.start_with?("test_")
59
+ add_code_lens(
60
+ node,
61
+ name: method_name,
62
+ command: BASE_COMMAND + @path + " --name " + method_name,
63
+ )
64
+ end
65
+ end
66
+ end
67
+
68
+ sig { override.params(node: SyntaxTree::Command).void }
69
+ def visit_command(node)
70
+ if node.message.value == "public"
71
+ with_visiblity("public", node)
72
+ end
73
+ end
74
+
75
+ sig { override.params(node: SyntaxTree::CallNode).void }
76
+ def visit_call(node)
77
+ ident = node.message if node.message.is_a?(SyntaxTree::Ident)
78
+
79
+ if ident
80
+ if T.cast(ident, SyntaxTree::Ident).value == "public"
81
+ with_visiblity("public", node)
82
+ end
83
+ end
84
+ end
85
+
86
+ sig { override.params(node: SyntaxTree::VCall).void }
87
+ def visit_vcall(node)
88
+ vcall_value = node.value.value
89
+
90
+ if ACCESS_MODIFIERS.include?(vcall_value)
91
+ @modifier = vcall_value
92
+ end
93
+ end
94
+
95
+ private
96
+
97
+ sig do
98
+ params(
99
+ visibility: String,
100
+ node: T.any(SyntaxTree::CallNode, SyntaxTree::Command),
101
+ ).void
102
+ end
103
+ def with_visiblity(visibility, node)
104
+ current_visibility = @modifier
105
+ @modifier = visibility
106
+ visit(node.arguments)
107
+ ensure
108
+ @modifier = T.must(current_visibility)
109
+ end
110
+
111
+ sig { params(node: SyntaxTree::Node, name: String, command: String).void }
112
+ def add_code_lens(node, name:, command:)
113
+ @results << Interface::CodeLens.new(
114
+ range: range_from_syntax_tree_node(node),
115
+ command: Interface::Command.new(
116
+ title: "Run",
117
+ command: "rubyLsp.runTest",
118
+ arguments: [
119
+ @path,
120
+ name,
121
+ command,
122
+ {
123
+ start_line: node.location.start_line - 1,
124
+ start_column: node.location.start_column,
125
+ end_line: node.location.end_line - 1,
126
+ end_column: node.location.end_column,
127
+ },
128
+ ],
129
+ ),
130
+ data: { type: "test" },
131
+ )
132
+ end
133
+ end
134
+ end
135
+ end
@@ -21,11 +21,11 @@ module RubyLsp
21
21
  class Diagnostics < BaseRequest
22
22
  extend T::Sig
23
23
 
24
- sig { params(uri: String, document: Document).void }
25
- def initialize(uri, document)
24
+ sig { params(document: Document).void }
25
+ def initialize(document)
26
26
  super(document)
27
27
 
28
- @uri = uri
28
+ @uri = T.let(document.uri, String)
29
29
  end
30
30
 
31
31
  sig { override.returns(T.nilable(T.all(T::Array[Support::RuboCopDiagnostic], Object))) }
@@ -29,14 +29,13 @@ module RubyLsp
29
29
  def initialize(document, position)
30
30
  super(document)
31
31
 
32
- @highlights = T.let([], T::Array[LanguageServer::Protocol::Interface::DocumentHighlight])
32
+ @highlights = T.let([], T::Array[Interface::DocumentHighlight])
33
33
  return unless document.parsed?
34
34
 
35
- position = document.create_scanner.find_char_position(position)
36
- @target = T.let(find(T.must(document.tree), position), T.nilable(Support::HighlightTarget))
35
+ @target = T.let(find(position), T.nilable(Support::HighlightTarget))
37
36
  end
38
37
 
39
- sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::DocumentHighlight], Object)) }
38
+ sig { override.returns(T.all(T::Array[Interface::DocumentHighlight], Object)) }
40
39
  def run
41
40
  # no @target means the target is not highlightable
42
41
  visit(@document.tree) if @document.parsed? && @target
@@ -68,12 +67,11 @@ module RubyLsp
68
67
 
69
68
  sig do
70
69
  params(
71
- node: SyntaxTree::Node,
72
- position: Integer,
70
+ position: Document::PositionShape,
73
71
  ).returns(T.nilable(Support::HighlightTarget))
74
72
  end
75
- def find(node, position)
76
- matched, parent = locate(node, position)
73
+ def find(position)
74
+ matched, parent = @document.locate_node(position)
77
75
 
78
76
  return unless matched && parent
79
77
  return unless matched.is_a?(SyntaxTree::Ident) || DIRECT_HIGHLIGHTS.include?(matched.class)
@@ -90,7 +88,7 @@ module RubyLsp
90
88
  sig { params(match: Support::HighlightTarget::HighlightMatch).void }
91
89
  def add_highlight(match)
92
90
  range = range_from_syntax_tree_node(match.node)
93
- @highlights << LanguageServer::Protocol::Interface::DocumentHighlight.new(range: range, kind: match.type)
91
+ @highlights << Interface::DocumentHighlight.new(range: range, kind: match.type)
94
92
  end
95
93
  end
96
94
  end
@@ -69,18 +69,18 @@ module RubyLsp
69
69
  end
70
70
  end
71
71
 
72
- sig { params(uri: String, document: Document).void }
73
- def initialize(uri, document)
72
+ sig { params(document: Document).void }
73
+ def initialize(document)
74
74
  super(document)
75
75
 
76
76
  # Match the version based on the version in the RBI file name. Notice that the `@` symbol is sanitized to `%40`
77
77
  # in the URI
78
- version_match = /(?<=%40)[\d.]+(?=\.rbi$)/.match(uri)
78
+ version_match = /(?<=%40)[\d.]+(?=\.rbi$)/.match(document.uri)
79
79
  @gem_version = T.let(version_match && version_match[0], T.nilable(String))
80
- @links = T.let([], T::Array[LanguageServer::Protocol::Interface::DocumentLink])
80
+ @links = T.let([], T::Array[Interface::DocumentLink])
81
81
  end
82
82
 
83
- sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::DocumentLink], Object)) }
83
+ sig { override.returns(T.all(T::Array[Interface::DocumentLink], Object)) }
84
84
  def run
85
85
  visit(@document.tree) if @document.parsed?
86
86
  @links
@@ -91,12 +91,12 @@ module RubyLsp
91
91
  match = node.value.match(%r{source://.*#\d+$})
92
92
  return unless match
93
93
 
94
- uri = T.cast(URI(match[0]), URI::Source)
94
+ uri = T.cast(URI(T.must(match[0])), URI::Source)
95
95
  gem_version = T.must(resolve_version(uri))
96
96
  file_path = self.class.gem_paths.dig(uri.gem_name, gem_version, uri.path)
97
97
  return if file_path.nil?
98
98
 
99
- @links << LanguageServer::Protocol::Interface::DocumentLink.new(
99
+ @links << Interface::DocumentLink.new(
100
100
  range: range_from_syntax_tree_node(node),
101
101
  target: "file://#{file_path}##{uri.line_number}",
102
102
  tooltip: "Jump to #{file_path}##{uri.line_number}",
@@ -66,12 +66,12 @@ module RubyLsp
66
66
  class SymbolHierarchyRoot
67
67
  extend T::Sig
68
68
 
69
- sig { returns(T::Array[LanguageServer::Protocol::Interface::DocumentSymbol]) }
69
+ sig { returns(T::Array[Interface::DocumentSymbol]) }
70
70
  attr_reader :children
71
71
 
72
72
  sig { void }
73
73
  def initialize
74
- @children = T.let([], T::Array[LanguageServer::Protocol::Interface::DocumentSymbol])
74
+ @children = T.let([], T::Array[Interface::DocumentSymbol])
75
75
  end
76
76
  end
77
77
 
@@ -82,11 +82,11 @@ module RubyLsp
82
82
  @root = T.let(SymbolHierarchyRoot.new, SymbolHierarchyRoot)
83
83
  @stack = T.let(
84
84
  [@root],
85
- T::Array[T.any(SymbolHierarchyRoot, LanguageServer::Protocol::Interface::DocumentSymbol)],
85
+ T::Array[T.any(SymbolHierarchyRoot, Interface::DocumentSymbol)],
86
86
  )
87
87
  end
88
88
 
89
- sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::DocumentSymbol], Object)) }
89
+ sig { override.returns(T.all(T::Array[Interface::DocumentSymbol], Object)) }
90
90
  def run
91
91
  visit(@document.tree) if @document.parsed?
92
92
  @root.children
@@ -134,7 +134,9 @@ module RubyLsp
134
134
 
135
135
  sig { override.params(node: SyntaxTree::DefNode).void }
136
136
  def visit_def(node)
137
- if node.target&.value&.value == "self"
137
+ target = node.target
138
+
139
+ if target.is_a?(SyntaxTree::VarRef) && target.value.is_a?(SyntaxTree::Kw) && target.value.value == "self"
138
140
  name = "self.#{node.name.value}"
139
141
  kind = :method
140
142
  else
@@ -180,7 +182,8 @@ module RubyLsp
180
182
 
181
183
  sig { override.params(node: SyntaxTree::VarField).void }
182
184
  def visit_var_field(node)
183
- kind = case node.value
185
+ value = node.value
186
+ kind = case value
184
187
  when SyntaxTree::Const
185
188
  :constant
186
189
  when SyntaxTree::CVar, SyntaxTree::IVar
@@ -190,10 +193,10 @@ module RubyLsp
190
193
  end
191
194
 
192
195
  create_document_symbol(
193
- name: node.value.value,
196
+ name: value.value,
194
197
  kind: kind,
195
198
  range_node: node,
196
- selection_range_node: node.value,
199
+ selection_range_node: value,
197
200
  )
198
201
  end
199
202
 
@@ -205,10 +208,10 @@ module RubyLsp
205
208
  kind: Symbol,
206
209
  range_node: SyntaxTree::Node,
207
210
  selection_range_node: SyntaxTree::Node,
208
- ).returns(LanguageServer::Protocol::Interface::DocumentSymbol)
211
+ ).returns(Interface::DocumentSymbol)
209
212
  end
210
213
  def create_document_symbol(name:, kind:, range_node:, selection_range_node:)
211
- symbol = LanguageServer::Protocol::Interface::DocumentSymbol.new(
214
+ symbol = Interface::DocumentSymbol.new(
212
215
  name: name,
213
216
  kind: SYMBOL_KIND[kind],
214
217
  range: range_from_syntax_tree_node(range_node),
@@ -63,11 +63,11 @@ module RubyLsp
63
63
  def initialize(document)
64
64
  super
65
65
 
66
- @ranges = T.let([], T::Array[LanguageServer::Protocol::Interface::FoldingRange])
66
+ @ranges = T.let([], T::Array[Interface::FoldingRange])
67
67
  @partial_range = T.let(nil, T.nilable(PartialRange))
68
68
  end
69
69
 
70
- sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::FoldingRange], Object)) }
70
+ sig { override.returns(T.all(T::Array[Interface::FoldingRange], Object)) }
71
71
  def run
72
72
  if @document.parsed?
73
73
  visit(@document.tree)
@@ -161,9 +161,9 @@ module RubyLsp
161
161
  node.is_a?(SyntaxTree::Comment) && @end_line + 1 != node.location.start_line - 1
162
162
  end
163
163
 
164
- sig { returns(LanguageServer::Protocol::Interface::FoldingRange) }
164
+ sig { returns(Interface::FoldingRange) }
165
165
  def to_range
166
- LanguageServer::Protocol::Interface::FoldingRange.new(
166
+ Interface::FoldingRange.new(
167
167
  start_line: @start_line,
168
168
  end_line: @end_line,
169
169
  kind: @kind,
@@ -220,7 +220,8 @@ module RubyLsp
220
220
 
221
221
  sig { params(node: T.any(SyntaxTree::CallNode, SyntaxTree::CommandCall)).void }
222
222
  def add_call_range(node)
223
- receiver = T.let(node.receiver, SyntaxTree::Node)
223
+ receiver = T.let(node.receiver, T.nilable(SyntaxTree::Node))
224
+
224
225
  loop do
225
226
  case receiver
226
227
  when SyntaxTree::CallNode
@@ -255,9 +256,10 @@ module RubyLsp
255
256
  def add_def_range(node)
256
257
  # For an endless method with no arguments, `node.params` returns `nil` for Ruby 3.0, but a `Syntax::Params`
257
258
  # for Ruby 3.1
258
- return unless node.params
259
+ params = node.params
260
+ return unless params
259
261
 
260
- params_location = node.params.location
262
+ params_location = params.location
261
263
 
262
264
  if params_location.start_line < params_location.end_line
263
265
  add_lines_range(params_location.end_line, node.location.end_line - 1)
@@ -276,7 +278,9 @@ module RubyLsp
276
278
 
277
279
  sig { params(node: SyntaxTree::Node, statements: SyntaxTree::Statements).void }
278
280
  def add_statements_range(node, statements)
279
- add_lines_range(node.location.start_line, statements.body.last.location.end_line) unless statements.empty?
281
+ return if statements.empty?
282
+
283
+ add_lines_range(node.location.start_line, T.must(statements.body.last).location.end_line)
280
284
  end
281
285
 
282
286
  sig { params(node: SyntaxTree::StringConcat).void }
@@ -291,7 +295,7 @@ module RubyLsp
291
295
  def add_lines_range(start_line, end_line)
292
296
  return if start_line >= end_line
293
297
 
294
- @ranges << LanguageServer::Protocol::Interface::FoldingRange.new(
298
+ @ranges << Interface::FoldingRange.new(
295
299
  start_line: start_line - 1,
296
300
  end_line: end_line - 1,
297
301
  kind: "region",
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "ruby_lsp/requests/support/rubocop_formatting_runner"
5
+ require "ruby_lsp/requests/support/syntax_tree_formatting_runner"
5
6
 
6
7
  module RubyLsp
7
8
  module Requests
@@ -26,11 +27,11 @@ module RubyLsp
26
27
 
27
28
  extend T::Sig
28
29
 
29
- sig { params(uri: String, document: Document, formatter: String).void }
30
- def initialize(uri, document, formatter: "auto")
30
+ sig { params(document: Document, formatter: String).void }
31
+ def initialize(document, formatter: "auto")
31
32
  super(document)
32
33
 
33
- @uri = uri
34
+ @uri = T.let(document.uri, String)
34
35
  @formatter = T.let(
35
36
  if formatter == "auto"
36
37
  defined?(Support::RuboCopFormattingRunner) ? "rubocop" : "syntax_tree"
@@ -73,7 +74,7 @@ module RubyLsp
73
74
  when "rubocop"
74
75
  Support::RuboCopFormattingRunner.instance.run(@uri, @document)
75
76
  when "syntax_tree"
76
- SyntaxTree.format(@document.source)
77
+ Support::SyntaxTreeFormattingRunner.instance.run(@uri, @document)
77
78
  else
78
79
  raise InvalidFormatter, "Unknown formatter: #{@formatter}"
79
80
  end