rbs-patch 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0fd64b165683dbb9350cc535cd07176d1e5b74fd3814b72b7d4e94ad9e8deafe
4
- data.tar.gz: 48b0f85c818af5f23bafd3d7b7acbe281b5f3c7ba8c26f270d04c1e1b910f9d3
3
+ metadata.gz: 3a45288c145d3c26618712866de56ebeebabc1034e29960877834d5fc0518c58
4
+ data.tar.gz: 12ae5d9f8fcd5721f05bac232b9dd10b8de6a23c47c73eba19801e65b3b33143
5
5
  SHA512:
6
- metadata.gz: 1548885b396ccff12ebe6ae6c1b5272133248623db7360378283b9f707f17462c1bd7e5d26a9107865277e23fad005f9f83b779cdca992943bc33dccefc3bad6
7
- data.tar.gz: 16da1d9c8b866dc6a4c6c0a6ed8713da52db81556a8e850f26badb6df0f25ba1fd883e81260f26764da667eea2fcb2dba0ceed4f975b7e4b71baeafe04ef5fdc
6
+ metadata.gz: b939dc31112ba51d86e88c66b0000ac3abcf7baa9d5b57db79af3ee8003dc2cafbb90e2d6c4d08ffb53c3aebd8bb3c1d2b1bec7609911d03f7ed12105621a2b4
7
+ data.tar.gz: '00998b3ec8d70b87cf45177244ed9dc0e8bdb572f95eb33e07f422a137a67922a6add9ddb06a00777de1eb00aa69eb787930b2e6b33704b890064e4c1b2683c5'
data/Rakefile CHANGED
@@ -9,4 +9,10 @@ require "rubocop/rake_task"
9
9
 
10
10
  RuboCop::RakeTask.new
11
11
 
12
- task default: %i[test rubocop]
12
+ require "steep/rake_task"
13
+ Steep::RakeTask.new do |t|
14
+ t.check.severity_level = :error
15
+ t.watch.verbose
16
+ end
17
+
18
+ task default: %i[test rubocop steep]
data/Steepfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ target :lib do
4
+ signature "sig"
5
+
6
+ check "lib"
7
+ library "rbs"
8
+ end
data/exe/rbs-patch CHANGED
@@ -6,6 +6,8 @@ require "rbs/patch"
6
6
  require "pathname"
7
7
 
8
8
  if ARGV.empty?
9
+ puts "rbs-patch #{RBS::Patch::VERSION}"
10
+ puts ""
9
11
  puts "Usage: rbs-patch RBS_FILE_PATH [RBS_FILE_PATH ...]"
10
12
  puts ""
11
13
  puts "Examples:"
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RBS
4
4
  class Patch
5
- VERSION = "0.1.2"
5
+ VERSION = "0.1.3"
6
6
  end
7
7
  end
data/lib/rbs/patch.rb CHANGED
@@ -12,7 +12,14 @@ module RBS
12
12
  ANNOTATION_PREPEND_BEFORE = /\Apatch:prepend_before:(.*)\Z/
13
13
 
14
14
  def initialize
15
- @env = ::RBS::Environment.new
15
+ @decls = []
16
+ end
17
+
18
+ def to_s
19
+ io = ::StringIO.new
20
+ ::RBS::Writer.new(out: io).write(@decls)
21
+ io.rewind
22
+ io.read || ""
16
23
  end
17
24
 
18
25
  def apply(source = nil, path: nil)
@@ -22,83 +29,140 @@ module RBS
22
29
  next if files.include?(path)
23
30
 
24
31
  files << path
25
- apply Buffer.new(name: path, content: path.read(encoding: "UTF-8"))
32
+ apply ::RBS::Buffer.new(name: path, content: path.read(encoding: "UTF-8"))
26
33
  end
27
34
  return
28
35
  end
29
36
 
