ruby-lsp 0.4.2 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +29 -35
- data/VERSION +1 -1
- data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +62 -0
- data/lib/ruby_lsp/document.rb +74 -6
- data/lib/ruby_lsp/event_emitter.rb +52 -0
- data/lib/ruby_lsp/executor.rb +60 -14
- data/lib/ruby_lsp/internal.rb +2 -0
- data/lib/ruby_lsp/listener.rb +39 -0
- data/lib/ruby_lsp/requests/base_request.rb +2 -85
- data/lib/ruby_lsp/requests/code_action_resolve.rb +46 -19
- data/lib/ruby_lsp/requests/code_actions.rb +5 -4
- data/lib/ruby_lsp/requests/code_lens.rb +135 -0
- data/lib/ruby_lsp/requests/diagnostics.rb +3 -3
- data/lib/ruby_lsp/requests/document_highlight.rb +7 -9
- data/lib/ruby_lsp/requests/document_link.rb +7 -7
- data/lib/ruby_lsp/requests/document_symbol.rb +13 -10
- data/lib/ruby_lsp/requests/folding_ranges.rb +13 -9
- data/lib/ruby_lsp/requests/formatting.rb +5 -4
- data/lib/ruby_lsp/requests/hover.rb +28 -32
- data/lib/ruby_lsp/requests/inlay_hints.rb +5 -4
- data/lib/ruby_lsp/requests/path_completion.rb +16 -10
- data/lib/ruby_lsp/requests/selection_ranges.rb +3 -3
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +29 -15
- data/lib/ruby_lsp/requests/support/common.rb +55 -0
- data/lib/ruby_lsp/requests/support/highlight_target.rb +5 -4
- data/lib/ruby_lsp/requests/support/rails_document_client.rb +7 -6
- data/lib/ruby_lsp/requests/support/selection_range.rb +1 -1
- data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +2 -2
- data/lib/ruby_lsp/requests/support/sorbet.rb +5 -15
- data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +39 -0
- data/lib/ruby_lsp/requests.rb +3 -0
- data/lib/ruby_lsp/server.rb +4 -1
- data/lib/ruby_lsp/store.rb +10 -10
- 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 =
|
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
|
-
#
|
68
|
-
|
69
|
-
|
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
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
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(
|
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
|
-
|
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
|
+
# 
|
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(
|
25
|
-
def initialize(
|
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[
|
32
|
+
@highlights = T.let([], T::Array[Interface::DocumentHighlight])
|
33
33
|
return unless document.parsed?
|
34
34
|
|
35
|
-
|
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[
|
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
|
-
|
72
|
-
position: Integer,
|
70
|
+
position: Document::PositionShape,
|
73
71
|
).returns(T.nilable(Support::HighlightTarget))
|
74
72
|
end
|
75
|
-
def find(
|
76
|
-
matched, parent =
|
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 <<
|
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(
|
73
|
-
def initialize(
|
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[
|
80
|
+
@links = T.let([], T::Array[Interface::DocumentLink])
|
81
81
|
end
|
82
82
|
|
83
|
-
sig { override.returns(T.all(T::Array[
|
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 <<
|
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[
|
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[
|
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,
|
85
|
+
T::Array[T.any(SymbolHierarchyRoot, Interface::DocumentSymbol)],
|
86
86
|
)
|
87
87
|
end
|
88
88
|
|
89
|
-
sig { override.returns(T.all(T::Array[
|
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
|
-
|
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
|
-
|
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:
|
196
|
+
name: value.value,
|
194
197
|
kind: kind,
|
195
198
|
range_node: node,
|
196
|
-
selection_range_node:
|
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(
|
211
|
+
).returns(Interface::DocumentSymbol)
|
209
212
|
end
|
210
213
|
def create_document_symbol(name:, kind:, range_node:, selection_range_node:)
|
211
|
-
symbol =
|
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[
|
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[
|
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(
|
164
|
+
sig { returns(Interface::FoldingRange) }
|
165
165
|
def to_range
|
166
|
-
|
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
|
-
|
259
|
+
params = node.params
|
260
|
+
return unless params
|
259
261
|
|
260
|
-
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
|
-
|
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 <<
|
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(
|
30
|
-
def initialize(
|
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
|
-
|
77
|
+
Support::SyntaxTreeFormattingRunner.instance.run(@uri, @document)
|
77
78
|
else
|
78
79
|
raise InvalidFormatter, "Unknown formatter: #{@formatter}"
|
79
80
|
end
|