rbs-inline 0.1.0 → 0.2.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: '091f44026573a60f39acf39a0b8865a3cee4a4f72401cd0ab8ce37d9e397caad'
4
+ data.tar.gz: 95236c91fac2461db268a7fbd81503210d0d818bad958a6b9ff03d708a28558b
5
5
  SHA512:
6
- metadata.gz: bbf05cddf2f37f7d1f9de08e0267c77b1af127560b6de0ed0710267579057b0c4c8000462da0faf375754d5da9a927da3b1c7df289b1614e4d60558ba1722619
7
- data.tar.gz: b0af1f6f5640604ea3b812c3f6df6dbcd8cc87e41c04ef72fe325928511a5ba73520a0e898f3998151e657a2f4d346022a503f1e6bbb8de67e6dec92f810df1e
6
+ metadata.gz: e76c5c6311109ffbac3a157625370a42f7f8b0cd2e1be58f8de4352d5e1528027acff819ecf7db6d9a6bb16bbb478adadfcb09d474816f7cc942a5a42be63729
7
+ data.tar.gz: 8d7d4aba2f04cac960975add67c7d7ad30366806a540cd899a11f1250a7045145a9578732c055cf75ad50d1a68949d6aea55c7f7e3077d958aafb042f7c52149
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
  #
@@ -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,12 +70,13 @@ 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?
25
75
 
26
76
  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)
77
+ opts.on("--base=[BASE]", "The path to calculate relative path of files (defaults to #{base_paths.join(File::PATH_SEPARATOR)})") do |str|
78
+ # @type var str: String
79
+ base_paths = str.split(File::PATH_SEPARATOR).map {|path| Pathname(path) }
29
80
  end
30
81
 
31
82
  opts.on("--output=[BASE]", "The directory where the RBS files are saved at (defaults to STDOUT if not specified)") do
@@ -37,17 +88,17 @@ module RBS
37
88
  end
38
89
  end.parse!(args)
39
90
 
40
- base_path = Pathname.pwd + base_path
91
+ logger.debug { "base_paths = #{base_paths.join(File::PATH_SEPARATOR)}, output_path = #{output_path}" }
41
92
 
42
- logger.debug { "base_path = #{base_path}, output_path = #{output_path}" }
43
-
44
- targets = args.flat_map do
45
- path = Pathname(_1)
93
+ if output_path
94
+ calculator = PathCalculator.new(Pathname.pwd, base_paths, output_path)
95
+ end
46
96
 
97
+ targets = args.flat_map { Pathname.glob(_1) }.flat_map do |path|
47
98
  if path.directory?
48
99
  pattern = path + "**/*.rb"
49
100
  Pathname.glob(pattern.to_s)
50
- else
101
+ else
51
102
  path
52
103
  end
53
104
  end
@@ -58,12 +109,14 @@ module RBS
58
109
  count = 0
59
110
 
60
111
  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")
112
+ absolute_path = Pathname.pwd + target
64
113
 
65
- unless output.to_s.start_with?(output_path.to_s)
66
- raise "Cannot calculate the output file path for #{target} in #{output_path}"
114
+ if output_path && calculator
115
+ output_file_path = calculator.calculate(absolute_path)
116
+ if output_file_path
117
+ output = output_file_path.sub_ext(".rbs")
118
+ else
119
+ raise "Cannot calculate the output file path for #{target} in #{output_path}: calculated = #{output}"
67
120
  end
68
121
 
69
122
  logger.debug { "Generating #{output} from #{target} ..." }
@@ -84,14 +137,18 @@ module RBS
84
137
  output.parent.mkpath
85
138
  end
86
139
 
87
- logger.debug { "Writing RBS file to #{output}..." }
88
- output.write(writer.output)
140
+ if output.file? && output.read == writer.output
141
+ logger.debug { "Skip writing identical RBS file" }
142
+ else
143
+ logger.debug { "Writing RBS file to #{output}..." }
144
+ output.write(writer.output)
145
+ count += 1
146
+ end
89
147
  else
90
148
  stdout.puts writer.output
91
149
  stdout.puts
150
+ count += 1
92
151
  end
93
-
94
- count += 1
95
152
  else
96
153
  logger.debug { "Skipping #{target} because `# rbs_inline: enabled` comment not found" }
97
154
  end
@@ -1,5 +1,3 @@
1
- # rbs_inline: enabled
2
-
3
1
  # @rbs use Prism::*
4
2
 
5
3
  module RBS
@@ -50,9 +48,10 @@ module RBS
50
48
  instance.comments[result.line_range.end] = result
51
49
  end
52
50
 
53
- if result.comments.none? {|comment| comment.location.slice =~ /\A# rbs_inline: enabled\Z/}
54
- return
55
- end
51
+ with_magic_comment = result.comments.any? {|comment| comment.location.slice =~ /\A# rbs_inline: enabled\Z/}
52
+ with_annotation = annots.any? {|result| result.annotations.any? }
53
+
54
+ return unless with_magic_comment || with_annotation
56
55
 
57
56
  uses = [] #: Array[AST::Annotations::Use]
58
57
  annots.each do |annot|
@@ -71,7 +70,7 @@ module RBS
71
70
  ]
72
71
  end
73
72
 
74
- # @rbs rturns AST::Declarations::ModuleDecl | AST::Declarations::ClassDecl | nil
73
+ # @rbs returns AST::Declarations::ModuleDecl | AST::Declarations::ClassDecl | nil
75
74
  def current_class_module_decl
76
75
  surrounding_decls.last
77
76
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RBS
4
4
  module Inline
5
- VERSION = "0.1.0"
5
+ VERSION = "0.2.0"
6
6
  end
7
7
  end
@@ -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
@@ -34,8 +34,8 @@ module RBS
34
34
  # @rbs returns [Array[AST::Annotations::Use], Array[AST::Declarations::t]]?
35
35
  def self.parse: (ParseResult[ProgramNode] result) -> [ Array[AST::Annotations::Use], Array[AST::Declarations::t] ]?
36
36
 
37
- # @rbs rturns AST::Declarations::ModuleDecl | AST::Declarations::ClassDecl | nil
38
- def current_class_module_decl: () -> untyped
37
+ # @rbs returns AST::Declarations::ModuleDecl | AST::Declarations::ClassDecl | nil
38
+ def current_class_module_decl: () -> (AST::Declarations::ModuleDecl | AST::Declarations::ClassDecl | nil)
39
39
 
40
40
  # @rbs returns AST::Declarations::ModuleDecl | AST::Declarations::ClassDecl
41
41
  def current_class_module_decl!: () -> (AST::Declarations::ModuleDecl | AST::Declarations::ClassDecl)
metadata CHANGED
@@ -1,14 +1,14 @@
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.2.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-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: prism