steep 1.4.0.dev.3 → 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.
- checksums.yaml +4 -4
- data/Gemfile.lock +3 -3
- data/Gemfile.steep.lock +5 -7
- data/lib/steep/diagnostic/ruby.rb +1 -2
- data/lib/steep/interface/builder.rb +3 -3
- data/lib/steep/method_name.rb +8 -0
- data/lib/steep/server/interaction_worker.rb +131 -177
- data/lib/steep/server/lsp_formatter.rb +308 -154
- data/lib/steep/server/master.rb +4 -1
- data/lib/steep/services/completion_provider.rb +140 -103
- data/lib/steep/services/hover_provider/rbs.rb +37 -32
- data/lib/steep/services/signature_help_provider.rb +108 -0
- data/lib/steep/services/type_name_completion.rb +9 -1
- data/lib/steep/type_construction.rb +61 -40
- data/lib/steep/type_inference/method_call.rb +1 -1
- data/lib/steep/type_inference/send_args.rb +3 -3
- data/lib/steep/type_inference/type_env.rb +5 -3
- data/lib/steep/version.rb +1 -1
- data/lib/steep.rb +1 -0
- data/sample/Steepfile +2 -0
- data/sig/shims/language-server_protocol.rbs +265 -0
- data/sig/shims/parser.rbs +3 -0
- data/sig/steep/method_name.rbs +5 -1
- data/sig/steep/server/interaction_worker.rbs +18 -12
- data/sig/steep/server/lsp_formatter.rbs +43 -18
- data/sig/steep/services/completion_provider.rbs +105 -29
- data/sig/steep/services/hover_provider/rbs.rbs +13 -9
- data/sig/steep/services/signature_help_provider.rbs +39 -0
- data/sig/steep/type_inference/method_call.rbs +1 -1
- data/sig/steep/type_inference/send_args.rbs +6 -2
- data/sig/steep/typing.rbs +1 -1
- metadata +4 -2
@@ -3,211 +3,323 @@ module Steep
|
|
3
3
|
module LSPFormatter
|
4
4
|
include Services
|
5
5
|
|
6
|
-
|
7
|
-
def initialize
|
8
|
-
@array = []
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.build
|
12
|
-
builder = CommentBuilder.new
|
13
|
-
yield builder
|
14
|
-
builder.to_s
|
15
|
-
end
|
6
|
+
LSP = LanguageServer::Protocol
|
16
7
|
|
17
|
-
|
18
|
-
unless @array.empty?
|
19
|
-
@array.join("\n\n----\n\n")
|
20
|
-
else
|
21
|
-
""
|
22
|
-
end
|
23
|
-
end
|
8
|
+
module_function
|
24
9
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
unless @array.include?(s)
|
29
|
-
@array << s
|
30
|
-
end
|
31
|
-
end
|
10
|
+
def markup_content(string = nil, &block)
|
11
|
+
if block
|
12
|
+
string = yield()
|
32
13
|
end
|
33
14
|
|
34
|
-
|
35
|
-
|
36
|
-
yield s
|
37
|
-
self << s
|
15
|
+
if string
|
16
|
+
LSP::Interface::MarkupContent.new(kind: LSP::Constant::MarkupKind::MARKDOWN, value: string)
|
38
17
|
end
|
39
18
|
end
|
40
19
|
|
41
|
-
module_function
|
42
|
-
|
43
20
|
def format_hover_content(content)
|
44
21
|
case content
|
45
22
|
when HoverProvider::Ruby::VariableContent
|
46
|
-
|
23
|
+
local_variable(content.name, content.type)
|
47
24
|
|
48
25
|
when HoverProvider::Ruby::MethodCallContent
|
49
|
-
|
50
|
-
|
51
|
-
builder.push do |s|
|
52
|
-
case call
|
53
|
-
when TypeInference::MethodCall::Special
|
54
|
-
mt = call.actual_method_type.with(
|
55
|
-
type: call.actual_method_type.type.with(return_type: call.return_type)
|
56
|
-
)
|
57
|
-
s << <<-EOM
|
58
|
-
**💡 Custom typing rule applies**
|
59
|
-
|
60
|
-
```rbs
|
61
|
-
#{mt.to_s}
|
62
|
-
```
|
26
|
+
io = StringIO.new
|
27
|
+
call = content.method_call
|
63
28
|
|
64
|
-
|
65
|
-
|
66
|
-
|
29
|
+
case call
|
30
|
+
when TypeInference::MethodCall::Typed
|
31
|
+
method_types = call.method_decls.map(&:method_type)
|
32
|
+
if call.is_a?(TypeInference::MethodCall::Special)
|
33
|
+
method_types = [
|
34
|
+
call.actual_method_type.with(
|
67
35
|
type: call.actual_method_type.type.with(return_type: call.return_type)
|
68
36
|
)
|
69
|
-
|
70
|
-
when TypeInference::MethodCall::Error
|
71
|
-
s << "```rbs\n( ??? ) -> #{call.return_type.to_s}\n```\n\n"
|
72
|
-
end
|
37
|
+
]
|
73
38
|
|
74
|
-
|
75
|
-
|
76
|
-
|
39
|
+
header = <<~MD
|
40
|
+
**💡 Custom typing rule applies**
|
41
|
+
|
42
|
+
----
|
43
|
+
MD
|
77
44
|
end
|
45
|
+
when TypeInference::MethodCall::Error
|
46
|
+
method_types = call.method_decls.map {|decl| decl.method_type }
|
78
47
|
|
79
|
-
|
80
|
-
|
81
|
-
builder << <<EOM
|
82
|
-
**#{decl.method_name.to_s}**
|
48
|
+
header = <<~MD
|
49
|
+
**🚨 No compatible method type found**
|
83
50
|
|
84
|
-
|
85
|
-
|
86
|
-
|
51
|
+
----
|
52
|
+
MD
|
53
|
+
end
|
87
54
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
55
|
+
method_names = call.method_decls.map {|decl| decl.method_name.relative }
|
56
|
+
docs = call.method_decls.map {|decl| [decl.method_name, decl.method_def.comment] }.to_h
|
57
|
+
|
58
|
+
if header
|
59
|
+
io.puts header
|
92
60
|
end
|
93
61
|
|
62
|
+
io.puts(
|
63
|
+
format_method_item_doc(method_types, method_names, docs)
|
64
|
+
)
|
65
|
+
|
66
|
+
io.string
|
67
|
+
|
94
68
|
when HoverProvider::Ruby::DefinitionContent
|
95
|
-
|
96
|
-
builder << <<EOM
|
97
|
-
```
|
98
|
-
#{content.method_name}: #{content.method_type}
|
99
|
-
```
|
100
|
-
EOM
|
101
|
-
if comments = content.definition&.comments
|
102
|
-
comments.each do |comment|
|
103
|
-
builder << comment.string
|
104
|
-
end
|
105
|
-
end
|
69
|
+
io = StringIO.new
|
106
70
|
|
107
|
-
|
108
|
-
|
71
|
+
method_name =
|
72
|
+
if content.method_name.is_a?(SingletonMethodName)
|
73
|
+
"self.#{content.method_name.method_name}"
|
74
|
+
else
|
75
|
+
content.method_name.method_name
|
109
76
|
end
|
77
|
+
|
78
|
+
prefix_size = "def ".size + method_name.size
|
79
|
+
method_types = content.definition.method_types
|
80
|
+
|
81
|
+
io.puts <<~MD
|
82
|
+
```rbs
|
83
|
+
def #{method_name}: #{method_types.join("\n" + " "*prefix_size + "| ") }
|
84
|
+
```
|
85
|
+
|
86
|
+
----
|
87
|
+
MD
|
88
|
+
|
89
|
+
if content.definition.method_types.size > 1
|
90
|
+
io.puts "**Internal method type**"
|
91
|
+
io.puts <<~MD
|
92
|
+
```rbs
|
93
|
+
#{content.method_type}
|
94
|
+
```
|
95
|
+
|
96
|
+
----
|
97
|
+
MD
|
110
98
|
end
|
99
|
+
|
100
|
+
io.puts format_comments(
|
101
|
+
content.definition.comments.map {|comment|
|
102
|
+
[content.method_name.relative.to_s, comment] #: [String, RBS::AST::Comment?]
|
103
|
+
}
|
104
|
+
)
|
105
|
+
|
106
|
+
io.string
|
111
107
|
when HoverProvider::Ruby::ConstantContent
|
112
|
-
|
108
|
+
io = StringIO.new
|
109
|
+
|
110
|
+
decl_summary =
|
113
111
|
case
|
114
112
|
when decl = content.class_decl
|
115
|
-
|
116
|
-
```rbs
|
117
|
-
#{declaration_summary(decl.primary.decl)}
|
118
|
-
```
|
119
|
-
EOM
|
113
|
+
declaration_summary(decl.primary.decl)
|
120
114
|
when decl = content.constant_decl
|
121
|
-
|
122
|
-
```rbs
|
123
|
-
#{content.full_name}: #{content.type}
|
124
|
-
```
|
125
|
-
EOM
|
115
|
+
declaration_summary(decl.decl)
|
126
116
|
when decl = content.class_alias
|
127
|
-
|
128
|
-
```rbs
|
129
|
-
#{decl.is_a?(::RBS::Environment::ClassAliasEntry) ? "class" : "module"} #{decl.decl.new_name} = #{decl.decl.old_name}
|
130
|
-
```
|
131
|
-
EOM
|
117
|
+
declaration_summary(decl.decl)
|
132
118
|
end
|
133
119
|
|
134
|
-
|
135
|
-
|
136
|
-
|
120
|
+
io.puts <<~MD
|
121
|
+
```rbs
|
122
|
+
#{decl_summary}
|
123
|
+
```
|
124
|
+
MD
|
125
|
+
|
126
|
+
comments = content.comments.map {|comment|
|
127
|
+
[content.full_name.relative!.to_s, comment] #: [String, RBS::AST::Comment?]
|
128
|
+
}
|
129
|
+
|
130
|
+
unless comments.all?(&:nil?)
|
131
|
+
io.puts "----"
|
132
|
+
io.puts format_comments(comments)
|
137
133
|
end
|
134
|
+
|
135
|
+
io.string
|
138
136
|
when HoverProvider::Ruby::TypeContent
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
```
|
146
|
-
EOM
|
147
|
-
if comment = content.decl.comment
|
148
|
-
builder << comment.string
|
149
|
-
end
|
150
|
-
end
|
137
|
+
<<~MD
|
138
|
+
```rbs
|
139
|
+
#{content.type}
|
140
|
+
```
|
141
|
+
MD
|
142
|
+
|
151
143
|
when HoverProvider::Ruby::TypeAssertionContent
|
152
|
-
|
153
|
-
|
154
|
-
|
144
|
+
<<~MD
|
145
|
+
```rbs
|
146
|
+
#{content.asserted_type}
|
147
|
+
```
|
155
148
|
|
156
|
-
↑ Converted from `#{content.original_type.to_s}`
|
157
|
-
|
149
|
+
↑ Converted from `#{content.original_type.to_s}`
|
150
|
+
MD
|
151
|
+
|
152
|
+
when HoverProvider::RBS::TypeAliasContent, HoverProvider::RBS::InterfaceContent
|
153
|
+
io = StringIO.new()
|
154
|
+
|
155
|
+
io.puts <<~MD
|
156
|
+
```rbs
|
157
|
+
#{declaration_summary(content.decl)}
|
158
|
+
```
|
159
|
+
MD
|
160
|
+
|
161
|
+
if comment = content.decl.comment
|
162
|
+
io.puts
|
163
|
+
io.puts "----"
|
164
|
+
|
165
|
+
io.puts format_comment(comment, header: content.decl.name.relative!.to_s)
|
158
166
|
end
|
167
|
+
|
168
|
+
io.string
|
169
|
+
|
159
170
|
when HoverProvider::RBS::ClassContent
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
171
|
+
io = StringIO.new
|
172
|
+
|
173
|
+
io << <<~MD
|
174
|
+
```rbs
|
175
|
+
#{declaration_summary(content.decl)}
|
176
|
+
```
|
177
|
+
MD
|
178
|
+
|
179
|
+
if content.decl.comment
|
180
|
+
io.puts "----"
|
181
|
+
|
182
|
+
class_name =
|
183
|
+
case content.decl
|
184
|
+
when RBS::AST::Declarations::ModuleAlias, RBS::AST::Declarations::ClassAlias
|
185
|
+
content.decl.new_name
|
186
|
+
when RBS::AST::Declarations::Class, RBS::AST::Declarations::Module
|
187
|
+
content.decl.name
|
188
|
+
else
|
189
|
+
raise
|
190
|
+
end
|
191
|
+
|
192
|
+
io << format_comments([[class_name.relative!.to_s, content.decl.comment]])
|
180
193
|
end
|
194
|
+
|
195
|
+
io.string
|
181
196
|
else
|
182
197
|
raise content.class.to_s
|
183
198
|
end
|
184
199
|
end
|
185
200
|
|
186
|
-
def
|
187
|
-
|
201
|
+
def format_completion_docs(item)
|
202
|
+
case item
|
203
|
+
when Services::CompletionProvider::LocalVariableItem
|
204
|
+
local_variable(item.identifier, item.type)
|
205
|
+
when Services::CompletionProvider::ConstantItem
|
206
|
+
io = StringIO.new
|
207
|
+
|
208
|
+
io.puts <<~MD
|
209
|
+
```rbs
|
210
|
+
#{declaration_summary(item.decl)}
|
211
|
+
```
|
212
|
+
MD
|
213
|
+
|
214
|
+
unless item.comments.all?(&:nil?)
|
215
|
+
io.puts "----"
|
216
|
+
io.puts format_comments(
|
217
|
+
item.comments.map {|comment|
|
218
|
+
[item.full_name.relative!.to_s, comment] #: [String, RBS::AST::Comment?]
|
219
|
+
}
|
220
|
+
)
|
221
|
+
end
|
222
|
+
|
223
|
+
io.string
|
224
|
+
when Services::CompletionProvider::InstanceVariableItem
|
225
|
+
instance_variable(item.identifier, item.type)
|
226
|
+
when Services::CompletionProvider::SimpleMethodNameItem
|
227
|
+
format_method_item_doc(item.method_types, [], { item.method_name => item.method_member.comment })
|
228
|
+
when Services::CompletionProvider::ComplexMethodNameItem
|
229
|
+
method_names = item.method_names.map(&:relative).uniq
|
230
|
+
comments = item.method_definitions.transform_values {|member| member.comment }
|
231
|
+
format_method_item_doc(item.method_types, method_names, comments)
|
232
|
+
when Services::CompletionProvider::GeneratedMethodNameItem
|
233
|
+
format_method_item_doc(item.method_types, [], {}, "🤖 Generated method for receiver type")
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def format_rbs_completion_docs(type_name, decl, comments)
|
238
|
+
io = StringIO.new
|
239
|
+
|
240
|
+
io.puts <<~MD
|
241
|
+
```rbs
|
242
|
+
#{declaration_summary(decl)}
|
243
|
+
```
|
244
|
+
MD
|
245
|
+
|
246
|
+
unless comments.empty?
|
247
|
+
io.puts
|
248
|
+
io.puts "----"
|
188
249
|
|
189
|
-
|
190
|
-
|
191
|
-
|
250
|
+
io.puts format_comments(
|
251
|
+
comments.map {|comment|
|
252
|
+
[type_name.relative!.to_s, comment] #: [String, RBS::AST::Comment?]
|
253
|
+
}
|
254
|
+
)
|
255
|
+
end
|
256
|
+
|
257
|
+
io.string
|
258
|
+
end
|
259
|
+
|
260
|
+
def format_comments(comments)
|
261
|
+
io = StringIO.new
|
262
|
+
|
263
|
+
with_docs = [] #: Array[[String, RBS::AST::Comment]]
|
264
|
+
without_docs = [] #: Array[String]
|
265
|
+
|
266
|
+
comments.each do |title, comment|
|
267
|
+
if comment
|
268
|
+
with_docs << [title, comment]
|
192
269
|
else
|
193
|
-
|
270
|
+
without_docs << title
|
194
271
|
end
|
272
|
+
end
|
195
273
|
|
196
|
-
|
197
|
-
|
274
|
+
unless with_docs.empty?
|
275
|
+
with_docs.each do |title, comment|
|
276
|
+
io.puts format_comment(comment, header: title)
|
277
|
+
io.puts
|
278
|
+
end
|
279
|
+
|
280
|
+
unless without_docs.empty?
|
281
|
+
io.puts
|
282
|
+
io.puts "----"
|
283
|
+
if without_docs.size == 1
|
284
|
+
io.puts "🔍 One more definition without docs"
|
285
|
+
else
|
286
|
+
io.puts "🔍 #{without_docs.size} more definitions without docs"
|
287
|
+
end
|
288
|
+
end
|
198
289
|
end
|
199
290
|
|
200
|
-
|
291
|
+
io.string
|
201
292
|
end
|
202
293
|
|
203
|
-
def
|
204
|
-
|
205
|
-
|
294
|
+
def format_comment(comment, header: nil, &block)
|
295
|
+
return unless comment
|
296
|
+
|
297
|
+
io = StringIO.new
|
298
|
+
if header
|
299
|
+
io.puts "### 📚 #{header}"
|
300
|
+
io.puts
|
301
|
+
end
|
302
|
+
io.puts comment.string.rstrip.gsub(/^[ \t]*<!--(?~-->)-->\n/, "").gsub(/\A([ \t]*\n)+/, "")
|
303
|
+
|
304
|
+
if block
|
305
|
+
yield io.string
|
206
306
|
else
|
207
|
-
|
307
|
+
io.string
|
208
308
|
end
|
209
309
|
end
|
210
310
|
|
311
|
+
def local_variable(name, type)
|
312
|
+
<<~MD
|
313
|
+
**Local variable** `#{name}: #{type}`
|
314
|
+
MD
|
315
|
+
end
|
316
|
+
|
317
|
+
def instance_variable(name, type)
|
318
|
+
<<~MD
|
319
|
+
**Instance variable** `#{name}: #{type}`
|
320
|
+
MD
|
321
|
+
end
|
322
|
+
|
211
323
|
def name_and_params(name, params)
|
212
324
|
if params.empty?
|
213
325
|
"#{name}"
|
@@ -238,28 +350,70 @@ EOM
|
|
238
350
|
end
|
239
351
|
end
|
240
352
|
|
353
|
+
def name_and_args(name, args)
|
354
|
+
if args.empty?
|
355
|
+
"#{name}"
|
356
|
+
else
|
357
|
+
"#{name}[#{args.map(&:to_s).join(", ")}]"
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
241
361
|
def declaration_summary(decl)
|
362
|
+
# Note that all names in the declarations is absolute
|
242
363
|
case decl
|
243
364
|
when RBS::AST::Declarations::Class
|
244
365
|
super_class = if super_class = decl.super_class
|
245
366
|
" < #{name_and_args(super_class.name, super_class.args)}"
|
246
367
|
end
|
247
|
-
"class #{name_and_params(decl.name
|
368
|
+
"class #{name_and_params(decl.name.relative!, decl.type_params)}#{super_class}"
|
248
369
|
when RBS::AST::Declarations::Module
|
249
370
|
self_type = unless decl.self_types.empty?
|
250
371
|
" : #{decl.self_types.map {|s| name_and_args(s.name, s.args) }.join(", ")}"
|
251
372
|
end
|
252
|
-
"module #{name_and_params(decl.name
|
373
|
+
"module #{name_and_params(decl.name.relative!, decl.type_params)}#{self_type}"
|
253
374
|
when RBS::AST::Declarations::TypeAlias
|
254
|
-
"type #{decl.name} = #{decl.type}"
|
375
|
+
"type #{name_and_params(decl.name.relative!, decl.type_params)} = #{decl.type}"
|
255
376
|
when RBS::AST::Declarations::Interface
|
256
|
-
"interface #{name_and_params(decl.name
|
377
|
+
"interface #{name_and_params(decl.name.relative!, decl.type_params)}"
|
257
378
|
when RBS::AST::Declarations::ClassAlias
|
258
|
-
"class #{decl.new_name} = #{decl.old_name}"
|
379
|
+
"class #{decl.new_name.relative!} = #{decl.old_name}"
|
259
380
|
when RBS::AST::Declarations::ModuleAlias
|
260
|
-
"module #{decl.new_name} = #{decl.old_name}"
|
381
|
+
"module #{decl.new_name.relative!} = #{decl.old_name}"
|
382
|
+
when RBS::AST::Declarations::Global
|
383
|
+
"#{decl.name}: #{decl.type}"
|
384
|
+
when RBS::AST::Declarations::Constant
|
385
|
+
"#{decl.name.relative!}: #{decl.type}"
|
261
386
|
end
|
262
387
|
end
|
388
|
+
|
389
|
+
def format_method_item_doc(method_types, method_names, comments, footer = "")
|
390
|
+
io = StringIO.new
|
391
|
+
|
392
|
+
io.puts "**Method type**:"
|
393
|
+
io.puts "```rbs"
|
394
|
+
if method_types.size == 1
|
395
|
+
io.puts method_types[0].to_s
|
396
|
+
else
|
397
|
+
io.puts " #{method_types.join("\n| ")}"
|
398
|
+
end
|
399
|
+
io.puts "```"
|
400
|
+
|
401
|
+
if method_names.size > 1
|
402
|
+
io.puts "**Possible methods**: #{method_names.map {|type| "`#{type.to_s}`" }.join(", ")}"
|
403
|
+
io.puts
|
404
|
+
end
|
405
|
+
|
406
|
+
unless comments.each_value.all?(&:nil?)
|
407
|
+
io.puts "----"
|
408
|
+
io.puts format_comments(comments.transform_keys {|name| name.relative.to_s }.entries)
|
409
|
+
end
|
410
|
+
|
411
|
+
unless footer.empty?
|
412
|
+
io.puts footer.rstrip
|
413
|
+
end
|
414
|
+
|
415
|
+
io.string
|
416
|
+
end
|
263
417
|
end
|
264
418
|
end
|
265
419
|
end
|
data/lib/steep/server/master.rb
CHANGED
@@ -548,6 +548,9 @@ module Steep
|
|
548
548
|
trigger_characters: [".", "@", ":"],
|
549
549
|
work_done_progress: true
|
550
550
|
),
|
551
|
+
signature_help_provider: {
|
552
|
+
triggerCharacters: ["("]
|
553
|
+
},
|
551
554
|
workspace_symbol_provider: true,
|
552
555
|
definition_provider: true,
|
553
556
|
declaration_provider: false,
|
@@ -596,7 +599,7 @@ module Steep
|
|
596
599
|
controller.update_priority(close: path)
|
597
600
|
end
|
598
601
|
|
599
|
-
when "textDocument/hover", "textDocument/completion"
|
602
|
+
when "textDocument/hover", "textDocument/completion", "textDocument/signatureHelp"
|
600
603
|
if interaction_worker
|
601
604
|
if path = pathname(message[:params][:textDocument][:uri])
|
602
605
|
result_controller << send_request(method: message[:method], params: message[:params], worker: interaction_worker) do |handler|
|