steep 0.40.0 → 0.44.0

Sign up to get free protection for your applications and to get access to all the features.
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