ruby-lsp 0.17.17 → 0.18.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.
- checksums.yaml +4 -4
- data/README.md +4 -110
- data/VERSION +1 -1
- data/exe/ruby-lsp +5 -4
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +14 -6
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +157 -27
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +5 -4
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +2 -2
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +10 -10
- data/lib/ruby_indexer/test/constant_test.rb +4 -4
- data/lib/ruby_indexer/test/enhancements_test.rb +2 -2
- data/lib/ruby_indexer/test/method_test.rb +257 -2
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +1 -1
- data/lib/ruby_lsp/base_server.rb +21 -1
- data/lib/ruby_lsp/document.rb +5 -3
- data/lib/ruby_lsp/erb_document.rb +29 -10
- data/lib/ruby_lsp/global_state.rb +3 -1
- data/lib/ruby_lsp/listeners/code_lens.rb +34 -5
- data/lib/ruby_lsp/listeners/signature_help.rb +55 -24
- data/lib/ruby_lsp/rbs_document.rb +5 -4
- data/lib/ruby_lsp/requests/code_action_resolve.rb +0 -15
- data/lib/ruby_lsp/requests/code_actions.rb +0 -10
- data/lib/ruby_lsp/requests/code_lens.rb +1 -11
- data/lib/ruby_lsp/requests/completion.rb +3 -20
- data/lib/ruby_lsp/requests/completion_resolve.rb +0 -8
- data/lib/ruby_lsp/requests/definition.rb +6 -20
- data/lib/ruby_lsp/requests/diagnostics.rb +0 -10
- data/lib/ruby_lsp/requests/document_highlight.rb +7 -14
- data/lib/ruby_lsp/requests/document_link.rb +0 -10
- data/lib/ruby_lsp/requests/document_symbol.rb +0 -17
- data/lib/ruby_lsp/requests/folding_ranges.rb +0 -10
- data/lib/ruby_lsp/requests/formatting.rb +0 -16
- data/lib/ruby_lsp/requests/hover.rb +9 -9
- data/lib/ruby_lsp/requests/inlay_hints.rb +0 -30
- data/lib/ruby_lsp/requests/on_type_formatting.rb +0 -10
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +0 -11
- data/lib/ruby_lsp/requests/request.rb +17 -1
- data/lib/ruby_lsp/requests/selection_ranges.rb +0 -10
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +1 -23
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +0 -11
- data/lib/ruby_lsp/requests/signature_help.rb +5 -20
- data/lib/ruby_lsp/requests/support/common.rb +1 -1
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +2 -0
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +0 -11
- data/lib/ruby_lsp/requests/workspace_symbol.rb +0 -12
- data/lib/ruby_lsp/ruby_document.rb +4 -3
- data/lib/ruby_lsp/server.rb +23 -8
- data/lib/ruby_lsp/setup_bundler.rb +31 -13
- data/lib/ruby_lsp/type_inferrer.rb +6 -2
- data/lib/ruby_lsp/utils.rb +11 -1
- metadata +3 -4
- data/lib/ruby_lsp/check_docs.rb +0 -130
@@ -3,20 +3,9 @@
|
|
3
3
|
|
4
4
|
module RubyLsp
|
5
5
|
module Requests
|
6
|
-
# 
|
7
|
-
#
|
8
6
|
# The [type hierarchy supertypes
|
9
7
|
# request](https://microsoft.github.io/language-server-protocol/specification#typeHierarchy_supertypes)
|
10
8
|
# displays the list of ancestors (supertypes) for the selected type.
|
11
|
-
#
|
12
|
-
# # Example
|
13
|
-
#
|
14
|
-
# ```ruby
|
15
|
-
# class Foo; end
|
16
|
-
# class Bar < Foo; end
|
17
|
-
#
|
18
|
-
# puts Bar # <-- right click on `Bar` and select "Show Type Hierarchy"
|
19
|
-
# ```
|
20
9
|
class TypeHierarchySupertypes < Request
|
21
10
|
extend T::Sig
|
22
11
|
|
@@ -3,21 +3,9 @@
|
|
3
3
|
|
4
4
|
module RubyLsp
|
5
5
|
module Requests
|
6
|
-
# 
|
7
|
-
#
|
8
6
|
# The [workspace symbol](https://microsoft.github.io/language-server-protocol/specification#workspace_symbol)
|
9
7
|
# request allows fuzzy searching declarations in the entire project. On VS Code, use CTRL/CMD + T to search for
|
10
8
|
# symbols.
|
11
|
-
#
|
12
|
-
# # Example
|
13
|
-
#
|
14
|
-
# ```ruby
|
15
|
-
# # Searching for `Floo` will fuzzy match and return all declarations according to the query, including this `Foo`
|
16
|
-
# class
|
17
|
-
# class Foo
|
18
|
-
# end
|
19
|
-
# ```
|
20
|
-
#
|
21
9
|
class WorkspaceSymbol < Request
|
22
10
|
extend T::Sig
|
23
11
|
include Support::Common
|
@@ -121,12 +121,13 @@ module RubyLsp
|
|
121
121
|
end
|
122
122
|
end
|
123
123
|
|
124
|
-
sig { override.returns(
|
125
|
-
def parse
|
126
|
-
return
|
124
|
+
sig { override.returns(T::Boolean) }
|
125
|
+
def parse!
|
126
|
+
return false unless @needs_parsing
|
127
127
|
|
128
128
|
@needs_parsing = false
|
129
129
|
@parse_result = Prism.parse(@source)
|
130
|
+
true
|
130
131
|
end
|
131
132
|
|
132
133
|
sig { override.returns(T::Boolean) }
|
data/lib/ruby_lsp/server.rb
CHANGED
@@ -9,12 +9,6 @@ module RubyLsp
|
|
9
9
|
sig { returns(GlobalState) }
|
10
10
|
attr_reader :global_state
|
11
11
|
|
12
|
-
sig { params(test_mode: T::Boolean).void }
|
13
|
-
def initialize(test_mode: false)
|
14
|
-
super
|
15
|
-
@global_state = T.let(GlobalState.new, GlobalState)
|
16
|
-
end
|
17
|
-
|
18
12
|
sig { override.params(message: T::Hash[Symbol, T.untyped]).void }
|
19
13
|
def process_message(message)
|
20
14
|
case message[:method]
|
@@ -98,6 +92,8 @@ module RubyLsp
|
|
98
92
|
when "$/cancelRequest"
|
99
93
|
@mutex.synchronize { @cancelled_requests << message[:params][:id] }
|
100
94
|
end
|
95
|
+
rescue DelegateRequestError
|
96
|
+
send_message(Error.new(id: message[:id], code: DelegateRequestError::CODE, message: "DELEGATE_REQUEST"))
|
101
97
|
rescue StandardError, LoadError => e
|
102
98
|
# If an error occurred in a request, we have to return an error response or else the editor will hang
|
103
99
|
if message[:id]
|
@@ -284,7 +280,15 @@ module RubyLsp
|
|
284
280
|
RubyVM::YJIT.enable if defined?(RubyVM::YJIT.enable)
|
285
281
|
|
286
282
|
if defined?(Requests::Support::RuboCopFormatter)
|
287
|
-
|
283
|
+
begin
|
284
|
+
@global_state.register_formatter("rubocop", Requests::Support::RuboCopFormatter.new)
|
285
|
+
rescue RuboCop::Error => e
|
286
|
+
# The user may have provided unknown config switches in .rubocop or
|
287
|
+
# is trying to load a non-existant config file.
|
288
|
+
send_message(Notification.window_show_error(
|
289
|
+
"RuboCop configuration error: #{e.message}. Formatting will not be available.",
|
290
|
+
))
|
291
|
+
end
|
288
292
|
end
|
289
293
|
if defined?(Requests::Support::SyntaxTreeFormatter)
|
290
294
|
@global_state.register_formatter("syntax_tree", Requests::Support::SyntaxTreeFormatter.new)
|
@@ -543,7 +547,7 @@ module RubyLsp
|
|
543
547
|
return
|
544
548
|
end
|
545
549
|
|
546
|
-
request = Requests::DocumentHighlight.new(document, params[:position], dispatcher)
|
550
|
+
request = Requests::DocumentHighlight.new(@global_state, document, params[:position], dispatcher)
|
547
551
|
dispatcher.dispatch(document.parse_result.value)
|
548
552
|
send_message(Result.new(id: message[:id], response: request.perform))
|
549
553
|
end
|
@@ -740,6 +744,17 @@ module RubyLsp
|
|
740
744
|
|
741
745
|
sig { params(message: T::Hash[Symbol, T.untyped]).void }
|
742
746
|
def text_document_completion_item_resolve(message)
|
747
|
+
# When responding to a delegated completion request, it means we're handling a completion item that isn't related
|
748
|
+
# to Ruby (probably related to an ERB host language like HTML). We need to return the original completion item
|
749
|
+
# back to the editor so that it's displayed correctly
|
750
|
+
if message.dig(:params, :data, :delegateCompletion)
|
751
|
+
send_message(Result.new(
|
752
|
+
id: message[:id],
|
753
|
+
response: message[:params],
|
754
|
+
))
|
755
|
+
return
|
756
|
+
end
|
757
|
+
|
743
758
|
send_message(Result.new(
|
744
759
|
id: message[:id],
|
745
760
|
response: Requests::CompletionResolve.new(@global_state, message[:params]).perform,
|
@@ -56,7 +56,7 @@ module RubyLsp
|
|
56
56
|
|
57
57
|
# Sets up the custom bundle and returns the `BUNDLE_GEMFILE`, `BUNDLE_PATH` and `BUNDLE_APP_CONFIG` that should be
|
58
58
|
# used for running the server
|
59
|
-
sig { returns([String,
|
59
|
+
sig { returns(T::Hash[String, String]) }
|
60
60
|
def setup!
|
61
61
|
raise BundleNotLocked if @gemfile&.exist? && !@lockfile&.exist?
|
62
62
|
|
@@ -176,22 +176,18 @@ module RubyLsp
|
|
176
176
|
dependencies
|
177
177
|
end
|
178
178
|
|
179
|
-
sig { params(bundle_gemfile: T.nilable(Pathname)).returns([String,
|
179
|
+
sig { params(bundle_gemfile: T.nilable(Pathname)).returns(T::Hash[String, String]) }
|
180
180
|
def run_bundle_install(bundle_gemfile = @gemfile)
|
181
|
+
env = bundler_settings_as_env
|
182
|
+
env["BUNDLE_GEMFILE"] = bundle_gemfile.to_s
|
183
|
+
|
181
184
|
# If the user has a custom bundle path configured, we need to ensure that we will use the absolute and not
|
182
185
|
# relative version of it when running `bundle install`. This is necessary to avoid installing the gems under the
|
183
186
|
# `.ruby-lsp` folder, which is not the user's intention. For example, if the path is configured as `vendor`, we
|
184
187
|
# want to install it in the top level `vendor` and not `.ruby-lsp/vendor`
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
# Use the absolute `BUNDLE_PATH` to prevent accidentally creating unwanted folders under `.ruby-lsp`
|
189
|
-
env = {}
|
190
|
-
env["BUNDLE_GEMFILE"] = bundle_gemfile.to_s
|
191
|
-
env["BUNDLE_PATH"] = expanded_path if expanded_path
|
192
|
-
|
193
|
-
local_config_path = File.join(@project_path, ".bundle")
|
194
|
-
env["BUNDLE_APP_CONFIG"] = local_config_path if Dir.exist?(local_config_path)
|
188
|
+
if env["BUNDLE_PATH"]
|
189
|
+
env["BUNDLE_PATH"] = File.expand_path(env["BUNDLE_PATH"], @project_path)
|
190
|
+
end
|
195
191
|
|
196
192
|
# If `ruby-lsp` and `debug` (and potentially `ruby-lsp-rails`) are already in the Gemfile, then we shouldn't try
|
197
193
|
# to upgrade them or else we'll produce undesired source control changes. If the custom bundle was just created
|
@@ -238,7 +234,29 @@ module RubyLsp
|
|
238
234
|
return setup!
|
239
235
|
end
|
240
236
|
|
241
|
-
|
237
|
+
env
|
238
|
+
end
|
239
|
+
|
240
|
+
# Gather all Bundler settings (global and local) and return them as a hash that can be used as the environment
|
241
|
+
sig { returns(T::Hash[String, String]) }
|
242
|
+
def bundler_settings_as_env
|
243
|
+
local_config_path = File.join(@project_path, ".bundle")
|
244
|
+
|
245
|
+
# If there's no Gemfile or if the local config path does not exist, we return an empty setting set (which has the
|
246
|
+
# global settings included). Otherwise, we also load the local settings
|
247
|
+
settings = begin
|
248
|
+
Dir.exist?(local_config_path) ? Bundler::Settings.new(local_config_path) : Bundler::Settings.new
|
249
|
+
rescue Bundler::GemfileNotFound
|
250
|
+
Bundler::Settings.new
|
251
|
+
end
|
252
|
+
|
253
|
+
# Map all settings to their environment variable names with `key_for` and their values. For example, the if the
|
254
|
+
# setting name `e` is `path` with a value of `vendor/bundle`, then it will return `"BUNDLE_PATH" =>
|
255
|
+
# "vendor/bundle"`
|
256
|
+
settings.all.to_h do |e|
|
257
|
+
key = Bundler::Settings.key_for(e)
|
258
|
+
[key, settings[e].to_s]
|
259
|
+
end
|
242
260
|
end
|
243
261
|
|
244
262
|
sig { returns(T::Boolean) }
|
@@ -121,8 +121,12 @@ module RubyLsp
|
|
121
121
|
return Type.new(node_context.fully_qualified_name) if node_context.surrounding_method
|
122
122
|
|
123
123
|
# If we're not inside a method, then we're inside the body of a class or module, which is a singleton
|
124
|
-
# context
|
125
|
-
|
124
|
+
# context.
|
125
|
+
#
|
126
|
+
# If the class/module definition is using compact style (e.g.: `class Foo::Bar`), then we need to split the name
|
127
|
+
# into its individual parts to build the correct singleton name
|
128
|
+
parts = nesting.flat_map { |part| part.split("::") }
|
129
|
+
Type.new("#{parts.join("::")}::<Class:#{parts.last}>")
|
126
130
|
end
|
127
131
|
|
128
132
|
sig do
|
data/lib/ruby_lsp/utils.rb
CHANGED
@@ -25,7 +25,17 @@ module RubyLsp
|
|
25
25
|
end,
|
26
26
|
String,
|
27
27
|
)
|
28
|
-
GUESSED_TYPES_URL = "https://github.
|
28
|
+
GUESSED_TYPES_URL = "https://shopify.github.io/ruby-lsp/design-and-roadmap.html#guessed-types"
|
29
|
+
|
30
|
+
# Request delegation for embedded languages is not yet standardized into the language server specification. Here we
|
31
|
+
# use this custom error class as a way to return a signal to the client that the request should be delegated to the
|
32
|
+
# language server for the host language. The support for delegation is custom built on the client side, so each editor
|
33
|
+
# needs to implement their own until this becomes a part of the spec
|
34
|
+
class DelegateRequestError < StandardError
|
35
|
+
# A custom error code that clients can use to handle delegate requests. This is past the range of error codes listed
|
36
|
+
# by the specification to avoid conflicting with other error types
|
37
|
+
CODE = -32900
|
38
|
+
end
|
29
39
|
|
30
40
|
# A notification to be sent to the client
|
31
41
|
class Message
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-lsp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.18.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-09-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: language_server-protocol
|
@@ -112,7 +112,6 @@ files:
|
|
112
112
|
- lib/ruby_indexer/test/test_case.rb
|
113
113
|
- lib/ruby_lsp/addon.rb
|
114
114
|
- lib/ruby_lsp/base_server.rb
|
115
|
-
- lib/ruby_lsp/check_docs.rb
|
116
115
|
- lib/ruby_lsp/document.rb
|
117
116
|
- lib/ruby_lsp/erb_document.rb
|
118
117
|
- lib/ruby_lsp/global_state.rb
|
@@ -201,7 +200,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
201
200
|
- !ruby/object:Gem::Version
|
202
201
|
version: '0'
|
203
202
|
requirements: []
|
204
|
-
rubygems_version: 3.5.
|
203
|
+
rubygems_version: 3.5.18
|
205
204
|
signing_key:
|
206
205
|
specification_version: 4
|
207
206
|
summary: An opinionated language server for Ruby
|
data/lib/ruby_lsp/check_docs.rb
DELETED
@@ -1,130 +0,0 @@
|
|
1
|
-
# typed: strict
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require "ruby_lsp/internal"
|
5
|
-
require "objspace"
|
6
|
-
|
7
|
-
module RubyLsp
|
8
|
-
# This rake task checks that all requests or addons are fully documented. Add the rake task to your Rakefile and
|
9
|
-
# specify the absolute path for all files that must be required in order to discover all requests and their related
|
10
|
-
# GIFs
|
11
|
-
#
|
12
|
-
# # Rakefile
|
13
|
-
# request_files = FileList.new("#{__dir__}/lib/ruby_lsp/requests/*.rb") do |fl|
|
14
|
-
# fl.exclude(/base_request\.rb/)
|
15
|
-
# end
|
16
|
-
# gif_files = FileList.new("#{__dir__}/**/*.gif")
|
17
|
-
# RubyLsp::CheckDocs.new(request_files, gif_files)
|
18
|
-
# # Run with bundle exec rake ruby_lsp:check_docs
|
19
|
-
class CheckDocs < Rake::TaskLib
|
20
|
-
extend T::Sig
|
21
|
-
|
22
|
-
sig { params(require_files: Rake::FileList, gif_files: Rake::FileList).void }
|
23
|
-
def initialize(require_files, gif_files)
|
24
|
-
super()
|
25
|
-
|
26
|
-
@name = T.let("ruby_lsp:check_docs", String)
|
27
|
-
@file_list = require_files
|
28
|
-
@gif_list = gif_files
|
29
|
-
define_task
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
sig { void }
|
35
|
-
def define_task
|
36
|
-
desc("Checks if all Ruby LSP requests are documented")
|
37
|
-
task(@name) { run_task }
|
38
|
-
end
|
39
|
-
|
40
|
-
sig { params(request_path: String).returns(T::Boolean) }
|
41
|
-
def gif_exists?(request_path)
|
42
|
-
request_gif = request_path.gsub(".rb", ".gif").split("/").last
|
43
|
-
|
44
|
-
@gif_list.any? { |gif_path| gif_path.end_with?(request_gif) }
|
45
|
-
end
|
46
|
-
|
47
|
-
sig { void }
|
48
|
-
def run_task
|
49
|
-
# Require all files configured to make sure all requests are loaded
|
50
|
-
@file_list.each { |f| require(f.delete_suffix(".rb")) }
|
51
|
-
|
52
|
-
# Find all classes that inherit from BaseRequest, which are the ones we want to make sure are
|
53
|
-
# documented
|
54
|
-
features = ObjectSpace.each_object(Class).select do |k|
|
55
|
-
klass = T.unsafe(k)
|
56
|
-
klass < Requests::Request
|
57
|
-
end
|
58
|
-
|
59
|
-
missing_docs = T.let(Hash.new { |h, k| h[k] = [] }, T::Hash[String, T::Array[String]])
|
60
|
-
|
61
|
-
features.each do |klass|
|
62
|
-
class_name = T.unsafe(klass).name
|
63
|
-
file_path, line_number = Module.const_source_location(class_name)
|
64
|
-
next unless file_path && line_number
|
65
|
-
|
66
|
-
# Adjust the line number to start searching right above the class definition
|
67
|
-
line_number -= 2
|
68
|
-
|
69
|
-
lines = File.readlines(file_path)
|
70
|
-
docs = []
|
71
|
-
|
72
|
-
# Extract the documentation on top of the request constant
|
73
|
-
while (line = lines[line_number]&.strip) && line.start_with?("#")
|
74
|
-
docs.unshift(line)
|
75
|
-
line_number -= 1
|
76
|
-
end
|
77
|
-
|
78
|
-
documentation = docs.join("\n")
|
79
|
-
|
80
|
-
if docs.empty?
|
81
|
-
T.must(missing_docs[class_name]) << "No documentation found"
|
82
|
-
elsif !%r{\(https://microsoft.github.io/language-server-protocol/specification#.*\)}.match?(documentation)
|
83
|
-
T.must(missing_docs[class_name]) << <<~DOCS
|
84
|
-
Missing specification link. Requests and addons should include a link to the LSP specification for the
|
85
|
-
related feature. For example:
|
86
|
-
|
87
|
-
[Inlay hint](https://microsoft.github.io/language-server-protocol/specification#textDocument_inlayHint)
|
88
|
-
DOCS
|
89
|
-
elsif !documentation.include?("# Example")
|
90
|
-
T.must(missing_docs[class_name]) << <<~DOCS
|
91
|
-
Missing example. Requests and addons should include a code example that explains what the feature does.
|
92
|
-
|
93
|
-
# # Example
|
94
|
-
# ```ruby
|
95
|
-
# class Foo # <- information is shown here
|
96
|
-
# end
|
97
|
-
# ```
|
98
|
-
DOCS
|
99
|
-
elsif !/\[.* demo\]\(.*\.gif\)/.match?(documentation)
|
100
|
-
T.must(missing_docs[class_name]) << <<~DOCS
|
101
|
-
Missing demonstration GIF. Each request and addon must be documented with a GIF that shows the feature
|
102
|
-
working. For example:
|
103
|
-
|
104
|
-
# [Inlay hint demo](../../inlay_hint.gif)
|
105
|
-
DOCS
|
106
|
-
elsif !gif_exists?(file_path)
|
107
|
-
T.must(missing_docs[class_name]) << <<~DOCS
|
108
|
-
The GIF for the request documentation does not exist. Make sure to add it,
|
109
|
-
with the same naming as the request. For example:
|
110
|
-
|
111
|
-
# lib/ruby_lsp/requests/code_lens.rb
|
112
|
-
# foo/bar/code_lens.gif
|
113
|
-
DOCS
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
if missing_docs.any?
|
118
|
-
$stderr.puts(<<~WARN)
|
119
|
-
The following requests are missing documentation:
|
120
|
-
|
121
|
-
#{missing_docs.map { |k, v| "#{k}\n\n#{v.join("\n")}" }.join("\n\n")}
|
122
|
-
WARN
|
123
|
-
|
124
|
-
abort
|
125
|
-
end
|
126
|
-
|
127
|
-
puts "All requests are documented!"
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|