decode 0.24.3 → 0.24.4

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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/bake/decode/rbs.rb +1 -1
  4. data/context/coverage.md +1 -1
  5. data/context/getting-started.md +1 -1
  6. data/context/ruby-documentation.md +3 -3
  7. data/context/types.md +127 -0
  8. data/lib/decode/comment/attribute.rb +4 -1
  9. data/lib/decode/comment/constant.rb +47 -0
  10. data/lib/decode/comment/node.rb +32 -12
  11. data/lib/decode/comment/option.rb +1 -1
  12. data/lib/decode/comment/parameter.rb +5 -1
  13. data/lib/decode/comment/rbs.rb +8 -8
  14. data/lib/decode/comment/tag.rb +13 -1
  15. data/lib/decode/comment/tags.rb +16 -5
  16. data/lib/decode/comment/text.rb +1 -0
  17. data/lib/decode/comment/yields.rb +5 -1
  18. data/lib/decode/definition.rb +33 -31
  19. data/lib/decode/documentation.rb +10 -5
  20. data/lib/decode/index.rb +12 -7
  21. data/lib/decode/language/generic.rb +10 -1
  22. data/lib/decode/language/reference.rb +7 -4
  23. data/lib/decode/language/ruby/class.rb +2 -2
  24. data/lib/decode/language/ruby/code.rb +21 -3
  25. data/lib/decode/language/ruby/definition.rb +15 -3
  26. data/lib/decode/language/ruby/generic.rb +2 -1
  27. data/lib/decode/language/ruby/parser.rb +132 -91
  28. data/lib/decode/language/ruby/reference.rb +4 -1
  29. data/lib/decode/language/ruby/segment.rb +2 -2
  30. data/lib/decode/languages.rb +29 -8
  31. data/lib/decode/location.rb +12 -1
  32. data/lib/decode/rbs/class.rb +91 -14
  33. data/lib/decode/rbs/generator.rb +67 -11
  34. data/lib/decode/rbs/method.rb +394 -68
  35. data/lib/decode/rbs/module.rb +81 -5
  36. data/lib/decode/rbs/type.rb +51 -0
  37. data/lib/decode/rbs/wrapper.rb +10 -3
  38. data/lib/decode/scope.rb +2 -2
  39. data/lib/decode/segment.rb +3 -2
  40. data/lib/decode/source.rb +5 -14
  41. data/lib/decode/syntax/rewriter.rb +4 -1
  42. data/lib/decode/trie.rb +29 -21
  43. data/lib/decode/version.rb +2 -1
  44. data/readme.md +6 -0
  45. data/releases.md +6 -0
  46. data/sig/decode.rbs +501 -113
  47. data.tar.gz.sig +0 -0
  48. metadata +4 -1
  49. metadata.gz.sig +0 -0
@@ -10,10 +10,11 @@ module Decode
10
10
  class Definition
11
11
  # Initialize the symbol.
12
12
  # @parameter path [Symbol | Array(Symbol)] The path of the definition relatve to the parent.
13
- # @parameter parent [Symbol] The parent lexical scope.
14
- # @parameter language [Language] The language in which the symbol is defined in.
15
- # @parameter comments [Array(String)] The comments associated with the definition.
16
- # @parameter source [Source] The source file containing this definition.
13
+ # @parameter parent [Definition?] The parent lexical scope.
14
+ # @parameter language [Language::Generic?] The language in which the symbol is defined in.
15
+ # @parameter comments [Array(String)?] The comments associated with the definition.
16
+ # @parameter visibility [Symbol] The visibility of the definition.
17
+ # @parameter source [Source?] The source file containing this definition.
17
18
  def initialize(path, parent: nil, language: parent&.language, comments: nil, visibility: :public, source: parent&.source)
18
19
  @path = Array(path).map(&:to_sym)
19
20
 
@@ -23,6 +24,7 @@ module Decode
23
24
 
24
25
  @comments = comments
25
26
  @visibility = visibility
27
+ @documentation = nil
26
28
 
27
29
  @full_path = nil
28
30
  @qualified_name = nil
@@ -37,9 +39,7 @@ module Decode
37
39
  # Generate a string representation of the definition.
38
40
  alias to_s inspect
39
41
 
40
- # The symbol name.
41
- # e.g. `:Decode`.
42
- # @attribute [Symbol]
42
+ # @attribute [Symbol] The symbol name e.g. `:Decode`.
43
43
  def name
44
44
  @path.last
45
45
  end
@@ -48,10 +48,11 @@ module Decode
48
48
  attr :path
49
49
 
50
50
  # The full path to the definition.
51
+ # @returns [Array(Symbol)] The complete path from root to this definition.
51
52
  def full_path
52
53
  @full_path ||= begin
53
- if @parent
54
- @parent.full_path + @path
54
+ if parent = @parent
55
+ parent.full_path + @path
55
56
  else
56
57
  @path
57
58
  end
@@ -62,56 +63,57 @@ module Decode
62
63
  # @returns [Array(Symbol)] The complete path from root to this definition.
63
64
  alias lexical_path full_path
64
65
 
65
- # @attribute [Definition | Nil] The parent definition, defining lexical scope.
66
+ # @attribute [Definition?] The parent definition, defining lexical scope.
66
67
  attr :parent
67
68
 
68
69
  # @attribute [Language::Generic] The language the symbol is defined within.
69
70
  attr :language
70
71
 
71
- # @attribute [Source | Nil] The source file containing this definition.
72
+ # @attribute [Source?] The source file containing this definition.
72
73
  attr :source
73
74
 
74
- # @attribute [Array(String)] The comment lines which directly preceeded the definition.
75
+ # @attribute [Array(String)?] The comment lines which directly preceeded the definition.
75
76
  attr :comments
76
77
 
77
78
  # Whether the definition is considered part of the public interface.
78
79
  # This is used to determine whether the definition should be documented for coverage purposes.
79
- # @returns [Boolean] True if the definition is public.
80
+ # @returns [bool] True if the definition is public.
80
81
  def public?
81
82
  true
82
83
  end
83
84
 
84
85
  # Whether the definition has documentation.
85
- # @returns [Boolean] True if the definition has non-empty comments.
86
+ # @returns [bool] True if the definition has non-empty comments.
86
87
  def documented?
87
- @comments&.any?
88
+ @comments&.any? || false
88
89
  end
89
90
 
90
91
  # The qualified name is an absolute name which includes any and all namespacing.
91
92
  # @returns [String]
92
93
  def qualified_name
93
94
  @qualified_name ||= begin
94
- if @parent
95
- [@parent.qualified_name, self.nested_name].join("::")
95
+ if parent = @parent
96
+ [parent.qualified_name, self.nested_name].join("::")
96
97
  else
97
98
  self.nested_name
98
99
  end
99
100
  end
100
101
  end
101
102
 
102
- # The name relative to the parent.
103
- # @returns [String]
103
+ # @returns [String] The name relative to the parent.
104
104
  def nested_name
105
105
  @nested_name ||= "#{@path.join("::")}"
106
106
  end
107
107
 
108
108
  # Does the definition name match the specified prefix?
109
- # @returns [Boolean]
109
+ # @parameter prefix [String] The prefix to match against.
110
+ # @returns [bool]
110
111
  def start_with?(prefix)
111
112
  self.nested_name.start_with?(prefix)
112
113
  end
113
114
 
114
115
  # Convert this definition into another kind of definition.
116
+ # @parameter kind [Symbol] The kind to convert to.
115
117
  def convert(kind)
116
118
  raise ArgumentError, "Unable to convert #{self} into #{kind}!"
117
119
  end
@@ -119,14 +121,14 @@ module Decode
119
121
  # A short form of the definition.
120
122
  # e.g. `def short_form`.
121
123
  #
122
- # @returns [String | Nil]
124
+ # @returns [String?]
123
125
  def short_form
124
126
  end
125
127
 
126
128
  # A long form of the definition.
127
129
  # e.g. `def initialize(kind, name, comments, **options)`.
128
130
  #
129
- # @returns [String | Nil]
131
+ # @returns [String?]
130
132
  def long_form
131
133
  self.short_form
132
134
  end
@@ -134,50 +136,50 @@ module Decode
134
136
  # A long form which uses the qualified name if possible.
135
137
  # Defaults to {long_form}.
136
138
  #
137
- # @returns [String | Nil]
139
+ # @returns [String?]
138
140
  def qualified_form
139
141
  self.long_form
140
142
  end
141
143
 
