steep 0.40.0 → 0.44.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.
Files changed (169) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +38 -0
  3. data/Gemfile +1 -0
  4. data/bin/output_rebaseline.rb +15 -30
  5. data/bin/output_test.rb +23 -57
  6. data/lib/steep.rb +89 -15
  7. data/lib/steep/annotation_parser.rb +10 -2
  8. data/lib/steep/ast/types/class.rb +4 -0
  9. data/lib/steep/cli.rb +31 -6
  10. data/lib/steep/diagnostic/ruby.rb +13 -8
  11. data/lib/steep/diagnostic/signature.rb +152 -2
  12. data/lib/steep/drivers/annotations.rb +18 -36
  13. data/lib/steep/drivers/check.rb +140 -31
  14. data/lib/steep/drivers/diagnostic_printer.rb +20 -11
  15. data/lib/steep/drivers/langserver.rb +4 -8
  16. data/lib/steep/drivers/print_project.rb +10 -9
  17. data/lib/steep/drivers/stats.rb +135 -119
  18. data/lib/steep/drivers/utils/driver_helper.rb +35 -0
  19. data/lib/steep/drivers/utils/jobs_count.rb +9 -0
  20. data/lib/steep/drivers/validate.rb +29 -18
  21. data/lib/steep/drivers/watch.rb +55 -49
  22. data/lib/steep/drivers/worker.rb +11 -8
  23. data/lib/steep/expectations.rb +159 -0
  24. data/lib/steep/index/signature_symbol_provider.rb +23 -1
  25. data/lib/steep/index/source_index.rb +55 -5
  26. data/lib/steep/interface/block.rb +4 -0
  27. data/lib/steep/project.rb +0 -30
  28. data/lib/steep/project/dsl.rb +5 -3
  29. data/lib/steep/project/pattern.rb +56 -0
  30. data/lib/steep/project/target.rb +11 -227
  31. data/lib/steep/server/base_worker.rb +1 -3
  32. data/lib/steep/server/change_buffer.rb +63 -0
  33. data/lib/steep/server/interaction_worker.rb +72 -57
  34. data/lib/steep/server/master.rb +652 -234
  35. data/lib/steep/server/type_check_worker.rb +304 -0
  36. data/lib/steep/server/worker_process.rb +16 -11
  37. data/lib/steep/{project → services}/completion_provider.rb +5 -5
  38. data/lib/steep/services/content_change.rb +61 -0
  39. data/lib/steep/services/file_loader.rb +48 -0
  40. data/lib/steep/services/goto_service.rb +321 -0
  41. data/lib/steep/{project → services}/hover_content.rb +19 -20
  42. data/lib/steep/services/path_assignment.rb +27 -0
  43. data/lib/steep/services/signature_service.rb +403 -0
  44. data/lib/steep/services/stats_calculator.rb +69 -0
  45. data/lib/steep/services/type_check_service.rb +413 -0
  46. data/lib/steep/signature/validator.rb +187 -85
  47. data/lib/steep/source.rb +21 -18
  48. data/lib/steep/subtyping/check.rb +246 -45
  49. data/lib/steep/subtyping/constraints.rb +4 -4
  50. data/lib/steep/type_construction.rb +428 -193
  51. data/lib/steep/type_inference/block_params.rb +1 -1
  52. data/lib/steep/type_inference/context.rb +22 -0
  53. data/lib/steep/type_inference/local_variable_type_env.rb +26 -12
  54. data/lib/steep/type_inference/logic.rb +1 -1
  55. data/lib/steep/type_inference/logic_type_interpreter.rb +4 -4
  56. data/lib/steep/type_inference/type_env.rb +43 -17
  57. data/lib/steep/version.rb +1 -1
  58. data/smoke/alias/test_expectations.yml +96 -0
  59. data/smoke/and/test_expectations.yml +31 -0
  60. data/smoke/array/test_expectations.yml +103 -0
  61. data/smoke/block/test_expectations.yml +125 -0
  62. data/smoke/case/test_expectations.yml +47 -0
  63. data/smoke/class/test_expectations.yml +120 -0
  64. data/smoke/const/test_expectations.yml +129 -0
  65. data/smoke/diagnostics-rbs-duplicated/test_expectations.yml +13 -0
  66. data/smoke/diagnostics-rbs/Steepfile +7 -4
  67. data/smoke/diagnostics-rbs/test_expectations.yml +231 -0
  68. data/smoke/diagnostics-rbs/unknown-type-name-2.rbs +5 -0
  69. data/smoke/{broken → diagnostics-ruby-unsat}/Steepfile +0 -0
  70. data/smoke/diagnostics-ruby-unsat/a.rbs +3 -0
  71. data/smoke/diagnostics-ruby-unsat/test_expectations.yml +27 -0
  72. data/smoke/{diagnostics → diagnostics-ruby-unsat}/unsatisfiable_constraint.rb +0 -1
  73. data/smoke/diagnostics/a.rbs +0 -4
  74. data/smoke/diagnostics/test_expectations.yml +451 -0
  75. data/smoke/dstr/test_expectations.yml +13 -0
  76. data/smoke/ensure/test_expectations.yml +62 -0
  77. data/smoke/enumerator/test_expectations.yml +135 -0
  78. data/smoke/extension/f.rb +2 -0
  79. data/smoke/extension/f.rbs +3 -0
  80. data/smoke/extension/test_expectations.yml +73 -0
  81. data/smoke/hash/test_expectations.yml +81 -0
  82. data/smoke/hello/test_expectations.yml +25 -0
  83. data/smoke/if/test_expectations.yml +34 -0
  84. data/smoke/implements/b.rb +13 -0
  85. data/smoke/implements/b.rbs +12 -0
  86. data/smoke/implements/test_expectations.yml +23 -0
  87. data/smoke/initialize/test_expectations.yml +1 -0
  88. data/smoke/integer/test_expectations.yml +101 -0
  89. data/smoke/interface/test_expectations.yml +23 -0
  90. data/smoke/kwbegin/test_expectations.yml +17 -0
  91. data/smoke/lambda/test_expectations.yml +39 -0
  92. data/smoke/literal/test_expectations.yml +106 -0
  93. data/smoke/map/test_expectations.yml +1 -0
  94. data/smoke/method/test_expectations.yml +90 -0
  95. data/smoke/module/test_expectations.yml +75 -0
  96. data/smoke/regexp/test_expectations.yml +615 -0
  97. data/smoke/regression/issue_328.rb +1 -0
  98. data/smoke/regression/issue_328.rbs +0 -0
  99. data/smoke/regression/issue_332.rb +11 -0
  100. data/smoke/regression/issue_332.rbs +19 -0
  101. data/smoke/regression/issue_372.rb +8 -0
  102. data/smoke/regression/issue_372.rbs +4 -0
  103. data/smoke/regression/masgn.rb +4 -0
  104. data/smoke/regression/test_expectations.yml +60 -0
  105. data/smoke/regression/thread.rb +7 -0
  106. data/smoke/rescue/test_expectations.yml +79 -0
  107. data/smoke/self/test_expectations.yml +23 -0
  108. data/smoke/skip/test_expectations.yml +23 -0
  109. data/smoke/stdout/test_expectations.yml +1 -0
  110. data/smoke/super/test_expectations.yml +69 -0
  111. data/smoke/toplevel/test_expectations.yml +15 -0
  112. data/smoke/tsort/Steepfile +2 -0
  113. data/smoke/tsort/test_expectations.yml +63 -0
  114. data/smoke/type_case/test_expectations.yml +48 -0
  115. data/smoke/unexpected/Steepfile +5 -0
  116. data/smoke/unexpected/test_expectations.yml +25 -0
  117. data/smoke/unexpected/unexpected.rb +1 -0
  118. data/smoke/unexpected/unexpected.rbs +3 -0
  119. data/smoke/yield/test_expectations.yml +68 -0
  120. data/steep.gemspec +4 -3
  121. metadata +127 -80
  122. data/lib/steep/project/file_loader.rb +0 -68
  123. data/lib/steep/project/signature_file.rb +0 -39
  124. data/lib/steep/project/source_file.rb +0 -129
  125. data/lib/steep/project/stats_calculator.rb +0 -80
  126. data/lib/steep/server/code_worker.rb +0 -150
  127. data/lib/steep/server/signature_worker.rb +0 -157
  128. data/lib/steep/server/utils.rb +0 -69
  129. data/smoke/alias/test.yaml +0 -73
  130. data/smoke/and/test.yaml +0 -24
  131. data/smoke/array/test.yaml +0 -80
  132. data/smoke/block/test.yaml +0 -96
  133. data/smoke/broken/broken.rb +0 -0
  134. data/smoke/broken/broken.rbs +0 -0
  135. data/smoke/broken/test.yaml +0 -6
  136. data/smoke/case/test.yaml +0 -36
  137. data/smoke/class/test.yaml +0 -89
  138. data/smoke/const/test.yaml +0 -96
  139. data/smoke/diagnostics-rbs-duplicated/test.yaml +0 -10
  140. data/smoke/diagnostics-rbs/test.yaml +0 -142
  141. data/smoke/diagnostics/test.yaml +0 -333
  142. data/smoke/dstr/test.yaml +0 -10
  143. data/smoke/ensure/test.yaml +0 -47
  144. data/smoke/enumerator/test.yaml +0 -100
  145. data/smoke/extension/test.yaml +0 -50
  146. data/smoke/hash/test.yaml +0 -62
  147. data/smoke/hello/test.yaml +0 -18
  148. data/smoke/if/test.yaml +0 -27
  149. data/smoke/implements/test.yaml +0 -16
  150. data/smoke/initialize/test.yaml +0 -4
  151. data/smoke/integer/test.yaml +0 -66
  152. data/smoke/interface/test.yaml +0 -16
  153. data/smoke/kwbegin/test.yaml +0 -14
  154. data/smoke/lambda/test.yaml +0 -28
  155. data/smoke/literal/test.yaml +0 -79
  156. data/smoke/map/test.yaml +0 -4
  157. data/smoke/method/test.yaml +0 -71
  158. data/smoke/module/test.yaml +0 -51
  159. data/smoke/regexp/test.yaml +0 -372
  160. data/smoke/regression/test.yaml +0 -38
  161. data/smoke/rescue/test.yaml +0 -60
  162. data/smoke/self/test.yaml +0 -16
  163. data/smoke/skip/test.yaml +0 -16
  164. data/smoke/stdout/test.yaml +0 -4
  165. data/smoke/super/test.yaml +0 -52
  166. data/smoke/toplevel/test.yaml +0 -12
  167. data/smoke/tsort/test.yaml +0 -32
  168. data/smoke/type_case/test.yaml +0 -33
  169. data/smoke/yield/test.yaml +0 -49
