class RBS::CLI - rbs: Ruby Standard Library Documentation

# class RBS::CLI

### Constants

COMMANDS

### Attributes

original_args[R]
stderr[R]
stdout[R]

### Public Class Methods

new(stdout:, stderr:) click to toggle source

    # File rbs-3.8.0/lib/rbs/cli.rb, line 91
    def initialize(stdout:, stderr:)
      @stdout = stdout
      @stderr = stderr
    end

### Public Instance Methods

autoload(name, path) click to toggle source
Calls superclass method

    # File rbs-3.8.0/lib/rbs/cli.rb, line 611
    def autoload(name, path)
      super
    end

collection_options(args) click to toggle source

    # File rbs-3.8.0/lib/rbs/cli.rb, line 1116
        def collection_options(args)
          OptionParser.new do |opts|
            opts.banner = <<~HELP
              Usage: rbs collection [install|update|init|clean|help]

              Manage RBS collection, which contains third party RBS.

              Examples:

                # Initialize the configuration file
                $ rbs collection init

                # Generate the lock file and install RBSs from the lock file
                $ rbs collection install

                # Update the RBSs
                $ rbs collection update

              Options:
            HELP
            opts.on('--frozen') if args[0] == 'install'
          end
        end

has_parser?() click to toggle source

    # File rbs-3.8.0/lib/rbs/cli.rb, line 111
    def has_parser?
      defined?(RubyVM::AbstractSyntaxTree) ? true : false
    end

parse_logging_options(opts) click to toggle source

    # File rbs-3.8.0/lib/rbs/cli.rb, line 98
    def parse_logging_options(opts)
      opts.on("--log-level LEVEL", "Specify log level (defaults to `warn`)") do |level|
        RBS.logger_level = level
      end

      opts.on("--log-output OUTPUT", "Specify the file to output log (defaults to stderr)") do |output|
        io = File.open(output, "a") or raise
        RBS.logger_output = io
      end

      opts
    end

run(args) click to toggle source

    # File rbs-3.8.0/lib/rbs/cli.rb, line 115
        def run(args)
          @original_args = args.dup

          options = LibraryOptions.new

          opts = OptionParser.new
          opts.banner = <<~USAGE
            Usage: rbs [options...] [command...]

            Available commands: #{COMMANDS.join(", ")}, version, help.

            Options:
          USAGE
          options.setup_library_options(opts)
          parse_logging_options(opts)
          opts.version = RBS::VERSION

          opts.order!(args)

          command = args.shift&.to_sym

          case command
          when :version
            stdout.puts opts.ver
          when *COMMANDS
            __send__ :"run_#{command}", args, options
          else
            stdout.puts opts.help
          end
        end

run_ancestors(args, options) click to toggle source

    # File rbs-3.8.0/lib/rbs/cli.rb, line 260
        def run_ancestors(args, options)
          # @type var kind: :instance | :singleton
          kind = :instance

          OptionParser.new do |opts|
            opts.banner = <<EOU
    Usage: rbs ancestors [options...] [type_name]

    Show ancestors of the given class or module.

    Examples:

      $ rbs ancestors --instance String
      $ rbs ancestors --singleton Array

    Options:
    EOU
            opts.on("--instance", "Ancestors of instance of the given type_name (default)") { kind = :instance }
            opts.on("--singleton", "Ancestors of singleton of the given type_name") { kind = :singleton }
          end.order!(args)

          unless args.size == 1
            stdout.puts "Expected one argument."
            return
          end

          loader = options.loader()

          env = Environment.from_loader(loader).resolve_type_names

          builder = DefinitionBuilder::AncestorBuilder.new(env: env)
          type_name = TypeName.parse(args[0]).absolute!

          case env.constant_entry(type_name)
          when Environment::ClassEntry, Environment::ModuleEntry, Environment::ClassAliasEntry, Environment::ModuleAliasEntry
            type_name = env.normalize_module_name(type_name)

            ancestors = case kind
                        when :instance
                          builder.instance_ancestors(type_name)
                        when :singleton
                          builder.singleton_ancestors(type_name)
                        else
                          raise
                        end

            ancestors.ancestors.each do |ancestor|
              case ancestor
              when Definition::Ancestor::Singleton
                stdout.puts "singleton(#{ancestor.name})"
              when Definition::Ancestor::Instance
                if ancestor.args.empty?
                  stdout.puts ancestor.name.to_s
                else
                  stdout.puts "#{ancestor.name}[#{ancestor.args.join(", ")}]"
                end
              end
            end
          else
            stdout.puts "Cannot find class: #{type_name}"
          end
        end

