steep 1.10.0 → 2.0.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.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +84 -1
  3. data/CLAUDE.md +114 -0
  4. data/README.md +1 -1
  5. data/Rakefile +15 -3
  6. data/Steepfile +13 -13
  7. data/lib/steep/annotation_parser.rb +5 -1
  8. data/lib/steep/annotations_helper.rb +12 -2
  9. data/lib/steep/ast/node/type_application.rb +22 -16
  10. data/lib/steep/ast/node/type_assertion.rb +7 -4
  11. data/lib/steep/ast/types/factory.rb +3 -2
  12. data/lib/steep/cli.rb +246 -2
  13. data/lib/steep/daemon/configuration.rb +19 -0
  14. data/lib/steep/daemon/server.rb +476 -0
  15. data/lib/steep/daemon.rb +201 -0
  16. data/lib/steep/diagnostic/ruby.rb +50 -8
  17. data/lib/steep/diagnostic/signature.rb +31 -8
  18. data/lib/steep/drivers/check.rb +301 -140
  19. data/lib/steep/drivers/print_project.rb +9 -10
  20. data/lib/steep/drivers/query.rb +102 -0
  21. data/lib/steep/drivers/start_server.rb +19 -0
  22. data/lib/steep/drivers/stop_server.rb +20 -0
  23. data/lib/steep/drivers/watch.rb +2 -2
  24. data/lib/steep/index/rbs_index.rb +38 -13
  25. data/lib/steep/index/signature_symbol_provider.rb +24 -3
  26. data/lib/steep/interface/builder.rb +48 -15
  27. data/lib/steep/interface/shape.rb +13 -5
  28. data/lib/steep/locator.rb +377 -0
  29. data/lib/steep/project/dsl.rb +26 -5
  30. data/lib/steep/project/group.rb +8 -2
  31. data/lib/steep/project/target.rb +16 -2
  32. data/lib/steep/project.rb +21 -2
  33. data/lib/steep/server/base_worker.rb +2 -2
  34. data/lib/steep/server/change_buffer.rb +2 -1
  35. data/lib/steep/server/custom_methods.rb +12 -0
  36. data/lib/steep/server/inline_source_change_detector.rb +94 -0
  37. data/lib/steep/server/interaction_worker.rb +51 -74
  38. data/lib/steep/server/lsp_formatter.rb +48 -12
  39. data/lib/steep/server/master.rb +100 -18
  40. data/lib/steep/server/target_group_files.rb +124 -151
  41. data/lib/steep/server/type_check_controller.rb +276 -123
  42. data/lib/steep/server/type_check_worker.rb +104 -3
  43. data/lib/steep/services/completion_provider/rbs.rb +74 -0
  44. data/lib/steep/services/completion_provider/ruby.rb +652 -0
  45. data/lib/steep/services/completion_provider/type_name.rb +243 -0
  46. data/lib/steep/services/completion_provider.rb +39 -662
  47. data/lib/steep/services/content_change.rb +14 -1
  48. data/lib/steep/services/file_loader.rb +4 -2
  49. data/lib/steep/services/goto_service.rb +271 -68
  50. data/lib/steep/services/hover_provider/content.rb +67 -0
  51. data/lib/steep/services/hover_provider/rbs.rb +8 -9
  52. data/lib/steep/services/hover_provider/ruby.rb +123 -64
  53. data/lib/steep/services/hover_provider/singleton_methods.rb +4 -0
  54. data/lib/steep/services/signature_service.rb +129 -54
  55. data/lib/steep/services/type_check_service.rb +72 -27
  56. data/lib/steep/signature/validator.rb +30 -18
  57. data/lib/steep/source/ignore_ranges.rb +14 -4
  58. data/lib/steep/source.rb +16 -2
  59. data/lib/steep/tagged_logging.rb +39 -0
  60. data/lib/steep/type_construction.rb +94 -21
  61. data/lib/steep/type_inference/block_params.rb +7 -7
  62. data/lib/steep/type_inference/context.rb +4 -2
  63. data/lib/steep/type_inference/logic_type_interpreter.rb +21 -3
  64. data/lib/steep/type_inference/method_call.rb +4 -0
  65. data/lib/steep/type_inference/type_env.rb +1 -1
  66. data/lib/steep/typing.rb +0 -2
  67. data/lib/steep/version.rb +1 -1
  68. data/lib/steep.rb +42 -32
  69. data/manual/ruby-diagnostics.md +67 -0
  70. data/sample/Steepfile +1 -0
  71. data/sample/lib/conference.rb +1 -0
  72. data/sample/lib/deprecated.rb +6 -0
  73. data/sample/lib/inline.rb +43 -0
  74. data/sample/sig/generics.rbs +3 -0
  75. data/steep.gemspec +4 -5
  76. metadata +26 -26
  77. data/lib/steep/services/type_name_completion.rb +0 -236
