refined-steep-server 0.1.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.
@@ -0,0 +1,376 @@
1
+ module Steep
2
+ module PathHelper
3
+ def self.to_pathname: (String uri) -> Pathname?
4
+ def self.to_uri: (Pathname path) -> URI::Generic
5
+ end
6
+
7
+ module Server
8
+ module LSPFormatter
9
+ def self.format_hover_content: (untyped content) -> untyped
10
+ def self.format_completion_docs: (untyped item) -> String?
11
+ def self.declaration_summary: (untyped decl) -> String?
12
+ def self.markup_content: (?String? string) { () -> String? } -> untyped
13
+ end
14
+ end
15
+
16
+ class Source
17
+ def self.parse: (String content, path: Pathname, factory: untyped) -> Source
18
+ def without_unrelated_defs: (line: Integer, column: Integer) -> Source
19
+ end
20
+
21
+ class Typing
22
+ class UnknownNodeError < StandardError
23
+ end
24
+ end
25
+
26
+ class Project
27
+ attr_reader targets: Array[Target]
28
+ attr_reader steepfile_path: Pathname
29
+ attr_reader base_dir: Pathname
30
+ attr_accessor global_options: untyped
31
+
32
+ def initialize: (steepfile_path: Pathname, ?base_dir: Pathname?) -> void
33
+ def relative_path: (Pathname path) -> Pathname
34
+ def absolute_path: (Pathname path) -> Pathname
35
+ def target_for_source_path: (Pathname path) -> Target?
36
+ def target_for_signature_path: (Pathname path) -> Target?
37
+
38
+ class Target
39
+ attr_reader name: Symbol
40
+ def possible_source_file?: (Pathname path) -> untyped
41
+ def code_diagnostics_config: () -> Hash[untyped, untyped]?
42
+ def new_env_loader: () -> untyped
43
+ def implicitly_returns_nil: () -> bool
44
+ end
45
+
46
+ class DSL
47
+ def self.parse: (Project project, String code, ?filename: String) -> void
48
+ end
49
+ end
50
+
51
+ module Services
52
+ class TypeCheckService
53
+ attr_reader project: Project
54
+ attr_reader source_files: Hash[Pathname, SourceFile]
55
+ attr_reader signature_services: Hash[Symbol, SignatureService]
56
+ attr_reader signature_validation_diagnostics: untyped
57
+
58
+ def initialize: (project: Project) -> void
59
+ def update: (changes: Hash[Pathname, Array[ContentChange]]) -> void
60
+ def typecheck_source: (path: Pathname, target: Project::Target) -> Array[untyped]?
61
+ def validate_signature: (path: Pathname, target: Project::Target) -> Array[untyped]?
62
+
63
+ class SourceFile
64
+ attr_reader path: Pathname
65
+ attr_reader content: String
66
+ attr_reader node: untyped
67
+ attr_reader typing: untyped
68
+ attr_reader errors: Array[untyped]?
69
+ attr_reader ignores: untyped
70
+
71
+ def diagnostics: () -> Array[untyped]?
72
+ end
73
+ end
74
+
75
+ class SignatureService
76
+ def current_subtyping: () -> untyped
77
+ def latest_rbs_index: () -> untyped
78
+ def latest_env: () -> untyped
79
+ def env_rbs_paths: () -> Set[Pathname]
80
+ end
81
+
82
+ class ContentChange
83
+ attr_reader range: [Position, Position]?
84
+ attr_reader text: String
85
+
86
+ def initialize: (?range: [Position, Position]?, text: String) -> void
87
+
88
+ class Position
89
+ attr_reader line: Integer
90
+ attr_reader column: Integer
91
+
92
+ def initialize: (line: Integer, column: Integer) -> void
93
+ end
94
+ end
95
+
96
+ class PathAssignment
97
+ def self.all: () -> PathAssignment
98
+ end
99
+
100
+ class FileLoader
101
+ def initialize: (base_dir: Pathname) -> void
102
+ def each_path_in_target: (Project::Target target, ?Array[String] command_line_args) { (Pathname) -> void } -> void
103
+ end
104
+
105
+ module HoverProvider
106
+ def self.content_for: (service: TypeCheckService, path: Pathname, line: Integer, column: Integer) -> untyped
107
+ end
108
+
109
+ class GotoService
110
+ def initialize: (type_check: TypeCheckService, assignment: PathAssignment) -> void
111
+ def definition: (path: Pathname, line: Integer, column: Integer) -> Array[untyped]
112
+ def implementation: (path: Pathname, line: Integer, column: Integer) -> Array[untyped]
113
+ def type_definition: (path: Pathname, line: Integer, column: Integer) -> Array[untyped]
114
+ end
115
+
116
+ class SignatureHelpProvider
117
+ def initialize: (source: Source, subtyping: untyped) -> void
118
+ def run: (line: Integer, column: Integer) -> [Array[untyped], Integer]?
119
+ end
120
+
121
+ class CompletionProvider
122
+ def initialize: (source_text: String, path: Pathname, subtyping: untyped) -> void
123
+ def run: (line: Integer, column: Integer) -> Array[untyped]
124
+
125
+ class LocalVariableItem
126
+ attr_reader identifier: Symbol
127
+ attr_reader type: untyped
128
+ attr_reader range: untyped
129
+ end
130
+
131
+ class ConstantItem
132
+ attr_reader identifier: Symbol
133
+ attr_reader range: untyped
134
+ attr_reader env: untyped
135
+ attr_reader full_name: untyped
136
+ def class?: () -> bool
137
+ def module?: () -> bool
138
+ def decl: () -> untyped
139
+ def comments: () -> Array[untyped]
140
+ def deprecated?: () -> bool
141
+ end
142
+
143
+ class SimpleMethodNameItem
144
+ attr_reader identifier: Symbol
145
+ attr_reader method_name: untyped
146
+ attr_reader range: untyped
147
+ attr_reader deprecated: bool
148
+ end
149
+
150
+ class ComplexMethodNameItem
151
+ attr_reader identifier: Symbol
152
+ attr_reader method_names: Array[untyped]
153
+ attr_reader range: untyped
154
+ end
155
+
156
+ class GeneratedMethodNameItem
157
+ attr_reader identifier: Symbol
158
+ attr_reader range: untyped
159
+ end
160
+
161
+ class InstanceVariableItem
162
+ attr_reader identifier: Symbol
163
+ attr_reader type: untyped
164
+ attr_reader range: untyped
165
+ end
166
+
167
+ class KeywordArgumentItem
168
+ attr_reader identifier: Symbol
169
+ attr_reader range: untyped
170
+ end
171
+
172
+ class TypeNameItem
173
+ attr_reader absolute_type_name: untyped
174
+ attr_reader relative_type_name: untyped
175
+ attr_reader range: untyped
176
+ attr_reader env: untyped
177
+ end
178
+
179
+ class TextItem
180
+ attr_reader label: String
181
+ attr_reader text: String
182
+ attr_reader help_text: String?
183
+ attr_reader range: untyped
184
+ end
185
+ end
186
+ end
187
+
188
+ module Diagnostic
189
+ class LSPFormatter
190
+ def initialize: (?Hash[untyped, untyped]? config, ?default_severity: Symbol) -> void
191
+ def format: (untyped diagnostic) -> untyped
192
+ end
193
+ end
194
+
195
+ module Index
196
+ class SignatureSymbolProvider
197
+ attr_reader indexes: Hash[untyped, untyped]
198
+
199
+ def initialize: (project: Project, assignment: Services::PathAssignment) -> void
200
+ def query_symbol: (String query) -> Array[untyped]
201
+ end
202
+ end
203
+ end
204
+
205
+ module LanguageServer
206
+ module Protocol
207
+ module Interface
208
+ # All interface classes return hash-like objects
209
+ class InitializeResult
210
+ def initialize: (**untyped) -> void
211
+ def to_hash: () -> Hash[Symbol, untyped]
212
+ end
213
+
214
+ class ServerCapabilities
215
+ def initialize: (**untyped) -> void
216
+ def to_hash: () -> Hash[Symbol, untyped]
217
+ end
218
+
219
+ class TextDocumentSyncOptions
220
+ def initialize: (**untyped) -> void
221
+ def to_hash: () -> Hash[Symbol, untyped]
222
+ end
223
+
224
+ class SaveOptions
225
+ def initialize: (**untyped) -> void
226
+ def to_hash: () -> Hash[Symbol, untyped]
227
+ end
228
+
229
+ class CompletionOptions
230
+ def initialize: (**untyped) -> void
231
+ def to_hash: () -> Hash[Symbol, untyped]
232
+ end
233
+
234
+ class SignatureHelpOptions
235
+ def initialize: (**untyped) -> void
236
+ def to_hash: () -> Hash[Symbol, untyped]
237
+ end
238
+
239
+ class Hover
240
+ def initialize: (**untyped) -> void
241
+ def to_hash: () -> Hash[Symbol, untyped]
242
+ end
243
+
244
+ class MarkupContent
245
+ def initialize: (**untyped) -> void
246
+ def to_hash: () -> Hash[Symbol, untyped]
247
+ end
248
+
249
+ class Range
250
+ def initialize: (**untyped) -> void
251
+ def to_hash: () -> Hash[Symbol, untyped]
252
+ end
253
+
254
+ class Position
255
+ def initialize: (**untyped) -> void
256
+ def to_hash: () -> Hash[Symbol, untyped]
257
+ end
258
+
259
+ class CompletionList
260
+ def initialize: (**untyped) -> void
261
+ def to_hash: () -> Hash[Symbol, untyped]
262
+ end
263
+
264
+ class CompletionItem
265
+ def initialize: (**untyped) -> void
266
+ def to_hash: () -> Hash[Symbol, untyped]
267
+ end
268
+
269
+ class CompletionItemLabelDetails
270
+ def initialize: (**untyped) -> void
271
+ def to_hash: () -> Hash[Symbol, untyped]
272
+ end
273
+
274
+ class TextEdit
275
+ def initialize: (**untyped) -> void
276
+ def to_hash: () -> Hash[Symbol, untyped]
277
+ end
278
+
279
+ class SignatureHelp
280
+ def initialize: (**untyped) -> void
281
+ def to_hash: () -> Hash[Symbol, untyped]
282
+ end
283
+
284
+ class SignatureInformation
285
+ def initialize: (**untyped) -> void
286
+ def to_hash: () -> Hash[Symbol, untyped]
287
+ end
288
+
289
+ class ParameterInformation
290
+ def initialize: (**untyped) -> void
291
+ def to_hash: () -> Hash[Symbol, untyped]
292
+ end
293
+
294
+ class SymbolInformation
295
+ def initialize: (**untyped) -> void
296
+ def to_hash: () -> Hash[Symbol, untyped]
297
+ end
298
+
299
+ class ShowMessageParams
300
+ def initialize: (**untyped) -> void
301
+ def to_hash: () -> Hash[Symbol, untyped]
302
+ end
303
+
304
+ class LogMessageParams
305
+ def initialize: (**untyped) -> void
306
+ def to_hash: () -> Hash[Symbol, untyped]
307
+ end
308
+
309
+ class PublishDiagnosticsParams
310
+ def initialize: (**untyped) -> void
311
+ def to_hash: () -> Hash[Symbol, untyped]
312
+ end
313
+
314
+ class ProgressParams
315
+ def initialize: (**untyped) -> void
316
+ def to_hash: () -> Hash[Symbol, untyped]
317
+ end
318
+
319
+ class WorkDoneProgressBegin
320
+ def initialize: (**untyped) -> void
321
+ def to_hash: () -> Hash[Symbol, untyped]
322
+ end
323
+
324
+ class WorkDoneProgressCreateParams
325
+ def initialize: (**untyped) -> void
326
+ def to_hash: () -> Hash[Symbol, untyped]
327
+ end
328
+ end
329
+
330
+ module Constant
331
+ module TextDocumentSyncKind
332
+ INCREMENTAL: Integer
333
+ end
334
+
335
+ module MessageType
336
+ LOG: Integer
337
+ INFO: Integer
338
+ WARNING: Integer
339
+ ERROR: Integer
340
+ end
341
+
342
+ module ErrorCodes
343
+ REQUEST_CANCELLED: Integer
344
+ INTERNAL_ERROR: Integer
345
+ REQUEST_FAILED: Integer
346
+ end
347
+
348
+ module CompletionItemKind
349
+ TEXT: Integer
350
+ FUNCTION: Integer
351
+ VARIABLE: Integer
352
+ CLASS: Integer
353
+ INTERFACE: Integer
354
+ FIELD: Integer
355
+ CONSTANT: Integer
356
+ SNIPPET: Integer
357
+ end
358
+
359
+ module InsertTextFormat
360
+ SNIPPET: Integer
361
+ end
362
+
363
+ module MarkupKind
364
+ MARKDOWN: String
365
+ end
366
+ end
367
+
368
+ module Transport
369
+ end
370
+ end
371
+ end
372
+
373
+ module Parser
374
+ class SyntaxError < StandardError
375
+ end
376
+ end
@@ -0,0 +1,8 @@
1
+ module Refined
2
+ module Steep
3
+ module Server
4
+ VERSION: String
5
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Refined::Steep::Server::BaseServer do
4
+ def build_message(method:, id: nil, params: {})
5
+ msg = { method: method, params: params }
6
+ msg[:id] = id if id
7
+ msg
8
+ end
9
+
10
+ def encode_message(hash)
11
+ json = JSON.generate(hash)
12
+ "Content-Length: #{json.bytesize}\r\n\r\n#{json}"
13
+ end
14
+
15
+ def create_server(messages)
16
+ raw = messages.map { |m| encode_message(m) }.join
17
+ reader = StringIO.new(raw)
18
+ writer = StringIO.new
19
+
20
+ server = TestServer.new(reader: reader, writer: writer)
21
+ [server, writer]
22
+ end
23
+
24
+ # Concrete subclass for testing
25
+ before do
26
+ stub_const("TestServer", Class.new(Refined::Steep::Server::BaseServer) do
27
+ attr_reader :processed_messages
28
+
29
+ def initialize(**options)
30
+ @processed_messages = []
31
+ super
32
+ end
33
+
34
+ def process_message(message)
35
+ @processed_messages << message
36
+ end
37
+
38
+ private
39
+
40
+ def shutdown
41
+ # no-op
42
+ end
43
+ end)
44
+ end
45
+
46
+ describe "#start" do
47
+ it "processes initialize message synchronously" do
48
+ messages = [build_message(method: "initialize", id: 1, params: { capabilities: {} })]
49
+ server, = create_server(messages)
50
+
51
+ server.start
52
+
53
+ expect(server.processed_messages.size).to eq(1)
54
+ expect(server.processed_messages[0][:method]).to eq("initialize")
55
+ end
56
+
57
+ it "processes initialized notification synchronously" do
58
+ messages = [build_message(method: "initialized")]
59
+ server, = create_server(messages)
60
+
61
+ server.start
62
+
63
+ expect(server.processed_messages.size).to eq(1)
64
+ expect(server.processed_messages[0][:method]).to eq("initialized")
65
+ end
66
+
67
+ it "queues other messages for worker thread" do
68
+ messages = [build_message(method: "textDocument/hover", id: 1)]
69
+ server, = create_server(messages)
70
+
71
+ server.start
72
+ # Give the worker thread time to process
73
+ sleep 0.1
74
+
75
+ expect(server.processed_messages.size).to eq(1)
76
+ expect(server.processed_messages[0][:method]).to eq("textDocument/hover")
77
+ end
78
+
79
+ it "handles shutdown and exit sequence" do
80
+ messages = [
81
+ build_message(method: "shutdown", id: 1),
82
+ build_message(method: "exit"),
83
+ ]
84
+ server, writer = create_server(messages)
85
+
86
+ expect { server.start }.to raise_error(SystemExit) do |error|
87
+ expect(error.status).to eq(0)
88
+ end
89
+
90
+ # Verify shutdown response was written
91
+ output = writer.string
92
+ expect(output).to include('"id":1')
93
+ expect(output).to include('"result":null')
94
+ end
95
+ end
96
+
97
+ describe "#send_message" do
98
+ it "sends result message through outgoing queue" do
99
+ messages = [
100
+ build_message(method: "initialize", id: 1, params: { capabilities: {} }),
101
+ ]
102
+ server, writer = create_server(messages)
103
+
104
+ # Override process_message to send a result
105
+ allow(server).to receive(:process_message) do |msg|
106
+ server.send(:send_message, Refined::Steep::Server::Result.new(id: msg[:id], response: { capabilities: {} }))
107
+ end
108
+
109
+ server.start
110
+ sleep 0.1
111
+
112
+ output = writer.string
113
+ expect(output).to include('"capabilities"')
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Refined::Steep::Server::MessageReader do
4
+ it "reads JSON-RPC messages from IO" do
5
+ json = '{"method":"initialize","params":{}}'
6
+ raw = "Content-Length: #{json.bytesize}\r\n\r\n#{json}"
7
+ io = StringIO.new(raw)
8
+
9
+ reader = Refined::Steep::Server::MessageReader.new(io)
10
+ messages = []
11
+ reader.each_message { |msg| messages << msg }
12
+
13
+ expect(messages.size).to eq(1)
14
+ expect(messages[0][:method]).to eq("initialize")
15
+ end
16
+
17
+ it "reads multiple messages" do
18
+ messages_data = [
19
+ { method: "initialize", params: {} },
20
+ { method: "initialized", params: {} },
21
+ ]
22
+
23
+ raw = messages_data.map do |data|
24
+ json = JSON.generate(data)
25
+ "Content-Length: #{json.bytesize}\r\n\r\n#{json}"
26
+ end.join
27
+
28
+ io = StringIO.new(raw)
29
+ reader = Refined::Steep::Server::MessageReader.new(io)
30
+ messages = []
31
+ reader.each_message { |msg| messages << msg }
32
+
33
+ expect(messages.size).to eq(2)
34
+ expect(messages[0][:method]).to eq("initialize")
35
+ expect(messages[1][:method]).to eq("initialized")
36
+ end
37
+ end
38
+
39
+ RSpec.describe Refined::Steep::Server::MessageWriter do
40
+ it "writes JSON-RPC messages to IO" do
41
+ io = StringIO.new
42
+ writer = Refined::Steep::Server::MessageWriter.new(io)
43
+
44
+ writer.write({ id: 1, result: nil })
45
+
46
+ output = io.string
47
+ expect(output).to match(/Content-Length: \d+\r\n\r\n/)
48
+ json_part = output.sub(/Content-Length: \d+\r\n\r\n/, "")
49
+ parsed = JSON.parse(json_part, symbolize_names: true)
50
+ expect(parsed[:jsonrpc]).to eq("2.0")
51
+ expect(parsed[:id]).to eq(1)
52
+ expect(parsed[:result]).to be_nil
53
+ end
54
+ end