steep 0.51.0 → 0.52.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 502d495f9b623c5d90a041a1e0bb7854be76251a8ac0f3567c1c91911eb641ee
4
- data.tar.gz: 7845d6d26f38ceae87407d507f3f67b8d35bae18ecdd5f4ee9fdb841d434a4ad
3
+ metadata.gz: 3c9d5eb873780b31a2431d2e6c4efdc558fbb3a4a42c08cbe2580bb191db5bc8
4
+ data.tar.gz: dee794e9cb098309b9875396ed1dc9904a4c6882aeadb27c83c26604ebcbc87d
5
5
  SHA512:
6
- metadata.gz: e2e61987da5ef5d78381affeb2f4fbb8b84dd2a6fede0baa60c68f54a3464e3cb7eea0e2822f22a66d62f1de3b9f8b223cd6399dceb927397a3dab0bed3fea5d
7
- data.tar.gz: ee91ba6b1a4d8f35ec1fbc157efd2fb54f27b11e398af09a7ef3d4f403ae0ae2fab3258404316c6ded646f688b61ff38b47a514fec83cd9fe1b4fcf136fdc441
6
+ metadata.gz: ba05f88c28a78414ffcd41744558bdea978b72621f422571cb1450b7b0d56fb77c2b65329064226863db2517d4769a6f54e7c47ce92eef59d97f0403bb9e9425
7
+ data.tar.gz: 6e260b48940214b7c0910bf2678c587b469e163e70265f197ef8fd17a1703a68fd9488c77c3d1f8254939c074281717593e49812875fb705615189cd3ec4cdf0
@@ -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,25 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.52.2 (2022-05-02)
6
+
7
+ * Handle class declaration with non-const super class ([\#546](https://github.com/soutaro/steep/pull/546))
8
+ * Remove `#to_a` error message ([\#545](https://github.com/soutaro/steep/pull/545))
9
+ * Add `#filter_map` shim ([\#544](https://github.com/soutaro/steep/pull/544))
10
+
11
+ ## 0.52.1 (2022-04-25)
12
+
13
+ * Better union type inference (it type checks `Array#filter_map` now!) ([\#531](https://github.com/soutaro/steep/pull/531))
14
+ * Improve method call hover message ([\#537](https://github.com/soutaro/steep/pull/537), [\#538](https://github.com/soutaro/steep/pull/538))
15
+ * Make `NilClass#!` a special method to improve flow-sensitive typing ([\#539](https://github.com/soutaro/steep/pull/539))
16
+ * Fix `steep binstub` ([\#540](https://github.com/soutaro/steep/pull/540), [\#541](https://github.com/soutaro/steep/pull/541))
17
+
18
+ ## 0.52.0 (2022-04-05)
19
+
20
+ * Add `steep binstub` command ([\#527](https://github.com/soutaro/steep/pull/527))
21
+ * Let hover and completion work in heredoc ([\#528](https://github.com/soutaro/steep/pull/528))
22
+ * Better constant typing ([\#529](https://github.com/soutaro/steep/pull/529))
23
+
5
24
  ## 0.51.0 (2022-04-01)
6
25
 
7
26
  * Completion for constant ([\#524](https://github.com/soutaro/steep/pull/524))
data/Gemfile.lock CHANGED
@@ -1,14 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- steep (0.51.0)
4
+ steep (0.52.2)
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
@@ -24,7 +24,7 @@ PATH
24
24
  GEM
25
25
  remote: https://rubygems.org/
26
26
  specs:
27
- activesupport (7.0.2.3)
27
+ activesupport (7.0.2.4)
28
28
  concurrent-ruby (~> 1.0, >= 1.0.2)
29
29
  i18n (>= 1.6, < 2)
30
30
  minitest (>= 5.1)
@@ -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.0)
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
@@ -18,7 +18,7 @@ module Steep
18
18
  end
19
19
 
20
20
  def self.available_commands
21
- [:init, :check, :validate, :annotations, :version, :project, :watch, :langserver, :stats]
21
+ [:init, :check, :validate, :annotations, :version, :project, :watch, :langserver, :stats, :binstub]
22
22
  end
23
23
 
24
24
  def process_global_options
@@ -208,6 +208,84 @@ module Steep
208
208
  end.run
209
209
  end
210
210
 
211
+ def process_binstub
212
+ path = Pathname("bin/steep")
213
+ root_path = Pathname.pwd
214
+ force = false
215
+
216
+ OptionParser.new do |opts|
217
+ opts.banner = <<BANNER
218
+ Usage: steep binstub [options]
219
+
220
+ Generate a binstub to execute Steep with setting up Bundler and rbenv/rvm.
221
+ Use the executable for LSP integration setup.
222
+
223
+ Options:
224
+ BANNER
225
+ handle_logging_options opts
226
+
227
+ opts.on("-o PATH", "--output=PATH", "The path of the executable file (defaults to `bin/steep`)") do |v|
228
+ path = Pathname(v)
229
+ end
230
+
231
+ opts.on("--root=PATH", "The repository root path (defaults to `.`)") do |v|
232
+ root_path = (Pathname.pwd + v).cleanpath
233
+ end
234
+
235
+ opts.on("--[no-]force", "Overwrite file (defaults to false)") do
236
+ force = true
237
+ end
238
+ end.parse!(argv)
239
+
240
+ binstub_path = (Pathname.pwd + path).cleanpath
241
+ bindir_path = binstub_path.parent
242
+
243
+ bindir_path.mkpath
244
+
245
+ gemfile_path =
246
+ if defined?(Bundler)
247
+ Bundler.default_gemfile.relative_path_from(bindir_path)
248
+ else
249
+ Pathname("../Gemfile")
250
+ end
251
+
252
+ if binstub_path.file?
253
+ if force
254
+ stdout.puts Rainbow("#{path} already exists. Overwriting...").yellow
255
+ else
256
+ stdout.puts Rainbow(''"⚠️ #{path} already exists. Bye! 👋").red
257
+ return 0
258
+ end
259
+ end
260
+
261
+ template = <<TEMPLATE
262
+ #!/usr/bin/env bash
263
+
264
+ BINSTUB_DIR=$(cd $(dirname $0); pwd)
265
+ GEMFILE=$(readlink -f ${BINSTUB_DIR}/#{gemfile_path})
266
+ ROOT_DIR=$(readlink -f ${BINSTUB_DIR}/#{root_path.relative_path_from(bindir_path)})
267
+
268
+ STEEP="bundle exec --gemfile=${GEMFILE} steep"
269
+
270
+ if type "rbenv" > /dev/null 2>&1; then
271
+ STEEP="rbenv exec ${STEEP}"
272
+ else
273
+ if type "rvm" > /dev/null 2>&1; then
274
+ STEEP="rvm ${ROOT_DIR} do ${STEEP}"
275
+ fi
276
+ fi
277
+
278
+ exec $STEEP $@
279
+ TEMPLATE
280
+
281
+ binstub_path.write(template)
282
+ binstub_path.chmod(0755)
283
+
284
+ stdout.puts Rainbow("Successfully generated executable #{path} 🎉").blue
285
+
286
+ 0
287
+ end
288
+
211
289
  def process_version
212
290
  stdout.puts Steep::VERSION
213
291
  0
@@ -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