decode 0.13.0 → 0.15.2

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/bake/decode/index.rb +1 -1
  3. data/lib/decode/comment/attribute.rb +53 -0
  4. data/lib/decode/comment/node.rb +90 -0
  5. data/lib/decode/comment/parameter.rb +58 -0
  6. data/lib/decode/comment/pragma.rb +50 -0
  7. data/lib/decode/comment/raises.rb +32 -0
  8. data/lib/decode/comment/returns.rb +32 -0
  9. data/lib/decode/comment/tag.rb +50 -0
  10. data/lib/decode/comment/tags.rb +80 -0
  11. data/lib/decode/comment/text.rb +37 -0
  12. data/lib/decode/comment/throws.rb +32 -0
  13. data/lib/decode/comment/yields.rb +52 -0
  14. data/lib/decode/definition.rb +26 -19
  15. data/lib/decode/documentation.rb +21 -66
  16. data/lib/decode/index.rb +7 -7
  17. data/lib/decode/language/generic.rb +2 -2
  18. data/lib/decode/language/reference.rb +39 -9
  19. data/lib/decode/language/ruby.rb +40 -6
  20. data/lib/decode/language/ruby/block.rb +1 -1
  21. data/lib/decode/language/ruby/class.rb +2 -2
  22. data/lib/decode/language/ruby/code.rb +85 -0
  23. data/lib/decode/language/ruby/definition.rb +1 -1
  24. data/lib/decode/language/ruby/method.rb +7 -1
  25. data/lib/decode/language/ruby/module.rb +6 -0
  26. data/lib/decode/language/ruby/parser.rb +11 -3
  27. data/lib/decode/language/ruby/reference.rb +31 -0
  28. data/lib/decode/language/ruby/segment.rb +1 -1
  29. data/lib/decode/scope.rb +3 -0
  30. data/lib/decode/segment.rb +4 -4
  31. data/lib/decode/source.rb +24 -8
  32. data/lib/decode/syntax/link.rb +44 -0
  33. data/lib/decode/syntax/match.rb +53 -0
  34. data/lib/decode/syntax/rewriter.rb +70 -0
  35. data/lib/decode/trie.rb +13 -13
  36. data/lib/decode/version.rb +1 -1
  37. metadata +23 -43
  38. data/.github/workflows/development.yml +0 -50
  39. data/.gitignore +0 -13
  40. data/.rspec +0 -2
  41. data/README.md +0 -47
  42. data/decode.gemspec +0 -33
  43. data/gems.rb +0 -3
  44. data/guides/extract-symbols/extract.rb +0 -26
@@ -0,0 +1,37 @@
1
+ # Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative 'node'
22
+
23
+ module Decode
24
+ module Comment
25
+ # A structured comment.
26
+ class Text
27
+ def initialize(line)
28
+ @line = line
29
+ end
30
+
31
+ attr :line
32
+
33
+ def traverse
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,32 @@
1
+ # Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative 'attribute'
22
+
23
+ module Decode
24
+ module Comment
25
+ # Identifies that a method might throw a specific symbol.
26
+ #
27
+ # - `@throws [:skip] To skip recursion.`
28
+ #
29
+ class Throws < Attribute
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,52 @@
1
+ # Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative 'tag'
22
+
23
+ module Decode
24
+ module Comment
25
+ # Describes a block parameter.
26
+ #
27
+ # - `@yields {|person| ... } If a block is given.`
28
+ #
29
+ # Should contain nested parameters.
30
+ class Yields < Tag
31
+ PATTERN = /\A(?<block>{.*?})(\s+(?<details>.*?))?\Z/
32
+
33
+ def self.build(directive, match)
34
+ node = self.new(directive, match[:block])
35
+
36
+ if details = match[:details]
37
+ node.add(Text.new(details))
38
+ end
39
+
40
+ return node
41
+ end
42
+
43
+ def initialize(directive, block)
44
+ super(directive)
45
+
46
+ @block = block
47
+ end
48
+
49
+ attr :block
50
+ end
51
+ end
52
+ end
@@ -22,10 +22,10 @@ module Decode
22
22
  # A symbol with attached documentation.
23
23
  class Definition
24
24
  # Initialize the symbol.
25
- # @param name [Symbol] The name of the definition.
26
- # @param parent [Symbol] The parent lexical scope.
27
- # @param language [Language] The language in which the symbol is defined in.
28
- # @param comments [Array(String)] The comments associated with the definition.
25
+ # @parameter name [Symbol] The name of the definition.
26
+ # @parameter parent [Symbol] The parent lexical scope.
27
+ # @parameter language [Language] The language in which the symbol is defined in.
28
+ # @parameter comments [Array(String)] The comments associated with the definition.
29
29
  def initialize(name, parent: nil, language: parent.language, comments: nil)
30
30
  @name = name
31
31
 
@@ -44,20 +44,23 @@ module Decode
44
44
 
45
45
  # The symbol name.
46
46
  # e.g. `:Decode`.
47
+ # @attribute [Symbol]
47
48
  attr :name
48
49
 
49
- # The parent symbol, defining lexical scope.
50
+ # The parent definition, defining lexical scope.
51
+ # @attribute [Definition | Nil]
50
52
  attr :parent
51
53
 
52
54
  # The language the symbol is defined within.
55
+ # @attribute [Language::Generic]
53
56
  attr :language
54
57
 
55
58
  # The comment lines which directly preceeded the definition.
56
- # @attr [Array(String)]
59
+ # @attribute [Array(String)]
57
60
  attr :comments
58
61
 
59
62
  # The qualified name is an absolute name which includes any and all namespacing.
60
- # @return [String]
63
+ # @returns [String]
61
64
  def qualified_name
62
65
  @qualified_name ||= begin
63
66
  if @parent
@@ -69,21 +72,25 @@ module Decode
69
72
  end
70
73
 
71
74
  # The name of this definition plus the nesting prefix.
72
- # @return [String]
75
+ # @returns [String]
73
76
  def nested_name
74
77
  "::#{@name}"
75
78
  end
76
79
 
80
+ # Does the definition name match the specified prefix?
81
+ # @returns [Boolean]
77
82
  def start_with?(prefix)
78
83
  self.nested_name.start_with?(prefix)
79
84
  end
80
85
 
86
+ # Convert this definition into another kind of definition.
81
87
  def convert(kind)
82
88
  raise ArgumentError, "Unable to convert #{self} into #{kind}!"
83
89
  end
84
90
 
85
- # The lexical scope which is an array of lexical {Key} instances as generated by {key}.
86
- # @return [Array]
91
+ # The lexical scope as an array of names.
92
+ # e.g. `[:Decode, :Definition]`
93
+ # @returns [Array]
87
94
  def path
88
95
  if @path
89
96
  # Cached version:
@@ -102,14 +109,14 @@ module Decode
102
109
  # A short form of the definition.
103
110
  # e.g. `def short_form`.
104
111
  #
105
- # @return [String | nil]
112
+ # @returns [String | nil]
106
113
  def short_form
107
114
  end
108
115
 
109
116
  # A long form of the definition.
110
117
  # e.g. `def initialize(kind, name, comments, **options)`.
111
118
  #
112
- # @return [String | nil]
119
+ # @returns [String | nil]
113
120
  def long_form
114
121
  self.short_form
115
122
  end
@@ -117,44 +124,44 @@ module Decode
117
124
  # A long form which uses the qualified name if possible.
118
125
  # Defaults to {long_form}.
119
126
  #
120
- # @return [String | nil]
127
+ # @returns [String | nil]
121
128
  def qualified_form
122
129
  self.long_form
123
130
  end
124
131
 
125
132
  # Whether the definition spans multiple lines.
126
133
  #
127
- # @return [Boolean]
134
+ # @returns [Boolean]
128
135
  def multiline?
129
136
  false
130
137
  end
131
138
 
132
139
  # The full text of the definition.
133
140
  #
134
- # @return [String | nil]
141
+ # @returns [String | nil]
135
142
  def text
136
143
  end
137
144
 
138
145
  # Whether this definition can contain nested definitions.
139
146
  #
140
- # @return [Boolean]
147
+ # @returns [Boolean]
141
148
  def container?
142
149
  false
143
150
  end
144
151
 
145
152
  # Whether this represents a single entity to be documented (along with it's contents).
146
153
  #
147
- # @return [Boolean]
154
+ # @returns [Boolean]
148
155
  def nested?
149
156
  container?
150
157
  end
151
158
 
152
159
  # Structured access to the definitions comments.
153
160
  #
154
- # @return [Documentation | Nil] A `Documentation` if this definition has comments.
161
+ # @returns [Documentation | Nil] A {Documentation} instance if this definition has comments.
155
162
  def documentation
156
163
  if @comments&.any?
157
- @documentation ||= Documentation.new(@comments)
164
+ @documentation ||= Documentation.new(@comments, @language)
158
165
  end
159
166
  end
160
167
  end
@@ -18,83 +18,38 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
+ require_relative 'comment/node'
22
+
23
+ require_relative 'comment/attribute'
24
+ require_relative 'comment/parameter'
25
+ require_relative 'comment/pragma'
26
+ require_relative 'comment/raises'
27
+ require_relative 'comment/returns'
28
+ require_relative 'comment/throws'
29
+ require_relative 'comment/yields'
30
+
21
31
  module Decode
22
32
  # Structured access to a set of comment lines.
23
- class Documentation
33
+ class Documentation < Comment::Node
24
34
  # Initialize the documentation with an array of comments, within a specific language.
25
35
  #
26
- # @param comments [Array(String)] An array of comment lines.
27
- # @param language [Language] The language in which the comments were extracted.
36
+ # @parameter comments [Array(String)] An array of comment lines.
37
+ # @parameter language [Language] The language in which the comments were extracted.
28
38
  def initialize(comments, language = nil)
29
39
  @comments = comments
30
40
  @language = language
31
- end
32
-
33
- # The language in which the documentation was extracted from.
34
- attr :language
35
-
36
- DESCRIPTION = /\A\s*([^@\s].*)?\z/
37
-
38
- # The text-only lines of the comment block.
39
- #
40
- # @yield [String]
41
- # @return [Enumerable]
42
- def description
43
- return to_enum(:description) unless block_given?
44
-
45
- # We track empty lines and only yield a single empty line when there is another line of text:
46
- gap = false
47
-
48
- @comments.each do |comment|
49
- if match = comment.match(DESCRIPTION)
50
- if match[1]
51
- if gap
52
- yield ""
53
- gap = false
54
- end
55
-
56
- yield match[1]
57
- else
58
- gap = true
59
- end
60
- else
61
- break
62
- end
63
- end
64
- end
65
-
66
- ATTRIBUTE = /\A\s*@(?<name>.*?)\s+(?<value>.*?)\z/
67
-
68
- # The attribute lines of the comment block.
69
- # e.g. `@return [String]`.
70
- #
71
- # @yield [String]
72
- # @return [Enumerable]
73
- def attributes
74
- return to_enum(:attributes) unless block_given?
75
41
 
76
- @comments.each do |comment|
77
- if match = comment.match(ATTRIBUTE)
78
- yield match
79
- end
42
+ language.tags.parse(@comments.dup) do |node|
43
+ self.add(node)
80
44
  end
81
45
  end
82
46
 
83
- PARAMETER = /\A\s*@param\s+(?<name>.*?)\s+\[(?<type>.*?)\]\s+(?<details>.*?)\z/
47
+ # The underlying comments from which the documentation is extracted.
48
+ # @attribute [Array(String)]
49
+ attr :comments
84
50
 
85
- # The parameter lines of the comment block.
86
- # e.g. `@param value [String] The value.`
87
- #
88
- # @yield [String]
89
- # @return [Enumerable]
90
- def parameters
91
- return to_enum(:parameters) unless block_given?
92
-
93
- @comments.each do |comment|
94
- if match = comment.match(PARAMETER)
95
- yield match
96
- end
97
- end
98
- end
51
+ # The language in which the documentation was extracted from.
52
+ # @attribute [Language::Generic]
53
+ attr :language
99
54
  end
100
55
  end
@@ -38,26 +38,26 @@ module Decode
38
38
  end
39
39
 
40
40
  # All supported languages for this index.
41
- # @attr [Languages]
41
+ # @attribute [Languages]
42
42
  attr :languages
43
43
 
44
44
  # All source files that have been parsed.
45
- # @attr [Array(Source)]
45
+ # @attribute [Array(Source)]
46
46
  attr :sources
47
47
 
48
48
  # All definitions which have been parsed.
49
- # @attr [Array(Symbol)]
49
+ # @attribute [Array(Symbol)]
50
50
  attr :definitions
51
51
 
52
52
  # A (prefix) trie of lexically scoped definitions.
53
- # @attr [Trie]
53
+ # @attribute [Trie]
54
54
 
55
55
  attr :trie
56
56
 
57
57
  # Updates the index by parsing the specified files.
58
58
  # All extracted definitions are merged into the existing index.
59
59
  #
60
- # @param paths [Array(String)] The source file paths.
60
+ # @parameter paths [Array(String)] The source file paths.
61
61
  def update(paths)
62
62
  paths.each do |path|
63
63
  if source = @languages.source_for(path)
@@ -75,8 +75,8 @@ module Decode
75
75
 
76
76
  # Lookup the specified reference and return matching definitions.
77
77
  #
78
- # @param reference [Reference] The reference to match.
79
- # @param relative_to [Definition] Lookup the reference relative to the scope of this definition.
78
+ # @parameter reference [Language::Reference] The reference to match.
79
+ # @parameter relative_to [Definition] Lookup the reference relative to the scope of this definition.
80
80
  def lookup(reference, relative_to: nil)
81
81
  if reference.absolute? || relative_to.nil?
82
82
  lexical_path = []
@@ -36,13 +36,13 @@ module Decode
36
36
  end
37
37
 
38
38
  # Parse the input yielding definitions.
39
- # @block `{|definition| ...}`
39
+ # @block {|definition| ... }
40
40
  # @yield definition [Definition]
41
41
  def definitions_for(input, &block)
42
42
  end
43
43
 
44
44
  # Parse the input yielding interleaved comments and code segments.
45
- # @block `{|segment| ...}`
45
+ # @block {|segment| ... }
46
46
  # @yield segment [Segment]
47
47
  def segments_for(input, &block)
48
48
  end
@@ -23,20 +23,29 @@ module Decode
23
23
  # An reference which can be resolved to zero or more definitions.
24
24
  class Reference
25
25
  # Initialize the reference.
26
- # @param identifier [String] The identifier part of the reference.
27
- def initialize(identifier, language)
26
+ # @parameter identifier [String] The identifier part of the reference.
27
+ def initialize(identifier, language, lexical_path = nil)
28
28
  @identifier = identifier
29
29
  @language = language
30
30
 
31
- @lexical_path = nil
31
+ @lexical_path = lexical_path
32
32
  @path = nil
33
33
  end
34
34
 
35
+ def to_s
36
+ "{#{self.language} #{self.identifier}}"
37
+ end
38
+
39
+ def inspect
40
+ "\#<#{self.class} {#{self.identifier}}>"
41
+ end
42
+
35
43
  # The identifier part of the reference.
36
- # @attr [String]
44
+ # @attribute [String]
37
45
  attr :identifier
38
46
 
39
47
  # The language associated with this reference.
48
+ # @attribute [Language::Generic]
40
49
  attr :language
41
50
 
42
51
  # Whether the reference starts at the base of the lexical tree.
@@ -58,18 +67,39 @@ module Decode
58
67
  @lexical_path ||= self.split(@identifier)
59
68
  end
60
69
 
70
+ def priority(definition, prefix)
71
+ if prefix.nil?
72
+ return 1
73
+ elsif definition.start_with?(prefix)
74
+ return 0
75
+ else
76
+ return 2
77
+ end
78
+ end
79
+
61
80
  def best(definitions)
62
81
  prefix, name = lexical_path.last
63
82
 
64
- definitions.select do |definition|
65
- definition.language == @language && (
66
- prefix.nil? || definition.start_with?(prefix)
67
- )
83
+ first = nil
84
+ without_prefix = nil
85
+
86
+ definitions.each do |definition|
87
+ first ||= definition
88
+
89
+ next unless definition.language == @language
90
+
91
+ if prefix.nil?
92
+ without_prefix ||= definition
93
+ elsif definition.start_with?(prefix)
94
+ return definition
95
+ end
68
96
  end
97
+
98
+ return without_prefix || first
69
99
  end
70
100
 
71
101
  # The lexical path of the reference.
72
- # @return [Array(String)]
102
+ # @returns [Array(String)]
73
103
  def path
74
104
  @path ||= self.lexical_path.map{|_, name| name.to_sym}
75
105
  end