run_annotate(args, options) click to toggle source

    # File rbs-3.8.0/lib/rbs/cli.rb, line 934
        def run_annotate(args, options)
          require "rbs/annotate"

          source = RBS::Annotate::RDocSource.new()
          annotator = RBS::Annotate::RDocAnnotator.new(source: source)

          preserve = true

          OptionParser.new do |opts|
            opts.banner = <<-EOB
    Usage: rbs annotate [options...] [files...]

    Import documents from RDoc and update RBS files.

    Examples:

      $ rbs annotate stdlib/pathname/**/*.rbs

    Options:
            EOB

            opts.on("--[no-]system", "Load RDoc from system (defaults to true)") {|b| source.with_system_dir = b }
            opts.on("--[no-]gems", "Load RDoc from gems (defaults to false)") {|b| source.with_gems_dir = b }
            opts.on("--[no-]site", "Load RDoc from site directory (defaults to false)") {|b| source.with_site_dir = b }
            opts.on("--[no-]home", "Load RDoc from home directory (defaults to false)") {|b| source.with_home_dir = b }
            opts.on("-d", "--dir DIRNAME", "Load RDoc from DIRNAME") {|d| source.extra_dirs << Pathname(d) }
            opts.on("--[no-]arglists", "Generate arglists section (defaults to true)") {|b| annotator.include_arg_lists = b }
            opts.on("--[no-]filename", "Include source file name in the documentation (defaults to true)") {|b| annotator.include_filename = b }
            opts.on("--[no-]preserve", "Try preserve the format of the original file (defaults to true)") {|b| preserve = b }
          end.parse!(args)

          source.load()

          args.each do |file|
            path = Pathname(file)
            if path.directory?
              Pathname.glob((path + "**/*.rbs").to_s).each do |path|
                stdout.puts "Processing #{path}..."
                annotator.annotate_file(path, preserve: preserve)
              end
            else
              stdout.puts "Processing #{path}..."
              annotator.annotate_file(path, preserve: preserve)
            end
          end
        end

run_ast(args, options) click to toggle source

    # File rbs-3.8.0/lib/rbs/cli.rb, line 146
        def run_ast(args, options)
          OptionParser.new do |opts|
            opts.banner = <<EOB
    Usage: rbs ast [patterns...]

    Print JSON AST of loaded environment.
    You can specify patterns to filter declarations with the file names.

    Examples:

      $ rbs ast
      $ rbs ast 'basic_object.rbs'
      $ rbs -I ./sig ast ./sig
      $ rbs -I ./sig ast '*/models/*.rbs'
    EOB
          end.order!(args)

          patterns = args.map do |arg|
            path = Pathname(arg)
            if path.exist?
              # Pathname means a directory or a file
              path
            else
              # String means a `fnmatch` pattern
              arg
            end
          end

          loader = options.loader()

          env = Environment.from_loader(loader).resolve_type_names

          decls = env.declarations.select do |decl|
            loc = decl.location or raise
            # @type var name: String
            name = loc.buffer.name

            patterns.empty? || patterns.any? do |pat|
              case pat
              when Pathname
                Pathname(name).ascend.any? {|p| p == pat }
              when String
                name.end_with?(pat) || File.fnmatch(pat, name, File::FNM_EXTGLOB)
              end
            end
          end

          stdout.print JSON.generate(decls)
          stdout.flush
        end

