ruby-lsp 0.10.1 → 0.11.1
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 +4 -4
- data/VERSION +1 -1
- data/exe/ruby-lsp-check +1 -1
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +40 -5
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +141 -5
- data/lib/ruby_indexer/lib/ruby_indexer/visitor.rb +66 -18
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +23 -0
- data/lib/ruby_indexer/test/configuration_test.rb +2 -0
- data/lib/ruby_indexer/test/constant_test.rb +202 -0
- data/lib/ruby_indexer/test/index_test.rb +20 -0
- data/lib/ruby_lsp/{extension.rb → addon.rb} +27 -25
- data/lib/ruby_lsp/check_docs.rb +7 -8
- data/lib/ruby_lsp/document.rb +35 -38
- data/lib/ruby_lsp/event_emitter.rb +239 -77
- data/lib/ruby_lsp/executor.rb +45 -55
- data/lib/ruby_lsp/internal.rb +2 -3
- data/lib/ruby_lsp/listener.rb +8 -7
- data/lib/ruby_lsp/parameter_scope.rb +33 -0
- data/lib/ruby_lsp/requests/base_request.rb +3 -3
- data/lib/ruby_lsp/requests/code_action_resolve.rb +14 -14
- data/lib/ruby_lsp/requests/code_lens.rb +39 -63
- data/lib/ruby_lsp/requests/completion.rb +54 -32
- data/lib/ruby_lsp/requests/definition.rb +30 -27
- data/lib/ruby_lsp/requests/diagnostics.rb +26 -3
- data/lib/ruby_lsp/requests/document_highlight.rb +18 -19
- data/lib/ruby_lsp/requests/document_link.rb +50 -9
- data/lib/ruby_lsp/requests/document_symbol.rb +82 -75
- data/lib/ruby_lsp/requests/folding_ranges.rb +199 -222
- data/lib/ruby_lsp/requests/formatting.rb +5 -6
- data/lib/ruby_lsp/requests/hover.rb +33 -22
- data/lib/ruby_lsp/requests/inlay_hints.rb +2 -3
- data/lib/ruby_lsp/requests/selection_ranges.rb +65 -40
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +187 -145
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +3 -4
- data/lib/ruby_lsp/requests/support/annotation.rb +18 -17
- data/lib/ruby_lsp/requests/support/common.rb +17 -26
- data/lib/ruby_lsp/requests/support/dependency_detector.rb +67 -42
- data/lib/ruby_lsp/requests/support/highlight_target.rb +64 -45
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +9 -4
- data/lib/ruby_lsp/requests/support/selection_range.rb +5 -4
- data/lib/ruby_lsp/requests/support/sorbet.rb +2 -57
- data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +7 -1
- data/lib/ruby_lsp/requests/workspace_symbol.rb +4 -1
- data/lib/ruby_lsp/server.rb +6 -44
- data/lib/ruby_lsp/utils.rb +2 -12
- metadata +11 -30
@@ -2,24 +2,24 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module RubyLsp
|
5
|
-
# To register an
|
5
|
+
# To register an addon, inherit from this class and implement both `name` and `activate`
|
6
6
|
#
|
7
7
|
# # Example
|
8
8
|
#
|
9
9
|
# ```ruby
|
10
10
|
# module MyGem
|
11
|
-
# class
|
11
|
+
# class MyAddon < Addon
|
12
12
|
# def activate
|
13
13
|
# # Perform any relevant initialization
|
14
14
|
# end
|
15
15
|
#
|
16
16
|
# def name
|
17
|
-
# "My
|
17
|
+
# "My addon name"
|
18
18
|
# end
|
19
19
|
# end
|
20
20
|
# end
|
21
21
|
# ```
|
22
|
-
class
|
22
|
+
class Addon
|
23
23
|
extend T::Sig
|
24
24
|
extend T::Helpers
|
25
25
|
|
@@ -28,37 +28,37 @@ module RubyLsp
|
|
28
28
|
class << self
|
29
29
|
extend T::Sig
|
30
30
|
|
31
|
-
# Automatically track and instantiate
|
32
|
-
sig { params(child_class: T.class_of(
|
31
|
+
# Automatically track and instantiate addon classes
|
32
|
+
sig { params(child_class: T.class_of(Addon)).void }
|
33
33
|
def inherited(child_class)
|
34
|
-
|
34
|
+
addons << child_class.new
|
35
35
|
super
|
36
36
|
end
|
37
37
|
|
38
|
-
sig { returns(T::Array[
|
39
|
-
def
|
40
|
-
@
|
38
|
+
sig { returns(T::Array[Addon]) }
|
39
|
+
def addons
|
40
|
+
@addons ||= T.let([], T.nilable(T::Array[Addon]))
|
41
41
|
end
|
42
42
|
|
43
|
-
# Discovers and loads all
|
44
|
-
sig { returns(T::Array[
|
45
|
-
def
|
46
|
-
# Require all
|
47
|
-
# `some_gem/lib/ruby_lsp/your_gem_name/
|
48
|
-
Gem.find_files("ruby_lsp/**/
|
49
|
-
require File.expand_path(
|
43
|
+
# Discovers and loads all addons. Returns the list of activated addons
|
44
|
+
sig { returns(T::Array[Addon]) }
|
45
|
+
def load_addons
|
46
|
+
# Require all addons entry points, which should be placed under
|
47
|
+
# `some_gem/lib/ruby_lsp/your_gem_name/addon.rb`
|
48
|
+
Gem.find_files("ruby_lsp/**/addon.rb").each do |addon|
|
49
|
+
require File.expand_path(addon)
|
50
50
|
rescue => e
|
51
51
|
warn(e.message)
|
52
52
|
warn(e.backtrace.to_s) # rubocop:disable Lint/RedundantStringCoercion
|
53
53
|
end
|
54
54
|
|
55
|
-
# Activate each one of the discovered
|
55
|
+
# Activate each one of the discovered addons. If any problems occur in the addons, we don't want to
|
56
56
|
# fail to boot the server
|
57
|
-
|
58
|
-
|
57
|
+
addons.each do |addon|
|
58
|
+
addon.activate
|
59
59
|
nil
|
60
60
|
rescue => e
|
61
|
-
|
61
|
+
addon.add_error(e)
|
62
62
|
end
|
63
63
|
end
|
64
64
|
end
|
@@ -92,17 +92,17 @@ module RubyLsp
|
|
92
92
|
@errors.filter_map(&:backtrace).join("\n\n")
|
93
93
|
end
|
94
94
|
|
95
|
-
# Each
|
95
|
+
# Each addon should implement `MyAddon#activate` and use to perform any sort of initialization, such as
|
96
96
|
# reading information into memory or even spawning a separate process
|
97
97
|
sig { abstract.void }
|
98
98
|
def activate; end
|
99
99
|
|
100
|
-
# Each
|
100
|
+
# Each addon should implement `MyAddon#deactivate` and use to perform any clean up, like shutting down a
|
101
101
|
# child process
|
102
102
|
sig { abstract.void }
|
103
103
|
def deactivate; end
|
104
104
|
|
105
|
-
#
|
105
|
+
# Addons should override the `name` method to return the addon name
|
106
106
|
sig { abstract.returns(String) }
|
107
107
|
def name; end
|
108
108
|
|
@@ -119,11 +119,13 @@ module RubyLsp
|
|
119
119
|
# Creates a new Hover listener. This method is invoked on every Hover request
|
120
120
|
sig do
|
121
121
|
overridable.params(
|
122
|
+
nesting: T::Array[String],
|
123
|
+
index: RubyIndexer::Index,
|
122
124
|
emitter: EventEmitter,
|
123
125
|
message_queue: Thread::Queue,
|
124
126
|
).returns(T.nilable(Listener[T.nilable(Interface::Hover)]))
|
125
127
|
end
|
126
|
-
def create_hover_listener(emitter, message_queue); end
|
128
|
+
def create_hover_listener(nesting, index, emitter, message_queue); end
|
127
129
|
|
128
130
|
# Creates a new DocumentSymbol listener. This method is invoked on every DocumentSymbol request
|
129
131
|
sig do
|
data/lib/ruby_lsp/check_docs.rb
CHANGED
@@ -5,9 +5,9 @@ require "ruby_lsp/internal"
|
|
5
5
|
require "objspace"
|
6
6
|
|
7
7
|
module RubyLsp
|
8
|
-
# This rake task checks that all requests or
|
9
|
-
# specify the absolute path for all files that must be required in order to discover all listeners and their
|
10
|
-
#
|
8
|
+
# This rake task checks that all requests or addons are fully documented. Add the rake task to your Rakefile and
|
9
|
+
# specify the absolute path for all files that must be required in order to discover all listeners and their related
|
10
|
+
# GIFs
|
11
11
|
#
|
12
12
|
# # Rakefile
|
13
13
|
# request_files = FileList.new("#{__dir__}/lib/ruby_lsp/requests/*.rb") do |fl|
|
@@ -53,8 +53,7 @@ module RubyLsp
|
|
53
53
|
# documented
|
54
54
|
features = ObjectSpace.each_object(Class).filter_map do |k|
|
55
55
|
klass = T.unsafe(k)
|
56
|
-
klass if klass <
|
57
|
-
(klass < RubyLsp::Listener && klass != RubyLsp::ExtensibleListener)
|
56
|
+
klass if klass < Requests::BaseRequest || (klass < Listener && klass != ExtensibleListener)
|
58
57
|
end
|
59
58
|
|
60
59
|
missing_docs = T.let(Hash.new { |h, k| h[k] = [] }, T::Hash[String, T::Array[String]])
|
@@ -82,14 +81,14 @@ module RubyLsp
|
|
82
81
|
T.must(missing_docs[class_name]) << "No documentation found"
|
83
82
|
elsif !%r{\(https://microsoft.github.io/language-server-protocol/specification#.*\)}.match?(documentation)
|
84
83
|
T.must(missing_docs[class_name]) << <<~DOCS
|
85
|
-
Missing specification link. Requests and
|
84
|
+
Missing specification link. Requests and addons should include a link to the LSP specification for the
|
86
85
|
related feature. For example:
|
87
86
|
|
88
87
|
[Inlay hint](https://microsoft.github.io/language-server-protocol/specification#textDocument_inlayHint)
|
89
88
|
DOCS
|
90
89
|
elsif !documentation.include?("# Example")
|
91
90
|
T.must(missing_docs[class_name]) << <<~DOCS
|
92
|
-
Missing example. Requests and
|
91
|
+
Missing example. Requests and addons should include a code example that explains what the feature does.
|
93
92
|
|
94
93
|
# # Example
|
95
94
|
# ```ruby
|
@@ -99,7 +98,7 @@ module RubyLsp
|
|
99
98
|
DOCS
|
100
99
|
elsif !/\[.* demo\]\(.*\.gif\)/.match?(documentation)
|
101
100
|
T.must(missing_docs[class_name]) << <<~DOCS
|
102
|
-
Missing demonstration GIF. Each request and
|
101
|
+
Missing demonstration GIF. Each request and addon must be documented with a GIF that shows the feature
|
103
102
|
working. For example:
|
104
103
|
|
105
104
|
# [Inlay hint demo](../../inlay_hint.gif)
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -9,8 +9,8 @@ module RubyLsp
|
|
9
9
|
RangeShape = T.type_alias { { start: PositionShape, end: PositionShape } }
|
10
10
|
EditShape = T.type_alias { { range: RangeShape, text: String } }
|
11
11
|
|
12
|
-
sig { returns(
|
13
|
-
attr_reader :
|
12
|
+
sig { returns(YARP::ParseResult) }
|
13
|
+
attr_reader :parse_result
|
14
14
|
|
15
15
|
sig { returns(String) }
|
16
16
|
attr_reader :source
|
@@ -28,11 +28,18 @@ module RubyLsp
|
|
28
28
|
@source = T.let(source, String)
|
29
29
|
@version = T.let(version, Integer)
|
30
30
|
@uri = T.let(uri, URI::Generic)
|
31
|
-
@
|
32
|
-
@
|
33
|
-
|
34
|
-
|
35
|
-
|
31
|
+
@needs_parsing = T.let(false, T::Boolean)
|
32
|
+
@parse_result = T.let(YARP.parse(@source), YARP::ParseResult)
|
33
|
+
end
|
34
|
+
|
35
|
+
sig { returns(YARP::ProgramNode) }
|
36
|
+
def tree
|
37
|
+
@parse_result.value
|
38
|
+
end
|
39
|
+
|
40
|
+
sig { returns(T::Array[YARP::Comment]) }
|
41
|
+
def comments
|
42
|
+
@parse_result.comments
|
36
43
|
end
|
37
44
|
|
38
45
|
sig { params(other: Document).returns(T::Boolean) }
|
@@ -80,29 +87,21 @@ module RubyLsp
|
|
80
87
|
end
|
81
88
|
|
82
89
|
@version = version
|
83
|
-
@
|
90
|
+
@needs_parsing = true
|
84
91
|
@cache.clear
|
85
92
|
end
|
86
93
|
|
87
94
|
sig { void }
|
88
95
|
def parse
|
89
|
-
return
|
96
|
+
return unless @needs_parsing
|
90
97
|
|
91
|
-
@
|
92
|
-
@
|
93
|
-
@syntax_error = false
|
94
|
-
rescue SyntaxTree::Parser::ParseError
|
95
|
-
@syntax_error = true
|
98
|
+
@needs_parsing = false
|
99
|
+
@parse_result = YARP.parse(@source)
|
96
100
|
end
|
97
101
|
|
98
102
|
sig { returns(T::Boolean) }
|
99
103
|
def syntax_error?
|
100
|
-
@
|
101
|
-
end
|
102
|
-
|
103
|
-
sig { returns(T::Boolean) }
|
104
|
-
def parsed?
|
105
|
-
!@tree.nil?
|
104
|
+
@parse_result.failure?
|
106
105
|
end
|
107
106
|
|
108
107
|
sig { returns(Scanner) }
|
@@ -113,27 +112,25 @@ module RubyLsp
|
|
113
112
|
sig do
|
114
113
|
params(
|
115
114
|
position: PositionShape,
|
116
|
-
node_types: T::Array[T.class_of(
|
117
|
-
).returns([T.nilable(
|
115
|
+
node_types: T::Array[T.class_of(YARP::Node)],
|
116
|
+
).returns([T.nilable(YARP::Node), T.nilable(YARP::Node), T::Array[String]])
|
118
117
|
end
|
119
118
|
def locate_node(position, node_types: [])
|
120
|
-
|
121
|
-
|
122
|
-
locate(T.must(@tree), create_scanner.find_char_position(position), node_types: node_types)
|
119
|
+
locate(@parse_result.value, create_scanner.find_char_position(position), node_types: node_types)
|
123
120
|
end
|
124
121
|
|
125
122
|
sig do
|
126
123
|
params(
|
127
|
-
node:
|
124
|
+
node: YARP::Node,
|
128
125
|
char_position: Integer,
|
129
|
-
node_types: T::Array[T.class_of(
|
130
|
-
).returns([T.nilable(
|
126
|
+
node_types: T::Array[T.class_of(YARP::Node)],
|
127
|
+
).returns([T.nilable(YARP::Node), T.nilable(YARP::Node), T::Array[String]])
|
131
128
|
end
|
132
129
|
def locate(node, char_position, node_types: [])
|
133
|
-
queue = T.let(node.child_nodes.compact, T::Array[T.nilable(
|
130
|
+
queue = T.let(node.child_nodes.compact, T::Array[T.nilable(YARP::Node)])
|
134
131
|
closest = node
|
135
|
-
parent = T.let(nil, T.nilable(
|
136
|
-
nesting = T.let([], T::Array[T.any(
|
132
|
+
parent = T.let(nil, T.nilable(YARP::Node))
|
133
|
+
nesting = T.let([], T::Array[T.any(YARP::ClassNode, YARP::ModuleNode)])
|
137
134
|
|
138
135
|
until queue.empty?
|
139
136
|
candidate = queue.shift
|
@@ -144,24 +141,24 @@ module RubyLsp
|
|
144
141
|
# Add the next child_nodes to the queue to be processed. The order here is important! We want to move in the
|
145
142
|
# same order as the visiting mechanism, which means searching the child nodes before moving on to the next
|
146
143
|
# sibling
|
147
|
-
queue.unshift(*candidate.child_nodes)
|
144
|
+
T.unsafe(queue).unshift(*candidate.child_nodes)
|
148
145
|
|
149
146
|
# Skip if the current node doesn't cover the desired position
|
150
147
|
loc = candidate.location
|
151
|
-
next unless (loc.
|
148
|
+
next unless (loc.start_offset...loc.end_offset).cover?(char_position)
|
152
149
|
|
153
150
|
# If the node's start character is already past the position, then we should've found the closest node
|
154
151
|
# already
|
155
|
-
break if char_position < loc.
|
152
|
+
break if char_position < loc.start_offset
|
156
153
|
|
157
154
|
# If the candidate starts after the end of the previous nesting level, then we've exited that nesting level and
|
158
155
|
# need to pop the stack
|
159
156
|
previous_level = nesting.last
|
160
|
-
nesting.pop if previous_level &&
|
157
|
+
nesting.pop if previous_level && loc.start_offset > previous_level.location.end_offset
|
161
158
|
|
162
159
|
# Keep track of the nesting where we found the target. This is used to determine the fully qualified name of the
|
163
160
|
# target when it is a constant
|
164
|
-
if candidate.is_a?(
|
161
|
+
if candidate.is_a?(YARP::ClassNode) || candidate.is_a?(YARP::ModuleNode)
|
165
162
|
nesting << candidate
|
166
163
|
end
|
167
164
|
|
@@ -170,13 +167,13 @@ module RubyLsp
|
|
170
167
|
|
171
168
|
# If the current node is narrower than or equal to the previous closest node, then it is more precise
|
172
169
|
closest_loc = closest.location
|
173
|
-
if loc.
|
170
|
+
if loc.end_offset - loc.start_offset <= closest_loc.end_offset - closest_loc.start_offset
|
174
171
|
parent = closest
|
175
172
|
closest = candidate
|
176
173
|
end
|
177
174
|
end
|
178
175
|
|
179
|
-
[closest, parent, nesting.map { |n| n.
|
176
|
+
[closest, parent, nesting.map { |n| n.constant_path.location.slice }]
|
180
177
|
end
|
181
178
|
|
182
179
|
class Scanner
|