steep 1.4.0.dev.2 → 1.4.0.dev.4

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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +1 -2
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +9 -11
  5. data/Gemfile.steep +1 -2
  6. data/Gemfile.steep.lock +11 -14
  7. data/README.md +7 -1
  8. data/Steepfile +0 -3
  9. data/bin/rbs +0 -1
  10. data/guides/README.md +5 -0
  11. data/guides/src/gem-rbs-collection/gem-rbs-collection.md +143 -0
  12. data/guides/src/getting-started/getting-started.md +164 -0
  13. data/guides/src/nil-optional/nil-optional.md +195 -0
  14. data/lib/steep/diagnostic/ruby.rb +80 -6
  15. data/lib/steep/drivers/check.rb +4 -4
  16. data/lib/steep/interface/block.rb +10 -0
  17. data/lib/steep/interface/builder.rb +3 -3
  18. data/lib/steep/method_name.rb +8 -0
  19. data/lib/steep/module_helper.rb +13 -11
  20. data/lib/steep/path_helper.rb +4 -0
  21. data/lib/steep/server/interaction_worker.rb +197 -230
  22. data/lib/steep/server/lsp_formatter.rb +308 -154
  23. data/lib/steep/server/master.rb +4 -1
  24. data/lib/steep/services/completion_provider.rb +140 -103
  25. data/lib/steep/services/hover_provider/rbs.rb +37 -32
  26. data/lib/steep/services/signature_help_provider.rb +108 -0
  27. data/lib/steep/services/type_name_completion.rb +165 -0
  28. data/lib/steep/source.rb +1 -0
  29. data/lib/steep/type_construction.rb +460 -266
  30. data/lib/steep/type_inference/block_params.rb +13 -0
  31. data/lib/steep/type_inference/context.rb +3 -3
  32. data/lib/steep/type_inference/method_call.rb +1 -1
  33. data/lib/steep/type_inference/method_params.rb +42 -16
  34. data/lib/steep/type_inference/send_args.rb +80 -51
  35. data/lib/steep/type_inference/type_env.rb +12 -4
  36. data/lib/steep/version.rb +1 -1
  37. data/lib/steep.rb +2 -0
  38. data/rbs_collection.steep.lock.yaml +0 -28
  39. data/rbs_collection.steep.yaml +10 -9
  40. data/sample/Steepfile +2 -0
  41. data/sample/lib/conference.rb +12 -0
  42. data/sample/sig/conference.rbs +5 -0
  43. data/sig/shims/language-server_protocol.rbs +277 -0
  44. data/sig/shims/parser/nodes.rbs +37 -0
  45. data/sig/shims/parser.rbs +4 -0
  46. data/sig/shims/string.rbs +4 -0
  47. data/sig/steep/ast/types/factory.rbs +10 -8
  48. data/sig/steep/diagnostic/lsp_formatter.rbs +1 -1
  49. data/sig/steep/diagnostic/ruby.rbs +38 -2
  50. data/sig/steep/drivers/check.rbs +1 -1
  51. data/sig/steep/drivers/checkfile.rbs +1 -1
  52. data/sig/steep/drivers/diagnostic_printer.rbs +1 -1
  53. data/sig/steep/drivers/watch.rbs +1 -1
  54. data/sig/steep/index/signature_symbol_provider.rbs +1 -1
  55. data/sig/steep/interface/block.rbs +2 -0
  56. data/sig/steep/interface/builder.rbs +5 -3
  57. data/sig/steep/interface/method_type.rbs +5 -3
  58. data/sig/steep/method_name.rbs +5 -1
  59. data/sig/steep/module_helper.rbs +9 -0
  60. data/sig/steep/path_helper.rbs +3 -1
  61. data/sig/steep/server/base_worker.rbs +1 -1
  62. data/sig/steep/server/interaction_worker.rbs +52 -17
  63. data/sig/steep/server/lsp_formatter.rbs +43 -18
  64. data/sig/steep/server/master.rbs +1 -1
  65. data/sig/steep/server/type_check_worker.rbs +7 -5
  66. data/sig/steep/server/worker_process.rbs +6 -4
  67. data/sig/steep/services/completion_provider.rbs +106 -28
  68. data/sig/steep/services/hover_provider/rbs.rbs +13 -9
  69. data/sig/steep/services/signature_help_provider.rbs +39 -0
  70. data/sig/steep/services/type_name_completion.rbs +122 -0
  71. data/sig/steep/type_construction.rbs +99 -30
  72. data/sig/steep/type_inference/block_params.rbs +4 -0
  73. data/sig/steep/type_inference/context.rbs +70 -22
  74. data/sig/steep/type_inference/method_call.rbs +1 -1
  75. data/sig/steep/type_inference/method_params.rbs +43 -24
  76. data/sig/steep/type_inference/multiple_assignment.rbs +1 -1
  77. data/sig/steep/type_inference/send_args.rbs +19 -5
  78. data/sig/steep/typing.rbs +8 -3
  79. data/smoke/diagnostics/test_expectations.yml +1 -0
  80. data/steep.gemspec +0 -1
  81. metadata +12 -16
