rucoa 0.2.0 → 0.5.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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +6 -0
  3. data/Gemfile +1 -0
  4. data/Gemfile.lock +11 -2
  5. data/README.md +39 -6
  6. data/data/definitions_ruby_3_1 +0 -0
  7. data/images/diagnostics.gif +0 -0
  8. data/images/document-formatting.gif +0 -0
  9. data/images/document-symbol.gif +0 -0
  10. data/images/selection-ranges.gif +0 -0
  11. data/lib/rucoa/cli.rb +2 -2
  12. data/lib/rucoa/configuration.rb +97 -0
  13. data/lib/rucoa/definition_archiver.rb +29 -0
  14. data/lib/rucoa/definition_builders/rbs_constant_definition_builder.rb +44 -0
  15. data/lib/rucoa/definition_builders/rbs_method_definition_builder.rb +106 -0
  16. data/lib/rucoa/definition_builders/yard_method_definition_builder.rb +191 -0
  17. data/lib/rucoa/definition_builders.rb +9 -0
  18. data/lib/rucoa/definition_store.rb +63 -0
  19. data/lib/rucoa/definitions/base.rb +12 -0
  20. data/lib/rucoa/definitions/constant_definition.rb +51 -0
  21. data/lib/rucoa/definitions/method_definition.rb +126 -0
  22. data/lib/rucoa/definitions/method_parameter_definition.rb +30 -0
  23. data/lib/rucoa/definitions.rb +9 -0
  24. data/lib/rucoa/handler_concerns/configuration_requestable.rb +35 -0
  25. data/lib/rucoa/handler_concerns/diagnostics_publishable.rb +172 -0
  26. data/lib/rucoa/handler_concerns.rb +8 -0
  27. data/lib/rucoa/handlers/base.rb +69 -0
  28. data/lib/rucoa/handlers/exit_handler.rb +11 -0
  29. data/lib/rucoa/handlers/initialize_handler.rb +29 -0
  30. data/lib/rucoa/handlers/initialized_handler.rb +23 -0
  31. data/lib/rucoa/handlers/shutdown_handler.rb +12 -0
  32. data/lib/rucoa/handlers/text_document_code_action_handler.rb +104 -0
  33. data/lib/rucoa/handlers/text_document_did_change_handler.rb +12 -0
  34. data/lib/rucoa/handlers/text_document_did_open_handler.rb +38 -0
  35. data/lib/rucoa/handlers/text_document_document_symbol_handler.rb +241 -0
  36. data/lib/rucoa/handlers/text_document_formatting_handler.rb +64 -0
  37. data/lib/rucoa/handlers/text_document_range_formatting_handler.rb +76 -0
  38. data/lib/rucoa/handlers/text_document_selection_range_handler.rb +141 -0
  39. data/lib/rucoa/handlers/text_document_signature_help_handler.rb +68 -0
  40. data/lib/rucoa/handlers/workspace_did_change_configuration_handler.rb +13 -0
  41. data/lib/rucoa/handlers.rb +20 -0
  42. data/lib/rucoa/message_reader.rb +3 -1
  43. data/lib/rucoa/message_writer.rb +3 -0
  44. data/lib/rucoa/node_concerns/name_full_qualifiable.rb +20 -0
  45. data/lib/rucoa/node_concerns.rb +7 -0
  46. data/lib/rucoa/node_inspector.rb +109 -0
  47. data/lib/rucoa/nodes/base.rb +43 -0
  48. data/lib/rucoa/nodes/casgn_node.rb +14 -0
  49. data/lib/rucoa/nodes/class_node.rb +8 -0
  50. data/lib/rucoa/nodes/const_node.rb +12 -0
  51. data/lib/rucoa/nodes/def_node.rb +71 -0
  52. data/lib/rucoa/nodes/defs_node.rb +12 -0
  53. data/lib/rucoa/nodes/lvar_node.rb +25 -0
  54. data/lib/rucoa/nodes/module_node.rb +21 -0
  55. data/lib/rucoa/nodes/sclass_node.rb +8 -0
  56. data/lib/rucoa/nodes/send_node.rb +60 -0
  57. data/lib/rucoa/nodes/str_node.rb +4 -0
  58. data/lib/rucoa/nodes/sym_node.rb +12 -0
  59. data/lib/rucoa/nodes.rb +10 -0
  60. data/lib/rucoa/parser_builder.rb +20 -10
  61. data/lib/rucoa/range.rb +89 -13
  62. data/lib/rucoa/rbs_document_loader.rb +43 -0
  63. data/lib/rucoa/rubocop_autocorrector.rb +1 -1
  64. data/lib/rucoa/rubocop_configuration_checker.rb +42 -0
  65. data/lib/rucoa/rubocop_investigator.rb +1 -1
  66. data/lib/rucoa/server.rb +96 -122
  67. data/lib/rucoa/source.rb +56 -6
  68. data/lib/rucoa/source_store.rb +11 -9
  69. data/lib/rucoa/types/method_type.rb +23 -0
  70. data/lib/rucoa/types.rb +7 -0
  71. data/lib/rucoa/version.rb +1 -1
  72. data/lib/rucoa/yard_glob_document_loader.rb +47 -0
  73. data/lib/rucoa/yard_string_document_loader.rb +70 -0
  74. data/lib/rucoa.rb +14 -4
  75. data/rucoa.gemspec +1 -0
  76. metadata +70 -6
  77. data/lib/rucoa/code_action_provider.rb +0 -102
  78. data/lib/rucoa/diagnostic_provider.rb +0 -159
  79. data/lib/rucoa/formatting_provider.rb +0 -52
  80. data/lib/rucoa/selection_range_provider.rb +0 -97
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Handlers
5
+ class TextDocumentSignatureHelpHandler < Base
6
+ def call
7
+ respond(signature_help)
8
+ end
9
+
10
+ private
11
+
12
+ # @return [Hash]
13
+ def signature_help
14
+ return unless respondable?
15
+
16
+ {
17
+ signatures: signature_informations
18
+ }
19
+ end
20
+
21
+ # @return [Boolean]
22
+ def respondable?
23
+ configuration.enables_signature_help? &&
24
+ node.is_a?(Nodes::SendNode)
25
+ end
26
+
27
+ # @return [Array<Hash>]
28
+ def signature_informations
29
+ method_definitions.map do |method_definition|
30
+ {
31
+ documentation: method_definition.description,
32
+ label: method_definition.signatures.join("\n")
33
+ }
34
+ end
35
+ end
36
+
37
+ # @return [Array<Rucoa::Definitions::MethodDefinition>]
38
+ def method_definitions
39
+ NodeInspector.new(
40
+ definition_store: definition_store,
41
+ node: node
42
+ ).method_definitions
43
+ end
44
+
45
+ # @return [Rucoa::Nodes::Base, nil]
46
+ def node
47
+ @node ||= source.node_at(position)
48
+ end
49
+
50
+ # @return [Rucoa::Position]
51
+ def position
52
+ Position.from_vscode_position(
53
+ request.dig('params', 'position')
54
+ )
55
+ end
56
+
57
+ # @return [Rucoa::Source]
58
+ def source
59
+ source_store.get(uri)
60
+ end
61
+
62
+ # @return [String]
63
+ def uri
64
+ request.dig('params', 'textDocument', 'uri')
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Handlers
5
+ class WorkspaceDidChangeConfigurationHandler < Base
6
+ include HandlerConcerns::ConfigurationRequestable
7
+
8
+ def call
9
+ request_workspace_configuration
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Handlers
5
+ autoload :Base, 'rucoa/handlers/base'
6
+ autoload :ExitHandler, 'rucoa/handlers/exit_handler'
7
+ autoload :InitializeHandler, 'rucoa/handlers/initialize_handler'
8
+ autoload :InitializedHandler, 'rucoa/handlers/initialized_handler'
9
+ autoload :ShutdownHandler, 'rucoa/handlers/shutdown_handler'
10
+ autoload :TextDocumentCodeActionHandler, 'rucoa/handlers/text_document_code_action_handler'
11
+ autoload :TextDocumentDidChangeHandler, 'rucoa/handlers/text_document_did_change_handler'
12
+ autoload :TextDocumentDidOpenHandler, 'rucoa/handlers/text_document_did_open_handler'
13
+ autoload :TextDocumentDocumentSymbolHandler, 'rucoa/handlers/text_document_document_symbol_handler'
14
+ autoload :TextDocumentFormattingHandler, 'rucoa/handlers/text_document_formatting_handler'
15
+ autoload :TextDocumentRangeFormattingHandler, 'rucoa/handlers/text_document_range_formatting_handler'
16
+ autoload :TextDocumentSelectionRangeHandler, 'rucoa/handlers/text_document_selection_range_handler'
17
+ autoload :TextDocumentSignatureHelpHandler, 'rucoa/handlers/text_document_signature_help_handler'
18
+ autoload :WorkspaceDidChangeConfigurationHandler, 'rucoa/handlers/workspace_did_change_configuration_handler'
19
+ end
20
+ end
@@ -11,8 +11,10 @@ module Rucoa
11
11
  end
