steep 0.50.0 → 0.51.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: 6ff552f6d8f112693dc9969f20d851ddbc6d7d2f16cebd45f802a5d703618b58
4
- data.tar.gz: 723464fe34c81c3e83b66338dfc6fd3e7c3da2aaf30c1fc48116c7de794ca961
3
+ metadata.gz: 502d495f9b623c5d90a041a1e0bb7854be76251a8ac0f3567c1c91911eb641ee
4
+ data.tar.gz: 7845d6d26f38ceae87407d507f3f67b8d35bae18ecdd5f4ee9fdb841d434a4ad
5
5
  SHA512:
6
- metadata.gz: bf9f83791cfb636c65a26f404fb164fa7fc3906376e36a79a1ca4ae7eeab8020e23c5b9ce5e649a38ed79080d0aa2c00fe53a5cce8a1c76c259237f38880bbf2
7
- data.tar.gz: 25bd9e11db283acdfc957682860e674d544b82be1b5a68bc87ca96888907966b721301c937cdaa3e06bf0226ec82fa8c78eacfaf4987637a601f75ca8d8e950c
6
+ metadata.gz: e2e61987da5ef5d78381affeb2f4fbb8b84dd2a6fede0baa60c68f54a3464e3cb7eea0e2822f22a66d62f1de3b9f8b223cd6399dceb927397a3dab0bed3fea5d
7
+ data.tar.gz: ee91ba6b1a4d8f35ec1fbc157efd2fb54f27b11e398af09a7ef3d4f403ae0ae2fab3258404316c6ded646f688b61ff38b47a514fec83cd9fe1b4fcf136fdc441
data/CHANGELOG.md CHANGED
@@ -2,7 +2,13 @@
2
2
 
3
3
  ## master
4
4
 
