rucoa 0.8.0 → 0.9.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/data/definitions_ruby_3_1 +0 -0
  4. data/lib/rucoa/definition_store.rb +114 -54
  5. data/lib/rucoa/definitions/class_definition.rb +70 -4
  6. data/lib/rucoa/definitions/constant_definition.rb +7 -7
  7. data/lib/rucoa/definitions/method_definition.rb +4 -4
  8. data/lib/rucoa/handlers/initialized_handler.rb +33 -5
  9. data/lib/rucoa/handlers/text_document_completion_handler.rb +1 -1
  10. data/lib/rucoa/handlers/text_document_definition_handler.rb +7 -7
  11. data/lib/rucoa/handlers/text_document_did_open_handler.rb +1 -4
  12. data/lib/rucoa/node_concerns/{name_full_qualifiable.rb → name_fully_qualifiable.rb} +2 -2
  13. data/lib/rucoa/node_concerns.rb +1 -1
  14. data/lib/rucoa/node_inspector.rb +18 -17
  15. data/lib/rucoa/nodes/base.rb +30 -5
  16. data/lib/rucoa/nodes/casgn_node.rb +1 -1
  17. data/lib/rucoa/nodes/cbase_node.rb +8 -0
  18. data/lib/rucoa/nodes/class_node.rb +62 -1
  19. data/lib/rucoa/nodes/const_node.rb +46 -5
  20. data/lib/rucoa/nodes/def_node.rb +11 -9
  21. data/lib/rucoa/nodes/defs_node.rb +21 -0
  22. data/lib/rucoa/nodes/lvar_node.rb +2 -1
  23. data/lib/rucoa/nodes/module_node.rb +1 -1
  24. data/lib/rucoa/nodes/send_node.rb +14 -10
  25. data/lib/rucoa/nodes.rb +1 -0
  26. data/lib/rucoa/parse_result.rb +29 -0
  27. data/lib/rucoa/parser.rb +40 -8
  28. data/lib/rucoa/parser_builder.rb +1 -0
  29. data/lib/rucoa/rbs/class_definition_mapper.rb +4 -4
  30. data/lib/rucoa/rbs/constant_definition_mapper.rb +2 -2
  31. data/lib/rucoa/rbs/module_definition_mapper.rb +2 -2
  32. data/lib/rucoa/rubocop/autocorrector.rb +2 -2
  33. data/lib/rucoa/rubocop/investigator.rb +2 -2
  34. data/lib/rucoa/server.rb +1 -1
  35. data/lib/rucoa/source.rb +42 -27
  36. data/lib/rucoa/source_store.rb +0 -13
  37. data/lib/rucoa/version.rb +1 -1
  38. data/lib/rucoa/yard/definitions_loader.rb +308 -59
  39. data/lib/rucoa/yard/type.rb +46 -0
  40. data/lib/rucoa/yard.rb +1 -1
  41. data/lib/rucoa.rb +1 -0
  42. metadata +6 -4
  43. data/lib/rucoa/yard/method_definition_mapper.rb +0 -215
@@ -89,7 +89,7 @@ module Rucoa
89
89
  # @return [String]
90
90
  # @example returns namespace
91
91
  # node = Rucoa::Source.new(
92
- # content: <<~RUBY
92
+ # content: <<~RUBY,
93
93
  # module Foo
94
94
  # class Bar
95
95
  # def baz
@@ -97,6 +97,7 @@ module Rucoa
97
97
  # end
98
98
  # end
99
99
  # RUBY
100
+ # uri: 'file:///path/to/foo/bar.rb'
100
101
  # ).node_at(
101
102
  # Rucoa::Position.new(
102
103
  # column: 4,
@@ -105,14 +106,38 @@ module Rucoa
105
106
  # )
106
107
  # expect(node.namespace).to eq('Foo::Bar')
107
108
  # @example returns "Object" when the node is not in a namespace
108
- # node = Rucoa::Parser.call(
109
- # <<~RUBY
109
+ # node = Rucoa::Source.new(
110
+ # content: <<~RUBY,
110
111
  # foo
111
112
  # RUBY
112
- # )
113
+ # uri: 'file:///path/to/example.rb'
114
+ # ).root_node
113
115
  # expect(node.namespace).to eq('Object')