12
12
 
13
13
  # @yieldparam message [Hash]
14
- # @return [void]
14
+ # @return [Enumerator<Hash>, void]
15
15
  def read
16
+ return enum_for(:read) unless block_given?
17
+
16
18
  while (buffer = @io.gets("\r\n\r\n"))
17
19
  content_length = buffer[/Content-Length: (\d+)/i, 1]
18
20
  raise Errors::ContentLengthHeaderNotFound unless content_length
@@ -19,6 +19,9 @@ module Rucoa
19
19
  end
20
20
  end
21
21
 
22
+ # @return [IO]
23
+ attr_reader :io
24
+
22
25
  # @param io [IO]
23
26
  def initialize(io)
24
27
  @io = io
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module NodeConcerns
5
+ module NameFullQualifiable
6
+ # @return [String]
7
+ def full_qualified_name
8
+ [
9
+ name,
10
+ *each_ancestor(:class, :constant, :module).map(&:name)
11
+ ].reverse.join('::')
12
+ end
13
+
14
+ # @return [String]
15
+ def name
16
+ raise NotImplementedError
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module NodeConcerns
5
+ autoload :NameFullQualifiable, 'rucoa/node_concerns/name_full_qualifiable'
6
+ end
7
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ module Rucoa
6
+ class NodeInspector
7
+ # @param definition_store [Rucoa::DefinitionStore]
8
+ # @param node [Rucoa::Node]
9
+ def initialize(definition_store:, node:)
10
+ @definition_store = definition_store
11
+ @node = node
12
+ end
13
+
14
+ # @return [Array<String>, nil]
15
+ def method_definitions
16
+ method_full_qualified_name&.flat_map do |full_qualified_name|
17
+ @definition_store.select_by_full_qualified_name(full_qualified_name)
18
+ end
19
+ end
20
+
21
+ # @return [Array<String>, nil]
22
+ def method_receiver_types
23
+ return unless @node.is_a?(Nodes::SendNode)
24
+
25
+ if @node.receiver
26
+ self.class.new(
27
+ definition_store: @definition_store,
28
+ node: @node.receiver
29
+ ).return_types
30
+ else
31
+ [@node.namespace]
32
+ end
33
+ end
34
+
35
+ # @return [Array<String>, nil]
36
+ def return_types
37
+ case @node.type
38
+ when :const
39
+ [@node.name]
40
+ when :lvar
41
+ return_types_for_lvar
42
+ when :send
43
+ return_types_for_send
44
+ when :array
45
+ %w[Array]
46
+ when :class, :module, :nil
47
+ %w[NilClass]
48
+ when :complex
49
+ %w[Complex]
50
+ when :def, :sym
51
+ %w[Symbol]
52
+ when :dstr, :str, :xstr
53
+ %w[String]
54
+ when :erange, :irange
55
+ %w[Range]
56
+ when false
57
+ %w[FalseClass]
58
+ when :float
59
+ %w[Float]
60
+ when :hash, :pair
61
+ %w[Hash]
62
+ when :int
63
+ %w[Integer]
64
+ when :rational
65
+ %w[Rational]
66
+ when :regexp, :regopt
67
+ %w[Regexp]
68
+ when true
69
+ %w[TrueClass]
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ # @return [Array<String>, nil]
76
+ def method_full_qualified_name
77
+ method_receiver_types&.map do |type|
78
+ [
79
+ type,
80
+ @node.name
81
+ ].join('#')
82
+ end
83
+ end
84
+
85
+ # @return [String, nil]
86
+ def nearest_def_full_qualified_name
87
+ @node.each_ancestor(:def).first&.full_qualified_name
88
+ end
89
+
90
+ # @return [Array<String>]
91
+ def return_types_for_lvar
92
+ full_qualified_name = nearest_def_full_qualified_name
93
+ return [] unless full_qualified_name
94
+
95
+ @definition_store.select_by_full_qualified_name(full_qualified_name).flat_map do |definition|
96
+ definition.parameters.select do |parameter|
97
+ parameter.name == @node.name
98
+ end.flat_map(&:types)
99
+ end
100
+ end
101
+
102
+ # @return [Array<String>]
103
+ def return_types_for_send
104
+ method_full_qualified_name.flat_map do |full_qualified_name|
105
+ @definition_store.select_by_full_qualified_name(full_qualified_name).flat_map(&:return_types)
106
+ end.uniq
107
+ end
108
+ end
109
+ end
@@ -64,6 +64,19 @@ module Rucoa
64
64
  self
65
65
  end
66
66
 
67
+ # @note Override.
68
+ # Some nodes change their type depending on the context.
69
+ # For example, `const` node can be `casgn` node.
70
+ # @return [Rucoa::Nodes::Base]
71
+ def updated(type = nil, children = nil, properties = {})
72
+ properties[:location] ||= @location
73
+ ParserBuilder.node_class_for(type || @type).new(
74
+ type || @type,
75
+ children || @children,
76
+ properties
77
+ )
78
+ end
79
+
67
80
  # @param position [Rucoa::Position]
68
81
  # @return [Boolean]
69
82
  def include_position?(position)
@@ -72,6 +85,36 @@ module Rucoa
72
85
  Range.from_parser_range(location.expression).include?(position)
73
86
  end
74
87
 
88
+ # @note namespace is a String representation of `Module.nesting`.
89
+ # @return [String]
90
+ # @example returns namespace
91
+ # node = Rucoa::Source.new(
92
+ # content: <<~RUBY
93
+ # module Foo
94
+ # class Bar
95
+ # def baz
96
+ # end
97
+ # end
98
+ # end
99
+ # RUBY
100
+ # ).node_at(
101
+ # Rucoa::Position.new(
102
+ # column: 4,
103
+ # line: 3
104
+ # )
105
+ # )
106
+ # expect(node.namespace).to eq('Foo::Bar')
107
+ # @example returns "Object" when the node is not in a namespace
108
+ # node = Rucoa::Parser.call(
109
+ # <<~RUBY
110
+ # foo
111
+ # RUBY
112
+ # )
113
+ # expect(node.namespace).to eq('Object')
114
+ def namespace
115
+ each_ancestor(:module, :class).first&.full_qualified_name || 'Object'
116
+ end
117
+
75
118
  protected
76
119
 
77
120
  # Visit all descendants.
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Nodes
5
+ class CasgnNode < Base
6
+ include NodeConcerns::NameFullQualifiable
7
+
8
+ # @return [String]
9
+ def name
10
+ children[1].to_s
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Nodes
5
+ class ClassNode < ModuleNode
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Nodes
5
+ class ConstNode < Base
6
+ # @return [String]
7
+ def name
8
+ children[1].to_s
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Nodes
5
+ class DefNode < Base
6
+ # @return [String]
7
+ # @example returns method name
8
+ # node = Rucoa::Source.new(
9
+ # content: <<~RUBY
10
+ # module Foo
11
+ # class Bar
12
+ # def baz
13
+ # end
14
+ # end
15
+ # end
16
+ # RUBY
17
+ # ).node_at(
18
+ # Rucoa::Position.new(
19
+ # column: 4,
20
+ # line: 3
21
+ # )
22
+ # )
23
+ # expect(node.name).to eq('baz')
24
+ def name
25
+ children[0].to_s
26
+ end
27
+
28
+ # @return [String]
29
+ # @example returns full qualified name
30
+ # node = Rucoa::Source.new(
31
+ # content: <<~RUBY
32
+ # module Foo
33
+ # class Bar
34
+ # def baz
35
+ # end
36
+ # end
37
+ # end
38
+ # RUBY
39
+ # ).node_at(
40
+ # Rucoa::Position.new(
41
+ # column: 4,
42
+ # line: 3
43
+ # )
44
+ # )
45
+ # expect(node.full_qualified_name).to eq('Foo::Bar#baz')
46
+ def full_qualified_name
47
+ [
48
+ namespace,
49
+ method_marker,
50
+ name
51
+ ].join
52
+ end
53
+
54
+ private
55
+
56
+ # @return [String]
57
+ def method_marker
58
+ if singleton?
59
+ '.'
60
+ else
61
+ '#'
62
+ end
63
+ end
64
+
65
+ # @return [Boolean]
66
+ def singleton?
67
+ each_ancestor(:sclass).any?
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Nodes
5
+ class DefsNode < Base
6
+ # @return [String]
7
+ def name
8
+ children[1].to_s
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Nodes
5
+ class LvarNode < Base
6
+ # @return [String]
7
+ # @example returns local variable name
8
+ # node = Rucoa::Source.new(
9
+ # content: <<~RUBY
10
+ # foo = 1
11
+ # foo
12
+ # RUBY
13
+ # ).node_at(
14
+ # Rucoa::Position.new(
15
+ # column: 2,
16
+ # line: 2
17
+ # )
18
+ # )
19
+ # expect(node.name).to eq('foo')
20
+ def name
21
+ children[0].to_s
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Nodes
5
+ class ModuleNode < Base
6
+ include NodeConcerns::NameFullQualifiable
7
+
8
+ # @return [String]
9
+ def name
10
+ const_node.name
11
+ end
12
+
13
+ private
14
+
15
+ # @return [Rucoa::Nodes::ConstNode]
16
+ def const_node
17
+ children[0]
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Nodes
5
+ class SclassNode < Base
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Nodes
5
+ class SendNode < Base
6
+ # @return [Array<Rucoa::Nodes::Base>]
7
+ # @example returns arguments
8
+ # node = Rucoa::Parser.call(
9
+ # <<~RUBY
10
+ # foo(bar, baz)
11
+ # RUBY
12
+ # )
13
+ # expect(node.arguments.map(&:name)).to eq(
14
+ # %w[
15
+ # bar
16
+ # baz
17
+ # ]
18
+ # )
19
+ def arguments
20
+ children[2..]
21
+ end
22
+
23
+ # @return [String]
24
+ # @example returns method name
25
+ # node = Rucoa::Parser.call(
26
+ # <<~RUBY
27
+ # foo(bar, baz)
28
+ # RUBY
29
+ # )
30
+ # expect(node.name).to eq('foo')
31
+ def name
32
+ children[1].to_s
33
+ end
34
+
35
+ # @return [Rucoa::Nodes::Base, nil]
36
+ # @example returns nil for receiver-less method call
37
+ # node = Rucoa::Parser.call(
38
+ # <<~RUBY
39
+ # foo(bar, baz)
40
+ # RUBY
41
+ # )
42
+ # expect(node.receiver).to be_nil
43
+ # @example returns receiver
44
+ # node = Rucoa::Source.new(
45
+ # content: <<~RUBY
46
+ # foo.bar
47
+ # RUBY
48
+ # ).node_at(
49
+ # Rucoa::Position.new(
50
+ # column: 4,
51
+ # line: 1
52
+ # )
53
+ # )
54
+ # expect(node.receiver).to be_a(Rucoa::Nodes::SendNode)
55
+ def receiver
56
+ children[0]
57
+ end
58
+ end
59
+ end
60
+ end
@@ -3,6 +3,10 @@
3
3
  module Rucoa
