rucoa 0.7.0 → 0.8.0

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