rucoa 0.7.0 → 0.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48f65475f89a895b8fa3429ddcb5a10738c78e2181a0a0f41a8fe14ec8a2fd17
4
- data.tar.gz: b94bffab0ae35cf83ee0ae5146f41b583abcea4b4192eabcdc8acbcedf3418b4
3
+ metadata.gz: 26c4a8d526acb34d3722fc4b78e4c8fa0a1bac7bfc11d72af4b5962afdbc01c9
4
+ data.tar.gz: b49e5bdca272898e10870fbad7f99b2ba5cf63e0d1acef074d3af1928b69058b
5
5
  SHA512:
6
- metadata.gz: 1693536178207fc2740ede4806a8803f140477b312c143e7ad0284875c1685244a09fa33dcd6208e993f4d0acb87284391a605a27e09d8479c71f556cf951d7f
7
- data.tar.gz: d4458ea8223b032e5df2680bfa4f8ed2f939f2971a19db327306a3c6fb6a9a6b3810ffd8ed56346b6e7defb36b916e9054bcb71d031f2494c30b0d6f216995eb
6
+ metadata.gz: 4de0736efd9d7b44d7756bfe5aedf6fc60d0b3a9ce9ab55cb0e3c6b83ca0525017e0ddb629d612ec4c05a8cae97f1d902bf33a3090c00570765c344821393bf6
7
+ data.tar.gz: 8228a81e3b4408274d2e51f2c339e724e4c4615f4af2a311bad68c48b74d32a09773cf5a0caaa780135780d7510b95aa64579ec6d009bf7fe711cb590660bde8
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rucoa (0.7.0)
4
+ rucoa (0.8.0)
5
5
  parser
6
6
  rbs
7
7
  rubocop
data/README.md CHANGED
@@ -62,6 +62,10 @@ This extension supports the folowiing types of symbols:
62
62
 
63
63
  Provides completion items for constant names and method names.
64
64
 
65
+ ### Definition
66
+
67
+ Provides "Go to Definition" command to jump to the definition.
68
+
65
69
  ### Hover
66
70
 
67
71
  Shows documentation for the symbol under the cursor.
