decode 0.13.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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