steep 1.8.3 → 1.9.0.dev.2

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +0 -22
  3. data/Steepfile +35 -26
  4. data/bin/rbs-inline +19 -0
  5. data/lib/steep/cli.rb +38 -5
  6. data/lib/steep/diagnostic/ruby.rb +11 -58
  7. data/lib/steep/drivers/annotations.rb +1 -1
  8. data/lib/steep/drivers/check.rb +103 -1
  9. data/lib/steep/drivers/checkfile.rb +10 -8
  10. data/lib/steep/drivers/print_project.rb +83 -40
  11. data/lib/steep/drivers/utils/driver_helper.rb +39 -6
  12. data/lib/steep/drivers/watch.rb +24 -2
  13. data/lib/steep/index/signature_symbol_provider.rb +8 -8
  14. data/lib/steep/interface/builder.rb +14 -1
  15. data/lib/steep/interface/function.rb +2 -2
  16. data/lib/steep/path_helper.rb +4 -2
  17. data/lib/steep/project/dsl.rb +176 -151
  18. data/lib/steep/project/group.rb +31 -0
  19. data/lib/steep/project/pattern.rb +4 -0
  20. data/lib/steep/project/target.rb +32 -6
  21. data/lib/steep/project.rb +38 -10
  22. data/lib/steep/server/custom_methods.rb +16 -0
  23. data/lib/steep/server/delay_queue.rb +0 -3
  24. data/lib/steep/server/interaction_worker.rb +2 -11
  25. data/lib/steep/server/master.rb +129 -279
  26. data/lib/steep/server/target_group_files.rb +205 -0
  27. data/lib/steep/server/type_check_controller.rb +366 -0
  28. data/lib/steep/server/type_check_worker.rb +60 -86
  29. data/lib/steep/services/file_loader.rb +23 -0
  30. data/lib/steep/services/goto_service.rb +40 -31
  31. data/lib/steep/services/hover_provider/singleton_methods.rb +4 -4
  32. data/lib/steep/services/path_assignment.rb +23 -4
  33. data/lib/steep/services/type_check_service.rb +76 -159
  34. data/lib/steep/signature/validator.rb +4 -4
  35. data/lib/steep/subtyping/check.rb +2 -2
  36. data/lib/steep/thread_waiter.rb +24 -16
  37. data/lib/steep/type_construction.rb +12 -3
  38. data/lib/steep/type_inference/block_params.rb +1 -2
  39. data/lib/steep/type_inference/context.rb +1 -1
  40. data/lib/steep/type_inference/type_env.rb +4 -4
  41. data/lib/steep/type_inference/type_env_builder.rb +1 -1
  42. data/lib/steep/version.rb +1 -1
  43. data/lib/steep.rb +6 -4
  44. data/sample/Steepfile +6 -0
  45. data/sample/lib/conference.rb +1 -5
  46. data/steep.gemspec +7 -1
  47. metadata +9 -6
  48. data/lib/steep/drivers/validate.rb +0 -65
@@ -3,252 +3,6 @@ module Steep
3
3
  class Master
4
4
  LSP = LanguageServer::Protocol
5
5
 
