ruby-lsp 0.12.0 → 0.12.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|