rbs-inline 0.1.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +6 -1
- data/lib/rbs/inline/annotation_parser.rb +1 -1
- data/lib/rbs/inline/ast/declarations.rb +2 -12
- data/lib/rbs/inline/ast/members.rb +2 -2
- data/lib/rbs/inline/cli.rb +91 -21
- data/lib/rbs/inline/parser.rb +12 -7
- data/lib/rbs/inline/version.rb +1 -1
- data/rbs_collection.lock.yaml +2 -2
- data/sig/generated/rbs/inline/annotation_parser.rbs +1 -1
- data/sig/generated/rbs/inline/ast/members.rbs +1 -1
- data/sig/generated/rbs/inline/cli.rbs +31 -0
- data/sig/generated/rbs/inline/parser.rbs +5 -4
- metadata +12 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 44afba2006b4ffed3719716d5ac515d474dc7ba9ee18c4f9b3298c9925441284
|
4
|
+
data.tar.gz: 2c008cd99c1de1a2cb80945eed727b74fe9450af07fd3d20299775d5ee728978
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d7d02d3e07d295c00890ac95bad4c2f7f49700a0cdddb9885dcc3c194d272ef5b387c168f675a46f2b5dc3f49aaeb5230603dbe7ebcea20087952aebb7516b66
|
7
|
+
data.tar.gz: 6e476d54d6db64e42cdc7a6bfe9dfb5b1454f290e2746836cc17fe77ca41db5dac2b1a01d311877af189ee7ad2bd06207f1db2aaac86e1e494dd5855e921254d
|
data/README.md
CHANGED
@@ -3,11 +3,16 @@
|
|
3
3
|
RBS::Inline allows embedding RBS type declarations into Ruby code as comments. You can declare types, write the implementation, and verifies they are consistent without leaving the editor opening the Ruby code.
|
4
4
|
|
5
5
|
> [!IMPORTANT]
|
6
|
-
> The
|
6
|
+
> The syntax is experimental. We are still seeking the best syntax with your feedbacks.
|
7
|
+
|
8
|
+
> [!IMPORTANT]
|
9
|
+
> This gem is a prototype for testing. We plan to merge this feature to rbs-gem and deprecate rbs-inline gem after that.
|
7
10
|
|
8
11
|
Here is a quick example of embedded declarations.
|
9
12
|
|
10
13
|
```rb
|
14
|
+
# rbs_inline: enabled
|
15
|
+
|
11
16
|
class Person
|
12
17
|
attr_reader :name #:: String
|
13
18
|
|
@@ -501,7 +501,7 @@ module RBS
|
|
501
501
|
#
|
502
502
|
# ```
|
503
503
|
# Integer -- Foo # => Returns `Integer`, tokenizer has `--` as its current token
|
504
|
-
# Integer[ -- Foo # => Returns a tree for `Integer[`, tokenizer has `--` as its
|
504
|
+
# Integer[ -- Foo # => Returns a tree for `Integer[`, tokenizer has `--` as its current token
|
505
505
|
# Integer[ Foo # => Returns a tree for `Integer[ Foo`, tokenizer is at the end of the input
|
506
506
|
# ```
|
507
507
|
#
|
@@ -9,18 +9,8 @@ module RBS
|
|
9
9
|
# @rbs returns TypeName?
|
10
10
|
def type_name(node)
|
11
11
|
case node
|
12
|
-
when Prism::ConstantReadNode
|
13
|
-
TypeName(node.
|
14
|
-
when Prism::ConstantPathNode
|
15
|
-
if node.parent
|
16
|
-
if parent = type_name(node.parent)
|
17
|
-
if child = type_name(node.child)
|
18
|
-
return parent + child
|
19
|
-
end
|
20
|
-
end
|
21
|
-
else
|
22
|
-
type_name(node.child)&.absolute!
|
23
|
-
end
|
12
|
+
when Prism::ConstantReadNode, Prism::ConstantPathNode
|
13
|
+
TypeName(node.full_name)
|
24
14
|
end
|
25
15
|
end
|
26
16
|
end
|
@@ -69,7 +69,7 @@ module RBS
|
|
69
69
|
# [FIXME] It only supports `self` receiver.
|
70
70
|
#
|
71
71
|
# ```rb
|
72
|
-
# def self.foo = () # :
|
72
|
+
# def self.foo = () # :singleton
|
73
73
|
# def object.foo = () # Not supported (returns :instance)
|
74
74
|
# ```
|
75
75
|
#
|
@@ -578,7 +578,7 @@ module RBS
|
|
578
578
|
end
|
579
579
|
RBS
|
580
580
|
|
581
|
-
_,
|
581
|
+
_, _, decls = RBS::Parser.parse_signature(source)
|
582
582
|
|
583
583
|
mod = decls[0]
|
584
584
|
mod.is_a?(RBS::AST::Declarations::Module) or raise
|
data/lib/rbs/inline/cli.rb
CHANGED
@@ -5,6 +5,56 @@ require "optparse"
|
|
5
5
|
module RBS
|
6
6
|
module Inline
|
7
7
|
class CLI
|
8
|
+
# Calculate the path under `output_path` that has the same structure relative to one of the `base_paths`
|
9
|
+
#
|
10
|
+
# ```rb
|
11
|
+
# calculator = PathCalculator.new(Pathname("/rbs-inline"), [Pathname("app"), Pathname("lib")], Pathname("/tmp/sig"))
|
12
|
+
# calculator.calculate(Pathname("/rbs-inline/app/models/foo.rb")) # => Pathname("/tmp/sig/models/foo.rb")
|
13
|
+
# calculator.calculate(Pathname("/rbs-inline/lib/bar.rb")) # => Pathname("/tmp/sig/bar.rb")
|
14
|
+
# calculator.calculate(Pathname("/rbs-inline/hello/world.rb")) # => Pathname("/tmp/sig/hello/world.rb")
|
15
|
+
# calculator.calculate(Pathname("/foo.rb")) # => nil
|
16
|
+
# ```
|
17
|
+
#
|
18
|
+
#
|
19
|
+
class PathCalculator
|
20
|
+
attr_reader :pwd #:: Pathname
|
21
|
+
|
22
|
+
attr_reader :base_paths #:: Array[Pathname]
|
23
|
+
|
24
|
+
attr_reader :output_path #:: Pathname
|
25
|
+
|
26
|
+
# @rbs pwd: Pathname
|
27
|
+
# @rbs base_paths: Array[Pathname]
|
28
|
+
# @rbs output_path: Pathname
|
29
|
+
def initialize(pwd, base_paths, output_path) #:: void
|
30
|
+
@pwd = pwd
|
31
|
+
@base_paths = base_paths
|
32
|
+
@output_path = output_path
|
33
|
+
end
|
34
|
+
|
35
|
+
#:: (Pathname) -> Pathname?
|
36
|
+
def calculate(path)
|
37
|
+
path = pwd + path if path.relative?
|
38
|
+
path = path.cleanpath
|
39
|
+
return nil unless has_prefix?(path, prefix: pwd)
|
40
|
+
|
41
|
+
if prefix = base_paths.find {|base| has_prefix?(path, prefix: pwd + base) }
|
42
|
+
relative_to_output = path.relative_path_from(pwd + prefix)
|
43
|
+
else
|
44
|
+
relative_to_output = path.relative_path_from(pwd)
|
45
|
+
end
|
46
|
+
|
47
|
+
output_path + relative_to_output
|
48
|
+
end
|
49
|
+
|
50
|
+
# @rbs path: Pathname
|
51
|
+
# @rbs prefix: Pathname
|
52
|
+
# @rbs returns bool
|
53
|
+
def has_prefix?(path, prefix:)
|
54
|
+
path.descend.include?(prefix)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
8
58
|
attr_reader :stdout, :stderr #:: IO
|
9
59
|
attr_reader :logger #:: Logger
|
10
60
|
|
@@ -20,16 +70,30 @@ module RBS
|
|
20
70
|
# @rbs args: Array[String]
|
21
71
|
# @rbs returns Integer
|
22
72
|
def run(args)
|
23
|
-
|
73
|
+
base_paths = [Pathname("lib"), Pathname("app")]
|
24
74
|
output_path = nil #: Pathname?
|
75
|
+
opt_in = true
|
25
76
|
|
26
77
|
OptionParser.new do |opts|
|
27
|
-
opts.on("--base=
|
28
|
-
|
78
|
+
opts.on("--base=BASE", "The path to calculate relative path of files (defaults to #{base_paths.join(File::PATH_SEPARATOR)})") do |str|
|
79
|
+
# @type var str: String
|
80
|
+
base_paths = str.split(File::PATH_SEPARATOR).map {|path| Pathname(path) }
|
29
81
|
end
|
30
82
|
|
31
|
-
opts.on("--output=
|
32
|
-
|
83
|
+
opts.on("--output[=DEST]", "Save the generated RBS files under `sig/generated` or DEST if specified (defaults to output to STDOUT)") do
|
84
|
+
if _1
|
85
|
+
output_path = Pathname(_1)
|
86
|
+
else
|
87
|
+
output_path = Pathname("sig/generated")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
opts.on("--opt-out", "Generates RBS files by default. Opt-out with `# rbs_inline: disabled` comment") do
|
92
|
+
opt_in = false
|
93
|
+
end
|
94
|
+
|
95
|
+
opts.on("--opt-in", "Generates RBS files only for files with `# rbs_inline: enabled` comment (default)") do
|
96
|
+
opt_in = true
|
33
97
|
end
|
34
98
|
|
35
99
|
opts.on("--verbose") do
|
@@ -37,17 +101,17 @@ module RBS
|
|
37
101
|
end
|
38
102
|
end.parse!(args)
|
39
103
|
|
40
|
-
|
41
|
-
|
42
|
-
logger.debug { "base_path = #{base_path}, output_path = #{output_path}" }
|
104
|
+
logger.debug { "base_paths = #{base_paths.join(File::PATH_SEPARATOR)}, output_path = #{output_path}" }
|
43
105
|
|
44
|
-
|
45
|
-
|
106
|
+
if output_path
|
107
|
+
calculator = PathCalculator.new(Pathname.pwd, base_paths, output_path)
|
108
|
+
end
|
46
109
|
|
110
|
+
targets = args.flat_map { Pathname.glob(_1) }.flat_map do |path|
|
47
111
|
if path.directory?
|
48
112
|
pattern = path + "**/*.rb"
|
49
113
|
Pathname.glob(pattern.to_s)
|
50
|
-
else
|
114
|
+
else
|
51
115
|
path
|
52
116
|
end
|
53
117
|
end
|
@@ -58,12 +122,14 @@ module RBS
|
|
58
122
|
count = 0
|
59
123
|
|
60
124
|
targets.each do |target|
|
61
|
-
|
62
|
-
if output_path
|
63
|
-
output = output_path + relative_path.sub_ext(".rbs")
|
125
|
+
absolute_path = Pathname.pwd + target
|
64
126
|
|
65
|
-
|
66
|
-
|
127
|
+
if output_path && calculator
|
128
|
+
output_file_path = calculator.calculate(absolute_path)
|
129
|
+
if output_file_path
|
130
|
+
output = output_file_path.sub_ext(".rbs")
|
131
|
+
else
|
132
|
+
raise "Cannot calculate the output file path for #{target} in #{output_path}: calculated = #{output}"
|
67
133
|
end
|
68
134
|
|
69
135
|
logger.debug { "Generating #{output} from #{target} ..." }
|
@@ -73,7 +139,7 @@ module RBS
|
|
73
139
|
|
74
140
|
logger.debug { "Parsing ruby file #{target}..." }
|
75
141
|
|
76
|
-
if (uses, decls = Parser.parse(Prism.parse_file(target.to_s)))
|
142
|
+
if (uses, decls = Parser.parse(Prism.parse_file(target.to_s), opt_in: opt_in))
|
77
143
|
writer = Writer.new()
|
78
144
|
writer.header("Generated from #{target.relative? ? target : target.relative_path_from(Pathname.pwd)} with RBS::Inline")
|
79
145
|
writer.write(uses, decls)
|
@@ -84,14 +150,18 @@ module RBS
|
|
84
150
|
output.parent.mkpath
|
85
151
|
end
|
86
152
|
|
87
|
-
|
88
|
-
|
153
|
+
if output.file? && output.read == writer.output
|
154
|
+
logger.debug { "Skip writing identical RBS file" }
|
155
|
+
else
|
156
|
+
logger.debug { "Writing RBS file to #{output}..." }
|
157
|
+
output.write(writer.output)
|
158
|
+
count += 1
|
159
|
+
end
|
89
160
|
else
|
90
161
|
stdout.puts writer.output
|
91
162
|
stdout.puts
|
163
|
+
count += 1
|
92
164
|
end
|
93
|
-
|
94
|
-
count += 1
|
95
165
|
else
|
96
166
|
logger.debug { "Skipping #{target} because `# rbs_inline: enabled` comment not found" }
|
97
167
|
end
|
data/lib/rbs/inline/parser.rb
CHANGED
@@ -38,20 +38,25 @@ module RBS
|
|
38
38
|
@comments = {}
|
39
39
|
end
|
40
40
|
|
41
|
-
# @rbs result: ParseResult
|
41
|
+
# @rbs result: ParseResult
|
42
|
+
# @rbs opt_in: bool -- `true` for *opt-out* mode, `false` for *opt-in* mode.
|
42
43
|
# @rbs returns [Array[AST::Annotations::Use], Array[AST::Declarations::t]]?
|
43
|
-
def self.parse(result)
|
44
|
+
def self.parse(result, opt_in:)
|
44
45
|
instance = Parser.new()
|
45
46
|
|
46
|
-
# pp result
|
47
|
-
|
48
47
|
annots = AnnotationParser.parse(result.comments)
|
49
48
|
annots.each do |result|
|
50
49
|
instance.comments[result.line_range.end] = result
|
51
50
|
end
|
52
51
|
|
53
|
-
|
54
|
-
|
52
|
+
with_enable_magic_comment = result.comments.any? {|comment| comment.location.slice =~ /\A# rbs_inline: enabled\Z/}
|
53
|
+
with_disable_magic_comment = result.comments.any? {|comment| comment.location.slice =~ /\A# rbs_inline: disabled\Z/}
|
54
|
+
|
55
|
+
return if with_disable_magic_comment # Skips if `rbs_inline: disabled`
|
56
|
+
|
57
|
+
if opt_in
|
58
|
+
# opt-in means the `rbs_inline: enable` is required.
|
59
|
+
return unless with_enable_magic_comment
|
55
60
|
end
|
56
61
|
|
57
62
|
uses = [] #: Array[AST::Annotations::Use]
|
@@ -71,7 +76,7 @@ module RBS
|
|
71
76
|
]
|
72
77
|
end
|
73
78
|
|
74
|
-
# @rbs
|
79
|
+
# @rbs returns AST::Declarations::ModuleDecl | AST::Declarations::ClassDecl | nil
|
75
80
|
def current_class_module_decl
|
76
81
|
surrounding_decls.last
|
77
82
|
end
|
data/lib/rbs/inline/version.rb
CHANGED
data/rbs_collection.lock.yaml
CHANGED
@@ -38,7 +38,7 @@ gems:
|
|
38
38
|
source:
|
39
39
|
type: stdlib
|
40
40
|
- name: prism
|
41
|
-
version: 0.
|
41
|
+
version: 0.29.0
|
42
42
|
source:
|
43
43
|
type: rubygems
|
44
44
|
- name: rake
|
@@ -46,7 +46,7 @@ gems:
|
|
46
46
|
source:
|
47
47
|
type: git
|
48
48
|
name: ruby/gem_rbs_collection
|
49
|
-
revision:
|
49
|
+
revision: 7a105f52053ce1c708b605dfa9c1ab8473424036
|
50
50
|
remote: https://github.com/ruby/gem_rbs_collection.git
|
51
51
|
repo_dir: gems
|
52
52
|
- name: rbs
|
@@ -146,7 +146,7 @@ module RBS
|
|
146
146
|
#
|
147
147
|
# ```
|
148
148
|
# Integer -- Foo # => Returns `Integer`, tokenizer has `--` as its current token
|
149
|
-
# Integer[ -- Foo # => Returns a tree for `Integer[`, tokenizer has `--` as its
|
149
|
+
# Integer[ -- Foo # => Returns a tree for `Integer[`, tokenizer has `--` as its current token
|
150
150
|
# Integer[ Foo # => Returns a tree for `Integer[ Foo`, tokenizer is at the end of the input
|
151
151
|
# ```
|
152
152
|
#
|
@@ -49,7 +49,7 @@ module RBS
|
|
49
49
|
# [FIXME] It only supports `self` receiver.
|
50
50
|
#
|
51
51
|
# ```rb
|
52
|
-
# def self.foo = () # :
|
52
|
+
# def self.foo = () # :singleton
|
53
53
|
# def object.foo = () # Not supported (returns :instance)
|
54
54
|
# ```
|
55
55
|
def method_kind: () -> RBS::AST::Members::MethodDefinition::kind
|
@@ -3,6 +3,37 @@
|
|
3
3
|
module RBS
|
4
4
|
module Inline
|
5
5
|
class CLI
|
6
|
+
# Calculate the path under `output_path` that has the same structure relative to one of the `base_paths`
|
7
|
+
#
|
8
|
+
# ```rb
|
9
|
+
# calculator = PathCalculator.new(Pathname("/rbs-inline"), [Pathname("app"), Pathname("lib")], Pathname("/tmp/sig"))
|
10
|
+
# calculator.calculate(Pathname("/rbs-inline/app/models/foo.rb")) # => Pathname("/tmp/sig/models/foo.rb")
|
11
|
+
# calculator.calculate(Pathname("/rbs-inline/lib/bar.rb")) # => Pathname("/tmp/sig/bar.rb")
|
12
|
+
# calculator.calculate(Pathname("/rbs-inline/hello/world.rb")) # => Pathname("/tmp/sig/hello/world.rb")
|
13
|
+
# calculator.calculate(Pathname("/foo.rb")) # => nil
|
14
|
+
# ```
|
15
|
+
#
|
16
|
+
class PathCalculator
|
17
|
+
attr_reader pwd: Pathname
|
18
|
+
|
19
|
+
attr_reader base_paths: Array[Pathname]
|
20
|
+
|
21
|
+
attr_reader output_path: Pathname
|
22
|
+
|
23
|
+
# @rbs pwd: Pathname
|
24
|
+
# @rbs base_paths: Array[Pathname]
|
25
|
+
# @rbs output_path: Pathname
|
26
|
+
def initialize: (Pathname pwd, Array[Pathname] base_paths, Pathname output_path) -> void
|
27
|
+
|
28
|
+
# :: (Pathname) -> Pathname?
|
29
|
+
def calculate: (Pathname) -> Pathname?
|
30
|
+
|
31
|
+
# @rbs path: Pathname
|
32
|
+
# @rbs prefix: Pathname
|
33
|
+
# @rbs returns bool
|
34
|
+
def has_prefix?: (Pathname path, prefix: Pathname) -> bool
|
35
|
+
end
|
36
|
+
|
6
37
|
attr_reader stdout: IO
|
7
38
|
|
8
39
|
attr_reader stderr: IO
|
@@ -30,12 +30,13 @@ module RBS
|
|
30
30
|
|
31
31
|
def initialize: () -> void
|
32
32
|
|
33
|
-
# @rbs result: ParseResult
|
33
|
+
# @rbs result: ParseResult
|
34
|
+
# @rbs opt_in: bool -- `true` for *opt-out* mode, `false` for *opt-in* mode.
|
34
35
|
# @rbs returns [Array[AST::Annotations::Use], Array[AST::Declarations::t]]?
|
35
|
-
def self.parse: (ParseResult
|
36
|
+
def self.parse: (ParseResult result, opt_in: bool) -> [ Array[AST::Annotations::Use], Array[AST::Declarations::t] ]?
|
36
37
|
|
37
|
-
# @rbs
|
38
|
-
def current_class_module_decl: () ->
|
38
|
+
# @rbs returns AST::Declarations::ModuleDecl | AST::Declarations::ClassDecl | nil
|
39
|
+
def current_class_module_decl: () -> (AST::Declarations::ModuleDecl | AST::Declarations::ClassDecl | nil)
|
39
40
|
|
40
41
|
# @rbs returns AST::Declarations::ModuleDecl | AST::Declarations::ClassDecl
|
41
42
|
def current_class_module_decl!: () -> (AST::Declarations::ModuleDecl | AST::Declarations::ClassDecl)
|
metadata
CHANGED
@@ -1,29 +1,35 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rbs-inline
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Soutaro Matsumoto
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-05-
|
11
|
+
date: 2024-05-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: prism
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.29'
|
20
|
+
- - "<"
|
18
21
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
22
|
+
version: '0.30'
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
|
-
- - "
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.29'
|
30
|
+
- - "<"
|
25
31
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
32
|
+
version: '0.30'
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: rbs
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|