6
- class TypeCheckRequest
7
- attr_reader :guid
8
- attr_reader :library_paths
9
- attr_reader :signature_paths
10
- attr_reader :code_paths
11
- attr_reader :priority_paths
12
- attr_reader :checked_paths
13
- attr_reader :work_done_progress
14
- attr_reader :started_at
15
- attr_accessor :needs_response
16
-
17
- def initialize(guid:, progress:)
18
- @guid = guid
19
- @library_paths = Set[]
20
- @signature_paths = Set[]
21
- @code_paths = Set[]
22
- @priority_paths = Set[]
23
- @checked_paths = Set[]
24
- @work_done_progress = progress
25
- @started_at = Time.now
26
- @needs_response = false
27
- end
28
-
29
- def uri(path)
30
- Steep::PathHelper.to_uri(path)
31
- end
32
-
33
- def as_json(assignment:)
34
- {
35
- guid: guid,
36
- library_uris: library_paths.grep(assignment).map {|path| uri(path).to_s },
37
- signature_uris: signature_paths.grep(assignment).map {|path| uri(path).to_s },
38
- code_uris: code_paths.grep(assignment).map {|path| uri(path).to_s },
39
- priority_uris: priority_paths.map {|path| uri(path).to_s }
40
- }
41
- end
42
-
43
- def total
44
- library_paths.size + signature_paths.size + code_paths.size
45
- end
46
-
47
- def percentage
48
- checked_paths.size * 100 / total
49
- end
50
-
51
- def all_paths
52
- library_paths + signature_paths + code_paths
53
- end
54
-
55
- def checking_path?(path)
56
- [library_paths, signature_paths, code_paths].any? do |paths|
57
- paths.include?(path)
58
- end
59
- end
60
-
61
- def checked(path)
62
- raise unless checking_path?(path)
63
- checked_paths << path
64
- end
65
-
66
- def finished?
67
- total <= checked_paths.size
68
- end
69
-
70
- def unchecked_paths
71
- all_paths - checked_paths
72
- end
73
-
74
- def unchecked_code_paths
75
- code_paths - checked_paths
76
- end
77
-
78
- def unchecked_library_paths
79
- library_paths - checked_paths
80
- end
81
-
82
- def unchecked_signature_paths
83
- signature_paths - checked_paths
84
- end
85
- end
86
-
87
- class TypeCheckController
88
- attr_reader :project
89
- attr_reader :priority_paths
90
- attr_reader :changed_paths
91
- attr_reader :target_paths
92
-
93
- class TargetPaths
94
- attr_reader :project
95
- attr_reader :target
96
- attr_reader :code_paths
97
- attr_reader :signature_paths
98
- attr_reader :library_paths
99
-
100
- def initialize(project:, target:)
101
- @project = project
102
- @target = target
103
- @code_paths = Set[]
104
- @signature_paths = Set[]
105
- @library_paths = Set[]
106
- end
107
-
108
- def all_paths
109
- code_paths + signature_paths + library_paths
110
- end
111
-
112
- def library_path?(path)
113
- library_paths.include?(path)
114
- end
115
-
116
- def signature_path?(path)
117
- signature_paths.include?(path)
118
- end
119
-
120
- def code_path?(path)
121
- code_paths.include?(path)
122
- end
123
-
124
- def add(path, library: false)
125
- return true if signature_path?(path) || code_path?(path) || library_path?(path)
126
-
127
- if library
128
- library_paths << path
129
- true
130
- else
131
- relative_path = project.relative_path(path)
132
-
133
- case
134
- when target.source_pattern =~ relative_path
135
- code_paths << path
136
- true
137
- when target.signature_pattern =~ relative_path
138
- signature_paths << path
139
- true
140
- else
141
- false
142
- end
143
- end
144
- end
145
-
146
- alias << add
147
- end
148
-
149
- def initialize(project:)
150
- @project = project
151
- @priority_paths = Set[]
152
- @changed_paths = Set[]
153
- @target_paths = project.targets.each.map {|target| TargetPaths.new(project: project, target: target) }
154
- end
155
-
156
- def load(command_line_args:)
157
- loader = Services::FileLoader.new(base_dir: project.base_dir)
158
-
159
- files = {} #: Hash[String, String]
160
-
161
- target_paths.each do |paths|
162
- target = paths.target
163
-
164
- signature_service = Services::SignatureService.load_from(target.new_env_loader(project: project))
165
- paths.library_paths.merge(signature_service.env_rbs_paths)
166
-
167
- loader.each_path_in_patterns(target.source_pattern, command_line_args) do |path|
168
- paths.code_paths << project.absolute_path(path)
169
- files[path.to_s] = project.absolute_path(path).read
170
- if files.size > 1000
171
- yield files.dup
172
- files.clear
173
- end
174
- end
175
- loader.each_path_in_patterns(target.signature_pattern) do |path|
176
- paths.signature_paths << project.absolute_path(path)
177
- files[path.to_s] = project.absolute_path(path).read
178
- if files.size > 1000
179
- yield files.dup
180
- files.clear
181
- end
182
- end
183
-
184
- changed_paths.merge(paths.all_paths)
185
- end
186
-
187
- yield files.dup unless files.empty?
188
- end
189
-
190
- def push_changes(path)
191
- return if target_paths.any? {|paths| paths.library_path?(path) }
192
-
193
- target_paths.each {|paths| paths << path }
194
-
195
- if target_paths.any? {|paths| paths.code_path?(path) || paths.signature_path?(path) }
196
- changed_paths << path
197
- end
198
- end
199
-
200
- def update_priority(open: nil, close: nil)
201
- path = open || close
202
- path or raise
203
-
204
- target_paths.each {|paths| paths << path }
205
-
206
- case
207
- when open
208
- priority_paths << path
209
- when close
210
- priority_paths.delete path
211
- end
212
- end
213
-
214
- def make_request(guid: SecureRandom.uuid, last_request: nil, include_unchanged: false, progress:)
215
- return if changed_paths.empty? && !include_unchanged
216
-
217
- TypeCheckRequest.new(guid: guid, progress: progress).tap do |request|
218
- if last_request
219
- request.library_paths.merge(last_request.unchecked_library_paths)
220
- request.signature_paths.merge(last_request.unchecked_signature_paths)
221
- request.code_paths.merge(last_request.unchecked_code_paths)
222
- end
223
-
224
- if include_unchanged
225
- target_paths.each do |paths|
226
- request.signature_paths.merge(paths.signature_paths)
227
- request.library_paths.merge(paths.library_paths)
228
- request.code_paths.merge(paths.code_paths)
229
- end
230
- else
231
- updated_paths = target_paths.select {|paths| changed_paths.intersect?(paths.all_paths) }
232
-
233
- updated_paths.each do |paths|
234
- case
235
- when paths.signature_paths.intersect?(changed_paths)
236
- request.signature_paths.merge(paths.signature_paths)
237
- request.library_paths.merge(paths.library_paths)
238
- request.code_paths.merge(paths.code_paths)
239
- when paths.code_paths.intersect?(changed_paths)
240
- request.code_paths.merge(paths.code_paths & changed_paths)
241
- end
242
- end
243
- end
244
-
245
- request.priority_paths.merge(priority_paths)
246
-
247
- changed_paths.clear()
248
- end
249
- end
250
- end
251
-
252
6
  class ResultHandler
