steep 0.49.1 → 0.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/Gemfile +4 -1
  4. data/Gemfile.lock +8 -5
  5. data/lib/steep/ast/annotation/collection.rb +10 -8
  6. data/lib/steep/ast/types/factory.rb +5 -5
  7. data/lib/steep/cli.rb +83 -1
  8. data/lib/steep/diagnostic/deprecated/unknown_constant_assigned.rb +28 -0
  9. data/lib/steep/diagnostic/ruby.rb +21 -15
  10. data/lib/steep/drivers/check.rb +1 -0
  11. data/lib/steep/drivers/langserver.rb +2 -2
  12. data/lib/steep/drivers/stats.rb +1 -0
  13. data/lib/steep/drivers/utils/jobs_count.rb +1 -1
  14. data/lib/steep/drivers/watch.rb +1 -1
  15. data/lib/steep/index/source_index.rb +18 -1
  16. data/lib/steep/interface/function.rb +5 -0
  17. data/lib/steep/module_helper.rb +4 -7
  18. data/lib/steep/project.rb +15 -1
  19. data/lib/steep/server/interaction_worker.rb +47 -4
  20. data/lib/steep/server/master.rb +10 -5
  21. data/lib/steep/server/type_check_worker.rb +19 -4
  22. data/lib/steep/server/worker_process.rb +15 -6
  23. data/lib/steep/services/completion_provider.rb +109 -1
  24. data/lib/steep/services/goto_service.rb +23 -10
  25. data/lib/steep/services/hover_content.rb +52 -4
  26. data/lib/steep/services/type_check_service.rb +6 -3
  27. data/lib/steep/source.rb +71 -18
  28. data/lib/steep/type_construction.rb +209 -162
  29. data/lib/steep/type_inference/constant_env.rb +33 -15
  30. data/lib/steep/type_inference/context.rb +6 -7
  31. data/lib/steep/type_inference/type_env.rb +16 -54
  32. data/lib/steep/version.rb +1 -1
  33. data/smoke/const/test_expectations.yml +24 -19
  34. data/smoke/diagnostics/test_expectations.yml +77 -17
  35. data/smoke/integer/test_expectations.yml +24 -4
  36. data/smoke/method/test_expectations.yml +30 -0
  37. data/smoke/regression/test_expectations.yml +24 -0
  38. data/smoke/yield/test_expectations.yml +20 -0
  39. data/steep.gemspec +1 -1
  40. metadata +5 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 44df04a21f7dc9d7514ad6acb34549f40fe0b4d962840c29ced4ceca926fd000
4
- data.tar.gz: 15ac298575e8af8d29c1b1ab5e2894f3e62677dedd58b166d147d3d1649026d3
3
+ metadata.gz: f6846cc048d7515ef655b81cf7f399e5852a96c390d90c7f859c07cc6a5c0f94
4
+ data.tar.gz: fecb8b0b0be4e338f3ccf0375064f39167693c0b25b68e3b4ea2f103f117260f
5
5
  SHA512:
6
- metadata.gz: cdc1e1dea1ef2aa9cebd6d40a850894340e2d0ed26f8a982c81c80d384522712c442a499ef12419b0b224cd1bb659944198b8dc897eea692e4d6c06284c86381
7
- data.tar.gz: 22a8d55bd324e3527ffd2a8886bbf7fe28e766ab2dab1e175c43a08b37d256c647f39a05e7890ed5b2726934428bb303c1fe98c45ad07166df2fb2698cc835af
6
+ metadata.gz: 9c08fa692eda8915ef4ce4fe725aecb2a9823c2da188900a3811fdf1d02c6b0369dae8755aed83fe9cc80a376c3309df29efcf849681cd2a9a249559ff6c3ae2
7
+ data.tar.gz: 77953f34eb6ab001dfee4f03e0ec1250f1128fedae9e5b427e0037b97b6b809c5bb8ae4c94d9d93f3af3a702de7dd33a182399fe2b7a81ba8ac0d2708c422579
data/CHANGELOG.md CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.52.0 (2022-04-05)
6
+
7
+ * Add `steep binstub` command ([\#527](https://github.com/soutaro/steep/pull/527))
8
+ * Let hover and completion work in heredoc ([\#528](https://github.com/soutaro/steep/pull/528))
9
+ * Better constant typing ([\#529](https://github.com/soutaro/steep/pull/529))
10
+
11
+ ## 0.51.0 (2022-04-01)
12
+
13
+ * Completion for constant ([\#524](https://github.com/soutaro/steep/pull/524))
14
+ * Better hover/completion message ([\#525](https://github.com/soutaro/steep/pull/525))
15
+ * Show available commands when using `--help` ([\#523](https://github.com/soutaro/steep/pull/523))
16
+
17
+ ## 0.50.0 (2022-03-22)
18
+
19
+ * CLI option for override steep command at spawn worker ([\#511](https://github.com/soutaro/steep/pull/511))
20
+ * LSP related improvements for Sublime LSP ([\#513](https://github.com/soutaro/steep/pull/513))
21
+ * Support Windows environment ([\#514](https://github.com/soutaro/steep/pull/514))
22
+ * Let `&:foo` proc work with methods with optional parameters ([\#516](https://github.com/soutaro/steep/pull/516))
23
+ * Fix unexpected error when or-asgn/and-asgn ([\#517](https://github.com/soutaro/steep/pull/517))
24
+ * Fix goto-definition from method call inside block ([\#518](https://github.com/soutaro/steep/pull/518))
25
+ * Better splat in array typing ([\#519](https://github.com/soutaro/steep/pull/519))
26
+
5
27
  ## 0.49.1 (2022-03-11)
6
28
 
7
29
  * Fix lambda typing ([\#506](https://github.com/soutaro/steep/pull/506))
data/Gemfile CHANGED
@@ -9,4 +9,7 @@ gem "without_steep_types", path: "test/gems/without_steep_types"
9
9
  gem "rake"
10
10
  gem "minitest", "~> 5.15"
11
11
  gem "minitest-hooks"
12
- gem "stackprof"
12
+ group :stackprof, optional: true do
13
+ gem "stackprof"
14
+ end
15
+ gem 'minitest-slow_test'
data/Gemfile.lock CHANGED
@@ -1,14 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- steep (0.49.1)
4
+ steep (0.52.0)
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.2.0)
11
+ rbs (>= 2.3.0)
12
12
  terminal-table (>= 2, < 4)
13
13
 
14
14
  PATH
@@ -30,7 +30,7 @@ GEM
30
30
  minitest (>= 5.1)
31
31
  tzinfo (~> 2.0)
32
32
  ast (2.4.2)
33
- concurrent-ruby (1.1.9)
33
+ concurrent-ruby (1.1.10)
34
34
  ffi (1.15.5)
35
35
  i18n (1.10.0)
36
36
  concurrent-ruby (~> 1.0)
@@ -41,7 +41,9 @@ GEM
41
41
  minitest (5.15.0)
42
42
  minitest-hooks (1.5.0)
43
43
  minitest (> 5.3)
44
- parallel (1.21.0)
44
+ minitest-slow_test (0.2.0)
45
+ minitest (>= 5.0)
46
+ parallel (1.22.1)
45
47
  parser (3.1.1.0)
46
48
  ast (~> 2.4.1)
47
49
  rainbow (3.1.1)
@@ -49,7 +51,7 @@ GEM
49
51
  rb-fsevent (0.11.1)
50
52
  rb-inotify (0.10.1)
51
53
  ffi (~> 1.0)
52
- rbs (2.2.2)
54
+ rbs (2.3.1)
53
55
  stackprof (0.2.19)
54
56
  terminal-table (3.0.2)
55
57
  unicode-display_width (>= 1.1.1, < 3)
@@ -63,6 +65,7 @@ PLATFORMS
63
65
  DEPENDENCIES
64
66
  minitest (~> 5.15)
65
67
  minitest-hooks
68
+ minitest-slow_test
66
69
  rake
67
70
  stackprof
68
71
  steep!
@@ -4,7 +4,7 @@ module Steep
4
4
  class Collection
5
5
  attr_reader :annotations
6
6
  attr_reader :factory
7
- attr_reader :current_module
7
+ attr_reader :context
8
8
 
9
9
  attr_reader :var_type_annotations
10
10
  attr_reader :const_type_annotations
@@ -19,10 +19,10 @@ module Steep
19
19
  attr_reader :dynamic_annotations
20
20
  attr_reader :break_type_annotation
21
21
 
22
- def initialize(annotations:, factory:, current_module:)
22
+ def initialize(annotations:, factory:, context:)
23
23
  @annotations = annotations
24
24
  @factory = factory
25
- @current_module = current_module
25
+ @context = context
26
26
 
27
27
  @var_type_annotations = {}
28
28
  @method_type_annotations = {}
@@ -64,7 +64,7 @@ module Steep
64
64
 
65
65
  def absolute_type(type)
66
66
  if type
67
- factory.absolute_type(type, namespace: current_module)
67
+ factory.absolute_type(type, context: context)
68
68
  end
69
69
  end
70
70
 
@@ -140,7 +140,7 @@ module Steep
140
140
  end
141
141
 
142
142
  def merge_block_annotations(annotations)
143
- if annotations.current_module != current_module || annotations.factory != factory
143
+ if annotations.context != context || annotations.factory != factory
144
144
  raise "Cannot merge another annotation: self=#{self}, other=#{annotations}"
145
145
  end
146
146
 
@@ -148,9 +148,11 @@ module Steep
148
148
  annotation.is_a?(BlockType) || annotation.is_a?(BreakType)
149
149
  end
150
150
 
151
- self.class.new(annotations: retained_annotations + annotations.annotations,
152
- factory: factory,
153
- current_module: current_module)
151
+ self.class.new(
152
+ annotations: retained_annotations + annotations.annotations,
153
+ factory: factory,
154
+ context: context
155
+ )
154
156
  end
155
157
 
156
158
  def any?(&block)
@@ -18,7 +18,7 @@ module Steep
18
18
  end
19
19
 
20
20
  def type_name_resolver
21
- @type_name_resolver ||= RBS::TypeNameResolver.from_env(definition_builder.env)
21
+ @type_name_resolver ||= RBS::Resolver::TypeNameResolver.new(definition_builder.env)
22
22
  end
23
23
 
24
24
  def type_opt(type)
@@ -810,15 +810,15 @@ module Steep
810
810
  @env ||= definition_builder.env
811
811
  end
812
812
 
813
- def absolute_type(type, namespace:)
813
+ def absolute_type(type, context:)
814
814
  absolute_type = type_1(type).map_type_name do |name|
815
- absolute_type_name(name, namespace: namespace) || name.absolute!
815
+ absolute_type_name(name, context: context) || name.absolute!
816
816
  end
817
817
  type(absolute_type)
818
818
  end
819
819
 
820
- def absolute_type_name(type_name, namespace:)
821
- type_name_resolver.resolve(type_name, context: namespace.ascend)
820
+ def absolute_type_name(type_name, context:)
821
+ type_name_resolver.resolve(type_name, context: context)
822
822
  end
823
823
 
824
824
  def instance_type(type_name, args: nil, location: nil)
data/lib/steep/cli.rb CHANGED
@@ -18,11 +18,19 @@ 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
25
25
  OptionParser.new do |opts|
26
+ opts.banner = <<~USAGE
27
+ Usage: steep [options]
28
+
29
+ available commands: #{CLI.available_commands.join(', ')}
30
+
31
+ Options:
32
+ USAGE
33
+
26
34
  opts.on("--version") do
27
35
  process_version
28
36
  exit 0
@@ -72,6 +80,11 @@ module Steep
72
80
  opts.on("-j N", "--jobs=N", "Specify the number of type check workers (defaults: #{default})") do |count|
73
81
  command.jobs_count = Integer(count) if Integer(count) > 0
74
82
  end
83
+
84
+ command.steep_command = "steep"
85
+ opts.on("--steep-command=COMMAND", "Specify command to exec Steep CLI for worker (defaults: steep)") do |cmd|
86
+ command.steep_command = cmd
87
+ end
75
88
  end
76
89
 
77
90
  def process_init
@@ -195,6 +208,75 @@ module Steep
195
208
  end.run
196
209
  end
197
210
 
211
+ def process_binstub
212
+ path = Pathname("bin/steep")
213
+ force = false
214
+
215
+ OptionParser.new do |opts|
216
+ opts.banner = <<BANNER
217
+ Usage: steep binstub [options]
218
+
219
+ Generate a binstub to execute Steep with setting up Bundler and rbenv/rvm.
220
+ Use the executable for LSP integration setup.
221
+
222
+ Options:
223
+ BANNER
224
+ handle_logging_options opts
225
+
226
+ opts.on("-o PATH", "--output=PATH", "The path of the executable file (defaults to `bin/steep`)") do |v|
227
+ path = Pathname(v)
228
+ end
229
+
230
+ opts.on("--[no-]force", "Overwrite file (defaults to false)") do
231
+ force = true
232
+ end
233
+ end.parse!(argv)
234
+
235
+ path.parent.mkpath
236
+
237
+ gemfile_path =
238
+ if defined?(Bundler)
239
+ Bundler.default_gemfile.relative_path_from(Pathname.pwd + path.parent)
240
+ else
241
+ Pathname("../Gemfile")
242
+ end
243
+
244
+ if path.file?
245
+ if force
246
+ stdout.puts Rainbow("#{path} already exists. Overwriting...").yellow
247
+ else
248
+ stdout.puts Rainbow(''"⚠️ #{path} already exists. Bye! 👋").red
249
+ return 0
250
+ end
251
+ end
252
+
253
+ template = <<TEMPLATE
254
+ #!/usr/bin/env bash
255
+
256
+ BINSTUB_DIR=$(cd $(dirname $0); pwd)
257
+ GEMFILE=${BINSTUB_DIR}/#{gemfile_path}
258
+
259
+ STEEP="bundle exec --gemfile=${GEMFILE} steep"
260
+
261
+ if type "rbenv" > /dev/null 2>&1; then
262
+ STEEP="rbenv exec ${STEEP}"
263
+ else
264
+ if type "rvm" > /dev/null 2>&1; then
265
+ STEEP="rvm ${REPO_ROOT} do ${STEEP}"
266
+ fi
267
+ fi
268
+
269
+ exec $STEEP $@
270
+ TEMPLATE
271
+
272
+ path.write(template)
273
+ path.chmod(0755)
274
+
275
+ stdout.puts Rainbow("Successfully generated executable #{path} 🎉").blue
276
+
277
+ 0
278
+ end
279
+
198
280
  def process_version
199
281
  stdout.puts Steep::VERSION
200
282
  0
@@ -0,0 +1,28 @@
1
+ Steep.logger.error "Diagnostic `Ruby::UnknownConstantAssigned` is deprecated. Use `Ruby::UnknownConstant` instead."
2
+
3
+ module Steep
4
+ module Diagnostic
5
+ module Ruby
6
+ class UnknownConstantAssigned < Base
7
+ attr_reader :context
8
+ attr_reader :name
9
+
10
+ def initialize(node:, context:, name:)
11
+ const = node.children[0]
12
+ loc = if const
13
+ const.loc.expression.join(node.loc.name)
14
+ else
15
+ node.loc.name
16
+ end
17
+ super(node: node, location: loc)
18
+ @context = context
19
+ @name = name
20
+ end
21
+
22
+ def header_line
23
+ "Cannot find the declaration of constant `#{name}`"
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -551,27 +551,33 @@ module Steep
551
551
  end
552
552
  end
553
553
 
554
- class UnknownConstantAssigned < Base
555
- attr_reader :context
554
+ class UnknownConstant < Base
556
555
  attr_reader :name
556
+ attr_reader :kind
557
557
 
558
- def initialize(node:, context:, name:)
559
- const = node.children[0]
560
- loc = if const
561
- const.loc.expression.join(node.loc.name)
562
- else
563
- node.loc.name
564
- end
565
- super(node: node, location: loc)
566
- @context = context
558
+ def initialize(node:, name:)
559
+ super(node: node, location: node.loc.name)
567
560
  @name = name
561
+ @kind = :constant
562
+ end
563
+
564
+ def class!
565
+ @kind = :class
566
+ self
567
+ end
568
+
569
+ def module!
570
+ @kind = :module
571
+ self
568
572
  end
569
573
 
570
574
  def header_line
571
- "Cannot find the declaration of constant `#{name}`"
575
+ "Cannot find the declaration of #{kind}: `#{name}`"
572
576
  end
573
577
  end
574
578
 
579
+ autoload :UnknownConstantAssigned, "steep/diagnostic/deprecated/unknown_constant_assigned"
580
+
575
581
  class FallbackAny < Base
576
582
  def initialize(node:)
577
583
  super(node: node)
@@ -744,7 +750,7 @@ module Steep
744
750
  ImplicitBreakValueMismatch => :warning,
745
751
  FallbackAny => :information,
746
752
  ElseOnExhaustiveCase => :warning,
747
- UnknownConstantAssigned => :warning,
753
+ UnknownConstant => :warning,
748
754
  MethodDefinitionMissing => :information
749
755
  }
750
756
  ).freeze
@@ -757,7 +763,7 @@ module Steep
757
763
  ImplicitBreakValueMismatch => nil,
758
764
  FallbackAny => nil,
759
765
  ElseOnExhaustiveCase => nil,
760
- UnknownConstantAssigned => nil,
766
+ UnknownConstant => nil,
761
767
  MethodDefinitionMissing => nil
762
768
  }
763
769
  ).freeze
@@ -770,7 +776,7 @@ module Steep
770
776
  ImplicitBreakValueMismatch => nil,
771
777
  FallbackAny => nil,
772
778
  ElseOnExhaustiveCase => nil,
773
- UnknownConstantAssigned => nil,
779
+ UnknownConstant => nil,
774
780
  MethodDefinitionMissing => nil,
775
781
  UnexpectedJump => nil
776
782
  }
@@ -39,6 +39,7 @@ module Steep
39
39
  steepfile: project.steepfile_path,
40
40
  args: command_line_patterns,
41
41
  delay_shutdown: true,
42
+ steep_command: steep_command,
42
43
  count: jobs_count
43
44
  )
44
45
 
@@ -37,8 +37,8 @@ module Steep
37
37
  def run
38
38
  @project = load_config()
39
39
 
40
- interaction_worker = Server::WorkerProcess.spawn_worker(:interaction, name: "interaction", steepfile: project.steepfile_path)
41
- typecheck_workers = Server::WorkerProcess.spawn_typecheck_workers(steepfile: project.steepfile_path, args: [], count: jobs_count)
40
+ interaction_worker = Server::WorkerProcess.spawn_worker(:interaction, name: "interaction", steepfile: project.steepfile_path, steep_command: steep_command)
41
+ typecheck_workers = Server::WorkerProcess.spawn_typecheck_workers(steepfile: project.steepfile_path, args: [], steep_command: steep_command, count: jobs_count)
42
42
 
43
43
  master = Server::Master.new(
44
44
  project: project,
@@ -129,6 +129,7 @@ module Steep
129
129
  steepfile: project.steepfile_path,
130
130
  delay_shutdown: true,
131
131
  args: command_line_patterns,
132
+ steep_command: steep_command,
132
133
  count: jobs_count
133
134
  )
134
135
 
@@ -2,7 +2,7 @@ module Steep
2
2
  module Drivers
3
3
  module Utils
4
4
  module JobsCount
5
- attr_accessor :jobs_count
5
+ attr_accessor :jobs_count, :steep_command
6
6
  end
7
7
  end
8
8
  end
@@ -41,7 +41,7 @@ module Steep
41
41
  server_reader = LanguageServer::Protocol::Transport::Io::Reader.new(server_read)
42
42
  server_writer = LanguageServer::Protocol::Transport::Io::Writer.new(server_write)
43
43
 
44
- typecheck_workers = Server::WorkerProcess.spawn_typecheck_workers(steepfile: project.steepfile_path, args: dirs.map(&:to_s), count: jobs_count)
44
+ typecheck_workers = Server::WorkerProcess.spawn_typecheck_workers(steepfile: project.steepfile_path, args: dirs.map(&:to_s), steep_command: steep_command, count: jobs_count)
45
45
 
46
46
  master = Server::Master.new(
47
47
  project: project,
@@ -16,7 +16,7 @@ module Steep
16
16
 
17
17
  def add_definition(node)
18
18
  case node.type
19
- when :casgn, :class, :module
19
+ when :casgn, :const
20
20
  @definitions << node
21
21
  else
22
22
  raise "Unexpected constant definition: #{node.type}"
@@ -145,6 +145,23 @@ module Steep
145
145
  raise
146
146
  end
147
147
  end
148
+
149
+ def reference(constant_node: nil)
150
+ case
151
+ when constant_node
152
+ constant_index.each do |name, entry|
153
+ if entry.references.include?(constant_node)
154
+ return name
155
+ end
156
+
157
+ if entry.definitions.include?(constant_node)
158
+ return name
159
+ end
160
+ end
161
+
162
+ nil
163
+ end
164
+ end
148
165
  end
149
166
  end
150
167
  end
@@ -879,6 +879,11 @@ module Steep
879
879
  !has_positional? && !has_keywords?
880
880
  end
881
881
 
882
+ # Returns true if all arguments are non-required.
883
+ def optional?
884
+ required.empty? && required_keywords.empty?
885
+ end
886
+
882
887
  # self + params returns a new params for overloading.
883
888
  #
884
889
  def +(other)
@@ -1,12 +1,9 @@
1
1
  module Steep
2
2
  module ModuleHelper
3
- def module_name_from_node(node)
4
- case node.type
5
- when :const, :casgn
6
- namespace = namespace_from_node(node.children[0]) or return
7
- name = node.children[1]
8
- RBS::TypeName.new(name: name, namespace: namespace)
9
- end
3
+ def module_name_from_node(parent_node, constant_name)
4
+ namespace = namespace_from_node(parent_node) or return
5
+ name = constant_name
6
+ RBS::TypeName.new(name: name, namespace: namespace)
10
7
  end
11
8
 
12
9
  def namespace_from_node(node)
data/lib/steep/project.rb CHANGED
@@ -16,7 +16,21 @@ module Steep
16
16
  steepfile_path.parent
17
17
  end
18
18
 
19
- def relative_path(path)
19
+ def relative_path(orig_path)
20
+ path = if Gem.win_platform?
21
+ path_str = URI.decode_www_form_component(
22
+ orig_path.to_s.delete_prefix("/")
23
+ )
24
+ unless path_str.start_with?(%r{[a-z]:/}i)
25
+ # FIXME: Sometimes drive letter is missing, taking from base_dir
26
+ path_str = base_dir.to_s.split("/")[0] + "/" + path_str
27
+ end
28
+ Pathname.new(
29
+ path_str
30
+ )
31
+ else
32
+ orig_path
33
+ end
20
34
  path.relative_path_from(base_dir)
21
35
  end
22
36
 
@@ -89,7 +89,7 @@ module Steep
89
89
  end
90
90
 
91
91
  LSP::Interface::Hover.new(
92
- contents: { kind: "markdown", value: format_hover(content) },
92
+ contents: { kind: "markdown", value: format_hover(content)&.gsub(/<!--(?~-->)-->/, "") },
93
93
  range: range
94
94
  )
95
95
  end
@@ -175,6 +175,21 @@ HOVER
175
175
  end
176
176
 
177
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")
178
193
  when Services::HoverContent::TypeContent
179
194
  "`#{content.type}`"
180
195
  end
@@ -271,7 +286,7 @@ HOVER
271
286
  ),
272
287
  end: LanguageServer::Protocol::Interface::Position.new(
273
288
  line: job.line - 1,
274
- character: job.column - prefix.size
289
+ character: job.column
275
290
  )
276
291
  )
277
292
 
@@ -327,7 +342,16 @@ HOVER
327
342
  if comment
328
343
  LSP::Interface::MarkupContent.new(
329
344
  kind: LSP::Constant::MarkupKind::MARKDOWN,
330
- value: comment.string
345
+ value: comment.string.gsub(/<!--(?~-->)-->/, "")
346
+ )
347
+ end
348
+ end
349
+
350
+ def format_comments(comments)
351
+ unless comments.empty?
352
+ LSP::Interface::MarkupContent.new(
353
+ kind: LSP::Constant::MarkupKind::MARKDOWN,
354
+ value: comments.map(&:string).join("\n----\n").gsub(/<!--(?~-->)-->/, "")
331
355
  )
332
356
  end
333
357
  end
@@ -408,6 +432,25 @@ HOVER
408
432
  new_text: item.identifier
409
433
  )
410
434
  )
435
+ when Services::CompletionProvider::ConstantItem
436
+ case
437
+ when item.class? || item.module?
438
+ kind = LanguageServer::Protocol::Constant::CompletionItemKind::CLASS
439
+ detail = item.full_name.to_s
440
+ else
441
+ kind = LanguageServer::Protocol::Constant::CompletionItemKind::CONSTANT
442
+ detail = item.type.to_s
443
+ end
444
+ LanguageServer::Protocol::Interface::CompletionItem.new(
445
+ label: item.identifier,
446
+ kind: kind,
447
+ detail: detail,
448
+ documentation: format_comments(item.comments),
449
+ text_edit: LanguageServer::Protocol::Interface::TextEdit.new(
450
+ range: range,
451
+ new_text: item.identifier
452
+ )
453
+ )
411
454
  when Services::CompletionProvider::MethodNameItem
412
455
  method_type_snippet = method_type_to_snippet(item.method_type)
413
456
  LanguageServer::Protocol::Interface::CompletionItem.new(
@@ -418,7 +461,7 @@ HOVER
418
461
  new_text: "#{item.identifier}#{method_type_snippet}",
419
462
  range: range
420
463
  ),
421
- documentation: item.comment&.string,
464
+ documentation: format_comment(item.comment),
422
465
  insert_text_format: LanguageServer::Protocol::Constant::InsertTextFormat::SNIPPET,
423
466
  sort_text: item.inherited? ? 'z' : 'a' # Ensure language server puts non-inherited methods before inherited methods
424
467
  )
@@ -49,9 +49,14 @@ module Steep
49
49
  end
50
50
 
51
51
  def checking_path?(path)
52
- library_paths.include?(path) ||
53
- signature_paths.include?(path) ||
54
- code_paths.include?(path)
52
+ [library_paths, signature_paths, code_paths].any? do |paths|
53
+ if Gem.win_platform?
54
+ # FIXME: Sometimes drive letter is missing, so comparing without drive letter.
55
+ paths.any? {|p| p.to_s.split("/", 2)[1] == path.to_s.split("/", 2)[1]}
56
+ else
57
+ paths.include?(path)
58
+ end
59
+ end
55
60
  end
56
61
 
57
62
  def checked(path)
@@ -496,7 +501,7 @@ module Steep
496
501
  partialResult: true
497
502
  },
498
503
  completion_provider: LSP::Interface::CompletionOptions.new(
499
- trigger_characters: [".", "@"],
504
+ trigger_characters: [".", "@", ":"],
500
505
  work_done_progress: true
501
506
  ),
502
507
  workspace_symbol_provider: true,
@@ -739,7 +744,7 @@ module Steep
739
744
  { kind: "end" }
740
745
  else
741
746
  progress_string = ("▮"*(percentage/5)) + ("▯"*(20 - percentage/5))
742
- { kind: "report", percentage: percentage, message: "#{progress_string} (#{percentage}%)" }
747
+ { kind: "report", percentage: percentage, message: "#{progress_string}" }
743
748
  end
744
749
 
745
750
  job_queue << SendMessageJob.to_client(