data/lib/steep/cli.rb CHANGED
@@ -16,7 +16,7 @@ module Steep
16
16
  end
17
17
 
18
18
  def self.available_commands
19
- [:init, :check, :validate, :annotations, :version, :project, :watch, :langserver, :stats, :binstub, :checkfile]
19
+ [:init, :check, :validate, :annotations, :version, :project, :watch, :langserver, :stats, :binstub, :checkfile, :server, :query]
20
20
  end
21
21
 
22
22
  def process_global_options
@@ -58,7 +58,8 @@ module Steep
58
58
  process_global_options or return 1
59
59
  setup_command or return 1
60
60
 
61
- __send__(:"process_#{command}")
61
+ method_name = command.to_s.gsub('-', '_')
62
+ __send__(:"process_#{method_name}")
62
63
  end
63
64
 
64
65
  def handle_steepfile_option(opts, command)
@@ -67,11 +68,13 @@ module Steep
67
68
 
68
69
  def handle_logging_options(opts)
69
70
  opts.on("--log-level=LEVEL", "Specify log level: debug, info, warn, error, fatal") do |level|
71
+ # @type var level: String
70
72
  Steep.logger.level = level
71
73
  Steep.ui_logger.level = level
72
74
  end
73
75
 
74
76
  opts.on("--log-output=PATH", "Print logs to given path") do |file|
77
+ # @type var file: String
75
78
  Steep.log_output = file
76
79
  end
77
80
 
@@ -127,6 +130,7 @@ BANNER
127
130
  OptionParser.new do |opts|
128
131
  opts.banner = <<BANNER
129
132
  Usage: steep check [options] [paths]
133
+ steep check [options] -e 'expr' [-e 'expr' ...]
130
134
 
131
135
  Description:
132
136
  Type check the program.
@@ -138,6 +142,9 @@ Options:
138
142
  BANNER
139
143
 
140
144
  handle_steepfile_option(opts, command)
145
+ opts.on("-e", "--expression=EXPRESSION", "Type check a Ruby expression (may be specified multiple times)") do |expr|
146
+ command.expressions << expr
147
+ end
141
148
  opts.on("--with-expectations[=PATH]", "Type check with expectations saved in PATH (or steep_expectations.yml)") do |path|
142
149
  command.with_expectations_path = Pathname(path || "steep_expectations.yml")
143
150
  end
@@ -191,6 +198,10 @@ BANNER
191
198
  command.formatter = formatter
192
199
  end
193
200
 
201
+ opts.on("--[no-]daemon", "Use daemon server if available (default: true)") do |v|
202
+ command.use_daemon = v ? true : false
203
+ end
204
+
194
205
  handle_jobs_option command.jobs_option, opts
195
206
  handle_logging_options opts
196
207
  end.parse!(argv)
@@ -198,6 +209,10 @@ BANNER
198
209
  setup_jobs_for_ci(command.jobs_option)
199
210
 
200
211
  command.command_line_patterns.push(*argv)
212
+
213
+ unless command.expressions.empty? || argv.empty?
214
+ abort "Cannot specify both -e and file paths"
215
+ end
201
216
  end.run
202
217
  end
203
218
 
@@ -476,5 +491,234 @@ BANNER
476
491
  command.commandline_args.push(*argv)
477
492
  end.run
478
493
  end
