ruby-lsp 0.27.0.beta1 → 0.27.0.beta3

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/exe/ruby-lsp +0 -46
  4. data/exe/ruby-lsp-check +0 -15
  5. data/lib/ruby_lsp/addon.rb +19 -19
  6. data/lib/ruby_lsp/global_state.rb +1 -6
  7. data/lib/ruby_lsp/internal.rb +3 -2
  8. data/lib/ruby_lsp/listeners/code_lens.rb +1 -1
  9. data/lib/ruby_lsp/listeners/completion.rb +246 -382
  10. data/lib/ruby_lsp/listeners/definition.rb +7 -10
  11. data/lib/ruby_lsp/listeners/document_link.rb +4 -0
  12. data/lib/ruby_lsp/listeners/hover.rb +234 -82
  13. data/lib/ruby_lsp/listeners/signature_help.rb +11 -12
  14. data/lib/ruby_lsp/listeners/spec_style.rb +6 -1
  15. data/lib/ruby_lsp/listeners/test_discovery.rb +38 -15
  16. data/lib/ruby_lsp/listeners/test_style.rb +21 -9
  17. data/lib/ruby_lsp/node_context.rb +31 -8
  18. data/lib/ruby_lsp/requests/completion_resolve.rb +55 -39
  19. data/lib/ruby_lsp/requests/discover_tests.rb +5 -41
  20. data/lib/ruby_lsp/requests/hover.rb +2 -5
  21. data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +66 -22
  22. data/lib/ruby_lsp/requests/references.rb +180 -66
  23. data/lib/ruby_lsp/requests/rename.rb +1 -1
  24. data/lib/ruby_lsp/requests/request.rb +3 -33
  25. data/lib/ruby_lsp/requests/support/common.rb +82 -68
  26. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +82 -46
  27. data/lib/ruby_lsp/ruby_document.rb +0 -73
  28. data/lib/ruby_lsp/rubydex/declaration.rb +174 -0
  29. data/lib/ruby_lsp/rubydex/definition.rb +73 -0
  30. data/lib/ruby_lsp/rubydex/reference.rb +6 -1
  31. data/lib/ruby_lsp/rubydex/signature.rb +107 -0
  32. data/lib/ruby_lsp/scripts/compose_bundle.rb +1 -1
  33. data/lib/ruby_lsp/server.rb +56 -171
  34. data/lib/ruby_lsp/test_helper.rb +0 -1
  35. data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +1 -1
  36. data/lib/ruby_lsp/type_inferrer.rb +89 -11
  37. metadata +12 -18
  38. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +0 -276
  39. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +0 -1101
  40. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +0 -44
  41. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +0 -605
  42. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +0 -1077
  43. data/lib/ruby_indexer/lib/ruby_indexer/location.rb +0 -37
  44. data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +0 -149
  45. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +0 -294
  46. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +0 -335
  47. data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +0 -32
  48. data/lib/ruby_indexer/ruby_indexer.rb +0 -20
  49. data/lib/ruby_lsp/static_docs.rb +0 -20
  50. data/static_docs/break.md +0 -103
  51. data/static_docs/yield.md +0 -81
  52. /data/lib/{ruby_indexer/lib/ruby_indexer → ruby_lsp}/uri.rb +0 -0
@@ -1,276 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- module RubyIndexer
5
- class Configuration
6
- CONFIGURATION_SCHEMA = {
7
- "excluded_gems" => Array,
8
- "included_gems" => Array,
9
- "excluded_patterns" => Array,
10
- "included_patterns" => Array,
11
- "excluded_magic_comments" => Array,
12
- }.freeze #: Hash[String, untyped]
13
-
14
- #: String
15
- attr_writer :workspace_path
16
-
17
- #: Encoding
18
- attr_accessor :encoding
19
-
20
- #: -> void
21
- def initialize
22
- @workspace_path = Dir.pwd #: String
23
- @encoding = Encoding::UTF_8 #: Encoding
24
- @excluded_gems = initial_excluded_gems #: Array[String]
25
- @included_gems = [] #: Array[String]
26
-
27
- @excluded_patterns = [
28
- "**/{test,spec}/**/{*_test.rb,test_*.rb,*_spec.rb}",
29
- "**/fixtures/**/*",
30
- ] #: Array[String]
31
-
32
- path = Bundler.settings["path"]
33
- if path
34
- # Substitute Windows backslashes into forward slashes, which are used in glob patterns
35
- glob = path.gsub(/[\\]+/, "/")
36
- glob.delete_suffix!("/")
37
- @excluded_patterns << "#{glob}/**/*.rb"
38
- end
39
-
40
- # We start the included patterns with only the non excluded directories so that we can avoid paying the price of
41
- # traversing large directories that don't include Ruby files like `node_modules`
42
- @included_patterns = ["{#{top_level_directories.join(",")}}/**/*.rb", "*.rb"] #: Array[String]
43
- @excluded_magic_comments = [
44
- "frozen_string_literal:",
45
- "typed:",
46
- "compiled:",
47
- "encoding:",
48
- "shareable_constant_value:",
49
- "warn_indent:",
50
- "rubocop:",
51
- "nodoc:",
52
- "doc:",
53
- "coding:",
54
- "warn_past_scope:",
55
- ] #: Array[String]
56
- end
57
-
58
- #: -> Array[URI::Generic]
59
- def indexable_uris
60
- excluded_gems = @excluded_gems - @included_gems
61
- locked_gems = Bundler.locked_gems&.specs
62
-
63
- # NOTE: indexing the patterns (both included and excluded) needs to happen before indexing gems, otherwise we risk
64
- # having duplicates if BUNDLE_PATH is set to a folder inside the project structure
65
-
66
- flags = File::FNM_PATHNAME | File::FNM_EXTGLOB
67
-
68
- uris = @included_patterns.flat_map do |pattern|
69
- load_path_entry = nil #: String?
70
-
71
- Dir.glob(File.join(@workspace_path, pattern), flags).map! do |path|
72
- # All entries for the same pattern match the same $LOAD_PATH entry. Since searching the $LOAD_PATH for every
73
- # entry is expensive, we memoize it until we find a path that doesn't belong to that $LOAD_PATH. This happens
74
- # on repositories that define multiple gems, like Rails. All frameworks are defined inside the current
75
- # workspace directory, but each one of them belongs to a different $LOAD_PATH entry
76
- if load_path_entry.nil? || !path.start_with?(load_path_entry)
77
- load_path_entry = $LOAD_PATH.find { |load_path| path.start_with?(load_path) }
78
- end
79
-
80
- URI::Generic.from_path(path: path, load_path_entry: load_path_entry)
81
- end
82
- end
83
-
84
- # If the patterns are relative, we make it relative to the workspace path. If they are absolute, then we shouldn't
85
- # concatenate anything
86
- excluded_patterns = @excluded_patterns.map do |pattern|
87
- if File.absolute_path?(pattern)
88
- pattern
89
- else
90
- File.join(@workspace_path, pattern)
91
- end
92
- end
93
-
94
- # Remove user specified patterns
95
- bundle_path = Bundler.settings["path"]&.gsub(/[\\]+/, "/")
96
- uris.reject! do |indexable|
97
- path = indexable.full_path #: as !nil
98
- next false if test_files_ignored_from_exclusion?(path, bundle_path)
99
-
100
- excluded_patterns.any? { |pattern| File.fnmatch?(pattern, path, flags) }
101
- end
102
-
103
- # Add default gems to the list of files to be indexed
104
- Dir.glob(File.join(RbConfig::CONFIG["rubylibdir"], "*")).each do |default_path|
105
- # The default_path might be a Ruby file or a folder with the gem's name. For example:
106
- # bundler/
107
- # bundler.rb
108
- # psych/
109
- # psych.rb
110
- pathname = Pathname.new(default_path)
111
- short_name = pathname.basename.to_s.delete_suffix(".rb")
112
-
113
- # If the gem name is excluded, then we skip it
114
- next if excluded_gems.include?(short_name)
115
-
116
- # If the default gem is also a part of the bundle, we skip indexing the default one and index only the one in
117
- # the bundle, which won't be in `default_path`, but will be in `Bundler.bundle_path` instead
118
- next if locked_gems&.any? do |locked_spec|
119
- locked_spec.name == short_name &&
120
- !Gem::Specification.find_by_name(short_name).full_gem_path.start_with?(RbConfig::CONFIG["rubylibprefix"])
121
- rescue Gem::MissingSpecError
122
- # If a default gem is scoped to a specific platform, then `find_by_name` will raise. We want to skip those
123
- # cases
124
- true
125
- end
126
-
127
- if pathname.directory?
128
- # If the default_path is a directory, we index all the Ruby files in it
129
- uris.concat(
130
- Dir.glob(File.join(default_path, "**", "*.rb"), File::FNM_PATHNAME | File::FNM_EXTGLOB).map! do |path|
131
- URI::Generic.from_path(path: path, load_path_entry: RbConfig::CONFIG["rubylibdir"])
132
- end,
133
- )
134
- elsif pathname.extname == ".rb"
135
- # If the default_path is a Ruby file, we index it
136
- uris << URI::Generic.from_path(path: default_path, load_path_entry: RbConfig::CONFIG["rubylibdir"])
137
- end
138
- end
139
-
140
- # Add the locked gems to the list of files to be indexed
141
- locked_gems&.each do |lazy_spec|
142
- next if excluded_gems.include?(lazy_spec.name)
143
-
144
- spec = Gem::Specification.find_by_name(lazy_spec.name)
145
-
146
- # When working on a gem, it will be included in the locked_gems list. Since these are the project's own files,
147
- # we have already included and handled exclude patterns for it and should not re-include or it'll lead to
148
- # duplicates or accidentally ignoring exclude patterns
149
- next if spec.full_gem_path == @workspace_path
150
-
151
- uris.concat(
152
- spec.require_paths.flat_map do |require_path|
153
- load_path_entry = File.join(spec.full_gem_path, require_path)
154
- Dir.glob(File.join(load_path_entry, "**", "*.rb")).map! do |path|
155
- URI::Generic.from_path(path: path, load_path_entry: load_path_entry)
156
- end
157
- end,
158
- )
159
- rescue Gem::MissingSpecError
160
- # If a gem is scoped only to some specific platform, then its dependencies may not be installed either, but they
161
- # are still listed in locked_gems. We can't index them because they are not installed for the platform, so we
162
- # just ignore if they're missing
163
- end
164
-
165
- uris.uniq!(&:to_s)
166
- uris
167
- end
168
-
169
- #: -> Regexp
170
- def magic_comment_regex
171
- @magic_comment_regex ||= /^#\s*#{@excluded_magic_comments.join("|")}/ #: Regexp?
172
- end
173
-
174
- #: (Hash[String, untyped] config) -> void
175
- def apply_config(config)
176
- validate_config!(config)
177
-
178
- @excluded_gems.concat(config["excluded_gems"]) if config["excluded_gems"]
179
- @included_gems.concat(config["included_gems"]) if config["included_gems"]
180
- @excluded_patterns.concat(config["excluded_patterns"]) if config["excluded_patterns"]
181
- @included_patterns.concat(config["included_patterns"]) if config["included_patterns"]
182
- @excluded_magic_comments.concat(config["excluded_magic_comments"]) if config["excluded_magic_comments"]
183
- end
184
-
185
- private
186
-
187
- #: (Hash[String, untyped] config) -> void
188
- def validate_config!(config)
189
- errors = config.filter_map do |key, value|
190
- type = CONFIGURATION_SCHEMA[key]
191
-
192
- if type.nil?
193
- "Unknown configuration option: #{key}"
194
- elsif !value.is_a?(type)
195
- "Expected #{key} to be a #{type}, but got #{value.class}"
196
- end
197
- end
198
-
199
- raise ArgumentError, errors.join("\n") if errors.any?
200
- end
201
-
202
- #: -> Array[String]
203
- def initial_excluded_gems
204
- excluded, others = Bundler.definition.dependencies.partition do |dependency|
205
- dependency.groups == [:development]
206
- end
207
-
208
- # When working on a gem, we need to make sure that its gemspec dependencies can't be excluded. This is necessary
209
- # because Bundler doesn't assign groups to gemspec dependencies
210
- #
211
- # If the dependency is prerelease, `to_spec` may return `nil` due to a bug in older version of Bundler/RubyGems:
212
- # https://github.com/Shopify/ruby-lsp/issues/1246
213
- this_gem = Bundler.definition.dependencies.find do |d|
214
- d.to_spec&.full_gem_path == @workspace_path
215
- rescue Gem::MissingSpecError
216
- false
217
- end
218
-
219
- others.concat(this_gem.to_spec.dependencies) if this_gem
220
- others.concat(
221
- others.filter_map do |d|
222
- d.to_spec&.dependencies
223
- rescue Gem::MissingSpecError
224
- nil
225
- end.flatten,
226
- )
227
- others.uniq!
228
- others.map!(&:name)
229
-
230
- transitive_excluded = excluded.each_with_object([]) do |dependency, acc|
231
- next unless dependency.runtime?
232
-
233
- spec = dependency.to_spec
234
- next unless spec
235
-
236
- spec.dependencies.each do |transitive_dependency|
237
- next if others.include?(transitive_dependency.name)
238
-
239
- acc << transitive_dependency
240
- end
241
- rescue Gem::MissingSpecError
242
- # If a gem is scoped only to some specific platform, then its dependencies may not be installed either, but they
243
- # are still listed in dependencies. We can't index them because they are not installed for the platform, so we
244
- # just ignore if they're missing
245
- end
246
-
247
- excluded.concat(transitive_excluded)
248
- excluded.uniq!
249
- excluded.map(&:name)
250
- rescue Bundler::GemfileNotFound
251
- []
252
- end
253
-
254
- # Checks if the test file is never supposed to be ignored from indexing despite matching exclusion patterns, like
255
- # `test_helper.rb` or `test_case.rb`. Also takes into consideration the possibility of finding these files under
256
- # fixtures or inside gem source code if the bundle path points to a directory inside the workspace
257
- #: (String path, String? bundle_path) -> bool
258
- def test_files_ignored_from_exclusion?(path, bundle_path)
259
- ["test_case.rb", "test_helper.rb"].include?(File.basename(path)) &&
260
- !File.fnmatch?("**/fixtures/**/*", path, File::FNM_PATHNAME | File::FNM_EXTGLOB) &&
261
- (!bundle_path || !path.start_with?(bundle_path))
262
- end
263
-
264
- #: -> Array[String]
265
- def top_level_directories
266
- excluded_directories = ["tmp", "node_modules", "sorbet"]
267
-
268
- Dir.glob("#{Dir.pwd}/*").filter_map do |path|
269
- dir_name = File.basename(path)
270
- next unless File.directory?(path) && !excluded_directories.include?(dir_name)
271
-
272
- dir_name
273
- end
274
- end
275
- end
276
- end