ruby-lsp 0.12.0 → 0.12.1
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/VERSION +1 -1
- data/exe/ruby-lsp +10 -2
- data/exe/ruby-lsp-check +2 -1
- data/exe/ruby-lsp-doctor +1 -2
- data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +125 -0
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +5 -1
- data/lib/ruby_indexer/lib/ruby_indexer/indexable_path.rb +1 -1
- data/lib/ruby_indexer/ruby_indexer.rb +3 -3
- data/lib/ruby_lsp/requests/code_lens.rb +18 -2
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +20 -18
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +19 -0
- data/lib/ruby_lsp/setup_bundler.rb +18 -12
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2774623b2161a416bfb07769e8eb5ccf3d40177f98edf9e4d4ed4c7e3f7fd47b
|
4
|
+
data.tar.gz: 23626aa84fdaf85571c7d039d670f9131409ccb7a8cd2c61d4889ffd08ecb8de
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ffd099ba6d95ce10cd3fd2c3fb1e431d5481e0e8bad77c062fdd9b2f9fce534f9c58319672e508d17ca089e10ba9fdf398479dd9dfac3adbc347912815800cdd
|
7
|
+
data.tar.gz: fb0f4c3111718c5f792266459e2694787ea7939b63c819707bba764395dc6cc3bf1d925517a1fa412b362fbd0b3222e8460723df4f2c3a4272ac73dc62eb872a
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.12.
|
1
|
+
0.12.1
|
data/exe/ruby-lsp
CHANGED
@@ -25,6 +25,13 @@ parser = OptionParser.new do |opts|
|
|
25
25
|
options[:branch] = branch
|
26
26
|
end
|
27
27
|
|
28
|
+
opts.on(
|
29
|
+
"--experimental",
|
30
|
+
"Run pre-release versions of the Ruby LSP",
|
31
|
+
) do
|
32
|
+
options[:experimental] = true
|
33
|
+
end
|
34
|
+
|
28
35
|
opts.on("-h", "--help", "Print this help") do
|
29
36
|
puts opts.help
|
30
37
|
puts
|
@@ -49,7 +56,7 @@ if ENV["BUNDLE_GEMFILE"].nil?
|
|
49
56
|
require_relative "../lib/ruby_lsp/setup_bundler"
|
50
57
|
|
51
58
|
begin
|
52
|
-
bundle_gemfile, bundle_path, bundle_app_config = RubyLsp::SetupBundler.new(Dir.pwd,
|
59
|
+
bundle_gemfile, bundle_path, bundle_app_config = RubyLsp::SetupBundler.new(Dir.pwd, **options).setup!
|
53
60
|
rescue RubyLsp::SetupBundler::BundleNotLocked
|
54
61
|
warn("Project contains a Gemfile, but no Gemfile.lock. Run `bundle install` to lock gems and restart the server")
|
55
62
|
exit(78)
|
@@ -78,7 +85,8 @@ rescue
|
|
78
85
|
nil
|
79
86
|
end
|
80
87
|
|
81
|
-
|
88
|
+
$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
|
89
|
+
require "ruby_lsp/internal"
|
82
90
|
|
83
91
|
if options[:debug]
|
84
92
|
if ["x64-mingw-ucrt", "x64-mingw32"].include?(RUBY_PLATFORM)
|
data/exe/ruby-lsp-check
CHANGED
data/exe/ruby-lsp-doctor
CHANGED
@@ -0,0 +1,125 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "rubocop"
|
5
|
+
require "sorbet-runtime"
|
6
|
+
|
7
|
+
module RuboCop
|
8
|
+
module Cop
|
9
|
+
module RubyLsp
|
10
|
+
# Avoid using register without handler method, or handler without register.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# # Register without handler method.
|
14
|
+
#
|
15
|
+
# # bad
|
16
|
+
# class MyListener < Listener
|
17
|
+
# def initialize(dispatcher)
|
18
|
+
# super()
|
19
|
+
# dispatcher.register(
|
20
|
+
# self,
|
21
|
+
# :on_string_node_enter,
|
22
|
+
# )
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# # good
|
27
|
+
# class MyListener < Listener
|
28
|
+
# def initialize(dispatcher)
|
29
|
+
# super()
|
30
|
+
# dispatcher.register(
|
31
|
+
# self,
|
32
|
+
# :on_string_node_enter,
|
33
|
+
# )
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# def on_string_node_enter(node)
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# @example
|
41
|
+
# # Handler method without register.
|
42
|
+
#
|
43
|
+
# # bad
|
44
|
+
# class MyListener < Listener
|
45
|
+
# def initialize(dispatcher)
|
46
|
+
# super()
|
47
|
+
# dispatcher.register(
|
48
|
+
# self,
|
49
|
+
# )
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# def on_string_node_enter(node)
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# # good
|
57
|
+
# class MyListener < Listener
|
58
|
+
# def initialize(dispatcher)
|
59
|
+
# super()
|
60
|
+
# dispatcher.register(
|
61
|
+
# self,
|
62
|
+
# :on_string_node_enter,
|
63
|
+
# )
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# def on_string_node_enter(node)
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
class UseRegisterWithHandlerMethod < RuboCop::Cop::Base
|
70
|
+
extend T::Sig
|
71
|
+
|
72
|
+
MSG_MISSING_HANDLER = "Registered to `%{listener}` without a handler defined."
|
73
|
+
MSG_MISSING_LISTENER = "Created a handler without registering the associated `%{listener}` event."
|
74
|
+
|
75
|
+
def_node_search(
|
76
|
+
:find_all_listeners,
|
77
|
+
"(send
|
78
|
+
(_ :dispatcher) :register
|
79
|
+
(self)
|
80
|
+
$(sym _)+)",
|
81
|
+
)
|
82
|
+
|
83
|
+
def_node_search(
|
84
|
+
:find_all_handlers,
|
85
|
+
"$(def [_ #valid_event_name?] (args (arg _)) ...)",
|
86
|
+
)
|
87
|
+
|
88
|
+
def on_new_investigation
|
89
|
+
return if processed_source.blank?
|
90
|
+
|
91
|
+
listeners = find_all_listeners(processed_source.ast).flat_map { |listener| listener }
|
92
|
+
handlers = find_all_handlers(processed_source.ast).flat_map { |handler| handler }
|
93
|
+
|
94
|
+
add_offense_to_listeners_without_handler(listeners, handlers)
|
95
|
+
add_offense_handlers_without_listener(listeners, handlers)
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
sig { params(event_name: Symbol).returns(T::Boolean) }
|
101
|
+
def valid_event_name?(event_name)
|
102
|
+
/^on_.*(node_enter|node_leave)$/.match?(event_name)
|
103
|
+
end
|
104
|
+
|
105
|
+
sig { params(listeners: T::Array[RuboCop::AST::SymbolNode], handlers: T::Array[RuboCop::AST::DefNode]).void }
|
106
|
+
def add_offense_to_listeners_without_handler(listeners, handlers)
|
107
|
+
return if listeners.none?
|
108
|
+
|
109
|
+
listeners
|
110
|
+
.filter { |node| handlers.map(&:method_name).none?(node.value) }
|
111
|
+
.each { |node| add_offense(node, message: format(MSG_MISSING_HANDLER, listener: node.value)) }
|
112
|
+
end
|
113
|
+
|
114
|
+
sig { params(listeners: T::Array[RuboCop::AST::SymbolNode], handlers: T::Array[RuboCop::AST::DefNode]).void }
|
115
|
+
def add_offense_handlers_without_listener(listeners, handlers)
|
116
|
+
return if handlers.none?
|
117
|
+
|
118
|
+
handlers
|
119
|
+
.filter { |node| listeners.map(&:value).none?(node.method_name) }
|
120
|
+
.each { |node| add_offense(node, message: format(MSG_MISSING_LISTENER, listener: node.method_name)) }
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -67,8 +67,12 @@ module RubyIndexer
|
|
67
67
|
|
68
68
|
# Add user specified patterns
|
69
69
|
indexables = @included_patterns.flat_map do |pattern|
|
70
|
+
load_path_entry = T.let(nil, T.nilable(String))
|
71
|
+
|
70
72
|
Dir.glob(pattern, File::FNM_PATHNAME | File::FNM_EXTGLOB).map! do |path|
|
71
|
-
|
73
|
+
# All entries for the same pattern match the same $LOAD_PATH entry. Since searching the $LOAD_PATH for every
|
74
|
+
# entry is expensive, we memoize it for the entire pattern
|
75
|
+
load_path_entry ||= $LOAD_PATH.find { |load_path| path.start_with?(load_path) }
|
72
76
|
IndexablePath.new(load_path_entry, path)
|
73
77
|
end
|
74
78
|
end
|
@@ -21,7 +21,7 @@ module RubyIndexer
|
|
21
21
|
def initialize(load_path_entry, full_path)
|
22
22
|
@full_path = full_path
|
23
23
|
@require_path = T.let(
|
24
|
-
load_path_entry ?
|
24
|
+
load_path_entry ? full_path.delete_prefix("#{load_path_entry}/").delete_suffix(".rb") : nil,
|
25
25
|
T.nilable(String),
|
26
26
|
)
|
27
27
|
end
|
@@ -12,12 +12,12 @@ require "ruby_indexer/lib/ruby_indexer/configuration"
|
|
12
12
|
require "ruby_indexer/lib/ruby_indexer/prefix_tree"
|
13
13
|
|
14
14
|
module RubyIndexer
|
15
|
+
@configuration = T.let(Configuration.new, Configuration)
|
16
|
+
|
15
17
|
class << self
|
16
18
|
extend T::Sig
|
17
19
|
|
18
20
|
sig { returns(Configuration) }
|
19
|
-
|
20
|
-
@configuration ||= T.let(Configuration.new, T.nilable(Configuration))
|
21
|
-
end
|
21
|
+
attr_reader :configuration
|
22
22
|
end
|
23
23
|
end
|
@@ -24,7 +24,23 @@ module RubyLsp
|
|
24
24
|
|
25
25
|
ResponseType = type_member { { fixed: T::Array[Interface::CodeLens] } }
|
26
26
|
|
27
|
-
BASE_COMMAND = T.let(
|
27
|
+
BASE_COMMAND = T.let(
|
28
|
+
begin
|
29
|
+
Bundler.with_original_env { Bundler.default_lockfile }
|
30
|
+
"bundle exec ruby"
|
31
|
+
rescue Bundler::GemfileNotFound
|
32
|
+
"ruby"
|
33
|
+
end + " -Itest ",
|
34
|
+
String,
|
35
|
+
)
|
36
|
+
GEMFILE_NAME = T.let(
|
37
|
+
begin
|
38
|
+
Bundler.with_original_env { Bundler.default_gemfile.basename.to_s }
|
39
|
+
rescue Bundler::GemfileNotFound
|
40
|
+
"Gemfile"
|
41
|
+
end,
|
42
|
+
String,
|
43
|
+
)
|
28
44
|
ACCESS_MODIFIERS = T.let([:public, :private, :protected], T::Array[Symbol])
|
29
45
|
SUPPORTED_TEST_LIBRARIES = T.let(["minitest", "test-unit"], T::Array[String])
|
30
46
|
|
@@ -111,7 +127,7 @@ module RubyLsp
|
|
111
127
|
return
|
112
128
|
end
|
113
129
|
|
114
|
-
if @path&.include?(
|
130
|
+
if @path&.include?(GEMFILE_NAME) && name == :gem && arguments
|
115
131
|
first_argument = arguments.arguments.first
|
116
132
|
return unless first_argument.is_a?(Prism::StringNode)
|
117
133
|
|
@@ -9,9 +9,9 @@ module RubyLsp
|
|
9
9
|
|
10
10
|
RUBOCOP_TO_LSP_SEVERITY = T.let(
|
11
11
|
{
|
12
|
-
|
13
|
-
info: Constant::DiagnosticSeverity::INFORMATION,
|
12
|
+
info: Constant::DiagnosticSeverity::HINT,
|
14
13
|
refactor: Constant::DiagnosticSeverity::INFORMATION,
|
14
|
+
convention: Constant::DiagnosticSeverity::INFORMATION,
|
15
15
|
warning: Constant::DiagnosticSeverity::WARNING,
|
16
16
|
error: Constant::DiagnosticSeverity::ERROR,
|
17
17
|
fatal: Constant::DiagnosticSeverity::ERROR,
|
@@ -19,12 +19,6 @@ module RubyLsp
|
|
19
19
|
T::Hash[Symbol, Integer],
|
20
20
|
)
|
21
21
|
|
22
|
-
# Cache cops to attach URLs to diagnostics. Only built-in cops for now.
|
23
|
-
COP_TO_DOC_URL = T.let(
|
24
|
-
RuboCop::Cop::Registry.global.to_h,
|
25
|
-
T::Hash[String, [T.class_of(RuboCop::Cop::Base)]],
|
26
|
-
)
|
27
|
-
|
28
22
|
sig { params(offense: RuboCop::Cop::Offense, uri: URI::Generic).void }
|
29
23
|
def initialize(offense, uri)
|
30
24
|
@offense = offense
|
@@ -53,16 +47,6 @@ module RubyLsp
|
|
53
47
|
|
54
48
|
sig { returns(Interface::Diagnostic) }
|
55
49
|
def to_lsp_diagnostic
|
56
|
-
severity = RUBOCOP_TO_LSP_SEVERITY[@offense.severity.name]
|
57
|
-
message = @offense.message
|
58
|
-
|
59
|
-
message += "\n\nThis offense is not auto-correctable.\n" unless @offense.correctable?
|
60
|
-
|
61
|
-
cop = COP_TO_DOC_URL[@offense.cop_name]&.first
|
62
|
-
if cop&.documentation_url
|
63
|
-
code_description = { href: cop.documentation_url }
|
64
|
-
end
|
65
|
-
|
66
50
|
Interface::Diagnostic.new(
|
67
51
|
message: message,
|
68
52
|
source: "RuboCop",
|
@@ -88,6 +72,24 @@ module RubyLsp
|
|
88
72
|
|
89
73
|
private
|
90
74
|
|
75
|
+
sig { returns(String) }
|
76
|
+
def message
|
77
|
+
message = @offense.message
|
78
|
+
message += "\n\nThis offense is not auto-correctable.\n" unless @offense.correctable?
|
79
|
+
message
|
80
|
+
end
|
81
|
+
|
82
|
+
sig { returns(T.nilable(Integer)) }
|
83
|
+
def severity
|
84
|
+
RUBOCOP_TO_LSP_SEVERITY[@offense.severity.name]
|
85
|
+
end
|
86
|
+
|
87
|
+
sig { returns(T.nilable(Interface::CodeDescription)) }
|
88
|
+
def code_description
|
89
|
+
doc_url = RuboCopRunner.find_cop_by_name(@offense.cop_name)&.documentation_url
|
90
|
+
Interface::CodeDescription.new(href: doc_url) if doc_url
|
91
|
+
end
|
92
|
+
|
91
93
|
sig { returns(T::Array[Interface::TextEdit]) }
|
92
94
|
def offense_replacements
|
93
95
|
@offense.corrector.as_replacements.map do |range, replacement|
|
@@ -101,6 +101,25 @@ module RubyLsp
|
|
101
101
|
@options[:stdin]
|
102
102
|
end
|
103
103
|
|
104
|
+
class << self
|
105
|
+
extend T::Sig
|
106
|
+
|
107
|
+
sig { params(cop_name: String).returns(T.nilable(T.class_of(RuboCop::Cop::Base))) }
|
108
|
+
def find_cop_by_name(cop_name)
|
109
|
+
cop_registry[cop_name]&.first
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
sig { returns(T::Hash[String, [T.class_of(RuboCop::Cop::Base)]]) }
|
115
|
+
def cop_registry
|
116
|
+
@cop_registry ||= T.let(
|
117
|
+
RuboCop::Cop::Registry.global.to_h,
|
118
|
+
T.nilable(T::Hash[String, [T.class_of(RuboCop::Cop::Base)]]),
|
119
|
+
)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
104
123
|
private
|
105
124
|
|
106
125
|
sig { params(_file: String, offenses: T::Array[RuboCop::Cop::Offense]).void }
|
@@ -20,17 +20,11 @@ module RubyLsp
|
|
20
20
|
|
21
21
|
FOUR_HOURS = T.let(4 * 60 * 60, Integer)
|
22
22
|
|
23
|
-
sig { params(project_path: String,
|
24
|
-
def initialize(project_path,
|
23
|
+
sig { params(project_path: String, options: T.untyped).void }
|
24
|
+
def initialize(project_path, **options)
|
25
25
|
@project_path = project_path
|
26
|
-
@branch = branch
|
27
|
-
|
28
|
-
# Custom bundle paths
|
29
|
-
@custom_dir = T.let(Pathname.new(".ruby-lsp").expand_path(Dir.pwd), Pathname)
|
30
|
-
@custom_gemfile = T.let(@custom_dir + "Gemfile", Pathname)
|
31
|
-
@custom_lockfile = T.let(@custom_dir + "Gemfile.lock", Pathname)
|
32
|
-
@lockfile_hash_path = T.let(@custom_dir + "main_lockfile_hash", Pathname)
|
33
|
-
@last_updated_path = T.let(@custom_dir + "last_updated", Pathname)
|
26
|
+
@branch = T.let(options[:branch], T.nilable(String))
|
27
|
+
@experimental = T.let(options[:experimental], T.nilable(T::Boolean))
|
34
28
|
|
35
29
|
# Regular bundle paths
|
36
30
|
@gemfile = T.let(
|
@@ -43,6 +37,15 @@ module RubyLsp
|
|
43
37
|
)
|
44
38
|
@lockfile = T.let(@gemfile ? Bundler.default_lockfile : nil, T.nilable(Pathname))
|
45
39
|
|
40
|
+
@gemfile_name = T.let(@gemfile&.basename&.to_s || "Gemfile", String)
|
41
|
+
|
42
|
+
# Custom bundle paths
|
43
|
+
@custom_dir = T.let(Pathname.new(".ruby-lsp").expand_path(Dir.pwd), Pathname)
|
44
|
+
@custom_gemfile = T.let(@custom_dir + @gemfile_name, Pathname)
|
45
|
+
@custom_lockfile = T.let(@custom_dir + (@lockfile&.basename || "Gemfile.lock"), Pathname)
|
46
|
+
@lockfile_hash_path = T.let(@custom_dir + "main_lockfile_hash", Pathname)
|
47
|
+
@last_updated_path = T.let(@custom_dir + "last_updated", Pathname)
|
48
|
+
|
46
49
|
@dependencies = T.let(load_dependencies, T::Hash[String, T.untyped])
|
47
50
|
end
|
48
51
|
|
@@ -118,7 +121,7 @@ module RubyLsp
|
|
118
121
|
# If there's a top level Gemfile, we want to evaluate from the custom bundle. We get the source from the top level
|
119
122
|
# Gemfile, so if there isn't one we need to add a default source
|
120
123
|
if @gemfile&.exist?
|
121
|
-
parts << "eval_gemfile(File.expand_path(\"
|
124
|
+
parts << "eval_gemfile(File.expand_path(\"../#{@gemfile_name}\", __dir__))"
|
122
125
|
else
|
123
126
|
parts.unshift('source "https://rubygems.org"')
|
124
127
|
end
|
@@ -188,7 +191,9 @@ module RubyLsp
|
|
188
191
|
command.prepend("(")
|
189
192
|
command << " && bundle update "
|
190
193
|
command << "ruby-lsp " unless @dependencies["ruby-lsp"]
|
191
|
-
command << "debug" unless @dependencies["debug"]
|
194
|
+
command << "debug " unless @dependencies["debug"]
|
195
|
+
command << "--pre" if @experimental
|
196
|
+
command.delete_suffix!(" ")
|
192
197
|
command << ")"
|
193
198
|
|
194
199
|
@last_updated_path.write(Time.now.iso8601)
|
@@ -202,6 +207,7 @@ module RubyLsp
|
|
202
207
|
|
203
208
|
# Add bundle update
|
204
209
|
warn("Ruby LSP> Running bundle install for the custom bundle. This may take a while...")
|
210
|
+
warn("Ruby LSP> Command: #{command}")
|
205
211
|
system(env, command)
|
206
212
|
[bundle_gemfile.to_s, expanded_path, env["BUNDLE_APP_CONFIG"]]
|
207
213
|
end
|
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.12.
|
4
|
+
version: 0.12.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-10-
|
11
|
+
date: 2023-10-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: language_server-protocol
|
@@ -76,6 +76,7 @@ files:
|
|
76
76
|
- exe/ruby-lsp-doctor
|
77
77
|
- lib/core_ext/uri.rb
|
78
78
|
- lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb
|
79
|
+
- lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb
|
79
80
|
- lib/ruby-lsp.rb
|
80
81
|
- lib/ruby_indexer/lib/ruby_indexer/configuration.rb
|
81
82
|
- lib/ruby_indexer/lib/ruby_indexer/entry.rb
|