steep 0.50.0 → 0.51.0

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