ruby-lsp 0.4.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
# ![Code action resolve demo](../../
|
6
|
+
# ![Code action resolve demo](../../code_action_resolve.gif)
|
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
|
-
# ![Code actions demo](../../
|
6
|
+
# ![Code actions demo](../../code_actions.gif)
|
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
|
+
# ![Code lens demo](../../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
|
+
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
|
-
# ![Diagnostics demo](../../
|
8
|
+
# ![Diagnostics demo](../../diagnostics.gif)
|
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
|
-
# ![Document highlight demo](../../
|
6
|
+
# ![Document highlight demo](../../document_highlight.gif)
|
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
|
-
# ![Document link demo](../../
|
8
|
+
# ![Document link demo](../../document_link.gif)
|
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
|
-
# ![Document symbol demo](../../
|
6
|
+
# ![Document symbol demo](../../document_symbol.gif)
|
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),
|