rucoa 0.8.0 → 0.10.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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +6 -0
  3. data/Gemfile.lock +8 -8
  4. data/data/definitions_ruby_3_1 +0 -0
  5. data/lib/rucoa/configuration.rb +4 -1
  6. data/lib/rucoa/definition_store.rb +268 -75
  7. data/lib/rucoa/definitions/base.rb +14 -3
  8. data/lib/rucoa/definitions/class_definition.rb +20 -4
  9. data/lib/rucoa/definitions/constant_definition.rb +31 -19
  10. data/lib/rucoa/definitions/method_definition.rb +44 -52
  11. data/lib/rucoa/definitions/method_parameter_definition.rb +4 -1
  12. data/lib/rucoa/definitions/module_definition.rb +39 -0
  13. data/lib/rucoa/handler_concerns/diagnostics_publishable.rb +14 -3
  14. data/lib/rucoa/handlers/base.rb +12 -3
  15. data/lib/rucoa/handlers/initialized_handler.rb +33 -5
  16. data/lib/rucoa/handlers/text_document_completion_handler.rb +1 -1
  17. data/lib/rucoa/handlers/text_document_definition_handler.rb +3 -99
  18. data/lib/rucoa/handlers/text_document_did_open_handler.rb +1 -4
  19. data/lib/rucoa/handlers/text_document_hover_handler.rb +21 -13
  20. data/lib/rucoa/handlers/text_document_selection_range_handler.rb +8 -2
  21. data/lib/rucoa/location.rb +37 -0
  22. data/lib/rucoa/node_concerns/body.rb +24 -0
  23. data/lib/rucoa/node_concerns/{name_full_qualifiable.rb → qualified_name.rb} +2 -2
  24. data/lib/rucoa/node_concerns.rb +2 -1
  25. data/lib/rucoa/node_inspector.rb +22 -26
  26. data/lib/rucoa/nodes/base.rb +51 -10
  27. data/lib/rucoa/nodes/begin_node.rb +8 -0
  28. data/lib/rucoa/nodes/casgn_node.rb +1 -1
  29. data/lib/rucoa/nodes/cbase_node.rb +8 -0
  30. data/lib/rucoa/nodes/class_node.rb +63 -1
  31. data/lib/rucoa/nodes/const_node.rb +64 -5
  32. data/lib/rucoa/nodes/def_node.rb +11 -9
  33. data/lib/rucoa/nodes/defs_node.rb +21 -0
  34. data/lib/rucoa/nodes/lvar_node.rb +2 -1
  35. data/lib/rucoa/nodes/module_node.rb +2 -1
  36. data/lib/rucoa/nodes/send_node.rb +14 -10
  37. data/lib/rucoa/nodes.rb +3 -1
  38. data/lib/rucoa/parse_result.rb +29 -0
  39. data/lib/rucoa/parser.rb +40 -8
  40. data/lib/rucoa/parser_builder.rb +7 -1
  41. data/lib/rucoa/position.rb +10 -1
  42. data/lib/rucoa/range.rb +11 -1
  43. data/lib/rucoa/rbs/class_definition_mapper.rb +13 -28
  44. data/lib/rucoa/rbs/constant_definition_mapper.rb +12 -6
  45. data/lib/rucoa/rbs/method_definition_mapper.rb +12 -6
  46. data/lib/rucoa/rbs/module_definition_mapper.rb +34 -6
  47. data/lib/rucoa/rubocop/autocorrector.rb +2 -2
  48. data/lib/rucoa/rubocop/investigator.rb +6 -3
  49. data/lib/rucoa/server.rb +9 -3
  50. data/lib/rucoa/source.rb +57 -27
  51. data/lib/rucoa/source_store.rb +0 -13
  52. data/lib/rucoa/unqualified_name.rb +9 -0
  53. data/lib/rucoa/version.rb +1 -1
  54. data/lib/rucoa/yard/definition_generators/attribute_reader_definition_generator.rb +60 -0
  55. data/lib/rucoa/yard/definition_generators/attribute_writer_definition_generator.rb +60 -0
  56. data/lib/rucoa/yard/definition_generators/base.rb +117 -0
  57. data/lib/rucoa/yard/definition_generators/class_definition_generator.rb +66 -0
  58. data/lib/rucoa/yard/definition_generators/constant_assignment_definition_generator.rb +29 -0
  59. data/lib/rucoa/yard/definition_generators/method_definition_generator.rb +47 -0
  60. data/lib/rucoa/yard/definition_generators/module_definition_generator.rb +62 -0
  61. data/lib/rucoa/yard/definition_generators.rb +15 -0
  62. data/lib/rucoa/yard/definitions_loader.rb +44 -65
  63. data/lib/rucoa/yard/type.rb +46 -0
  64. data/lib/rucoa/yard.rb +2 -1
  65. data/lib/rucoa.rb +4 -1
  66. metadata +18 -4
  67. data/lib/rucoa/yard/method_definition_mapper.rb +0 -215