253
7
  attr_reader :request
254
8
  attr_reader :completion_handler
@@ -424,6 +178,7 @@ module Steep
424
178
  attr_reader :job_queue, :write_queue
425
179
 
426
180
  attr_reader :current_type_check_request
181
+ attr_reader :current_diagnostics
427
182
  attr_reader :controller
428
183
  attr_reader :result_controller
429
184
 
@@ -442,6 +197,7 @@ module Steep
442
197
  @commandline_args = []
443
198
  @job_queue = queue
444
199
  @write_queue = SizedQueue.new(100)
200
+ @current_diagnostics = {}
445
201
 
446
202
  @controller = TypeCheckController.new(project: project)
447
203
  @result_controller = ResultController.new()
@@ -533,7 +289,10 @@ module Steep
533
289
  end
534
290
  end
535
291
 
536
- waiter = ThreadWaiter.new(each_worker.to_a) {|worker| worker.wait_thread }
292
+ waiter = ThreadWaiter.new
293
+ each_worker do |worker|
294
+ waiter << worker.wait_thread
295
+ end
537
296
  waiter.wait_one()
538
297
 
539
298
  unless job_queue.closed?
@@ -623,7 +382,11 @@ module Steep
623
382
  declaration_provider: false,
624
383
  implementation_provider: true,
625
384
  type_definition_provider: true
626
- )
385
+ ),
386
+ server_info: {
387
+ name: "steep",
388
+ version: VERSION
389
+ }
627
390
  )
628
391
  }
629
392
  )
@@ -647,6 +410,10 @@ module Steep
647
410
  end
648
411
  end
649
412
 
413
+ if typecheck_automatically
414
+ progress.end()
415
+ end
416
+
650
417
  if file_system_watcher_supported?
651
418
  patterns = [] #: Array[String]
652
419
  project.targets.each do |target|
@@ -693,11 +460,13 @@ module Steep
693
460
  )
694
461
  end
695
462
 
696
- if typecheck_automatically
697
- if request = controller.make_request(guid: progress.guid, include_unchanged: true, progress: progress)
698
- start_type_check(request: request, last_request: nil)
699
- end
700
- end
463
+ controller.changed_paths.clear()
464
+
465
+ # if typecheck_automatically
466
+ # if request = controller.make_request(guid: progress.guid, include_unchanged: true, progress: progress)
467
+ # start_type_check(request: request, last_request: nil)
468
+ # end
469
+ # end
701
470
  end
