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.
- checksums.yaml +4 -4
- data/bake/decode/index.rb +1 -1
- data/lib/decode/comment/attribute.rb +53 -0
- data/lib/decode/comment/node.rb +90 -0
- data/lib/decode/comment/parameter.rb +58 -0
- data/lib/decode/comment/pragma.rb +50 -0
- data/lib/decode/comment/raises.rb +32 -0
- data/lib/decode/comment/returns.rb +32 -0
- data/lib/decode/comment/tag.rb +50 -0
- data/lib/decode/comment/tags.rb +80 -0
- data/lib/decode/comment/text.rb +37 -0
- data/lib/decode/comment/throws.rb +32 -0
- data/lib/decode/comment/yields.rb +52 -0
- data/lib/decode/definition.rb +26 -19
- data/lib/decode/documentation.rb +21 -66
- data/lib/decode/index.rb +16 -8
- data/lib/decode/language.rb +1 -19
- data/lib/decode/language/generic.rb +51 -0
- data/lib/decode/language/reference.rb +108 -0
- data/lib/decode/language/ruby.rb +50 -8
- data/lib/decode/language/ruby/block.rb +1 -1
- data/lib/decode/language/ruby/class.rb +2 -2
- data/lib/decode/language/ruby/code.rb +85 -0
- data/lib/decode/language/ruby/definition.rb +1 -1
- data/lib/decode/language/ruby/method.rb +7 -1
- data/lib/decode/language/ruby/parser.rb +11 -3
- data/lib/decode/language/ruby/reference.rb +29 -30
- data/lib/decode/language/ruby/segment.rb +1 -1
- data/lib/decode/languages.rb +92 -0
- data/lib/decode/scope.rb +3 -0
- data/lib/decode/segment.rb +4 -4
- data/lib/decode/source.rb +26 -16
- data/lib/decode/syntax/link.rb +44 -0
- data/lib/decode/syntax/match.rb +53 -0
- data/lib/decode/syntax/rewriter.rb +70 -0
- data/lib/decode/trie.rb +13 -13
- data/lib/decode/version.rb +1 -1
- metadata +27 -16
- data/.github/workflows/development.yml +0 -50
- data/.gitignore +0 -13
- data/.rspec +0 -2
- data/README.md +0 -47
- data/decode.gemspec +0 -33
- data/gems.rb +0 -3
- 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
|
data/lib/decode/definition.rb
CHANGED
@@ -22,10 +22,10 @@ module Decode
|
|
22
22
|
# A symbol with attached documentation.
|
23
23
|
class Definition
|
24
24
|
# Initialize the symbol.
|
25
|
-
# @
|
26
|
-
# @
|
27
|
-
# @
|
28
|
-
# @
|
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
|
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
|
-
# @
|
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
|
-
# @
|
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
|
-
# @
|
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
|
86
|
-
#
|
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
|
-
# @
|
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
|
-
# @
|
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
|
-
# @
|
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
|
-
# @
|
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
|
-
# @
|
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
|
-
# @
|
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
|
-
# @
|
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
|
-
# @
|
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
|
data/lib/decode/documentation.rb
CHANGED
@@ -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
|
-
# @
|
27
|
-
# @
|
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.
|
77
|
-
|
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
|
-
|
47
|
+
# The underlying comments from which the documentation is extracted.
|
48
|
+
# @attribute [Array(String)]
|
49
|
+
attr :comments
|
84
50
|
|
85
|
-
# The
|
86
|
-
#
|
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
|
data/lib/decode/index.rb
CHANGED
@@ -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
|
-
# @
|
45
|
+
# @attribute [Array(Source)]
|
38
46
|
attr :sources
|
39
47
|
|
40
48
|
# All definitions which have been parsed.
|
41
|
-
# @
|
49
|
+
# @attribute [Array(Symbol)]
|
42
50
|
attr :definitions
|
43
51
|
|
44
52
|
# A (prefix) trie of lexically scoped definitions.
|
45
|
-
# @
|
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
|
-
# @
|
60
|
+
# @parameter paths [Array(String)] The source file paths.
|
53
61
|
def update(paths)
|
54
62
|
paths.each do |path|
|
55
|
-
if source =
|
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
|
-
# @
|
71
|
-
# @
|
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 = []
|
data/lib/decode/language.rb
CHANGED
@@ -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
|