5
- # 0.50.0 (2022-03-22)
5
+ ## 0.51.0 (2022-04-01)
6
+
7
+ * Completion for constant ([\#524](https://github.com/soutaro/steep/pull/524))
8
+ * Better hover/completion message ([\#525](https://github.com/soutaro/steep/pull/525))
9
+ * Show available commands when using `--help` ([\#523](https://github.com/soutaro/steep/pull/523))
10
+
11
+ ## 0.50.0 (2022-03-22)
6
12
 
7
13
  * CLI option for override steep command at spawn worker ([\#511](https://github.com/soutaro/steep/pull/511))
8
14
  * LSP related improvements for Sublime LSP ([\#513](https://github.com/soutaro/steep/pull/513))
data/Gemfile CHANGED
@@ -12,3 +12,4 @@ gem "minitest-hooks"
12
12
  group :stackprof, optional: true do
13
13
  gem "stackprof"
14
14
  end
15
+ gem 'minitest-slow_test'
data/Gemfile.lock CHANGED
@@ -1,14 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- steep (0.50.0)
4
+ steep (0.51.0)
5
5
  activesupport (>= 5.1)
6
6
  language_server-protocol (>= 3.15, < 4.0)
7
7
  listen (~> 3.0)
8
8
  parallel (>= 1.0.0)
9
9
  parser (>= 3.0)
10
10
  rainbow (>= 2.2.2, < 4.0)
11
- rbs (>= 2.2.0)
11
+ rbs (>= 2.3.0)
12
12
  terminal-table (>= 2, < 4)
13
13
 
14
14
  PATH
@@ -30,7 +30,7 @@ GEM
30
30
  minitest (>= 5.1)
31
31
  tzinfo (~> 2.0)
32
32
  ast (2.4.2)
33
- concurrent-ruby (1.1.9)
33
+ concurrent-ruby (1.1.10)
34
34
  ffi (1.15.5)
35
35
  i18n (1.10.0)
36
36
  concurrent-ruby (~> 1.0)
@@ -41,7 +41,9 @@ GEM
41
41
  minitest (5.15.0)
42
42
  minitest-hooks (1.5.0)
43
43
  minitest (> 5.3)
44
- parallel (1.22.0)
44
+ minitest-slow_test (0.2.0)
45
+ minitest (>= 5.0)
46
+ parallel (1.22.1)
45
47
  parser (3.1.1.0)
46
48
  ast (~> 2.4.1)
47
49
  rainbow (3.1.1)
@@ -49,7 +51,7 @@ GEM
49
51
  rb-fsevent (0.11.1)
50
52
  rb-inotify (0.10.1)
51
53
  ffi (~> 1.0)
52
- rbs (2.2.2)
54
+ rbs (2.3.0)
53
55
  stackprof (0.2.19)
54
56
  terminal-table (3.0.2)
55
57
  unicode-display_width (>= 1.1.1, < 3)
@@ -63,6 +65,7 @@ PLATFORMS
63
65
  DEPENDENCIES
64
66
  minitest (~> 5.15)
65
67
  minitest-hooks
68
+ minitest-slow_test
66
69
  rake
67
70
  stackprof
68
71
  steep!
@@ -4,7 +4,7 @@ module Steep
4
4
  class Collection
5
5
  attr_reader :annotations
6
6
  attr_reader :factory
7
- attr_reader :current_module
7
+ attr_reader :context
8
8
 
9
9
  attr_reader :var_type_annotations
10
10
  attr_reader :const_type_annotations
@@ -19,10 +19,10 @@ module Steep
19
19
  attr_reader :dynamic_annotations
20
20
  attr_reader :break_type_annotation
21
21
 
22
- def initialize(annotations:, factory:, current_module:)
22
+ def initialize(annotations:, factory:, context:)
23
23
  @annotations = annotations
24
24
  @factory = factory
25
- @current_module = current_module
25
+ @context = context
26
26
 
27
27
  @var_type_annotations = {}
28
28
  @method_type_annotations = {}
@@ -64,7 +64,7 @@ module Steep
64
64
 
65
65
  def absolute_type(type)
66
66
  if type
67
- factory.absolute_type(type, namespace: current_module)
67
+ factory.absolute_type(type, context: context)
68
68
  end
69
69
  end
70
70
 
@@ -140,7 +140,7 @@ module Steep
140
140
  end
141
141
 
142
142
  def merge_block_annotations(annotations)
143
- if annotations.current_module != current_module || annotations.factory != factory
143
+ if annotations.context != context || annotations.factory != factory
144
144
  raise "Cannot merge another annotation: self=#{self}, other=#{annotations}"
145
145
  end
146
146
 
@@ -148,9 +148,11 @@ module Steep
148
148
  annotation.is_a?(BlockType) || annotation.is_a?(BreakType)
149
149
  end
150
150
 
151
- self.class.new(annotations: retained_annotations + annotations.annotations,
152
- factory: factory,
153
- current_module: current_module)
151
+ self.class.new(
152
+ annotations: retained_annotations + annotations.annotations,
153
+ factory: factory,
154
+ context: context
155
+ )
154
156
  end
155
157
 
156
158
  def any?(&block)
@@ -18,7 +18,7 @@ module Steep
18
18
  end
19
19
 
20
20
  def type_name_resolver
21
- @type_name_resolver ||= RBS::TypeNameResolver.from_env(definition_builder.env)
21
+ @type_name_resolver ||= RBS::Resolver::TypeNameResolver.new(definition_builder.env)
22
22
  end
23
23
 
24
24
  def type_opt(type)
@@ -810,15 +810,15 @@ module Steep
810
810
  @env ||= definition_builder.env
811
811
  end
812
812
 
813
- def absolute_type(type, namespace:)
813
+ def absolute_type(type, context:)
814
814
  absolute_type = type_1(type).map_type_name do |name|
815
- absolute_type_name(name, namespace: namespace) || name.absolute!
815
+ absolute_type_name(name, context: context) || name.absolute!
816
816
  end
817
817
  type(absolute_type)
818
818
  end
819
819
 
820
- def absolute_type_name(type_name, namespace:)
821
- type_name_resolver.resolve(type_name, context: namespace.ascend)
820
+ def absolute_type_name(type_name, context:)
821
+ type_name_resolver.resolve(type_name, context: context)
822
822
  end
823
823
 
824
824
  def instance_type(type_name, args: nil, location: nil)
data/lib/steep/cli.rb CHANGED
@@ -23,6 +23,14 @@ module Steep
23
23
 
24
24
  def process_global_options
25
25
  OptionParser.new do |opts|
26
+ opts.banner = <<~USAGE
27
+ Usage: steep [options]
28
+
29
+ available commands: #{CLI.available_commands.join(', ')}
30
+
31
+ Options:
32
+ USAGE
33
+
26
34
  opts.on("--version") do
27
35
  process_version
28
36
  exit 0
@@ -0,0 +1,28 @@
1
+ Steep.logger.error "Diagnostic `Ruby::UnknownConstantAssigned` is deprecated. Use `Ruby::UnknownConstant` instead."
2
+
3
+ module Steep
4
+ module Diagnostic
5
+ module Ruby
6
+ class UnknownConstantAssigned < Base
7
+ attr_reader :context
8
+ attr_reader :name
9
+
10
+ def initialize(node:, context:, name:)
11
+ const = node.children[0]
12
+ loc = if const
13
+ const.loc.expression.join(node.loc.name)
14
+ else
15
+ node.loc.name
16
+ end
17
+ super(node: node, location: loc)
18
+ @context = context
19
+ @name = name
20
+ end
21
+
22
+ def header_line
23
+ "Cannot find the declaration of constant `#{name}`"
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -551,27 +551,33 @@ module Steep
551
551
  end
552
552
  end
553
553
 
554
- class UnknownConstantAssigned < Base
555
- attr_reader :context
554
+ class UnknownConstant < Base
556
555
  attr_reader :name
556
+ attr_reader :kind
557
557
 
558
- def initialize(node:, context:, name:)
559
- const = node.children[0]
560
- loc = if const
561
- const.loc.expression.join(node.loc.name)
562
- else
563
- node.loc.name
564
- end
565
- super(node: node, location: loc)
566
- @context = context
558
+ def initialize(node:, name:)
559
+ super(node: node, location: node.loc.name)
567
560
  @name = name
561
+ @kind = :constant
562
+ end
563
+
564
+ def class!
565
+ @kind = :class
566
+ self
567
+ end
568
+
569
+ def module!
570
+ @kind = :module
571
+ self
568
572
  end
569
573
 
570
574
  def header_line
571
- "Cannot find the declaration of constant `#{name}`"
575
+ "Cannot find the declaration of #{kind}: `#{name}`"
572
576
  end
573
577
  end
574
578
 
579
+ autoload :UnknownConstantAssigned, "steep/diagnostic/deprecated/unknown_constant_assigned"
580
+
575
581
  class FallbackAny < Base
576
582
  def initialize(node:)
577
583
  super(node: node)
@@ -744,7 +750,7 @@ module Steep
744
750
  ImplicitBreakValueMismatch => :warning,
745
751
  FallbackAny => :information,
746
752
  ElseOnExhaustiveCase => :warning,
747
- UnknownConstantAssigned => :warning,
753
+ UnknownConstant => :warning,
748
754
  MethodDefinitionMissing => :information
749
755
  }
750
756
  ).freeze
@@ -757,7 +763,7 @@ module Steep
757
763
  ImplicitBreakValueMismatch => nil,
758
764
  FallbackAny => nil,
759
765
  ElseOnExhaustiveCase => nil,
760
- UnknownConstantAssigned => nil,
766
+ UnknownConstant => nil,
761
767
  MethodDefinitionMissing => nil
762
768
  }
763
769
  ).freeze
@@ -770,7 +776,7 @@ module Steep
770
776
  ImplicitBreakValueMismatch => nil,
771
777
  FallbackAny => nil,
772
778
  ElseOnExhaustiveCase => nil,
773
- UnknownConstantAssigned => nil,
779
+ UnknownConstant => nil,
774
780
  MethodDefinitionMissing => nil,
775
781
  UnexpectedJump => nil
776
782
  }
@@ -16,7 +16,7 @@ module Steep
16
16
 
17
17
  def add_definition(node)
18
18
  case node.type
19
- when :casgn, :class, :module
19
+ when :casgn, :const
20
20
  @definitions << node
21
21
  else
22
22
  raise "Unexpected constant definition: #{node.type}"
@@ -145,6 +145,23 @@ module Steep
145
145
  raise
146
146
  end
147
147
  end
148
+
149
+ def reference(constant_node: nil)
150
+ case
151
+ when constant_node
152
+ constant_index.each do |name, entry|
153
+ if entry.references.include?(constant_node)
154
+ return name
155
+ end
156
+
157
+ if entry.definitions.include?(constant_node)
158
+ return name
159
+ end
160
+ end
161
+
162
+ nil
163
+ end
164
+ end
148
165
  end
149
166
  end
150
167
  end
@@ -1,12 +1,9 @@
1
1
  module Steep
2
2
  module ModuleHelper
3
- def module_name_from_node(node)
4
- case node.type
5
- when :const, :casgn
6
- namespace = namespace_from_node(node.children[0]) or return
7
- name = node.children[1]
8
- RBS::TypeName.new(name: name, namespace: namespace)
9
- end
3
+ def module_name_from_node(parent_node, constant_name)
4
+ namespace = namespace_from_node(parent_node) or return
5
+ name = constant_name
6
+ RBS::TypeName.new(name: name, namespace: namespace)
10
7
  end
11
8
 
12
9
  def namespace_from_node(node)
@@ -175,6 +175,21 @@ HOVER
175
175
  end
176
176
 
177
177
  string
178
+ when Services::HoverContent::ConstantContent
179
+ ss = []
180
+ if content.class_or_module?
181
+ ss << ["```rbs", retrieve_decl_information(content.decl.primary.decl), "```"].join("\n")
182
+ end
183
+
184
+ if content.constant?
185
+ ss << ["```rbs", "#{content.full_name}: #{content.type}", "```"].join("\n")
186
+ end
187
+
188
+ if s = content.comment_string
189
+ ss << s
190
+ end
191
+
192
+ ss.join("\n\n----\n\n")
178
193
  when Services::HoverContent::TypeContent
179
194
  "`#{content.type}`"
180
195
  end
@@ -327,7 +342,16 @@ HOVER
327
342
  if comment
328
343
  LSP::Interface::MarkupContent.new(
329
344
  kind: LSP::Constant::MarkupKind::MARKDOWN,
330
- value: comment.string
345
+ value: comment.string.gsub(/<!--(?~-->)-->/, "")
346
+ )
347
+ end
348
+ end
349
+
350
+ def format_comments(comments)
351
+ unless comments.empty?
352
+ LSP::Interface::MarkupContent.new(
353
+ kind: LSP::Constant::MarkupKind::MARKDOWN,
354
+ value: comments.map(&:string).join("\n----\n").gsub(/<!--(?~-->)-->/, "")
331
355
  )
332
356
  end
333
357
  end
@@ -408,6 +432,25 @@ HOVER
408
432
  new_text: item.identifier
409
433
  )
410
434
  )
435
+ when Services::CompletionProvider::ConstantItem
436
+ case
437
+ when item.class? || item.module?
438
+ kind = LanguageServer::Protocol::Constant::CompletionItemKind::CLASS
439
+ detail = item.full_name.to_s
440
+ else
441
+ kind = LanguageServer::Protocol::Constant::CompletionItemKind::CONSTANT
442
+ detail = item.type.to_s
443
+ end
444
+ LanguageServer::Protocol::Interface::CompletionItem.new(
445
+ label: item.identifier,
446
+ kind: kind,
447
+ detail: detail,
448
+ documentation: format_comments(item.comments),
449
+ text_edit: LanguageServer::Protocol::Interface::TextEdit.new(
450
+ range: range,
451
+ new_text: item.identifier
452
+ )
453
+ )
411
454
  when Services::CompletionProvider::MethodNameItem
412
455
  method_type_snippet = method_type_to_snippet(item.method_type)
413
456
  LanguageServer::Protocol::Interface::CompletionItem.new(
@@ -418,7 +461,7 @@ HOVER
418
461
  new_text: "#{item.identifier}#{method_type_snippet}",
419
462
  range: range
420
463
  ),
421
- documentation: item.comment&.string,
464
+ documentation: format_comment(item.comment),
422
465
  insert_text_format: LanguageServer::Protocol::Constant::InsertTextFormat::SNIPPET,
423
466
  sort_text: item.inherited? ? 'z' : 'a' # Ensure language server puts non-inherited methods before inherited methods
424
467
  )
@@ -501,7 +501,7 @@ module Steep
501
501
  partialResult: true
502
502
  },
503
503
  completion_provider: LSP::Interface::CompletionOptions.new(
504
- trigger_characters: [".", "@"],
504
+ trigger_characters: [".", "@", ":"],
505
505
  work_done_progress: true
506
506
  ),
507
507
  workspace_symbol_provider: true,
@@ -10,6 +10,30 @@ module Steep
10
10
 
11
11
  InstanceVariableItem = Struct.new(:identifier, :range, :type, keyword_init: true)
12
12
  LocalVariableItem = Struct.new(:identifier, :range, :type, keyword_init: true)
13
+ ConstantItem = Struct.new(:env, :identifier, :range, :type, :full_name, keyword_init: true) do
14
+ def class?
15
+ if decl = env.class_decls[full_name]
16
+ decl.primary.decl.is_a?(RBS::AST::Declarations::Class)
17
+ end
18
+ end
19
+
20
+ def module?
21
+ if decl = env.class_decls[full_name]
22
+ decl.primary.decl.is_a?(RBS::AST::Declarations::Module)
23
+ end
24
+ end
25
+
26
+ def comments
27
+ case
28
+ when decl = env.class_decls[full_name]
29
+ decl.decls.filter_map {|d| d.decl.comment }
30
+ when comment = env.constant_decls[full_name]&.decl&.comment
31
+ [comment]
32
+ else
33
+ []
34
+ end
35
+ end
36
+ end
13
37
  MethodNameItem = Struct.new(:identifier, :range, :receiver_type, :method_type, :method_decls, keyword_init: true) do
14
38
  def comment
15
39
  case method_decls.size
@@ -61,6 +85,10 @@ module Steep
61
85
  end
62
86
  end
63
87
 
88
+ def env
89
+ subtyping.factory.env
90
+ end
91
+
64
92
  def run(line:, column:)
65
93
  source_text = self.source_text.dup
66
94
  index = index_for(source_text, line:line, column: column)
@@ -92,6 +120,15 @@ module Steep
92
120
  source_text[index-1] = " "
93
121
  type_check!(source_text, line: line, column: column)
94
122
  items_for_atmark(position: position)
123
+ when ":"
124
+ if source_text[index-2] == ":"
125
+ source_text[index-1] = " "
126
+ source_text[index-2] = " "
127
+ type_check!(source_text, line: line, column: column)
128
+ items_for_colon2(position: position)
129
+ else
130
+ []
131
+ end
95
132
  else
96
133
  []
97
134
  end
@@ -170,8 +207,25 @@ module Steep
170
207
  prefix: prefix,
171
208
  position: position,
172
209
  items: items)
