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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f1a71b1f31f0a31d47ce7d19bde6f0145203c52e830f781dad464be32f46402b
4
- data.tar.gz: fbccd964aaaff4af93ee63a72a6b52e610bd3e66c5a12f3e042c180096e40d14
3
+ metadata.gz: 44afba2006b4ffed3719716d5ac515d474dc7ba9ee18c4f9b3298c9925441284
4
+ data.tar.gz: 2c008cd99c1de1a2cb80945eed727b74fe9450af07fd3d20299775d5ee728978
5
5
  SHA512:
6
- metadata.gz: bbf05cddf2f37f7d1f9de08e0267c77b1af127560b6de0ed0710267579057b0c4c8000462da0faf375754d5da9a927da3b1c7df289b1614e4d60558ba1722619
7
- data.tar.gz: b0af1f6f5640604ea3b812c3f6df6dbcd8cc87e41c04ef72fe325928511a5ba73520a0e898f3998151e657a2f4d346022a503f1e6bbb8de67e6dec92f810df1e
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 gem is experimental. We are still seeking the best syntax with your feedbacks.
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 curren token
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.name.to_s)
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 = () # :sigleton
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
- _, dirs, decls = RBS::Parser.parse_signature(source)
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
@@ -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
- base_path = Pathname("lib")
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=[BASE]", "The path to calculate relative path of files (defaults to #{base_path})") do
28
- base_path = Pathname(_1)
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=[BASE]", "The directory where the RBS files are saved at (defaults to STDOUT if not specified)") do
32
- output_path = Pathname(_1)
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
- base_path = Pathname.pwd + base_path
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
- targets = args.flat_map do
45
- path = Pathname(_1)
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
- relative_path = (Pathname.pwd + target).relative_path_from(base_path)
62
- if output_path
63
- output = output_path + relative_path.sub_ext(".rbs")
125
+ absolute_path = Pathname.pwd + target
64
126
 
65
- unless output.to_s.start_with?(output_path.to_s)
66
- raise "Cannot calculate the output file path for #{target} in #{output_path}"
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
- logger.debug { "Writing RBS file to #{output}..." }
88
- output.write(writer.output)
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
@@ -38,20 +38,25 @@ module RBS
38
38
  @comments = {}
39
39
  end
40
40
 
41
- # @rbs result: ParseResult[ProgramNode]
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
- if result.comments.none? {|comment| comment.location.slice =~ /\A# rbs_inline: enabled\Z/}
54
- return
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 rturns AST::Declarations::ModuleDecl | AST::Declarations::ClassDecl | nil
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RBS
4
4
  module Inline
5
- VERSION = "0.1.0"
5
+ VERSION = "0.3.0"
6
6
  end
7
7
  end
@@ -38,7 +38,7 @@ gems:
38
38
  source:
39
39
  type: stdlib
40
40
  - name: prism
41
- version: 0.24.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: d2e93d426c927fdab90ef12e30a9875aa05d60d6
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 curren token
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 = () # :sigleton
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[ProgramNode]
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[ProgramNode] result) -> [ Array[AST::Annotations::Use], Array[AST::Declarations::t] ]?
36
+ def self.parse: (ParseResult result, opt_in: bool) -> [ Array[AST::Annotations::Use], Array[AST::Declarations::t] ]?
36
37
 
37
- # @rbs rturns AST::Declarations::ModuleDecl | AST::Declarations::ClassDecl | nil
38
- def current_class_module_decl: () -> untyped
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.1.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-02 00:00:00.000000000 Z
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.24.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.24.0
32
+ version: '0.30'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: rbs
29
35
  requirement: !ruby/object:Gem::Requirement