run_collection(args, options) click to toggle source

    # File rbs-3.8.0/lib/rbs/cli.rb, line 1055
        def run_collection(args, options)
          require 'bundler'

          opts = collection_options(args)
          params = {} #: Hash[Symbol, untyped]
          opts.order args.drop(1), into: params
          config_path = options.config_path or raise
          lock_path = Collection::Config.to_lockfile_path(config_path)

          case args[0]
          when 'install', 'instal', 'insta', 'inst', 'ins', 'in', 'i'
            unless params[:frozen]
              Collection::Config.generate_lockfile(config_path: config_path, definition: Bundler.definition)
            end
            Collection::Installer.new(lockfile_path: lock_path, stdout: stdout).install_from_lockfile
          when 'update', 'updat', 'upda', 'upd', 'up', 'u'
            # TODO: Be aware of argv to update only specified gem
            Collection::Config.generate_lockfile(config_path: config_path, definition: Bundler.definition, with_lockfile: false)
            Collection::Installer.new(lockfile_path: lock_path, stdout: stdout).install_from_lockfile
          when 'init'
            if config_path.exist?
              puts "#{config_path} already exists"
              exit 1
            end

            config_path.write(<<~'YAML')
              # Download sources
              sources:
                - type: git
                  name: ruby/gem_rbs_collection
                  remote: https://github.com/ruby/gem_rbs_collection.git
                  revision: main
                  repo_dir: gems

              # You can specify local directories as sources also.
              # - type: local
              #   path: path/to/your/local/repository

              # A directory to install the downloaded RBSs
              path: .gem_rbs_collection

              # gems:
              #   # If you want to avoid installing rbs files for gems, you can specify them here.
              #   - name: GEM_NAME
              #     ignore: true
            YAML
            stdout.puts "created: #{config_path}"
          when 'clean'
            unless lock_path.exist?
              puts "#{lock_path} should exist to clean"
              exit 1
            end
            Collection::Cleaner.new(lockfile_path: lock_path)
          when 'help', 'hel', 'he', 'h'
            puts opts.help
          else
            puts opts.help
            exit 1
          end
        end

run_constant(args, options) click to toggle source

    # File rbs-3.8.0/lib/rbs/cli.rb, line 450
        def run_constant(args, options)
          # @type var context: String?
          context = nil

          OptionParser.new do |opts|
            opts.banner = <<EOU
    Usage: rbs constant [options...] [name]

    Resolve constant based on RBS.

    Examples:

      $ rbs constant ::Object
      $ rbs constant UTF_8
      $ rbs constant --context=::Encoding UTF_8

    Options:
    EOU
            opts.on("--context CONTEXT", "Name of the module where the constant resolution starts") {|c| context = c }
          end.order!(args)

          unless args.size == 1
            stdout.puts "Expected one argument."
            return
          end

          loader = options.loader()
          env = Environment.from_loader(loader).resolve_type_names

          builder = DefinitionBuilder.new(env: env)
          resolver = Resolver::ConstantResolver.new(builder: builder)

          resolver_context = context ? [nil, TypeName.parse(context).absolute!] : nil #: Resolver::context
          stdout.puts "Context: #{context}"
          const_name = TypeName.parse(args[0])
          stdout.puts "Constant name: #{const_name}"

          if const_name.absolute?
            constant = resolver.table.constant(const_name)
          else
            head, *components = const_name.to_namespace.path
            head or raise

            constant = resolver.resolve(head, context: resolver_context)
            constant = components.inject(constant) do |const, component|
              if const
                resolver.resolve_child(const.name, component)
              end
            end
          end

          if constant
            stdout.puts " => #{constant.name}: #{constant.type}"
          else
            stdout.puts " => [no constant]"
          end
        end

run_diff(argv, library_options) click to toggle source

    # File rbs-3.8.0/lib/rbs/cli.rb, line 1219
    def run_diff(argv, library_options)
      Diff.new(argv: argv, library_options: library_options, stdout: stdout, stderr: stderr).run
    end

run_list(args, options) click to toggle source

    # File rbs-3.8.0/lib/rbs/cli.rb, line 197
        def run_list(args, options)
          # @type var list: Set[:class | :module | :interface]
          list = Set[]

          OptionParser.new do |opts|
            opts.banner = <<EOB
    Usage: rbs list [options...]

    List classes, modules, and interfaces.

    Examples:

      $ rbs list
      $ rbs list --class --module --interface

    Options:
    EOB
            opts.on("--class", "List classes") { list << :class }
            opts.on("--module", "List modules") { list << :module }
            opts.on("--interface", "List interfaces") { list << :interface }
          end.order!(args)

          list.merge(_ = [:class, :module, :interface]) if list.empty?

          loader = options.loader()

          env = Environment.from_loader(loader).resolve_type_names

          if list.include?(:class) || list.include?(:module)
            env.class_decls.each do |name, entry|
              case entry
              when Environment::ModuleEntry
                if list.include?(:module)
                  stdout.puts "#{name} (module)"
                end
              when Environment::ClassEntry
                if list.include?(:class)
                  stdout.puts "#{name} (class)"
                end
              end
            end

            env.class_alias_decls.each do |name, entry|
              case entry
              when Environment::ModuleAliasEntry
                if list.include?(:module)
                  stdout.puts "#{name} (module alias)"
                end
              when Environment::ClassAliasEntry
                if list.include?(:class)
                  stdout.puts "#{name} (class alias)"
                end
              end
            end
          end

          if list.include?(:interface)
            env.interface_decls.each do |name, entry|
              stdout.puts "#{name} (interface)"
            end
          end
        end

