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
@@ -40,7 +40,7 @@ module Decode
40
40
  end
41
41
 
42
42
  # The source code trailing the comments.
43
- # @return [String | nil]
43
+ # @returns [String | nil]
44
44
  def code
45
45
  @expression.source
46
46
  end
@@ -0,0 +1,92 @@
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 'language/generic'
22
+ require_relative 'language/ruby'
23
+
24
+ module Decode
25
+ # A context for looking up languages based on file extension or name.
26
+ class Languages
27
+ def self.all
28
+ self.new.tap do |languages|
29
+ languages.add(Language::Ruby)
30
+ end
31
+ end
32
+
33
+ def initialize
34
+ @named = {}
35
+ @extensions = {}
36
+ end
37
+
38
+ def freeze
39
+ return unless frozen?
40
+
41
+ @named.freeze
42
+ @extensions.freeze
43
+
44
+ super
45
+ end
46
+
47
+ def add(language)
48
+ language.names.each do |name|
49
+ @named[name] = language
50
+ end
51
+
52
+ language.extensions.each do |extension|
53
+ @extensions[extension] = language
54
+ end
55
+ end
56
+
57
+ def fetch(name)
58
+ @named.fetch(name) do
59
+ unless @named.frozen?
60
+ @named[name] = Language::Generic.new(name)
61
+ end
62
+ end
63
+ end
64
+
65
+ def source_for(path)
66
+ extension = File.extname(path)
67
+
68
+ if language = @extensions[extension]
69
+ Source.new(path, language)
70
+ end
71
+ end
72
+
73
+ REFERENCE = /\A(?<name>[a-z]+)?\s+(?<identifier>.*?)\z/
74
+
75
+ # Parse a language agnostic reference:
76
+ # e.g. `ruby MyModule::MyClass`
77
+ #
78
+ def parse_reference(text, default_language: nil)
79
+ if match = REFERENCE.match(text)
80
+ language = self.fetch(match[:name]) || default_language
81
+
82
+ return language.reference_for(match[:identifier])
83
+ elsif default_language
84
+ return default_language.reference_for(text)
85
+ end
86
+ end
87
+
88
+ def reference_for(name, identifier)
89
+ self.fetch(name).reference_for(identifier)
90
+ end
91
+ end
92
+ end
@@ -23,10 +23,13 @@ require_relative 'definition'
23
23
  module Decode
24
24
  # An abstract namespace for nesting definitions.
25
25
  class Scope < Definition
26
+ # @returns [String] The name of the scope.
26
27
  def short_form
27
28
  @name
28
29
  end
29
30
 
31
+ # Scopes are always containers.
32
+ # @returns [Boolean] Always `true`.
30
33
  def container?
31
34
  true
32
35
  end
@@ -35,15 +35,15 @@ module Decode
35
35
  end
36
36
 
37
37
  # The preceeding comments.
38
- # @attr [Array(String)]
38
+ # @attribute [Array(String)]
39
39
  attr :comments
40
40
 
41
41
  # The language of the code attached to this segment.
42
- # @attr [Language]
42
+ # @attribute [Language::Generic]
43
43
  attr :language
44
44
 
45
45
  # An interface for accsssing the documentation of the definition.
46
- # @return [Documentation | nil] A `Documentation` if this definition has comments.
46
+ # @returns [Documentation | nil] A {Documentation} instance if this definition has comments.
47
47
  def documentation
48
48
  if @comments&.any?
49
49
  @documentation ||= Documentation.new(@comments, @language)
@@ -51,7 +51,7 @@ module Decode
51
51
  end
52
52
 
53
53
  # The source code trailing the comments.
54
- # @return [String | nil]
54
+ # @returns [String | nil]
55
55
  def code
56
56
  end
57
57
  end
@@ -21,40 +21,50 @@
21
21
  require_relative 'language'
22
22
 
23
23
  module Decode
24
+ # Represents a source file in a specific language.
24
25
  class Source
25
- def self.for?(path)
26
- if language = Language.detect(path)
27
- self.new(path, language)
28
- end
29
- end
30
-
31
- def initialize(path, language = nil)
26
+ def initialize(path, language)
32
27
  @path = path
33
- @language = language || Language.detect(path)
28
+ @buffer = nil
29
+ @language = language
34
30
  end
35
31
 
32
+ # The path of the source file.
33
+ # @attribute [String] A file-system path.
36
34
  attr :path
37
35
 
36
+ # The language of the source file.
37
+ # @attribute [Language::Generic]
38
38
  attr :language
39
39
 
40
- def open(&block)
41
- File.open(@path, &block)
40
+ # Read the source file into an internal buffer/cache.
41
+ # @returns [String]
42
+ def read
43
+ @buffer ||= File.read(@path).freeze
42
44
  end
43
45
 
46
+ # Open the source file and read all definitions.
47
+ # @yields {|definition| ...} All definitions from the source file.
48
+ # @parameter definition [Definition]
49
+ # @returns [Enumerator(Definition)] If no block given.
44
50
  def definitions(&block)
45
51
  return to_enum(:definitions) unless block_given?
46
52
 
47
- self.open do |file|
48
- @language.definitions_for(file, &block)
49
- end
53
+ @language.definitions_for(self.read, &block)
50
54
  end
51
55
 
56
+ # Open the source file and read all segments.
57
+ # @yields {|segment| ...} All segments from the source file.
58
+ # @parameter segment [Segment]
59
+ # @returns [Enumerator(Segment)] If no block given.
52
60
  def segments(&block)
53
61
  return to_enum(:segments) unless block_given?
54
62
 
55
- self.open do |file|
56
- @language.segments_for(file, &block)
57
- end
63
+ @language.segments_for(self.read, &block)
64
+ end
65
+
66
+ def code(index = nil, relative_to: nil)
67
+ @language.code_for(self.read, index, relative_to: relative_to)
58
68
  end
