Skip to content

Commit

Permalink
Merge pull request #1287 from pocke/subtract
Browse files Browse the repository at this point in the history
Implement `rbs subtract` command
  • Loading branch information
pocke authored Apr 26, 2023
2 parents ca435f9 + 964ba02 commit 6cfaba1
Show file tree
Hide file tree
Showing 12 changed files with 1,103 additions and 36 deletions.
2 changes: 2 additions & 0 deletions lib/rbs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
require "rbs/type_name"
require "rbs/types"
require "rbs/method_type"
require "rbs/file_finder"
require "rbs/ast/type_param"
require "rbs/ast/directives"
require "rbs/ast/declarations"
Expand Down Expand Up @@ -46,6 +47,7 @@
require "rbs/validator"
require "rbs/factory"
require "rbs/repository"
require "rbs/subtractor"
require "rbs/ancestor_graph"
require "rbs/locator"
require "rbs/type_alias_dependency"
Expand Down
83 changes: 78 additions & 5 deletions lib/rbs/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require "optparse"
require "shellwords"
require "abbrev"
require "stringio"

module RBS
class CLI
Expand Down Expand Up @@ -89,7 +90,7 @@ def initialize(stdout:, stderr:)
@stderr = stderr
end

COMMANDS = [:ast, :annotate, :list, :ancestors, :methods, :method, :validate, :constant, :paths, :prototype, :vendor, :parse, :test, :collection]
COMMANDS = [:ast, :annotate, :list, :ancestors, :methods, :method, :validate, :constant, :paths, :prototype, :vendor, :parse, :test, :collection, :subtract]

def parse_logging_options(opts)
opts.on("--log-level LEVEL", "Specify log level (defaults to `warn`)") do |level|
Expand Down Expand Up @@ -451,7 +452,6 @@ def run_validate(args, options)
EOU

opts.on("--silent") do
require "stringio"
@stdout = StringIO.new
end
end.parse!(args)
Expand Down Expand Up @@ -943,12 +943,10 @@ def run_parse(args, options)
opts.on('--method-type', 'Parse code as a method type') { |e| parse_method = :parse_method_type }
end.parse!(args)

loader = options.loader()

syntax_error = false
bufs = args.flat_map do |path|
path = Pathname(path)
loader.each_file(path, skip_hidden: false, immediate: true).map do |file_path|
FileFinder.each_file(path, skip_hidden: false, immediate: true).map do |file_path|
Buffer.new(content: file_path.read, name: file_path)
end
end
Expand Down Expand Up @@ -1175,5 +1173,80 @@ def collection_options(args)
opts.on('--frozen') if args[0] == 'install'
end
end

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), immediate: true, 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
rbs_path.write(io.string)
else
stdout.puts(io.string)
end
end
end
end
end
end
28 changes: 3 additions & 25 deletions lib/rbs/environment_loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ def initialize(lib:)
end
end

include FileFinder

Library = _ = Struct.new(:name, :version, keyword_init: true)

attr_reader :core_root
Expand Down Expand Up @@ -129,37 +131,13 @@ def each_dir
end
end

def each_file(path, immediate:, skip_hidden:, &block)
return enum_for(__method__, path, immediate: immediate, skip_hidden: skip_hidden) unless block

case
when path.file?
if path.extname == ".rbs" || immediate
yield path
end

when path.directory?
if path.basename.to_s.start_with?("_")
if skip_hidden
unless immediate
return
end
end
end

path.children.sort.each do |child|
each_file(child, immediate: false, skip_hidden: skip_hidden, &block)
end
end
end

def each_signature
files = Set[]

each_dir do |source, dir|
skip_hidden = !source.is_a?(Pathname)

each_file(dir, skip_hidden: skip_hidden, immediate: true) do |path|
FileFinder.each_file(dir, skip_hidden: skip_hidden, immediate: true) do |path|
next if files.include?(path)

files << path
Expand Down
31 changes: 31 additions & 0 deletions lib/rbs/file_finder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

module RBS
module FileFinder
module_function

def self.each_file(path, immediate:, skip_hidden:, &block)
return enum_for(__method__, path, immediate: immediate, skip_hidden: skip_hidden) unless block

case
when path.file?
if path.extname == ".rbs" || immediate
yield path
end

when path.directory?
if path.basename.to_s.start_with?("_")
if skip_hidden
unless immediate
return
end
end
end

path.children.sort.each do |child|
each_file(child, immediate: false, skip_hidden: skip_hidden, &block)
end
end
end
end
end
Loading

0 comments on commit 6cfaba1

Please sign in to comment.