run_method(args, options) click to toggle source

    # File rbs-3.8.0/lib/rbs/cli.rb, line 379
        def run_method(args, options)
          # @type var kind: :instance | :singleton
          kind = :instance

          OptionParser.new do |opts|
            opts.banner = <<EOU
    Usage: rbs method [options...] [type_name] [method_name]

    Show the information of the method specified by type_name and method_name.

    Examples:

      $ rbs method --instance Kernel puts
      $ rbs method --singleton String try_convert

    Options:
    EOU
            opts.on("--instance", "Show an instance method (default)") { kind = :instance }
            opts.on("--singleton", "Show a singleton method") { kind = :singleton }
          end.order!(args)

          unless args.size == 2
            stdout.puts "Expected two arguments, but given #{args.size}."
            return
          end

          loader = options.loader()
          env = Environment.from_loader(loader).resolve_type_names

          builder = DefinitionBuilder.new(env: env)
          type_name = TypeName.parse(args[0]).absolute!
          method_name = args[1].to_sym

          unless env.module_name?(type_name)
            stdout.puts "Cannot find class: #{type_name}"
            return
          end

          definition = case kind
                       when :instance
                         builder.build_instance(type_name)
                       when :singleton
                         builder.build_singleton(type_name)
                       else
                         raise
                       end

          method = definition.methods[method_name]

          unless method
            stdout.puts "Cannot find method: #{method_name}"
            return
          end

          stdout.puts "#{type_name}#{kind == :instance ? "#" : "."}#{method_name}"
          stdout.puts "  defined_in: #{method.defined_in}"
          stdout.puts "  implementation: #{method.implemented_in}"
          stdout.puts "  accessibility: #{method.accessibility}"
          stdout.puts "  types:"
          separator = " "
          length_max = method.method_types.map { |type| type.to_s.length }.max or raise
          method.method_types.each do |type|
            stdout.puts format("    %s %-#{length_max}s   at %s", separator, type, type.location)
            separator = "|"
          end
        end

run_methods(args, options) click to toggle source

    # File rbs-3.8.0/lib/rbs/cli.rb, line 323
        def run_methods(args, options)
          # @type var kind: :instance | :singleton
          kind = :instance
          inherit = true

          OptionParser.new do |opts|
            opts.banner = <<EOU
    Usage: rbs methods [options...] [type_name]

    Show methods defined in the class or module.

    Examples:

      $ rbs methods --instance Kernel
      $ rbs methods --singleton --no-inherit String

    Options:
    EOU
            opts.on("--instance", "Show instance methods (default)") { kind = :instance }
            opts.on("--singleton", "Show singleton methods") { kind = :singleton }
            opts.on("--[no-]inherit", "Show methods defined in super class and mixed modules too") {|v| inherit = v }
          end.order!(args)

          unless args.size == 1
            stdout.puts "Expected one argument."
            return
          end

          loader = options.loader()

          env = Environment.from_loader(loader).resolve_type_names

          builder = DefinitionBuilder.new(env: env)
          type_name = TypeName.parse(args[0]).absolute!

          if env.module_name?(type_name)
            definition = case kind
                         when :instance
                           builder.build_instance(type_name)
                         when :singleton
                           builder.build_singleton(type_name)
                         else
                           raise
                         end

            definition.methods.keys.sort.each do |name|
              method = definition.methods[name]
              if inherit || method.implemented_in == type_name
                stdout.puts "#{name} (#{method.accessibility})"
              end
            end
          else
            stdout.puts "Cannot find class: #{type_name}"
          end
        end

