decode 0.24.3 → 0.24.5
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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/bake/decode/rbs.rb +1 -1
- data/context/coverage.md +1 -1
- data/context/getting-started.md +1 -1
- data/context/ruby-documentation.md +3 -3
- data/context/types.md +127 -0
- data/lib/decode/comment/attribute.rb +4 -1
- data/lib/decode/comment/constant.rb +47 -0
- data/lib/decode/comment/node.rb +32 -12
- data/lib/decode/comment/option.rb +1 -1
- data/lib/decode/comment/parameter.rb +5 -1
- data/lib/decode/comment/rbs.rb +8 -8
- data/lib/decode/comment/tag.rb +13 -1
- data/lib/decode/comment/tags.rb +16 -5
- data/lib/decode/comment/text.rb +1 -0
- data/lib/decode/comment/yields.rb +5 -1
- data/lib/decode/definition.rb +33 -31
- data/lib/decode/documentation.rb +10 -5
- data/lib/decode/index.rb +12 -7
- data/lib/decode/language/generic.rb +10 -1
- data/lib/decode/language/reference.rb +7 -4
- data/lib/decode/language/ruby/class.rb +2 -2
- data/lib/decode/language/ruby/code.rb +21 -3
- data/lib/decode/language/ruby/definition.rb +15 -3
- data/lib/decode/language/ruby/generic.rb +2 -1
- data/lib/decode/language/ruby/parser.rb +132 -91
- data/lib/decode/language/ruby/reference.rb +4 -1
- data/lib/decode/language/ruby/segment.rb +2 -2
- data/lib/decode/languages.rb +29 -8
- data/lib/decode/location.rb +15 -1
- data/lib/decode/rbs/class.rb +91 -14
- data/lib/decode/rbs/generator.rb +67 -11
- data/lib/decode/rbs/method.rb +394 -68
- data/lib/decode/rbs/module.rb +81 -5
- data/lib/decode/rbs/type.rb +52 -0
- data/lib/decode/rbs/wrapper.rb +10 -3
- data/lib/decode/scope.rb +2 -2
- data/lib/decode/segment.rb +3 -2
- data/lib/decode/source.rb +5 -14
- data/lib/decode/syntax/rewriter.rb +4 -1
- data/lib/decode/trie.rb +28 -20
- data/lib/decode/version.rb +2 -1
- data/readme.md +6 -0
- data/releases.md +6 -0
- data/sig/decode.rbs +503 -113
- data.tar.gz.sig +0 -0
- metadata +4 -1
- metadata.gz.sig +0 -0
data/lib/decode/rbs/module.rb
CHANGED
@@ -5,6 +5,8 @@
|
|
5
5
|
|
6
6
|
require "rbs"
|
7
7
|
require_relative "wrapper"
|
8
|
+
require_relative "method"
|
9
|
+
require_relative "type"
|
8
10
|
|
9
11
|
module Decode
|
10
12
|
module RBS
|
@@ -18,18 +20,29 @@ module Decode
|
|
18
20
|
end
|
19
21
|
|
20
22
|
# Convert the module definition to RBS AST
|
21
|
-
|
23
|
+
# @parameter method_definitions [Array(Method)] The method definitions to convert.
|
24
|
+
# @parameter constant_definitions [Array(Constant)] The constant definitions to convert.
|
25
|
+
# @parameter attribute_definitions [Array(Attribute)] The attribute definitions to convert.
|
26
|
+
# @parameter index [Index?] The index for resolving references.
|
27
|
+
# @returns [RBS::AST::Declarations::Module] The RBS AST for the module.
|
28
|
+
def to_rbs_ast(method_definitions = [], constant_definitions = [], attribute_definitions = [], index = nil)
|
22
29
|
name = simple_name_to_rbs(@definition.name)
|
23
|
-
comment =
|
30
|
+
comment = self.comment
|
24
31
|
|
25
32
|
# Build method definitions
|
26
33
|
methods = method_definitions.map{|method_def| Method.new(method_def).to_rbs_ast(index)}.compact
|
27
34
|
|
35
|
+
# Build constant definitions:
|
36
|
+
constants = constant_definitions.map{|const_def| build_constant_rbs(const_def)}.compact
|
37
|
+
|
38
|
+
# Build attribute definitions and infer instance variable types:
|
39
|
+
attributes, instance_variables = build_attributes_rbs(attribute_definitions)
|
40
|
+
|
28
41
|
::RBS::AST::Declarations::Module.new(
|
29
42
|
name: name,
|
30
43
|
type_params: [],
|
31
44
|
self_types: [],
|
32
|
-
members: methods,
|
45
|
+
members: constants + attributes + instance_variables + methods,
|
33
46
|
annotations: [],
|
34
47
|
location: nil,
|
35
48
|
comment: comment
|
@@ -38,11 +51,74 @@ module Decode
|
|
38
51
|
|
39
52
|
private
|
40
53
|
|
41
|
-
#
|
54
|
+
# Build a constant RBS declaration.
|
55
|
+
def build_constant_rbs(constant_definition)
|
56
|
+
# Look for @constant tags in the constant's documentation:
|
57
|
+
documentation = constant_definition.documentation
|
58
|
+
constant_tags = documentation&.filter(Decode::Comment::Constant)&.to_a
|
59
|
+
|
60
|
+
if constant_tags&.any?
|
61
|
+
type_string = constant_tags.first.type.strip
|
62
|
+
type = ::Decode::RBS::Type.parse(type_string)
|
63
|
+
|
64
|
+
::RBS::AST::Declarations::Constant.new(
|
65
|
+
name: constant_definition.name.to_sym,
|
66
|
+
type: type,
|
67
|
+
location: nil,
|
68
|
+
comment: nil
|
69
|
+
)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Convert a simple name to RBS TypeName (not qualified).
|
42
74
|
def simple_name_to_rbs(name)
|
43
75
|
::RBS::TypeName.new(name: name.to_sym, namespace: ::RBS::Namespace.empty)
|
44
76
|
end
|
45
77
|
|
78
|
+
# Build attribute RBS declarations and infer instance variable types.
|
79
|
+
# @parameter attribute_definitions [Array] Array of Attribute definition objects
|
80
|
+
# @returns [Array] A tuple of [attribute_declarations, instance_variable_declarations]
|
81
|
+
def build_attributes_rbs(attribute_definitions)
|
82
|
+
attributes = []
|
83
|
+
instance_variables = []
|
84
|
+
|
85
|
+
# Create a mapping from attribute names to their types:
|
86
|
+
attribute_types = {}
|
87
|
+
|
88
|
+
attribute_definitions.each do |attribute_definition|
|
89
|
+
# Extract @attribute type annotation from documentation:
|
90
|
+
documentation = attribute_definition.documentation
|
91
|
+
attribute_tags = documentation&.filter(Decode::Comment::Attribute)&.to_a
|
92
|
+
|
93
|
+
if attribute_tags&.any?
|
94
|
+
type_string = attribute_tags.first.type.strip
|
95
|
+
type = ::Decode::RBS::Type.parse(type_string)
|
96
|
+
|
97
|
+
attribute_types[attribute_definition.name] = type
|
98
|
+
|
99
|
+
# Generate attr_reader RBS declaration:
|
100
|
+
attributes << ::RBS::AST::Members::AttrReader.new(
|
101
|
+
name: attribute_definition.name.to_sym,
|
102
|
+
type: type,
|
103
|
+
ivar_name: :"@#{attribute_definition.name}",
|
104
|
+
kind: :instance,
|
105
|
+
annotations: [],
|
106
|
+
location: nil,
|
107
|
+
comment: nil
|
108
|
+
)
|
109
|
+
|
110
|
+
# Generate instance variable declaration:
|
111
|
+
instance_variables << ::RBS::AST::Members::InstanceVariable.new(
|
112
|
+
name: :"@#{attribute_definition.name}",
|
113
|
+
type: type,
|
114
|
+
location: nil,
|
115
|
+
comment: nil
|
116
|
+
)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
[attributes, instance_variables]
|
121
|
+
end
|
46
122
|
end
|
47
123
|
end
|
48
|
-
end
|
124
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2024, by Samuel Williams.
|
5
|
+
|
6
|
+
require "rbs"
|
7
|
+
require "console"
|
8
|
+
|
9
|
+
module Decode
|
10
|
+
module RBS
|
11
|
+
# Utilities for working with RBS types.
|
12
|
+
module Type
|
13
|
+
# Check if an RBS type represents a nullable/optional type
|
14
|
+
# This method recursively traverses the type tree to find nil anywhere
|
15
|
+
# @parameter rbs_type [untyped] The RBS type to check for nullability.
|
16
|
+
# @returns [bool] True if the type can be nil, false otherwise.
|
17
|
+
def self.nullable?(rbs_type)
|
18
|
+
case rbs_type
|
19
|
+
when ::RBS::Types::Optional
|
20
|
+
# Type? form - directly optional
|
21
|
+
true
|
22
|
+
when ::RBS::Types::Union
|
23
|
+
# Type | nil form - recursively check all union members
|
24
|
+
rbs_type.types.any? {|type| nullable?(type)}
|
25
|
+
when ::RBS::Types::Tuple
|
26
|
+
# [Type] form - recursively check all tuple elements
|
27
|
+
rbs_type.types.any? {|type| nullable?(type)}
|
28
|
+
when ::RBS::Types::Bases::Nil
|
29
|
+
# Direct nil type
|
30
|
+
true
|
31
|
+
else
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Parse a type string and convert it to RBS type
|
37
|
+
# @parameter type_string [String] The type string to parse.
|
38
|
+
# @returns [untyped] The parsed RBS type object.
|
39
|
+
def self.parse(type_string)
|
40
|
+
# This is for backwards compatibility with the old syntax, eventually we will emit warnings for these:
|
41
|
+
type_string = type_string.tr("()", "[]")
|
42
|
+
type_string.gsub!(/\s*\| Nil/, "?")
|
43
|
+
type_string.gsub!("Boolean", "bool")
|
44
|
+
|
45
|
+
return ::RBS::Parser.parse_type(type_string)
|
46
|
+
rescue => error
|
47
|
+
warn("Failed to parse type string: #{type_string}") if $DEBUG
|
48
|
+
return ::RBS::Parser.parse_type("untyped")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/decode/rbs/wrapper.rb
CHANGED
@@ -14,6 +14,7 @@ module Decode
|
|
14
14
|
def initialize(definition)
|
15
15
|
@definition = definition
|
16
16
|
@tags = nil
|
17
|
+
@comment = nil
|
17
18
|
end
|
18
19
|
|
19
20
|
# Extract RBS tags from the definition's documentation.
|
@@ -22,6 +23,12 @@ module Decode
|
|
22
23
|
@tags ||= extract_tags
|
23
24
|
end
|
24
25
|
|
26
|
+
# Extract comment from the definition's documentation.
|
27
|
+
# @returns [RBS::AST::Comment?] The RBS comment object.
|
28
|
+
def comment
|
29
|
+
@comment ||= extract_comment
|
30
|
+
end
|
31
|
+
|
25
32
|
private
|
26
33
|
|
27
34
|
# Extract RBS tags from the definition's documentation.
|
@@ -34,13 +41,13 @@ module Decode
|
|
34
41
|
|
35
42
|
# Extract comment from definition documentation.
|
36
43
|
# @parameter definition [Definition] The definition to extract comment from (defaults to @definition).
|
37
|
-
# @returns [RBS::AST::Comment
|
44
|
+
# @returns [RBS::AST::Comment?] The extracted comment or nil if no documentation.
|
38
45
|
def extract_comment(definition = @definition)
|
39
46
|
documentation = definition.documentation
|
40
47
|
return nil unless documentation
|
41
48
|
|
42
49
|
# Extract the main description text (non-tag content)
|
43
|
-
comment_lines = []
|
50
|
+
comment_lines = [] #: Array[String]
|
44
51
|
|
45
52
|
documentation.children&.each do |child|
|
46
53
|
if child.is_a?(Decode::Comment::Text)
|
@@ -61,4 +68,4 @@ module Decode
|
|
61
68
|
end
|
62
69
|
end
|
63
70
|
end
|
64
|
-
end
|
71
|
+
end
|
data/lib/decode/scope.rb
CHANGED
@@ -10,11 +10,11 @@ module Decode
|
|
10
10
|
class Scope < Definition
|
11
11
|
# @returns [String] The name of the scope.
|
12
12
|
def short_form
|
13
|
-
|
13
|
+
name.to_s
|
14
14
|
end
|
15
15
|
|
16
16
|
# Scopes are always containers.
|
17
|
-
# @returns [
|
17
|
+
# @returns [bool] Always `true`.
|
18
18
|
def container?
|
19
19
|
true
|
20
20
|
end
|
data/lib/decode/segment.rb
CHANGED
@@ -20,6 +20,7 @@ module Decode
|
|
20
20
|
def initialize(comments, language)
|
21
21
|
@comments = comments
|
22
22
|
@language = language
|
23
|
+
@documentation = nil
|
23
24
|
end
|
24
25
|
|
25
26
|
# @attribute [Array(String)] The preceeding comments.
|
@@ -29,7 +30,7 @@ module Decode
|
|
29
30
|
attr :language
|
30
31
|
|
31
32
|
# An interface for accsssing the documentation of the definition.
|
32
|
-
# @returns [Documentation
|
33
|
+
# @returns [Documentation?] A {Documentation} instance if this definition has comments.
|
33
34
|
def documentation
|
34
35
|
if @comments&.any?
|
35
36
|
@documentation ||= Documentation.new(@comments, @language)
|
@@ -37,7 +38,7 @@ module Decode
|
|
37
38
|
end
|
38
39
|
|
39
40
|
# The source code trailing the comments.
|
40
|
-
# @returns [String
|
41
|
+
# @returns [String?]
|
41
42
|
def code
|
42
43
|
end
|
43
44
|
end
|
data/lib/decode/source.rb
CHANGED
@@ -13,24 +13,15 @@ module Decode
|
|
13
13
|
# @parameter language [Language::Generic] The language parser to use.
|
14
14
|
def initialize(path, language)
|
15
15
|
@path = path
|
16
|
-
@buffer = nil
|
17
16
|
@language = language
|
17
|
+
|
18
|
+
@buffer = nil
|
18
19
|
end
|
19
20
|
|
20
21
|
# The path of the source file.
|
21
|
-
# @attribute [
|
22
|
+
# @attribute [StringPath] A file-system path to the source file.
|
22
23
|
attr :path
|
23
24
|
|
24
|
-
# The relative path of the source, if it is known.
|
25
|
-
# @returns [String] The relative path or the full path if relative path is unknown.
|
26
|
-
def relative_path
|
27
|
-
if @path.respond_to?(:relative_path)
|
28
|
-
@path.relative_path
|
29
|
-
else
|
30
|
-
@path
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
25
|
# The language of the source file.
|
35
26
|
# @attribute [Language::Generic] The language parser for this source.
|
36
27
|
attr :language
|
@@ -62,8 +53,8 @@ module Decode
|
|
62
53
|
end
|
63
54
|
|
64
55
|
# Generate code representation with optional index for link resolution.
|
65
|
-
# @parameter index [Index] Optional index for resolving links.
|
66
|
-
# @parameter relative_to [Definition] Optional definition to resolve relative references.
|
56
|
+
# @parameter index [Index?] Optional index for resolving links.
|
57
|
+
# @parameter relative_to [Definition?] Optional definition to resolve relative references.
|
67
58
|
# @returns [String] The formatted code representation.
|
68
59
|
def code(index = nil, relative_to: nil)
|
69
60
|
@language.code_for(self.read, index, relative_to: relative_to)
|
@@ -14,19 +14,22 @@ module Decode
|
|
14
14
|
@matches = []
|
15
15
|
end
|
16
16
|
|
17
|
+
# @attribute [String] The text to rewrite.
|
17
18
|
attr :text
|
18
19
|
|
20
|
+
# @attribute [Array[Match]] The matches to apply.
|
19
21
|
attr :matches
|
20
22
|
|
21
23
|
# Add a match to the rewriter.
|
22
24
|
# @parameter match [Match] The match to add.
|
23
25
|
def << match
|
24
26
|
@matches << match
|
27
|
+
return self
|
25
28
|
end
|
26
29
|
|
27
30
|
# Returns a chunk of raw text with no formatting.
|
28
31
|
def text_for(range)
|
29
|
-
@text[range]
|
32
|
+
@text[range] || ""
|
30
33
|
end
|
31
34
|
|
32
35
|
# Apply all matches to generate the rewritten output.
|
data/lib/decode/trie.rb
CHANGED
@@ -7,13 +7,14 @@ require_relative "source"
|
|
7
7
|
|
8
8
|
module Decode
|
9
9
|
# Represents a prefix-trie data structure for fast lexical lookups.
|
10
|
+
# @rbs generic T
|
10
11
|
class Trie
|
11
12
|
# Represents a single node in the trie.
|
12
13
|
class Node
|
13
|
-
# Initialize
|
14
|
+
# Initialize an empty node.
|
14
15
|
def initialize
|
16
|
+
@children = {}
|
15
17
|
@values = nil
|
16
|
-
@children = Hash.new
|
17
18
|
end
|
18
19
|
|
19
20
|
# Generate a string representation of this node.
|
@@ -26,17 +27,17 @@ module Decode
|
|
26
27
|
alias to_s inspect
|
27
28
|
|
28
29
|
# A mutable array of all values that terminate at this node.
|
29
|
-
# @attribute [Array
|
30
|
+
# @attribute [Array[T]?] The values stored at this node, or nil if no values.
|
30
31
|
attr_accessor :values
|
31
32
|
|
32
33
|
# A hash table of all children nodes, indexed by name.
|
33
|
-
# @attribute [Hash(
|
34
|
+
# @attribute [Hash(Symbol, Node)] Child nodes indexed by their path component.
|
34
35
|
attr :children
|
35
36
|
|
36
37
|
# Look up a lexical path starting at this node.
|
37
|
-
# @parameter path [Array(
|
38
|
+
# @parameter path [Array(Symbol)] The path to resolve.
|
38
39
|
# @parameter index [Integer] The current index in the path (used for recursion).
|
39
|
-
# @returns [Node
|
40
|
+
# @returns [Node?] The node at the specified path, or nil if not found.
|
40
41
|
def lookup(path, index = 0)
|
41
42
|
if index < path.size
|
42
43
|
if child = @children[path[index]]
|
@@ -49,11 +50,12 @@ module Decode
|
|
49
50
|
|
50
51
|
# Traverse the trie from this node.
|
51
52
|
# Invoke `descend.call` to traverse the children of the current node.
|
52
|
-
# @parameter path [Array(
|
53
|
+
# @parameter path [Array(Symbol)] The current lexical path.
|
53
54
|
# @yields {|path, node, descend| ...} Called for each node during traversal.
|
54
|
-
# @parameter path [Array(
|
55
|
+
# @parameter path [Array(Symbol)] The current lexical path.
|
55
56
|
# @parameter node [Node] The current node which is being traversed.
|
56
57
|
# @parameter descend [Proc] The recursive method for traversing children.
|
58
|
+
# @rbs (?Array[Symbol]) { (Array[Symbol], Node, Proc) -> void } -> void
|
57
59
|
def traverse(path = [], &block)
|
58
60
|
descend = lambda do
|
59
61
|
@children.each do |name, node|
|
@@ -75,8 +77,8 @@ module Decode
|
|
75
77
|
attr :root
|
76
78
|
|
77
79
|
# Insert the specified value at the given path into the trie.
|
78
|
-
# @parameter path [Array(
|
79
|
-
# @parameter value [
|
80
|
+
# @parameter path [Array(Symbol)] The lexical path where the value will be inserted.
|
81
|
+
# @parameter value [T] The value to insert at the specified path.
|
80
82
|
def insert(path, value)
|
81
83
|
node = @root
|
82
84
|
|
@@ -86,24 +88,29 @@ module Decode
|
|
86
88
|
end
|
87
89
|
|
88
90
|
# Add the value to the target node:
|
89
|
-
|
91
|
+
if node.values
|
92
|
+
node.values << value
|
93
|
+
else
|
94
|
+
node.values = [value]
|
95
|
+
end
|
90
96
|
end
|
91
97
|
|
92
98
|
# Lookup the values at the specified path.
|
93
|
-
# @parameter path [Array(
|
94
|
-
# @returns [Node
|
99
|
+
# @parameter path [Array(Symbol)] The lexical path which contains the values.
|
100
|
+
# @returns [Node?] The node at the specified path, or nil if not found.
|
95
101
|
def lookup(path)
|
96
102
|
@root.lookup(path)
|
97
103
|
end
|
98
104
|
|
99
105
|
# Enumerate all lexical scopes under the specified path.
|
100
|
-
# @parameter path [Array(
|
106
|
+
# @parameter path [Array(Symbol)] The starting path to enumerate from.
|
101
107
|
# @yields {|path, values| ...} Called for each path with values.
|
102
|
-
# @parameter path [Array(
|
103
|
-
# @parameter values [Array
|
108
|
+
# @parameter path [Array(Symbol)] The lexical path.
|
109
|
+
# @parameter values [Array[T]?] The values that exist at the given path.
|
110
|
+
# @rbs (?Array[Symbol]) { (Array[Symbol], (Array[T] | nil)) -> void } -> void
|
104
111
|
def each(path = [], &block)
|
105
|
-
if node = @root.lookup(path)
|
106
|
-
node.traverse do |path, node, descend|
|
112
|
+
if node = @root.lookup(path, 0)
|
113
|
+
node.traverse(path) do |path, node, descend|
|
107
114
|
yield path, node.values
|
108
115
|
|
109
116
|
descend.call
|
@@ -113,11 +120,12 @@ module Decode
|
|
113
120
|
|
114
121
|
# Traverse the trie starting from the specified path.
|
115
122
|
# See {Node#traverse} for details.
|
116
|
-
# @parameter path [Array(
|
123
|
+
# @parameter path [Array(Symbol)] The starting path to traverse from.
|
117
124
|
# @yields {|path, node, descend| ...} Called for each node during traversal.
|
125
|
+
# @rbs (?Array[Symbol]) { (Array[Symbol], Node, Proc) -> void } -> void
|
118
126
|
def traverse(path = [], &block)
|
119
127
|
if node = @root.lookup(path)
|
120
|
-
node.traverse(&block)
|
128
|
+
node.traverse(path, &block)
|
121
129
|
end
|
122
130
|
end
|
123
131
|
end
|
data/lib/decode/version.rb
CHANGED
data/readme.md
CHANGED
@@ -22,6 +22,12 @@ Please see the [project documentation](https://socketry.github.io/decode/) for m
|
|
22
22
|
|
23
23
|
Please see the [project releases](https://socketry.github.io/decode/releases/index) for all releases.
|
24
24
|
|
25
|
+
### v0.24.4
|
26
|
+
|
27
|
+
- Add support for `@constant [Type] Description.` tags.
|
28
|
+
- Add support for instance variable type inference from `@attribute` tags.
|
29
|
+
- Add support for method visibility in RBS output.
|
30
|
+
|
25
31
|
### v0.24.0
|
26
32
|
|
27
33
|
- [Introduce support for RBS signature generation](https://socketry.github.io/decode/releases/index#introduce-support-for-rbs-signature-generation)
|
data/releases.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Releases
|
2
2
|
|
3
|
+
## v0.24.4
|
4
|
+
|
5
|
+
- Add support for `@constant [Type] Description.` tags.
|
6
|
+
- Add support for instance variable type inference from `@attribute` tags.
|
7
|
+
- Add support for method visibility in RBS output.
|
8
|
+
|
3
9
|
## v0.24.0
|
4
10
|
|
5
11
|
### Introduce support for RBS signature generation
|