steep 1.8.3 → 1.9.0.dev.2

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