run_parse(args, options) click to toggle source

    # File rbs-3.8.0/lib/rbs/cli.rb, line 886
        def run_parse(args, options)
          parse_method = :parse_signature
          # @type var e_code: String?
          e_code = nil

          OptionParser.new do |opts|
            opts.banner = <<-EOB
    Usage: rbs parse [files...]

    Parse given RBS files and print syntax errors.

    Examples:

      $ rbs parse sig/app/models.rbs sig/app/controllers.rbs

    Options:
            EOB

            opts.on('-e CODE', 'One line RBS script to parse') { |e| e_code = e }
            opts.on('--type', 'Parse code as a type') { |e| parse_method = :parse_type }
            opts.on('--method-type', 'Parse code as a method type') { |e| parse_method = :parse_method_type }
          end.parse!(args)

          syntax_error = false
          bufs = args.flat_map do |path|
            path = Pathname(path)
            FileFinder.each_file(path, skip_hidden: false).map do |file_path|
              Buffer.new(content: file_path.read, name: file_path)
            end
          end
          bufs << Buffer.new(content: e_code, name: '-e') if e_code

          bufs.each do |buf|
            RBS.logger.info "Parsing #{buf.name}..."
            case parse_method
            when :parse_signature
              Parser.parse_signature(buf)
            else
              Parser.public_send(parse_method, buf, require_eof: true)
            end
          rescue RBS::ParsingError => ex
            stdout.print ex.detailed_message(highlight: true)
            syntax_error = true
          end

          exit 1 if syntax_error
        end

run_paths(args, options) click to toggle source

    # File rbs-3.8.0/lib/rbs/cli.rb, line 508
        def run_paths(args, options)
          OptionParser.new do |opts|
            opts.banner = <<EOU
    Usage: rbs paths

    Show paths to directories where the RBS files are loaded from.

    Examples:

      $ rbs paths
      $ rbs -r set paths
    EOU
          end.parse!(args)

          loader = options.loader()

          kind_of = -> (path) {
            # @type var path: Pathname
            case
            when path.file?
              "file"
            when path.directory?
              "dir"
            when !path.exist?
              "absent"
            else
              "unknown"
            end
          }

          loader.each_dir do |source, dir|
            case source
            when :core
              stdout.puts "#{dir} (#{kind_of[dir]}, core)"
            when Pathname
              stdout.puts "#{dir} (#{kind_of[dir]})"
            when EnvironmentLoader::Library
              stdout.puts "#{dir} (#{kind_of[dir]}, library, name=#{source.name})"
            end
          end
        end

run_prototype(args, options) click to toggle source

    # File rbs-3.8.0/lib/rbs/cli.rb, line 550
        def run_prototype(args, options)
          format = args.shift

          case format
          when "rbi", "rb"
            run_prototype_file(format, args)
          when "runtime"
            require_libs = [] #: Array[String]
            relative_libs = [] #: Array[String]
            merge = false
            todo = false
            owners_included = [] #: Array[Symbol]
            outline = false
            autoload = false

            OptionParser.new do |opts|
              opts.banner = <<EOU
    Usage: rbs prototype runtime [options...] [pattern...]

    Generate RBS prototype based on runtime introspection.
    It loads Ruby code specified in [options] and generates RBS prototypes for classes matches to [pattern].

    Examples:

      $ rbs prototype runtime String
      $ rbs prototype runtime --require set Set
      $ rbs prototype runtime -R lib/rbs RBS RBS::*

    Options:
    EOU
              opts.on("-r", "--require LIB", "Load library using `require`") do |lib|
                require_libs << lib
              end
              opts.on("-R", "--require-relative LIB", "Load library using `require_relative`") do |lib|
                relative_libs << lib
              end
              opts.on("--merge", "Merge generated prototype RBS with existing RBS") do
                merge = true
              end
              opts.on("--todo", "Generates only undefined methods compared to objects") do
                Warning.warn("Geneating prototypes with `--todo` option is experimental\n", category: :experimental)
                todo = true
              end
              opts.on("--method-owner CLASS", "Generate method prototypes if the owner of the method is [CLASS]") do |klass|
                owners_included << klass.to_sym
              end
              opts.on("--outline", "Generates only module/class/constant declaration (no method definition)") do
                outline = true
              end
              opts.on("--autoload", "Load all autoload path") do
                autoload = true
              end
            end.parse!(args)

            loader = options.loader()
            env = Environment.from_loader(loader).resolve_type_names

            # @type var autoloader: ^() { () -> void } -> void
            autoloader = ->(&block) {
              if autoload
                hook = Module.new do
                  def autoload(name, path)
                    super
                  end
                end
                ::Module.prepend(hook)
                ::Kernel.prepend(hook)

                arguments = [] #: Array[[Module, interned]]
                TracePoint.new(:call) do |tp|
                  base = tp.self.kind_of?(Module) ? tp.self : Kernel #: Module
                  name = (tp.binding or raise).local_variable_get(:name)
                  arguments << [base, name]
                end.enable(target: hook.instance_method(:autoload), &block)

                arguments.each do |(base, name)|
                  begin
                    base.const_get(name)
                  rescue LoadError, StandardError
                  end
                end
              else
                block.call
              end
            }
            autoloader.call do
              require_libs.each do |lib|
                require(lib)
              end
              relative_libs.each do |lib|
                eval("require_relative(lib)", binding, "rbs")
              end
            end

            runtime = Prototype::Runtime.new(patterns: args, env: env, merge: merge, todo: todo, owners_included: owners_included)
            runtime.outline = outline

            decls = runtime.decls

            writer = Writer.new(out: stdout)
            writer.write decls
          else
            stdout.puts <<EOU
    Usage: rbs prototype [generator...] [args...]

    Generate prototype of RBS files.
    Supported generators are rb, rbi, runtime.

    Examples:

      $ rbs prototype rb foo.rb
      $ rbs prototype rbi foo.rbi
      $ rbs prototype runtime String
    EOU
            exit 1
          end
        end

