steep 0.15.0 → 0.16.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 498c3c95c9c03e8ef047c774aff47158d8681e2ae485d5b0449a74fae33cd06b
4
- data.tar.gz: 64e9572dc4ec5b5fea6035c1a1fbb7241a758f76c5d328d2ac0011c37e176044
3
+ metadata.gz: b4dda8122216dc9af384ceba2c84f7cb1076df5be7060d2aa32dbaff32140ed6
4
+ data.tar.gz: d4ce7351acc02b06cd608a10639c00bc8bb1bed8ca81596bb1ed718112392a68
5
5
  SHA512:
6
- metadata.gz: acef7a13109ecd0660d72dff4559b4432f96c62f79aa4dcb762722f9a96678bb6f68f0bd288017381c6ebef65f9648a24f2b349d2af28f1a6f0c66560d56aee6
7
- data.tar.gz: 9d31c49a37770c7493cefcde0f608ee6b6c94bd5fa1935c3ce963bae70b3e526b678000ae1b2b9f59d94a775c5103cbabf7effd480262725db476f70e6124dcf
6
+ metadata.gz: 0bc09bf14d4fbfe8cea15d8f939efd2f9b75672a25fdfbbd19011461001786d15e7d84fadfa37cf67a0303f742c6b2a718855095603119276d1154c4b97cc673
7
+ data.tar.gz: 7d3d1d4f8acb18de067228fe38d52a7cb62dbd5049555fa748d96226c6ce1906fc49e034f3e19ae346c215ee89a715e04e0138b985cbedcc89b922499adc0a08
data/.gitmodules CHANGED
@@ -1,3 +1,3 @@
1
1
  [submodule "vendor/ruby-signature"]
2
2
  path = vendor/ruby-signature
