ruby-lsp 0.4.2 → 0.5.0
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 -116
- data/VERSION +1 -1
- data/exe/ruby-lsp +10 -1
- data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +62 -0
- data/lib/ruby_lsp/check_docs.rb +112 -0
- data/lib/ruby_lsp/document.rb +87 -8
- data/lib/ruby_lsp/event_emitter.rb +120 -0
- data/lib/ruby_lsp/executor.rb +191 -44
- data/lib/ruby_lsp/extension.rb +104 -0
- data/lib/ruby_lsp/internal.rb +4 -0
- data/lib/ruby_lsp/listener.rb +42 -0
- data/lib/ruby_lsp/requests/base_request.rb +2 -90
- data/lib/ruby_lsp/requests/code_action_resolve.rb +47 -20
- data/lib/ruby_lsp/requests/code_actions.rb +6 -5
- data/lib/ruby_lsp/requests/code_lens.rb +151 -0
- data/lib/ruby_lsp/requests/diagnostics.rb +5 -5
- data/lib/ruby_lsp/requests/document_highlight.rb +8 -10
- data/lib/ruby_lsp/requests/document_link.rb +17 -15
- data/lib/ruby_lsp/requests/document_symbol.rb +63 -40
- data/lib/ruby_lsp/requests/folding_ranges.rb +14 -10
- data/lib/ruby_lsp/requests/formatting.rb +15 -15
- data/lib/ruby_lsp/requests/hover.rb +45 -34
- data/lib/ruby_lsp/requests/inlay_hints.rb +6 -5
- data/lib/ruby_lsp/requests/on_type_formatting.rb +5 -1
- data/lib/ruby_lsp/requests/path_completion.rb +21 -51
- data/lib/ruby_lsp/requests/selection_ranges.rb +4 -4
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +30 -16
- data/lib/ruby_lsp/requests/support/common.rb +91 -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/rubocop_diagnostics_runner.rb +0 -1
- data/lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb +0 -1
- 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 +42 -0
- data/lib/ruby_lsp/requests.rb +17 -14
- data/lib/ruby_lsp/server.rb +45 -9
- data/lib/ruby_lsp/store.rb +11 -11
- data/lib/ruby_lsp/utils.rb +9 -8
- metadata +13 -5
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
module RubyLsp
|
5
5
|
module Requests
|
6
|
-
# 
|
7
7
|
#
|
8
8
|
# The [code action resolve](https://microsoft.github.io/language-server-protocol/specification#codeAction_resolve)
|
9
9
|
# request is used to to resolve the edit field for a given code action, if it is not already provided in the
|
@@ -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(
|
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
module RubyLsp
|
5
5
|
module Requests
|
6
|
-
# 
|
7
7
|
#
|
8
8
|
# The [code actions](https://microsoft.github.io/language-server-protocol/specification#textDocument_codeAction)
|
9
9
|
# request informs the editor of RuboCop quick fixes that can be applied. These are accessible by hovering over a
|
@@ -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,151 @@
|
|
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
|
+
class CodeLens < Listener
|
23
|
+
extend T::Sig
|
24
|
+
extend T::Generic
|
25
|
+
|
26
|
+
ResponseType = type_member { { fixed: T::Array[Interface::CodeLens] } }
|
27
|
+
|
28
|
+
BASE_COMMAND = T.let((File.exist?("Gemfile.lock") ? "bundle exec ruby" : "ruby") + " -Itest ", String)
|
29
|
+
ACCESS_MODIFIERS = T.let(["public", "private", "protected"], T::Array[String])
|
30
|
+
|
31
|
+
sig { override.returns(ResponseType) }
|
32
|
+
attr_reader :response
|
33
|
+
|
34
|
+
sig { params(uri: String, emitter: EventEmitter, message_queue: Thread::Queue).void }
|
35
|
+
def initialize(uri, emitter, message_queue)
|
36
|
+
super(emitter, message_queue)
|
37
|
+
|
38
|
+
@response = T.let([], ResponseType)
|
39
|
+
@path = T.let(T.must(URI(uri).path), String)
|
40
|
+
@visibility = T.let("public", String)
|
41
|
+
@prev_visibility = T.let("public", String)
|
42
|
+
|
43
|
+
emitter.register(self, :on_class, :on_def, :on_command, :after_command, :on_call, :after_call, :on_vcall)
|
44
|
+
end
|
45
|
+
|
46
|
+
sig { params(node: SyntaxTree::ClassDeclaration).void }
|
47
|
+
def on_class(node)
|
48
|
+
class_name = node.constant.constant.value
|
49
|
+
if class_name.end_with?("Test")
|
50
|
+
add_code_lens(node, name: class_name, command: BASE_COMMAND + @path)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
sig { params(node: SyntaxTree::DefNode).void }
|
55
|
+
def on_def(node)
|
56
|
+
if @visibility == "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 { params(node: SyntaxTree::Command).void }
|
69
|
+
def on_command(node)
|
70
|
+
if ACCESS_MODIFIERS.include?(node.message.value) && node.arguments.parts.any?
|
71
|
+
@prev_visibility = @visibility
|
72
|
+
@visibility = node.message.value
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
sig { params(node: SyntaxTree::Command).void }
|
77
|
+
def after_command(node)
|
78
|
+
@visibility = @prev_visibility
|
79
|
+
end
|
80
|
+
|
81
|
+
sig { params(node: SyntaxTree::CallNode).void }
|
82
|
+
def on_call(node)
|
83
|
+
ident = node.message if node.message.is_a?(SyntaxTree::Ident)
|
84
|
+
|
85
|
+
if ident
|
86
|
+
ident_value = T.cast(ident, SyntaxTree::Ident).value
|
87
|
+
if ACCESS_MODIFIERS.include?(ident_value)
|
88
|
+
@prev_visibility = @visibility
|
89
|
+
@visibility = ident_value
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
sig { params(node: SyntaxTree::CallNode).void }
|
95
|
+
def after_call(node)
|
96
|
+
@visibility = @prev_visibility
|
97
|
+
end
|
98
|
+
|
99
|
+
sig { params(node: SyntaxTree::VCall).void }
|
100
|
+
def on_vcall(node)
|
101
|
+
vcall_value = node.value.value
|
102
|
+
|
103
|
+
if ACCESS_MODIFIERS.include?(vcall_value)
|
104
|
+
@prev_visibility = vcall_value
|
105
|
+
@visibility = vcall_value
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
sig { params(other: Listener[ResponseType]).returns(T.self_type) }
|
110
|
+
def merge_response!(other)
|
111
|
+
@response.concat(other.response)
|
112
|
+
self
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
sig { params(node: SyntaxTree::Node, name: String, command: String).void }
|
118
|
+
def add_code_lens(node, name:, command:)
|
119
|
+
@response << create_code_lens(
|
120
|
+
node,
|
121
|
+
title: "Run",
|
122
|
+
command_name: "rubyLsp.runTest",
|
123
|
+
path: @path,
|
124
|
+
name: name,
|
125
|
+
test_command: command,
|
126
|
+
type: "test",
|
127
|
+
)
|
128
|
+
|
129
|
+
@response << create_code_lens(
|
130
|
+
node,
|
131
|
+
title: "Run In Terminal",
|
132
|
+
command_name: "rubyLsp.runTestInTerminal",
|
133
|
+
path: @path,
|
134
|
+
name: name,
|
135
|
+
test_command: command,
|
136
|
+
type: "test_in_terminal",
|
137
|
+
)
|
138
|
+
|
139
|
+
@response << create_code_lens(
|
140
|
+
node,
|
141
|
+
title: "Debug",
|
142
|
+
command_name: "rubyLsp.debugTest",
|
143
|
+
path: @path,
|
144
|
+
name: name,
|
145
|
+
test_command: command,
|
146
|
+
type: "debug",
|
147
|
+
)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -5,7 +5,7 @@ require "ruby_lsp/requests/support/rubocop_diagnostics_runner"
|
|
5
5
|
|
6
6
|
module RubyLsp
|
7
7
|
module Requests
|
8
|
-
# 
|
9
9
|
#
|
10
10
|
# The
|
11
11
|
# [diagnostics](https://microsoft.github.io/language-server-protocol/specification#textDocument_publishDiagnostics)
|
@@ -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))) }
|
@@ -34,7 +34,7 @@ module RubyLsp
|
|
34
34
|
return unless defined?(Support::RuboCopDiagnosticsRunner)
|
35
35
|
|
36
36
|
# Don't try to run RuboCop diagnostics for files outside the current working directory
|
37
|
-
return unless @uri.start_with?(WORKSPACE_URI)
|
37
|
+
return unless URI(@uri).path&.start_with?(T.must(WORKSPACE_URI.path))
|
38
38
|
|
39
39
|
Support::RuboCopDiagnosticsRunner.instance.run(@uri, @document)
|
40
40
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
module RubyLsp
|
5
5
|
module Requests
|
6
|
-
# 
|
7
7
|
#
|
8
8
|
# The [document highlight](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentHighlight)
|
9
9
|
# informs the editor all relevant elements of the currently pointed item for highlighting. For example, when
|
@@ -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
|
@@ -5,7 +5,7 @@ require "ruby_lsp/requests/support/source_uri"
|
|
5
5
|
|
6
6
|
module RubyLsp
|
7
7
|
module Requests
|
8
|
-
# 
|
9
9
|
#
|
10
10
|
# The [document link](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentLink)
|
11
11
|
# makes `# source://PATH_TO_FILE#line` comments in a Ruby/RBI file clickable if the file exists.
|
@@ -18,8 +18,11 @@ module RubyLsp
|
|
18
18
|
# def format(source, maxwidth = T.unsafe(nil))
|
19
19
|
# end
|
20
20
|
# ```
|
21
|
-
class DocumentLink <
|
21
|
+
class DocumentLink < Listener
|
22
22
|
extend T::Sig
|
23
|
+
extend T::Generic
|
24
|
+
|
25
|
+
ResponseType = type_member { { fixed: T::Array[Interface::DocumentLink] } }
|
23
26
|
|
24
27
|
GEM_TO_VERSION_MAP = T.let(
|
25
28
|
[*::Gem::Specification.default_stubs, *::Gem::Specification.stubs].map! do |s|
|
@@ -69,34 +72,33 @@ module RubyLsp
|
|
69
72
|
end
|
70
73
|
end
|
71
74
|
|
72
|
-
sig {
|
73
|
-
|
74
|
-
|
75
|
+
sig { override.returns(ResponseType) }
|
76
|
+
attr_reader :response
|
77
|
+
|
78
|
+
sig { params(uri: String, emitter: EventEmitter, message_queue: Thread::Queue).void }
|
79
|
+
def initialize(uri, emitter, message_queue)
|
80
|
+
super(emitter, message_queue)
|
75
81
|
|
76
82
|
# Match the version based on the version in the RBI file name. Notice that the `@` symbol is sanitized to `%40`
|
77
83
|
# in the URI
|
78
84
|
version_match = /(?<=%40)[\d.]+(?=\.rbi$)/.match(uri)
|
79
85
|
@gem_version = T.let(version_match && version_match[0], T.nilable(String))
|
80
|
-
@
|
81
|
-
end
|
86
|
+
@response = T.let([], T::Array[Interface::DocumentLink])
|
82
87
|
|
83
|
-
|
84
|
-
def run
|
85
|
-
visit(@document.tree) if @document.parsed?
|
86
|
-
@links
|
88
|
+
emitter.register(self, :on_comment)
|
87
89
|
end
|
88
90
|
|
89
|
-
sig {
|
90
|
-
def
|
91
|
+
sig { params(node: SyntaxTree::Comment).void }
|
92
|
+
def on_comment(node)
|
91
93
|
match = node.value.match(%r{source://.*#\d+$})
|
92
94
|
return unless match
|
93
95
|
|
94
|
-
uri = T.cast(URI(match[0]), URI::Source)
|
96
|
+
uri = T.cast(URI(T.must(match[0])), URI::Source)
|
95
97
|
gem_version = T.must(resolve_version(uri))
|
96
98
|
file_path = self.class.gem_paths.dig(uri.gem_name, gem_version, uri.path)
|
97
99
|
return if file_path.nil?
|
98
100
|
|
99
|
-
@
|
101
|
+
@response << Interface::DocumentLink.new(
|
100
102
|
range: range_from_syntax_tree_node(node),
|
101
103
|
target: "file://#{file_path}##{uri.line_number}",
|
102
104
|
tooltip: "Jump to #{file_path}##{uri.line_number}",
|
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
module RubyLsp
|
5
5
|
module Requests
|
6
|
-
# 
|
7
7
|
#
|
8
8
|
# The [document
|
9
9
|
# symbol](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentSymbol) request
|
@@ -26,8 +26,11 @@ module RubyLsp
|
|
26
26
|
# end
|
27
27
|
# end
|
28
28
|
# ```
|
29
|
-
class DocumentSymbol <
|
29
|
+
class DocumentSymbol < Listener
|
30
30
|
extend T::Sig
|
31
|
+
extend T::Generic
|
32
|
+
|
33
|
+
ResponseType = type_member { { fixed: T::Array[Interface::DocumentSymbol] } }
|
31
34
|
|
32
35
|
SYMBOL_KIND = T.let(
|
33
36
|
{
|
@@ -66,49 +69,62 @@ module RubyLsp
|
|
66
69
|
class SymbolHierarchyRoot
|
67
70
|
extend T::Sig
|
68
71
|
|
69
|
-
sig { returns(T::Array[
|
72
|
+
sig { returns(T::Array[Interface::DocumentSymbol]) }
|
70
73
|
attr_reader :children
|
71
74
|
|
72
75
|
sig { void }
|
73
76
|
def initialize
|
74
|
-
@children = T.let([], T::Array[
|
77
|
+
@children = T.let([], T::Array[Interface::DocumentSymbol])
|
75
78
|
end
|
76
79
|
end
|
77
80
|
|
78
|
-
sig {
|
79
|
-
|
81
|
+
sig { override.returns(T::Array[Interface::DocumentSymbol]) }
|
82
|
+
attr_reader :response
|
83
|
+
|
84
|
+
sig { params(emitter: EventEmitter, message_queue: Thread::Queue).void }
|
85
|
+
def initialize(emitter, message_queue)
|
80
86
|
super
|
81
87
|
|
82
88
|
@root = T.let(SymbolHierarchyRoot.new, SymbolHierarchyRoot)
|
89
|
+
@response = T.let(@root.children, T::Array[Interface::DocumentSymbol])
|
83
90
|
@stack = T.let(
|
84
91
|
[@root],
|
85
|
-
T::Array[T.any(SymbolHierarchyRoot,
|
92
|
+
T::Array[T.any(SymbolHierarchyRoot, Interface::DocumentSymbol)],
|
86
93
|
)
|
87
|
-
end
|
88
94
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
95
|
+
emitter.register(
|
96
|
+
self,
|
97
|
+
:on_class,
|
98
|
+
:after_class,
|
99
|
+
:on_command,
|
100
|
+
:on_const_path_field,
|
101
|
+
:on_def,
|
102
|
+
:after_def,
|
103
|
+
:on_module,
|
104
|
+
:after_module,
|
105
|
+
:on_top_const_field,
|
106
|
+
:on_var_field,
|
107
|
+
)
|
93
108
|
end
|
94
109
|
|
95
|
-
sig {
|
96
|
-
def
|
97
|
-
|
110
|
+
sig { params(node: SyntaxTree::ClassDeclaration).void }
|
111
|
+
def on_class(node)
|
112
|
+
@stack << create_document_symbol(
|
98
113
|
name: full_constant_name(node.constant),
|
99
114
|
kind: :class,
|
100
115
|
range_node: node,
|
101
116
|
selection_range_node: node.constant,
|
102
117
|
)
|
118
|
+
end
|
103
119
|
|
104
|
-
|
105
|
-
|
120
|
+
sig { params(node: SyntaxTree::ClassDeclaration).void }
|
121
|
+
def after_class(node)
|
106
122
|
@stack.pop
|
107
123
|
end
|
108
124
|
|
109
|
-
sig {
|
110
|
-
def
|
111
|
-
return
|
125
|
+
sig { params(node: SyntaxTree::Command).void }
|
126
|
+
def on_command(node)
|
127
|
+
return unless ATTR_ACCESSORS.include?(node.message.value)
|
112
128
|
|
113
129
|
node.arguments.parts.each do |argument|
|
114
130
|
next unless argument.is_a?(SyntaxTree::SymbolLiteral)
|
@@ -122,8 +138,8 @@ module RubyLsp
|
|
122
138
|
end
|
123
139
|
end
|
124
140
|
|
125
|
-
sig {
|
126
|
-
def
|
141
|
+
sig { params(node: SyntaxTree::ConstPathField).void }
|
142
|
+
def on_const_path_field(node)
|
127
143
|
create_document_symbol(
|
128
144
|
name: node.constant.value,
|
129
145
|
kind: :constant,
|
@@ -132,9 +148,11 @@ module RubyLsp
|
|
132
148
|
)
|
133
149
|
end
|
134
150
|
|
135
|
-
sig {
|
136
|
-
def
|
137
|
-
|
151
|
+
sig { params(node: SyntaxTree::DefNode).void }
|
152
|
+
def on_def(node)
|
153
|
+
target = node.target
|
154
|
+
|
155
|
+
if target.is_a?(SyntaxTree::VarRef) && target.value.is_a?(SyntaxTree::Kw) && target.value.value == "self"
|
138
156
|
name = "self.#{node.name.value}"
|
139
157
|
kind = :method
|
140
158
|
else
|
@@ -150,26 +168,30 @@ module RubyLsp
|
|
150
168
|
)
|
151
169
|
|
152
170
|
@stack << symbol
|
153
|
-
|
171
|
+
end
|
172
|
+
|
173
|
+
sig { params(node: SyntaxTree::DefNode).void }
|
174
|
+
def after_def(node)
|
154
175
|
@stack.pop
|
155
176
|
end
|
156
177
|
|
157
|
-
sig {
|
158
|
-
def
|
159
|
-
|
178
|
+
sig { params(node: SyntaxTree::ModuleDeclaration).void }
|
179
|
+
def on_module(node)
|
180
|
+
@stack << create_document_symbol(
|
160
181
|
name: full_constant_name(node.constant),
|
161
182
|
kind: :module,
|
162
183
|
range_node: node,
|
163
184
|
selection_range_node: node.constant,
|
164
185
|
)
|
186
|
+
end
|
165
187
|
|
166
|
-
|
167
|
-
|
188
|
+
sig { params(node: SyntaxTree::ModuleDeclaration).void }
|
189
|
+
def after_module(node)
|
168
190
|
@stack.pop
|
169
191
|
end
|
170
192
|
|
171
|
-
sig {
|
172
|
-
def
|
193
|
+
sig { params(node: SyntaxTree::TopConstField).void }
|
194
|
+
def on_top_const_field(node)
|
173
195
|
create_document_symbol(
|
174
196
|
name: node.constant.value,
|
175
197
|
kind: :constant,
|
@@ -178,9 +200,10 @@ module RubyLsp
|
|
178
200
|
)
|
179
201
|
end
|
180
202
|
|
181
|
-
sig {
|
182
|
-
def
|
183
|
-
|
203
|
+
sig { params(node: SyntaxTree::VarField).void }
|
204
|
+
def on_var_field(node)
|
205
|
+
value = node.value
|
206
|
+
kind = case value
|
184
207
|
when SyntaxTree::Const
|
185
208
|
:constant
|
186
209
|
when SyntaxTree::CVar, SyntaxTree::IVar
|
@@ -190,10 +213,10 @@ module RubyLsp
|
|
190
213
|
end
|
191
214
|
|
192
215
|
create_document_symbol(
|
193
|
-
name:
|
216
|
+
name: value.value,
|
194
217
|
kind: kind,
|
195
218
|
range_node: node,
|
196
|
-
selection_range_node:
|
219
|
+
selection_range_node: value,
|
197
220
|
)
|
198
221
|
end
|
199
222
|
|
@@ -205,10 +228,10 @@ module RubyLsp
|
|
205
228
|
kind: Symbol,
|
206
229
|
range_node: SyntaxTree::Node,
|
207
230
|
selection_range_node: SyntaxTree::Node,
|
208
|
-
).returns(
|
231
|
+
).returns(Interface::DocumentSymbol)
|
209
232
|
end
|
210
233
|
def create_document_symbol(name:, kind:, range_node:, selection_range_node:)
|
211
|
-
symbol =
|
234
|
+
symbol = Interface::DocumentSymbol.new(
|
212
235
|
name: name,
|
213
236
|
kind: SYMBOL_KIND[kind],
|
214
237
|
range: range_from_syntax_tree_node(range_node),
|