210
+ constant_items_for_context(context, prefix: prefix, position: position, items: items)
173
211
 
174
- when node.type == :send && at_end?(position, of: node.loc.dot)
212
+ when node.type == :const && node.children[0] && at_end?(position, of: node.loc)
213
+ # Foo::Ba ← (const)
214
+ parent_node = node.children[0]
215
+ parent_type = typing.type_of(node: parent_node)
216
+
217
+ if parent_type
218
+ prefix = node.children[1].to_s
219
+
220
+ method_items_for_receiver_type(parent_type,
221
+ include_private: false,
222
+ prefix: prefix,
223
+ position: position,
224
+ items: items)
225
+ constant_items_for_context(context, parent: parent_node, prefix: prefix, position: position, items: items)
226
+ end
227
+
228
+ when node.type == :send && at_end?(position, of: node.loc.dot) && node.loc.dot.source == "."
175
229
  # foo.← ba
176
230
  receiver_type = case (type = typing.type_of(node: node.children[0]))
177
231
  when AST::Types::Self
@@ -186,6 +240,10 @@ module Steep
186
240
  position: position,
187
241
  items: items)
188
242
 
243
+ when node.type == :send && at_end?(position, of: node.loc.dot) && node.loc.dot.source == "::"
244
+ # foo::← ba
245
+ items.push(*items_for_colon2(position: position))
246
+
189
247
  when node.type == :ivar && at_end?(position, of: node.loc)