4
4
  module Nodes
5
5
  class StrNode < Base
6
+ # @return [String]
7
+ def value
8
+ children[0]
9
+ end
6
10
  end
7
11
  end
8
12
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Nodes
5
+ class SymNode < Base
6
+ # @return [String]
7
+ def value
8
+ children[0]
9
+ end
10
+ end
11
+ end
12
+ end
data/lib/rucoa/nodes.rb CHANGED
@@ -3,6 +3,16 @@
3
3
  module Rucoa
4
4
  module Nodes
5
5
  autoload :Base, 'rucoa/nodes/base'
6
+ autoload :ClassNode, 'rucoa/nodes/class_node'
7
+ autoload :CasgnNode, 'rucoa/nodes/casgn_node'
8
+ autoload :ConstNode, 'rucoa/nodes/const_node'
9
+ autoload :DefNode, 'rucoa/nodes/def_node'
10
+ autoload :DefsNode, 'rucoa/nodes/defs_node'
11
+ autoload :LvarNode, 'rucoa/nodes/lvar_node'
12
+ autoload :ModuleNode, 'rucoa/nodes/module_node'
13
+ autoload :SclassNode, 'rucoa/nodes/sclass_node'
14
+ autoload :SendNode, 'rucoa/nodes/send_node'
6
15
  autoload :StrNode, 'rucoa/nodes/str_node'
16
+ autoload :SymNode, 'rucoa/nodes/sym_node'
7
17
  end
8
18
  end
@@ -5,24 +5,34 @@ require 'parser/current'
5
5
  module Rucoa
6
6
  class ParserBuilder < ::Parser::Builders::Default
7
7
  NODE_CLASS_BY_TYPE = {
8
- str: Nodes::StrNode
8
+ casgn: Nodes::CasgnNode,
9
+ class: Nodes::ClassNode,
10
+ const: Nodes::ConstNode,
11
+ def: Nodes::DefNode,
12
+ defs: Nodes::DefsNode,
13
+ lvar: Nodes::LvarNode,
14
+ module: Nodes::ModuleNode,
15
+ sclass: Nodes::SclassNode,
16
+ send: Nodes::SendNode,
17
+ str: Nodes::StrNode,
18
+ sym: Nodes::SymNode
9
19
  }.freeze
10
20
 
21
+ class << self
22
+ # @param type [Symbol]
23
+ # @return [Class]
24
+ def node_class_for(type)
25
+ NODE_CLASS_BY_TYPE.fetch(type, Nodes::Base)
26
+ end
27
+ end
28
+
11
29
  # @note Override.
12
30
  def n(type, children, source_map)
13
- node_class_for(type).new(
31
+ self.class.node_class_for(type).new(
14
32
  type,
15
33
  children,
16
34
  location: source_map
17
35
  )
18
36
  end
19
-
20
- private
21
-
22
- # @param type [Symbol]
23
- # @return [Class]
24
- def node_class_for(type)
25
- NODE_CLASS_BY_TYPE.fetch(type, Nodes::Base)
26
- end
27
37
  end
28
38
  end