142
144
  # Whether the definition spans multiple lines.
143
145
  #
144
- # @returns [Boolean]
146
+ # @returns [bool]
145
147
  def multiline?
146
148
  false
147
149
  end
148
150
 
149
151
  # The full text of the definition.
150
152
  #
151
- # @returns [String | Nil]
153
+ # @returns [String?]
152
154
  def text
153
155
  end
154
156
 
155
157
  # Whether this definition can contain nested definitions.
156
158
  #
157
- # @returns [Boolean]
159
+ # @returns [bool]
158
160
  def container?
159
161
  false
160
162
  end
161
163
 
162
164
  # Whether this represents a single entity to be documented (along with it's contents).
163
165
  #
164
- # @returns [Boolean]
166
+ # @returns [bool]
165
167
  def nested?
166
168
  container?
167
169
  end
168
170
 
169
171
  # Structured access to the definitions comments.
170
172
  #
171
- # @returns [Documentation | Nil] A {Documentation} instance if this definition has comments.
173
+ # @returns [Documentation?] A {Documentation} instance if this definition has comments.
172
174
  def documentation
173
- if @comments&.any?
174
- @documentation ||= Documentation.new(@comments, @language)
175
+ if comments = @comments and comments.any?
176
+ @documentation ||= Documentation.new(comments, @language)
175
177
  end
176
178
  end
177
179
 
178
180
  # The location of the definition.
179
181
  #
180
- # @returns [Location | Nil] A {Location} instance if this definition has a location.
182
+ # @returns [Location?] A {Location} instance if this definition has a location.
181
183
  def location
182
184
  nil
183
185
  end
@@ -7,6 +7,7 @@ require_relative "comment/node"
7
7
 
8
8
  require_relative "comment/tags"
9
9
  require_relative "comment/attribute"
10
+ require_relative "comment/constant"
10
11
  require_relative "comment/parameter"
11
12
  require_relative "comment/option"
12
13
  require_relative "comment/pragma"
@@ -21,20 +22,24 @@ module Decode
21
22
  # Initialize the documentation with an array of comments, within a specific language.
22
23
  #
23
24
  # @parameter comments [Array(String)] An array of comment lines.
24
- # @parameter language [Language] The language in which the comments were extracted.
25
- def initialize(comments, language = nil)
25
+ # @parameter language [Language::Generic?] The language in which the comments were extracted.
26
+ def initialize(comments, language)
27
+ super(nil)
28
+
26
29
  @comments = comments
27
30
  @language = language
28
31
 
29
- language.tags.parse(@comments.dup) do |node|
30
- self.add(node)
32
+ if language
33
+ language.tags.parse(@comments.dup) do |node|
34
+ self.add(node)
35
+ end
31
36
  end
32
37
  end
33
38
 
34
39
  # @attribute [Array(String)] The underlying comments from which the documentation is extracted.
35
40
  attr :comments
36
41
 
37
- # @attribute [Language::Generic] The language in which the documentation was extracted from.
42
+ # @attribute [Language::Generic?] The language in which the documentation was extracted from.
38
43
  attr :language
39
44
  end
40
45
  end
data/lib/decode/index.rb CHANGED
@@ -11,10 +11,11 @@ require_relative "languages"
11
11
  module Decode
12
12
  # Represents a list of definitions organised for quick lookup and lexical enumeration.
13
13
  class Index
14
- # Create and populate an index from the given paths.
15
- # @parameter paths [Array(String)] The paths to index (files, directories, or glob patterns).
14
+ # Create and populate an index from the given paths.
15
+ # @parameter paths [Array(String)] Variable number of paths to index (files, directories, or glob patterns).
16
16
  # @parameter languages [Languages] The languages to support in this index.
17
17
  # @returns [Index] A new index populated with definitions from the given paths.
18
+ # @rbs (*String, ?languages: Languages) -> Index
18
19
  def self.for(*paths, languages: Languages.all)
19
20
  # Resolve all paths to actual files:
20
21
  resolved_paths = paths.flat_map do |path|
@@ -74,7 +75,7 @@ module Decode
74
75
  attr :definitions
75
76
 
76
77
  # A (prefix) trie of lexically scoped definitions.
77
- # @attribute [Trie] The trie structure for efficient lookups.
78
+ # @attribute [Trie[Definition]] The trie structure for efficient lookups.
78
79
  attr :trie