run_prototype_file(format, args) click to toggle source

    # File rbs-3.8.0/lib/rbs/cli.rb, line 668
        def run_prototype_file(format, args)
          availability = unless has_parser?
                           "\n** This command does not work on this interpreter (#{RUBY_ENGINE}) **\n"
                         end

          # @type var output_dir: Pathname?
          output_dir = nil
          # @type var base_dir: Pathname?
          base_dir = nil
          # @type var force: bool
          force = false

          opts = OptionParser.new
          opts.banner = <<EOU
    Usage: rbs prototype #{format} [files...]
    #{availability}
    Generate RBS prototype from source code.
    It parses specified Ruby code and and generates RBS prototypes.

    It only works on MRI because it parses Ruby code with `RubyVM::AbstractSyntaxTree`.

    Examples:

      $ rbs prototype rb lib/foo.rb
      $ rbs prototype rbi sorbet/rbi/foo.rbi

    You can run the tool in *batch* mode by passing `--out-dir` option.

      $ rbs prototype rb --out-dir=sig lib/foo.rb
      $ rbs prototype rbi --out-dir=sig/models --base-dir=app/models app/models
    EOU

          opts.on("--out-dir=DIR", "Specify the path to save the generated RBS files") do |path|
            output_dir = Pathname(path)
          end

          opts.on("--base-dir=DIR", "Specify the path to calculate the relative path to save the generated RBS files") do |path|
            base_dir = Pathname(path)
          end

          opts.on("--force", "Overwrite existing RBS files") do
            force = true
          end

          opts.parse!(args)

          unless has_parser?
            stdout.puts "Not supported on this interpreter (#{RUBY_ENGINE})."
            exit 1
          end

          if args.empty?
            stdout.puts opts
            return nil
          end

          new_parser = -> do
            case format
            when "rbi"
              Prototype::RBI.new()
            when "rb"
              Prototype::RB.new()
            else
              raise
            end
          end

          input_paths = args.map {|arg| Pathname(arg) }

          if output_dir
            # @type var skip_paths: Array[Pathname]
            skip_paths = []

            # batch mode
            input_paths.each do |path|
              stdout.puts "Processing `#{path}`..."
              ruby_files =
                if path.file?
                  [path]
                else
                  path.glob("**/*.rb").sort
                end

              ruby_files.each do |file_path|
                stdout.puts "  Generating RBS for `#{file_path}`..."

                relative_path =
                  if base_dir
                    file_path.relative_path_from(base_dir)
                  else
                    if top = file_path.descend.first
                      case
                      when top == Pathname("lib")
                        file_path.relative_path_from(top)
                      when top == Pathname("app")
                        file_path.relative_path_from(top)
                      else
                        file_path
                      end
                    else
                      file_path
                    end
                  end
                relative_path = relative_path.cleanpath()

                if relative_path.absolute? || relative_path.descend.first&.to_s == ".."
                  stdout.puts "  ⚠️  Cannot write the RBS to outside of the output dir: `#{relative_path}`"
                  next
                end

                output_path = (output_dir + relative_path).sub_ext(".rbs")

                parser = new_parser[]
                begin
                  parser.parse file_path.read()
                rescue SyntaxError
                  stdout.puts "  ⚠️  Unable to parse due to SyntaxError: `#{file_path}`"
                  next
                end

                if output_path.file?
                  if force
                    stdout.puts "    - Writing RBS to existing file `#{output_path}`..."
                  else
                    stdout.puts "    - Skipping existing file `#{output_path}`..."
                    skip_paths << file_path
                    next
                  end
                else
                  stdout.puts "    - Writing RBS to `#{output_path}`..."
                end

                (output_path.parent).mkpath
                output_path.open("w") do |io|
                  writer = Writer.new(out: io)
                  writer.write(parser.decls)
                end
              end
            end

            unless skip_paths.empty?
              stdout.puts
              stdout.puts ">>>> Skipped existing #{skip_paths.size} files. Use `--force` option to update the files."
              command = original_args.take(original_args.size - input_paths.size)

              skip_paths.take(10).each do |path|
                stdout.puts "  #{defined?(Bundler) ? "bundle exec " : ""}rbs #{Shellwords.join(command)} --force #{Shellwords.escape(path.to_s)}"
              end
              if skip_paths.size > 10
                stdout.puts "  ..."
              end
            end
          else
            # file mode
            parser = new_parser[]

            input_paths.each do |file|
              parser.parse file.read()
            end

            writer = Writer.new(out: stdout)
            writer.write parser.decls
          end
        end

