rucoa 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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]