@@ -4,7 +4,10 @@ module Rucoa
4
4
  class NodeInspector
5
5
  # @param definition_store [Rucoa::DefinitionStore]
6
6
  # @param node [Rucoa::Node]
7
- def initialize(definition_store:, node:)
7
+ def initialize(
8
+ definition_store:,
9
+ node:
10
+ )
8
11
  @definition_store = definition_store
9
12
  @node = node
10
13
  end
@@ -90,22 +93,14 @@ module Rucoa
90
93
  def constant_definition
91
94
  return unless @node.is_a?(Nodes::ConstNode)
92
95
 
93
- module_nesting_full_qualified_names = @node.each_ancestor(:class, :module).map(&:full_qualified_name)
94
- candidate_full_qualified_names = module_nesting_full_qualified_names.flat_map do |module_nesting_full_qualified_name|
95
- [
96
- module_nesting_full_qualified_name
97
- # TODO: *ancestors_of(module_nesting_full_qualified_name)
98
- ].map do |full_qualified_name|
99
- [
100
- full_qualified_name,
101
- @node.chained_name
102
- ].join('::')
103
- end
104
- end + [@node.chained_name]
105
- candidate_full_qualified_names.find do |candidate_full_qualified_name|
106
- definition = @definition_store.select_by_full_qualified_name(candidate_full_qualified_name).first
107
- break definition if definition
108
- end
96
+ @definition_store.find_definition_by_qualified_name(
97
+ @definition_store.resolve_constant(
98
+ UnqualifiedName.new(
99
+ chained_name: @node.chained_name,
100
+ module_nesting: @node.module_nesting
101
+ )
102
+ )
103
+ )
109
104
  end
110
105
 
111
106
  # @return [Boolean]
@@ -114,20 +109,21 @@ module Rucoa
114
109
  end
115
110
 
116
111
  # @return [String, nil]
117
- def nearest_def_full_qualified_name
118
- @node.each_ancestor(:def).first&.full_qualified_name
112
+ def nearest_def_qualified_name
113
+ @node.each_ancestor(:def).first&.qualified_name
119
114
  end
120
115
 
121
116
  # @return [Array<String>]
122
117
  def return_types_for_lvar
123
- full_qualified_name = nearest_def_full_qualified_name
124
- return [] unless full_qualified_name
118
+ qualified_name = nearest_def_qualified_name
119
+ return [] unless qualified_name
125
120
 
126
- @definition_store.select_by_full_qualified_name(full_qualified_name).flat_map do |definition|
127
- definition.parameters.select do |parameter|
128
- parameter.name == @node.name
129
- end.flat_map(&:types)
130
- end
121
+ definition = @definition_store.find_definition_by_qualified_name(qualified_name)
122
+ return [] unless definition
123
+
124
+ definition.parameters.select do |parameter|
125
+ parameter.name == @node.name
126
+ end.flat_map(&:types)
131
127
  end
132
128
 
133
129
  # @return [Array<String>]
@@ -37,7 +37,10 @@ module Rucoa
37
37
  # @param types [Array<Symbol>]
38
38
  # @return [Rucoa::Nodes::Base] if a block is given
39
39
  # @return [Enumerator] if no block is given
