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
@@ -1,60 +1,332 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'logger'
4
+ require 'set'
4
5
  require 'yard'
5
6
 
6
7
  module Rucoa
7
8
  module Yard
8
9
  class DefinitionsLoader
9
10
  class << self
10
- # @param content [String]
11
- # @return [Array<Rucoa::Definitions::Base>]
12
- # @example returns method definitions from Ruby source code
13
- # content = <<~RUBY
14
- # class Foo
15
- # # Return given argument as an Integer.
16
- # # @param bar [String]
17
- # # @return [Integer]
18
- # def foo(bar)
19
- # bar.to_i
20
- # end
21
- # end
22
- # RUBY
23
- # definitions = Rucoa::Yard::DefinitionsLoader.load_string(
24
- # content: content,
25
- # path: '/path/to/foo.rb'
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])
32
- def load_string(content:, path:)
33
- ::YARD::Registry.clear
34
- quietly do
35
- ::YARD.parse_string(content)
36
- end
37
- ::YARD::Registry.all.filter_map do |code_object|
38
- DefinitionMapper.call(code_object, path: path)
39
- end
40
- end
41
-
42
- # @param globs [String]
43
- # @return [Array<Rucoa::Definitions::Base>]
44
- def load_globs(globs:)
45
- ::YARD::Registry.clear
46
- quietly do
47
- ::YARD.parse(globs)
48
- end
49
- ::YARD::Registry.all.filter_map do |code_object|
50
- DefinitionMapper.call(code_object)
51
- end
52
- end
53
-
54
- private
55
-
56
- def quietly(&block)
57
- ::YARD::Logger.instance.enter_level(::Logger::FATAL, &block)
11
+ # @param associations [Hash]
12
+ # @param root_node [Rucoa::Nodes::Base]
13
+ def call(
14
+ associations:,
15
+ root_node:
16
+ )
17
+ new(
18
+ associations: associations,
19
+ root_node: root_node
20
+ ).call
21
+ end
22
+ end
23
+
24
+ # @param associations [Hash]
25
+ # @param root_node [Rucoa::Nodes::Base]
26
+ def initialize(
27
+ associations:,
28
+ root_node:
29
+ )
30
+ @associations = associations
31
+ @root_node = root_node
32
+ end
33
+
34
+ # @return [Array<Rucoa::Definition::Base>]
35
+ def call
36
+ [
37
+ @root_node,
38
+ *@root_node.descendants
39
+ ].flat_map do |node|
40
+ [
41
+ DefinitionGenerators::ClassDefinitionGenerator,
42
+ DefinitionGenerators::MethodDefinitionGenerator,
43
+ DefinitionGenerators::ModuleDefinitionGenerator,
44
+ DefinitionGenerators::AttributeReaderDefinitionGenerator,
45
+ DefinitionGenerators::AttributeWriterDefinitionGenerator
46
+ ].flat_map do |generator|
47
+ generator.call(
48
+ comment: comment_for(node),
49
+ node: node
50
+ )
51
+ end
52
+ end
53
+ end
54
+
55
+ # @return [String]
56
+ def comment_for(node)
57
+ @associations[node.location].map do |parser_comment|
58
+ parser_comment.text.gsub(/^#\s*/m, '')
59
+ end.join("\n")
60
+ end
61
+
62
+ module DefinitionGenerators
63
+ class Base
64
+ class << self
65
+ # @param comment [String]
66
+ # @param node [Rucoa::Nodes::Base]
67
+ # @return [Array<Rucoa::Definitions::Base>]
68
+ def call(
69
+ comment:,
70
+ node:
71
+ )
72
+ new(
73
+ comment: comment,
74
+ node: node
75
+ ).call
76
+ end
77
+ end
78
+
79
+ # @param comment [String]
80
+ # @param node [Rucoa::Nodes::Base]
81
+ def initialize(
82
+ comment:,
83
+ node:
84
+ )
85
+ @comment = comment
86
+ @node = node
87
+ end
88
+
89
+ # @return [Array<Rucoa::Definitions::Base>]
90
+ def call
91
+ raise ::NotImplementedError
92
+ end
93
+
94
+ private
95
+
96
+ # @return [YARD::DocstringParser]
97
+ def docstring_parser
98
+ @docstring_parser ||= ::YARD::Logger.instance.enter_level(::Logger::FATAL) do
99
+ ::YARD::Docstring.parser.parse(
100
+ @comment,
101
+ ::YARD::CodeObjects::Base.new(:root, 'stub')
102
+ )
103
+ end
104
+ end
105
+
106
+ # @return [Array<String>]
107
+ # @example returns annotated return types if return tag is provided
108
+ # definitions = Rucoa::Source.new(
109
+ # content: <<~RUBY,
110
+ # # @return [String]
111
+ # def foo
112
+ # end
113
+ # RUBY
114
+ # uri: '/path/to/foo.rb'
115
+ # ).definitions
116
+ # expect(definitions[0].return_types).to eq(%w[String])
117
+ # @example returns Object if no return tag is provided
118
+ # definitions = Rucoa::Source.new(
119
+ # content: <<~RUBY,
120
+ # def foo
121
+ # end
122
+ # RUBY
123
+ # uri: '/path/to/foo.rb'
124
+ # ).definitions
125
+ # expect(definitions[0].return_types).to eq(%w[Object])
126
+ def return_types
127
+ types = docstring_parser.tags.select do |tag|
128
+ tag.tag_name == 'return'
129
+ end.flat_map(&:types).compact.map do |yard_type|
130
+ Type.new(yard_type).to_rucoa_type
131
+ end
132
+ if types.empty?
133
+ %w[Object]
134
+ else
135
+ types
136
+ end
137
+ end
138
+ end
139
+
140
+ class ClassDefinitionGenerator < Base
141
+ # @example returns class definition for class node
142
+ # definitions = Rucoa::Source.new(
143
+ # content: <<~RUBY,
144
+ # class Foo
145
+ # end
146
+ # RUBY
147
+ # uri: '/path/to/foo.rb'
148
+ # ).definitions
149
+ # expect(definitions[0]).to be_a(Rucoa::Definitions::ClassDefinition)
150
+ def call
151
+ return [] unless @node.is_a?(Nodes::ClassNode)
152
+
153
+ [
154
+ Definitions::ClassDefinition.new(
155
+ fully_qualified_name: @node.fully_qualified_name,
156
+ module_nesting: @node.module_nesting,
157
+ source_path: @node.location.expression.source_buffer.name,
158
+ super_class_chained_name: @node.super_class_chained_name
159
+ )
160
+ ]
161
+ end
162
+ end
163
+
164
+ class ModuleDefinitionGenerator < Base
165
+ # @example returns module definition for module node
166
+ # definitions = Rucoa::Source.new(
167
+ # content: <<~RUBY,
168
+ # module Foo
169
+ # end
170
+ # RUBY
171
+ # uri: '/path/to/foo.rb'
172
+ # ).definitions
173
+ # expect(definitions[0]).to be_a(Rucoa::Definitions::ModuleDefinition)
174
+ def call
175
+ return [] unless @node.is_a?(Nodes::ModuleNode)
176
+
177
+ [
178
+ Definitions::ModuleDefinition.new(
179
+ fully_qualified_name: @node.fully_qualified_name,
180
+ source_path: @node.location.expression.source_buffer.name
181
+ )
182
+ ]
183
+ end
184
+ end
185
+
186
+ class MethodDefinitionGenerator < Base
187
+ # @example returns method definition for def node
188
+ # definitions = Rucoa::Source.new(
189
+ # content: <<~RUBY,
190
+ # def foo
191
+ # end
192
+ # RUBY
193
+ # uri: '/path/to/foo.rb'
194
+ # ).definitions
195
+ # expect(definitions[0]).to be_a(Rucoa::Definitions::MethodDefinition)
196
+ # @example returns method definition for defs node
197
+ # definitions = Rucoa::Source.new(
198
+ # content: <<~RUBY,
199
+ # def self.foo
200
+ # end
201
+ # RUBY
202
+ # uri: '/path/to/foo.rb'
203
+ # ).definitions
204
+ # expect(definitions[0]).to be_a(Rucoa::Definitions::MethodDefinition)
205
+ def call
206
+ return [] unless @node.is_a?(Nodes::DefNode) || @node.is_a?(Nodes::DefsNode)
207
+
208
+ [
209
+ Definitions::MethodDefinition.new(
210
+ description: docstring_parser.to_docstring.to_s,
211
+ kind: @node.singleton? ? :singleton : :instance,
212
+ method_name: @node.name,
213
+ namespace: @node.namespace,
214
+ source_path: @node.location.expression.source_buffer.name,
215
+ types: return_types.map do |type|
216
+ Types::MethodType.new(
217
+ parameters_string: '', # TODO
218
+ return_type: type
219
+ )
220
+ end
221
+ )
222
+ ]
223
+ end
224
+ end
225
+
226
+ class AttributeReaderDefinitionGenerator < Base
227
+ READER_METHOD_NAMES = ::Set[
228
+ 'attr_accessor',
229
+ 'attr_reader'
230
+ ].freeze
231
+
232
+ # @example returns method definition for attr_reader node
233
+ # definitions = Rucoa::Source.new(
234
+ # content: <<~RUBY,
235
+ # class Foo
236
+ # attr_reader :bar
237
+ # end
238
+ # RUBY
239
+ # uri: '/path/to/foo.rb'
240
+ # ).definitions
241
+ # expect(definitions[1]).to be_a(Rucoa::Definitions::MethodDefinition)
242
+ # @example returns method definition for attr_accessor node
243
+ # definitions = Rucoa::Source.new(
244
+ # content: <<~RUBY,
245
+ # class Foo
246
+ # attr_accessor :bar
247
+ # end
248
+ # RUBY
249
+ # uri: '/path/to/foo.rb'
250
+ # ).definitions
251
+ # expect(definitions.map(&:fully_qualified_name)).to eq(
252
+ # %w[
253
+ # Foo
254
+ # Foo#bar
255
+ # Foo#bar=
256
+ # ]
257
+ # )
258
+ def call
259
+ return [] unless @node.is_a?(Nodes::SendNode) && READER_METHOD_NAMES.include?(@node.name)
260
+
261
+ @node.arguments.map do |argument|
262
+ Definitions::MethodDefinition.new(
263
+ description: docstring_parser.to_docstring.to_s,
264
+ kind: :instance,
265
+ method_name: argument.value.to_s,
266
+ namespace: @node.namespace,
267
+ source_path: @node.location.expression.source_buffer.name,
268
+ types: return_types.map do |type|
269
+ Types::MethodType.new(
270
+ parameters_string: '', # TODO
271
+ return_type: type
272
+ )
273
+ end
274
+ )
275
+ end
276
+ end
277
+ end
278
+
279
+ class AttributeWriterDefinitionGenerator < Base
280
+ WRITER_METHOD_NAMES = ::Set[
281
+ 'attr_accessor',
282
+ 'attr_writer'
283
+ ].freeze
284
+
285
+ # @example returns method definition for attr_writer node
286
+ # definitions = Rucoa::Source.new(
287
+ # content: <<~RUBY,
288
+ # class Foo
289
+ # attr_writer :bar
290
+ # end
291
+ # RUBY
292
+ # uri: '/path/to/foo.rb'
293
+ # ).definitions
294
+ # expect(definitions[1]).to be_a(Rucoa::Definitions::MethodDefinition)
295
+ # @example returns method definition for attr_accessor node
296
+ # definitions = Rucoa::Source.new(
297
+ # content: <<~RUBY,
298
+ # class Foo
299
+ # attr_accessor :bar
300
+ # end
301
+ # RUBY
302
+ # uri: '/path/to/foo.rb'
303
+ # ).definitions
304
+ # expect(definitions.map(&:fully_qualified_name)).to eq(
305
+ # %w[
306
+ # Foo
307
+ # Foo#bar
308
+ # Foo#bar=
309
+ # ]
310
+ # )
311
+ def call
312
+ return [] unless @node.is_a?(Nodes::SendNode) && WRITER_METHOD_NAMES.include?(@node.name)
313
+
314
+ @node.arguments.map do |argument|
315
+ Definitions::MethodDefinition.new(
316
+ description: docstring_parser.to_docstring.to_s,
317
+ kind: :instance,
318
+ method_name: "#{argument.value}=",
319
+ namespace: @node.namespace,
320
+ source_path: @node.location.expression.source_buffer.name,
321
+ types: return_types.map do |type|
322
+ Types::MethodType.new(
323
+ parameters_string: 'value', # TODO
324
+ return_type: type
325
+ )
326
+ end
327
+ )
328
+ end
329
+ end
58
330
  end
59
331
  end
60
332
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Yard
5
+ class Type
6
+ # @param value [String]
7
+ def initialize(value)
8
+ @value = value
9
+ end
10
+
11
+ # @return [String]
12
+ # @example scrubs "Array<String>" to "Array"
13
+ # yard_type = Rucoa::Yard::Type.new(
14
+ # 'Array<String>'
15
+ # )
16
+ # expect(yard_type.to_rucoa_type).to eq('Array')
17
+ # @example scrubs "Array(String, Integer)" to "Array"
18
+ # yard_type = Rucoa::Yard::Type.new(
19
+ # 'Array(String, Integer)'
20
+ # )
21
+ # expect(yard_type.to_rucoa_type).to eq('Array')
22
+ # @example scrubs "::Array" to "Array"
23
+ # yard_type = Rucoa::Yard::Type.new(
24
+ # '::Array'
25
+ # )
26
+ # expect(yard_type.to_rucoa_type).to eq('Array')
27
+ # @example scrubs "Hash{Symbol => Object}" to "Hash"
28
+ # yard_type = Rucoa::Yard::Type.new(
29
+ # 'Hash{Symbol => Object}'
30
+ # )
31
+ # expect(yard_type.to_rucoa_type).to eq('Hash')
32
+ # @example scrubs "Array<Array<Integer>>" to "Array"
33
+ # yard_type = Rucoa::Yard::Type.new(
34
+ # 'Array<Array<Integer>>'
35
+ # )
36
+ # expect(yard_type.to_rucoa_type).to eq('Array')
37
+ def to_rucoa_type
38
+ @value
39
+ .delete_prefix('::')
40
+ .gsub(/<.+>/, '')
41
+ .gsub(/\{.+\}/, '')
42
+ .gsub(/\(.+\)/, '')
43
+ end
44
+ end
45
+ end
46
+ end
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 :Type, 'rucoa/yard/type'
7
7
  end
8
8
  end
data/lib/rucoa.rb CHANGED
@@ -18,6 +18,7 @@ module Rucoa
18
18
  autoload :Nodes, 'rucoa/nodes'
19
19
  autoload :Parser, 'rucoa/parser'
20
20
  autoload :ParserBuilder, 'rucoa/parser_builder'
21
+ autoload :ParseResult, 'rucoa/parse_result'
21
22
  autoload :Position, 'rucoa/position'
22
23
  autoload :Range, 'rucoa/range'
23
24
  autoload :Rbs, 'rucoa/rbs'
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.9.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-21 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
@@ -123,11 +126,12 @@ files:
123
126
  - lib/rucoa/message_reader.rb
124
127
  - lib/rucoa/message_writer.rb
125
128
  - lib/rucoa/node_concerns.rb
126
- - lib/rucoa/node_concerns/name_full_qualifiable.rb
129
+ - lib/rucoa/node_concerns/name_fully_qualifiable.rb
127
130
  - lib/rucoa/node_inspector.rb
128
131
  - lib/rucoa/nodes.rb
129
132
  - lib/rucoa/nodes/base.rb
130
133
  - lib/rucoa/nodes/casgn_node.rb
134
+ - lib/rucoa/nodes/cbase_node.rb
131
135
  - lib/rucoa/nodes/class_node.rb
132
136
  - lib/rucoa/nodes/const_node.rb
133
137
  - lib/rucoa/nodes/def_node.rb
@@ -138,13 +142,16 @@ files:
138
142
  - lib/rucoa/nodes/send_node.rb
139
143
  - lib/rucoa/nodes/str_node.rb
140
144
  - lib/rucoa/nodes/sym_node.rb
145
+ - lib/rucoa/parse_result.rb
141
146
  - lib/rucoa/parser.rb
142
147
  - lib/rucoa/parser_builder.rb
143
148
  - lib/rucoa/position.rb
144
149
  - lib/rucoa/range.rb
145
150
  - lib/rucoa/rbs.rb
151
+ - lib/rucoa/rbs/class_definition_mapper.rb
146
152
  - lib/rucoa/rbs/constant_definition_mapper.rb
147
153
  - lib/rucoa/rbs/method_definition_mapper.rb
154
+ - lib/rucoa/rbs/module_definition_mapper.rb
148
155
  - lib/rucoa/rbs/ruby_definitions_loader.rb
149
156
  - lib/rucoa/rubocop.rb
150
157
  - lib/rucoa/rubocop/autocorrector.rb
@@ -157,8 +164,8 @@ files:
157
164
  - lib/rucoa/types/method_type.rb
158
165
  - lib/rucoa/version.rb
159
166
  - lib/rucoa/yard.rb
160
- - lib/rucoa/yard/definition_mapper.rb
161
167
  - lib/rucoa/yard/definitions_loader.rb
168
+ - lib/rucoa/yard/type.rb
162
169
  - rucoa.gemspec
163
170
  homepage: https://github.com/r7kamura/rucoa
164
171
  licenses: