rucoa 0.7.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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/README.md +4 -1
  4. data/data/definitions_ruby_3_1 +0 -0
  5. data/lib/rucoa/configuration.rb +10 -0
  6. data/lib/rucoa/definition_store.rb +194 -27
  7. data/lib/rucoa/definitions/class_definition.rb +82 -0
  8. data/lib/rucoa/definitions/constant_definition.rb +7 -7
  9. data/lib/rucoa/definitions/method_definition.rb +18 -6
  10. data/lib/rucoa/definitions/module_definition.rb +8 -0
  11. data/lib/rucoa/definitions.rb +2 -0
  12. data/lib/rucoa/handlers/initialize_handler.rb +1 -0
  13. data/lib/rucoa/handlers/initialized_handler.rb +33 -5
  14. data/lib/rucoa/handlers/text_document_completion_handler.rb +2 -2
  15. data/lib/rucoa/handlers/text_document_definition_handler.rb +155 -0
  16. data/lib/rucoa/handlers/text_document_did_open_handler.rb +1 -4
  17. data/lib/rucoa/handlers/text_document_hover_handler.rb +3 -4
  18. data/lib/rucoa/handlers.rb +1 -0
  19. data/lib/rucoa/node_concerns/{name_full_qualifiable.rb → name_fully_qualifiable.rb} +2 -2
  20. data/lib/rucoa/node_concerns.rb +1 -1
  21. data/lib/rucoa/node_inspector.rb +56 -34
  22. data/lib/rucoa/nodes/base.rb +32 -7
  23. data/lib/rucoa/nodes/casgn_node.rb +1 -1
  24. data/lib/rucoa/nodes/cbase_node.rb +8 -0
  25. data/lib/rucoa/nodes/class_node.rb +62 -1
  26. data/lib/rucoa/nodes/const_node.rb +72 -0
  27. data/lib/rucoa/nodes/def_node.rb +11 -9
  28. data/lib/rucoa/nodes/defs_node.rb +21 -0
  29. data/lib/rucoa/nodes/lvar_node.rb +2 -1
  30. data/lib/rucoa/nodes/module_node.rb +1 -1
  31. data/lib/rucoa/nodes/send_node.rb +14 -10
  32. data/lib/rucoa/nodes.rb +1 -0
  33. data/lib/rucoa/parse_result.rb +29 -0
  34. data/lib/rucoa/parser.rb +40 -8
  35. data/lib/rucoa/parser_builder.rb +1 -0
  36. data/lib/rucoa/position.rb +1 -1
  37. data/lib/rucoa/rbs/class_definition_mapper.rb +46 -0
  38. data/lib/rucoa/rbs/constant_definition_mapper.rb +2 -2
  39. data/lib/rucoa/rbs/module_definition_mapper.rb +40 -0
  40. data/lib/rucoa/rbs/ruby_definitions_loader.rb +11 -7
  41. data/lib/rucoa/rbs.rb +2 -0
  42. data/lib/rucoa/rubocop/autocorrector.rb +12 -1
  43. data/lib/rucoa/rubocop/investigator.rb +10 -1
  44. data/lib/rucoa/server.rb +2 -1
  45. data/lib/rucoa/source.rb +47 -24
  46. data/lib/rucoa/source_store.rb +0 -13
  47. data/lib/rucoa/version.rb +1 -1
  48. data/lib/rucoa/yard/definitions_loader.rb +320 -48
  49. data/lib/rucoa/yard/type.rb +46 -0
  50. data/lib/rucoa/yard.rb +1 -1
  51. data/lib/rucoa.rb +1 -0
  52. metadata +11 -4
  53. data/lib/rucoa/yard/definition_mapper.rb +0 -217
@@ -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,
@@ -39,7 +39,7 @@ module Rucoa
39
39
 
40
40
  # @param column [Integer] 0-origin column number
41
41
  # @param line [Integer] 1-origin line number
42
- def initialize(column:, line:)
42
+ def initialize(column: 0, line: 1)
43
43
  @column = column
44
44
  @line = line
45
45
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Rbs
5
+ class ClassDefinitionMapper
6
+ class << self
7
+ # @param declaration [RBS::AST::Declarations::Class]
8
+ # @return [Rucoa::Definitions::ClassDefinition]
9
+ def call(declaration:)
10
+ new(declaration: declaration).call
11
+ end
12
+ end
13
+
14
+ # @param declaration [RBS::AST::Declarations::Class]
15
+ def initialize(declaration:)
16
+ @declaration = declaration
17
+ end
18
+
19
+ # @return [Rucoa::Definitions::ClassDefinition]
20
+ def call
21
+ Definitions::ClassDefinition.new(
22
+ fully_qualified_name: fully_qualified_name,
23
+ source_path: source_path,
24
+ super_class_fully_qualified_name: super_class_fully_qualified_name
25
+ )
26
+ end
27
+
28
+ private
29
+
30
+ # @return [String]
31
+ def fully_qualified_name
32
+ @declaration.name.to_s.delete_prefix('::')
33
+ end
34
+
35
+ # @return [String]
36
+ def source_path
37
+ @declaration.location.name
38
+ end
39
+
40
+ # @return [String, nil]
41
+ def super_class_fully_qualified_name
42
+ @declaration.super_class&.name&.to_s&.delete_prefix('::')
43
+ end
44
+ end
45
+ end
46
+ 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
 
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Rbs
5
+ class ModuleDefinitionMapper
6
+ class << self
7
+ # @param declaration [RBS::AST::Declarations::Module]
8
+ # @return [Rucoa::Definitions::ModuleDefinition]
9
+ def call(declaration:)
10
+ new(declaration: declaration).call
11
+ end
12
+ end
13
+
14
+ # @param declaration [RBS::AST::Declarations::Module]
15
+ def initialize(declaration:)
16
+ @declaration = declaration
17
+ end
18
+
19
+ # @return [Rucoa::Definitions::ModuleDefinition]
20
+ def call
21
+ Definitions::ModuleDefinition.new(
22
+ fully_qualified_name: fully_qualified_name,
23
+ source_path: source_path
24
+ )
25
+ end
26
+
27
+ private
28
+
29
+ # @return [String]
30
+ def fully_qualified_name
31
+ @declaration.name.to_s.delete_prefix('::')
32
+ end
33
+
34
+ # @return [String]
35
+ def source_path
36
+ @declaration.location.name
37
+ end
38
+ end
39
+ end
40
+ end
@@ -18,13 +18,17 @@ module Rucoa
18
18
  declarations.flat_map do |declaration|
19
19
  case declaration
20
20
  when ::RBS::AST::Declarations::Constant
21
- [
22
- ConstantDefinitionMapper.call(declaration: declaration)
23
- ]
24
- when ::RBS::AST::Declarations::Class, ::RBS::AST::Declarations::Module
25
- [
26
- ConstantDefinitionMapper.call(declaration: declaration)
27
- ] +
21
+ [ConstantDefinitionMapper.call(declaration: declaration)]
22
+ when ::RBS::AST::Declarations::Class
23
+ [ClassDefinitionMapper.call(declaration: declaration)] +
24
+ declaration.members.grep(::RBS::AST::Members::MethodDefinition).map do |method_definition|
25
+ MethodDefinitionMapper.call(
26
+ declaration: declaration,
27
+ method_definition: method_definition
28
+ )
29
+ end
30
+ when ::RBS::AST::Declarations::Module
31
+ [ModuleDefinitionMapper.call(declaration: declaration)] +
28
32
  declaration.members.grep(::RBS::AST::Members::MethodDefinition).map do |method_definition|