79
80
 
80
81
  # Updates the index by parsing the specified files.
@@ -102,12 +103,12 @@ module Decode
102
103
 
103
104
  # Lookup the specified reference and return matching definitions.
104
105
  # @parameter reference [Language::Reference] The reference to match.
105
- # @parameter relative_to [Definition] Lookup the reference relative to the scope of this definition.
106
- # @returns [Definition | Nil] The best matching definition, or nil if not found.
106
+ # @parameter relative_to [Definition?] Lookup the reference relative to the scope of this definition.
107
+ # @returns [Definition?] The best matching definition, or nil if not found.
107
108
  def lookup(reference, relative_to: nil)
108
109
  if reference.absolute? || relative_to.nil?
109
110
  # Start from root scope:
110
- lexical_path = []
111
+ lexical_path = [] #: Array[Symbol]
111
112
  else
112
113
  # Start from the given definition's scope:
113
114
  lexical_path = relative_to.full_path.dup
@@ -122,7 +123,11 @@ module Decode
122
123
  if node.children[path.first]
123
124
  if target = node.lookup(path)
124
125
  # Return the best matching definition:
125
- return reference.best(target.values)
126
+ if values = target.values
127
+ return reference.best(values)
128
+ else
129
+ return nil
130
+ end
126
131
  else
127
132
  return nil
128
133
  end
@@ -65,7 +65,7 @@ module Decode
65
65
  end
66
66
 
67
67
  # Get the parser for this language.
68
- # @returns [Parser | Nil] The parser instance, or nil if not available.
68
+ # @returns [untyped] The parser instance, or nil if not available.
69
69
  def parser
70
70
  nil
71
71
  end
@@ -92,6 +92,15 @@ module Decode
92
92
  parser.segments_for(source, &block)
93
93
  end
94
94
  end
95
+
96
+ # Generate a code representation with syntax highlighting and link resolution.
97
+ # @parameter text [String] The source code text to format.
98
+ # @parameter index [Index] The index for resolving references.
99
+ # @parameter relative_to [Definition] The definition to resolve relative references from.
100
+ # @returns [untyped] A formatted code object with syntax highlighting.
101
+ def code_for(text, index, relative_to: nil)
102
+ raise NotImplementedError, "Code generation is not implemented for #{self.class}!"
103
+ end
95
104
  end
96
105
  end
97
106
  end
@@ -9,6 +9,8 @@ module Decode
9
9
  class Reference
10
10
  # Initialize the reference.
11
11
  # @parameter identifier [String] The identifier part of the reference.
12
+ # @parameter language [Language::Generic] The language this reference belongs to.
13
+ # @parameter lexical_path [Array(String)?] The lexical path scope for resolution.
12
14
  def initialize(identifier, language, lexical_path = nil)
13
15
  @identifier = identifier
14
16
  @language = language
@@ -70,12 +72,13 @@ module Decode
70
72
  end
71
73
 
72
74
  # Find the best matching definition from a list.
73
- # @parameter definitions [Array(String)] The definitions to choose from.
75
+ # @parameter definitions [Array(Definition)] The definitions to choose from.
76
+ # @returns [Definition?] The best matching definition, if any.
74
77
  def best(definitions)
75
78
  prefix, name = lexical_path.last
76
79
 
77
- first = nil
78
- without_prefix = nil
80
+ first = nil #: Definition?
81
+ without_prefix = nil #: Definition?
79
82
 
80
83
  definitions.each do |definition|
81
84
  first ||= definition
@@ -93,7 +96,7 @@ module Decode
93
96
  end
94
97
 
95
98
  # The lexical path of the reference.
96
- # @returns [Array(String)]
99
+ # @returns [Array(Symbol)]
97
100
  def path
98
101
  @path ||= self.lexical_path.map{|_, name| name.to_sym}
99
102
  end
@@ -58,13 +58,13 @@ module Decode
58
58
  end
59
59
 
60
60
  # A singleton class is a container for other definitions.
61
- # @returns [Boolean]
61
+ # @returns [bool]
62
62
  def container?
63
63
  true
64
64
  end
65
65
 
66
66
  # Typically, a singleton class does not contain other definitions.
67
- # @returns [Boolean]
67
+ # @returns [bool]
68
68
  def nested?