40
- def each_ancestor(*types, &block)
40
+ def each_ancestor(
41
+ *types,
42
+ &block
43
+ )
41
44
  return to_enum(__method__, *types) unless block
42
45
 
43
46
  visit_ancestors(types, &block)
@@ -47,7 +50,10 @@ module Rucoa
47
50
  # @param types [Array<Symbol>]
48
51
  # @return [Rucoa::Nodes::Base] if a block is given
49
52
  # @return [Enumerator] if no block is given
50
- def each_child_node(*types, &block)
53
+ def each_child_node(
54
+ *types,
55
+ &block
56
+ )
51
57
  return to_enum(__method__, *types) unless block
52
58
 
53
59
  visit_child_node(types, &block)
@@ -57,7 +63,10 @@ module Rucoa
57
63
  # @param types [Array<Symbol>]
58
64
  # @return [Rucoa::Nodes::Base] if a block is given
59
65
  # @return [Enumerator] if no block is given
60
- def each_descendant(*types, &block)
66
+ def each_descendant(
67
+ *types,
68
+ &block
69
+ )
61
70
  return to_enum(__method__, *types) unless block
62
71
 
63
72
  visit_descendants(types, &block)
@@ -68,7 +77,11 @@ module Rucoa
68
77
  # Some nodes change their type depending on the context.
69
78
  # For example, `const` node can be `casgn` node.
70
79
  # @return [Rucoa::Nodes::Base]
71
- def updated(type = nil, children = nil, properties = {})
80
+ def updated(
81
+ type = nil,
82
+ children = nil,
83
+ properties = {}
84
+ )
72
85
  properties[:location] ||= @location