30
- _, dirs, decls = ::RBS::Parser.parse_signature(source)
31
- @env.add_source(::RBS::Source::RBS.new(source, dirs, decls))
32
- @env.class_decls.each_value.map do |class_entry|
33
- class_entry.context_decls.map { _2 }.inject do |decl_a, decl_b|
34
- decl_b.members.delete_if do |member_b|
35
- ope, arg = if member_b.annotations.any? { |a| a.string == ANNOTATION_OVERRIDE }
36
- [:override, nil]
37
- elsif member_b.annotations.any? { |a| a.string == ANNOTATION_DELETE }
38
- [:delete, nil]
39
- elsif (anno = member_b.annotations.find { |a| a.string.match(ANNOTATION_APPEND_AFTER) })
40
- [:append_after, anno.string.match(ANNOTATION_APPEND_AFTER)[1]]
41
- elsif (anno = member_b.annotations.find { |a| a.string.match(ANNOTATION_PREPEND_BEFORE) })
42
- [:prepend_before, anno.string.match(ANNOTATION_PREPEND_BEFORE)[1]]
43
- end
44
-
45
- next unless ope
46
-
47
- case ope
48
- when :override
49
- index = decl_a.members.find_index { |member_a| member_a.name == member_b.name }
50
- if index
51
- decl_a.members[index] = decl_a.members[index].update(overloads: member_b.overloads)
52
- true
53
- else
54
- false
55
- end
56
- when :delete
57
- decl_a.members.reject! { |member_a| member_a.name == member_b.name }
58
- when :append_after, :prepend_before
59
- target_name = arg.to_sym
60
- index = decl_a.members.find_index { |member_a| member_a.name == target_name }
61
- if index
62
- if ope == :append_after
63
- offset = 1
64
- annotations = member_b.annotations.reject { |a| a.string.match(ANNOTATION_APPEND_AFTER) }
65
- else
66
- offset = 0
67
- annotations = member_b.annotations.reject { |a| a.string.match(ANNOTATION_PREPEND_BEFORE) }
68
- end
69
- decl_a.members.insert(index + offset, member_b.update(annotations:))
70
- true
71
- else
72
- false
73
- end
74
- end
75
- end
76
- decl_a
37
+ _, _, decls = ::RBS::Parser.parse_signature(source)
38
+ walk(decls) do |decl, name|
39
+ ope, arg = process_annotations(decl.annotations) if decl.respond_to?(:annotations) # steep:ignore
40
+
41
+ case ope
42
+ when :override
43
+ override(name, with: decl)
44
+ when :delete
45
+ delete(name)
46
+ when :append_after
47
+ add(decl, to: name, after: arg)
48
+ when :prepend_before
49
+ add(decl, to: name, before: arg)
50
+ else
51
+ add(decl, to: name)
77
52
  end
78
53
  end
79
54
  end
80
55
 
81
- def to_s
82
- decls = @env.class_decls.each_value.map do |class_entry|
83
- decls = class_entry.context_decls.map { _2 }
84
- decls.each_with_object(decls.first.update(members: [])) do |decl, new_decl|
85
- # merge multiple class decls into a single one
86
- new_decl.members.concat decl.members
56
+ private
57
+
58
+ def walk(decls, name_stack = [], &block)
59
+ decls.each do |decl|
60
+ name = if decl.is_a?(::RBS::AST::Declarations::AliasDecl) # rubocop:disable Style/CaseLikeIf
61
+ decl.new_name.to_s
62
+ elsif decl.is_a?(::RBS::AST::Declarations::Base)
63
+ decl.name.to_s
64
+ elsif decl.is_a?(::RBS::AST::Members::LocationOnly)
65
+ ""
66
+ elsif decl.is_a?(::RBS::AST::Members::Alias) # rubocop:disable Lint/DuplicateBranch
67
+ decl.new_name.to_s
68
+ else # rubocop:disable Lint/DuplicateBranch
69
+ # ::RBS::AST::Members::t
70
+ decl.name.to_s
71
+ end
72
+ name_stack << name
73
+ if decl.is_a?(::RBS::AST::Members::Base)
74
+ yield decl, "#{name_stack[..-2]&.join("::")}##{name_stack[-1]}"
75
+ else
76
+ yield decl, name_stack.join("::")
87
77
  end
78
+ walk(decl.members, name_stack, &block) if decl.is_a?(::RBS::AST::Declarations::NestedDeclarationHelper)
79
+ name_stack.pop
88
80
  end
81
+ end
89
82
 
90
- classes = Set[]
91
- decls.each do |decl|
92
- decl.members.each do |member|
93
- classes << member.name if member.respond_to?(:name)
83
+ def decl_map
84
+ # @type var map: Hash[String, ::RBS::Patch::t]
85
+ map = {}
86
+ walk(@decls) { |decl, name| map[name] = decl }
87
+ map
88
+ end
89
+
90
+ def add(decl, to:, after: nil, before: nil)
91
+ map = decl_map
92
+ return if map.key?(to)
93
+
94
+ sep = decl.is_a?(::RBS::AST::Members::Base) ? "#" : "::"
95
+ namespace, = to.rpartition(sep)
96
+
97
+ target = namespace.empty? ? @decls : map[namespace]&.members # steep:ignore
98
+
99
+ # steep:ignore:start
100
+ if target
101
+ if after
102
+ index = target.find_index { |m| m.name.to_s == after }
103
+ target.insert(index + 1, decl) if index
104
+ elsif before
105
+ index = target.find_index { |m| m.name.to_s == before }
106
+ target.insert(index, decl) if index
107
+ else
108
+ target << decl
94
109
  end
110
+ decl.annotations.delete_if { |a| process_annotations([a]) }
111
+ else
112
+ @decls << decl
95
113
  end
96
- decls.delete_if { |c| classes.include?(c.name) }
114
+ # steep:ignore:end
115
+ end
97
116
 
