6
6
7
7
module RubyLsp
8
8
module Rails
9
- class RunnerClient
9
+ module Client
10
+ extend T ::Sig
11
+ extend T ::Helpers
12
+
13
+ abstract!
14
+
10
15
class << self
11
16
extend T ::Sig
12
17
13
- sig { returns ( RunnerClient ) }
18
+ sig { returns ( Client ) }
14
19
def create_client
15
20
if File . exist? ( "bin/rails" )
16
- new
21
+ RunnerClient . new
17
22
else
18
23
$stderr. puts ( <<~MSG )
19
24
Ruby LSP Rails failed to locate bin/rails in the current directory: #{ Dir . pwd } "
@@ -32,6 +37,94 @@ class InitializationError < StandardError; end
32
37
class IncompleteMessageError < StandardError ; end
33
38
class EmptyMessageError < StandardError ; end
34
39
40
+ sig { overridable . params ( name : String ) . returns ( T . nilable ( T ::Hash [ Symbol , T . untyped ] ) ) }
41
+ def model ( name )
42
+ make_request ( "model" , name : name )
43
+ rescue IncompleteMessageError
44
+ $stderr. puts ( "Ruby LSP Rails failed to get model information: #{ read_error } " )
45
+ nil
46
+ end
47
+
48
+ sig { overridable . params ( controller : String , action : String ) . returns ( T . nilable ( T ::Hash [ Symbol , T . untyped ] ) ) }
49
+ def route ( controller :, action :)
50
+ make_request ( "route_info" , controller : controller , action : action )
51
+ rescue IncompleteMessageError
52
+ $stderr. puts ( "Ruby LSP Rails failed to get route information: #{ read_error } " )
53
+ nil
54
+ end
55
+
56
+ sig { overridable . void }
57
+ def trigger_reload
58
+ $stderr. puts ( "Reloading Rails application" )
59
+ send_notification ( "reload" )
60
+ rescue IncompleteMessageError
61
+ $stderr. puts ( "Ruby LSP Rails failed to trigger reload" )
62
+ nil
63
+ end
64
+
65
+ sig do
66
+ params (
67
+ model_name : String ,
68
+ association_name : String ,
69
+ ) . returns ( T . nilable ( T ::Hash [ Symbol , T . untyped ] ) )
70
+ end
71
+ def association_target_location ( model_name :, association_name :)
72
+ make_request (
73
+ "association_target_location" ,
74
+ model_name : model_name ,
75
+ association_name : association_name ,
76
+ )
77
+ rescue => e
78
+ $stderr. puts ( "Ruby LSP Rails failed with #{ e . message } : #{ read_error } " )
79
+ end
80
+
81
+ sig { overridable . params ( name : String ) . returns ( T . nilable ( T ::Hash [ Symbol , T . untyped ] ) ) }
82
+ def route_location ( name )
83
+ make_request ( "route_location" , name : name )
84
+ rescue IncompleteMessageError
85
+ $stderr. puts ( "Ruby LSP Rails failed to get route location: #{ read_error } " )
86
+ nil
87
+ end
88
+
89
+ sig { abstract . void }
90
+ def shutdown ; end
91
+
92
+ sig { abstract . returns ( T ::Boolean ) }
93
+ def stopped? ; end
94
+
95
+ sig { abstract . returns ( String ) }
96
+ def rails_root ; end
97
+
98
+ private
99
+
100
+ sig do
101
+ overridable . params (
102
+ request : String ,
103
+ params : T . nilable ( T ::Hash [ Symbol , T . untyped ] ) ,
104
+ ) . returns ( T . nilable ( T ::Hash [ Symbol , T . untyped ] ) )
105
+ end
106
+ def make_request ( request , params = nil )
107
+ send_message ( request , params )
108
+ read_response
109
+ end
110
+
111
+ sig { abstract . params ( request : String , params : T . nilable ( T ::Hash [ Symbol , T . untyped ] ) ) . void }
112
+ def send_message ( request , params = nil ) ; end
113
+
114
+ # Notifications are like messages, but one-way, with no response sent back.
115
+ sig { params ( request : String , params : T . nilable ( T ::Hash [ Symbol , T . untyped ] ) ) . void }
116
+ def send_notification ( request , params = nil ) = send_message ( request , params )
117
+
118
+ sig { abstract . returns ( T . nilable ( T ::Hash [ Symbol , T . untyped ] ) ) }
119
+ def read_response ; end
120
+
121
+ sig { abstract . returns ( String ) }
122
+ def read_error ; end
123
+ end
124
+
125
+ class RunnerClient
126
+ include Client
127
+
35
128
MAX_RETRIES = 5
36
129
37
130
extend T ::Sig
@@ -89,59 +182,10 @@ def initialize
89
182
end
90
183
end
91
184
rescue Errno ::EPIPE , IncompleteMessageError
92
- raise InitializationError , @stderr . read
185
+ raise InitializationError , read_error
93
186
end
94
187
95
- sig { params ( name : String ) . returns ( T . nilable ( T ::Hash [ Symbol , T . untyped ] ) ) }
96
- def model ( name )
97
- make_request ( "model" , name : name )
98
- rescue IncompleteMessageError
99
- $stderr. puts ( "Ruby LSP Rails failed to get model information: #{ @stderr . read } " )
100
- nil
101
- end
102
-
103
- sig do
104
- params (
105
- model_name : String ,
106
- association_name : String ,
107
- ) . returns ( T . nilable ( T ::Hash [ Symbol , T . untyped ] ) )
108
- end
109
- def association_target_location ( model_name :, association_name :)
110
- make_request (
111
- "association_target_location" ,
112
- model_name : model_name ,
113
- association_name : association_name ,
114
- )
115
- rescue => e
116
- $stderr. puts ( "Ruby LSP Rails failed with #{ e . message } : #{ @stderr . read } " )
117
- end
118
-
119
- sig { params ( name : String ) . returns ( T . nilable ( T ::Hash [ Symbol , T . untyped ] ) ) }
120
- def route_location ( name )
121
- make_request ( "route_location" , name : name )
122
- rescue IncompleteMessageError
123
- $stderr. puts ( "Ruby LSP Rails failed to get route location: #{ @stderr . read } " )
124
- nil
125
- end
126
-
127
- sig { params ( controller : String , action : String ) . returns ( T . nilable ( T ::Hash [ Symbol , T . untyped ] ) ) }
128
- def route ( controller :, action :)
129
- make_request ( "route_info" , controller : controller , action : action )
130
- rescue IncompleteMessageError
131
- $stderr. puts ( "Ruby LSP Rails failed to get route information: #{ @stderr . read } " )
132
- nil
133
- end
134
-
135
- sig { void }
136
- def trigger_reload
137
- $stderr. puts ( "Reloading Rails application" )
138
- send_notification ( "reload" )
139
- rescue IncompleteMessageError
140
- $stderr. puts ( "Ruby LSP Rails failed to trigger reload" )
141
- nil
142
- end
143
-
144
- sig { void }
188
+ sig { override . void }
145
189
def shutdown
146
190
$stderr. puts ( "Ruby LSP Rails shutting down server" )
147
191
send_message ( "shutdown" )
@@ -152,25 +196,14 @@ def shutdown
152
196
force_kill
153
197
end
154
198
155
- sig { returns ( T ::Boolean ) }
199
+ sig { override . returns ( T ::Boolean ) }
156
200
def stopped?
157
201
[ @stdin , @stdout , @stderr ] . all? ( &:closed? ) && !@wait_thread . alive?
158
202
end
159
203
160
204
private
161
205
162
- sig do
163
- params (
164
- request : String ,
165
- params : T . nilable ( T ::Hash [ Symbol , T . untyped ] ) ,
166
- ) . returns ( T . nilable ( T ::Hash [ Symbol , T . untyped ] ) )
167
- end
168
- def make_request ( request , params = nil )
169
- send_message ( request , params )
170
- read_response
171
- end
172
-
173
- sig { overridable . params ( request : String , params : T . nilable ( T ::Hash [ Symbol , T . untyped ] ) ) . void }
206
+ sig { override . params ( request : String , params : T . nilable ( T ::Hash [ Symbol , T . untyped ] ) ) . void }
174
207
def send_message ( request , params = nil )
175
208
message = { method : request }
176
209
message [ :params ] = params if params
@@ -181,11 +214,7 @@ def send_message(request, params = nil)
181
214
# The server connection died
182
215
end
183
216
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 )
187
-
188
- sig { overridable . returns ( T . nilable ( T ::Hash [ Symbol , T . untyped ] ) ) }
217
+ sig { override . returns ( T . nilable ( T ::Hash [ Symbol , T . untyped ] ) ) }
189
218
def read_response
190
219
headers = @stdout . gets ( "\r \n \r \n " )
191
220
raise IncompleteMessageError unless headers
@@ -207,19 +236,20 @@ def read_response
207
236
nil
208
237
end
209
238
239
+ sig { override . returns ( String ) }
240
+ def read_error = @stderr . read
241
+
210
242
sig { void }
211
243
def force_kill
212
244
# Windows does not support the `TERM` signal, so we're forced to use `KILL` here
213
245
Process . kill ( T . must ( Signal . list [ "KILL" ] ) , @wait_thread . pid )
214
246
end
215
247
end
216
248
217
- class NullClient < RunnerClient
249
+ class NullClient
218
250
extend T ::Sig
219
251
220
- sig { void }
221
- def initialize # rubocop:disable Lint/MissingSuper
222
- end
252
+ include Client
223
253
224
254
sig { override . void }
225
255
def shutdown
@@ -247,6 +277,9 @@ def send_message(request, params = nil)
247
277
def read_response
248
278
# no-op
249
279
end
280
+
281
+ sig { override . returns ( String ) }
282
+ def read_error = "<no error information available>"
250
283
end
251
284
end
252
285
end
0 commit comments