decode 0.13.0 → 0.14.0

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.
@@ -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
 
@@ -53,11 +53,11 @@ module Decode
53
53
  attr :language
54
54
 
55
55
  # The comment lines which directly preceeded the definition.
56
- # @attr [Array(String)]
56
+ # @attribute [Array(String)]
57
57
  attr :comments
58
58
 
59
59
  # The qualified name is an absolute name which includes any and all namespacing.
60
- # @return [String]
60
+ # @returns [String]
61
61
  def qualified_name
62
62
  @qualified_name ||= begin
63
63
  if @parent
@@ -69,7 +69,7 @@ module Decode
69
69
  end
70
70
 
71
71
  # The name of this definition plus the nesting prefix.
72
- # @return [String]
72
+ # @returns [String]
73
73
  def nested_name
74
74
  "::#{@name}"
75
75
  end
@@ -78,12 +78,13 @@ module Decode
78
78
  self.nested_name.start_with?(prefix)
79
79
  end
80
80
 
81
+ # Convert this definition into another kind of definition.
81
82
  def convert(kind)
82
83
  raise ArgumentError, "Unable to convert #{self} into #{kind}!"
83
84
  end
84
85
 
85
86
  # The lexical scope which is an array of lexical {Key} instances as generated by {key}.
86
- # @return [Array]
87
+ # @returns [Array]
87
88
  def path
88
89
  if @path
89
90
  # Cached version:
@@ -102,14 +103,14 @@ module Decode
102
103
  # A short form of the definition.
103
104
  # e.g. `def short_form`.
104
105
  #
105
- # @return [String | nil]
106
+ # @returns [String | nil]
106
107
  def short_form
107
108
  end
108
109
 
109
110
  # A long form of the definition.
110
111
  # e.g. `def initialize(kind, name, comments, **options)`.
111
112
  #
112
- # @return [String | nil]
113
+ # @returns [String | nil]
113
114
  def long_form
114
115
  self.short_form
115
116
  end
@@ -117,44 +118,44 @@ module Decode
117
118
  # A long form which uses the qualified name if possible.
118
119
  # Defaults to {long_form}.
119
120
  #
120
- # @return [String | nil]
121
+ # @returns [String | nil]
121
122
  def qualified_form
122
123
  self.long_form
123
124
  end
124
125
 
125
126
  # Whether the definition spans multiple lines.
126
127
  #
127
- # @return [Boolean]
128
+ # @returns [Boolean]
128
129
  def multiline?
129
130
  false
130
131
  end
131
132
 
132
133
  # The full text of the definition.
133
134
  #
134
- # @return [String | nil]
135
+ # @returns [String | nil]
135
136
  def text
136
137
  end
137
138
 
138
139
  # Whether this definition can contain nested definitions.
139
140
  #
140
- # @return [Boolean]
141
+ # @returns [Boolean]
141
142
  def container?
142
143
  false
143
144
  end
144
145
 
145
146
  # Whether this represents a single entity to be documented (along with it's contents).
146
147
  #
147
- # @return [Boolean]
148
+ # @returns [Boolean]
148
149
  def nested?
149
150
  container?
150
151
  end
151
152
 
152
153
  # Structured access to the definitions comments.
153
154
  #
154
- # @return [Documentation | Nil] A `Documentation` if this definition has comments.
155
+ # @returns [Documentation | Nil] A {Documentation} instance if this definition has comments.
155
156
  def documentation
156
157
  if @comments&.any?
157
- @documentation ||= Documentation.new(@comments)
158
+ @documentation ||= Documentation.new(@comments, @language)
158
159
  end
159
160
  end
160
161
  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]
53
+ attr :language
99
54
  end
100
55
  end
data/lib/decode/index.rb CHANGED
@@ -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,7 +23,7 @@ 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.
26
+ # @parameter identifier [String] The identifier part of the reference.
27
27
  def initialize(identifier, language)
28
28
  @identifier = identifier
29
29
  @language = language
@@ -32,8 +32,16 @@ module Decode
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.
@@ -69,7 +77,7 @@ module Decode
69
77
  end
70
78
 
71
79
  # The lexical path of the reference.
72
- # @return [Array(String)]
80
+ # @returns [Array(String)]
73
81
  def path
74
82
  @path ||= self.lexical_path.map{|_, name| name.to_sym}
75
83
  end
@@ -21,9 +21,14 @@
21
21
  require_relative 'ruby/reference'
22
22
  require_relative 'ruby/parser'
23
23
 
24
+ require_relative '../comment/tags'
25
+ require_relative '../comment/parameter'
26
+ require_relative '../comment/yields'
27
+ require_relative '../comment/returns'
28
+
24
29
  module Decode
25
30
  module Language
26
- # The Ruby language.
31
+ # An interface for extracting information from Ruby source code.
27
32
  module Ruby
28
33
  # The canoical name of the language for use in output formatting.
29
34
  # e.g. source code highlighting.
@@ -39,21 +44,45 @@ module Decode
39
44
  ['.rb', '.ru']
40
45
  end
41
46
 
47
+ TAGS = Comment::Tags.build do |tags|
48
+ tags['attribute'] = Comment::Attribute
49
+ tags['parameter'] = Comment::Parameter
50
+ tags['yields'] = Comment::Yields
51
+ tags['returns'] = Comment::Returns
52
+ tags['raises'] = Comment::Raises
53
+ tags['throws'] = Comment::Throws
54
+
55
+ tags['reentrant'] = Comment::Pragma
56
+ tags['deprecated'] = Comment::Pragma
57
+ tags['blocking'] = Comment::Pragma
58
+ tags['asynchronous'] = Comment::Pragma
59
+ end
60
+
61
+ def self.tags
62
+ TAGS
63
+ end
64
+
42
65
  # Generate a language-specific reference.
66
+ # @parameter identifier [String] A valid identifier.
43
67
  def self.reference_for(identifier)
44
68
  Reference.new(identifier, self)
45
69
  end
46
70
 
47
71
  # Parse the input yielding definitions.
48
- # @block `{|definition| ...}`
49
- # @yield definition [Definition]
72
+ # @parameter input [File] The input file which contains the source code.
73
+ # @yields {|definition| ...} Receives the definitions extracted from the source code.
74
+ # @parameter definition [Definition] The source code definition including methods, classes, etc.
75
+ # @returns [Enumerator(Segment)] If no block given.
50
76
  def self.definitions_for(input, &block)
51
77
  Parser.new.definitions_for(input, &block)
52
78
  end
53
79
 
54
- # Parse the input yielding interleaved comments and code segments.
55
- # @block `{|segment| ...}`
56
- # @yield segment [Segment]
80
+ # Parse the input yielding segments.
81
+ # Segments are constructed from a block of top level comments followed by a block of code.
82
+ # @parameter input [File] The input file which contains the source code.
83
+ # @yields {|segment| ...}
84
+ # @parameter segment [Segment]
85
+ # @returns [Enumerator(Segment)] If no block given.
57
86
  def self.segments_for(input, &block)
58
87
  Parser.new.segments_for(input, &block)
59
88
  end