@@ -3,9 +3,10 @@ module Steep
3
3
  class InteractionWorker < BaseWorker
4
4
  include ChangeBuffer
5
5
 
6
- ApplyChangeJob = Class.new()
7
- HoverJob = Struct.new(:id, :path, :line, :column, keyword_init: true)
8
- CompletionJob = Struct.new(:id, :path, :line, :column, :trigger, keyword_init: true)
6
+ ApplyChangeJob = _ = Class.new()
7
+ HoverJob = _ = Struct.new(:id, :path, :line, :column, keyword_init: true)
8
+ CompletionJob = _ = Struct.new(:id, :path, :line, :column, :trigger, keyword_init: true)
9
+ SignatureHelpJob = _ = Struct.new(:id, :path, :line, :column, keyword_init: true)
9
10
 
10
11
  LSP = LanguageServer::Protocol
11
12
 
@@ -35,6 +36,8 @@ module Steep
35
36
  writer.write({ id: job.id, result: process_hover(job) })
36
37
  when CompletionJob
37
38
  writer.write({ id: job.id, result: process_completion(job) })
39
+ when SignatureHelpJob
40
+ writer.write({ id: job.id, result: process_signature_help(job) })
38
41
  end
39
42
  end
40
43
  end
@@ -53,7 +56,7 @@ module Steep
53
56
  when "textDocument/hover"
54
57
  id = request[:id]
55
58
 
56
- path = project.relative_path(Steep::PathHelper.to_pathname(request[:params][:textDocument][:uri]))
59
+ path = project.relative_path(PathHelper.to_pathname!(request[:params][:textDocument][:uri]))
57
60
  line = request[:params][:position][:line]+1
58
61
  column = request[:params][:position][:character]
59
62
 
@@ -63,11 +66,19 @@ module Steep
63
66
  id = request[:id]
64
67
 
65
68
  params = request[:params]
66
- path = project.relative_path(Steep::PathHelper.to_pathname(params[:textDocument][:uri]))
69
+
70
+ path = project.relative_path(PathHelper.to_pathname!(params[:textDocument][:uri]))
67
71
  line, column = params[:position].yield_self {|hash| [hash[:line]+1, hash[:character]] }
68
72
  trigger = params.dig(:context, :triggerCharacter)
69
73
 
70
74
  queue << CompletionJob.new(id: id, path: path, line: line, column: column, trigger: trigger)
75
+ when "textDocument/signatureHelp"
76
+ id = request[:id]
77
+ params = request[:params]
78
+ path = project.relative_path(PathHelper.to_pathname!(params[:textDocument][:uri]))
79
+ line, column = params[:position].yield_self {|hash| [hash[:line]+1, hash[:character]] }
80
+
81
+ queue << SignatureHelpJob.new(id: id, path: path, line: line, column: column)
71
82
  end
72
83
  end
73
84
 
@@ -80,16 +91,13 @@ module Steep
80
91
  if content
81
92
  range = content.location.yield_self do |location|
82
93
  lsp_range = location.as_lsp_range
83
- start_position = { line: lsp_range[:start][:line], character: lsp_range[:start][:character] }
84
- end_position = { line: lsp_range[:end][:line], character: lsp_range[:end][:character] }
85
- { start: start_position, end: end_position }
94
+ start_position = LSP::Interface::Position.new(line: lsp_range[:start][:line], character: lsp_range[:start][:character])
95
+ end_position = LSP::Interface::Position.new(line: lsp_range[:end][:line], character: lsp_range[:end][:character])
96
+ LSP::Interface::Range.new(start: start_position, end: end_position)
86
97
  end
87
98
 
88
99
  LSP::Interface::Hover.new(
89
- contents: {
90
- kind: "markdown",
91
- value: LSPFormatter.format_hover_content(content).to_s
92
- },
100
+ contents: LSP::Interface::MarkupContent.new(kind: "markdown", value: LSPFormatter.format_hover_content(content).to_s),
93
101
  range: range
94
102
  )
95
103
  end
@@ -104,6 +112,7 @@ module Steep
104
112
  Steep.logger.tagged("#response_to_completion") do
105
113
  Steep.measure "Generating response" do
106
114
  Steep.logger.info "path: #{job.path}, line: #{job.line}, column: #{job.column}, trigger: #{job.trigger}"
115
+
107
116
  case
108
117
  when target = project.target_for_source_path(job.path)
109
118
  file = service.source_files[job.path] or return
@@ -127,55 +136,76 @@ module Steep
127
136
  items: completion_items
128
137
  )
129
138
  when (targets = project.targets_for_path(job.path)).is_a?(Array)
130
- target = targets[0] or return
131
- sig_service = service.signature_services[target.name] #: Services::SignatureService
139
+ target = targets[0] or raise
140
+ sig_service = service.signature_services[target.name] or raise
132
141
  relative_path = job.path
133
- buffer = RBS::Buffer.new(name: relative_path, content: sig_service.files[relative_path].content)
134
- pos = buffer.loc_to_pos([job.line, job.column])
135
- prefix = buffer.content[0...pos].reverse[/\A[\w\d]*/].reverse
136
-
137
- case sig_service.status
138
- when Steep::Services::SignatureService::SyntaxErrorStatus, Steep::Services::SignatureService::AncestorErrorStatus
139
- return
140
- end
141
142
 
142
- sig = sig_service.files[relative_path].signature
143
- sig.is_a?(Array) or raise
144
- decls = sig[2]
145
- locator = RBS::Locator.new(buffer: sig[0], dirs: sig[1], decls: decls)
143
+ context = nil #: RBS::Resolver::context
146
144
 
147
- (_hd, tail = locator.find2(line: job.line, column: job.column)) or return []
145
+ case sig_service.status
146
+ when Services::SignatureService::SyntaxErrorStatus, Services::SignatureService::AncestorErrorStatus
148
147
 
149
- namespace = []
150
- tail.each do |t|
151
- case t
152
- when RBS::AST::Declarations::Module, RBS::AST::Declarations::Class
153
- namespace << t.name.to_namespace
148
+ if buffer = sig_service.latest_env.buffers.find {|buf| Pathname(buf.name) == Pathname(relative_path) }
149
+ dirs = sig_service.latest_env.signatures[buffer][0]
150
+ else
151
+ dirs = [] #: Array[RBS::AST::Directives::t]
152
+ end
153
+ else
154
+ signature = sig_service.files[relative_path].signature
155
+ signature.is_a?(Array) or raise
156
+ buffer, dirs, decls = signature
157
+
158
+ locator = RBS::Locator.new(buffer: buffer, dirs: dirs, decls: decls)
159
+
160
+ _hd, tail = locator.find2(line: job.line, column: job.column)
161
+ tail ||= []
162
+
163
+ tail.reverse_each do |t|
164
+ case t
165
+ when RBS::AST::Declarations::Module, RBS::AST::Declarations::Class
166
+ if (last_type_name = context&.[](1)).is_a?(RBS::TypeName)
167
+ context = [context, last_type_name + t.name]
168
+ else
169
+ context = [context, t.name.absolute!]
170
+ end
171
+ end
154
172
  end
155
173
  end
156
- context = []
157
-
158
- namespace.each do |ns|
159
- context.map! { |n| ns + n }
160
- context << ns
161
- end
162
-
163
- context.map!(&:absolute!)
164
174
 
165
- class_names = sig_service.latest_env.class_decls.keys + sig_service.latest_env.class_alias_decls.keys
166
- class_items = class_names.map { |type_name|
167
- format_completion_item_for_rbs(sig_service, type_name, context, job, prefix)
168
- }.compact
175
+ buffer = RBS::Buffer.new(name: relative_path, content: sig_service.files[relative_path].content)
176
+ prefix = Services::TypeNameCompletion::Prefix.parse(buffer, line: job.line, column: job.column)
169
177
 
170
- alias_items = sig_service.latest_env.type_alias_decls.keys.map { |type_name|
171
- format_completion_item_for_rbs(sig_service, type_name, context, job, prefix)
172
- }.compact
178
+ completion = Services::TypeNameCompletion.new(env: sig_service.latest_env, context: context, dirs: dirs)
179
+ type_names = completion.find_type_names(prefix)
180
+ prefix_size = prefix ? prefix.size : 0
173
181
 
174
- interface_items = sig_service.latest_env.interface_decls.keys.map {|type_name|
175
- format_completion_item_for_rbs(sig_service, type_name, context, job, prefix)
176
- }.compact
182
+ completion_items = type_names.map do |type_name|
183
+ absolute_name, relative_name = completion.resolve_name_in_context(type_name)
184
+ format_completion_item_for_rbs(sig_service, absolute_name, job, relative_name.to_s, prefix_size)
185
+ end
177
186
 
178
- completion_items = class_items + alias_items + interface_items
187
+ ["untyped", "void", "bool", "class", "module", "instance", "nil"].each do |name|
188
+ completion_items << LSP::Interface::CompletionItem.new(
189
+ label: name,
190
+ detail: "(builtin type)",
191
+ text_edit: LSP::Interface::TextEdit.new(
192
+ range: LSP::Interface::Range.new(
193
+ start: LSP::Interface::Position.new(
194
+ line: job.line - 1,
195
+ character: job.column - prefix_size
196
+ ),
197
+ end: LSP::Interface::Position.new(
198
+ line: job.line - 1,
199
+ character: job.column
200
+ )
201
+ ),
202
+ new_text: name
203
+ ),
204
+ kind: LSP::Constant::CompletionItemKind::KEYWORD,
205
+ filter_text: name,
206
+ sort_text: "zz__#{name}"
207
+ )
208
+ end
179
209
 
180
210
  LSP::Interface::CompletionList.new(
181
211
  is_incomplete: false,
@@ -186,22 +216,18 @@ module Steep
186
216
  end
187
217
  end
188
218
 
189
- def format_completion_item_for_rbs(sig_service, type_name, context, job, prefix)
190
- range = LanguageServer::Protocol::Interface::Range.new(
191
- start: LanguageServer::Protocol::Interface::Position.new(
219
+ def format_completion_item_for_rbs(sig_service, type_name, job, complete_text, prefix_size)
220
+ range = LSP::Interface::Range.new(
221
+ start: LSP::Interface::Position.new(
192
222
  line: job.line - 1,
193
- character: job.column - prefix.size
223
+ character: job.column - prefix_size
194
224
  ),
195
- end: LanguageServer::Protocol::Interface::Position.new(
225
+ end: LSP::Interface::Position.new(
196
226
  line: job.line - 1,
197
227
  character: job.column
198
228
  )
199
229
  )
200
230
 
201
- name = relative_name_in_context(type_name, context).to_s
202
-
203
- return unless name.start_with?(prefix)
204
-
205
231
  case type_name.kind
206
232
  when :class
207
233
  env = sig_service.latest_env #: RBS::Environment
@@ -209,82 +235,61 @@ module Steep
209
235
 
210
236
  case class_entry
211
237
  when RBS::Environment::ClassEntry, RBS::Environment::ModuleEntry
212
- LanguageServer::Protocol::Interface::CompletionItem.new(
213
- label: "#{name}",
214
- documentation: format_comment(class_entry.primary.decl.comment),
215
- text_edit: LanguageServer::Protocol::Interface::TextEdit.new(
216
- range: range,
217
- new_text: name
218
- ),
219
- kind: LSP::Constant::CompletionItemKind::CLASS,
220
- insert_text_format: LSP::Constant::InsertTextFormat::SNIPPET
221
- )
238
+ comments = class_entry.decls.map {|decl| decl.decl.comment }.compact
239
+ decl = class_entry.primary.decl
222
240
  when RBS::Environment::ClassAliasEntry, RBS::Environment::ModuleAliasEntry
223
- LanguageServer::Protocol::Interface::CompletionItem.new(
224
- label: "#{name}",
225
- documentation: format_comment(class_entry.decl.comment),
226
- text_edit: LanguageServer::Protocol::Interface::TextEdit.new(
227
- range: range,
228
- new_text: name
229
- ),
230
- kind: LSP::Constant::CompletionItemKind::CLASS,
231
- insert_text_format: LSP::Constant::InsertTextFormat::SNIPPET
232
- )
241
+ comments = [class_entry.decl.comment].compact
242
+ decl = class_entry.decl
233
243
  end
244
+
245
+ LSP::Interface::CompletionItem.new(
246
+ label: complete_text,
247
+ label_details: LSP::Interface::CompletionItemLabelDetails.new(description: LSPFormatter.declaration_summary(decl)),
248
+ documentation: LSPFormatter.markup_content { LSPFormatter.format_rbs_completion_docs(type_name, decl, comments) },
249
+ text_edit: LSP::Interface::TextEdit.new(
250
+ range: range,
251
+ new_text: complete_text
252
+ ),
253
+ kind: LSP::Constant::CompletionItemKind::CLASS
254
+ )
234
255
  when :alias
235
256
  alias_decl = sig_service.latest_env.type_alias_decls[type_name]&.decl or raise
236
- LanguageServer::Protocol::Interface::CompletionItem.new(
237
- label: "#{name}",
238
- text_edit: LanguageServer::Protocol::Interface::TextEdit.new(
257
+
258
+ LSP::Interface::CompletionItem.new(
259
+ label: complete_text,
260
+ label_details: LSP::Interface::CompletionItemLabelDetails.new(description: LSPFormatter.declaration_summary(alias_decl)),
261
+ text_edit: LSP::Interface::TextEdit.new(
239
262
  range: range,
240
- new_text: name
263
+ new_text: complete_text
241
264
  ),
242
- documentation: format_comment(alias_decl.comment),
243
- # https://github.com/microsoft/vscode-languageserver-node/blob/6d78fc4d25719b231aba64a721a606f58b9e0a5f/client/src/common/client.ts#L624-L650
244
- kind: LSP::Constant::CompletionItemKind::FIELD,
245
- insert_text_format: LSP::Constant::InsertTextFormat::SNIPPET
265
+ documentation: LSPFormatter.markup_content { LSPFormatter.format_rbs_completion_docs(type_name, alias_decl, [alias_decl.comment].compact) },
266
+ kind: LSP::Constant::CompletionItemKind::FIELD
246
267
  )
247
268
  when :interface
248
269
  interface_decl = sig_service.latest_env.interface_decls[type_name]&.decl or raise
249
270
 
250
- LanguageServer::Protocol::Interface::CompletionItem.new(
251
- label: "#{name}",
252
- text_edit: LanguageServer::Protocol::Interface::TextEdit.new(
271
+ LSP::Interface::CompletionItem.new(
272
+ label: complete_text,
273
+ label_details: LSP::Interface::CompletionItemLabelDetails.new(description: LSPFormatter.declaration_summary(interface_decl)),
274
+ text_edit: LSP::Interface::TextEdit.new(
253
275
  range: range,
254
- new_text: name
276
+ new_text: complete_text
255
277
  ),
256
- documentation: format_comment(interface_decl.comment),
257
- kind: LanguageServer::Protocol::Constant::CompletionItemKind::INTERFACE,
258
- insert_text_format: LanguageServer::Protocol::Constant::InsertTextFormat::SNIPPET
259
- )
260
- end
261
- end
262
-
263
- def format_comment(comment)
264
- if comment
265
- LSP::Interface::MarkupContent.new(
266
- kind: LSP::Constant::MarkupKind::MARKDOWN,
267
- value: comment.string.gsub(/<!--(?~-->)-->/, "")
268
- )
269
- end
270
- end
271
-
272
- def format_comments(comments)
273
- unless comments.empty?
274
- LSP::Interface::MarkupContent.new(
275
- kind: LSP::Constant::MarkupKind::MARKDOWN,
276
- value: comments.map(&:string).join("\n----\n").gsub(/<!--(?~-->)-->/, "")
278
+ documentation: LSPFormatter.markup_content { LSPFormatter.format_rbs_completion_docs(type_name, interface_decl, [interface_decl.comment].compact) },
279
+ kind: LSP::Constant::CompletionItemKind::INTERFACE
277
280
  )
281
+ else
282
+ raise
278
283
  end
279
284
  end
280
285
 
281
286
  def format_completion_item(item)
282
- range = LanguageServer::Protocol::Interface::Range.new(
283
- start: LanguageServer::Protocol::Interface::Position.new(
287
+ range = LSP::Interface::Range.new(
288
+ start: LSP::Interface::Position.new(
284
289
  line: item.range.start.line-1,
285
290
  character: item.range.start.column
286
291
  ),
287
- end: LanguageServer::Protocol::Interface::Position.new(
292
+ end: LSP::Interface::Position.new(
288
293
  line: item.range.end.line-1,
289
294
  character: item.range.end.column
290
295
  )
@@ -292,141 +297,103 @@ module Steep
292
297
 
293
298
  case item
294
299
  when Services::CompletionProvider::LocalVariableItem
295
- LanguageServer::Protocol::Interface::CompletionItem.new(
296
- label: item.identifier,
297
- kind: LanguageServer::Protocol::Constant::CompletionItemKind::VARIABLE,
298
- detail: item.type.to_s,
299
- text_edit: LanguageServer::Protocol::Interface::TextEdit.new(
300
- range: range,
301
- new_text: item.identifier
302
- )
300
+ LSP::Interface::CompletionItem.new(
301
+ label: item.identifier.to_s,
302
+ kind: LSP::Constant::CompletionItemKind::VARIABLE,
303
+ label_details: LSP::Interface::CompletionItemLabelDetails.new(description: item.type.to_s),
304
+ documentation: LSPFormatter.markup_content { LSPFormatter.format_completion_docs(item) },
305
+ insert_text: item.identifier.to_s,
306
+ sort_text: item.identifier.to_s
303
307
  )
304
308
  when Services::CompletionProvider::ConstantItem
305
309
  case
306
310
  when item.class? || item.module?
307
- kind = LanguageServer::Protocol::Constant::CompletionItemKind::CLASS
308
- detail = item.full_name.to_s
311
+ kind = LSP::Constant::CompletionItemKind::CLASS
309
312
  else
310
- kind = LanguageServer::Protocol::Constant::CompletionItemKind::CONSTANT
311
- detail = item.type.to_s
313
+ kind = LSP::Constant::CompletionItemKind::CONSTANT
312
314
  end
313
- LanguageServer::Protocol::Interface::CompletionItem.new(
314
- label: item.identifier,
315
+
316
+ detail = LSPFormatter.declaration_summary(item.decl)
317
+
318
+ LSP::Interface::CompletionItem.new(
319
+ label: item.identifier.to_s,
315
320
  kind: kind,
316
- detail: detail,
317
- documentation: format_comments(item.comments),
318
- text_edit: LanguageServer::Protocol::Interface::TextEdit.new(
321
+ label_details: LSP::Interface::CompletionItemLabelDetails.new(description: detail),
322
+ documentation: LSPFormatter.markup_content { LSPFormatter.format_completion_docs(item) },
323
+ text_edit: LSP::Interface::TextEdit.new(
319
324
  range: range,
320
- new_text: item.identifier
325
+ new_text: item.identifier.to_s
321
326
  )
322
327
  )
323
- when Services::CompletionProvider::MethodNameItem
324
- method_type_snippet = method_type_to_snippet(item.method_type)
325
- LanguageServer::Protocol::Interface::CompletionItem.new(
326
- label: item.identifier,
327
- kind: LanguageServer::Protocol::Constant::CompletionItemKind::METHOD,
328
- detail: item.method_type.to_s,
329
- text_edit: LanguageServer::Protocol::Interface::TextEdit.new(
330
- new_text: "#{item.identifier}#{method_type_snippet}",
331
- range: range
332
- ),
333
- documentation: format_comment(item.comment),
334
- insert_text_format: LanguageServer::Protocol::Constant::InsertTextFormat::SNIPPET,
335
- sort_text: item.inherited? ? 'z' : 'a' # Ensure language server puts non-inherited methods before inherited methods
328
+ when Services::CompletionProvider::SimpleMethodNameItem
329
+ LSP::Interface::CompletionItem.new(
330
+ label: item.identifier.to_s,
331
+ kind: LSP::Constant::CompletionItemKind::FUNCTION,
332
+ label_details: LSP::Interface::CompletionItemLabelDetails.new(description: item.method_name.relative.to_s),
333
+ insert_text: item.identifier.to_s,
334
+ documentation: LSPFormatter.markup_content { LSPFormatter.format_completion_docs(item) }
335
+ )
336
+ when Services::CompletionProvider::ComplexMethodNameItem
337
+ method_names = item.method_names.map(&:relative).uniq
338
+
339
+ LSP::Interface::CompletionItem.new(
340
+ label: item.identifier.to_s,
341
+ kind: LSP::Constant::CompletionItemKind::FUNCTION,
342
+ label_details: LSP::Interface::CompletionItemLabelDetails.new(description: method_names.join(", ")),
343
+ insert_text: item.identifier.to_s,
344
+ documentation: LSPFormatter.markup_content { LSPFormatter.format_completion_docs(item) }
345
+ )
346
+ when Services::CompletionProvider::GeneratedMethodNameItem
347
+ LSP::Interface::CompletionItem.new(
348
+ label: item.identifier.to_s,
349
+ kind: LSP::Constant::CompletionItemKind::FUNCTION,
350
+ label_details: LSP::Interface::CompletionItemLabelDetails.new(description: "(Generated)"),
351
+ insert_text: item.identifier.to_s,
352
+ documentation: LSPFormatter.markup_content { LSPFormatter.format_completion_docs(item) }
336
353
  )
337
354
  when Services::CompletionProvider::InstanceVariableItem
338
- LanguageServer::Protocol::Interface::CompletionItem.new(
339
- label: item.identifier,
340
- kind: LanguageServer::Protocol::Constant::CompletionItemKind::FIELD,
341
- detail: item.type.to_s,
342
- text_edit: LanguageServer::Protocol::Interface::TextEdit.new(
355
+ LSP::Interface::CompletionItem.new(
356
+ label: item.identifier.to_s,
357
+ kind: LSP::Constant::CompletionItemKind::FIELD,
358
+ label_details: LSP::Interface::CompletionItemLabelDetails.new(description: item.type.to_s),
359
+ documentation: LSPFormatter.markup_content { LSPFormatter.format_completion_docs(item) },
360
+ text_edit: LSP::Interface::TextEdit.new(
343
361
  range: range,
344
- new_text: item.identifier,
345
- ),
346
- insert_text_format: LanguageServer::Protocol::Constant::InsertTextFormat::SNIPPET
362
+ new_text: item.identifier.to_s
363
+ )
347
364
  )
348
365
  end
349
366
  end
350
367
 
351
- def method_type_to_snippet(method_type)
352
- params = if method_type.type.each_param.count == 0
353
- ""
354
- else
355
- "(#{params_to_snippet(method_type.type)})"
356
- end
357
-
358
-
359
- block = if method_type.block
360
- open, space, close = if method_type.block.type.return_type.is_a?(RBS::Types::Bases::Void)
361
- ["do", " ", "end"]
362
- else
363
- ["{", "", "}"]
364
- end
365
-
366
- if method_type.block.type.each_param.count == 0
367
- " #{open} $0 #{close}"
368
- else
369
- " #{open}#{space}|#{params_to_snippet(method_type.block.type)}| $0 #{close}"
368
+ def process_signature_help(job)
369
+ Steep.logger.tagged("##{__method__}") do
370
+ if target = project.target_for_source_path(job.path)
371
+ file = service.source_files[job.path] or return
372
+ subtyping = service.signature_services[target.name].current_subtyping or return
373
+ source = Source.parse(file.content, path: file.path, factory: subtyping.factory)
374
+
375
+ provider = Services::SignatureHelpProvider.new(source: source, subtyping: subtyping)
376
+
377
+ if (items, index = provider.run(line: job.line, column: job.column))
378
+ signatures = items.map do |item|
379
+ LSP::Interface::SignatureInformation.new(
380
+ label: "(#{item.method_type.type.param_to_s})",
381
+ documentation: item.comment&.yield_self do |comment|
382
+ LSP::Interface::MarkupContent.new(
383
+ kind: LSP::Constant::MarkupKind::MARKDOWN,
384
+ value: comment.string.gsub(/<!--(?~-->)-->/, "")
385
+ )
370
386
  end
371
- else
372
- ""
373
- end
374
-
375
- "#{params}#{block}"
376
- end
377
-
378
- def params_to_snippet(fun)
379
- params = []
380
-
381
- index = 1
382
-
383
- fun.required_positionals.each do |param|
384
- if name = param.name
385
- params << "${#{index}:#{param.type}}"
386
- else
387
- params << "${#{index}:#{param.type}}"
388
- end
389
-
390
- index += 1
391
- end
392
-
393
- if fun.rest_positionals
394
- params << "${#{index}:*#{fun.rest_positionals.type}}"
395
- index += 1
396
- end
397
-
398
- fun.trailing_positionals.each do |param|
399
- if name = param.name
400
- params << "${#{index}:#{param.type}}"
401
- else
402
- params << "${#{index}:#{param.type}}"
403
- end
404
-
405
- index += 1
406
- end
407
-
408
- fun.required_keywords.each do |keyword, param|
409
- if name = param.name
410
- params << "#{keyword}: ${#{index}:#{name}_}"
411
- else
412
- params << "#{keyword}: ${#{index}:#{param.type}_}"
413
- end
414
-
415
- index += 1
416
- end
417
-
418
- params.join(", ")
419
- end
387
+ )
388
+ end
420
389
 
421
- def relative_name_in_context(type_name, context)
422
- context.each do |namespace|
423
- if (type_name.to_s == namespace.to_type_name.to_s || type_name.namespace.to_s == "::")
424
- return RBS::TypeName.new(namespace: RBS::Namespace.empty, name: type_name.name)
425
- elsif type_name.to_s.start_with?(namespace.to_s)
426
- return TypeName(type_name.to_s.sub(namespace.to_type_name.to_s, '')).relative!
390
+ LSP::Interface::SignatureHelp.new(
391
+ signatures: signatures,
392
+ active_signature: index
393
+ )
394
+ end
427
395
  end
428
396
  end
429
- type_name
430
397
  end
431
398
  end
432
399
  end