@@ -0,0 +1,304 @@
1
+ module Steep
2
+ module Server
3
+ class TypeCheckWorker < BaseWorker
4
+ attr_reader :project, :assignment, :service
5
+ attr_reader :commandline_args
6
+ attr_reader :current_type_check_guid
7
+
8
+ WorkspaceSymbolJob = Struct.new(:query, :id, keyword_init: true)
9
+ StatsJob = Struct.new(:id, keyword_init: true)
10
+ StartTypeCheckJob = Struct.new(:guid, :changes, keyword_init: true)
11
+ TypeCheckCodeJob = Struct.new(:guid, :path, keyword_init: true)
12
+ ValidateAppSignatureJob = Struct.new(:guid, :path, keyword_init: true)
13
+ ValidateLibrarySignatureJob = Struct.new(:guid, :path, keyword_init: true)
14
+ GotoJob = Struct.new(:id, :kind, :params, keyword_init: true) do
15
+ def self.implementation(id:, params:)
16
+ new(
17
+ kind: :implementation,
18
+ id: id,
19
+ params: params
20
+ )
21
+ end
22
+
23
+ def self.definition(id:, params:)
24
+ new(
25
+ kind: :definition,
26
+ id: id,
27
+ params: params
28
+ )
29
+ end
30
+
31
+ def implementation?
32
+ kind == :implementation
33
+ end
34
+
35
+ def definition?
36
+ kind == :definition
37
+ end
38
+ end
39
+
40
+ include ChangeBuffer
41
+
42
+ def initialize(project:, reader:, writer:, assignment:, commandline_args:)
43
+ super(project: project, reader: reader, writer: writer)
44
+
45
+ @assignment = assignment
46
+ @service = Services::TypeCheckService.new(project: project)
47
+ @buffered_changes = {}
48
+ @mutex = Mutex.new()
49
+ @queue = Queue.new
50
+ @commandline_args = commandline_args
51
+ @current_type_check_guid = nil
52
+ end
53
+
54
+ def handle_request(request)
55
+ case request[:method]
56
+ when "initialize"
57
+ load_files(project: project, commandline_args: commandline_args)
58
+ writer.write({ id: request[:id], result: nil})
59
+ when "textDocument/didChange"
60
+ collect_changes(request)
61
+ when "workspace/symbol"
62
+ query = request[:params][:query]
63
+ queue << WorkspaceSymbolJob.new(id: request[:id], query: query)
64
+ when "workspace/executeCommand"
65
+ case request[:params][:command]
66
+ when "steep/stats"
67
+ queue << StatsJob.new(id: request[:id])
68
+ end
69
+ when "$/typecheck/start"
70
+ params = request[:params]
71
+ enqueue_typecheck_jobs(params)
72
+ when "textDocument/definition"
73
+ queue << GotoJob.definition(id: request[:id], params: request[:params])
74
+ when "textDocument/implementation"
75
+ queue << GotoJob.implementation(id: request[:id], params: request[:params])
76
+ end
77
+ end
78
+
79
+ def enqueue_typecheck_jobs(params)
80
+ guid = params[:guid]
81
+
82
+ @current_type_check_guid = guid
83
+
84
+ pop_buffer() do |changes|
85
+ Steep.logger.info { "Enqueueing StartTypeCheckJob for guid=#{guid}" }
86
+ queue << StartTypeCheckJob.new(guid: guid, changes: changes)
87
+ end
88
+
89
+ priority_paths = Set.new(params[:priority_uris].map {|uri| Pathname(URI.parse(uri).path) })
90
+ library_paths = params[:library_uris].map {|uri| Pathname(URI.parse(uri).path) }
91
+ signature_paths = params[:signature_uris].map {|uri| Pathname(URI.parse(uri).path) }
92
+ code_paths = params[:code_uris].map {|uri| Pathname(URI.parse(uri).path) }
93
+
94
+ library_paths.each do |path|
95
+ if priority_paths.include?(path)
96
+ Steep.logger.info { "Enqueueing ValidateLibrarySignatureJob for guid=#{guid}, path=#{path}" }
97
+ queue << ValidateLibrarySignatureJob.new(guid: guid, path: path)
98
+ end
99
+ end
100
+
101
+ code_paths.each do |path|
102
+ if priority_paths.include?(path)
103
+ Steep.logger.info { "Enqueueing TypeCheckCodeJob for guid=#{guid}, path=#{path}" }
104
+ queue << TypeCheckCodeJob.new(guid: guid, path: path)
105
+ end
106
+ end
107
+
108
+ signature_paths.each do |path|
109
+ if priority_paths.include?(path)
110
+ Steep.logger.info { "Enqueueing ValidateAppSignatureJob for guid=#{guid}, path=#{path}" }
111
+ queue << ValidateAppSignatureJob.new(guid: guid, path: path)
112
+ end
113
+ end
114
+
115
+ library_paths.each do |path|
116
+ unless priority_paths.include?(path)
117
+ Steep.logger.info { "Enqueueing ValidateLibrarySignatureJob for guid=#{guid}, path=#{path}" }
118
+ queue << ValidateLibrarySignatureJob.new(guid: guid, path: path)
119
+ end
120
+ end
121
+
122
+ code_paths.each do |path|
123
+ unless priority_paths.include?(path)
124
+ Steep.logger.info { "Enqueueing TypeCheckCodeJob for guid=#{guid}, path=#{path}" }
125
+ queue << TypeCheckCodeJob.new(guid: guid, path: path)
126
+ end
127
+ end
128
+
129
+ signature_paths.each do |path|
130
+ unless priority_paths.include?(path)
131
+ Steep.logger.info { "Enqueueing ValidateAppSignatureJob for guid=#{guid}, path=#{path}" }
132
+ queue << ValidateAppSignatureJob.new(guid: guid, path: path)
133
+ end
134
+ end
135
+ end
136
+
137
+ def handle_job(job)
138
+ case job
139
+ when StartTypeCheckJob
140
+ Steep.logger.info { "Processing StartTypeCheckJob for guid=#{job.guid}" }
141
+ service.update(changes: job.changes)
142
+
143
+ when ValidateAppSignatureJob
144
+ if job.guid == current_type_check_guid
145
+ Steep.logger.info { "Processing ValidateAppSignature for guid=#{job.guid}, path=#{job.path}" }
146
+ service.validate_signature(path: project.relative_path(job.path)) do |path, diagnostics|
147
+ formatter = Diagnostic::LSPFormatter.new()
148
+
149
+ writer.write(
150
+ method: :"textDocument/publishDiagnostics",
151
+ params: LSP::Interface::PublishDiagnosticsParams.new(
152
+ uri: URI.parse(job.path.to_s).tap {|uri| uri.scheme = "file"},
153
+ diagnostics: diagnostics.map {|diagnostic| formatter.format(diagnostic) }.uniq
154
+ )
155
+ )
156
+ end
157
+
158
+ typecheck_progress(path: job.path, guid: job.guid)
159
+ end
160
+
161
+ when ValidateLibrarySignatureJob
162
+ if job.guid == current_type_check_guid
163
+ Steep.logger.info { "Processing ValidateLibrarySignature for guid=#{job.guid}, path=#{job.path}" }
164
+ service.validate_signature(path: job.path) do |path, diagnostics|
165
+ formatter = Diagnostic::LSPFormatter.new()
166
+
167
+ writer.write(
168
+ method: :"textDocument/publishDiagnostics",
169
+ params: LSP::Interface::PublishDiagnosticsParams.new(
170
+ uri: URI.parse(job.path.to_s).tap {|uri| uri.scheme = "file"},
171
+ diagnostics: diagnostics.map {|diagnostic| formatter.format(diagnostic) }.uniq
172
+ )
173
+ )
174
+ end
175
+
176
+ typecheck_progress(path: job.path, guid: job.guid)
177
+ end
178
+
179
+ when TypeCheckCodeJob
180
+ if job.guid == current_type_check_guid
181
+ Steep.logger.info { "Processing TypeCheckCodeJob for guid=#{job.guid}, path=#{job.path}" }
182
+ service.typecheck_source(path: project.relative_path(job.path)) do |path, diagnostics|
183
+ if target = project.target_for_source_path(path)
184
+ diagnostics = diagnostics.select {|diagnostic| target.options.error_to_report?(diagnostic) }
185
+ end
186
+
187
+ formatter = Diagnostic::LSPFormatter.new()
188
+
189
+ writer.write(
190
+ method: :"textDocument/publishDiagnostics",
191
+ params: LSP::Interface::PublishDiagnosticsParams.new(
192
+ uri: URI.parse(job.path.to_s).tap {|uri| uri.scheme = "file"},
193
+ diagnostics: diagnostics.map {|diagnostic| formatter.format(diagnostic) }.uniq
194
+ )
195
+ )
196
+ end
197
+
198
+ typecheck_progress(path: job.path, guid: job.guid)
199
+ end
200
+
201
+ when WorkspaceSymbolJob
202
+ writer.write(
203
+ id: job.id,
204
+ result: workspace_symbol_result(job.query)
205
+ )
206
+ when StatsJob
207
+ writer.write(
208
+ id: job.id,
209
+ result: stats_result().map(&:as_json)
210
+ )
211
+ when GotoJob
212
+ writer.write(
213
+ id: job.id,
214
+ result: goto(job)
215
+ )
216
+ end
217
+ end
218
+
219
+ def typecheck_progress(guid:, path:)
220
+ writer.write(
221
+ method: "$/typecheck/progress",
222
+ params: { guid: guid, path: path }
223
+ )
224
+ end
225
+
226
+ def workspace_symbol_result(query)
227
+ Steep.measure "Generating workspace symbol list for query=`#{query}`" do
228
+ indexes = project.targets.map {|target| service.signature_services[target.name].latest_rbs_index }
229
+
230
+ provider = Index::SignatureSymbolProvider.new(project: project, assignment: assignment)
231
+ provider.indexes.push(*indexes)
232
+
233
+ symbols = provider.query_symbol(query)
234
+
235
+ symbols.map do |symbol|
236
+ LSP::Interface::SymbolInformation.new(
237
+ name: symbol.name,
238
+ kind: symbol.kind,
239
+ location: symbol.location.yield_self do |location|
240
+ path = Pathname(location.buffer.name)
241
+ {
242
+ uri: URI.parse(project.absolute_path(path).to_s).tap {|uri| uri.scheme = "file" },
243
+ range: {
244
+ start: { line: location.start_line - 1, character: location.start_column },
245
+ end: { line: location.end_line - 1, character: location.end_column }
246
+ }
247
+ }
248
+ end,
249
+ container_name: symbol.container_name
250
+ )
251
+ end
252
+ end
253
+ end
254
+
255
+ def stats_result
256
+ calculator = Services::StatsCalculator.new(service: service)
257
+
258
+ project.targets.each.with_object([]) do |target, stats|
259
+ service.source_files.each_value do |file|
260
+ next unless target.possible_source_file?(file.path)
261
+ absolute_path = project.absolute_path(file.path)
262
+ next unless assignment =~ absolute_path
263
+
264
+ stats << calculator.calc_stats(target, file: file)
265
+ end
266
+ end
267
+ end
268
+
269
+ def goto(job)
270
+ path = Pathname(URI.parse(job.params[:textDocument][:uri]).path)
271
+ line = job.params[:position][:line] + 1
272
+ column = job.params[:position][:character]
273
+
274
+ goto_service = Services::GotoService.new(type_check: service)
275
+ locations =
276
+ case
277
+ when job.definition?
278
+ goto_service.definition(path: path, line: line, column: column)
279
+ when job.implementation?
280
+ goto_service.implementation(path: path, line: line, column: column)
281
+ else
282
+ raise
283
+ end
284
+
285
+ locations.map do |loc|
286
+ path =
287
+ case loc
288
+ when RBS::Location
289
+ Pathname(loc.buffer.name)
290
+ else
291
+ Pathname(loc.source_buffer.name)
292
+ end
293
+
294
+ path = project.absolute_path(path)
295
+
296
+ {
297
+ uri: URI.parse(path.to_s).tap {|uri| uri.scheme = "file" }.to_s,
298
+ range: loc.as_lsp_range
299
+ }
300
+ end
301
+ end
302
+ end
303
+ end
304
+ end
@@ -7,26 +7,26 @@ module Steep
7
7
 
