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

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