steep 1.10.0 → 2.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +84 -1
- data/CLAUDE.md +114 -0
- data/README.md +1 -1
- data/Rakefile +15 -3
- data/Steepfile +13 -13
- data/lib/steep/annotation_parser.rb +5 -1
- data/lib/steep/annotations_helper.rb +12 -2
- data/lib/steep/ast/node/type_application.rb +22 -16
- data/lib/steep/ast/node/type_assertion.rb +7 -4
- data/lib/steep/ast/types/factory.rb +3 -2
- data/lib/steep/cli.rb +246 -2
- data/lib/steep/daemon/configuration.rb +19 -0
- data/lib/steep/daemon/server.rb +476 -0
- data/lib/steep/daemon.rb +201 -0
- data/lib/steep/diagnostic/ruby.rb +50 -8
- data/lib/steep/diagnostic/signature.rb +31 -8
- data/lib/steep/drivers/check.rb +301 -140
- data/lib/steep/drivers/print_project.rb +9 -10
- data/lib/steep/drivers/query.rb +102 -0
- data/lib/steep/drivers/start_server.rb +19 -0
- data/lib/steep/drivers/stop_server.rb +20 -0
- data/lib/steep/drivers/watch.rb +2 -2
- data/lib/steep/index/rbs_index.rb +38 -13
- data/lib/steep/index/signature_symbol_provider.rb +24 -3
- data/lib/steep/interface/builder.rb +48 -15
- data/lib/steep/interface/shape.rb +13 -5
- data/lib/steep/locator.rb +377 -0
- data/lib/steep/project/dsl.rb +26 -5
- data/lib/steep/project/group.rb +8 -2
- data/lib/steep/project/target.rb +16 -2
- data/lib/steep/project.rb +21 -2
- data/lib/steep/server/base_worker.rb +2 -2
- data/lib/steep/server/change_buffer.rb +2 -1
- data/lib/steep/server/custom_methods.rb +12 -0
- data/lib/steep/server/inline_source_change_detector.rb +94 -0
- data/lib/steep/server/interaction_worker.rb +51 -74
- data/lib/steep/server/lsp_formatter.rb +48 -12
- data/lib/steep/server/master.rb +100 -18
- data/lib/steep/server/target_group_files.rb +124 -151
- data/lib/steep/server/type_check_controller.rb +276 -123
- data/lib/steep/server/type_check_worker.rb +104 -3
- data/lib/steep/services/completion_provider/rbs.rb +74 -0
- data/lib/steep/services/completion_provider/ruby.rb +652 -0
- data/lib/steep/services/completion_provider/type_name.rb +243 -0
- data/lib/steep/services/completion_provider.rb +39 -662
- data/lib/steep/services/content_change.rb +14 -1
- data/lib/steep/services/file_loader.rb +4 -2
- data/lib/steep/services/goto_service.rb +271 -68
- data/lib/steep/services/hover_provider/content.rb +67 -0
- data/lib/steep/services/hover_provider/rbs.rb +8 -9
- data/lib/steep/services/hover_provider/ruby.rb +123 -64
- data/lib/steep/services/hover_provider/singleton_methods.rb +4 -0
- data/lib/steep/services/signature_service.rb +129 -54
- data/lib/steep/services/type_check_service.rb +72 -27
- data/lib/steep/signature/validator.rb +30 -18
- data/lib/steep/source/ignore_ranges.rb +14 -4
- data/lib/steep/source.rb +16 -2
- data/lib/steep/tagged_logging.rb +39 -0
- data/lib/steep/type_construction.rb +94 -21
- data/lib/steep/type_inference/block_params.rb +7 -7
- data/lib/steep/type_inference/context.rb +4 -2
- data/lib/steep/type_inference/logic_type_interpreter.rb +21 -3
- data/lib/steep/type_inference/method_call.rb +4 -0
- data/lib/steep/type_inference/type_env.rb +1 -1
- data/lib/steep/typing.rb +0 -2
- data/lib/steep/version.rb +1 -1
- data/lib/steep.rb +42 -32
- data/manual/ruby-diagnostics.md +67 -0
- data/sample/Steepfile +1 -0
- data/sample/lib/conference.rb +1 -0
- data/sample/lib/deprecated.rb +6 -0
- data/sample/lib/inline.rb +43 -0
- data/sample/sig/generics.rbs +3 -0
- data/steep.gemspec +4 -5
- metadata +26 -26
- data/lib/steep/services/type_name_completion.rb +0 -236
|
@@ -7,10 +7,12 @@ module Steep
|
|
|
7
7
|
|
|
8
8
|
WorkspaceSymbolJob = _ = Struct.new(:query, :id, keyword_init: true)
|
|
9
9
|
StatsJob = _ = Struct.new(:id, keyword_init: true)
|
|
10
|
+
QueryDefinitionJob = _ = Struct.new(:id, :name, keyword_init: true)
|
|
10
11
|
StartTypeCheckJob = _ = Struct.new(:guid, :changes, keyword_init: true)
|
|
11
12
|
TypeCheckCodeJob = _ = Struct.new(:guid, :path, :target, keyword_init: true)
|
|
12
13
|
ValidateAppSignatureJob = _ = Struct.new(:guid, :path, :target, keyword_init: true)
|
|
13
14
|
ValidateLibrarySignatureJob = _ = Struct.new(:guid, :path, :target, keyword_init: true)
|
|
15
|
+
TypeCheckInlineCodeJob = _ = Struct.new(:guid, :path, :target, keyword_init: true)
|
|
14
16
|
class GotoJob < Struct.new(:id, :kind, :params, keyword_init: true)
|
|
15
17
|
def self.implementation(id:, params:)
|
|
16
18
|
new(
|
|
@@ -52,6 +54,7 @@ module Steep
|
|
|
52
54
|
include ChangeBuffer
|
|
53
55
|
|
|
54
56
|
attr_reader :io_socket
|
|
57
|
+
attr_reader :need_to_warmup
|
|
55
58
|
|
|
56
59
|
def initialize(project:, reader:, writer:, assignment:, commandline_args:, io_socket: nil, buffered_changes: nil, service: nil)
|
|
57
60
|
super(project: project, reader: reader, writer: writer)
|
|
@@ -65,6 +68,7 @@ module Steep
|
|
|
65
68
|
@io_socket = io_socket
|
|
66
69
|
@service = service if service
|
|
67
70
|
@child_pids = []
|
|
71
|
+
@need_to_warmup = defined?(Process.warmup)
|
|
68
72
|
|
|
69
73
|
if io_socket
|
|
70
74
|
Signal.trap "SIGCHLD" do
|
|
@@ -105,6 +109,9 @@ module Steep
|
|
|
105
109
|
when CustomMethods::TypeCheck__Start::METHOD
|
|
106
110
|
params = request[:params] #: CustomMethods::TypeCheck__Start::params
|
|
107
111
|
enqueue_typecheck_jobs(params)
|
|
112
|
+
when CustomMethods::Query__Definition::METHOD
|
|
113
|
+
params = request[:params] #: CustomMethods::Query__Definition::params
|
|
114
|
+
queue << QueryDefinitionJob.new(id: request[:id], name: params[:name])
|
|
108
115
|
when "textDocument/definition"
|
|
109
116
|
queue << GotoJob.definition(id: request[:id], params: request[:params])
|
|
110
117
|
when "textDocument/implementation"
|
|
@@ -118,6 +125,11 @@ module Steep
|
|
|
118
125
|
stdin = io_socket.recv_io
|
|
119
126
|
stdout = io_socket.recv_io
|
|
120
127
|
|
|
128
|
+
if need_to_warmup
|
|
129
|
+
Process.warmup
|
|
130
|
+
@need_to_warmup = false
|
|
131
|
+
end
|
|
132
|
+
|
|
121
133
|
if pid = fork
|
|
122
134
|
stdin.close
|
|
123
135
|
stdout.close
|
|
@@ -137,9 +149,11 @@ module Steep
|
|
|
137
149
|
|
|
138
150
|
worker = self.class.new(project: project, reader: reader, writer: writer, assignment: assignment, commandline_args: commandline_args, io_socket: nil, buffered_changes: buffered_changes, service: service)
|
|
139
151
|
|
|
140
|
-
tags = Steep.logger.
|
|
141
|
-
tags
|
|
142
|
-
|
|
152
|
+
tags = Steep.logger.current_tags.dup
|
|
153
|
+
if (index = tags.find_index("typecheck:typecheck@0"))
|
|
154
|
+
tags[index] = "typecheck:typecheck@#{index}-reforked"
|
|
155
|
+
end
|
|
156
|
+
Steep.logger.push_tags(*tags)
|
|
143
157
|
worker.run()
|
|
144
158
|
|
|
145
159
|
raise "unreachable"
|
|
@@ -165,10 +179,12 @@ module Steep
|
|
|
165
179
|
libraries = params[:library_uris].map {|target_name, uri| [targets.fetch(target_name), Steep::PathHelper.to_pathname!(uri)] } #: Array[[Project::Target, Pathname]]
|
|
166
180
|
signatures = params[:signature_uris].map {|target_name, uri| [targets.fetch(target_name), Steep::PathHelper.to_pathname!(uri)] } #: Array[[Project::Target, Pathname]]
|
|
167
181
|
codes = params[:code_uris].map {|target_name, uri| [targets.fetch(target_name), Steep::PathHelper.to_pathname!(uri)] } #: Array[[Project::Target, Pathname]]
|
|
182
|
+
inlines = params[:inline_uris].map {|target_name, uri| [targets.fetch(target_name), Steep::PathHelper.to_pathname!(uri)] } #: Array[[Project::Target, Pathname]]
|
|
168
183
|
|
|
169
184
|
priority_libs, non_priority_libs = libraries.partition {|_, path| priority_paths.include?(path) }
|
|
170
185
|
priority_sigs, non_priority_sigs = signatures.partition {|_, path| priority_paths.include?(path) }
|
|
171
186
|
priority_codes, non_priority_codes = codes.partition {|_, path| priority_paths.include?(path) }
|
|
187
|
+
priority_inlines, non_priority_inlines = inlines.partition {|_, path| priority_paths.include?(path) }
|
|
172
188
|
|
|
173
189
|
priority_codes.each do |target, path|
|
|
174
190
|
Steep.logger.info { "Enqueueing TypeCheckCodeJob for guid=#{guid}, path=#{path}, target=#{target.name}" }
|
|
@@ -185,6 +201,11 @@ module Steep
|
|
|
185
201
|
queue << ValidateLibrarySignatureJob.new(guid: guid, path: path, target: target)
|
|
186
202
|
end
|
|
187
203
|
|
|
204
|
+
priority_inlines.each do |target, path|
|
|
205
|
+
Steep.logger.info { "Enqueueing TypeCheckInlineCodeJob for guid=#{guid}, path=#{path}, target=#{target.name}" }
|
|
206
|
+
queue << TypeCheckInlineCodeJob.new(guid: guid, path: path, target: target)
|
|
207
|
+
end
|
|
208
|
+
|
|
188
209
|
non_priority_codes.each do |target, path|
|
|
189
210
|
Steep.logger.info { "Enqueueing TypeCheckCodeJob for guid=#{guid}, path=#{path}, target=#{target.name}" }
|
|
190
211
|
queue << TypeCheckCodeJob.new(guid: guid, path: path, target: target)
|
|
@@ -199,6 +220,11 @@ module Steep
|
|
|
199
220
|
Steep.logger.info { "Enqueueing ValidateLibrarySignatureJob for guid=#{guid}, path=#{path}, target=#{target.name}" }
|
|
200
221
|
queue << ValidateLibrarySignatureJob.new(guid: guid, path: path, target: target)
|
|
201
222
|
end
|
|
223
|
+
|
|
224
|
+
non_priority_inlines.each do |target, path|
|
|
225
|
+
Steep.logger.info { "Enqueueing TypeCheckInlineCodeJob for guid=#{guid}, path=#{path}, target=#{target.name}" }
|
|
226
|
+
queue << TypeCheckInlineCodeJob.new(guid: guid, path: path, target: target)
|
|
227
|
+
end
|
|
202
228
|
end
|
|
203
229
|
|
|
204
230
|
def handle_job(job)
|
|
@@ -243,6 +269,25 @@ module Steep
|
|
|
243
269
|
typecheck_progress(path: job.path, guid: job.guid, target: job.target, diagnostics: diagnostics&.filter_map { formatter.format(_1) })
|
|
244
270
|
end
|
|
245
271
|
|
|
272
|
+
when TypeCheckInlineCodeJob
|
|
273
|
+
if job.guid == current_type_check_guid
|
|
274
|
+
Steep.logger.info { "Processing TypeCheckInlineCodeJob for guid=#{job.guid}, path=#{job.path}, target=#{job.target.name}" }
|
|
275
|
+
group_target = project.group_for_inline_source_path(job.path) || job.target
|
|
276
|
+
formatter = Diagnostic::LSPFormatter.new(group_target.code_diagnostics_config)
|
|
277
|
+
relative_path = project.relative_path(job.path)
|
|
278
|
+
diagnostics = service.typecheck_source(path: relative_path, target: job.target) #: Array[Diagnostic::Ruby::Base | Diagnostic::Signature::Base] | nil
|
|
279
|
+
signature_diagnostics = service.validate_signature(path: relative_path, target: job.target)
|
|
280
|
+
if diagnostics
|
|
281
|
+
diagnostics.concat(signature_diagnostics)
|
|
282
|
+
else
|
|
283
|
+
unless signature_diagnostics.empty?
|
|
284
|
+
diagnostics = signature_diagnostics
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
typecheck_progress(path: job.path, guid: job.guid, target: job.target, diagnostics: diagnostics&.filter_map { formatter.format(_1) })
|
|
289
|
+
end
|
|
290
|
+
|
|
246
291
|
when WorkspaceSymbolJob
|
|
247
292
|
writer.write(
|
|
248
293
|
id: job.id,
|
|
@@ -258,6 +303,10 @@ module Steep
|
|
|
258
303
|
id: job.id,
|
|
259
304
|
result: goto(job)
|
|
260
305
|
)
|
|
306
|
+
when QueryDefinitionJob
|
|
307
|
+
writer.write(
|
|
308
|
+
CustomMethods::Query__Definition.response(job.id, query_definition_result(job.name))
|
|
309
|
+
)
|
|
261
310
|
end
|
|
262
311
|
end
|
|
263
312
|
|
|
@@ -309,6 +358,58 @@ module Steep
|
|
|
309
358
|
end
|
|
310
359
|
end
|
|
311
360
|
|
|
361
|
+
def query_definition_result(name_string)
|
|
362
|
+
name = Services::GotoService.parse_name(name_string)
|
|
363
|
+
|
|
364
|
+
kind =
|
|
365
|
+
case name
|
|
366
|
+
when RBS::TypeName
|
|
367
|
+
"type_name"
|
|
368
|
+
when InstanceMethodName
|
|
369
|
+
"instance_method"
|
|
370
|
+
when SingletonMethodName
|
|
371
|
+
"singleton_method"
|
|
372
|
+
else
|
|
373
|
+
"unknown"
|
|
374
|
+
end #: CustomMethods::Query__Definition::kind
|
|
375
|
+
|
|
376
|
+
locations = [] #: Array[CustomMethods::Query__Definition::location]
|
|
377
|
+
|
|
378
|
+
if name
|
|
379
|
+
goto_service = Services::GotoService.new(type_check: service, assignment: assignment)
|
|
380
|
+
goto_service.query_definition(name).each do |loc|
|
|
381
|
+
case loc
|
|
382
|
+
when RBS::Location
|
|
383
|
+
path = Pathname(loc.buffer.name)
|
|
384
|
+
source = "rbs" #: CustomMethods::Query__Definition::source
|
|
385
|
+
if path.extname == ".rb"
|
|
386
|
+
source = "ruby" #: CustomMethods::Query__Definition::source
|
|
387
|
+
end
|
|
388
|
+
path = project.absolute_path(path)
|
|
389
|
+
locations << {
|
|
390
|
+
uri: Steep::PathHelper.to_uri(path).to_s,
|
|
391
|
+
range: loc.as_lsp_range,
|
|
392
|
+
source: source
|
|
393
|
+
}
|
|
394
|
+
else
|
|
395
|
+
path = Pathname(loc.source_buffer.name)
|
|
396
|
+
path = project.absolute_path(path)
|
|
397
|
+
locations << {
|
|
398
|
+
uri: Steep::PathHelper.to_uri(path).to_s,
|
|
399
|
+
range: loc.as_lsp_range,
|
|
400
|
+
source: "ruby"
|
|
401
|
+
}
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
{
|
|
407
|
+
name: name_string,
|
|
408
|
+
kind: kind,
|
|
409
|
+
locations: locations
|
|
410
|
+
}
|
|
411
|
+
end
|
|
412
|
+
|
|
312
413
|
def goto(job)
|
|
313
414
|
path = Steep::PathHelper.to_pathname(job.params[:textDocument][:uri]) or return []
|
|
314
415
|
line = job.params[:position][:line] + 1
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module Steep
|
|
2
|
+
module Services
|
|
3
|
+
module CompletionProvider
|
|
4
|
+
class RBS
|
|
5
|
+
attr_reader :project, :signature, :path
|
|
6
|
+
|
|
7
|
+
def initialize(path, signature)
|
|
8
|
+
@path = path
|
|
9
|
+
@signature = signature
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def run(line, column)
|
|
13
|
+
context = nil #: RBS::Resolver::context
|
|
14
|
+
|
|
15
|
+
case signature.status
|
|
16
|
+
when Services::SignatureService::SyntaxErrorStatus, Services::SignatureService::AncestorErrorStatus
|
|
17
|
+
if source = signature.latest_env.each_rbs_source.find { _1.buffer.name == path }
|
|
18
|
+
dirs = source.directives
|
|
19
|
+
else
|
|
20
|
+
dirs = [] #: Array[RBS::AST::Directives::t]
|
|
21
|
+
end
|
|
22
|
+
else
|
|
23
|
+
file = signature.files.fetch(path)
|
|
24
|
+
file.is_a?(Services::SignatureService::RBSFileStatus) or raise
|
|
25
|
+
source = file.source
|
|
26
|
+
source.is_a?(::RBS::Source::RBS) or raise
|
|
27
|
+
buffer = source.buffer
|
|
28
|
+
dirs = source.directives
|
|
29
|
+
decls = source.declarations
|
|
30
|
+
|
|
31
|
+
locator = ::RBS::Locator.new(buffer: buffer, dirs: dirs, decls: decls)
|
|
32
|
+
|
|
33
|
+
_hd, tail = locator.find2(line: line, column: column)
|
|
34
|
+
tail ||= [] #: Array[RBS::Locator::component]
|
|
35
|
+
|
|
36
|
+
tail.reverse_each do |t|
|
|
37
|
+
case t
|
|
38
|
+
when ::RBS::AST::Declarations::Module, ::RBS::AST::Declarations::Class
|
|
39
|
+
if (last_type_name = context&.[](1)).is_a?(::RBS::TypeName)
|
|
40
|
+
context = [context, last_type_name + t.name]
|
|
41
|
+
else
|
|
42
|
+
context = [context, t.name.absolute!]
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
content =
|
|
49
|
+
case file = signature.files.fetch(path)
|
|
50
|
+
when ::RBS::Source::Ruby
|
|
51
|
+
file.buffer.content
|
|
52
|
+
when Services::SignatureService::RBSFileStatus
|
|
53
|
+
file.content
|
|
54
|
+
end
|
|
55
|
+
buffer = ::RBS::Buffer.new(name: path, content: content)
|
|
56
|
+
prefix = Services::CompletionProvider::TypeName::Prefix.parse(buffer, line: line, column: column)
|
|
57
|
+
|
|
58
|
+
completion = Services::CompletionProvider::TypeName.new(env: signature.latest_env, context: context, dirs: dirs)
|
|
59
|
+
type_names = completion.find_type_names(prefix)
|
|
60
|
+
prefix_size = prefix ? prefix.size : 0
|
|
61
|
+
|
|
62
|
+
[
|
|
63
|
+
prefix_size,
|
|
64
|
+
type_names.filter_map do |type_name|
|
|
65
|
+
if (absolute_name, relative_name = completion.resolve_name_in_context(type_name))
|
|
66
|
+
[absolute_name, relative_name]
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
]
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|