190
248
  # @fo ←
191
249
  instance_variable_items_for_context(context, position: position, prefix: node.children[0].to_s, items: items)
@@ -198,6 +256,7 @@ module Steep
198
256
  items: items)
199
257
  local_variable_items_for_context(context, position: position, prefix: "", items: items)
200
258
  instance_variable_items_for_context(context, position: position, prefix: "", items: items)
259
+ constant_items_for_context(context, position: position, prefix: "", items: items)
201
260
  end
202
261
 
203
262
  items
@@ -236,6 +295,31 @@ module Steep
236
295
  end
237
296
  end
238
297
 
298
+ def items_for_colon2(position:)
299
+ # :: ←
300
+ shift_pos = position-2
301
+ node, *_ = source.find_nodes(line: shift_pos.line, column: shift_pos.column)
302
+ node ||= source.node
303
+
304
+ items = []
305
+ case node&.type
306
+ when :const
307
+ # Constant:: ←
308
+ context = typing.context_at(line: position.line, column: position.column)
309
+ constant_items_for_context(context, parent: node, position: position, items: items, prefix: "")
310
+ when nil
311
+ # :: ←
312
+ context = typing.context_at(line: position.line, column: position.column)
313
+ constant_items_for_context(context, parent: nil, position: position, items: items, prefix: "")
314
+ end
315
+
316
+ if node
317
+ items.push(*items_for_dot(position: position - 1))
318
+ end
319
+
320
+ items
321
+ end
322
+
239
323
  def items_for_atmark(position:)