98
- io = ::StringIO.new
99
- RBS::Writer.new(out: io).write(decls)
100
- io.rewind
101
- io.read
117
+ def override(name, with:)
118
+ map = decl_map
119
+ return unless map.key?(name)
120
+
121
+ sep = with.is_a?(::RBS::AST::Members::Base) ? "#" : "::"
122
+ namespace, _, name = name.rpartition(sep)
123
+
124
+ # steep:ignore:start
125
+ if namespace.empty?
126
+ # top level decl
127
+ index = @decls.find_index { |d| d.name.to_s == name }
128
+ @decls[index] = with
129
+ else
130
+ index = map[namespace].members.find_index do |m|
131
+ m.name.to_s == name
132
+ end
133
+ map[namespace].members[index] = with
134
+ end
135
+ with.annotations.delete_if { |a| process_annotations([a]) }
136
+ # steep:ignore:end
137
+ end
138
+
139
+ def delete(name)
140
+ map = decl_map
141
+ return unless map.key?(name)
142
+
143
+ sep = name.index("#") ? "#" : "::"
144
+ namespace, _, name = name.rpartition(sep)
145
+
146
+ # steep:ignore:start
147
+ if namespace.empty?
148
+ # top level decl
149
+ @decls.delete_if { |d| d.name.to_s == name }
150
+ else
151
+ map[namespace].members.delete_if { |m| m.name.to_s == name }
152
+ end
153
+ # steep:ignore:end
154
+ end
155
+
156
+ def process_annotations(annotations) # steep:ignore
157
+ if annotations.any? { |a| a.string == ANNOTATION_OVERRIDE }
158
+ [:override, nil]
159
+ elsif annotations.any? { |a| a.string == ANNOTATION_DELETE }
160
+ [:delete, nil]
161
+ elsif (anno = annotations.find { |a| a.string.match(ANNOTATION_APPEND_AFTER) })
162
+ [:append_after, anno.string.match(ANNOTATION_APPEND_AFTER)&.[](1) || ""]
163
+ elsif (anno = annotations.find { |a| a.string.match(ANNOTATION_PREPEND_BEFORE) })
164
+ [:prepend_before, anno.string.match(ANNOTATION_PREPEND_BEFORE)&.[](1) || ""]
165
+ end
102
166
  end
103
167
  end
104
168
  end
data/sig/rbs/patch.rbs CHANGED
@@ -2,12 +2,36 @@ module RBS
2
2
  class Patch
3
3
  VERSION: String
4
4
 
5
- @env: untyped
5
+ @decls: ::Array[::RBS::AST::Declarations::t]
6
6
 
7
- def initialize: () -> void
7
+ ANNOTATION_OVERRIDE: "patch:override"
8
+
9
+ ANNOTATION_DELETE: "patch:delete"
8
10
 
9
- def apply: (String source?, path: Pathname?) -> void
11
+ ANNOTATION_APPEND_AFTER: ::Regexp
12
+
13
+ ANNOTATION_PREPEND_BEFORE: ::Regexp
14
+
15
+ def initialize: () -> void
10
16
 
11
17
  def to_s: () -> String
18
+
19
+ def apply: (?untyped? String, ?path: Pathname?) -> void
20
+
21
+ type t = ::RBS::AST::Declarations::t | ::RBS::AST::Members::t
22
+
23
+ private
24
+
25
+ def walk: (Array[t] decls, ?Array[String] name_stack) { (::RBS::AST::Declarations::t | ::RBS::AST::Members::t, String) -> void } -> void
26
+
27
+ def decl_map: () -> Hash[String, t]
28
+
29
+ def add: (t decl, to: String, ?after: String?, ?before: String?) -> void
30
+
31
+ def override: (String name, with: t) -> void
32
+
33
+ def delete: (String name) -> void
34
+
35
+ def process_annotations: (Array[::RBS::AST::Annotation] annotations) -> ([:override, nil] | [:delte, nil] | [:append_after, String] | [:prepend_before, String] | nil)
12
36
  end
13
37
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbs-patch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Koji NAKAMURA
@@ -13,16 +13,16 @@ dependencies:
13
13
  name: rbs
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
- - - '='
16
+ - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: 4.0.0.dev.5
18
+ version: '3.0'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
- - - '='
23
+ - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: 4.0.0.dev.5
25
+ version: '3.0'
26
26
  description: RBS::Patch manages RBS (Ruby Signature) type definitions through patches.
27
27
  It applies incremental changes to existing RBS signatures.
28
28
  email:
@@ -35,6 +35,7 @@ files:
35
35
  - LICENSE.txt
36
36
  - README.md
37
37
  - Rakefile
38
+ - Steepfile
38
39
  - exe/rbs-patch
39
40
  - lib/rbs/patch.rb
40
41
  - lib/rbs/patch/version.rb