steep 0.52.0 → 0.52.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f6846cc048d7515ef655b81cf7f399e5852a96c390d90c7f859c07cc6a5c0f94
4
- data.tar.gz: fecb8b0b0be4e338f3ccf0375064f39167693c0b25b68e3b4ea2f103f117260f
3
+ metadata.gz: c373c6a4366397b2b014850a8993ce6d8a0c27bcdd8bc8c5135bb64b4132b162
4
+ data.tar.gz: '097245d5e3745ceb634a2f2bcd007cca0c549449c835b644feb62c23c3c6e3ed'
5
5
  SHA512:
6
- metadata.gz: 9c08fa692eda8915ef4ce4fe725aecb2a9823c2da188900a3811fdf1d02c6b0369dae8755aed83fe9cc80a376c3309df29efcf849681cd2a9a249559ff6c3ae2
7
- data.tar.gz: 77953f34eb6ab001dfee4f03e0ec1250f1128fedae9e5b427e0037b97b6b809c5bb8ae4c94d9d93f3af3a702de7dd33a182399fe2b7a81ba8ac0d2708c422579
6
+ metadata.gz: eadc49c7c81bce14e4e4c5f9adbcb0411669cd1f78eb290029953e26ea3997d15a65db53329124812e841f3fbe486d30f25bbf89d7bce74d528692176250c795
7
+ data.tar.gz: 3257fa14e2888e68afd6266e1c1d28dc95b8ef7e1ec499393994f35cb715abfc133f005d0ce79962fc6642f7fdeb0b28c35fa0d3bb5687972ea1b31d0a9d6c38
@@ -1,8 +1,15 @@
1
1
  version: 2
2
+
2
3
  updates:
3
- - package-ecosystem: bundler
4
- directory: "/"
5
- schedule:
6
- interval: daily
7
- time: "20:00"
8
- open-pull-requests-limit: 10
4
+ - package-ecosystem: bundler
5
+ directory: "/"
6
+ schedule:
7
+ interval: daily
8
+ time: "20:00"
9
+ open-pull-requests-limit: 10
10
+
11
+ - package-ecosystem: "github-actions"
12
+ directory: "/"
13
+ schedule:
14
+ # Check for updates to GitHub Actions every weekday
15
+ interval: "daily"
@@ -14,6 +14,7 @@ jobs:
14
14
  container_tag:
15
15
  - "2.7"
16
16
  - "3.0"
17
+ - "3.1"
17
18
  - "master-nightly-focal"
18
19
  task:
19
20
  - test
@@ -22,9 +23,10 @@ jobs:
22
23
  container:
23
24
  image: rubylang/ruby:${{ matrix.container_tag }}
24
25
  steps:
25
- - uses: actions/checkout@v1
26
+ - uses: actions/checkout@v3
26
27
  - name: Run test
27
28
  run: |
29
+ git config --global --add safe.directory /__w/steep/steep
28
30
  ruby -v
29
31
  gem install bundler
30
32
  bundle install --jobs 4 --retry 3
data/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.52.1 (2022-04-25)
6
+
7
+ * Better union type inference (it type checks `Array#filter_map` now!) ([\#531](https://github.com/soutaro/steep/pull/531))
8
+ * Improve method call hover message ([\#537](https://github.com/soutaro/steep/pull/537), [\#538](https://github.com/soutaro/steep/pull/538))
9
+ * Make `NilClass#!` a special method to improve flow-sensitive typing ([\#539](https://github.com/soutaro/steep/pull/539))
10
+ * Fix `steep binstub` ([\#540](https://github.com/soutaro/steep/pull/540), [\#541](https://github.com/soutaro/steep/pull/541))
11
+
5
12
  ## 0.52.0 (2022-04-05)
6
13
 
7
14
  * Add `steep binstub` command ([\#527](https://github.com/soutaro/steep/pull/527))
data/Gemfile.lock CHANGED
@@ -1,14 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- steep (0.52.0)
4
+ steep (0.52.1)
5
5
  activesupport (>= 5.1)
6
6
  language_server-protocol (>= 3.15, < 4.0)
7
7
  listen (~> 3.0)
8
8
  parallel (>= 1.0.0)
9
9
  parser (>= 3.0)
10
10
  rainbow (>= 2.2.2, < 4.0)
11
- rbs (>= 2.3.0)
11
+ rbs (>= 2.3.2)
12
12
  terminal-table (>= 2, < 4)
13
13
 
14
14
  PATH
@@ -44,14 +44,14 @@ GEM
44
44
  minitest-slow_test (0.2.0)
45
45
  minitest (>= 5.0)
46
46
  parallel (1.22.1)
47
- parser (3.1.1.0)
47
+ parser (3.1.2.0)
48
48
  ast (~> 2.4.1)
49
49
  rainbow (3.1.1)
50
50
  rake (13.0.6)
51
51
  rb-fsevent (0.11.1)
52
52
  rb-inotify (0.10.1)
53
53
  ffi (~> 1.0)
54
- rbs (2.3.1)
54
+ rbs (2.3.2)
55
55
  stackprof (0.2.19)
56
56
  terminal-table (3.0.2)
57
57
  unicode-display_width (>= 1.1.1, < 3)
@@ -431,7 +431,8 @@ module Steep
431
431
  case defined_in
432
432
  when RBS::BuiltinNames::BasicObject.name,
433
433
  RBS::BuiltinNames::TrueClass.name,
434
- RBS::BuiltinNames::FalseClass.name
434
+ RBS::BuiltinNames::FalseClass.name,
435
+ AST::Builtin::NilClass.module_name
435
436
  return method_type.with(
436
437
  type: method_type.type.with(
437
438
  return_type: AST::Types::Logic::Not.new(location: method_type.type.return_type.location)
data/lib/steep/cli.rb CHANGED
@@ -210,6 +210,7 @@ module Steep
210
210
 
211
211
  def process_binstub
212
212
  path = Pathname("bin/steep")
213
+ root_path = Pathname.pwd
213
214
  force = false
214
215
 
215
216
  OptionParser.new do |opts|
@@ -227,21 +228,28 @@ BANNER
227
228
  path = Pathname(v)
228
229
  end
229
230
 
231
+ opts.on("--root=PATH", "The repository root path (defaults to `.`)") do |v|
232
+ root_path = (Pathname.pwd + v).cleanpath
233
+ end
234
+
230
235
  opts.on("--[no-]force", "Overwrite file (defaults to false)") do
231
236
  force = true
232
237
  end
233
238
  end.parse!(argv)
234
239
 
235
- path.parent.mkpath
240
+ binstub_path = (Pathname.pwd + path).cleanpath
241
+ bindir_path = binstub_path.parent
242
+
243
+ bindir_path.mkpath
236
244
 
237
245
  gemfile_path =
238
246
  if defined?(Bundler)
239
- Bundler.default_gemfile.relative_path_from(Pathname.pwd + path.parent)
247
+ Bundler.default_gemfile.relative_path_from(bindir_path)
240
248
  else
241
249
  Pathname("../Gemfile")
242
250
  end
243
251
 
244
- if path.file?
252
+ if binstub_path.file?
245
253
  if force
246
254
  stdout.puts Rainbow("#{path} already exists. Overwriting...").yellow
247
255
  else
@@ -254,7 +262,8 @@ BANNER
254
262
  #!/usr/bin/env bash
255
263
 
256
264
  BINSTUB_DIR=$(cd $(dirname $0); pwd)
257
- GEMFILE=${BINSTUB_DIR}/#{gemfile_path}
265
+ GEMFILE=$(readlink -f ${BINSTUB_DIR}/#{gemfile_path})
266
+ ROOT_DIR=$(readlink -f ${BINSTUB_DIR}/#{root_path.relative_path_from(bindir_path)})
258
267
 
259
268
  STEEP="bundle exec --gemfile=${GEMFILE} steep"
260
269
 
@@ -262,15 +271,15 @@ if type "rbenv" > /dev/null 2>&1; then
262
271
  STEEP="rbenv exec ${STEEP}"
263
272
  else
264
273
  if type "rvm" > /dev/null 2>&1; then
265
- STEEP="rvm ${REPO_ROOT} do ${STEEP}"
274
+ STEEP="rvm ${ROOT_DIR} do ${STEEP}"
266
275
  fi
267
276
  fi
268
277
 
269
278
  exec $STEEP $@
270
279
  TEMPLATE
271
280
 
272
- path.write(template)
273
- path.chmod(0755)
281
+ binstub_path.write(template)
282
+ binstub_path.chmod(0755)
274
283
 
275
284
  stdout.puts Rainbow("Successfully generated executable #{path} 🎉").blue
276
285
 
@@ -78,8 +78,7 @@ module Steep
78
78
  Steep.measure "Generating hover response" do
79
79
  Steep.logger.info { "path=#{job.path}, line=#{job.line}, column=#{job.column}" }
80
80
 
81
- hover = Services::HoverContent.new(service: service)
82
- content = hover.content_for(path: job.path, line: job.line, column: job.column)
81
+ content = Services::HoverProvider.content_for(service: service, path: job.path, line: job.line, column: job.column)
83
82
  if content
84
83
  range = content.location.yield_self do |location|
85
84
  lsp_range = location.as_lsp_range
@@ -89,7 +88,10 @@ module Steep
89
88
  end
90
89
 
91
90
  LSP::Interface::Hover.new(
92
- contents: { kind: "markdown", value: format_hover(content)&.gsub(/<!--(?~-->)-->/, "") },
91
+ contents: {
92
+ kind: "markdown",
93
+ value: LSPFormatter.format_hover_content(content).to_s
94
+ },
93
95
  range: range
94
96
  )
95
97
  end
@@ -100,101 +102,6 @@ module Steep
100
102
  end
101
103
  end
102
104
 
103
- def format_hover(content)
104
- case content
105
- when Services::HoverContent::TypeAliasContent
106
- comment = content.decl.comment&.string || ''
107
-
108
- <<-MD
109
- #{comment}
110
-
111
- ```rbs
112
- #{retrieve_decl_information(content.decl)}
113
- ```
114
- MD
115
- when Services::HoverContent::InterfaceContent
116
- comment = content.decl.comment&.string || ''
117
-
118
- <<-MD
119
- #{comment}
120
-
121
- ```rbs
122
- #{retrieve_decl_information(content.decl)}
123
- ```
124
- MD
125
- when Services::HoverContent::ClassContent
126
- comment = content.decl.comment&.string || ''
127
-
128
- <<-MD
129
- #{comment}
130
-
131
- ```rbs
132
- #{retrieve_decl_information(content.decl)}
133
- ```
134
- MD
135
- when Services::HoverContent::VariableContent
136
- "`#{content.name}`: `#{content.type.to_s}`"
137
- when Services::HoverContent::MethodCallContent
138
- method_name = case content.method_name
139
- when Services::HoverContent::InstanceMethodName
140
- "#{content.method_name.class_name}##{content.method_name.method_name}"
141
- when Services::HoverContent::SingletonMethodName
142
- "#{content.method_name.class_name}.#{content.method_name.method_name}"
143
- else
144
- nil
145
- end
146
-
147
- if method_name
148
- string = <<HOVER
149
- ```
150
- #{method_name} ~> #{content.type}
151
- ```
152
- HOVER
153
- if content.definition
154
- if content.definition.comments
155
- string << "\n----\n\n#{content.definition.comments.map(&:string).join("\n\n")}"
156
- end
157
-
158
- string << "\n----\n\n#{content.definition.method_types.map {|x| "- `#{x}`\n" }.join()}"
159
- end
160
- else
161
- "`#{content.type}`"
162
- end
163
- when Services::HoverContent::DefinitionContent
164
- string = <<HOVER
165
- ```
166
- def #{content.method_name}: #{content.method_type}
167
- ```
168
- HOVER
169
- if (comment = content.comment_string)
170
- string << "\n----\n\n#{comment}\n"
171
- end
172
-
173
- if content.definition.method_types.size > 1
174
- string << "\n----\n\n#{content.definition.method_types.map {|x| "- `#{x}`\n" }.join()}"
175
- end
176
-
177
- string
178
- when Services::HoverContent::ConstantContent
179
- ss = []
180
- if content.class_or_module?
181
- ss << ["```rbs", retrieve_decl_information(content.decl.primary.decl), "```"].join("\n")
182
- end
183
-
184
- if content.constant?
185
- ss << ["```rbs", "#{content.full_name}: #{content.type}", "```"].join("\n")
186
- end
187
-
188
- if s = content.comment_string
189
- ss << s
190
- end
191
-
192
- ss.join("\n\n----\n\n")
193
- when Services::HoverContent::TypeContent
194
- "`#{content.type}`"
195
- end
196
- end
197
-
198
105
  def process_completion(job)
199
106
  Steep.logger.tagged("#response_to_completion") do
200
107
  Steep.measure "Generating response" do
@@ -356,59 +263,6 @@ HOVER
356
263
  end
357
264
  end
358
265
 
359
- def name_and_params(name, params)
360
- if params.empty?
361
- "#{name}"
362
- else
363
- ps = params.each.map do |param|
364
- s = ""
365
- if param.unchecked?
366
- s << "unchecked "
367
- end
368
- case param.variance
369
- when :invariant
370
- # nop
371
- when :covariant
372
- s << "out "
373
- when :contravariant
374
- s << "in "
375
- end
376
- s + param.name.to_s
377
- end
378
-
379
- "#{name}[#{ps.join(", ")}]"
380
- end
381
- end
382
-
383
- def name_and_args(name, args)
384
- if name && args
385
- if args.empty?
386
- "#{name}"
387
- else
388
- "#{name}[#{args.join(", ")}]"
389
- end
390
- end
391
- end
392
-
393
- def retrieve_decl_information(decl)
394
- case decl
395
- when RBS::AST::Declarations::Class
396
- super_class = if super_class = decl.super_class
397
- " < #{name_and_args(super_class.name, super_class.args)}"
398
- end
399
- "class #{name_and_params(decl.name, decl.type_params)}#{super_class}"
400
- when RBS::AST::Declarations::Module
401
- self_type = unless decl.self_types.empty?
402
- " : #{decl.self_types.join(", ")}"
403
- end
404
- "module #{name_and_params(decl.name, decl.type_params)}#{self_type}"
405
- when RBS::AST::Declarations::Alias
406
- "type #{decl.name} = #{decl.type}"
407
- when RBS::AST::Declarations::Interface
408
- "interface #{name_and_params(decl.name, decl.type_params)}"
409
- end
410
- end
411
-
412
266
  def format_completion_item(item)
413
267
  range = LanguageServer::Protocol::Interface::Range.new(
414
268
  start: LanguageServer::Protocol::Interface::Position.new(
@@ -0,0 +1,234 @@
1
+ module Steep
2
+ module Server
3
+ module LSPFormatter
4
+ include Services
5
+
6
+ class CommentBuilder
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
16
+
17
+ def to_s
18
+ unless @array.empty?
19
+ @array.join("\n\n----\n\n")
20
+ end
21
+ end
22
+
23
+ def <<(string)
24
+ if string
25
+ s = string.rstrip.gsub(/^[ \t]*<!--(?~-->)-->\n/, "").gsub(/\A([ \t]*\n)+/, "")
26
+ unless @array.include?(s)
27
+ @array << s
28
+ end
29
+ end
30
+ end
31
+
32
+ def push
33
+ s = ""
34
+ yield s
35
+ self << s
36
+ end
37
+ end
38
+
39
+ module_function
40
+
41
+ def format_hover_content(content)
42
+ case content
43
+ when HoverProvider::Ruby::VariableContent
44
+ "`#{content.name}`: `#{content.type.to_s}`"
45
+
46
+ when HoverProvider::Ruby::MethodCallContent
47
+ CommentBuilder.build do |builder|
48
+ call = content.method_call
49
+ builder.push do |s|
50
+ case call
51
+ when TypeInference::MethodCall::Typed
52
+ mt = call.actual_method_type.with(
53
+ type: call.actual_method_type.type.with(return_type: call.return_type)
54
+ )
55
+ s << "```rbs\n#{mt.to_s}\n```\n\n"
56
+ when TypeInference::MethodCall::Error
57
+ s << "```rbs\n( ??? ) -> #{call.return_type.to_s}\n```\n\n"
58
+ end
59
+
60
+ s << to_list(call.method_decls) do |decl|
61
+ "`#{decl.method_name}`"
62
+ end
63
+ end
64
+
65
+ call.method_decls.each do |decl|
66
+ if comment = decl.method_def.comment
67
+ builder << <<EOM
68
+ **#{decl.method_name.to_s}**
69
+
70
+ ```rbs
71
+ #{decl.method_type}
72
+ ```
73
+
74
+ #{decl.method_def.comment.string.gsub(/\A([ \t]*\n)+/, "")}
75
+ EOM
76
+ end
77
+ end
78
+ end
79
+
80
+ when HoverProvider::Ruby::DefinitionContent
81
+ CommentBuilder.build do |builder|
82
+ builder << <<EOM
83
+ ```
84
+ #{content.method_name}: #{content.method_type}
85
+ ```
86
+ EOM
87
+ if comments = content.definition&.comments
88
+ comments.each do |comment|
89
+ builder << comment.string
90
+ end
91
+ end
92
+
93
+ if content.definition.method_types.size > 1
94
+ builder << to_list(content.definition.method_types) {|type| "`#{type.to_s}`" }
95
+ end
96
+ end
97
+ when HoverProvider::Ruby::ConstantContent
98
+ CommentBuilder.build do |builder|
99
+ if content.class_or_module?
100
+ builder << <<EOM
101
+ ```rbs
102
+ #{declaration_summary(content.decl.primary.decl)}
103
+ ```
104
+ EOM
105
+ end
106
+
107
+ if content.constant?
108
+ builder << <<EOM
109
+ ```rbs
110
+ #{content.full_name}: #{content.type}
111
+ ```
112
+ EOM
113
+ end
114
+
115
+ content.comments.each do |comment|
116
+ builder << comment.string
117
+ end
118
+ end
119
+ when HoverProvider::Ruby::TypeContent
120
+ "`#{content.type}`"
121
+ when HoverProvider::RBS::TypeAliasContent
122
+ CommentBuilder.build do |builder|
123
+ builder << <<EOM
124
+ ```rbs
125
+ #{declaration_summary(content.decl)}
126
+ ```
127
+ EOM
128
+ if comment = content.decl.comment
129
+ builder << comment.string
130
+ end
131
+ end
132
+ when HoverProvider::RBS::ClassContent
133
+ CommentBuilder.build do |builder|
134
+ builder << <<EOM
135
+ ```rbs
136
+ #{declaration_summary(content.decl)}
137
+ ```
138
+ EOM
139
+ if comment = content.decl.comment
140
+ builder << comment.string
141
+ end
142
+ end
143
+ when HoverProvider::RBS::InterfaceContent
144
+ CommentBuilder.build do |builder|
145
+ builder << <<EOM
146
+ ```rbs
147
+ #{declaration_summary(content.decl)}
148
+ ```
149
+ EOM
150
+ if comment = content.decl.comment
151
+ builder << comment.string
152
+ end
153
+ end
154
+ else
155
+ raise content.class.to_s
156
+ end
157
+ end
158
+
159
+ def to_list(collection, &block)
160
+ buffer = ""
161
+
162
+ strings =
163
+ if block
164
+ collection.map(&block)
165
+ else
166
+ collection.map(&:to_s)
167
+ end
168
+
169
+ strings.each do |s|
170
+ buffer << "- #{s}\n"
171
+ end
172
+
173
+ buffer
174
+ end
175
+
176
+ def name_and_args(name, args)
177
+ if args.empty?
178
+ "#{name}"
179
+ else
180
+ "#{name}[#{args.map(&:to_s).join(", ")}]"
181
+ end
182
+ end
183
+
184
+ def name_and_params(name, params)
185
+ if params.empty?
186
+ "#{name}"
187
+ else
188
+ ps = params.each.map do |param|
189
+ s = ""
190
+ if param.unchecked?
191
+ s << "unchecked "
192
+ end
193
+ case param.variance
194
+ when :invariant
195
+ # nop
196
+ when :covariant
197
+ s << "out "
198
+ when :contravariant
199
+ s << "in "
200
+ end
201
+ s << param.name.to_s
202
+
203
+ if param.upper_bound
204
+ s << " < #{param.upper_bound.to_s}"
205
+ end
206
+
207
+ s
208
+ end
209
+
210
+ "#{name}[#{ps.join(", ")}]"
211
+ end
212
+ end
213
+
214
+ def declaration_summary(decl)
215
+ case decl
216
+ when RBS::AST::Declarations::Class
217
+ super_class = if super_class = decl.super_class
218
+ " < #{name_and_args(super_class.name, super_class.args)}"
219
+ end
220
+ "class #{name_and_params(decl.name, decl.type_params)}#{super_class}"
221
+ when RBS::AST::Declarations::Module
222
+ self_type = unless decl.self_types.empty?
223
+ " : #{decl.self_types.map {|s| name_and_args(s.name, s.args) }.join(", ")}"
224
+ end
225
+ "module #{name_and_params(decl.name, decl.type_params)}#{self_type}"
226
+ when RBS::AST::Declarations::Alias
227
+ "type #{decl.name} = #{decl.type}"
228
+ when RBS::AST::Declarations::Interface
229
+ "interface #{name_and_params(decl.name, decl.type_params)}"
230
+ end
231
+ end
232
+ end
233
+ end
234
+ end
@@ -0,0 +1,63 @@
1
+ module Steep
2
+ module Services
3
+ module HoverProvider
4
+ class RBS
5
+ TypeAliasContent = Struct.new(:location, :decl, keyword_init: true)
6
+ ClassContent = Struct.new(:location, :decl, keyword_init: true)
7
+ InterfaceContent = Struct.new(:location, :decl, keyword_init: true)
8
+
9
+ attr_reader :service
10
+
11
+ def initialize(service:)
12
+ @service = service
13
+ end
14
+
15
+ def project
16
+ service.project
17
+ end
18
+
19
+ def content_for(target:, path:, line:, column:)
20
+ service = self.service.signature_services[target.name]
21
+
22
+ _, decls = service.latest_env.buffers_decls.find do |buffer, _|
23
+ Pathname(buffer.name) == path
24
+ end
25
+
26
+ return if decls.nil?
27
+
28
+ loc_key, path = ::RBS::Locator.new(decls: decls).find2(line: line, column: column) || return
29
+ head, *tail = path
30
+
31
+ case head
32
+ when ::RBS::Types::Alias
33
+ alias_decl = service.latest_env.alias_decls[head.name]&.decl or raise
34
+
35
+ TypeAliasContent.new(
36
+ location: head.location,
37
+ decl: alias_decl
38
+ )
39
+ when ::RBS::Types::ClassInstance, ::RBS::Types::ClassSingleton
40
+ if loc_key == :name
41
+ env = service.latest_env
42
+ class_decl = env.class_decls[head.name]&.decls[0]&.decl or raise
43
+ location = head.location[:name]
44
+ ClassContent.new(
45
+ location: location,
46
+ decl: class_decl
47
+ )
48
+ end
49
+ when ::RBS::Types::Interface
50
+ env = service.latest_env
51
+ interface_decl = env.interface_decls[head.name]&.decl or raise
52
+ location = head.location[:name]
53
+
54
+ InterfaceContent.new(
55
+ location: location,
56
+ decl: interface_decl
57
+ )
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,168 @@
1
+ module Steep
2
+ module Services
3
+ module HoverProvider
4
+ class Ruby
5
+ TypeContent = Struct.new(:node, :type, :location, keyword_init: true)
6
+ VariableContent = Struct.new(:node, :name, :type, :location, keyword_init: true)
7
+ MethodCallContent = Struct.new(:node, :method_call, :location, keyword_init: true)
8
+ DefinitionContent = Struct.new(:node, :method_name, :method_type, :definition, :location, keyword_init: true)
9
+ ConstantContent = Struct.new(:location, :full_name, :type, :decl, keyword_init: true) do
10
+ def comments
11
+ case
12
+ when class_or_module?
13
+ decl.decls.filter_map {|d| d.decl.comment }
14
+ when constant?
15
+ [decl.decl.comment]
16
+ else
17
+ []
18
+ end.compact
19
+ end
20
+
21
+ def constant?
22
+ decl.is_a?(::RBS::Environment::SingleEntry)
23
+ end
24
+
25
+ def class_or_module?
26
+ decl.is_a?(::RBS::Environment::MultiEntry)
27
+ end
28
+ end
29
+
30
+ attr_reader :service
31
+
32
+ def initialize(service:)
33
+ @service = service
34
+ end
35
+
36
+ def project
37
+ service.project
38
+ end
39
+
40
+ def method_definition_for(factory, type_name, singleton_method: nil, instance_method: nil)
41
+ case
42
+ when instance_method
43
+ factory.definition_builder.build_instance(type_name).methods[instance_method]
44
+ when singleton_method
45
+ methods = factory.definition_builder.build_singleton(type_name).methods
46
+
47
+ if singleton_method == :new
48
+ methods[:new] || methods[:initialize]
49
+ else
50
+ methods[singleton_method]
51
+ end
52
+ end
53
+ end
54
+
55
+ def typecheck(target, path:, content:, line:, column:)
56
+ subtyping = service.signature_services[target.name].current_subtyping or return
57
+ source = Source.parse(content, path: path, factory: subtyping.factory)
58
+ source = source.without_unrelated_defs(line: line, column: column)
59
+ Services::TypeCheckService.type_check(source: source, subtyping: subtyping)
60
+ rescue
61
+ nil
62
+ end
63
+
64
+ def method_name_from_method(context, builder:)
65
+ defined_in = context.method.defined_in
66
+ method_name = context.name
67
+
68
+ case
69
+ when defined_in.class?
70
+ case
71
+ when builder.build_instance(defined_in).methods.key?(method_name)
72
+ InstanceMethodName.new(type_name: defined_in, method_name: method_name)
73
+ when builder.build_singleton(defined_in).methods.key?(method_name)
74
+ SingletonMethodName.new(type_name: defined_in, method_name: method_name)
75
+ end
76
+ else
77
+ InstanceMethodName.new(type_name: defined_in, method_name: method_name)
78
+ end
79
+ end
80
+
81
+ def content_for(target:, path:, line:, column:)
82
+ file = service.source_files[path]
83
+ typing = typecheck(target, path: path, content: file.content, line: line, column: column) or return
84
+ node, *parents = typing.source.find_nodes(line: line, column: column)
85
+
86
+ if node
87
+ case node.type
88
+ when :lvar
89
+ var_name = node.children[0]
90
+ context = typing.context_at(line: line, column: column)
91
+ var_type = context.lvar_env[var_name] || AST::Types::Any.new(location: nil)
92
+
93
+ return VariableContent.new(node: node, name: var_name, type: var_type, location: node.location.name)
94
+
95
+ when :lvasgn
96
+ var_name, rhs = node.children
97
+ context = typing.context_at(line: line, column: column)
98
+ type = context.lvar_env[var_name] || typing.type_of(node: rhs)
99
+
100
+ return VariableContent.new(node: node, name: var_name, type: type, location: node.location.name)
101
+
102
+ when :send, :csend
103
+ result_node =
104
+ case parents[0]&.type
105
+ when :block, :numblock
106
+ if node == parents[0].children[0]
107
+ parents[0]
108
+ else
109
+ node
110
+ end
111
+ else
112
+ node
113
+ end
114
+
115
+ case call = typing.call_of(node: result_node)
116
+ when TypeInference::MethodCall::Typed, TypeInference::MethodCall::Error
117
+ unless call.method_decls.empty?
118
+ return MethodCallContent.new(
119
+ node: result_node,
120
+ method_call: call,
121
+ location: node.location.selector
122
+ )
123
+ end
124
+ end
125
+
126
+ when :def, :defs
127
+ context = typing.context_at(line: line, column: column)
128
+ method_context = context.method_context
129
+
130
+ if method_context && method_context.method
131
+ return DefinitionContent.new(
132
+ node: node,
133
+ method_name: method_name_from_method(method_context, builder: context.factory.definition_builder),
134
+ method_type: method_context.method_type,
135
+ definition: method_context.method,
136
+ location: node.loc.name
137
+ )
138
+ end
139
+
140
+ when :const, :casgn
141
+ context = typing.context_at(line: line, column: column)
142
+
143
+ type = typing.type_of(node: node)
144
+ const_name = typing.source_index.reference(constant_node: node)
145
+
146
+ if const_name
147
+ decl = context.env.class_decls[const_name] || context.env.constant_decls[const_name]
148
+
149
+ return ConstantContent.new(
150
+ location: node.location.name,
151
+ full_name: const_name,
152
+ type: type,
153
+ decl: decl
154
+ )
155
+ end
156
+ end
157
+
158
+ TypeContent.new(
159
+ node: node,
160
+ type: typing.type_of(node: node),
161
+ location: node.location.expression
162
+ )
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,21 @@
1
+ module Steep
2
+ module Services
3
+ module HoverProvider
4
+ module SingletonMethods
5
+ def content_for(service:, path:, line:, column:)
6
+ project = service.project
7
+ target_for_code, targets_for_sigs = project.targets_for_path(path)
8
+
9
+ case
10
+ when target_for_code
11
+ Ruby.new(service: service).content_for(target: target_for_code, path: path, line: line, column: column)
12
+ when target = targets_for_sigs.first
13
+ RBS.new(service: service).content_for(target: target, path: path, line: line, column: column)
14
+ end
15
+ end
16
+ end
17
+
18
+ extend SingletonMethods
19
+ end
20
+ end
21
+ end
@@ -347,7 +347,7 @@ module Steep
347
347
 
348
348
  when relation.super_type.is_a?(AST::Types::Union)
349
349
  Any(relation) do |result|
350
- relation.super_type.types.sort_by {|ty| (path = hole_path(ty)) ? -path.size : 1 }.each do |super_type|
350
+ relation.super_type.types.sort_by {|ty| (path = hole_path(ty)) ? -path.size : -Float::INFINITY }.each do |super_type|
351
351
  rel = Relation.new(sub_type: relation.sub_type, super_type: super_type)
352
352
  result.add(rel) do
353
353
  check_type(rel)
@@ -357,7 +357,7 @@ module Steep
357
357
 
358
358
  when relation.sub_type.is_a?(AST::Types::Intersection)
359
359
  Any(relation) do |result|
360
- relation.sub_type.types.sort_by {|ty| (path = hole_path(ty)) ? -path.size : 1 }.each do |sub_type|
360
+ relation.sub_type.types.sort_by {|ty| (path = hole_path(ty)) ? -path.size : -Float::INFINITY }.each do |sub_type|
361
361
  rel = Relation.new(sub_type: sub_type, super_type: relation.super_type)
362
362
  result.add(rel) do
363
363
  check_type(rel)
@@ -2910,6 +2910,11 @@ module Steep
2910
2910
  call = call.with_return_type(typing.type_of(node: arguments.last))
2911
2911
  end
2912
2912
  end
2913
+
2914
+ if node.type == :csend || ((node.type == :block || node.type == :numblock) && node.children[0].type == :csend)
2915
+ optional_type = AST::Types::Union.build(types: [call.return_type, AST::Builtin.nil_type])
2916
+ call = call.with_return_type(optional_type)
2917
+ end
2913
2918
  else
2914
2919
  error = Diagnostic::Ruby::UnresolvedOverloading.new(
2915
2920
  node: node,
@@ -171,6 +171,10 @@ module Steep
171
171
  )
172
172
  end
173
173
 
174
+ def factory
175
+ type_env.subtyping.factory
176
+ end
177
+
174
178
  def env
175
179
  type_env.subtyping.factory.env
176
180
  end
data/lib/steep/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Steep
2
- VERSION = "0.52.0"
2
+ VERSION = "0.52.1"
3
3
  end
data/lib/steep.rb CHANGED
@@ -92,23 +92,26 @@ require "steep/index/rbs_index"
92
92
  require "steep/index/signature_symbol_provider"
93
93
  require "steep/index/source_index"
94
94
 
95
- require "steep/server/change_buffer"
96
- require "steep/server/base_worker"
97
- require "steep/server/worker_process"
98
- require "steep/server/interaction_worker"
99
- require "steep/server/type_check_worker"
100
- require "steep/server/master"
101
-
102
95
  require "steep/services/content_change"
103
96
  require "steep/services/path_assignment"
104
97
  require "steep/services/signature_service"
105
98
  require "steep/services/type_check_service"
106
- require "steep/services/hover_content"
99
+ require "steep/services/hover_provider/singleton_methods"
100
+ require "steep/services/hover_provider/ruby"
101
+ require "steep/services/hover_provider/rbs"
107
102
  require "steep/services/completion_provider"
108
103
  require "steep/services/stats_calculator"
109
104
  require "steep/services/file_loader"
110
105
  require "steep/services/goto_service"
111
106
 
107
+ require "steep/server/lsp_formatter"
108
+ require "steep/server/change_buffer"
109
+ require "steep/server/base_worker"
110
+ require "steep/server/worker_process"
111
+ require "steep/server/interaction_worker"
112
+ require "steep/server/type_check_worker"
113
+ require "steep/server/master"
114
+
112
115
  require "steep/project"
113
116
  require "steep/project/pattern"
114
117
  require "steep/project/options"
data/steep.gemspec CHANGED
@@ -33,7 +33,7 @@ Gem::Specification.new do |spec|
33
33
  spec.add_runtime_dependency "rainbow", ">= 2.2.2", "< 4.0"
34
34
  spec.add_runtime_dependency "listen", "~> 3.0"
35
35
  spec.add_runtime_dependency "language_server-protocol", ">= 3.15", "< 4.0"
36
- spec.add_runtime_dependency "rbs", ">= 2.3.0"
36
+ spec.add_runtime_dependency "rbs", ">= 2.3.2"
37
37
  spec.add_runtime_dependency "parallel", ">= 1.0.0"
38
38
  spec.add_runtime_dependency "terminal-table", ">= 2", "< 4"
39
39
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: steep
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.52.0
4
+ version: 0.52.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Soutaro Matsumoto
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-04-05 00:00:00.000000000 Z
11
+ date: 2022-04-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -98,14 +98,14 @@ dependencies:
98
98
  requirements:
99
99
  - - ">="
100
100
  - !ruby/object:Gem::Version
101
- version: 2.3.0
101
+ version: 2.3.2
102
102
  type: :runtime
103
103
  prerelease: false
104
104
  version_requirements: !ruby/object:Gem::Requirement
105
105
  requirements:
106
106
  - - ">="
107
107
  - !ruby/object:Gem::Version
108
- version: 2.3.0
108
+ version: 2.3.2
109
109
  - !ruby/object:Gem::Dependency
110
110
  name: parallel
111
111
  requirement: !ruby/object:Gem::Requirement
@@ -233,6 +233,7 @@ files:
233
233
  - lib/steep/server/base_worker.rb
234
234
  - lib/steep/server/change_buffer.rb
235
235
  - lib/steep/server/interaction_worker.rb
236
+ - lib/steep/server/lsp_formatter.rb
236
237
  - lib/steep/server/master.rb
237
238
  - lib/steep/server/type_check_worker.rb
238
239
  - lib/steep/server/worker_process.rb
@@ -240,7 +241,9 @@ files:
240
241
  - lib/steep/services/content_change.rb
241
242
  - lib/steep/services/file_loader.rb
242
243
  - lib/steep/services/goto_service.rb
243
- - lib/steep/services/hover_content.rb
244
+ - lib/steep/services/hover_provider/rbs.rb
245
+ - lib/steep/services/hover_provider/ruby.rb
246
+ - lib/steep/services/hover_provider/singleton_methods.rb
244
247
  - lib/steep/services/path_assignment.rb
245
248
  - lib/steep/services/signature_service.rb
246
249
  - lib/steep/services/stats_calculator.rb
@@ -1,254 +0,0 @@
1
- module Steep
2
- module Services
3
- class HoverContent
4
- TypeContent = Struct.new(:node, :type, :location, keyword_init: true)
5
- VariableContent = Struct.new(:node, :name, :type, :location, keyword_init: true)
6
- MethodCallContent = Struct.new(:node, :method_name, :type, :definition, :location, keyword_init: true)
7
- DefinitionContent = Struct.new(:node, :method_name, :method_type, :definition, :location, keyword_init: true) do
8
- def comment_string
9
- if comments = definition&.comments
10
- comments.map {|c| c.string.chomp }.uniq.join("\n----\n")
11
- end
12
- end
13
- end
14
- ConstantContent = Struct.new(:location, :full_name, :type, :decl, keyword_init: true) do
15
- def comment_string
16
- if class_or_module?
17
- comments = decl.decls.filter_map {|d| d.decl.comment&.string }
18
- unless comments.empty?
19
- return comments.join("\n----\n")
20
- end
21
- end
22
-
23
- if constant?
24
- if comment = decl.decl.comment
25
- return comment.string
26
- end
27
- end
28
-
29
- nil
30
- end
31
-
32
- def constant?
33
- decl.is_a?(RBS::Environment::SingleEntry)
34
- end
35
-
36
- def class_or_module?
37
- decl.is_a?(RBS::Environment::MultiEntry)
38
- end
39
- end
40
-
41
- TypeAliasContent = Struct.new(:location, :decl, keyword_init: true)
42
- ClassContent = Struct.new(:location, :decl, keyword_init: true)
43
- InterfaceContent = Struct.new(:location, :decl, keyword_init: true)
44
-
45
- InstanceMethodName = Struct.new(:class_name, :method_name)
46
- SingletonMethodName = Struct.new(:class_name, :method_name)
47
-
48
- attr_reader :service
49
-
50
- def initialize(service:)
51
- @service = service
52
- end
53
-
54
- def project
55
- service.project
56
- end
57
-
58
- def method_definition_for(factory, type_name, singleton_method: nil, instance_method: nil)
59
- case
60
- when instance_method
61
- factory.definition_builder.build_instance(type_name).methods[instance_method]
62
- when singleton_method
63
- methods = factory.definition_builder.build_singleton(type_name).methods
64
-
65
- if singleton_method == :new
66
- methods[:new] || methods[:initialize]
67
- else
68
- methods[singleton_method]
69
- end
70
- end
71
- end
72
-
73
- def typecheck(target, path:, content:, line:, column:)
74
- subtyping = service.signature_services[target.name].current_subtyping or return
75
- source = Source.parse(content, path: path, factory: subtyping.factory)
76
- source = source.without_unrelated_defs(line: line, column: column)
77
- Services::TypeCheckService.type_check(source: source, subtyping: subtyping)
78
- rescue
79
- nil
80
- end
81
-
82
- def content_for(path:, line:, column:)
83
- target_for_code, targets_for_sigs = project.targets_for_path(path)
84
-
85
- case
86
- when target = target_for_code
87
- Steep.logger.info "target #{target}"
88
-
89
- hover_for_source(column, line, path, target)
90
-
91
- when target = targets_for_sigs[0]
92
- service = self.service.signature_services[target.name]
93
-
94
- _buffer, decls = service.latest_env.buffers_decls.find do |buffer, _|
95
- Pathname(buffer.name) == path
96
- end
97
-
98
- return if decls.nil?
99
-
100
- locator = RBS::Locator.new(decls: decls)
101
- hd, tail = locator.find2(line: line, column: column)
102
-
103
- # Maybe hover on comment
104
- return if tail.nil?
105
-
106
- case type = tail[0]
107
- when RBS::Types::Alias
108
- alias_decl = service.latest_env.alias_decls[type.name]&.decl or raise
109
-
110
- location = tail[0].location
111
- TypeAliasContent.new(
112
- location: location,
113
- decl: alias_decl
114
- )
115
- when RBS::Types::ClassInstance, RBS::Types::ClassSingleton
116
- if hd == :name
117
- env = service.latest_env
118
- class_decl = env.class_decls[type.name]&.decls[0]&.decl or raise
119
- location = tail[0].location[:name]
120
- ClassContent.new(
121
- location: location,
122
- decl: class_decl
123
- )
124
- end
125
- when RBS::Types::Interface
126
- env = service.latest_env
127
- interface_decl = env.interface_decls[type.name]&.decl or raise
128
- location = type.location[:name]
129
-
130
- InterfaceContent.new(
131
- location: location,
132
- decl: interface_decl
133
- )
134
- end
135
- end
136
- end
137
-
138
- def hover_for_source(column, line, path, target)
139
- file = service.source_files[path]
140
- typing = typecheck(target, path: path, content: file.content, line: line, column: column) or return
141
- node, *parents = typing.source.find_nodes(line: line, column: column)
142
-
143
- if node
144
- case node.type
145
- when :lvar
146
- var_name = node.children[0]
147
- context = typing.context_at(line: line, column: column)
148
- var_type = context.lvar_env[var_name] || AST::Types::Any.new(location: nil)
149
-
150
- VariableContent.new(node: node, name: var_name, type: var_type, location: node.location.name)
151
- when :lvasgn
152
- var_name, rhs = node.children
153
- context = typing.context_at(line: line, column: column)
154
- type = context.lvar_env[var_name] || typing.type_of(node: rhs)
155
-
156
- VariableContent.new(node: node, name: var_name, type: type, location: node.location.name)
157
- when :send
158
- receiver, method_name, *_ = node.children
159
-
160
- result_node = case parents[0]&.type
161
- when :block, :numblock
162
- parents[0]
163
- else
164
- node
165
- end
166
-
167
- context = typing.context_at(line: line, column: column)
168
-
169
- receiver_type = if receiver
170
- typing.type_of(node: receiver)
171
- else
172
- context.self_type
173
- end
174
-
175
- factory = context.type_env.subtyping.factory
176
- method_name, definition = case receiver_type
177
- when AST::Types::Name::Instance
178
- method_definition = method_definition_for(factory, receiver_type.name, instance_method: method_name)
179
- if method_definition&.defined_in
180
- owner_name = method_definition.defined_in
181
- [
182
- InstanceMethodName.new(owner_name, method_name),
183
- method_definition
184
- ]
185
- end
186
- when AST::Types::Name::Singleton
187
- method_definition = method_definition_for(factory, receiver_type.name, singleton_method: method_name)
188
- if method_definition&.defined_in
189
- owner_name = method_definition.defined_in
190
- [
191
- SingletonMethodName.new(owner_name, method_name),
192
- method_definition
193
- ]
194
- end
195
- else
196
- nil
197
- end
198
-
199
- MethodCallContent.new(
200
- node: node,
201
- method_name: method_name,
202
- type: typing.type_of(node: result_node),
203
- definition: definition,
204
- location: result_node.location.expression
205
- )
206
- when :def, :defs
207
- context = typing.context_at(line: line, column: column)
208
- method_context = context.method_context
209
-
210
- if method_context && method_context.method
211
- DefinitionContent.new(
212
- node: node,
213
- method_name: method_context.name,
214
- method_type: method_context.method_type,
215
- definition: method_context.method,
216
- location: node.loc.expression
217
- )
218
- end
219
- when :const, :casgn
220
- context = typing.context_at(line: line, column: column)
221
-
222
- type = typing.type_of(node: node)
223
- const_name = typing.source_index.reference(constant_node: node)
224
-
225
- if const_name
226
- decl = context.env.class_decls[const_name] || context.env.constant_decls[const_name]
227
-
228
- ConstantContent.new(
229
- location: node.location.name,
230
- full_name: const_name,
231
- type: type,
232
- decl: decl
233
- )
234
- else
235
- TypeContent.new(
236
- node: node,
237
- type: type,
238
- location: node.location.expression
239
- )
240
- end
241
- else
242
- type = typing.type_of(node: node)
243
-
244
- TypeContent.new(
245
- node: node,
246
- type: type,
247
- location: node.location.expression
248
- )
249
- end
250
- end
251
- end
252
- end
253
- end
254
- end