240
324
  # @ ←
241
325
  shift_pos = position-1
@@ -290,6 +374,30 @@ module Steep
290
374
  end
291
375
  end
292
376
 
377
+ def constant_items_for_context(context, parent: nil, position:, prefix:, items:)
378
+ range = range_for(position, prefix: prefix)
379
+
380
+ if parent
381
+ case parent.type
382
+ when :const
383
+ const_name = typing.source_index.reference(constant_node: parent)
384
+ consts = context.module_context.const_env.children(const_name)
385
+ end
386
+ else
387
+ consts = context.module_context.const_env.constants
388
+ end
389
+
390
+ if consts
391
+ consts.each do |name, tuple|
392
+ type, full_name, _ = tuple
393
+
394
+ if name.to_s.start_with?(prefix)
395
+ items << ConstantItem.new(env: env, identifier: name, range: range, type: type, full_name: full_name)
396
+ end
397
+ end
398
+ end
399
+ end
400
+
293
401
  def instance_variable_items_for_context(context, position:, prefix:, items:)
294
402
  range = range_for(position, prefix: prefix)
295
403
  context.type_env.ivar_types.map do |name, type|
@@ -116,10 +116,8 @@ module Steep
116
116
  case node.type
117
117
  when :const, :casgn
118
118
  if test_ast_location(node.location.name, line: line, column: column)
119
- if module_context = typing.context_at(line: line, column: column).module_context
120
- const_env = module_context.const_env
121
- const = const_env.lookup_constant(module_name_from_node(node))
122
- queries << ConstantQuery.new(name: const.name, from: :ruby)
119
+ if name = typing.source_index.reference(constant_node: node)
120
+ queries << ConstantQuery.new(name: name, from: :ruby)
123
121
  end
124
122
  end
125
123
  when :def, :defs
@@ -247,8 +245,8 @@ module Steep
247
245
  entry = typing.source_index.entry(constant: name)
248
246
  entry.definitions.each do |node|
249
247
  case node.type
250
- when :class, :module
251
- locations << node.children[0].location.expression
248
+ when :const
249
+ locations << node.location.expression
252
250
  when :casgn
253
251
  parent = node.children[0]
254
252
  location =