494
+
495
+ def process_server
496
+ subcommand = argv.shift
497
+
498
+ if subcommand.nil? || subcommand == "--help" || subcommand == "-h"
499
+ stderr.puts <<~HELP
500
+ Usage: steep server <subcommand> [options]
501
+
502
+ Description:
503
+ Manage the Steep daemon server for faster type checking.
504
+ The daemon keeps RBS environment loaded in memory.
505
+
506
+ Available subcommands:
507
+ start Start the daemon server
508
+ stop Stop the daemon server
509
+ restart Restart the daemon server
510
+ status Show daemon server status
511
+
512
+ Options:
513
+ --help Show this help message
514
+
515
+ Examples:
516
+ steep server start
517
+ steep server stop
518
+ steep server restart
519
+ steep server status
520
+ HELP
521
+ return 0
522
+ end
523
+
524
+ case subcommand
525
+ when "start"
526
+ unless Steep.can_fork?
527
+ stderr.puts "Error: `steep server start` is not supported on this platform (fork() is not available)"
528
+ return 1
529
+ end
530
+ Drivers::StartServer.new(stdout: stdout, stderr: stderr).tap do |command|
531
+ OptionParser.new do |opts|
532
+ opts.banner = <<BANNER
533
+ Usage: steep server start [options]
534
+
535
+ Description:
536
+ Starts a persistent daemon server for faster type checking.
537
+ The daemon keeps RBS environment loaded in memory.
538
+
539
+ Options:
540
+ BANNER
541
+ handle_logging_options opts
542
+ end.parse!(argv)
543
+ end.run
544
+ when "stop"
545
+ Drivers::StopServer.new(stdout: stdout, stderr: stderr).tap do |command|
546
+ OptionParser.new do |opts|
547
+ opts.banner = <<BANNER
548
+ Usage: steep server stop [options]
549
+
550
+ Description:
551
+ Stops the running daemon server.
552
+
553
+ Options:
554
+ BANNER
555
+ handle_logging_options opts
556
+ end.parse!(argv)
557
+ end.run
558
+ when "restart"
559
+ unless Steep.can_fork?
560
+ stderr.puts "Error: `steep server restart` is not supported on this platform (fork() is not available)"
561
+ return 1
562
+ end
563
+ OptionParser.new do |opts|
564
+ opts.banner = <<BANNER
565
+ Usage: steep server restart [options]
566
+
567
+ Description:
568
+ Restarts the daemon server (stops and then starts it).
569
+
570
+ Options:
571
+ BANNER
572
+ handle_logging_options opts
573
+ end.parse!(argv)
574
+
575
+ stop_command = Drivers::StopServer.new(stdout: stdout, stderr: stderr)
576
+ stop_command.run
577
+
578
+ # Brief pause to ensure clean shutdown
579
+ sleep 0.5
580
+
581
+ start_command = Drivers::StartServer.new(stdout: stdout, stderr: stderr)
582
+ start_command.run
583
+ when "status"
584
+ OptionParser.new do |opts|
585
+ opts.banner = <<BANNER
586
+ Usage: steep server status [options]
587
+
588
+ Description:
589
+ Shows the status of the daemon server.
590
+
591
+ Options:
592
+ BANNER
593
+ handle_logging_options opts
594
+ end.parse!(argv)
595
+
596
+ Daemon.status(stderr: stderr)
597
+ 0
598
+ else
599
+ stderr.puts "Unknown server subcommand: #{subcommand}"
600
+ stderr.puts " available subcommands: start, stop, restart, status"
601
+ 1
602
+ end
603
+ end
604
+
605
+ def process_query
606
+ subcommand = argv.shift
607
+
608
+ if subcommand.nil? || subcommand == "--help" || subcommand == "-h"
609
+ stderr.puts <<~HELP
610
+ Usage: steep query <subcommand> [options]
611
+
612
+ Description:
613
+ Query type information from the Steep daemon server.
614
+ The daemon must be running (start it with `steep server start`).
615
+
616
+ Note:
617
+ `steep query` is an experimental command.
618
+ The user interface and output format may change without deprecation.
619
+
620
+ Available subcommands:
621
+ hover Get hover information (type, documentation) for a position
622
+ definition Get the definition(s) of a class, type alias, constant, or method name
623
+
624
+ Options:
625
+ --help Show this help message
626
+
627
+ Examples:
628
+ steep query hover lib/foo.rb:10:5
629
+ steep query definition RBS::Location
630
+ steep query definition RBS::Parser.parse_signature
631
+ HELP
632
+ return 0
633
+ end
634
+
635
+ case subcommand
636
+ when "hover"
637
+ OptionParser.new do |opts|
638
+ opts.banner = <<BANNER
639
+ Usage: steep query hover [options] FILE:LINE:COL [FILE:LINE:COL ...]
640
+
641
+ Description:
642
+ Get hover information for the specified position(s).
643
+ Connects to the running Steep daemon and returns type information as JSONL
644
+ (one JSON object per line for each queried position).
645
+
646
+ FILE:LINE:COL - File path with 1-based line and column numbers.
647
+
648
+ Note:
649
+ This is an experimental command.
650
+ The user interface and output format may change without deprecation.
651
+
652
+ Options:
653
+ BANNER
654
+ handle_logging_options opts
655
+ end.parse!(argv)
656
+
657
+ if argv.empty?
658
+ stderr.puts "Error: Missing FILE:LINE:COL argument"
659
+ stderr.puts " Usage: steep query hover FILE:LINE:COL [FILE:LINE:COL ...]"
660
+ return 1
661
+ end
662
+
663
+ locations = [] #: Array[[String, Integer, Integer]]
664
+
665
+ argv.each do |location|
666
+ match = location.match(/\A(.+):(\d+):(\d+)\z/)
667
+ unless match
668
+ stderr.puts "Error: Invalid format: #{location}"
669
+ stderr.puts " Expected format: FILE:LINE:COL (e.g., lib/foo.rb:10:5)"
670
+ return 1
671
+ end
672
+
673
+ path = match[1] or raise
674
+ line = match[2].to_i
675
+ column = match[3].to_i
676
+
677
+ if line < 1 || column < 1
678
+ stderr.puts "Error: LINE and COL must be positive integers (1-based)"
679
+ return 1
680
+ end
681
+
682
+ locations << [path, line, column]
683
+ end
684
+
685
+ Drivers::Query.new(stdout: stdout, stderr: stderr).run_hover(locations: locations)
686
+ when "definition"
687
+ OptionParser.new do |opts|
688
+ opts.banner = <<BANNER
689
+ Usage: steep query definition [options] NAME [NAME ...]
690
+
691
+ Description:
692
+ Get the definition(s) of the specified name(s).
693
+ Connects to the running Steep daemon and returns both RBS declarations and
694
+ Ruby definitions as JSONL (one JSON object per line for each queried name).
695
+
696
+ NAME can be one of:
697
+ * A class, module, interface, type alias, or constant (e.g., RBS::Location)
698
+ * An instance method (e.g., RBS::Parser#parse_type)
699
+ * A singleton method (e.g., RBS::Parser.parse_signature)
700
+
701
+ Note:
702
+ This is an experimental command.
703
+ The user interface and output format may change without deprecation.
704
+
705
+ Options:
706
+ BANNER
707
+ handle_logging_options opts
708
+ end.parse!(argv)
709
+
710
+ if argv.empty?
711
+ stderr.puts "Error: Missing NAME argument"
712
+ stderr.puts " Usage: steep query definition NAME [NAME ...]"
713
+ return 1
714
+ end
715
+
716
+ Drivers::Query.new(stdout: stdout, stderr: stderr).run_definition(names: argv.dup)
717
+ else
718
+ stderr.puts "Unknown query subcommand: #{subcommand}"
719
+ stderr.puts " available subcommands: hover, definition"
720
+ 1
721
+ end
722
+ end
479
723
  end
480
724
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Steep
4
+ module Daemon
5
+ class Configuration
6
+ attr_reader :socket_path, :pid_path, :log_path, :project_id, :socket_dir
7
+
8
+ def initialize(base_dir: Dir.pwd)
9
+ @socket_dir = File.join(Dir.tmpdir, "steep-server")
10
+ @project_id = Digest::MD5.hexdigest(base_dir)[0, 8] #: String
11
+
12
+ FileUtils.mkdir_p(@socket_dir)
13
+ @socket_path = File.join(@socket_dir, "steep-#{@project_id}.sock")
14
+ @pid_path = @socket_path.sub(".sock", ".pid")
15
+ @log_path = @socket_path.sub(".sock", ".log")
16
+ end
17
+ end
18
+ end
19
+ end