rucoa 0.7.0 → 0.9.0

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