8
8
  attr_reader :name
9
9
  attr_reader :wait_thread
10
+ attr_reader :index
10
11
 
11
- def initialize(reader:, writer:, stderr:, wait_thread:, name:)
12
+ def initialize(reader:, writer:, stderr:, wait_thread:, name:, index: nil)
12
13
  @reader = reader
13
14
  @writer = writer
14
15
  @stderr = stderr
15
16
  @wait_thread = wait_thread
16
17
  @name = name
18
+ @index = index
17
19
  end
18
20
 
19
- def self.spawn_worker(type, name:, steepfile:, delay_shutdown: false)
21
+ def self.spawn_worker(type, name:, steepfile:, options: [], delay_shutdown: false, index: nil)
20
22
  log_level = %w(debug info warn error fatal unknown)[Steep.logger.level]
21
23
  command = case type
22
- when :code
23
- ["steep", "worker", "--code", "--name=#{name}", "--log-level=#{log_level}", "--steepfile=#{steepfile}"]
24
- when :signature
25
- ["steep", "worker", "--signature", "--name=#{name}", "--log-level=#{log_level}", "--steepfile=#{steepfile}"]
26
24
  when :interaction
27
- ["steep", "worker", "--interaction", "--name=#{name}", "--log-level=#{log_level}", "--steepfile=#{steepfile}"]
25
+ ["steep", "worker", "--interaction", "--name=#{name}", "--log-level=#{log_level}", "--steepfile=#{steepfile}", *options]
26
+ when :typecheck
27
+ ["steep", "worker", "--typecheck", "--name=#{name}", "--log-level=#{log_level}", "--steepfile=#{steepfile}", *options]
28
28
  else