59
69
  end
60
70
  end
@@ -0,0 +1,44 @@
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 'match'
22
+
23
+ module Decode
24
+ module Syntax
25
+ class Link < Match
26
+ def initialize(range, definition)
27
+ @definition = definition
28
+
29
+ super(range)
30
+ end
31
+
32
+ attr :definition
33
+
34
+ def apply(output, rewriter)
35
+ output << rewriter.link_to(
36
+ @definition,
37
+ rewriter.text_for(@range)
38
+ )
39
+
40
+ return self.size
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,53 @@
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
+ module Decode
22
+ module Syntax
23
+ class Match
24
+ def initialize(range)
25
+ @range = range
26
+ end
27
+
28
+ attr :range
29
+
30
+ def apply(source)
31
+ return source[range]
32
+ end
33
+
34
+ def <=> other
35
+ @range.min <=> other.range.min
36
+ end
37
+
38
+ def offset
39
+ @range.min
40
+ end
41
+
42
+ def size
43
+ @range.size
44
+ end
45
+
46
+ def apply(output, rewriter)
47
+ output << rewriter.text_for(@range)
48
+
49
+ return self.size
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,70 @@
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
+ module Decode
22
+ module Syntax
23
+ class Rewriter
24
+ def initialize(text)
25
+ @text = text
26
+ @matches = []
27
+ end
28
+
29
+ attr :text
30
+
31
+ attr :matches
32
+
33
+ def << match
34
+ @matches << match
35
+ end
36
+
37
+ # Returns a chunk of raw text with no formatting.
38
+ def text_for(range)
39
+ @text[range]
40
+ end
41
+
42
+ def apply(output = [])
43
+ offset = 0
44
+
45
+ @matches.sort.each do |match|
46
+ if match.offset > offset
47
+ output << text_for(offset...match.offset)
48
+
49
+ offset = match.offset
50
+ elsif match.offset < offset
51
+ # Match intersects last output buffer.
52
+ next
53
+ end
54
+
55
+ offset += match.apply(output, self)
56
+ end
57
+
58
+ if offset < @text.size
59
+ output << text_for(offset...@text.size)
60
+ end
61
+
62
+ return output
63
+ end
64
+
65
+ def link_to(definition, text)
66
+ "[#{text}]"
67
+ end
68
+ end
69
+ end
70
+ end
@@ -31,17 +31,17 @@ module Decode
31
31
  end
32
32
 
33
33
  # A mutable array of all values that terminate at this node.
34
- # @attr [Array]
34
+ # @attribute [Array]
35
35
  attr_accessor :values
36
36
 
37
37
  # A hash table of all children nodes, indexed by name.
38
- # @attr [Hash(String, Node)]
38
+ # @attribute [Hash(String, Node)]
39
39
  attr :children
40
40
 
41
41
  # Look up a lexical path starting at this node.
42
42
  #
43
- # @param path [Array(String)] The path to resolve.
44
- # @return [Node | Nil]
43
+ # @parameter path [Array(String)] The path to resolve.
44
+ # @returns [Node | Nil]
45
45
  def lookup(path, index = 0)
46
46
  if index < path.size
47
47
  if child = @children[path[index]]
@@ -55,9 +55,9 @@ module Decode
55
55
  # Traverse the trie from this node.
56
56
  # Invoke `descend.call` to traverse the children of the current node.
57
57
  #
58
- # @param path [Array(String)] The current lexical path.
58
+ # @parameter path [Array(String)] The current lexical path.
59
59
  #
60
- # @block `{|path, node, descend| descend.call}`
60
+ # @block {|path, node, descend| descend.call}
61
61
  # @yield path [Array(String)] The current lexical path.
62
62
  # @yield node [Node] The current node which is being traversed.
63
63
  # @yield descend [Proc] The recursive method for traversing children.
@@ -76,12 +76,12 @@ module Decode
76
76
  end
77
77
 
78
78
  # The root of the trie.
79
- # @attr [Node]
79
+ # @attribute [Node]
80
80
  attr :root
81
81
 
82
82
  # Insert the specified value at the given path into the trie.
83
- # @param path [Array(String)] The lexical path where the value will be inserted.
84
- # @param value [Object] The value to insert.
83
+ # @parameter path [Array(String)] The lexical path where the value will be inserted.
84
+ # @parameter value [Object] The value to insert.
85
85
  def insert(path, value)
86
86
  node = @root
87
87
 
@@ -94,15 +94,15 @@ module Decode
94
94
 
95
95
  # Lookup the values at the specified path.
96
96
  #
97
- # @param path [Array(String)] The lexical path which contains the values.
98
- # @return [Array(Object) | Nil] The values that existed (or not) at the specified path.
97
+ # @parameter path [Array(String)] The lexical path which contains the values.
98
+ # @returns [Array(Object) | Nil] The values that existed (or not) at the specified path.
99
99
  def lookup(path)
100
100
  @root.lookup(path)
101
101
  end
102
102
 
103
103
  # Enumerate all lexical scopes under the specified path.
104
104
  #
105
- # @block `{|path, values| ...}`
105
+ # @block {|path, values| ...}
106
106
  # @yield path [Array(String)] The lexical path.
107
107
  # @yield values [Array(Object)] The values that exist at the given path.
108
108
  def each(path = [], &block)
@@ -116,7 +116,7 @@ module Decode
116
116
  end
117
117
 
118
118
  # Traverse the trie.
119
- # See {Node:traverse} for details.
119
+ # See {Node#traverse} for details.
120
120
  def traverse(path = [], &block)
121
121
  if node = @root.lookup(path)
122
122
  node.traverse(&block)