3
- url = https://github.com/ruby/ruby-signature.git
3
+ url = https://github.com/ruby/rbs.git
data/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.16.0 (2020-05-19)
6
+
7
+ * Spawn workers for type check performance ([#137](https://github.com/soutaro/steep/pull/137))
8
+ * Fix `check` and `signature` methods in Steepfile ([8f3e4c7](https://github.com/soutaro/steep/pull/137/commits/8f3e4c75b29ac26920f02294be06d6c68dbd4dca))
9
+
5
10
  ## 0.15.0 (2020-05-05)
6
11
 
7
12
  * Add type checking configuration to dsl ([#132](https://github.com/soutaro/steep/pull/132))
data/lib/steep/cli.rb CHANGED
@@ -34,7 +34,7 @@ module Steep
34
34
 
35
35
  def setup_command
36
36
  @command = argv.shift&.to_sym
37
- if CLI.available_commands.include?(@command)
37
+ if CLI.available_commands.include?(@command) || @command == :worker
38
38
  true
39
39
  else
40
40
  stderr.puts "Unknown command: #{command}"
@@ -159,5 +159,20 @@ module Steep
159
159
  stdout.puts Steep::VERSION
160
160
  0
161
161
  end
162
+
163
+ def process_worker
164
+ Drivers::Worker.new(stdout: stdout, stderr: stderr, stdin: stdin).tap do |command|
165
+ OptionParser.new do |opts|
166
+ opts.banner = "Usage: steep worker [options] [dir]"
167
+ handle_logging_options opts
168
+
169
+ opts.on("--interaction") { command.worker_type = :interaction }
170
+ opts.on("--code") { command.worker_type = :code }
171
+ opts.on("--signature") { command.worker_type = :signature }
172
+ opts.on("--steepfile=PATH") {|path| command.steepfile = Pathname(path) }
173
+ opts.on("--name=NAME") {|name| command.worker_name = name }
174
+ end.parse!(argv)
175
+ end.run
176
+ end
162
177
  end
163
178
  end
@@ -25,7 +25,7 @@ module Steep
25
25
 
26
26
  project.targets.each do |target|
27
27
  Steep.logger.tagged "target=#{target.name}" do
28
- target.load_signatures do |_, subtyping, _|
28
+ target.load_signatures(validate: false) do |_, subtyping, _|
29
29
  case (status = target.status)
30
30
  when nil # status set on error cases
31
31
  target.source_files.each_value do |file|
@@ -33,11 +33,6 @@ module Steep
33
33
  @project or raise "Empty #project"
34
34
  end
35
35
 
36
- def enqueue_type_check(version)
37
- @latest_update_version = version
38
- type_check_queue << TypeCheckRequest.new(version: version)
39
- end
40
-
41
36
  def run
42
37
  @project = load_config()
43
38
 
@@ -45,466 +40,22 @@ module Steep
45
40
  loader.load_sources([])
46
41
  loader.load_signatures()
47
42
 
48
- start_type_check()
49
-
50
- reader.read do |request|
51
- Steep.logger.tagged "lsp" do
52
- Steep.logger.debug { "Received a request: request=#{request.to_json}" }
53
- handle_request(request) do |id, result|
54
- if id
55
- write_mutex.synchronize do
56
- Steep.logger.debug { "Writing response to #{id}: #{result.to_json}" }
57
- writer.write(id: id, result: result)
58
- end
59
- end
60
- end
61
- end
62
- end
63
-
64
- 0
65
- end
66
-
67
- def write(method:, params:)
68
- write_mutex.synchronize do
69
- Steep.logger.debug { "Sending request: method=#{method}, params=#{params.to_json}"}
70
- writer.write(method: method, params: params)
71
- end
72
- end
73
-
74
- def handle_request(request)
75
- id = request[:id]
76
- method = request[:method].to_sym
77
-
78
- Steep.logger.tagged "id=#{id}, method=#{method}" do
79
- case method
80
- when :initialize
81
- yield id, LanguageServer::Protocol::Interface::InitializeResult.new(
82
- capabilities: LanguageServer::Protocol::Interface::ServerCapabilities.new(
83
- text_document_sync: LanguageServer::Protocol::Interface::TextDocumentSyncOptions.new(
84
- change: LanguageServer::Protocol::Constant::TextDocumentSyncKind::FULL
85
- ),
86
- hover_provider: true,
87
- completion_provider: LanguageServer::Protocol::Interface::CompletionOptions.new(
88
- trigger_characters: [".", "@"],
89
- )
90
- )
91
- )
92
-
93
- enqueue_type_check nil
94
-
95
- when :"textDocument/completion"
96
- Steep.logger.error request.inspect
97
- begin
98
- params = request[:params]
99
- uri = URI.parse(params[:textDocument][:uri])
100
- path = project.relative_path(Pathname(uri.path))
101
- target = project.targets.find {|target| target.source_file?(path) }
102
- case (status = target&.status)
103
- when Project::Target::TypeCheckStatus
104
- subtyping = status.subtyping
105
- source = target.source_files[path]
106
-
107
- line, column = params[:position].yield_self {|hash| [hash[:line]+1, hash[:character]] }
108
- trigger = params[:context][:triggerCharacter]
109
-
110
- Steep.logger.error "line: #{line}, column: #{column}, trigger: #{trigger}"
111
-
112
- provider = Project::CompletionProvider.new(source_text: source.content, path: path, subtyping: subtyping)
113
- items = begin
114
- provider.run(line: line, column: column)
115
- rescue Parser::SyntaxError
116
- []
117
- end
118
-
119
- completion_items = items.map do |item|
120
- format_completion_item(item)
121
- end
122
-
123
- Steep.logger.debug "items = #{completion_items.inspect}"
124
-
125
- yield id, LanguageServer::Protocol::Interface::CompletionList.new(
126
- is_incomplete: false,
127
- items: completion_items
128
- )
129
- end
130
-
131
- rescue Typing::UnknownNodeError => exn
132
- Steep.log_error exn, message: "Failed to compute completion: #{exn.inspect}"
133
- yield id, nil
134
- end
135
-
136
- when :"textDocument/didChange"
137
- uri = URI.parse(request[:params][:textDocument][:uri])
138
- path = project.relative_path(Pathname(uri.path))
139
- text = request[:params][:contentChanges][0][:text]
140
-
141
- Steep.logger.debug { "path=#{path}, content=#{text.lines.first&.chomp}..." }
142
-
143
- project.targets.each do |target|
144
- Steep.logger.tagged "target=#{target.name}" do
145
- case
146
- when target.source_file?(path)
147
- if text.empty? && !path.file?
148
- Steep.logger.info { "Deleting source file: #{path}..." }
149
- target.remove_source(path)
150
- report_diagnostics path, []
151
- else
152
- Steep.logger.info { "Updating source file: #{path}..." }
153
- target.update_source(path, text)
154
- end
155
- when target.possible_source_file?(path)
156
- Steep.logger.info { "Adding source file: #{path}..." }
157
- target.add_source(path, text)
158
- when target.signature_file?(path)
159
- if text.empty? && !path.file?
160
- Steep.logger.info { "Deleting signature file: #{path}..." }
161
- target.remove_signature(path)
162
- report_diagnostics path, []
163
- else
164
- Steep.logger.info { "Updating signature file: #{path}..." }
165
- target.update_signature(path, text)
166
- end
167
- when target.possible_signature_file?(path)
168
- Steep.logger.info { "Adding signature file: #{path}..." }
169
- target.add_signature(path, text)
170
- end
171
- end
172
- end
173
-
174
- version = request[:params][:textDocument][:version]
175
- enqueue_type_check version
176
- when :"textDocument/hover"
177
- uri = URI.parse(request[:params][:textDocument][:uri])
178
- path = project.relative_path(Pathname(uri.path))
179
- line = request[:params][:position][:line]
180
- column = request[:params][:position][:character]
181
-
182
- yield id, response_to_hover(path: path, line: line, column: column)
183
-
184
- when :shutdown
185
- yield id, nil
186
-
187
- when :exit
188
- type_check_queue << nil
189
- type_check_thread.join
190
- exit
191
- end
192
- end
193
- end
194
-
195
- def start_type_check
196
- @type_check_thread = Thread.start do
197
- while request = type_check_queue.deq
198
- if @latest_update_version == nil || @latest_update_version == request.version
199
- begin
200
- run_type_check()
201
- rescue => exn
202
- Steep.log_error exn
203
- end
204
- end
205
- end
206
- end
207
- end
208
-
209
- def run_type_check()
210
- Steep.logger.tagged "#run_type_check" do
211
- Steep.logger.info { "Running type check..." }
212
- type_check project
213
-
214
- Steep.logger.info { "Sending diagnostics..." }
215
- project.targets.each do |target|
216
- Steep.logger.tagged "target=#{target.name}, status=#{target.status.class}" do
217
- Steep.logger.info { "Clearing signature diagnostics..." }
218
- target.signature_files.each_value do |file|
219
- report_diagnostics file.path, []
220
- end
221
-
222
- case (status = target.status)
223
- when Project::Target::SignatureValidationErrorStatus
224
- Steep.logger.info { "Signature validation error" }
225
- status.errors.group_by(&:path).each do |path, errors|
226
- diagnostics = errors.map {|error| diagnostic_for_validation_error(error) }
227
- report_diagnostics path, diagnostics
228
- end
229
- when Project::Target::TypeCheckStatus
230
- Steep.logger.info { "Type check" }
231
- status.type_check_sources.each do |source|
232
- diagnostics = case source.status
233
- when Project::SourceFile::TypeCheckStatus
234
- source.errors.map {|error| diagnostic_for_type_error(error) }
235
- when Project::SourceFile::AnnotationSyntaxErrorStatus
236
- [diagnostics_raw(source.status.error.message, source.status.location)]
237
- when Project::SourceFile::ParseErrorStatus
238
- []
239
- when Project::SourceFile::TypeCheckErrorStatus
240
- Steep.log_error source.status.error
241
- []
242
- end
243
-
244
- if diagnostics
245
- report_diagnostics source.path, diagnostics
246
- end
247
- end
248
- when Project::Target::SignatureSyntaxErrorStatus
249
- Steep.logger.info { "Signature syntax error" }
250
- end
251
- end
252
- end
253
- end
254
- end
255
-
256
- def report_diagnostics(path, diagnostics)
257
- Steep.logger.info { "Reporting #{diagnostics.size} diagnostics for #{path}..." }
258
- write(
259
- method: :"textDocument/publishDiagnostics",
260
- params: LanguageServer::Protocol::Interface::PublishDiagnosticsParams.new(
261
- uri: URI.parse(project.absolute_path(path).to_s).tap {|uri| uri.scheme = "file"},
262
- diagnostics: diagnostics,
263
- )
264
- )
265
- end
266
-
267
- def diagnostic_for_validation_error(error)
268
- LanguageServer::Protocol::Interface::Diagnostic.new(
269
- message: StringIO.new("").tap {|io| error.puts(io) }.string,
270
- severity: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
271
- range: LanguageServer::Protocol::Interface::Range.new(
272
- start: LanguageServer::Protocol::Interface::Position.new(
273
- line: error.location.start_line - 1,
274
- character: error.location.start_column,
275
- ),
276
- end: LanguageServer::Protocol::Interface::Position.new(
277
- line: error.location.end_line - 1,
278
- character: error.location.end_column,
279
- ),
280
- )
281
- )
282
- end
283
-
284
- def diagnostics_raw(message, loc)
285
- LanguageServer::Protocol::Interface::Diagnostic.new(
286
- message: message,
287
- severity: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
288
- range: LanguageServer::Protocol::Interface::Range.new(
289
- start: LanguageServer::Protocol::Interface::Position.new(
290
- line: loc.start_line - 1,
291
- character: loc.start_column,
292
- ),
293
- end: LanguageServer::Protocol::Interface::Position.new(
294
- line: loc.end_line - 1,
295
- character: loc.end_column,
296
- ),
297
- )
298
- )
299
- end
300
-
301
- def diagnostic_for_type_error(error)
302
- LanguageServer::Protocol::Interface::Diagnostic.new(
303
- message: error.to_s,
304
- severity: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
305
- range: LanguageServer::Protocol::Interface::Range.new(
306
- start: LanguageServer::Protocol::Interface::Position.new(
307
- line: error.node.loc.line - 1,
308
- character: error.node.loc.column,
309
- ),
310
- end: LanguageServer::Protocol::Interface::Position.new(
311
- line: error.node.loc.last_line - 1,
312
- character: error.node.loc.last_column,
313
- ),
314
- )
43
+ interaction_worker = Server::WorkerProcess.spawn_worker(:interaction, name: "interaction", steepfile: project.steepfile_path)
44
+ signature_worker = Server::WorkerProcess.spawn_worker(:signature, name: "signature", steepfile: project.steepfile_path)
45
+ code_workers = Server::WorkerProcess.spawn_code_workers(steepfile: project.steepfile_path)
46
+
47
+ master = Server::Master.new(
48
+ project: project,
49
+ reader: reader,
50
+ writer: writer,
51
+ interaction_worker: interaction_worker,
52
+ signature_worker: signature_worker,
53
+ code_workers: code_workers
315
54
  )
316
- end
317
-
318
- def response_to_hover(path:, line:, column:)
319
- Steep.logger.info { "path=#{path}, line=#{line}, column=#{column}" }
320
-
321
- hover = Project::HoverContent.new(project: project)
322
- content = hover.content_for(path: path, line: line+1, column: column+1)
323
- if content
324
- range = content.location.yield_self do |location|
325
- start_position = { line: location.line - 1, character: location.column }
326
- end_position = { line: location.last_line - 1, character: location.last_column }
327
- { start: start_position, end: end_position }
328
- end
329
55
 
330
- LanguageServer::Protocol::Interface::Hover.new(
331
- contents: { kind: "markdown", value: format_hover(content) },
332
- range: range
333
- )
334
- end
335
- rescue Typing::UnknownNodeError => exn
336
- Steep.log_error exn, message: "Failed to compute hover: #{exn.inspect}"
337
- nil
338
- end
339
-
340
- def format_hover(content)
341
- case content
342
- when Project::HoverContent::VariableContent
343
- "`#{content.name}`: `#{content.type.to_s}`"
344
- when Project::HoverContent::MethodCallContent
345
- method_name = case content.method_name
346
- when Project::HoverContent::InstanceMethodName
347
- "#{content.method_name.class_name}##{content.method_name.method_name}"
348
- when Project::HoverContent::SingletonMethodName
349
- "#{content.method_name.class_name}.#{content.method_name.method_name}"
350
- else
351
- nil
352
- end
353
-
354
- if method_name
355
- string = <<HOVER
356
- ```
357
- #{method_name} ~> #{content.type}
358
- ```
359
- HOVER
360
- if content.definition
361
- if content.definition.comment
362
- string << "\n----\n\n#{content.definition.comment.string}"
363
- end
364
-
365
- string << "\n----\n\n#{content.definition.method_types.map {|x| "- `#{x}`\n" }.join()}"
366
- end
367
- else
368
- "`#{content.type}`"
369
- end
370
- when Project::HoverContent::DefinitionContent
371
- string = <<HOVER
372
- ```
373
- def #{content.method_name}: #{content.method_type}
374
- ```
375
- HOVER
376
- if (comment = content.definition.comment)
377
- string << "\n----\n\n#{comment.string}\n"
378
- end
379
-
380
- if content.definition.method_types.size > 1
381
- string << "\n----\n\n#{content.definition.method_types.map {|x| "- `#{x}`\n" }.join()}"
382
- end
383
-
384
- string
385
- when Project::HoverContent::TypeContent
386
- "`#{content.type}`"
387
- end
388
- end
389
-
390
- def format_completion_item(item)
391
- range = LanguageServer::Protocol::Interface::Range.new(
392
- start: LanguageServer::Protocol::Interface::Position.new(
393
- line: item.range.start.line-1,
394
- character: item.range.start.column
395
- ),
396
- end: LanguageServer::Protocol::Interface::Position.new(
397
- line: item.range.end.line-1,
398
- character: item.range.end.column
399
- )
400
- )
56
+ master.start()
401
57
 
402
- case item
403
- when Project::CompletionProvider::LocalVariableItem
404
- LanguageServer::Protocol::Interface::CompletionItem.new(
405
- label: item.identifier,
406
- kind: LanguageServer::Protocol::Constant::CompletionItemKind::VARIABLE,
407
- detail: "#{item.identifier}: #{item.type}",
408
- text_edit: LanguageServer::Protocol::Interface::TextEdit.new(
409
- range: range,
410
- new_text: "#{item.identifier}"
411
- )
412
- )
413
- when Project::CompletionProvider::MethodNameItem
414
- label = "def #{item.identifier}: #{item.method_type}"
415
- method_type_snippet = method_type_to_snippet(item.method_type)
416
- LanguageServer::Protocol::Interface::CompletionItem.new(
417
- label: label,
418
- kind: LanguageServer::Protocol::Constant::CompletionItemKind::METHOD,
419
- text_edit: LanguageServer::Protocol::Interface::TextEdit.new(
420
- new_text: "#{item.identifier}#{method_type_snippet}",
421
- range: range
422
- ),
423
- documentation: item.definition.comment&.string,
424
- insert_text_format: LanguageServer::Protocol::Constant::InsertTextFormat::SNIPPET
425
- )
426
- when Project::CompletionProvider::InstanceVariableItem
427
- label = "#{item.identifier}: #{item.type}"
428
- LanguageServer::Protocol::Interface::CompletionItem.new(
429
- label: label,
430
- kind: LanguageServer::Protocol::Constant::CompletionItemKind::FIELD,
431
- text_edit: LanguageServer::Protocol::Interface::TextEdit.new(
432
- range: range,
433
- new_text: item.identifier,
434
- ),
435
- insert_text_format: LanguageServer::Protocol::Constant::InsertTextFormat::SNIPPET
436
- )
437
- end
438
- end
439
-
440
- def method_type_to_snippet(method_type)
441
- params = if method_type.type.each_param.count == 0
442
- ""
443
- else
444
- "(#{params_to_snippet(method_type.type)})"
445
- end
446
-
447
-
448
- block = if method_type.block
449
- open, space, close = if method_type.block.type.return_type.is_a?(RBS::Types::Bases::Void)
450
- ["do", " ", "end"]
451
- else
452
- ["{", "", "}"]
453
- end
454
-
455
- if method_type.block.type.each_param.count == 0
456
- " #{open} $0 #{close}"
457
- else
458
- " #{open}#{space}|#{params_to_snippet(method_type.block.type)}| $0 #{close}"
459
- end
460
- else
461
- ""
462
- end
463
-
464
- "#{params}#{block}"
465
- end
466
-
467
- def params_to_snippet(fun)
468
- params = []
469
-
470
- index = 1
471
-
472
- fun.required_positionals.each do |param|
473
- if name = param.name
474
- params << "${#{index}:#{param.type}}"
475
- else
476
- params << "${#{index}:#{param.type}}"
477
- end
478
-
479
- index += 1
480
- end
481
-
482
- if fun.rest_positionals
483
- params << "${#{index}:*#{fun.rest_positionals.type}}"
484
- index += 1
485
- end
486
-
487
- fun.trailing_positionals.each do |param|
488
- if name = param.name
489
- params << "${#{index}:#{param.type}}"
490
- else
491
- params << "${#{index}:#{param.type}}"
492
- end
493
-
494
- index += 1
495
- end
496
-
497
- fun.required_keywords.each do |keyword, param|
498
- if name = param.name
499
- params << "#{keyword}: ${#{index}:#{name}_}"
500
- else
501
- params << "#{keyword}: ${#{index}:#{param.type}_}"
502
- end
503
-
504
- index += 1
505
- end
506
-
507
- params.join(", ")
58
+ 0
508
59
  end
509
60
  end
510
61
  end
@@ -8,7 +8,7 @@ module Steep
8
8
  raise "Cannot find a configuration at #{path}: `steep init` to scaffold" unless path.file?
9
9
 
10
10
  steep_file_path = path.absolute? ? path : Pathname.pwd + path
11
- Project.new(base_dir: steep_file_path.parent).tap do |project|
11
+ Project.new(steepfile_path: steep_file_path).tap do |project|
12
12
  Project::DSL.parse(project, path.read, filename: path.to_s)
13
13
  end
14
14
  end