@@ -72,6 +76,5 @@ Shows method signature help when you start to type method arguments like `"100".
72
76
 
73
77
  ## Coming soon
74
78
 
75
- - Go to Definition
76
79
  - Highlight
77
80
  - Semantic Tokens
Binary file
@@ -16,6 +16,11 @@ module Rucoa
16
16
  disable_feature('completion')
17
17
  end
18
18
 
19
+ # @return [void]
20
+ def disable_definition
21
+ disable_feature('definition')
22
+ end
23
+
19
24
  # @return [void]
20
25
  def disable_diagnostics
21
26
  disable_feature('diagnostics')
@@ -82,6 +87,11 @@ module Rucoa
82
87
  enables_feature?('completion')
83
88
  end
84
89
 
90
+ # @return [Boolean]
91
+ def enables_definition?
92
+ enables_feature?('definition')
93
+ end
94
+
85
95
  # @return [Boolean]
86
96
  def enables_diagnostics?
87
97
  enables_feature?('diagnostics')
@@ -24,14 +24,86 @@ module Rucoa
24
24
  end
25
25
  end
26
26
 
27
+ # @param method_name [String]
27
28
  # @param namespace [String]
29
+ # @param singleton [Boolean]
30
+ # @return [Rucoa::Definition::MethodDefinition, nil]
31
+ # @example has the ability to find `IO.write` from `File.write`
32
+ # definition_store = Rucoa::DefinitionStore.new
33
+ # definition_store.definitions += Rucoa::DefinitionArchiver.load
34
+ # subject = definition_store.find_method_definition_by(
35
+ # method_name: 'write',
36
+ # namespace: 'File',
37
+ # singleton: true
38
+ # )
39
+ # expect(subject.full_qualified_name).to eq('IO.write')
40
+ def find_method_definition_by(method_name:, namespace:, singleton: false)
41
+ if singleton
42
+ singleton_method_definitions_of(namespace)
43
+ else
44
+ instance_method_definitions_of(namespace)
45
+ end.find do |method_definition|
46
+ method_definition.method_name == method_name
47
+ end
48
+ end
49
+
50
+ # @param type [String]
51
+ # @return [Array<Rucoa::Definitions::MethodDefinition>]
52
+ # @example includes ancestors' methods
53
+ # definition_store = Rucoa::DefinitionStore.new
54
+ # definition_store.definitions += Rucoa::DefinitionArchiver.load
55
+ # subject = definition_store.instance_method_definitions_of('File')
56
+ # expect(subject.map(&:full_qualified_name)).to include('IO#raw')
57
+ # @example responds to `singleton<File>`
58
+ # definition_store = Rucoa::DefinitionStore.new
59
+ # definition_store.definitions += Rucoa::DefinitionArchiver.load
60
+ # subject = definition_store.instance_method_definitions_of('singleton<File>')
61
+ # expect(subject.map(&:full_qualified_name)).to include('IO.write')
62
+ def instance_method_definitions_of(type)
63
+ singleton_class_name = singleton_class_name_from(type)
64
+ return singleton_method_definitions_of(singleton_class_name) if singleton_class_name
65
+
66
+ class_or_module_definition = find_class_or_module_definition(type)
67
+ return [] unless class_or_module_definition
68
+
69
+ definitions = instance_method_definitions
70
+ [
71
+ class_or_module_definition,
72
+ *ancestor_definitions_of(class_or_module_definition)
73
+ ].map(&:full_qualified_name).flat_map do |full_qualified_type_name|
74
+ definitions.select do |definition|
75
+ definition.namespace == full_qualified_type_name
76
+ end
77
+ end
78
+ end
79
+
80
+ # @param type [String]
28
81
  # @return [Array<Rucoa::Definitions::MethodDefinition>]
29
- def method_definitions_of(namespace)
30
- method_definitions.select do |method_definition|
31
- method_definition.namespace == namespace
82
+ # @example returns singleton method definitions of File
83
+ # definition_store = Rucoa::DefinitionStore.new
84
+ # definition_store.definitions += Rucoa::DefinitionArchiver.load
85
+ # subject = definition_store.singleton_method_definitions_of('File')
86
+ # expect(subject.map(&:full_qualified_name)).to include('IO.write')
87
+ def singleton_method_definitions_of(type)
88
+ class_or_module_definition = find_class_or_module_definition(type)
89
+ return [] unless class_or_module_definition
90
+
91
+ definitions = singleton_method_definitions
92
+ [
93
+ class_or_module_definition,
94
+ *ancestor_definitions_of(class_or_module_definition)
95
+ ].map(&:full_qualified_name).flat_map do |full_qualified_type_name|
96
+ definitions.select do |definition|
97
+ definition.namespace == full_qualified_type_name
98
+ end
32
99
  end
33
100
  end
34
101
 
102
+ # @return [Array<Rucoa::Definition::ConstantDefinition>]
103
+ def constant_definitions
104
+ @definitions.grep(Definitions::ConstantDefinition)
105
+ end
106
+
35
107
  # @param namespace [String]
36
108
  # @return [Array<Rucoa::Definitions::ConstantDefinition>] e.g. File::Separator, File::SEPARATOR, etc.
37
109
  def constant_definitions_under(namespace)
@@ -42,9 +114,34 @@ module Rucoa
42
114
 
43
115
  private
44
116
 
45
- # @return [Array<Rucoa::Definition::ConstantDefinition>]
46
- def constant_definitions
47
- @definitions.grep(Definitions::ConstantDefinition)
117
+ # @param type [String]
118
+ # @return [String, nil]
119
+ def singleton_class_name_from(type)
120
+ type[/singleton<(\w+)>/, 1]
121
+ end
122
+
123
+ # @param class_or_module_definition [Rucoa::Definitions::Class, Rucoa::Definitions::Module]
124
+ # @return [Array<Rucoa::Definitions::Class>]
125
+ def ancestor_definitions_of(class_or_module_definition)
126
+ return [] unless class_or_module_definition.is_a?(Definitions::ClassDefinition)
127
+
128
+ result = []
129
+ class_definition = class_or_module_definition
130
+ while (super_class_name = class_definition.super_class_name)
131
+ class_definition = find_class_or_module_definition(super_class_name)
132
+ break unless class_definition
133
+
134
+ result << class_definition
135
+ end
136
+ result
137
+ end
138
+
139
+ # @param type [String]
140
+ # @return [Rucoa::Definitions::Class, Rucoa::Definitions::Module, nil]
141
+ def find_class_or_module_definition(type)
142
+ @definitions.find do |definition|
143
+ definition.full_qualified_name == type
144
+ end
48
145
  end
49
146
 
50
147
  # @return [Array<Rucoa::Definition::MethodDefinition>]
@@ -52,6 +149,16 @@ module Rucoa
52
149
  @definitions.grep(Definitions::MethodDefinition)
53
150
  end
54
151
 
152
+ # @return [Array<Rucoa::Definition::MethodDefinition>]
153
+ def instance_method_definitions
154
+ method_definitions.select(&:instance_method?)
155
+ end
156
+
157
+ # @return [Array<Rucoa::Definition::MethodDefinition>]
158
+ def singleton_method_definitions
159
+ method_definitions.select(&:singleton_method?)
160
+ end
161
+
55
162
  # @param source_path [String]
56
163
  # @return [Array<Rucoa::Definition::Base>]
57
164
  def delete_definitions_defined_in(source_path)
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Definitions
5
+ class ClassDefinition < ModuleDefinition
6
+ # @return [String, nil]
7
+ attr_reader :super_class_name
8
+
9
+ # @param super_class_name [String, nil]
10
+ def initialize(super_class_name:, **keyword_arguments)
11
+ super(**keyword_arguments)
12
+ @super_class_name = super_class_name
13
+ end
14
+ end
15
+ end
16
+ end
@@ -6,6 +6,9 @@ module Rucoa
6
6
  # @return [String, nil]
7
7
  attr_reader :description
8
8
 
9
+ # @return [Symbol]
10
+ attr_reader :kind
11
+
9
12
  # @return [String]
10
13
  attr_reader :method_name
11
14
 
@@ -110,12 +113,21 @@ module Rucoa
110
113
  end
111
114
  end
112
115
 
116
+ # @return [Boolean]
117
+ def instance_method?
118
+ @kind == :instance
119
+ end
120
+
121
+ # @return [Boolean]
122
+ def singleton_method?
123
+ !instance_method?
124
+ end
125
+
113
126
  private
114
127
 
115
128
  # @return [String]
116
129
  def method_kind_symbol
117
- case @kind
118
- when :instance
130
+ if instance_method?
119
131
  '#'
120
132
  else
121
133
  '.'
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Definitions
5
+ class ModuleDefinition < ConstantDefinition
6
+ end
7
+ end
8
+ end
@@ -3,7 +3,9 @@
3
3
  module Rucoa
4
4
  module Definitions
5
5
  autoload :Base, 'rucoa/definitions/base'
6
+ autoload :ClassDefinition, 'rucoa/definitions/class_definition'
6
7
  autoload :ConstantDefinition, 'rucoa/definitions/constant_definition'
7
8
  autoload :MethodDefinition, 'rucoa/definitions/method_definition'
9
+ autoload :ModuleDefinition, 'rucoa/definitions/module_definition'
8
10
  end
9
11
  end
@@ -13,6 +13,7 @@ module Rucoa
13
13
  .
14
14
  ]
15
15
  },
16
+ definitionProvider: true,
16
17
  documentFormattingProvider: true,
17
18
  documentRangeFormattingProvider: true,
18
19
  documentSymbolProvider: true,
@@ -148,7 +148,7 @@ module Rucoa
148
148
  # @return [Array<String>]
149
149
  def callable_method_definitions
150
150
  receiver_types.flat_map do |type|
151
- definition_store.method_definitions_of(type)
151
+ definition_store.instance_method_definitions_of(type)
152
152
  end
153
153
  end
154
154
 
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Handlers
5
+ class TextDocumentDefinitionHandler < Base
6
+ def call
7
+ respond(location)
8
+ end
9
+
10
+ private
11
+
12
+ # @return [Hash, nil]
13
+ def location
14
+ return unless reponsible?
15
+
16
+ {
17
+ range: location_range.to_vscode_range,
18
+ uri: location_uri
19
+ }
20
+ end
21
+
22
+ # @return [Boolean]
23
+ def reponsible?
24
+ configuration.enables_definition? &&
25
+ !location_uri.nil? &&
26
+ !location_source.nil?
27
+ end
28
+
29
+ # @return [Rucoa::Range]
30
+ def location_range
31
+ return Range.from_parser_range(location_node.location.expression) if location_node
32
+
33
+ Position.new.to_range
34
+ end
35
+
36
+ # @return [String, nil]
37
+ def location_uri
38
+ return unless definition
39
+
40
+ if definition.source_path.start_with?('Untitled-')
41
+ "untitled:#{definition.source_path}"
42
+ else
43
+ "file://#{definition.source_path}"
44
+ end
45
+ end
46
+
47
+ # @return [Rucoa::Nodes::Base, nil]
48
+ def location_node
49
+ @location_node ||=
50
+ case definition
51
+ when Definitions::ClassDefinition
52
+ find_class_node
53
+ when Definitions::ModuleDefinition
54
+ find_module_node
55
+ when Definitions::MethodDefinition
56
+ find_method_node
57
+ end
58
+ end
59
+
60
+ # @return [Rucoa::Source, nil]
61
+ def location_source
62
+ source_store.get(location_uri)
63
+ end
64
+
65
+ # @return [Rucoa::Position]
66
+ def position
67
+ Position.from_vscode_position(
68
+ request.dig('params', 'position')
69
+ )
70
+ end
71
+
72
+ # @return [String]
73
+ def uri
74
+ request.dig('params', 'textDocument', 'uri')
75
+ end
76
+
77
+ # @return [Rucoa::Source, nil]
78
+ def source
79
+ source_store.get(uri)
80
+ end
81
+
82
+ # @return [Rucoa::Nodes::Base]
83
+ def node
84
+ source&.node_at(position)
85
+ end
86
+
87
+ # @return [Rucoa::Definitions::Base, nil]
88
+ def definition
89
+ @definition ||= NodeInspector.new(
90
+ definition_store: definition_store,
91
+ node: node
92
+ ).definitions.first
93
+ end
94
+
95
+ # @return [Rucoa::Nodes::ClassNode, nil]
96
+ def find_class_node
97
+ find_by_full_qualified_name(
98
+ full_qualified_name: definition.full_qualified_name,
99
+ klass: Nodes::ClassNode
100
+ ) || find_by_name(
101
+ klass: Nodes::ClassNode,
102
+ name: definition.name
103
+ )
104
+ end
105
+
106
+ # @return [Rucoa::Nodes::ModuleNode, nil]
107
+ def find_module_node
108
+ find_by_full_qualified_name(
109
+ full_qualified_name: definition.full_qualified_name,
110
+ klass: Nodes::ModuleNode
111
+ ) || find_by_name(
112
+ klass: Nodes::ModuleNode,
113
+ name: definition.name
114
+ )
115
+ end
116
+
117
+ # @return [Rucoa::Nodes::MethodNode, nil]
118
+ def find_method_node
119
+ location_root_or_descendant_nodes.reverse.find do |node|
120
+ node.is_a?(Nodes::DefNode) &&
121
+ node.name == definition.method_name &&
122
+ node.namespace == definition.namespace
123
+ end
124
+ end
125
+
126
+ # @param full_qualified_name [String]
127
+ # @param klass [Class]
128
+ # @return [Rucoa::Nodes::Base, nil]
129
+ def find_by_full_qualified_name(full_qualified_name:, klass:)
130
+ location_root_or_descendant_nodes.reverse.find do |node|
131
+ node.is_a?(klass) &&
132
+ node.full_qualified_name == full_qualified_name
133
+ end
134
+ end
135
+
136
+ # @param name [String]
137
+ # @param klass [Class]
138
+ # @return [Rucoa::Nodes::Base, nil]
139
+ def find_by_name(klass:, name:)
140
+ location_root_or_descendant_nodes.reverse.find do |node|
141
+ node.is_a?(klass) &&
142
+ node.name == name
143
+ end
144
+ end
145
+
146
+ # @return [Array<Rucoa::Nodes::Base>]
147
+ def location_root_or_descendant_nodes
148
+ @location_root_or_descendant_nodes ||= [
149
+ location_source.root_node,
150
+ *location_source.root_node.descendants
151
+ ]
152
+ end
153
+ end
154
+ end
155
+ end
@@ -23,14 +23,13 @@ module Rucoa
23
23
  def responsible?
24
24
  configuration.enables_hover? &&
25
25
  !source.nil? &&
26
- !node.nil?
26
+ !node.nil? &&
27
+ !method_definitions.empty?
27
28
  end
28
29
 
29
30
  # @return [String, nil]
30
31
  def contents
31
32
  method_definition = method_definitions.first
32
- return unless method_definition
33
-
34
33
  [
35
34
  method_definition.signatures.join("\n"),
36
35
  method_definition.description
@@ -66,7 +65,7 @@ module Rucoa
66
65
 
67
66
  # @return [Array<Rucoa::Definitions::MethodDefinition>]
68
67
  def method_definitions
69
- NodeInspector.new(
68
+ @method_definitions ||= NodeInspector.new(
70
69
  definition_store: definition_store,
71
70
  node: node
72
71
  ).method_definitions
@@ -9,6 +9,7 @@ module Rucoa
9
9
  autoload :ShutdownHandler, 'rucoa/handlers/shutdown_handler'
10
10
  autoload :TextDocumentCodeActionHandler, 'rucoa/handlers/text_document_code_action_handler'
11
11
  autoload :TextDocumentCompletionHandler, 'rucoa/handlers/text_document_completion_handler'
12
+ autoload :TextDocumentDefinitionHandler, 'rucoa/handlers/text_document_definition_handler'
12
13
  autoload :TextDocumentDidChangeHandler, 'rucoa/handlers/text_document_did_change_handler'
13
14
  autoload :TextDocumentDidCloseHandler, 'rucoa/handlers/text_document_did_close_handler'
14
15
  autoload :TextDocumentDidOpenHandler, 'rucoa/handlers/text_document_did_open_handler'
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
4
-
5
3
  module Rucoa
6
4
  class NodeInspector
7
5
  # @param definition_store [Rucoa::DefinitionStore]
@@ -11,10 +9,24 @@ module Rucoa
11
9
  @node = node
12
10
  end
13
11
 
14
- # @return [Array<String>]
12
+ # @return [Array<Rucoa::Definitions::Base>]
13
+ def definitions
14
+ case @node
15
+ when Nodes::ConstNode
16
+ [constant_definition]
17
+ when Nodes::SendNode
18
+ method_definitions
19
+ else
20
+ []
21
+ end
22
+ end
23
+
24
+ # @return [Array<Rucoa::Definitions::MethodDefinition>]
15
25
  def method_definitions
16
- method_full_qualified_names.flat_map do |full_qualified_name|
17
- @definition_store.select_by_full_qualified_name(full_qualified_name)
26
+ method_receiver_types.flat_map do |type|
27
+ @definition_store.instance_method_definitions_of(type)
28
+ end.select do |method_definition|
29
+ method_definition.method_name == @node.name
18
30
  end
19
31
  end
20
32
 
@@ -36,7 +48,7 @@ module Rucoa
36
48
  def return_types
37
49
  case @node.type
38
50
  when :const
39
- [@node.name]
51
+ ["singleton<#{@node.name}>"]
40
52
  when :lvar
41
53
  return_types_for_lvar
42
54
  when :send
@@ -74,26 +86,31 @@ module Rucoa
74
86
 
75
87
  private
76
88
 
77
- # @return [Array<String>]
78
- def method_full_qualified_names
79
- method_receiver_types.map do |type|
89
+ # @return [Rucoa::Definitions::ConstantDefinition, nil]
90
+ def constant_definition
91
+ return unless @node.is_a?(Nodes::ConstNode)
92
+
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|
80
95
  [
81
- type,
82
- @node.name
83
- ].join(method_kind_symbol)
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
84
108
  end
85
109
  end
86
110
 
87
- # @return [String, nil]
88
- def method_kind_symbol
89
- return unless @node.is_a?(Nodes::SendNode)
90
-
91
- case @node.receiver
92
- when Nodes::ConstNode
93
- '.'
94
- else
95
- '#'
96
- end
111
+ # @return [Boolean]
112
+ def singleton_method_call?
113
+ @node.is_a?(Nodes::ConstNode)
97
114
  end
98
115
 
99
116
  # @return [String, nil]
@@ -115,9 +132,13 @@ module Rucoa
115
132
 
116
133
  # @return [Array<String>]
117
134
  def return_types_for_send
118
- method_full_qualified_names.flat_map do |full_qualified_name|
119
- @definition_store.select_by_full_qualified_name(full_qualified_name).flat_map(&:return_types)
120
- end.uniq
135
+ method_receiver_types.flat_map do |type|
136
+ @definition_store.find_method_definition_by(
137
+ method_name: @node.name,
138
+ namespace: type,
139
+ singleton: singleton_method_call?
140
+ )&.return_types
141
+ end.compact
121
142
  end
122
143
  end
123
144
  end
@@ -121,8 +121,8 @@ module Rucoa
121
121
  # @param types [Array<Symbol>]
122
122
  # @return [void]
123
123
  def visit_descendants(types, &block)
124
- each_child_node(*types) do |child|
125
- yield(child)
124
+ each_child_node do |child|
125
+ yield(child) if types.empty? || types.include?(child.type)
126
126
  child.visit_descendants(types, &block)
127
127
  end
128
128
  end
@@ -4,9 +4,40 @@ module Rucoa
4
4
  module Nodes
5
5
  class ConstNode < Base
6
6
  # @return [String]
7
+ # @example returns "A" for "A"
8
+ # node = Rucoa::Parser.call('A')
9
+ # expect(node.name).to eq('A')
10
+ # @example returns "B" for "A::B"
11
+ # node = Rucoa::Parser.call('A::B')
12
+ # expect(node.name).to eq('B')
7
13
  def name
8
14
  children[1].to_s
9
15
  end
16
+
17
+ # @return [String]
18
+ # @example returns "A" for "A"
19
+ # node = Rucoa::Parser.call('A')
20
+ # expect(node.chained_name).to eq('A')
21
+ # @example returns "A::B" for "A::B"
22
+ # node = Rucoa::Parser.call('A::B')
23
+ # expect(node.chained_name).to eq('A::B')
24
+ def chained_name
25
+ if receiver.is_a?(ConstNode)
26
+ [
27
+ receiver.chained_name,
28
+ name
29
+ ].join('::')
30
+ else
31
+ name
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ # @return [Rucoa::Nodes::Base, nil]
38
+ def receiver
39
+ children[0]
40
+ end
10
41
  end
11
42
  end
12
43
  end
@@ -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
+ full_qualified_name: full_qualified_name,
23
+ source_path: source_path,
24
+ super_class_name: super_class_name
25
+ )
26
+ end
27
+
28
+ private
29
+
30
+ # @return [String]
31
+ def full_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_name
42
+ @declaration.super_class&.name&.to_s&.delete_prefix('::')
43
+ end
44
+ end
45
+ end
46
+ end
@@ -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
+ full_qualified_name: full_qualified_name,
23
+ source_path: source_path
24
+ )
25
+ end
26
+
27
+ private
28
+
29
+ # @return [String]
30
+ def full_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? || @source.path.nil?
44
+ 'untitled'
45
+ else
46
+ @source.path
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? || @source.path.nil?
52
+ 'untitled'
53
+ else
54
+ @source.path
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,
data/lib/rucoa/source.rb CHANGED
@@ -29,6 +29,7 @@ module Rucoa
29
29
  # )
30
30
  # expect(source.definitions).to match(
31
31
  # [
32
+ # a_kind_of(Rucoa::Definitions::ClassDefinition),
32
33
  # a_kind_of(Rucoa::Definitions::MethodDefinition)
33
34
  # ]
34
35
  # )
@@ -46,15 +47,17 @@ module Rucoa
46
47
  # uri: 'file:///path/to/foo.rb'
47
48
  # )
48
49
  # expect(source.path).to eq('/path/to/foo.rb')
49
- # @example returns nil for untitled URI
50
+ # @example returns name for untitled URI
50
51
  # source = Rucoa::Source.new(
51
52
  # content: '',
52
53
  # uri: 'untitled:Untitled-1'
53
54
  # )
54
- # expect(source.path).to be_nil
55
+ # expect(source.path).to eq('Untitled-1')
55
56
  def path
56
57
  return unless @uri
57
58
 
59
+ return @uri.split(':', 2).last if untitled?
60
+
58
61
  path = ::URI.parse(@uri).path
59
62
  return unless path
60
63
 
@@ -83,6 +86,11 @@ module Rucoa
83
86
  root_node.nil?
84
87
  end
85
88
 
89
+ # @return [Boolean]
90
+ def untitled?
91
+ @uri&.start_with?('untitled:')
92
+ end
93
+
86
94
  private
87
95
 
88
96
  # @return [Array<Rucoa::Nodes::Base>]
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.8.0'
5
5
  end
@@ -24,35 +24,58 @@ module Rucoa
24
24
  # content: content,
25
25
  # path: '/path/to/foo.rb'
26
26
  # )
27
- # expect(definitions.size).to eq(1)
28
- # expect(definitions.first.full_qualified_name).to eq('Foo#foo')
29
- # expect(definitions.first.source_path).to eq('/path/to/foo.rb')
30
- # expect(definitions.first.description).to eq('Return given argument as an Integer.')
31
- # expect(definitions.first.return_types).to eq(%w[Integer])
27
+ # expect(definitions.size).to eq(2)
28
+ # expect(definitions[1].full_qualified_name).to eq('Foo#foo')
29
+ # expect(definitions[1].source_path).to eq('/path/to/foo.rb')
30
+ # expect(definitions[1].description).to eq('Return given argument as an Integer.')
31
+ # expect(definitions[1].return_types).to eq(%w[Integer])
32
32
  def load_string(content:, path:)
33
- ::YARD::Registry.clear
34
- quietly do
33
+ load(path: path) do
35
34
  ::YARD.parse_string(content)
36
35
  end
37
- ::YARD::Registry.all.filter_map do |code_object|
38
- DefinitionMapper.call(code_object, path: path)
39
- end
40
36
  end
41
37
 
42
38
  # @param globs [String]
43
39
  # @return [Array<Rucoa::Definitions::Base>]
44
40
  def load_globs(globs:)
45
- ::YARD::Registry.clear
46
- quietly do
41
+ load do
47
42
  ::YARD.parse(globs)
48
43
  end
49
- ::YARD::Registry.all.filter_map do |code_object|
50
- DefinitionMapper.call(code_object)
51
- end
52
44
  end
53
45
 
54
46
  private
55
47
 
48
+ # @param code_object [YARD::CodeObjects::Base]
49
+ # @param path [String, nil]
50
+ # @return [Rucoa::Definitions::Base, nil]
51
+ def map(code_object, path:)
52
+ case code_object
53
+ when ::YARD::CodeObjects::ClassObject
54
+ Definitions::ClassDefinition.new(
55
+ full_qualified_name: code_object.path,
56
+ source_path: path || code_object.file,
57
+ super_class_name: code_object.superclass.to_s # TODO: superclass may not include full namespace on `YARD.parse_string`.
58
+ )
59
+ when ::YARD::CodeObjects::ModuleObject
60
+ Definitions::ModuleDefinition.new(
61
+ full_qualified_name: code_object.path,
62
+ source_path: path || code_object.file
63
+ )
64
+ when ::YARD::CodeObjects::MethodObject
65
+ MethodDefinitionMapper.call(code_object, path: path)
66
+ end
67
+ end
68
+
69
+ # @param path [String, nil]
70
+ # @return [Array<Rucoa::Definitions::Base>]
71
+ def load(path: nil, &block)
72
+ ::YARD::Registry.clear
73
+ quietly(&block)
74
+ ::YARD::Registry.all.filter_map do |code_object|
75
+ map(code_object, path: path)
76
+ end
77
+ end
78
+
56
79
  def quietly(&block)
57
80
  ::YARD::Logger.instance.enter_level(::Logger::FATAL, &block)
58
81
  end
@@ -4,9 +4,9 @@ require 'yard'
4
4
 
5
5
  module Rucoa
6
6
  module Yard
7
- class DefinitionMapper
7
+ class MethodDefinitionMapper
8
8
  class << self
9
- # @param code_object [YARD::CodeObjects::Base]
9
+ # @param code_object [YARD::CodeObjects::MethodObject]
10
10
  # @param path [String] This must be passed if the path is not available from code object.
11
11
  # @return [Rucoa::Definitions::Base, nil]
12
12
  def call(code_object, path: code_object.file)
@@ -14,17 +14,15 @@ module Rucoa
14
14
  end
15
15
  end
16
16
 
17
- # @param code_object [YARD::CodeObjects::Base]
17
+ # @param code_object [YARD::CodeObjects::MethodObject]
18
18
  # @param path [String]
19
19
  def initialize(code_object, path:)
20
20
  @code_object = code_object
21
21
  @path = path
22
22
  end
23
23
 
24
- # @return [Rucoa::Definitions::Base, nil]
24
+ # @return [Rucoa::Definitions::MethodDefinition]
25
25
  def call
26
- return unless @code_object.is_a?(::YARD::CodeObjects::MethodObject)
27
-
28
26
  Definitions::MethodDefinition.new(
29
27
  description: description,
30
28
  kind: kind,
@@ -51,9 +49,9 @@ module Rucoa
51
49
  end
52
50
  end
53
51
 
54
- # @return [String
52
+ # @return [String]
55
53
  def method_name
56
- @code_object.name
54
+ @code_object.name.to_s
57
55
  end
58
56
 
59
57
  # @return [String]
@@ -90,7 +88,7 @@ module Rucoa
90
88
  # RUBY
91
89
  # path: '/path/to/foo.rb'
92
90
  # )
93
- # expect(definitions.first.signatures).to eq(
91
+ # expect(definitions[1].signatures).to eq(
94
92
  # [
95
93
  # 'Foo#bar(argument1, argument2 = nil, *arguments, keyword1:, keyword2: nil, **keywords, &block) -> Object'
96
94
  # ]
@@ -115,7 +113,7 @@ module Rucoa
115
113
  # @return [Array<Rucoa::Definitions::MethodParameterDefinition>]
116
114
  def parameters
117
115
  parameter_tags.map do |parameter_tag|
118
- ::Rucoa::Definitions::MethodParameterDefinition.new(
116
+ Definitions::MethodParameterDefinition.new(
119
117
  name: parameter_tag.name,
120
118
  types: parameter_tag.types
121
119
  )
@@ -180,27 +178,27 @@ module Rucoa
180
178
 
181
179
  # @return [String]
182
180
  # @example scrubs "Array<String>" to "Array"
183
- # yard_type = Rucoa::Yard::DefinitionMapper::YardType.new(
181
+ # yard_type = Rucoa::Yard::MethodDefinitionMapper::YardType.new(
184
182
  # 'Array<String>'
185
183
  # )
186
184
  # expect(yard_type.to_rucoa_type).to eq('Array')
187
185
  # @example scrubs "Array(String, Integer)" to "Array"
188
- # yard_type = Rucoa::Yard::DefinitionMapper::YardType.new(
186
+ # yard_type = Rucoa::Yard::MethodDefinitionMapper::YardType.new(
189
187
  # 'Array(String, Integer)'
190
188
  # )
191
189
  # expect(yard_type.to_rucoa_type).to eq('Array')
192
190
  # @example scrubs "::Array" to "Array"
193
- # yard_type = Rucoa::Yard::DefinitionMapper::YardType.new(
191
+ # yard_type = Rucoa::Yard::MethodDefinitionMapper::YardType.new(
194
192
  # '::Array'
195
193
  # )
196
194
  # expect(yard_type.to_rucoa_type).to eq('Array')
197
195
  # @example scrubs "Hash{Symbol => Object}" to "Hash"
198
- # yard_type = Rucoa::Yard::DefinitionMapper::YardType.new(
196
+ # yard_type = Rucoa::Yard::MethodDefinitionMapper::YardType.new(
199
197
  # 'Hash{Symbol => Object}'
200
198
  # )
201
199
  # expect(yard_type.to_rucoa_type).to eq('Hash')
202
200
  # @example scrubs "Array<Array<Integer>>" to "Array"
203
- # yard_type = Rucoa::Yard::DefinitionMapper::YardType.new(
201
+ # yard_type = Rucoa::Yard::MethodDefinitionMapper::YardType.new(
204
202
  # 'Array<Array<Integer>>'
205
203
  # )
206
204
  # expect(yard_type.to_rucoa_type).to eq('Array')
data/lib/rucoa/yard.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Rucoa
4
4
  module Yard
5
- autoload :DefinitionMapper, 'rucoa/yard/definition_mapper'
6
5
  autoload :DefinitionsLoader, 'rucoa/yard/definitions_loader'
6
+ autoload :MethodDefinitionMapper, 'rucoa/yard/method_definition_mapper'
7
7
  end
8
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rucoa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryo Nakamura
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-09-15 00:00:00.000000000 Z
11
+ date: 2022-09-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -95,9 +95,11 @@ files:
95
95
  - lib/rucoa/definition_store.rb
96
96
  - lib/rucoa/definitions.rb
97
97
  - lib/rucoa/definitions/base.rb
98
+ - lib/rucoa/definitions/class_definition.rb
98
99
  - lib/rucoa/definitions/constant_definition.rb
99
100
  - lib/rucoa/definitions/method_definition.rb
100
101
  - lib/rucoa/definitions/method_parameter_definition.rb
102
+ - lib/rucoa/definitions/module_definition.rb
101
103
  - lib/rucoa/errors.rb
102
104
  - lib/rucoa/handler_concerns.rb
103
105
  - lib/rucoa/handler_concerns/configuration_requestable.rb
@@ -110,6 +112,7 @@ files:
110
112
  - lib/rucoa/handlers/shutdown_handler.rb
111
113
  - lib/rucoa/handlers/text_document_code_action_handler.rb
112
114
  - lib/rucoa/handlers/text_document_completion_handler.rb
115
+ - lib/rucoa/handlers/text_document_definition_handler.rb
113
116
  - lib/rucoa/handlers/text_document_did_change_handler.rb
114
117
  - lib/rucoa/handlers/text_document_did_close_handler.rb
115
118
  - lib/rucoa/handlers/text_document_did_open_handler.rb
@@ -143,8 +146,10 @@ files:
143
146
  - lib/rucoa/position.rb
144
147
  - lib/rucoa/range.rb
145
148
  - lib/rucoa/rbs.rb
149
+ - lib/rucoa/rbs/class_definition_mapper.rb
146
150
  - lib/rucoa/rbs/constant_definition_mapper.rb
147
151
  - lib/rucoa/rbs/method_definition_mapper.rb
152
+ - lib/rucoa/rbs/module_definition_mapper.rb
148
153
  - lib/rucoa/rbs/ruby_definitions_loader.rb
149
154
  - lib/rucoa/rubocop.rb
150
155
  - lib/rucoa/rubocop/autocorrector.rb
@@ -157,8 +162,8 @@ files:
157
162
  - lib/rucoa/types/method_type.rb
158
163
  - lib/rucoa/version.rb
159
164
  - lib/rucoa/yard.rb
160
- - lib/rucoa/yard/definition_mapper.rb
161
165
  - lib/rucoa/yard/definitions_loader.rb
166
+ - lib/rucoa/yard/method_definition_mapper.rb
162
167
  - rucoa.gemspec
163
168
  homepage: https://github.com/r7kamura/rucoa
164
169
  licenses: