decode 0.12.0 → 0.15.1

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 (45) 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 +16 -8
  17. data/lib/decode/language.rb +1 -19
  18. data/lib/decode/language/generic.rb +51 -0
  19. data/lib/decode/language/reference.rb +108 -0
  20. data/lib/decode/language/ruby.rb +50 -8
  21. data/lib/decode/language/ruby/block.rb +1 -1
  22. data/lib/decode/language/ruby/class.rb +2 -2
  23. data/lib/decode/language/ruby/code.rb +85 -0
  24. data/lib/decode/language/ruby/definition.rb +1 -1
  25. data/lib/decode/language/ruby/method.rb +7 -1
  26. data/lib/decode/language/ruby/parser.rb +11 -3
  27. data/lib/decode/language/ruby/reference.rb +29 -30
  28. data/lib/decode/language/ruby/segment.rb +1 -1
  29. data/lib/decode/languages.rb +92 -0
  30. data/lib/decode/scope.rb +3 -0
  31. data/lib/decode/segment.rb +4 -4
  32. data/lib/decode/source.rb +26 -16
  33. data/lib/decode/syntax/link.rb +44 -0
  34. data/lib/decode/syntax/match.rb +53 -0
  35. data/lib/decode/syntax/rewriter.rb +70 -0
  36. data/lib/decode/trie.rb +13 -13
  37. data/lib/decode/version.rb +1 -1
  38. metadata +27 -16
  39. data/.github/workflows/development.yml +0 -50
  40. data/.gitignore +0 -13
  41. data/.rspec +0 -2
  42. data/README.md +0 -47
  43. data/decode.gemspec +0 -33
  44. data/gems.rb +0 -3
  45. 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
@@ -21,11 +21,15 @@
21
21
  require_relative 'source'
22
22
  require_relative 'trie'
23
23
 
24
+ require_relative 'languages'
25
+
24
26
  module Decode
25
27
  # A list of definitions organised for quick lookup and lexical enumeration.
26
28
  class Index
27
29
  # Initialize an empty index.
28
- def initialize
30
+ def initialize(languages = Languages.all)
31
+ @languages = languages
32
+
29
33
  @sources = {}
30
34
  @definitions = {}
31
35
 
@@ -33,26 +37,30 @@ module Decode
33
37
  @trie = Trie.new
34
38
  end
35
39
 
40
+ # All supported languages for this index.
41
+ # @attribute [Languages]
42
+ attr :languages
43
+
36
44
  # All source files that have been parsed.
37
- # @attr [Array(Source)]
45
+ # @attribute [Array(Source)]
38
46
  attr :sources
39
47
 
40
48
  # All definitions which have been parsed.
41
- # @attr [Array(Symbol)]
49
+ # @attribute [Array(Symbol)]
42
50
  attr :definitions
43
51
 
44
52
  # A (prefix) trie of lexically scoped definitions.
45
- # @attr [Trie]
53
+ # @attribute [Trie]
46
54
 
47
55
  attr :trie
48
56
 
49
57
  # Updates the index by parsing the specified files.
50
58
  # All extracted definitions are merged into the existing index.
51
59
  #
52
- # @param paths [Array(String)] The source file paths.
60
+ # @parameter paths [Array(String)] The source file paths.
53
61
  def update(paths)
54
62
  paths.each do |path|
55
- if source = Source.for?(path)
63
+ if source = @languages.source_for(path)
56
64
  @sources[path] = source
57
65
 
58
66
  source.definitions do |symbol|
@@ -67,8 +75,8 @@ module Decode
67
75
 
68
76
  # Lookup the specified reference and return matching definitions.
69
77
  #
70
- # @param reference [Reference] The reference to match.
71
- # @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.
72
80
  def lookup(reference, relative_to: nil)
73
81
  if reference.absolute? || relative_to.nil?
74
82
  lexical_path = []
@@ -18,29 +18,11 @@
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 'language/generic'
21
22
  require_relative 'language/ruby'
22
23
 
23
24
  module Decode
24
25
  # Language specific parsers and definitions.
25
26
  module Language
26
- def self.detect(path)
27
- case path
28
- when /\.(rb|ru)\z/
29
- return Language::Ruby
30
- end
31
- end
32
-
33
- REFERENCE = /\A(?<language>\.[a-z]+)?\s+(?<text>.*?)\z/
34
-
35
- # A language agnostic reference:
36
- # e.g. `.rb MyModule::MyClass`
37
- #
38
- def self.reference(string, language = nil)
39
- if match = REFERENCE.match(string)
40
- language = self.detect(match[:language]) || language
41
-
42
- return language.reference(match[:text])
43
- end
44
- end
45
27
  end
46
28
  end