run_subtract(args, _) click to toggle source

    # File rbs-3.8.0/lib/rbs/cli.rb, line 1140
        def run_subtract(args, _)
          write_to_file = false
          # @type var subtrahend_paths: Array[String]
          subtrahend_paths = []

          opts = OptionParser.new do |opts|
            opts.banner = <<~HELP
              Usage:
                rbs subtract [options...] minuend.rbs [minuend2.rbs, ...] subtrahend.rbs
                rbs subtract [options...] minuend.rbs [minuend2.rbs, ...] --subtrahend subtrahend_1.rbs --subtrahend subtrahend_2.rbs

              Remove duplications between RBS files.

              Examples:

                # Generate RBS files from the codebase.
                $ rbs prototype rb lib/ > generated.rbs

                # Write more descrictive types by hand.
                $ $EDITOR handwritten.rbs

                # Remove hand-written method definitions from generated.rbs.
                $ rbs subtract --write generated.rbs handwritten.rbs

              Options:
            HELP
            opts.on('-w', '--write', 'Overwrite files directry') { write_to_file = true }
            opts.on('--subtrahend=PATH', '') { |path| subtrahend_paths << path }
            opts.parse!(args)
          end

          if subtrahend_paths.empty?
            *minuend_paths, subtrahend_path = args
            unless subtrahend_path
              stdout.puts opts.help
              exit 1
            end
            subtrahend_paths << subtrahend_path
          else
            minuend_paths = args
          end

          if minuend_paths.empty?
            stdout.puts opts.help
            exit 1
          end

          subtrahend = Environment.new.tap do |env|
            loader = EnvironmentLoader.new(core_root: nil)
            subtrahend_paths.each do |path|
              loader.add(path: Pathname(path))
            end
            loader.load(env: env)
          end

          minuend_paths.each do |minuend_path|
            FileFinder.each_file(Pathname(minuend_path), skip_hidden: true) do |rbs_path|
              buf = Buffer.new(name: rbs_path, content: rbs_path.read)
              _, dirs, decls = Parser.parse_signature(buf)
              subtracted = Subtractor.new(decls, subtrahend).call

              io = StringIO.new
              w = Writer.new(out: io)
              w.write(dirs)
              w.write(subtracted)

              if write_to_file
                if io.string.empty?
                  rbs_path.delete
                else
                  rbs_path.write(io.string)
                end
              else
                stdout.puts(io.string)
              end
            end
          end
        end