29
33
  MethodDefinitionMapper.call(
30
34
  declaration: declaration,
data/lib/rucoa/rbs.rb CHANGED
@@ -2,8 +2,10 @@
2
2
 
3
3
  module Rucoa
4
4
  module Rbs
5
+ autoload :ClassDefinitionMapper, 'rucoa/rbs/class_definition_mapper'
5
6
  autoload :ConstantDefinitionMapper, 'rucoa/rbs/constant_definition_mapper'
6
7
  autoload :MethodDefinitionMapper, 'rucoa/rbs/method_definition_mapper'
8
+ autoload :ModuleDefinitionMapper, 'rucoa/rbs/module_definition_mapper'
7
9
  autoload :RubyDefinitionsLoader, 'rucoa/rbs/ruby_definitions_loader'
8
10
  end
9
11
  end
@@ -32,9 +32,20 @@ module Rucoa
32
32
  # @return [String]
33
33
  def call
34
34
  @options[:stdin] = @source.content
35
- run([@source.path || 'untitled'])
35
+ run([path])
36
36
  @options[:stdin]
37
37
  end
38
+
39
+ private
40
+
41
+ # @return [String]
42
+ def path
43
+ if @source.untitled?
44
+ 'untitled'
45
+ else
46
+ @source.name
47
+ end
48
+ end
38
49
  end
39
50
  end
40
51
  end
@@ -32,7 +32,7 @@ module Rucoa
32
32
  # @return [Array<RuboCop::Cop::Offense>]
33
33
  def call
34
34
  @options[:stdin] = @source.content
35
- run([@source.path || 'untitled'])
35
+ run([path])
36
36
  @offenses
37
37
  end
38
38
 
@@ -45,6 +45,15 @@ module Rucoa
45
45
  @offenses = offenses
46
46
  super
47
47
  end
48
+
49
+ # @return [String]
50
+ def path
51
+ if @source.untitled?
52
+ 'untitled'
53
+ else
54
+ @source.name
55
+ end
56
+ end
48
57
  end
49
58
  end
50
59
  end
data/lib/rucoa/server.rb CHANGED
@@ -13,6 +13,7 @@ module Rucoa
13
13
  'shutdown' => Handlers::ShutdownHandler,
14
14
  'textDocument/codeAction' => Handlers::TextDocumentCodeActionHandler,
15
15
  'textDocument/completion' => Handlers::TextDocumentCompletionHandler,
16
+ 'textDocument/definition' => Handlers::TextDocumentDefinitionHandler,
16
17
  'textDocument/didChange' => Handlers::TextDocumentDidChangeHandler,
17
18
  'textDocument/didClose' => Handlers::TextDocumentDidCloseHandler,
18
19
  'textDocument/didOpen' => Handlers::TextDocumentDidOpenHandler,
@@ -58,7 +59,7 @@ module Rucoa
58
59
  @source_store = SourceStore.new
59
60
 
60
61
  @definition_store = DefinitionStore.new
61
- @definition_store.definitions += DefinitionArchiver.load
62
+ @definition_store.bulk_add(DefinitionArchiver.load)
62
63
  end
63
64
 
64
65
  # @return [void]
data/lib/rucoa/source.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'uri'
4
+
3
5
  module Rucoa
4
6
  class Source
5
7
  # @return [String]
@@ -9,8 +11,8 @@ module Rucoa
9
11
  attr_reader :uri
10
12
 
11
13
  # @param content [String]
12
- # @param uri [String, nil]
13
- def initialize(content:, uri: nil)
14
+ # @param uri [String]
15
+ def initialize(content:, uri:)
14
16
  @content = content
15
17
  @uri = uri
16
18
  end
@@ -29,36 +31,41 @@ module Rucoa
29
31
  # )
30
32
  # expect(source.definitions).to match(
31
33
  # [
34
+ # a_kind_of(Rucoa::Definitions::ClassDefinition),
32
35
  # a_kind_of(Rucoa::Definitions::MethodDefinition)
33
36
  # ]
34
37
  # )
35
38
  def definitions
36
- @definitions ||= Yard::DefinitionsLoader.load_string(
37
- content: @content,
38
- path: path
39
- )
39
+ @definitions ||=
40
+ if parse_result.failed?
41
+ []
42
+ else
43
+ Yard::DefinitionsLoader.call(
44
+ associations: parse_result.associations,
45
+ root_node: parse_result.root_node
46
+ )
47
+ end
40
48
  end
41
49
 
42
50
  # @return [String, nil]
43
- # @example returns path from given VSCode URI
51
+ # @example returns path for file URI
44
52
  # source = Rucoa::Source.new(
45
53
  # content: '',
46
54
  # uri: 'file:///path/to/foo.rb'
47
55
  # )
48
- # expect(source.path).to eq('/path/to/foo.rb')
49
- # @example returns nil for untitled URI
56
+ # expect(source.name).to eq('/path/to/foo.rb')
57
+ # @example returns opaque for untitled URI
50
58
  # source = Rucoa::Source.new(
51
59
  # content: '',
52
60
  # uri: 'untitled:Untitled-1'
53
61
  # )
54
- # expect(source.path).to be_nil
55
- def path
56
- return unless @uri
57
-
58
- path = ::URI.parse(@uri).path
59
- return unless path
60
-
61
- ::CGI.unescape(path)
62
+ # expect(source.name).to eq('Untitled-1')
63
+ def name
64
+ if untitled?
65
+ uri_object.opaque
66
+ else
67
+ uri_object.path
68
+ end
62
69
  end
63
70
 
64
71
  # @param position [Rucoa::Position]
@@ -67,29 +74,45 @@ module Rucoa
67
74
  root_and_descendant_nodes.reverse.find do |node|
68
75
  node.include_position?(position)
69
76
  end
70
- rescue ::Parser::SyntaxError
71
- nil
72
77
  end
73
78
 
74
79
  # @return [Rucoa::Nodes::Base, nil]
75
80
  def root_node
76
- @root_node ||= Parser.call(@content)
77
- rescue ::Parser::SyntaxError
78
- nil
81
+ parse_result.root_node
82
+ end
83
+
84
+ # @return [Boolean]
85
+ def failed_to_parse?
86
+ parse_result.failed?
79
87
  end
80
88
 
81
89
  # @return [Boolean]
82
- def syntax_error?
83
- root_node.nil?
90
+ def untitled?
91
+ uri_object.scheme == 'untitled'
84
92
  end
85
93
 
86
94
  private
87
95
 
96
+ # @return [Rucoa::ParseResult]
97
+ def parse_result
98
+ return @parse_result if instance_variable_defined?(:@parse_result)
99
+
100
+ @parse_result = Parser.call(
101
+ path: name,
102
+ text: @content
103
+ )
104
+ end
105
+
88
106
  # @return [Array<Rucoa::Nodes::Base>]
89
107
  def root_and_descendant_nodes
90
108
  return [] unless root_node
91
109
 
92
110
  [root_node, *root_node.descendants]
93
111
  end
112
+
113
+ # @return [URI]
114
+ def uri_object
115
+ @uri_object ||= ::URI.parse(@uri)
116
+ end
94
117
  end
95
118
  end
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'cgi'
4
- require 'uri'
5
-
6
3
  module Rucoa
7
4
  class SourceStore
8
5
  def initialize
@@ -26,15 +23,5 @@ module Rucoa
26
23
  def each_uri(&block)
27
24
  @data.each_key(&block)
28
25
  end
29
-
30
- private
31
-
32
- # @param uri [String]
33
- # @return [String]
34
- def path_from_uri(uri)
35
- ::CGI.unescape(
36
- ::URI.parse(uri).path || 'untitled'
37
- )
38
- end
39
26
  end
40
27
  end
data/lib/rucoa/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rucoa
4
- VERSION = '0.7.0'
4
+ VERSION = '0.9.0'
5
5
  end