702
471
  end
703
472
 
@@ -742,7 +511,6 @@ module Steep
742
511
 
743
512
  start_type_check(
744
513
  last_request: last_request,
745
- include_unchanged: true,
746
514
  progress: work_done_progress(guid),
747
515
  needs_response: false
748
516
  )
@@ -781,8 +549,15 @@ module Steep
781
549
  text = message[:params][:textDocument][:text]
782
550
 
783
551
  if path = pathname(uri)
784
- controller.update_priority(open: path)
785
- broadcast_notification(CustomMethods::FileReset.notification({ uri: uri, content: text }))
552
+ if target = project.group_for_path(path)
553
+ controller.update_priority(open: path)
554
+ # broadcast_notification(CustomMethods::FileReset.notification({ uri: uri, content: text }))
555
+
556
+ start_type_checking_queue.execute do
557
+ guid = SecureRandom.uuid
558
+ start_type_check(last_request: current_type_check_request, progress: work_done_progress(guid), needs_response: true)
559
+ end
560
+ end
786
561
  end
787
562
 
788
563
  when "textDocument/didClose"
@@ -821,6 +596,7 @@ module Steep
821
596
 
822
597
  group.on_completion do |handlers|
823
598
  result = handlers.flat_map(&:result)
599
+ result.uniq!
824
600
  enqueue_write_job SendMessageJob.to_client(message: { id: message[:id], result: result })
825
601
  end
826
602
  end
@@ -848,6 +624,7 @@ module Steep
848
624
 
849
625
  group.on_completion do |handlers|
850
626
  links = handlers.flat_map(&:result)