29
- raise
29
+ raise "Unknown type: #{type}"
30
30
  end
31
31
 
32
32
  if delay_shutdown
@@ -39,12 +39,17 @@ module Steep
39
39
  writer = LanguageServer::Protocol::Transport::Io::Writer.new(stdin)
40
40
  reader = LanguageServer::Protocol::Transport::Io::Reader.new(stdout)
41
41
 
42
- new(reader: reader, writer: writer, stderr: stderr, wait_thread: thread, name: name)
42
+ new(reader: reader, writer: writer, stderr: stderr, wait_thread: thread, name: name, index: index)
43
43
  end
44
44
 
45
- def self.spawn_code_workers(steepfile:, count: [Etc.nprocessors-3, 1].max, delay_shutdown: false)
45
+ def self.spawn_typecheck_workers(steepfile:, args:, count: [Etc.nprocessors - 1, 1].max, delay_shutdown: false)
46
46
  count.times.map do |i|
47
- spawn_worker(:code, name: "code@#{i}", steepfile: steepfile, delay_shutdown: delay_shutdown)
47
+ spawn_worker(:typecheck,
48
+ name: "typecheck@#{i}",
49
+ steepfile: steepfile,
50
+ options: ["--max-index=#{count}", "--index=#{i}", *args],
51
+ delay_shutdown: delay_shutdown,
52
+ index: i)
48
53
  end
49
54
  end
50
55
 
@@ -1,5 +1,5 @@
1
1
  module Steep
2
- class Project
2
+ module Services
3
3
  class CompletionProvider
4
4
  Position = Struct.new(:line, :column, keyword_init: true) do
5
5
  def -(size)
@@ -51,13 +51,13 @@ module Steep
51
51
  @modified_text = text
52
52
 
53
53
  Steep.measure "parsing" do
54
- @source = SourceFile
54
+ @source = Source
55
55
  .parse(text, path: path, factory: subtyping.factory)
56
56
  .without_unrelated_defs(line: line, column: column)
57
57
  end
58
58
 
59
59
  Steep.measure "typechecking" do
60
- @typing = SourceFile.type_check(source, subtyping: subtyping)
60
+ @typing = TypeCheckService.type_check(source: source, subtyping: subtyping)
61
61
  end
62
62
  end
63
63
 
@@ -82,7 +82,7 @@ module Steep
82
82
  end
83
83
 
84
84
  rescue Parser::SyntaxError => exn
85
- Steep.logger.error "recovering syntax error: #{exn.inspect}"
85
+ Steep.logger.info "recovering syntax error: #{exn.inspect}"
86
86
  case possible_trigger
87
87
  when "."
88
88
  source_text[index-1] = " "
