steep 0.13.0 → 0.14.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: 0e95857ced793435c07c948b2a51ac914c52816811ae8e5d18337bcf946eb33d
4
- data.tar.gz: 9038e63999222be3e0b7172aa423056ef7792b905e1d277271fa795b6ca06889
3
+ metadata.gz: 86924db409406024edb29a087196304bacba8730dfa3c986c604fbe8b6b9a6fd
4
+ data.tar.gz: d688f6b2f58343da51328f13bc26da7e4e54de7f1dd9393f0cd98d48c5ea8420
5
5
  SHA512:
6
- metadata.gz: 2747b3e5f3303ae48a91e76b2256cd1e402e7022caa714083267da0820f1bce2d81188586e551a10158d4c6716ab7a8db8562aa84b31c98731872b08d05048c9
7
- data.tar.gz: 9d5bd44685d8228ee304bdaf94ae52e5eef2a2871580e7190ef941ea510406eab28bdb3aa6849fd9234649da10cd9344e2c38ba309a11a73cca70c6ed7e946e3
6
+ metadata.gz: a70b109afc79267f2a47425dcc8b93f05074fbffa1e410fa62824b5a94320f18a9274182ad2fbaa4083867dd0bab9e4e8796fc8b472edd2328d2b332e76f9f66
7
+ data.tar.gz: 6b4dc08b74b0f042b325180f1b358862d510fefe06a9e07f06380bbf7e86f47e3547d365d0cc94a278c80946befec64689a5c56e87a3e820db767103727e69f7
data/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.14.0 (2020-02-24)
6
+
7
+ * Implementat LSP _completion_. ([#119](https://github.com/soutaro/steep/pull/119))
8
+ * Update ruby-signature. ([#120](https://github.com/soutaro/steep/pull/120))
9
+ * Rescue errors during `langserver`. ([#121](https://github.com/soutaro/steep/pull/121))
10
+ * Pass hint when type checking `return`. ([#122](https://github.com/soutaro/steep/pull/122))
11
+ * Remove trailing spaces from Steepfile. ([#118](https://github.com/soutaro/steep/pull/118))
12
+
5
13
  ## 0.13.0 (2020-02-16)
6
14
 
7
15
  * Improve LSP _hover_ support. ([#117](https://github.com/soutaro/steep/pull/117))
data/lib/steep.rb CHANGED
@@ -74,6 +74,7 @@ require "steep/project/target"
74
74
  require "steep/project/dsl"
75
75
  require "steep/project/file_loader"
76
76
  require "steep/project/hover_content"
77
+ require "steep/project/completion_provider"
77
78
  require "steep/drivers/utils/driver_helper"
78
79
  require "steep/drivers/check"
79
80
  require "steep/drivers/validate"
@@ -114,4 +115,19 @@ module Steep
114
115
 
115
116
  @logger = nil
116
117
  self.log_output = STDERR
118
+
119
+ def self.measure(message)
120
+ start = Time.now
121
+ yield.tap do
122
+ time = Time.now - start
123
+ self.logger.info "#{message} took #{time} seconds"
124
+ end
125
+ end
126
+
127
+ def self.log_error(exn, message: "Unexpected error: #{exn.inspect}")
128
+ Steep.logger.error message
129
+ exn.backtrace.each do |loc|
130
+ Steep.logger.warn " #{loc}"
131
+ end
132
+ end
117
133
  end
@@ -187,47 +187,82 @@ module Steep
187
187
  end
188
188
 
189
189
  def method_type(method_type, self_type:)
190
- case method_type
191
- when Ruby::Signature::MethodType
192
- fvs = self_type.free_variables()
193
-
194
- type_params = []
195
- alpha_vars = []
196
- alpha_types = []
197
-
198
- method_type.type_params.map do |name|
199
- if fvs.include?(name)
200
- type = Types::Var.fresh(name)
201
- alpha_vars << name
202
- alpha_types << type
203
- type_params << type.name
204
- else
205
- type_params << name
206
- end
190
+ fvs = self_type.free_variables()
191
+
192
+ type_params = []
193
+ alpha_vars = []
194
+ alpha_types = []
195
+
196
+ method_type.type_params.map do |name|
197
+ if fvs.include?(name)
198
+ type = Types::Var.fresh(name)
199
+ alpha_vars << name
200
+ alpha_types << type
201
+ type_params << type.name
202
+ else
203
+ type_params << name
207
204
  end
208
- subst = Interface::Substitution.build(alpha_vars, alpha_types)
209
-
210
- type = Interface::MethodType.new(
211
- type_params: type_params,
212
- return_type: type(method_type.type.return_type).subst(subst),
213
- params: params(method_type.type).subst(subst),
214
- location: nil,
215
- block: method_type.block&.yield_self do |block|
216
- Interface::Block.new(
217
- optional: !block.required,
218
- type: Proc.new(params: params(block.type).subst(subst),
219
- return_type: type(block.type.return_type).subst(subst), location: nil)
220
- )
221
- end
222
- )
205
+ end
206
+ subst = Interface::Substitution.build(alpha_vars, alpha_types)
207
+
208
+ type = Interface::MethodType.new(
209
+ type_params: type_params,
210
+ return_type: type(method_type.type.return_type).subst(subst),
211
+ params: params(method_type.type).subst(subst),
212
+ location: nil,
213
+ block: method_type.block&.yield_self do |block|
214
+ Interface::Block.new(
215
+ optional: !block.required,
216
+ type: Proc.new(params: params(block.type).subst(subst),
217
+ return_type: type(block.type.return_type).subst(subst), location: nil)
218
+ )
219
+ end
220
+ )
221
+
222
+ if block_given?
223
+ yield type
224
+ else
225
+ type
226
+ end
227
+ end
228
+
229
+ def method_type_1(method_type, self_type:)
230
+ fvs = self_type.free_variables()
223
231
 
224
- if block_given?
225
- yield type
232
+ type_params = []
233
+ alpha_vars = []
234
+ alpha_types = []
235
+
236
+ method_type.type_params.map do |name|
237
+ if fvs.include?(name)
238
+ type = Ruby::Signature::Types::Variable.new(name: name, location: nil),
239
+ alpha_vars << name
240
+ alpha_types << type
241
+ type_params << type.name
226
242
  else
227
- type
243
+ type_params << name
228
244
  end
229
- when :any
230
- :any
245
+ end
246
+ subst = Interface::Substitution.build(alpha_vars, alpha_types)
247
+
248
+ type = Ruby::Signature::MethodType.new(
249
+ type_params: type_params,
250
+ type: function_1(method_type.params.subst(subst), method_type.return_type.subst(subst)),
251
+ block: method_type.block&.yield_self do |block|
252
+ block_type = block.type.subst(subst)
253
+
254
+ Ruby::Signature::MethodType::Block.new(
255
+ type: function_1(block_type.params, block_type.return_type),
256
+ required: !block.optional
257
+ )
258
+ end,
259
+ location: nil
260
+ )
261
+
262
+ if block_given?
263
+ yield type
264
+ else
265
+ type
231
266
  end
232
267
  end
233
268
 
@@ -10,21 +10,21 @@ module Steep
10
10
  TEMPLATE = <<~EOF
11
11
  # target :lib do
12
12
  # signature "sig"
13
- #
13
+ #
14
14
  # check "lib" # Directory name
15
15
  # check "Gemfile" # File name
16
16
  # check "app/models/**/*.rb" # Glob
17
- # # ignore "lib/templates/*.rb"
18
- #
17
+ # # ignore "lib/templates/*.rb"
18
+ #
19
19
  # # library "pathname", "set" # Standard libraries
20
20
  # # library "strong_json" # Gems
21
21
  # end
22
22
 
23
23
  # target :spec do
24
24
  # signature "sig", "sig-private"
25
- #
25
+ #
26
26
  # check "spec"
27
- #
27
+ #
28
28
  # # library "pathname", "set" # Standard libraries
29
29
  # # library "rspec"
30
30
  # end
@@ -84,10 +84,55 @@ module Steep
84
84
  change: LanguageServer::Protocol::Constant::TextDocumentSyncKind::FULL
85
85
  ),
86
86
  hover_provider: true,
87
+ completion_provider: LanguageServer::Protocol::Interface::CompletionOptions.new(
88
+ trigger_characters: [".", "@"],
87
89
  )
90
+ )
88
91
  )
89
92
 
90
93
  enqueue_type_check nil
94
+
95
+ when :"textDocument/completion"
96
+ Steep.logger.error request.inspect
97
+ begin
98
+ params = request[:params]
99
+ uri = URI.parse(params[:textDocument][:uri])
100
+ path = project.relative_path(Pathname(uri.path))
101
+ target = project.targets.find {|target| target.source_file?(path) }
102
+ case (status = target&.status)
103
+ when Project::Target::TypeCheckStatus
104
+ subtyping = status.subtyping
105
+ source = target.source_files[path]
106
+
107
+ line, column = params[:position].yield_self {|hash| [hash[:line]+1, hash[:character]] }
108
+ trigger = params[:context][:triggerCharacter]
109
+
110
+ Steep.logger.error "line: #{line}, column: #{column}, trigger: #{trigger}"
111
+
112
+ provider = Project::CompletionProvider.new(source_text: source.content, path: path, subtyping: subtyping)
113
+ items = begin
114
+ provider.run(line: line, column: column)
115
+ rescue Parser::SyntaxError
116
+ []
117
+ end
118
+
119
+ completion_items = items.map do |item|
120
+ format_completion_item(item)
121
+ end
122
+
123
+ Steep.logger.debug "items = #{completion_items.inspect}"
124
+
125
+ yield id, LanguageServer::Protocol::Interface::CompletionList.new(
126
+ is_incomplete: false,
127
+ items: completion_items
128
+ )
129
+ end
130
+
131
+ rescue Typing::UnknownNodeError => exn
132
+ Steep.log_error exn, message: "Failed to compute completion: #{exn.inspect}"
133
+ yield id, nil
134
+ end
135
+
91
136
  when :"textDocument/didChange"
92
137
  uri = URI.parse(request[:params][:textDocument][:uri])
93
138
  path = project.relative_path(Pathname(uri.path))
@@ -151,7 +196,11 @@ module Steep
151
196
  @type_check_thread = Thread.start do
152
197
  while request = type_check_queue.deq
153
198
  if @latest_update_version == nil || @latest_update_version == request.version
154
- run_type_check()
199
+ begin
200
+ run_type_check()
201
+ rescue => exn
202
+ Steep.log_error exn
203
+ end
155
204
  end
156
205
  end
157
206
  end
@@ -185,13 +234,11 @@ module Steep
185
234
  source.errors.map {|error| diagnostic_for_type_error(error) }
186
235
  when Project::SourceFile::AnnotationSyntaxErrorStatus
187
236
  [diagnostics_raw(source.status.error.message, source.status.location)]
188
- when Project::SourceFile::ParseErrorStatus
189
- []
190
- when Project::SourceFile::TypeCheckErrorStatus
191
- []
192
237
  end
193
238
 
194
- report_diagnostics source.path, diagnostics
239
+ if diagnostics
240
+ report_diagnostics source.path, diagnostics
241
+ end
195
242
  end
196
243
  when Project::Target::SignatureSyntaxErrorStatus
197
244
  Steep.logger.info { "Signature syntax error" }
@@ -280,6 +327,9 @@ module Steep
280
327
  range: range
281
328
  )
282
329
  end
330
+ rescue Typing::UnknownNodeError => exn
331
+ Steep.log_error exn, message: "Failed to compute hover: #{exn.inspect}"
332
+ nil
283
333
  end
284
334
 
285
335
  def format_hover(content)
@@ -331,6 +381,126 @@ HOVER
331
381
  "`#{content.type}`"
332
382
  end
333
383
  end
384
+
385
+ def format_completion_item(item)
386
+ range = LanguageServer::Protocol::Interface::Range.new(
387
+ start: LanguageServer::Protocol::Interface::Position.new(
388
+ line: item.range.start.line-1,
389
+ character: item.range.start.column
390
+ ),
391
+ end: LanguageServer::Protocol::Interface::Position.new(
392
+ line: item.range.end.line-1,
393
+ character: item.range.end.column
394
+ )
395
+ )
396
+
397
+ case item
398
+ when Project::CompletionProvider::LocalVariableItem
399
+ LanguageServer::Protocol::Interface::CompletionItem.new(
400
+ label: item.identifier,
401
+ kind: LanguageServer::Protocol::Constant::CompletionItemKind::VARIABLE,
402
+ detail: "#{item.identifier}: #{item.type}",
403
+ text_edit: LanguageServer::Protocol::Interface::TextEdit.new(
404
+ range: range,
405
+ new_text: "#{item.identifier}"
406
+ )
407
+ )
408
+ when Project::CompletionProvider::MethodNameItem
409
+ label = "def #{item.identifier}: #{item.method_type}"
410
+ method_type_snippet = method_type_to_snippet(item.method_type)
411
+ LanguageServer::Protocol::Interface::CompletionItem.new(
412
+ label: label,
413
+ kind: LanguageServer::Protocol::Constant::CompletionItemKind::METHOD,
414
+ text_edit: LanguageServer::Protocol::Interface::TextEdit.new(
415
+ new_text: "#{item.identifier}#{method_type_snippet}",
416
+ range: range
417
+ ),
418
+ documentation: item.definition.comment&.string,
419
+ insert_text_format: LanguageServer::Protocol::Constant::InsertTextFormat::SNIPPET
420
+ )
421
+ when Project::CompletionProvider::InstanceVariableItem
422
+ label = "#{item.identifier}: #{item.type}"
423
+ LanguageServer::Protocol::Interface::CompletionItem.new(
424
+ label: label,
425
+ kind: LanguageServer::Protocol::Constant::CompletionItemKind::FIELD,
426
+ text_edit: LanguageServer::Protocol::Interface::TextEdit.new(
427
+ range: range,
428
+ new_text: item.identifier,
429
+ ),
430
+ insert_text_format: LanguageServer::Protocol::Constant::InsertTextFormat::SNIPPET
431
+ )
432
+ end
433
+ end
434
+
435
+ def method_type_to_snippet(method_type)
436
+ params = if method_type.type.each_param.count == 0
437
+ ""
438
+ else
439
+ "(#{params_to_snippet(method_type.type)})"
440
+ end
441
+
442
+
443
+ block = if method_type.block
444
+ open, space, close = if method_type.block.type.return_type.is_a?(Ruby::Signature::Types::Bases::Void)
445
+ ["do", " ", "end"]
446
+ else
447
+ ["{", "", "}"]
448
+ end
449
+
450
+ if method_type.block.type.each_param.count == 0
451
+ " #{open} $0 #{close}"
452
+ else
453
+ " #{open}#{space}|#{params_to_snippet(method_type.block.type)}| $0 #{close}"
454
+ end
455
+ else
456
+ ""
457
+ end
458
+
459
+ "#{params}#{block}"
460
+ end
461
+
462
+ def params_to_snippet(fun)
463
+ params = []
464
+
465
+ index = 1
466
+
467
+ fun.required_positionals.each do |param|
468
+ if name = param.name
469
+ params << "${#{index}:#{param.type}}"
470
+ else
471
+ params << "${#{index}:#{param.type}}"
472
+ end
473
+
474
+ index += 1
475
+ end
476
+
477
+ if fun.rest_positionals
478
+ params << "${#{index}:*#{fun.rest_positionals.type}}"
479
+ index += 1
480
+ end
481
+
482
+ fun.trailing_positionals.each do |param|
483
+ if name = param.name
484
+ params << "${#{index}:#{param.type}}"
485
+ else
486
+ params << "${#{index}:#{param.type}}"
487
+ end
488
+
489
+ index += 1
490
+ end
491
+
492
+ fun.required_keywords.each do |keyword, param|
493
+ if name = param.name
494
+ params << "#{keyword}: ${#{index}:#{name}_}"
495
+ else
496
+ params << "#{keyword}: ${#{index}:#{param.type}_}"
497
+ end
498
+
499
+ index += 1
500
+ end
501
+
502
+ params.join(", ")
503
+ end
334
504
  end
335
505
  end
336
506
  end
@@ -0,0 +1,302 @@
1
+ module Steep
2
+ class Project
3
+ class CompletionProvider
4
+ Position = Struct.new(:line, :column, keyword_init: true) do
5
+ def -(size)
6
+ Position.new(line: line, column: column - size)
7
+ end
8
+ end
9
+ Range = Struct.new(:start, :end, keyword_init: true)
10
+
11
+ InstanceVariableItem = Struct.new(:identifier, :range, :type, keyword_init: true)
12
+ LocalVariableItem = Struct.new(:identifier, :range, :type, keyword_init: true)
13
+ MethodNameItem = Struct.new(:identifier, :range, :definition, :method_type, keyword_init: true)
14
+
15
+ attr_reader :source_text
16
+ attr_reader :path
17
+ attr_reader :subtyping
18
+ attr_reader :modified_text
19
+ attr_reader :source
20
+ attr_reader :typing
21
+
22
+ def initialize(source_text:, path:, subtyping:)
23
+ @source_text = source_text
24
+ @path = path
25
+ @subtyping = subtyping
26
+ end
27
+
28
+ def type_check!(text)
29
+ @modified_text = text
30
+
31
+ Steep.measure "parsing" do
32
+ @source = SourceFile.parse(text, path: path, factory: subtyping.factory)
33
+ end
34
+
35
+ Steep.measure "typechecking" do
36
+ @typing = SourceFile.type_check(source, subtyping: subtyping)
37
+ end
38
+ end
39
+
40
+ def run(line:, column:)
41
+ source_text = self.source_text.dup
42
+ index = index_for(source_text, line:line, column: column)
43
+ possible_trigger = source_text[index-1]
44
+
45
+ Steep.logger.debug "possible_trigger: #{possible_trigger.inspect}"
46
+
47
+ position = Position.new(line: line, column: column)
48
+
49
+ begin
50
+ Steep.measure "type_check!" do
51
+ type_check!(source_text)
52
+ end
53
+
54
+ Steep.measure "completion item collection" do
55
+ items_for_trigger(position: position)
56
+ end
57
+
58
+ rescue Parser::SyntaxError => exn
59
+ Steep.logger.error "recovering syntax error: #{exn.inspect}"
60
+ case possible_trigger
61
+ when "."
62
+ source_text[index-1] = " "
63
+ type_check!(source_text)
64
+ items_for_dot(position: position)
65
+ when "@"
66
+ source_text[index-1] = " "
67
+ type_check!(source_text)
68
+ items_for_atmark(position: position)
69
+ else
70
+ []
71
+ end
72
+ end
73
+ end
74
+
75
+ def range_from_loc(loc)
76
+ Range.new(
77
+ start: Position.new(line: loc.line, column: loc.column),
78
+ end: Position.new(line: loc.last_line, column: loc.last_line)
79
+ )
80
+ end
81
+
82
+ def at_end?(pos, of:)
83
+ of.last_line == pos.line && of.last_column == pos.column
84
+ end
85
+
86
+ def range_for(position, prefix: "")
87
+ if prefix.empty?
88
+ Range.new(start: position, end: position)
89
+ else
90
+ Range.new(start: position - prefix.size, end: position)
91
+ end
92
+ end
93
+
94
+ def items_for_trigger(position:)
95
+ node, *parents = source.find_nodes(line: position.line, column: position.column)
96
+ node ||= source.node
97
+
98
+ return [] unless node
99
+
100
+ items = []
101
+
102
+ case
103
+ when node.type == :send && node.children[0] == nil && at_end?(position, of: node.loc.selector)
104
+ # foo ←
105
+ context = typing.context_of(node: node)
106
+ prefix = node.children[1].to_s
107
+
108
+ method_items_for_receiver_type(context.self_type,
109
+ include_private: true,
110
+ prefix: prefix,
111
+ position: position,
112
+ items: items)
113
+ local_variable_items_for_context(context, position: position, prefix: prefix, items: items)
114
+
115
+ when node.type == :lvar && at_end?(position, of: node.loc)
116
+ # foo ← (lvar)
117
+ context = typing.context_of(node: node)
118
+ local_variable_items_for_context(context, position: position, prefix: node.children[0].name.to_s, items: items)
119
+
120
+ when node.type == :send && node.children[0] && at_end?(position, of: node.loc.selector)
121
+ # foo.ba ←
122
+ context = typing.context_of(node: node)
123
+ receiver_type = case (type = typing.type_of(node: node.children[0]))
124
+ when AST::Types::Self
125
+ context.self_type
126
+ else
127
+ type
128
+ end
129
+ prefix = node.children[1].to_s
130
+
131
+ method_items_for_receiver_type(receiver_type,
132
+ include_private: false,
133
+ prefix: prefix,
134
+ position: position,
135
+ items: items)
136
+
137
+ when node.type == :const && node.children[0] == nil && at_end?(position, of: node.loc)
138
+ # Foo ← (const)
139
+ context = typing.context_of(node: node)
140
+ prefix = node.children[1].to_s
141
+
142
+ method_items_for_receiver_type(context.self_type,
143
+ include_private: false,
144
+ prefix: prefix,
145
+ position: position,
146
+ items: items)
147
+
148
+ when node.type == :send && at_end?(position, of: node.loc.dot)
149
+ # foo.← ba
150
+ context = typing.context_of(node: node)
151
+ receiver_type = case (type = typing.type_of(node: node.children[0]))
152
+ when AST::Types::Self
153
+ context.self_type
154
+ else
155
+ type
156
+ end
157
+
158
+ method_items_for_receiver_type(receiver_type,
159
+ include_private: false,
160
+ prefix: "",
161
+ position: position,
162
+ items: items)
163
+
164
+ when node.type == :ivar && at_end?(position, of: node.loc)
165
+ # @fo ←
166
+ context = typing.context_of(node: node)
167
+ instance_variable_items_for_context(context, position: position, prefix: node.children[0].to_s, items: items)
168
+
169
+ else
170
+ context = typing.context_of(node: node)
171
+
172
+ method_items_for_receiver_type(context.self_type,
173
+ include_private: true,
174
+ prefix: "",
175
+ position: position,
176
+ items: items)
177
+ local_variable_items_for_context(context, position: position, prefix: "", items: items)
178
+ instance_variable_items_for_context(context, position: position, prefix: "", items: items)
179
+ end
180
+
181
+ items
182
+ end
183
+
184
+ def items_for_dot(position:)
185
+ # foo. ←
186
+ shift_pos = position-1
187
+ node, *parents = source.find_nodes(line: shift_pos.line, column: shift_pos.column)
188
+ node ||= source.node
189
+
190
+ return [] unless node
191
+
192
+ if at_end?(shift_pos, of: node.loc)
193
+ context = typing.context_of(node: node)
194
+ receiver_type = case (type = typing.type_of(node: node))
195
+ when AST::Types::Self
196
+ context.self_type
197
+ else
198
+ type
199
+ end
200
+
201
+ items = []
202
+ method_items_for_receiver_type(receiver_type,
203
+ include_private: false,
204
+ prefix: "",
205
+ position: position,
206
+ items: items)
207
+ items
208
+ else
209
+ []
210
+ end
211
+ end
212
+
213
+ def items_for_atmark(position:)
214
+ # @ ←
215
+ shift_pos = position-1
216
+ node, *parents = source.find_nodes(line: shift_pos.line, column: shift_pos.column)
217
+ node ||= source.node
218
+
219
+ return [] unless node
220
+
221
+ context = typing.context_of(node: node)
222
+ items = []
223
+ instance_variable_items_for_context(context, prefix: "", position: position, items: items)
224
+ items
225
+ end
226
+
227
+ def method_items_for_receiver_type(type, include_private:, prefix:, position:, items:)
228
+ range = range_for(position, prefix: prefix)
229
+ definition = case type
230
+ when AST::Types::Name::Instance
231
+ type_name = subtyping.factory.type_name_1(type.name)
232
+ subtyping.factory.definition_builder.build_instance(type_name)
233
+ when AST::Types::Name::Class, AST::Types::Name::Module
234
+ type_name = subtyping.factory.type_name_1(type.name)
235
+ subtyping.factory.definition_builder.build_singleton(type_name)
236
+ when AST::Types::Name::Interface
237
+ type_name = subtyping.factory.type_name_1(type.name)
238
+ interface = subtyping.factory.env.find_class(type_name)
239
+ subtyping.factory.definition_builder.build_interface(type_name, interface)
240
+ end
241
+
242
+ if definition
243
+ definition.methods.each do |name, method|
244
+ if include_private || method.public?
245
+ if name.to_s.start_with?(prefix)
246
+ if word_name?(name.to_s)
247
+ method.method_types.each do |method_type|
248
+ items << MethodNameItem.new(identifier: name,
249
+ range: range,
250
+ definition: method,
251
+ method_type: method_type)
252
+ end
253
+ end
254
+ end
255
+ end
256
+ end
257
+ end
258
+ end
259
+
260
+ def word_name?(name)
261
+ name =~ /\w/
262
+ end
263
+
264
+ def local_variable_items_for_context(context, position:, prefix:, items:)
265
+ range = range_for(position, prefix: prefix)
266
+ context.type_env.lvar_types.each do |name, type|
267
+ if name.to_s.start_with?(prefix)
268
+ items << LocalVariableItem.new(identifier: name,
269
+ range: range,
270
+ type: type)
271
+ end
272
+ end
273
+ end
274
+
275
+ def instance_variable_items_for_context(context, position:, prefix:, items:)
276
+ range = range_for(position, prefix: prefix)
277
+ context.type_env.ivar_types.map do |name, type|
278
+ if name.to_s.start_with?(prefix)
279
+ items << InstanceVariableItem.new(identifier: name,
280
+ range: range,
281
+ type: type)
282
+ end
283
+ end
284
+ end
285
+
286
+ def index_for(string, line:, column:)
287
+ index = 0
288
+
289
+ string.each_line.with_index do |s, i|
290
+ if i+1 == line
291
+ index += column
292
+ break
293
+ else
294
+ index += s.size
295
+ end
296
+ end
297
+
298
+ index
299
+ end
300
+ end
301
+ end
302
+ end