114
116
  def namespace
115
- each_ancestor(:module, :class).first&.full_qualified_name || 'Object'
117
+ module_nesting.first || 'Object'
118
+ end
119
+
120
+ # @return [Array<String>]
121
+ # @example return ["Bar::Foo", "Foo"] for class Foo::Bar::Baz
122
+ # node = Rucoa::Source.new(
123
+ # content: <<~RUBY,
124
+ # module Foo
125
+ # module Bar
126
+ # module Baz
127
+ # end
128
+ # end
129
+ # end
130
+ # RUBY
131
+ # uri: 'file:///path/to/foo/bar/baz.rb'
132
+ # ).node_at(
133
+ # Rucoa::Position.new(
134
+ # column: 4,
135
+ # line: 3
136
+ # )
137
+ # )
138
+ # expect(node.module_nesting).to eq(['Foo::Bar', 'Foo'])
139
+ def module_nesting
140
+ each_ancestor(:class, :module).map(&:fully_qualified_name)
116
141
  end
117
142
 
118
143
  protected
@@ -3,7 +3,7 @@
3
3
  module Rucoa
4
4
  module Nodes
5
5
  class CasgnNode < Base
6
- include NodeConcerns::NameFullQualifiable
6
+ include NodeConcerns::NameFullyQualifiable
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,68 @@
2
2
 
3
3
  module Rucoa
4
4
  module Nodes
5
- class ClassNode < ModuleNode
5
+ class ClassNode < Base
6
+ include NodeConcerns::NameFullyQualifiable
7
+
8
+ # @return [String]
9
+ def name
10
+ const_node.name
11
+ end
12
+
13
+ # @return [String, nil]
14
+ # @example returns nil for class for `class Foo`
15
+ # node = Rucoa::Source.new(
16
+ # content: <<~RUBY,
17
+ # class Foo
18
+ # end
19
+ # RUBY
20
+ # uri: 'file:///path/to/foo.rb'
21
+ # ).root_node
22
+ # expect(node.super_class_chained_name).to be_nil
23
+ # @example returns "Bar" for class for `class Foo < Bar`
24
+ # node = Rucoa::Source.new(
25
+ # content: <<~RUBY,
26
+ # class Foo < Bar
27
+ # end
28
+ # RUBY
29
+ # uri: 'file:///path/to/foo.rb'
30
+ # ).root_node
31
+ # expect(node.super_class_chained_name).to eq('Bar')
32
+ # @example returns "Bar::Baz" for class for `class Foo < Bar::Baz`
33
+ # node = Rucoa::Source.new(
34
+ # content: <<~RUBY,
35
+ # class Foo < Bar::Baz
36
+ # end
37
+ # RUBY
38
+ # uri: 'file:///path/to/foo.rb'
39
+ # ).root_node
40
+ # expect(node.super_class_chained_name).to eq('Bar::Baz')
41
+ # @example returns "::Bar" for class for `class Foo < ::Bar`
42
+ # node = Rucoa::Source.new(
43
+ # content: <<~RUBY,
44
+ # class Foo < ::Bar
45
+ # end
46
+ # RUBY
47
+ # uri: 'file:///path/to/foo.rb'
48
+ # ).root_node
49
+ # expect(node.super_class_chained_name).to eq('::Bar')
50
+ def super_class_chained_name
51
+ return unless super_class_node.is_a?(Nodes::ConstNode)
52
+
53
+ super_class_node.chained_name
54
+ end
55
+
56
+ private
57
+
58
+ # @return [Rucoa::Nodes::ConstNode]
59
+ def const_node
60
+ children[0]
61
+ end
62
+
63
+ # @return [Rucoa::Nodes::Base, nil]
64
+ def super_class_node
65
+ children[1]
66
+ end
6
67
  end
7
68
  end
8
69
  end
@@ -5,10 +5,16 @@ 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::Parser.call(
9
+ # path: '/path/to/example.rb',
10
+ # text: 'A'
11
+ # ).root_node
9
12
  # expect(node.name).to eq('A')
10
13
  # @example returns "B" for "A::B"
11
- # node = Rucoa::Parser.call('A::B')
14
+ # node = Rucoa::Parser.call(
15
+ # path: '/path/to/example.rb',
16
+ # text: 'A::B'
17
+ # ).root_node
12
18
  # expect(node.name).to eq('B')
13
19
  def name
14
20
  children[1].to_s
@@ -16,13 +22,25 @@ module Rucoa
16
22
 
17
23
  # @return [String]
18
24
  # @example returns "A" for "A"
19
- # node = Rucoa::Parser.call('A')
25
+ # node = Rucoa::Parser.call(
26
+ # path: '/path/to/example.rb',
27
+ # text: 'A'
28
+ # ).root_node
20
29
  # expect(node.chained_name).to eq('A')
21
30
  # @example returns "A::B" for "A::B"
22
- # node = Rucoa::Parser.call('A::B')
31
+ # node = Rucoa::Parser.call(
32
+ # path: '/path/to/example.rb',
33
+ # text: 'A::B'
34
+ # ).root_node
23
35
  # expect(node.chained_name).to eq('A::B')
24
36
  def chained_name
25
- if receiver.is_a?(ConstNode)
37
+ case receiver
38
+ when Nodes::CbaseNode
39
+ [
40
+ '',
41
+ name
42
+ ].join('::')
43
+ when Nodes::ConstNode
26
44
  [
27
45
  receiver.chained_name,
28
46
  name
@@ -32,6 +50,29 @@ module Rucoa
32
50
  end
33
51
  end
34
52
 
53
+ # @return [Array<String>]
54
+ # @example return ["Bar::Foo", "Foo"] for class Foo::Bar::Baz
55
+ # node = Rucoa::Source.new(
56
+ # content: <<~RUBY,
57
+ # module Foo
58
+ # module Bar
59
+ # module Baz
60
+ # end
61
+ # end
62
+ # end
63
+ # RUBY
64
+ # uri: 'file:///path/to/foo/bar/baz.rb'
65
+ # ).node_at(
66
+ # Rucoa::Position.new(
67
+ # column: 4,
68
+ # line: 3
69
+ # )
70
+ # )
71
+ # expect(node.module_nesting).to eq(['Foo::Bar', 'Foo'])
72
+ def module_nesting
73
+ each_ancestor(:class, :module).map(&:fully_qualified_name)
74
+ end
75
+
35
76
  private
36
77
 
37
78
  # @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.fully_qualified_name).to eq('Foo::Bar#baz')
48
+ def fully_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 fully_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,7 @@
3
3
  module Rucoa
4
4
  module Nodes
5
5
  class ModuleNode < Base
6
- include NodeConcerns::NameFullQualifiable
6
+ include NodeConcerns::NameFullyQualifiable
7
7
 
8
8
  # @return [String]
9
9
  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,6 +3,7 @@
3
3
  module Rucoa
4
4
  module Nodes
5
5
  autoload :Base, 'rucoa/nodes/base'
6
+ autoload :CbaseNode, 'rucoa/nodes/cbase_node'
6
7
  autoload :ClassNode, 'rucoa/nodes/class_node'
7
8
  autoload :CasgnNode, 'rucoa/nodes/casgn_node'
8
9
  autoload :ConstNode, 'rucoa/nodes/const_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
8
+ # @param path [String]
9
9
  # @param text [String]
10
- # @return [Rucoa::Nodes::Base]
11
- def call(text)
12
- new(text).call
10
+ # @return [Rucoa::ParseResult]
11
+ # @example returns non-failed parse result for valid Ruby source
12
+ # result = Rucoa::Parser.call(
13
+ # path: '/path/to/foo.rb',
14
+ # text: 'foo'
15
+ # )
16
+ # expect(result).not_to be_failed
17
+ # @example returns failed parse result for invalid Ruby source
18
+ # result = Rucoa::Parser.call(
19
+ # path: '/path/to/foo.rb',
20
+ # text: 'foo('
21
+ # )
22
+ # expect(result).to be_failed
23
+ def call(
24
+ path:,
25
+ text:
26
+ )
27
+ new(
28
+ path: path,
29
+ text: text
30
+ ).call
13
31
  end
14
32
  end
15
33
 
34
+ # @param path [String]
16
35
  # @param text [String]
17
- def initialize(text)
36
+ def initialize(
37
+ path:,
38
+ text:
39
+ )
40
+ @path = path
18
41
  @text = text
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
+ @path,
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
@@ -6,6 +6,7 @@ module Rucoa
6
6
  class ParserBuilder < ::Parser::Builders::Default
7
7
  NODE_CLASS_BY_TYPE = {
8
8
  casgn: Nodes::CasgnNode,
9
+ cbase: Nodes::CbaseNode,
9
10
  class: Nodes::ClassNode,
10
11
  const: Nodes::ConstNode,
11
12
  def: Nodes::DefNode,
@@ -19,16 +19,16 @@ module Rucoa
19
19
  # @return [Rucoa::Definitions::ClassDefinition]
20
20
  def call
21
21
  Definitions::ClassDefinition.new(
22
- full_qualified_name: full_qualified_name,
22
+ fully_qualified_name: fully_qualified_name,
23
23
  source_path: source_path,
24
- super_class_name: super_class_name
24
+ super_class_fully_qualified_name: super_class_fully_qualified_name
25
25
  )
26
26
  end
27
27
 
28
28
  private
29
29
 
30
30
  # @return [String]
31
- def full_qualified_name
31
+ def fully_qualified_name
32
32
  @declaration.name.to_s.delete_prefix('::')
33
33
  end
34
34
 
@@ -38,7 +38,7 @@ module Rucoa
38
38
  end
39
39
 
40
40
  # @return [String, nil]
41
- def super_class_name
41
+ def super_class_fully_qualified_name
42
42
  @declaration.super_class&.name&.to_s&.delete_prefix('::')
43
43
  end
44
44
  end
@@ -19,7 +19,7 @@ module Rucoa
19
19
  # @return [Rucoa::Definitions::ConstantDefinition]
20
20
  def call
21
21
  Definitions::ConstantDefinition.new(
22
- full_qualified_name: full_qualified_name,
22
+ fully_qualified_name: fully_qualified_name,
23
23
  source_path: source_path
24
24
  )
25
25
  end
@@ -27,7 +27,7 @@ module Rucoa
27
27
  private
28
28
 
29
29
  # @return [String]
30
- def full_qualified_name
30
+ def fully_qualified_name
31
31
  @declaration.name.to_s.delete_prefix('::')
32
32
  end
33
33
 
@@ -19,7 +19,7 @@ module Rucoa
19
19
  # @return [Rucoa::Definitions::ModuleDefinition]
20
20
  def call
21
21
  Definitions::ModuleDefinition.new(
22
- full_qualified_name: full_qualified_name,
22
+ fully_qualified_name: fully_qualified_name,
23
23
  source_path: source_path
24
24
  )
25
25
  end
@@ -27,7 +27,7 @@ module Rucoa
27
27
  private
28
28
 
29
29
  # @return [String]
30
- def full_qualified_name
30
+ def fully_qualified_name
31
31
  @declaration.name.to_s.delete_prefix('::')
32
32
  end
33
33
 
@@ -40,10 +40,10 @@ module Rucoa
40
40
 
41
41
  # @return [String]
42
42
  def path
43
- if @source.untitled? || @source.path.nil?
43
+ if @source.untitled?
44
44
  'untitled'
45
45
  else
46
- @source.path
46
+ @source.name
47
47
  end
48
48
  end
49
49
  end
@@ -48,10 +48,10 @@ module Rucoa
48
48
 
49
49
  # @return [String]
50
50
  def path
51
- if @source.untitled? || @source.path.nil?
51
+ if @source.untitled?
52
52
  'untitled'
53
53
  else
54
- @source.path
54
+ @source.name
55
55
  end
56
56
  end
57
57
  end
data/lib/rucoa/server.rb CHANGED
@@ -59,7 +59,7 @@ module Rucoa
59
59
  @source_store = SourceStore.new
60
60
 
61
61
  @definition_store = DefinitionStore.new
62
- @definition_store.definitions += DefinitionArchiver.load
62
+ @definition_store.bulk_add(DefinitionArchiver.load)
63
63
  end
64
64
 
65
65
  # @return [void]