Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement rbs subtract command #1287

Merged
merged 40 commits into from
Apr 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
0eaa27f
subtract
pocke Mar 20, 2023
2befa28
minimum implementation
pocke Mar 20, 2023
e7662b6
const in class
pocke Mar 20, 2023
cf1c3bf
methods
pocke Mar 20, 2023
4901e44
public/private
pocke Mar 20, 2023
27cc4f8
Treat alias
pocke Mar 20, 2023
8e37890
method - attr
pocke Mar 20, 2023
b020f35
interface
pocke Mar 20, 2023
5f143b1
reafctor
pocke Mar 20, 2023
9e012ba
mixin
pocke Mar 20, 2023
153e404
refactor
pocke Mar 20, 2023
978e28e
alias
pocke Mar 20, 2023
3092199
attr
pocke Mar 20, 2023
8eb4ee9
Ivar
pocke Mar 20, 2023
c60e84c
cvar
pocke Mar 20, 2023
56c47cc
refactor each_member
pocke Mar 20, 2023
609e07f
global
pocke Mar 20, 2023
b55d5fe
type alias
pocke Mar 20, 2023
afc2aea
class alias
pocke Mar 20, 2023
b85e0c7
module alias
pocke Mar 20, 2023
ad66f9d
frozen string literal
pocke Mar 20, 2023
8933c00
non exist class
pocke Mar 20, 2023
1912f4a
Implement CLI of rbs subtract
pocke Mar 23, 2023
775c34a
Remove TypeNameResolver from Subtractor
pocke Mar 23, 2023
93b7f8a
Do not remain constant when the same name class/module is defined
pocke Mar 23, 2023
e0243cf
Remove entire interface when duplicated
pocke Mar 23, 2023
a7acd3d
Remove class/module decl when it is duplicated with class alias and so
pocke Mar 23, 2023
848624f
Refactor: Remove unnecessary case-when
pocke Mar 23, 2023
7d36e42
Refactor: each_member now does not receive an interface
pocke Mar 23, 2023
c23dee0
Remove empty class/module by rbs subtract
pocke Mar 23, 2023
92152b1
Use FileFinder.each-file
pocke Mar 30, 2023
22e0506
Add test for subtract CLI
pocke Mar 30, 2023
f9b6282
Remove duplicate mixins
pocke Mar 30, 2023
1975ac9
Fix --write option of rbs subtract
pocke Apr 3, 2023
f5f89b3
Add a spec to test rbs subtract --write
pocke Apr 17, 2023
bfa76c4
Avoid to use filter_map for old Rubies
pocke Apr 17, 2023
1e8739e
Make FileFinder.each_file a module function
pocke Apr 17, 2023
a6da136
Write help message of rbs subtract
pocke Apr 17, 2023
8beab48
Allow multiple subtrahends for rbs subtract
pocke Apr 20, 2023
964ba02
Remove TODO comments already solved
pocke Apr 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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