run_test(args, options) click to toggle source

    # File rbs-3.8.0/lib/rbs/cli.rb, line 991
        def run_test(args, options)
          # @type var unchecked_classes: Array[String]
          unchecked_classes = []
          # @type var targets: Array[String]
          targets = []
          # @type var sample_size: String?
          sample_size = nil
          # @type var double_suite: String?
          double_suite = nil

          (opts = OptionParser.new do |opts|
            opts.banner = <<EOB
    Usage: rbs [rbs options...] test [test options...] COMMAND

    Examples:

      $ rbs test rake test
      $ rbs --log-level=debug test --target SomeModule::* rspec
      $ rbs test --target SomeModule::* --target AnotherModule::* --target SomeClass rake test

    Options:
    EOB
            opts.on("--target TARGET", "Sets the runtime test target") do |target|
              targets << target
            end

            opts.on("--sample-size SAMPLE_SIZE", "Sets the sample size") do |size|
              sample_size = size
            end

            opts.on("--unchecked-class UNCHECKED_CLASS", "Sets the class that would not be checked") do |unchecked_class|
              unchecked_classes << unchecked_class
            end

            opts.on("--double-suite DOUBLE_SUITE", "Sets the double suite in use (currently supported: rspec | minitest)") do |suite|
              double_suite = suite
            end
          end).order!(args)

          if args.length.zero?
            stdout.puts opts.help
            exit 1
          end

          # @type var env_hash: Hash[String, String?]
          env_hash = {
            'RUBYOPT' => "#{ENV['RUBYOPT']} -rrbs/test/setup",
            'RBS_TEST_OPT' => test_opt(options),
            'RBS_TEST_LOGLEVEL' => %w(DEBUG INFO WARN ERROR FATAL)[RBS.logger_level || 5] || "UNKNOWN",
            'RBS_TEST_SAMPLE_SIZE' => sample_size,
            'RBS_TEST_DOUBLE_SUITE' => double_suite,
            'RBS_TEST_UNCHECKED_CLASSES' => (unchecked_classes.join(',') unless unchecked_classes.empty?),
            'RBS_TEST_TARGET' => (targets.join(',') unless targets.empty?)
          }

          # @type var out: String
          # @type var err: String
          out, err, status = __skip__ = Open3.capture3(env_hash, *args)
          stdout.print(out)
          stderr.print(err)

          status
        end

run_validate(args, options) click to toggle source

    # File rbs-3.8.0/lib/rbs/cli.rb, line 446
    def run_validate(args, options)
      CLI::Validate.new(args: args, options: options).run
    end

run_vendor(args, options) click to toggle source

    # File rbs-3.8.0/lib/rbs/cli.rb, line 833
        def run_vendor(args, options)
          clean = false
          vendor_dir = Pathname("vendor/sigs")

          OptionParser.new do |opts|
            opts.banner = <<-EOB
    Usage: rbs vendor [options...] [gems...]

    Vendor signatures in the project directory.
    This command ignores the RBS loading global options, `-r` and `-I`.

    Examples:

      $ rbs vendor
      $ rbs vendor --vendor-dir=sig
      $ rbs vendor --no-stdlib

    Options:
            EOB

            opts.on("--[no-]clean", "Clean vendor directory (default: no)") do |v|
              clean = v
            end

            opts.on("--vendor-dir [DIR]", "Specify the directory for vendored signatures (default: vendor/sigs)") do |path|
              vendor_dir = Pathname(path)
            end
          end.parse!(args)

          stdout.puts "Vendoring signatures to #{vendor_dir}..."

          loader = options.loader()

          args.each do |gem|
            name, version = gem.split(/:/, 2)

            next unless name

            stdout.puts "  Loading library: #{name}, version=#{version}..."
            loader.add(library: name, version: version)
          end

          vendorer = Vendorer.new(vendor_dir: vendor_dir, loader: loader)

          if clean
            stdout.puts "  Deleting #{vendor_dir}..."
            vendorer.clean!
          end

          stdout.puts "  Copying RBS files..."
          vendorer.copy!
        end

test_opt(options) click to toggle source

    # File rbs-3.8.0/lib/rbs/cli.rb, line 981
    def test_opt options
      opts = [] #: Array[String]

      opts.push(*options.repos.map {|dir| "--repo #{Shellwords.escape(dir)}"})
      opts.push(*options.dirs.map {|dir| "-I #{Shellwords.escape(dir)}"})
      opts.push(*options.libs.map {|lib| "-r#{Shellwords.escape(lib)}"})

      opts.empty? ? nil : opts.join(" ")
    end