627
+ links.uniq!
851
628
  enqueue_write_job SendMessageJob.to_client(
852
629
  message: {
853
630
  id: message[:id],
@@ -866,15 +643,35 @@ module Steep
866
643
  end
867
644
 
868
645
  when CustomMethods::TypeCheck::METHOD
646
+ id = message[:id]
869
647
  params = message[:params] #: CustomMethods::TypeCheck::params
870
- guid = params[:guid]
871
648
 
872
- start_type_check(
873
- last_request: current_type_check_request,
874
- include_unchanged: true,
875
- progress: work_done_progress(guid || SecureRandom.uuid),
876
- needs_response: true
877
- )
649
+ request = TypeCheckController::Request.new(guid: id, progress: work_done_progress(id))
650
+ request.needs_response = true
651
+
652
+ params[:code_paths].each do |target_name, path|
653
+ request.code_paths << [target_name.to_sym, Pathname(path)]
654
+ end
655
+ params[:signature_paths].each do |target_name, path|
656
+ request.signature_paths << [target_name.to_sym, Pathname(path)]
657
+ end
658
+ params[:library_paths].each do |target_name, path|
659
+ request.library_paths << [target_name.to_sym, Pathname(path)]
660
+ end
661
+
662
+ start_type_check(request: request, last_request: nil)
663
+
664
+ when CustomMethods::TypeCheckGroups::METHOD
665
+ params = message[:params] #: CustomMethods::TypeCheckGroups::params
666
+
667
+ groups = params.fetch(:groups)
668
+
669
+ progress = work_done_progress(SecureRandom.uuid)
670
+ progress.begin("Type checking #{groups.empty? ? "project" : groups.join(", ")}", request_id: fresh_request_id)
671
+
672
+ request = controller.make_group_request(groups, progress: progress)
673
+ request.needs_response = false
674
+ start_type_check(request: request, last_request: current_type_check_request, report_progress_threshold: 0)
878
675
 
879
676
  when "$/ping"
880
677
  enqueue_write_job SendMessageJob.to_client(
@@ -884,6 +681,25 @@ module Steep
884
681
  }
885
682
  )
886
683
 
684
+ when CustomMethods::Groups::METHOD
685
+ groups = [] #: Array[String]
686
+
687
+ project.targets.each do |target|
688
+ unless target.source_pattern.empty? && target.signature_pattern.empty?
689
+ groups << target.name.to_s
690
+ end
691
+
692
+ target.groups.each do |group|
693
+ unless group.source_pattern.empty? && group.signature_pattern.empty?
694
+ groups << "#{target.name}.#{group.name}"
695
+ end
696
+ end
697
+ end
698
+
699
+ enqueue_write_job(SendMessageJob.to_client(
700
+ message: CustomMethods::Groups.response(message[:id], groups)
701
+ ))
702
+
887
703
  when "shutdown"
888
704
  start_type_checking_queue.cancel
889
705
 
@@ -916,9 +732,12 @@ module Steep
916
732
  case message[:method]
917
733
  when CustomMethods::TypeCheck__Progress::METHOD
918
734
  params = message[:params] #: CustomMethods::TypeCheck__Progress::params
735
+ target = project.targets.find {|target| target.name.to_s == params[:target] } or raise
919
736
  on_type_check_update(
920
737
  guid: params[:guid],
921
- path: Pathname(params[:path])
738
+ path: Pathname(params[:path]),
739
+ target: target,
740
+ diagnostics: params[:diagnostics]
922
741
  )
923
742
  else
924
743
  # Forward other notifications
@@ -966,23 +785,35 @@ module Steep
966
785
  request.needs_response = needs_response ? true : false
967
786
  end
968
787
 
788
+ if last_request
789
+ request.merge!(last_request)
790
+ end
791
+
969
792
  if request.total > report_progress_threshold
970
- Steep.logger.info "Starting new progress..."
793
+ request.report_progress!
794
+ end
971
795
 
972
- @current_type_check_request = request
796
+ if request.each_unchecked_target_path.to_a.empty?
797
+ finish_type_check(request)
798
+ @current_type_check_request = nil
799
+ return
800
+ end
973
801
 
974
- if progress
975
- # If `request:` keyword arg is not given
976
- request.work_done_progress.begin("Type checking", request_id: fresh_request_id)
977
- end
802
+ Steep.logger.info "Starting new progress..."
978
803
 
979
- if request.finished?
980
- finish_type_check(request)
981
- @current_type_check_request = nil
982
- return
804
+ @current_type_check_request = request
805
+ if last_request
806
+ checking_paths = request.each_path.to_set
807
+ current_diagnostics.keep_if do |path, _|
808
+ checking_paths.include?(path)
983
809
  end
984
810
  else
985
- @current_type_check_request = nil
811
+ current_diagnostics.clear
812
+ end
813
+
814
+ if progress
815
+ # If `request:` keyword arg is not given
816
+ request.work_done_progress.begin("Type checking", request_id: fresh_request_id)
986
817
  end
987
818
 
988
819
  Steep.logger.info "Sending $/typecheck/start notifications"
@@ -1000,14 +831,17 @@ module Steep
1000
831
  end
1001
832
  end
1002
833
 
1003
- def on_type_check_update(guid:, path:)
834
+ def on_type_check_update(guid:, path:, target:, diagnostics:)
1004
835
  if current = current_type_check_request()
1005
836
  if current.guid == guid
1006
- current.checked(path)
1007
- Steep.logger.info { "Request updated: checked=#{path}, unchecked=#{current.unchecked_paths.size}" }
837
+ current.checked(path, target)
838
+
839
+ Steep.logger.info { "Request updated: checked=#{path}, unchecked=#{current.each_unchecked_code_target_path.size}, diagnostics=#{diagnostics&.size}" }
1008
840
 
1009
841
  percentage = current.percentage
1010
- current.work_done_progress.report(percentage, "#{percentage}%")
842
+ current.work_done_progress.report(percentage, "#{current.checked_paths.size}/#{current.total}") if current.report_progress
843
+
844
+ push_diagnostics(path, diagnostics)
1011
845
 
1012
846
  if current.finished?
1013
847
  finish_type_check(current)
@@ -1072,6 +906,22 @@ module Steep
1072
906
  end
1073
907
  end
1074
908
  end
909
+
910
+ def push_diagnostics(path, diagnostics)
911
+ if diagnostics
912
+ ds = (current_diagnostics[path] ||= [])
913
+
914
+ ds.concat(diagnostics)
915
+ ds.uniq!
916
+
917
+ write_queue.push SendMessageJob.to_client(
918
+ message: {
919
+ method: :"textDocument/publishDiagnostics",
920
+ params: { uri: Steep::PathHelper.to_uri(path).to_s, diagnostics: ds }
921
+ }
922
+ )
923
+ end
924
+ end
1075
925
  end
1076
926
  end
1077
927
  end