@@ -143,7 +143,7 @@ module Steep
143
143
 
144
144
  when node.type == :lvar && at_end?(position, of: node.loc)
145
145
  # foo ← (lvar)
146
- local_variable_items_for_context(context, position: position, prefix: node.children[0].name.to_s, items: items)
146
+ local_variable_items_for_context(context, position: position, prefix: node.children[0].to_s, items: items)
147
147
 
148
148
  when node.type == :send && node.children[0] && at_end?(position, of: node.loc.selector)
149
149
  # foo.ba ←
@@ -0,0 +1,61 @@
1
+ module Steep
2
+ module Services
3
+ class ContentChange
4
+ class Position
5
+ attr_reader :line, :column
6
+
7
+ def initialize(line:, column:)
8
+ @line = line
9
+ @column = column
10
+ end
11
+
12
+ def ==(other)
13
+ other.is_a?(Position) && other.line == line && other.column == column
14
+ end
15
+
16
+ alias eql? ==
17
+
18
+ def hash
19
+ self.class.hash ^ line ^ column
20
+ end
21
+ end
22
+
23
+ attr_reader :range, :text
24
+
25
+ def initialize(range: nil, text:)
26
+ @range = range
27
+ @text = text
28
+ end
29
+
30
+ def ==(other)
31
+ other.is_a?(ContentChange) && other.range == range && other.text == text
32
+ end
33
+
34
+ alias eql? ==
35
+
36
+ def hash
37
+ self.class.hash ^ range.hash ^ text.hash
38
+ end
39
+
40
+ def self.string(string)
41
+ new(text: string)
42
+ end
43
+
44
+ def apply_to(text)
45
+ if range
46
+ text = text.dup
47
+ start_pos, end_pos = range
48
+
49
+ buf = RBS::Buffer.new(name: :_, content: text)
50
+ start_pos = buf.loc_to_pos([start_pos.line, start_pos.column])
51
+ end_pos = buf.loc_to_pos([end_pos.line, end_pos.column])
52
+
53
+ text[start_pos...end_pos] = self.text
54
+ text
55
+ else
56
+ self.text
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end