69
69
  false
70
70
  end
@@ -16,9 +16,9 @@ module Decode
16
16
  # Initialize a new code block.
17
17
  # @parameter text [String] The code text.
18
18
  # @parameter index [Index] The index to use.
19
- # @parameter relative_to [Definition] The definition this code is relative to.
20
- # @parameter language [Language] The language of the code.
21
- def initialize(text, index, relative_to: nil, language: relative_to&.language)
19
+ # @parameter relative_to [Definition?] The definition this code is relative to.
20
+ # @parameter language [Language::Generic] The language of the code.
21
+ def initialize(text, index, relative_to: nil, language:)
22
22
  @text = text
23
23
  @root = ::Prism.parse(text)
24
24
  @index = index
@@ -26,12 +26,24 @@ module Decode
26
26
  @language = language
27
27
  end
28
28
 
29
+ # @attribute [String] The code text.
29
30
  attr :text
30
31
 
32
+ # @attribute [untyped] The parsed syntax tree.
33
+ attr :root
34
+
35
+ # @attribute [Index] The index to use for lookups.
36
+ attr :index
37
+
38
+ # @attribute [Definition?] The definition this code is relative to.
39
+ attr :relative_to
40
+
41
+ # @attribute [Language::Generic] The language of the code.
31
42
  attr :language
32
43
 
33
44
  # Extract definitions from the code.
34
45
  # @parameter into [Array] The array to extract definitions into.
46
+ # @returns [Array] The array with extracted definitions.
35
47
  def extract(into = [])
36
48
  if @index
37
49
  traverse(@root.value, into)
@@ -42,6 +54,10 @@ module Decode
42
54
 
43
55
  private
44
56
 
57
+ # Traverse the syntax tree and extract definitions.
58
+ # @parameter node [untyped] The syntax tree node to traverse.
59
+ # @parameter into [Array] The array to extract definitions into.
60
+ # @returns [self]
45
61
  def traverse(node, into)
46
62
  case node&.type
47
63
  when :program_node
@@ -75,6 +91,8 @@ module Decode
75
91
  traverse(child, into)
76
92
  end
77
93
  end
94
+
95
+ return self
78
96
  end
79
97
  end
80
98
  end
@@ -31,13 +31,25 @@ module Decode
31
31
  attr_accessor :visibility
32
32
 
33
33
  # Check if this definition is public.
34
- # @returns [Boolean] True if the definition is public.
34
+ # @returns [bool] True if the definition is public.
35
35
  def public?
36
36
  @visibility == :public
37
37
  end
38
38
 
39
+ # Check if this definition is protected.
40
+ # @returns [bool] True if the definition is protected.
41
+ def protected?
42
+ @visibility == :protected
43
+ end
44
+
45
+ # Check if this definition is private.
46
+ # @returns [bool] True if the definition is private.
47
+ def private?
48
+ @visibility == :private
49
+ end
50
+
39
51
  # Check if this definition spans multiple lines.
40
- # @returns [Boolean] True if the definition spans multiple lines.
52
+ # @returns [bool] True if the definition spans multiple lines.
41
53
  def multiline?
42
54
  @node.location.start_line != @node.location.end_line
43
55
  end
@@ -63,7 +75,7 @@ module Decode
63
75
  end
64
76
 
65
77
  # Get the location of this definition.
66
- # @returns [Location | Nil] The location object if source is available.
78
+ # @returns [Location?] The location object if source is available.
67
79
  def location
68
80
  if @source and location = @node&.location
69
81
  Location.new(@source.path, location.start_line)
@@ -19,6 +19,7 @@ module Decode
19
19
 
20
20
  TAGS = Comment::Tags.build do |tags|
21
21
  tags["attribute"] = Comment::Attribute
22
+ tags["constant"] = Comment::Constant
22
23
  tags["parameter"] = Comment::Parameter
23
24
  tags["option"] = Comment::Option
24
25
  tags["yields"] = Comment::Yields
@@ -37,7 +38,7 @@ module Decode
37
38
  end
38
39
 
39
40
  # Get the parser for Ruby source code.
40
- # @returns [Parser] The Ruby parser instance.
41
+ # @returns [Language::Ruby::Parser] The Ruby parser instance.
41
42
  def parser
42
43
  @parser ||= Parser.new(self)
43
44
  end