73
86
  ParserBuilder.node_class_for(type || @type).new(
74
87
  type || @type,
@@ -89,7 +102,7 @@ module Rucoa
89
102
  # @return [String]
90
103
  # @example returns namespace
91
104
  # node = Rucoa::Source.new(
92
- # content: <<~RUBY
105
+ # content: <<~RUBY,
93
106
  # module Foo
94
107
  # class Bar
95
108
  # def baz
@@ -97,6 +110,7 @@ module Rucoa
97
110
  # end
98
111
  # end
99
112
  # RUBY
113
+ # uri: 'file:///path/to/foo/bar.rb'
100
114
  # ).node_at(
101
115
  # Rucoa::Position.new(
102
116
  # column: 4,
@@ -105,14 +119,38 @@ module Rucoa
105
119
  # )
106
120
  # expect(node.namespace).to eq('Foo::Bar')
107
121
  # @example returns "Object" when the node is not in a namespace
108
- # node = Rucoa::Parser.call(
109
- # <<~RUBY
122
+ # node = Rucoa::Source.new(
123
+ # content: <<~RUBY,
110
124
  # foo
111
125
  # RUBY
112
- # )
126
+ # uri: 'file:///path/to/example.rb'
127
+ # ).root_node
113
128
  # expect(node.namespace).to eq('Object')
114
129
  def namespace
115
- each_ancestor(:module, :class).first&.full_qualified_name || 'Object'
130
+ module_nesting.first || 'Object'
131
+ end
132
+
133
+ # @return [Array<String>]
134
+ # @example return ["Bar::Foo", "Foo"] for class Foo::Bar::Baz
135
+ # node = Rucoa::Source.new(
136
+ # content: <<~RUBY,
137
+ # module Foo
138
+ # module Bar
139
+ # module Baz
140
+ # end
141
+ # end
142
+ # end
143
+ # RUBY
144
+ # uri: 'file:///path/to/foo/bar/baz.rb'
145
+ # ).node_at(
146
+ # Rucoa::Position.new(
147
+ # column: 4,
148
+ # line: 3
149
+ # )
150
+ # )
151
+ # expect(node.module_nesting).to eq(['Foo::Bar', 'Foo'])
152
+ def module_nesting
153
+ each_ancestor(:class, :module).map(&:qualified_name)
116
154
  end
117
155
 
118
156
  protected
@@ -120,7 +158,10 @@ module Rucoa
120
158
  # Visit all descendants.
121
159
  # @param types [Array<Symbol>]
122
160
  # @return [void]
123
- def visit_descendants(types, &block)
161
+ def visit_descendants(
162
+ types,
163
+ &block
164
+ )
124
165
  each_child_node do |child|
125
166
  yield(child) if types.empty? || types.include?(child.type)
126
167
  child.visit_descendants(types, &block)
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Nodes
5
+ class BeginNode < Base
6
+ end
7
+ end
8
+ end
@@ -3,7 +3,7 @@
3
3
  module Rucoa
4
4
  module Nodes
5
5
  class CasgnNode < Base
6
- include NodeConcerns::NameFullQualifiable
6
+ include NodeConcerns::QualifiedName
7
7
 
8
8
  # @return [String]
9
9
  def name
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Nodes
5
+ class CbaseNode < Base
6
+ end
7
+ end
8
+ end
@@ -2,7 +2,69 @@
2
2
 
3
3
  module Rucoa
4
4
  module Nodes
5
- class ClassNode < ModuleNode
5
+ class ClassNode < Base
6
+ include NodeConcerns::Body
7
+ include NodeConcerns::QualifiedName
8
+
9
+ # @return [String]
10
+ def name
11
+ const_node.name
12
+ end
13
+
14
+ # @return [String, nil]
15
+ # @example returns nil for class for `class Foo`
16
+ # node = Rucoa::Source.new(
17
+ # content: <<~RUBY,
18
+ # class Foo
19
+ # end
20
+ # RUBY
21
+ # uri: 'file:///path/to/foo.rb'
22
+ # ).root_node
23
+ # expect(node.super_class_chained_name).to be_nil
24
+ # @example returns "Bar" for class for `class Foo < Bar`
25
+ # node = Rucoa::Source.new(
26
+ # content: <<~RUBY,
27
+ # class Foo < Bar
28
+ # end
29
+ # RUBY
30
+ # uri: 'file:///path/to/foo.rb'
31
+ # ).root_node
32
+ # expect(node.super_class_chained_name).to eq('Bar')
33
+ # @example returns "Bar::Baz" for class for `class Foo < Bar::Baz`
34
+ # node = Rucoa::Source.new(
35
+ # content: <<~RUBY,
36
+ # class Foo < Bar::Baz
37
+ # end
38
+ # RUBY
39
+ # uri: 'file:///path/to/foo.rb'
40
+ # ).root_node
41
+ # expect(node.super_class_chained_name).to eq('Bar::Baz')
42
+ # @example returns "::Bar" for class for `class Foo < ::Bar`
43
+ # node = Rucoa::Source.new(
44
+ # content: <<~RUBY,
45
+ # class Foo < ::Bar
46
+ # end
47
+ # RUBY
48
+ # uri: 'file:///path/to/foo.rb'
49
+ # ).root_node
50
+ # expect(node.super_class_chained_name).to eq('::Bar')
51
+ def super_class_chained_name
52
+ return unless super_class_node.is_a?(Nodes::ConstNode)
53
+
54
+ super_class_node.chained_name
55
+ end
56
+
57
+ private
58
+
59
+ # @return [Rucoa::Nodes::ConstNode]
60
+ def const_node
61
+ children[0]
62
+ end
63
+
64
+ # @return [Rucoa::Nodes::Base, nil]
65
+ def super_class_node
66
+ children[1]
67
+ end
6
68
  end
7
69
  end
8
70
  end
@@ -5,10 +5,25 @@ module Rucoa
5
5
  class ConstNode < Base
6
6
  # @return [String]
7
7
  # @example returns "A" for "A"
8
- # node = Rucoa::Parser.call('A')
8
+ # node = Rucoa::Source.new(
9
+ # content: <<~RUBY,
10
+ # A
11
+ # RUBY
12
+ # uri: 'file:///path/to/a.rb'
13
+ # ).root_node
9
14
  # expect(node.name).to eq('A')
10
15
  # @example returns "B" for "A::B"
11
- # node = Rucoa::Parser.call('A::B')
16
+ # node = Rucoa::Source.new(
17
+ # content: <<~RUBY,
18
+ # A::B
19
+ # RUBY
20
+ # uri: 'file:///path/to/a.rb'
21
+ # ).node_at(
22
+ # Rucoa::Position.new(
23
+ # column: 4,
24
+ # line: 1
25
+ # )
26
+ # )
12
27
  # expect(node.name).to eq('B')
13
28
  def name
14
29
  children[1].to_s
@@ -16,13 +31,34 @@ module Rucoa
16
31
 
17
32
  # @return [String]
18
33
  # @example returns "A" for "A"
19
- # node = Rucoa::Parser.call('A')
34
+ # node = Rucoa::Source.new(
35
+ # content: <<~RUBY,
36
+ # A
37
+ # RUBY
38
+ # uri: 'file:///path/to/a.rb'
39
+ # ).root_node
20
40
  # expect(node.chained_name).to eq('A')
21
41
  # @example returns "A::B" for "A::B"
22
- # node = Rucoa::Parser.call('A::B')
42
+ # node = Rucoa::Source.new(
43
+ # content: <<~RUBY,
44
+ # A::B
45
+ # RUBY
46
+ # uri: 'file:///path/to/a.rb'
47
+ # ).node_at(
48
+ # Rucoa::Position.new(
49
+ # column: 4,
50
+ # line: 1
51
+ # )
52
+ # )
23
53
  # expect(node.chained_name).to eq('A::B')
24
54
  def chained_name
25
- if receiver.is_a?(ConstNode)
55
+ case receiver
56
+ when Nodes::CbaseNode
57
+ [
58
+ '',
59
+ name
60
+ ].join('::')
61
+ when Nodes::ConstNode
26
62
  [
27
63
  receiver.chained_name,
28
64
  name
@@ -32,6 +68,29 @@ module Rucoa
32
68
  end
33
69
  end
34
70
 
71
+ # @return [Array<String>]
72
+ # @example return ["Bar::Foo", "Foo"] for class Foo::Bar::Baz
73
+ # node = Rucoa::Source.new(
74
+ # content: <<~RUBY,
75
+ # module Foo
76
+ # module Bar
77
+ # module Baz
78
+ # end
79
+ # end
80
+ # end
81
+ # RUBY
82
+ # uri: 'file:///path/to/foo/bar/baz.rb'
83
+ # ).node_at(
84
+ # Rucoa::Position.new(
85
+ # column: 4,
86
+ # line: 3
87
+ # )
88
+ # )
89
+ # expect(node.module_nesting).to eq(['Foo::Bar', 'Foo'])
90
+ def module_nesting
91
+ each_ancestor(:class, :module).map(&:qualified_name)
92
+ end
93
+
35
94
  private
36
95
 
37
96
  # @return [Rucoa::Nodes::Base, nil]
@@ -6,7 +6,7 @@ module Rucoa
6
6
  # @return [String]
7
7
  # @example returns method name
8
8
  # node = Rucoa::Source.new(
9
- # content: <<~RUBY
9
+ # content: <<~RUBY,
10
10
  # module Foo
11
11
  # class Bar
12
12
  # def baz
@@ -14,6 +14,7 @@ module Rucoa
14
14
  # end
15
15
  # end
16
16
  # RUBY
17
+ # uri: 'file:///path/to/foo/bar.rb'
17
18
  # ).node_at(
18
19
  # Rucoa::Position.new(
19
20
  # column: 4,
@@ -28,7 +29,7 @@ module Rucoa
28
29
  # @return [String]
29
30
  # @example returns full qualified name
30
31
  # node = Rucoa::Source.new(
31
- # content: <<~RUBY
32
+ # content: <<~RUBY,
32
33
  # module Foo
33
34
  # class Bar
34
35
  # def baz
@@ -36,14 +37,15 @@ module Rucoa
36
37
  # end
37
38
  # end
38
39
  # RUBY
40
+ # uri: 'file:///path/to/foo/bar.rb'
39
41
  # ).node_at(
40
42
  # Rucoa::Position.new(
41
43
  # column: 4,
42
44
  # line: 3
43
45
  # )
44
46
  # )
45
- # expect(node.full_qualified_name).to eq('Foo::Bar#baz')
46
- def full_qualified_name
47
+ # expect(node.qualified_name).to eq('Foo::Bar#baz')
48
+ def qualified_name
47
49
  [
48
50
  namespace,
49
51
  method_marker,
@@ -51,6 +53,11 @@ module Rucoa
51
53
  ].join
52
54
  end
53
55
 
56
+ # @return [Boolean]
57
+ def singleton?
58
+ each_ancestor(:sclass).any?
59
+ end
60
+
54
61
  private
55
62
 
56
63
  # @return [String]
@@ -61,11 +68,6 @@ module Rucoa
61
68
  '#'
62
69
  end
63
70
  end
64
-
65
- # @return [Boolean]
66
- def singleton?
67
- each_ancestor(:sclass).any?
68
- end
69
71
  end
70
72
  end
71
73
  end
@@ -3,10 +3,31 @@
3
3
  module Rucoa
4
4
  module Nodes
5
5
  class DefsNode < Base
6
+ # @return [String]
7
+ def qualified_name
8
+ [
9
+ namespace,
10
+ method_marker,
11
+ name
12
+ ].join
13
+ end
14
+
6
15
  # @return [String]
7
16
  def name
8
17
  children[1].to_s
9
18
  end
19
+
20
+ # @return [Boolean]
21
+ def singleton?
22
+ true
23
+ end
24
+
25
+ private
26
+
27
+ # @return [String]
28
+ def method_marker
29
+ '.'
30
+ end
10
31
  end
11
32
  end
12
33
  end
@@ -6,10 +6,11 @@ module Rucoa
6
6
  # @return [String]
7
7
  # @example returns local variable name
8
8
  # node = Rucoa::Source.new(
9
- # content: <<~RUBY
9
+ # content: <<~RUBY,
10
10
  # foo = 1
11
11
  # foo
12
12
  # RUBY
13
+ # uri: 'file:///path/to/example.rb'
13
14
  # ).node_at(
14
15
  # Rucoa::Position.new(
15
16
  # column: 2,
@@ -3,7 +3,8 @@
3
3
  module Rucoa
4
4
  module Nodes
5
5
  class ModuleNode < Base
6
- include NodeConcerns::NameFullQualifiable
6
+ include NodeConcerns::Body
7
+ include NodeConcerns::QualifiedName
7
8
 
8
9
  # @return [String]
9
10
  def name
@@ -5,11 +5,12 @@ module Rucoa
5
5
  class SendNode < Base
6
6
  # @return [Array<Rucoa::Nodes::Base>]
7
7
  # @example returns arguments
8
- # node = Rucoa::Parser.call(
9
- # <<~RUBY
8
+ # node = Rucoa::Source.new(
9
+ # content: <<~RUBY,
10
10
  # foo(bar, baz)
11
11
  # RUBY
12
- # )
12
+ # uri: 'file:///path/to/example.rb'
13
+ # ).root_node
13
14
  # expect(node.arguments.map(&:name)).to eq(
14
15
  # %w[
15
16
  # bar
@@ -22,11 +23,12 @@ module Rucoa
22
23
 
23
24
  # @return [String]
24
25
  # @example returns method name
25
- # node = Rucoa::Parser.call(
26
- # <<~RUBY
26
+ # node = Rucoa::Source.new(
27
+ # content: <<~RUBY,
27
28
  # foo(bar, baz)
28
29
  # RUBY
29
- # )
30
+ # uri: 'file:///path/to/example.rb'
31
+ # ).root_node
30
32
  # expect(node.name).to eq('foo')
31
33
  def name
32
34
  children[1].to_s
@@ -34,17 +36,19 @@ module Rucoa
34
36
 
35
37
  # @return [Rucoa::Nodes::Base, nil]
36
38
  # @example returns nil for receiver-less method call
37
- # node = Rucoa::Parser.call(
38
- # <<~RUBY
39
+ # node = Rucoa::Source.new(
40
+ # content: <<~RUBY,
39
41
  # foo(bar, baz)
40
42
  # RUBY
41
- # )
43
+ # uri: 'file:///path/to/example.rb'
44
+ # ).root_node
42
45
  # expect(node.receiver).to be_nil
43
46
  # @example returns receiver
44
47
  # node = Rucoa::Source.new(
45
- # content: <<~RUBY
48
+ # content: <<~RUBY,
46
49
  # foo.bar
47
50
  # RUBY
51
+ # uri: 'file:///path/to/example.rb'
48
52
  # ).node_at(
49
53
  # Rucoa::Position.new(
50
54
  # column: 4,
data/lib/rucoa/nodes.rb CHANGED
@@ -3,8 +3,10 @@
3
3
  module Rucoa
4
4
  module Nodes
5
5
  autoload :Base, 'rucoa/nodes/base'
6
- autoload :ClassNode, 'rucoa/nodes/class_node'
6
+ autoload :BeginNode, 'rucoa/nodes/begin_node'
7
7
  autoload :CasgnNode, 'rucoa/nodes/casgn_node'
8
+ autoload :CbaseNode, 'rucoa/nodes/cbase_node'
9
+ autoload :ClassNode, 'rucoa/nodes/class_node'
8
10
  autoload :ConstNode, 'rucoa/nodes/const_node'
9
11
  autoload :DefNode, 'rucoa/nodes/def_node'
10
12
  autoload :DefsNode, 'rucoa/nodes/defs_node'
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ class ParseResult
5
+ # @return [Array<Parser::Source::Comment>, nil]
6
+ attr_reader :associations
7
+
8
+ # @return [Rucoa::Nodes::Base, nil]
9
+ attr_reader :root_node
10
+
11
+ # @param associations [Array<Parser::Source::Comment>, nil]
12
+ # @param failed [Boolean]
13
+ # @param root_node [Rucoa::Nodes::Base, nil]
14
+ def initialize(
15
+ associations: nil,
16
+ failed: false,
17
+ root_node: nil
18
+ )
19
+ @associations = associations
20
+ @failed = failed
21
+ @root_node = root_node
22
+ end
23
+
24
+ # @return [Boolean]
25
+ def failed?
26
+ @failed
27
+ end
28
+ end
29
+ end
data/lib/rucoa/parser.rb CHANGED
@@ -3,29 +3,61 @@
3
3
  require 'parser/current'
4
4
 
5
5
  module Rucoa
6
- # Parses Ruby text code.
7
6
  class Parser
8
7
  class << self
9
8
  # @param text [String]
10
- # @return [Rucoa::Nodes::Base]
11
- def call(text)
12
- new(text).call
9
+ # @param uri [String]
10
+ # @return [Rucoa::ParseResult]
11
+ # @example returns non-failed parse result for valid Ruby source
12
+ # result = Rucoa::Parser.call(
13
+ # text: 'foo',
14
+ # uri: 'file:///path/to/foo.rb'
15
+ # )
16
+ # expect(result).not_to be_failed
17
+ # @example returns failed parse result for invalid Ruby source
18
+ # result = Rucoa::Parser.call(
19
+ # text: 'foo(',
20
+ # uri: 'file:///path/to/foo.rb'
21
+ # )
22
+ # expect(result).to be_failed
23
+ def call(
24
+ text:,
25
+ uri:
26
+ )
27
+ new(
28
+ text: text,
29
+ uri: uri
30
+ ).call
13
31
  end
14
32
  end
15
33
 
16
34
  # @param text [String]
17
- def initialize(text)
35
+ # @param uri [String]
36
+ def initialize(
37
+ text:,
38
+ uri:
39
+ )
18
40
  @text = text
41
+ @uri = uri
19
42
  end
20
43
 
21
- # @return [Rucoa::Nodes::Base]
44
+ # @return [Rucoa::ParseResult]
22
45
  def call
23
- parser.parse(
46
+ root_node, comments = parser.parse_with_comments(
24
47
  ::Parser::Source::Buffer.new(
25
- '',
48
+ @uri,
26
49
  source: @text
27
50
  )
28
51
  )
52
+ ParseResult.new(
53
+ associations: ::Parser::Source::Comment.associate_locations(
54
+ root_node,
55
+ comments
56
+ ),
57
+ root_node: root_node
58
+ )
59
+ rescue ::Parser::SyntaxError
60
+ ParseResult.new(failed: true)
29
61
  end
30
62
 
31
63
  private