ruby-lsp-rails 0.3.10 → 0.3.12
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/lib/ruby_lsp/ruby_lsp_rails/addon.rb +7 -4
- data/lib/ruby_lsp/ruby_lsp_rails/code_lens.rb +52 -22
- data/lib/ruby_lsp/ruby_lsp_rails/definition.rb +4 -2
- data/lib/ruby_lsp/ruby_lsp_rails/indexing_enhancement.rb +113 -0
- data/lib/ruby_lsp/ruby_lsp_rails/runner_client.rb +25 -7
- data/lib/ruby_lsp/ruby_lsp_rails/server.rb +1 -1
- data/lib/ruby_lsp_rails/version.rb +1 -1
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9c4bfe664b0783f9ae11cc912ec6eff3cd8c2d9c5c9c51731a85d0ed7de2fbf1
|
4
|
+
data.tar.gz: 1f3f74de8ec6891464b84a8d88fd339e33ce1ddcdc770a6a34177c60d4001ebe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2e4493fb0f18638b208e3d9f40671846e351670936214420131ae0482fe7d6410f74afc5b808a707dd616471f83233eb201be51a25e29152817daa1ec0380e73
|
7
|
+
data.tar.gz: 9eb01978fc74c0d212a65240ff2f5874d38768abf85b647943c7eba0aee84116912a0ebe9c6206ddf84b6ee7b48f60535f1a288cd66c538ff56a40c501adc4ee
|
@@ -13,6 +13,7 @@ require_relative "hover"
|
|
13
13
|
require_relative "code_lens"
|
14
14
|
require_relative "document_symbol"
|
15
15
|
require_relative "definition"
|
16
|
+
require_relative "indexing_enhancement"
|
16
17
|
|
17
18
|
module RubyLsp
|
18
19
|
module Rails
|
@@ -35,6 +36,8 @@ module RubyLsp
|
|
35
36
|
# Start booting the real client in a background thread. Until this completes, the client will be a NullClient
|
36
37
|
Thread.new { @client = RunnerClient.create_client }
|
37
38
|
register_additional_file_watchers(global_state: global_state, message_queue: message_queue)
|
39
|
+
|
40
|
+
T.must(@global_state).index.register_enhancement(IndexingEnhancement.new)
|
38
41
|
end
|
39
42
|
|
40
43
|
sig { override.void }
|
@@ -51,9 +54,7 @@ module RubyLsp
|
|
51
54
|
).void
|
52
55
|
end
|
53
56
|
def create_code_lens_listener(response_builder, uri, dispatcher)
|
54
|
-
|
55
|
-
|
56
|
-
CodeLens.new(@client, response_builder, uri, dispatcher)
|
57
|
+
CodeLens.new(@client, T.must(@global_state), response_builder, uri, dispatcher)
|
57
58
|
end
|
58
59
|
|
59
60
|
sig do
|
@@ -79,7 +80,9 @@ module RubyLsp
|
|
79
80
|
|
80
81
|
sig do
|
81
82
|
override.params(
|
82
|
-
response_builder: ResponseBuilders::CollectionResponseBuilder[
|
83
|
+
response_builder: ResponseBuilders::CollectionResponseBuilder[T.any(
|
84
|
+
Interface::Location, Interface::LocationLink
|
85
|
+
)],
|
83
86
|
uri: URI::Generic,
|
84
87
|
node_context: NodeContext,
|
85
88
|
dispatcher: Prism::Dispatcher,
|
@@ -79,20 +79,30 @@ module RubyLsp
|
|
79
79
|
sig do
|
80
80
|
params(
|
81
81
|
client: RunnerClient,
|
82
|
+
global_state: GlobalState,
|
82
83
|
response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens],
|
83
84
|
uri: URI::Generic,
|
84
85
|
dispatcher: Prism::Dispatcher,
|
85
86
|
).void
|
86
87
|
end
|
87
|
-
def initialize(client, response_builder, uri, dispatcher)
|
88
|
+
def initialize(client, global_state, response_builder, uri, dispatcher)
|
88
89
|
@client = client
|
90
|
+
@global_state = global_state
|
89
91
|
@response_builder = response_builder
|
90
92
|
@path = T.let(uri.to_standardized_path, T.nilable(String))
|
91
93
|
@group_id = T.let(1, Integer)
|
92
94
|
@group_id_stack = T.let([], T::Array[Integer])
|
93
95
|
@constant_name_stack = T.let([], T::Array[[String, T.nilable(String)]])
|
94
96
|
|
95
|
-
dispatcher.register(
|
97
|
+
dispatcher.register(
|
98
|
+
self,
|
99
|
+
:on_call_node_enter,
|
100
|
+
:on_class_node_enter,
|
101
|
+
:on_def_node_enter,
|
102
|
+
:on_class_node_leave,
|
103
|
+
:on_module_node_enter,
|
104
|
+
:on_module_node_leave,
|
105
|
+
)
|
96
106
|
end
|
97
107
|
|
98
108
|
sig { params(node: Prism::CallNode).void }
|
@@ -119,6 +129,7 @@ module RubyLsp
|
|
119
129
|
|
120
130
|
if controller?
|
121
131
|
add_route_code_lens_to_action(node)
|
132
|
+
add_jump_to_view(node)
|
122
133
|
end
|
123
134
|
end
|
124
135
|
|
@@ -156,6 +167,16 @@ module RubyLsp
|
|
156
167
|
@constant_name_stack.pop
|
157
168
|
end
|
158
169
|
|
170
|
+
sig { params(node: Prism::ModuleNode).void }
|
171
|
+
def on_module_node_enter(node)
|
172
|
+
@constant_name_stack << [node.constant_path.slice, nil]
|
173
|
+
end
|
174
|
+
|
175
|
+
sig { params(node: Prism::ModuleNode).void }
|
176
|
+
def on_module_node_leave(node)
|
177
|
+
@constant_name_stack.pop
|
178
|
+
end
|
179
|
+
|
159
180
|
private
|
160
181
|
|
161
182
|
sig { returns(T.nilable(T::Boolean)) }
|
@@ -167,34 +188,42 @@ module RubyLsp
|
|
167
188
|
end
|
168
189
|
|
169
190
|
sig { params(node: Prism::DefNode).void }
|
170
|
-
def
|
171
|
-
class_name
|
172
|
-
|
173
|
-
|
174
|
-
|
191
|
+
def add_jump_to_view(node)
|
192
|
+
class_name = @constant_name_stack.map(&:first).join("::")
|
193
|
+
action_name = node.name
|
194
|
+
controller_name = class_name
|
195
|
+
.delete_suffix("Controller")
|
196
|
+
.gsub(/([a-z])([A-Z])/, "\\1_\\2")
|
197
|
+
.gsub("::", "/")
|
198
|
+
.downcase
|
199
|
+
|
200
|
+
view_uris = Dir.glob("#{@client.rails_root}/app/views/#{controller_name}/#{action_name}*").map! do |path|
|
201
|
+
URI::Generic.from_path(path: path).to_s
|
202
|
+
end
|
203
|
+
return if view_uris.empty?
|
204
|
+
|
205
|
+
@response_builder << create_code_lens(
|
206
|
+
node,
|
207
|
+
title: "Jump to view",
|
208
|
+
command_name: "rubyLsp.openFile",
|
209
|
+
arguments: [view_uris],
|
210
|
+
data: { type: "file" },
|
175
211
|
)
|
212
|
+
end
|
176
213
|
|
214
|
+
sig { params(node: Prism::DefNode).void }
|
215
|
+
def add_route_code_lens_to_action(node)
|
216
|
+
class_name, _ = T.must(@constant_name_stack.last)
|
217
|
+
route = @client.route(controller: class_name, action: node.name.to_s)
|
177
218
|
return unless route
|
178
219
|
|
179
|
-
|
180
|
-
verb = route[:verb]
|
181
|
-
source_location = route[:source_location]
|
182
|
-
|
183
|
-
arguments = [
|
184
|
-
source_location,
|
185
|
-
{
|
186
|
-
start_line: node.location.start_line - 1,
|
187
|
-
start_column: node.location.start_column,
|
188
|
-
end_line: node.location.end_line - 1,
|
189
|
-
end_column: node.location.end_column,
|
190
|
-
},
|
191
|
-
]
|
220
|
+
file_path, line = route[:source_location]
|
192
221
|
|
193
222
|
@response_builder << create_code_lens(
|
194
223
|
node,
|
195
|
-
title: [verb
|
224
|
+
title: "#{route[:verb]} #{route[:path]}",
|
196
225
|
command_name: "rubyLsp.openFile",
|
197
|
-
arguments:
|
226
|
+
arguments: [["file://#{file_path}#L#{line}"]],
|
198
227
|
data: { type: "file" },
|
199
228
|
)
|
200
229
|
end
|
@@ -230,6 +259,7 @@ module RubyLsp
|
|
230
259
|
sig { params(node: Prism::Node, name: String, command: String, kind: Symbol).void }
|
231
260
|
def add_test_code_lens(node, name:, command:, kind:)
|
232
261
|
return unless @path
|
262
|
+
return unless @global_state.test_library == "rails"
|
233
263
|
|
234
264
|
arguments = [
|
235
265
|
@path,
|
@@ -17,7 +17,7 @@ module RubyLsp
|
|
17
17
|
# # Example
|
18
18
|
#
|
19
19
|
# ```ruby
|
20
|
-
# before_action :foo # <- Go to definition on this symbol will jump to the method
|
20
|
+
# before_action :foo # <- Go to definition on this symbol will jump to the method
|
21
21
|
# ```
|
22
22
|
#
|
23
23
|
# Notes for named routes:
|
@@ -34,7 +34,9 @@ module RubyLsp
|
|
34
34
|
sig do
|
35
35
|
params(
|
36
36
|
client: RunnerClient,
|
37
|
-
response_builder: ResponseBuilders::CollectionResponseBuilder[
|
37
|
+
response_builder: RubyLsp::ResponseBuilders::CollectionResponseBuilder[T.any(
|
38
|
+
Interface::Location, Interface::LocationLink
|
39
|
+
)],
|
38
40
|
node_context: NodeContext,
|
39
41
|
index: RubyIndexer::Index,
|
40
42
|
dispatcher: Prism::Dispatcher,
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Rails
|
6
|
+
class IndexingEnhancement
|
7
|
+
extend T::Sig
|
8
|
+
include RubyIndexer::Enhancement
|
9
|
+
|
10
|
+
sig do
|
11
|
+
override.params(
|
12
|
+
index: RubyIndexer::Index,
|
13
|
+
owner: T.nilable(RubyIndexer::Entry::Namespace),
|
14
|
+
node: Prism::CallNode,
|
15
|
+
file_path: String,
|
16
|
+
).void
|
17
|
+
end
|
18
|
+
def on_call_node(index, owner, node, file_path)
|
19
|
+
return unless owner
|
20
|
+
|
21
|
+
name = node.name
|
22
|
+
|
23
|
+
case name
|
24
|
+
when :extend
|
25
|
+
handle_concern_extend(index, owner, node)
|
26
|
+
when :has_one, :has_many, :belongs_to, :has_and_belongs_to_many
|
27
|
+
handle_association(index, owner, node, file_path)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
sig do
|
34
|
+
params(
|
35
|
+
index: RubyIndexer::Index,
|
36
|
+
owner: RubyIndexer::Entry::Namespace,
|
37
|
+
node: Prism::CallNode,
|
38
|
+
file_path: String,
|
39
|
+
).void
|
40
|
+
end
|
41
|
+
def handle_association(index, owner, node, file_path)
|
42
|
+
arguments = node.arguments&.arguments
|
43
|
+
return unless arguments
|
44
|
+
|
45
|
+
name_arg = arguments.first
|
46
|
+
|
47
|
+
name = case name_arg
|
48
|
+
when Prism::StringNode
|
49
|
+
name_arg.content
|
50
|
+
when Prism::SymbolNode
|
51
|
+
name_arg.value
|
52
|
+
end
|
53
|
+
|
54
|
+
return unless name
|
55
|
+
|
56
|
+
# Reader
|
57
|
+
index.add(RubyIndexer::Entry::Method.new(
|
58
|
+
name,
|
59
|
+
file_path,
|
60
|
+
name_arg.location,
|
61
|
+
name_arg.location,
|
62
|
+
[],
|
63
|
+
[RubyIndexer::Entry::Signature.new([])],
|
64
|
+
RubyIndexer::Entry::Visibility::PUBLIC,
|
65
|
+
owner,
|
66
|
+
))
|
67
|
+
|
68
|
+
# Writer
|
69
|
+
index.add(RubyIndexer::Entry::Method.new(
|
70
|
+
"#{name}=",
|
71
|
+
file_path,
|
72
|
+
name_arg.location,
|
73
|
+
name_arg.location,
|
74
|
+
[],
|
75
|
+
[RubyIndexer::Entry::Signature.new([RubyIndexer::Entry::RequiredParameter.new(name: name.to_sym)])],
|
76
|
+
RubyIndexer::Entry::Visibility::PUBLIC,
|
77
|
+
owner,
|
78
|
+
))
|
79
|
+
end
|
80
|
+
|
81
|
+
sig do
|
82
|
+
params(
|
83
|
+
index: RubyIndexer::Index,
|
84
|
+
owner: RubyIndexer::Entry::Namespace,
|
85
|
+
node: Prism::CallNode,
|
86
|
+
).void
|
87
|
+
end
|
88
|
+
def handle_concern_extend(index, owner, node)
|
89
|
+
arguments = node.arguments&.arguments
|
90
|
+
return unless arguments
|
91
|
+
|
92
|
+
arguments.each do |node|
|
93
|
+
next unless node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode)
|
94
|
+
|
95
|
+
module_name = node.full_name
|
96
|
+
next unless module_name == "ActiveSupport::Concern"
|
97
|
+
|
98
|
+
index.register_included_hook(owner.name) do |index, base|
|
99
|
+
class_methods_name = "#{owner.name}::ClassMethods"
|
100
|
+
|
101
|
+
if index.indexed?(class_methods_name)
|
102
|
+
singleton = index.existing_or_new_singleton_class(base.name)
|
103
|
+
singleton.mixin_operations << RubyIndexer::Entry::Include.new(class_methods_name)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError,
|
107
|
+
Prism::ConstantPathNode::MissingNodesInConstantPathError
|
108
|
+
# Do nothing
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -36,6 +36,9 @@ module RubyLsp
|
|
36
36
|
|
37
37
|
extend T::Sig
|
38
38
|
|
39
|
+
sig { returns(String) }
|
40
|
+
attr_reader :rails_root
|
41
|
+
|
39
42
|
sig { void }
|
40
43
|
def initialize
|
41
44
|
# Spring needs a Process session ID. It uses this ID to "attach" itself to the parent process, so that when the
|
@@ -67,7 +70,8 @@ module RubyLsp
|
|
67
70
|
|
68
71
|
begin
|
69
72
|
count += 1
|
70
|
-
read_response
|
73
|
+
initialize_response = T.must(read_response)
|
74
|
+
@rails_root = T.let(initialize_response[:root], String)
|
71
75
|
rescue EmptyMessageError
|
72
76
|
$stderr.puts("Ruby LSP Rails is retrying initialize (#{count})")
|
73
77
|
retry if count < MAX_RETRIES
|
@@ -80,9 +84,7 @@ module RubyLsp
|
|
80
84
|
if @wait_thread.alive?
|
81
85
|
$stderr.puts("Ruby LSP Rails is force killing the server")
|
82
86
|
sleep(0.5) # give the server a bit of time if we already issued a shutdown notification
|
83
|
-
|
84
|
-
# Windows does not support the `TERM` signal, so we're forced to use `KILL` here
|
85
|
-
Process.kill(T.must(Signal.list["KILL"]), @wait_thread.pid)
|
87
|
+
force_kill
|
86
88
|
end
|
87
89
|
end
|
88
90
|
end
|
@@ -145,6 +147,9 @@ module RubyLsp
|
|
145
147
|
send_message("shutdown")
|
146
148
|
sleep(0.5) # give the server a bit of time to shutdown
|
147
149
|
[@stdin, @stdout, @stderr].each(&:close)
|
150
|
+
rescue IOError
|
151
|
+
# The server connection may have died
|
152
|
+
force_kill
|
148
153
|
end
|
149
154
|
|
150
155
|
sig { returns(T::Boolean) }
|
@@ -165,7 +170,7 @@ module RubyLsp
|
|
165
170
|
read_response
|
166
171
|
end
|
167
172
|
|
168
|
-
sig { params(request: String, params: T.nilable(T::Hash[Symbol, T.untyped])).void }
|
173
|
+
sig { overridable.params(request: String, params: T.nilable(T::Hash[Symbol, T.untyped])).void }
|
169
174
|
def send_message(request, params = nil)
|
170
175
|
message = { method: request }
|
171
176
|
message[:params] = params if params
|
@@ -176,9 +181,11 @@ module RubyLsp
|
|
176
181
|
# The server connection died
|
177
182
|
end
|
178
183
|
|
179
|
-
|
184
|
+
# Notifications are like messages, but one-way, with no response sent back.
|
185
|
+
sig { params(request: String, params: T.nilable(T::Hash[Symbol, T.untyped])).void }
|
186
|
+
def send_notification(request, params = nil) = send_message(request, params)
|
180
187
|
|
181
|
-
sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
|
188
|
+
sig { overridable.returns(T.nilable(T::Hash[Symbol, T.untyped])) }
|
182
189
|
def read_response
|
183
190
|
headers = @stdout.gets("\r\n\r\n")
|
184
191
|
raise IncompleteMessageError unless headers
|
@@ -199,6 +206,12 @@ module RubyLsp
|
|
199
206
|
# The server connection died
|
200
207
|
nil
|
201
208
|
end
|
209
|
+
|
210
|
+
sig { void }
|
211
|
+
def force_kill
|
212
|
+
# Windows does not support the `TERM` signal, so we're forced to use `KILL` here
|
213
|
+
Process.kill(T.must(Signal.list["KILL"]), @wait_thread.pid)
|
214
|
+
end
|
202
215
|
end
|
203
216
|
|
204
217
|
class NullClient < RunnerClient
|
@@ -218,6 +231,11 @@ module RubyLsp
|
|
218
231
|
true
|
219
232
|
end
|
220
233
|
|
234
|
+
sig { override.returns(String) }
|
235
|
+
def rails_root
|
236
|
+
Dir.pwd
|
237
|
+
end
|
238
|
+
|
221
239
|
private
|
222
240
|
|
223
241
|
sig { override.params(request: String, params: T.nilable(T::Hash[Symbol, T.untyped])).void }
|
@@ -24,7 +24,7 @@ module RubyLsp
|
|
24
24
|
routes_reloader = ::Rails.application.routes_reloader
|
25
25
|
routes_reloader.execute_unless_loaded if routes_reloader&.respond_to?(:execute_unless_loaded)
|
26
26
|
|
27
|
-
initialize_result = { result: { message: "ok" } }.to_json
|
27
|
+
initialize_result = { result: { message: "ok", root: ::Rails.root.to_s } }.to_json
|
28
28
|
$stdout.write("Content-Length: #{initialize_result.length}\r\n\r\n#{initialize_result}")
|
29
29
|
|
30
30
|
while @running
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-lsp-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-08-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-lsp
|
@@ -16,7 +16,7 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.17.
|
19
|
+
version: 0.17.12
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
22
|
version: 0.18.0
|
@@ -26,7 +26,7 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: 0.17.
|
29
|
+
version: 0.17.12
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: 0.18.0
|
@@ -46,6 +46,7 @@ files:
|
|
46
46
|
- lib/ruby_lsp/ruby_lsp_rails/definition.rb
|
47
47
|
- lib/ruby_lsp/ruby_lsp_rails/document_symbol.rb
|
48
48
|
- lib/ruby_lsp/ruby_lsp_rails/hover.rb
|
49
|
+
- lib/ruby_lsp/ruby_lsp_rails/indexing_enhancement.rb
|
49
50
|
- lib/ruby_lsp/ruby_lsp_rails/runner_client.rb
|
50
51
|
- lib/ruby_lsp/ruby_lsp_rails/server.rb
|
51
52
|
- lib/ruby_lsp/ruby_lsp_rails/support/active_support_test_case_helper.rb
|
@@ -79,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
79
80
|
- !ruby/object:Gem::Version
|
80
81
|
version: '0'
|
81
82
|
requirements: []
|
82
|
-
rubygems_version: 3.5.
|
83
|
+
rubygems_version: 3.5.16
|
83
84
|
signing_key:
|
84
85
|
specification_version: 4
|
85
86
|
summary: A Ruby LSP addon for Rails
|