ruby-lsp-rails 0.3.26 → 0.3.28
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/lib/ruby-lsp-rails.rb +0 -12
- data/lib/ruby_lsp/ruby_lsp_rails/addon.rb +13 -1
- data/lib/ruby_lsp/ruby_lsp_rails/code_lens.rb +3 -3
- data/lib/ruby_lsp/ruby_lsp_rails/completion.rb +92 -0
- data/lib/ruby_lsp/ruby_lsp_rails/definition.rb +2 -8
- data/lib/ruby_lsp/ruby_lsp_rails/indexing_enhancement.rb +41 -51
- data/lib/ruby_lsp/ruby_lsp_rails/runner_client.rb +3 -4
- data/lib/ruby_lsp/ruby_lsp_rails/server.rb +96 -76
- data/lib/ruby_lsp_rails/version.rb +1 -1
- metadata +8 -11
- data/lib/ruby_lsp_rails/railtie.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 231b488bf88047c642f2556980d6c0ce96edbeafbf2ded65a4104515584a5100
|
4
|
+
data.tar.gz: 207b91a283859f74804b4e825d4ca71ee645b0f73ac84e5d7e7348b382bf2745
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8ef4e5363a31a1821da8d43738c5049239e827d609ffa8c8214f9e8fae148739770614a4f06af082da98a755bfd3522426560936d1ac57f0c14092ace8ced86b
|
7
|
+
data.tar.gz: 348fab1dc3ada6a6a2672c4c9b54e0ae0f67f2f056023929af7c76dac5d66583c32c9fbdcc1347fc3913cb59a3e30ae7e495977b762228aad6a52f502254a930
|
data/lib/ruby-lsp-rails.rb
CHANGED
@@ -2,15 +2,3 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "ruby_lsp_rails/version"
|
5
|
-
require "ruby_lsp_rails/railtie"
|
6
|
-
|
7
|
-
module RubyLsp
|
8
|
-
# # Supported features
|
9
|
-
#
|
10
|
-
# - [Hover](rdoc-ref:RubyLsp::Rails::Hover)
|
11
|
-
# - [CodeLens](rdoc-ref:RubyLsp::Rails::CodeLens)
|
12
|
-
# - [DocumentSymbol](rdoc-ref:RubyLsp::Rails::DocumentSymbol)
|
13
|
-
# - [Definition](rdoc-ref:RubyLsp::Rails::Definition)
|
14
|
-
module Rails
|
15
|
-
end
|
16
|
-
end
|
@@ -13,6 +13,7 @@ require_relative "hover"
|
|
13
13
|
require_relative "code_lens"
|
14
14
|
require_relative "document_symbol"
|
15
15
|
require_relative "definition"
|
16
|
+
require_relative "completion"
|
16
17
|
require_relative "indexing_enhancement"
|
17
18
|
|
18
19
|
module RubyLsp
|
@@ -56,7 +57,6 @@ module RubyLsp
|
|
56
57
|
@outgoing_queue << Notification.window_log_message("Activating Ruby LSP Rails add-on v#{VERSION}")
|
57
58
|
|
58
59
|
register_additional_file_watchers(global_state: global_state, outgoing_queue: outgoing_queue)
|
59
|
-
@global_state.index.register_enhancement(IndexingEnhancement.new(@global_state.index))
|
60
60
|
|
61
61
|
# Start booting the real client in a background thread. Until this completes, the client will be a NullClient
|
62
62
|
@client_mutex.unlock
|
@@ -120,6 +120,18 @@ module RubyLsp
|
|
120
120
|
Definition.new(@rails_runner_client, response_builder, node_context, index, dispatcher)
|
121
121
|
end
|
122
122
|
|
123
|
+
sig do
|
124
|
+
override.params(
|
125
|
+
response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem],
|
126
|
+
node_context: NodeContext,
|
127
|
+
dispatcher: Prism::Dispatcher,
|
128
|
+
uri: URI::Generic,
|
129
|
+
).void
|
130
|
+
end
|
131
|
+
def create_completion_listener(response_builder, node_context, dispatcher, uri)
|
132
|
+
Completion.new(@rails_runner_client, response_builder, node_context, dispatcher, uri)
|
133
|
+
end
|
134
|
+
|
123
135
|
sig { params(changes: T::Array[{ uri: String, type: Integer }]).void }
|
124
136
|
def workspace_did_change_watched_files(changes)
|
125
137
|
if changes.any? { |c| c[:uri].end_with?("db/schema.rb") || c[:uri].end_with?("structure.sql") }
|
@@ -254,7 +254,7 @@ module RubyLsp
|
|
254
254
|
|
255
255
|
@response_builder << create_code_lens(
|
256
256
|
node,
|
257
|
-
title: "Run",
|
257
|
+
title: "▶ Run",
|
258
258
|
command_name: "rubyLsp.runTask",
|
259
259
|
arguments: [command],
|
260
260
|
data: { type: "migrate" },
|
@@ -283,7 +283,7 @@ module RubyLsp
|
|
283
283
|
|
284
284
|
@response_builder << create_code_lens(
|
285
285
|
node,
|
286
|
-
title: "Run",
|
286
|
+
title: "▶ Run",
|
287
287
|
command_name: "rubyLsp.runTest",
|
288
288
|
arguments: arguments,
|
289
289
|
data: { type: "test", **grouping_data },
|
@@ -291,7 +291,7 @@ module RubyLsp
|
|
291
291
|
|
292
292
|
@response_builder << create_code_lens(
|
293
293
|
node,
|
294
|
-
title: "Run In Terminal",
|
294
|
+
title: "▶ Run In Terminal",
|
295
295
|
command_name: "rubyLsp.runTestInTerminal",
|
296
296
|
arguments: arguments,
|
297
297
|
data: { type: "test_in_terminal", **grouping_data },
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Rails
|
6
|
+
class Completion
|
7
|
+
extend T::Sig
|
8
|
+
include Requests::Support::Common
|
9
|
+
|
10
|
+
sig do
|
11
|
+
override.params(
|
12
|
+
client: RunnerClient,
|
13
|
+
response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem],
|
14
|
+
node_context: NodeContext,
|
15
|
+
dispatcher: Prism::Dispatcher,
|
16
|
+
uri: URI::Generic,
|
17
|
+
).void
|
18
|
+
end
|
19
|
+
def initialize(client, response_builder, node_context, dispatcher, uri)
|
20
|
+
@response_builder = response_builder
|
21
|
+
@client = client
|
22
|
+
@node_context = node_context
|
23
|
+
dispatcher.register(
|
24
|
+
self,
|
25
|
+
:on_call_node_enter,
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
sig { params(node: Prism::CallNode).void }
|
30
|
+
def on_call_node_enter(node)
|
31
|
+
call_node = @node_context.call_node
|
32
|
+
return unless call_node
|
33
|
+
|
34
|
+
receiver = call_node.receiver
|
35
|
+
if call_node.name == :where && receiver.is_a?(Prism::ConstantReadNode)
|
36
|
+
handle_active_record_where_completions(node: node, receiver: receiver)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
sig { params(node: Prism::CallNode, receiver: Prism::ConstantReadNode).void }
|
43
|
+
def handle_active_record_where_completions(node:, receiver:)
|
44
|
+
resolved_class = @client.model(receiver.name.to_s)
|
45
|
+
return if resolved_class.nil?
|
46
|
+
|
47
|
+
arguments = T.must(@node_context.call_node).arguments&.arguments
|
48
|
+
indexed_call_node_args = T.let({}, T::Hash[String, Prism::Node])
|
49
|
+
|
50
|
+
if arguments
|
51
|
+
indexed_call_node_args = index_call_node_args(arguments: arguments)
|
52
|
+
return if indexed_call_node_args.values.any? { |v| v == node }
|
53
|
+
end
|
54
|
+
|
55
|
+
range = range_from_location(node.location)
|
56
|
+
|
57
|
+
resolved_class[:columns].each do |column|
|
58
|
+
next unless column[0].start_with?(node.name.to_s)
|
59
|
+
next if indexed_call_node_args.key?(column[0])
|
60
|
+
|
61
|
+
@response_builder << Interface::CompletionItem.new(
|
62
|
+
label: column[0],
|
63
|
+
filter_text: column[0],
|
64
|
+
label_details: Interface::CompletionItemLabelDetails.new(
|
65
|
+
description: "Filter #{receiver.name} records by #{column[0]}",
|
66
|
+
),
|
67
|
+
text_edit: Interface::TextEdit.new(range: range, new_text: "#{column[0]}: "),
|
68
|
+
kind: Constant::CompletionItemKind::FIELD,
|
69
|
+
)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
sig { params(arguments: T::Array[Prism::Node]).returns(T::Hash[String, Prism::Node]) }
|
74
|
+
def index_call_node_args(arguments:)
|
75
|
+
indexed_call_node_args = {}
|
76
|
+
arguments.each do |argument|
|
77
|
+
next unless argument.is_a?(Prism::KeywordHashNode)
|
78
|
+
|
79
|
+
argument.elements.each do |e|
|
80
|
+
next unless e.is_a?(Prism::AssocNode)
|
81
|
+
|
82
|
+
key = e.key
|
83
|
+
if key.is_a?(Prism::SymbolNode)
|
84
|
+
indexed_call_node_args[key.value] = e.value
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
indexed_call_node_args
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -144,15 +144,9 @@ module RubyLsp
|
|
144
144
|
return unless methods
|
145
145
|
|
146
146
|
methods.each do |target_method|
|
147
|
-
location = target_method.location
|
148
|
-
file_path = target_method.file_path
|
149
|
-
|
150
147
|
@response_builder << Interface::Location.new(
|
151
|
-
uri:
|
152
|
-
range:
|
153
|
-
start: Interface::Position.new(line: location.start_line - 1, character: location.start_column),
|
154
|
-
end: Interface::Position.new(line: location.end_line - 1, character: location.end_column),
|
155
|
-
),
|
148
|
+
uri: target_method.uri.to_s,
|
149
|
+
range: range_from_location(target_method.location),
|
156
150
|
)
|
157
151
|
end
|
158
152
|
end
|
@@ -8,25 +8,32 @@ module RubyLsp
|
|
8
8
|
|
9
9
|
sig do
|
10
10
|
override.params(
|
11
|
-
|
12
|
-
node: Prism::CallNode,
|
13
|
-
file_path: String,
|
14
|
-
code_units_cache: T.any(
|
15
|
-
T.proc.params(arg0: Integer).returns(Integer),
|
16
|
-
Prism::CodeUnitsCache,
|
17
|
-
),
|
11
|
+
call_node: Prism::CallNode,
|
18
12
|
).void
|
19
13
|
end
|
20
|
-
def on_call_node_enter(
|
14
|
+
def on_call_node_enter(call_node)
|
15
|
+
owner = @listener.current_owner
|
21
16
|
return unless owner
|
22
17
|
|
23
|
-
|
24
|
-
|
25
|
-
case name
|
18
|
+
case call_node.name
|
26
19
|
when :extend
|
27
|
-
handle_concern_extend(owner,
|
20
|
+
handle_concern_extend(owner, call_node)
|
28
21
|
when :has_one, :has_many, :belongs_to, :has_and_belongs_to_many
|
29
|
-
handle_association(owner,
|
22
|
+
handle_association(owner, call_node)
|
23
|
+
# for `class_methods do` blocks within concerns
|
24
|
+
when :class_methods
|
25
|
+
handle_class_methods(owner, call_node)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
sig do
|
30
|
+
override.params(
|
31
|
+
call_node: Prism::CallNode,
|
32
|
+
).void
|
33
|
+
end
|
34
|
+
def on_call_node_leave(call_node)
|
35
|
+
if call_node.name == :class_methods && call_node.block
|
36
|
+
@listener.pop_namespace_stack
|
30
37
|
end
|
31
38
|
end
|
32
39
|
|
@@ -35,16 +42,11 @@ module RubyLsp
|
|
35
42
|
sig do
|
36
43
|
params(
|
37
44
|
owner: RubyIndexer::Entry::Namespace,
|
38
|
-
|
39
|
-
file_path: String,
|
40
|
-
code_units_cache: T.any(
|
41
|
-
T.proc.params(arg0: Integer).returns(Integer),
|
42
|
-
Prism::CodeUnitsCache,
|
43
|
-
),
|
45
|
+
call_node: Prism::CallNode,
|
44
46
|
).void
|
45
47
|
end
|
46
|
-
def handle_association(owner,
|
47
|
-
arguments =
|
48
|
+
def handle_association(owner, call_node)
|
49
|
+
arguments = call_node.arguments&.arguments
|
48
50
|
return unless arguments
|
49
51
|
|
50
52
|
name_arg = arguments.first
|
@@ -58,41 +60,22 @@ module RubyLsp
|
|
58
60
|
|
59
61
|
return unless name
|
60
62
|
|
61
|
-
loc =
|
63
|
+
loc = name_arg.location
|
62
64
|
|
63
65
|
# Reader
|
64
|
-
|
65
|
-
|
66
|
-
file_path,
|
67
|
-
loc,
|
68
|
-
loc,
|
69
|
-
nil,
|
70
|
-
[RubyIndexer::Entry::Signature.new([])],
|
71
|
-
RubyIndexer::Entry::Visibility::PUBLIC,
|
72
|
-
owner,
|
73
|
-
))
|
66
|
+
reader_signatures = [RubyIndexer::Entry::Signature.new([])]
|
67
|
+
@listener.add_method(name, loc, reader_signatures)
|
74
68
|
|
75
69
|
# Writer
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
loc,
|
81
|
-
nil,
|
82
|
-
[RubyIndexer::Entry::Signature.new([RubyIndexer::Entry::RequiredParameter.new(name: name.to_sym)])],
|
83
|
-
RubyIndexer::Entry::Visibility::PUBLIC,
|
84
|
-
owner,
|
85
|
-
))
|
70
|
+
writer_signatures = [
|
71
|
+
RubyIndexer::Entry::Signature.new([RubyIndexer::Entry::RequiredParameter.new(name: name.to_sym)]),
|
72
|
+
]
|
73
|
+
@listener.add_method("#{name}=", loc, writer_signatures)
|
86
74
|
end
|
87
75
|
|
88
|
-
sig
|
89
|
-
|
90
|
-
|
91
|
-
node: Prism::CallNode,
|
92
|
-
).void
|
93
|
-
end
|
94
|
-
def handle_concern_extend(owner, node)
|
95
|
-
arguments = node.arguments&.arguments
|
76
|
+
sig { params(owner: RubyIndexer::Entry::Namespace, call_node: Prism::CallNode).void }
|
77
|
+
def handle_concern_extend(owner, call_node)
|
78
|
+
arguments = call_node.arguments&.arguments
|
96
79
|
return unless arguments
|
97
80
|
|
98
81
|
arguments.each do |node|
|
@@ -101,7 +84,7 @@ module RubyLsp
|
|
101
84
|
module_name = node.full_name
|
102
85
|
next unless module_name == "ActiveSupport::Concern"
|
103
86
|
|
104
|
-
@
|
87
|
+
@listener.register_included_hook do |index, base|
|
105
88
|
class_methods_name = "#{owner.name}::ClassMethods"
|
106
89
|
|
107
90
|
if index.indexed?(class_methods_name)
|
@@ -114,6 +97,13 @@ module RubyLsp
|
|
114
97
|
# Do nothing
|
115
98
|
end
|
116
99
|
end
|
100
|
+
|
101
|
+
sig { params(owner: RubyIndexer::Entry::Namespace, call_node: Prism::CallNode).void }
|
102
|
+
def handle_class_methods(owner, call_node)
|
103
|
+
return unless call_node.block
|
104
|
+
|
105
|
+
@listener.add_module("ClassMethods", call_node.location, call_node.location)
|
106
|
+
end
|
117
107
|
end
|
118
108
|
end
|
119
109
|
end
|
@@ -27,7 +27,7 @@ module RubyLsp
|
|
27
27
|
|
28
28
|
NullClient.new
|
29
29
|
end
|
30
|
-
rescue
|
30
|
+
rescue StandardError => e
|
31
31
|
unless outgoing_queue.closed?
|
32
32
|
outgoing_queue << RubyLsp::Notification.window_log_message(
|
33
33
|
<<~MESSAGE.chomp,
|
@@ -44,7 +44,6 @@ module RubyLsp
|
|
44
44
|
|
45
45
|
class InitializationError < StandardError; end
|
46
46
|
class MessageError < StandardError; end
|
47
|
-
class IncompleteMessageError < MessageError; end
|
48
47
|
class EmptyMessageError < MessageError; end
|
49
48
|
|
50
49
|
extend T::Sig
|
@@ -109,7 +108,7 @@ module RubyLsp
|
|
109
108
|
end,
|
110
109
|
Thread,
|
111
110
|
)
|
112
|
-
rescue
|
111
|
+
rescue StandardError
|
113
112
|
raise InitializationError, @stderr.read
|
114
113
|
end
|
115
114
|
|
@@ -282,7 +281,7 @@ module RubyLsp
|
|
282
281
|
json = message.to_json
|
283
282
|
|
284
283
|
@mutex.synchronize do
|
285
|
-
@stdin.write("Content-Length: #{json.
|
284
|
+
@stdin.write("Content-Length: #{json.bytesize}\r\n\r\n", json)
|
286
285
|
end
|
287
286
|
rescue Errno::EPIPE
|
288
287
|
# The server connection died
|
@@ -13,13 +13,49 @@ module RubyLsp
|
|
13
13
|
# Write a message to the client. Can be used for sending notifications to the editor
|
14
14
|
def send_message(message)
|
15
15
|
json_message = message.to_json
|
16
|
-
@stdout.write("Content-Length: #{json_message.
|
16
|
+
@stdout.write("Content-Length: #{json_message.bytesize}\r\n\r\n#{json_message}")
|
17
17
|
end
|
18
18
|
|
19
19
|
# Log a message to the editor's output panel
|
20
20
|
def log_message(message)
|
21
21
|
$stderr.puts(message)
|
22
|
-
|
22
|
+
end
|
23
|
+
|
24
|
+
# Sends an error result to a request, if the request failed. DO NOT INVOKE THIS METHOD FOR NOTIFICATIONS! Use
|
25
|
+
# `log_message` instead, otherwise the client/server communication will go out of sync
|
26
|
+
def send_error_response(message)
|
27
|
+
send_message({ error: message })
|
28
|
+
end
|
29
|
+
|
30
|
+
# Sends a result back to the client
|
31
|
+
def send_result(result)
|
32
|
+
send_message({ result: result })
|
33
|
+
end
|
34
|
+
|
35
|
+
# Handle possible errors for a request. This should only be used for requests, which means messages that return a
|
36
|
+
# response back to the client. Errors are returned as an error object back to the client
|
37
|
+
def with_request_error_handling(request_name, &block)
|
38
|
+
block.call
|
39
|
+
rescue ActiveRecord::ConnectionNotEstablished
|
40
|
+
# Since this is a common problem, we show a specific error message to the user, instead of the full stack trace.
|
41
|
+
send_error_response("Request #{request_name} failed because database connection was not established.")
|
42
|
+
rescue ActiveRecord::NoDatabaseError
|
43
|
+
send_error_response("Request #{request_name} failed because the database does not exist.")
|
44
|
+
rescue => e
|
45
|
+
send_error_response("Request #{request_name} failed:\n#{e.full_message(highlight: false)}")
|
46
|
+
end
|
47
|
+
|
48
|
+
# Handle possible errors for a notification. This should only be used for notifications, which means messages that
|
49
|
+
# do not return a response back to the client. Errors are logged to the editor's output panel
|
50
|
+
def with_notification_error_handling(notification_name, &block)
|
51
|
+
block.call
|
52
|
+
rescue ActiveRecord::ConnectionNotEstablished
|
53
|
+
# Since this is a common problem, we show a specific error message to the user, instead of the full stack trace.
|
54
|
+
log_message("Request #{notification_name} failed because database connection was not established.")
|
55
|
+
rescue ActiveRecord::NoDatabaseError
|
56
|
+
log_message("Request #{notification_name} failed because the database does not exist.")
|
57
|
+
rescue => e
|
58
|
+
log_message("Request #{notification_name} failed:\n#{e.full_message(highlight: false)}")
|
23
59
|
end
|
24
60
|
end
|
25
61
|
|
@@ -88,11 +124,8 @@ module RubyLsp
|
|
88
124
|
end
|
89
125
|
|
90
126
|
def start
|
91
|
-
|
92
|
-
|
93
|
-
routes_reloader.execute_unless_loaded if routes_reloader&.respond_to?(:execute_unless_loaded)
|
94
|
-
|
95
|
-
send_message({ result: { message: "ok", root: ::Rails.root.to_s } })
|
127
|
+
load_routes
|
128
|
+
send_result({ message: "ok", root: ::Rails.root.to_s })
|
96
129
|
|
97
130
|
while @running
|
98
131
|
headers = @stdin.gets("\r\n\r\n")
|
@@ -104,41 +137,50 @@ module RubyLsp
|
|
104
137
|
end
|
105
138
|
|
106
139
|
def execute(request, params)
|
107
|
-
request_name = request
|
108
|
-
request_name = "#{params[:server_addon_name]}##{params[:request_name]}" if request == "server_addon/delegate"
|
109
|
-
|
110
140
|
case request
|
111
141
|
when "shutdown"
|
112
142
|
@running = false
|
113
143
|
when "model"
|
114
|
-
|
144
|
+
with_request_error_handling(request) do
|
145
|
+
send_result(resolve_database_info_from_model(params.fetch(:name)))
|
146
|
+
end
|
115
147
|
when "association_target_location"
|
116
|
-
|
148
|
+
with_request_error_handling(request) do
|
149
|
+
send_result(resolve_association_target(params))
|
150
|
+
end
|
117
151
|
when "pending_migrations_message"
|
118
|
-
|
152
|
+
with_request_error_handling(request) do
|
153
|
+
send_result({ pending_migrations_message: pending_migrations_message })
|
154
|
+
end
|
119
155
|
when "run_migrations"
|
120
|
-
|
156
|
+
with_request_error_handling(request) do
|
157
|
+
send_result(run_migrations)
|
158
|
+
end
|
121
159
|
when "reload"
|
122
|
-
|
160
|
+
with_notification_error_handling(request) do
|
161
|
+
::Rails.application.reloader.reload!
|
162
|
+
end
|
123
163
|
when "route_location"
|
124
|
-
|
164
|
+
with_request_error_handling(request) do
|
165
|
+
send_result(route_location(params.fetch(:name)))
|
166
|
+
end
|
125
167
|
when "route_info"
|
126
|
-
|
168
|
+
with_request_error_handling(request) do
|
169
|
+
send_result(resolve_route_info(params))
|
170
|
+
end
|
127
171
|
when "server_addon/register"
|
128
|
-
|
129
|
-
|
172
|
+
with_notification_error_handling(request) do
|
173
|
+
require params[:server_addon_path]
|
174
|
+
ServerAddon.finalize_registrations!(@stdout)
|
175
|
+
end
|
130
176
|
when "server_addon/delegate"
|
131
177
|
server_addon_name = params[:server_addon_name]
|
132
178
|
request_name = params[:request_name]
|
179
|
+
|
180
|
+
# Do not wrap this in error handlers. Server add-ons need to have the flexibility to choose if they want to
|
181
|
+
# include a response or not as part of error handling, so a blanket approach is not appropriate.
|
133
182
|
ServerAddon.delegate(server_addon_name, request_name, params.except(:request_name, :server_addon_name))
|
134
183
|
end
|
135
|
-
# Since this is a common problem, we show a specific error message to the user, instead of the full stack trace.
|
136
|
-
rescue ActiveRecord::ConnectionNotEstablished
|
137
|
-
log_message("Request #{request_name} failed because database connection was not established.")
|
138
|
-
rescue ActiveRecord::NoDatabaseError
|
139
|
-
log_message("Request #{request_name} failed because the database does not exist.")
|
140
|
-
rescue => e
|
141
|
-
log_message("Request #{request_name} failed:\n" + e.full_message(highlight: false))
|
142
184
|
end
|
143
185
|
|
144
186
|
private
|
@@ -156,19 +198,15 @@ module RubyLsp
|
|
156
198
|
end
|
157
199
|
|
158
200
|
source_location = route&.respond_to?(:source_location) && route.source_location
|
201
|
+
return unless source_location
|
159
202
|
|
160
|
-
|
161
|
-
file, _, line = source_location.rpartition(":")
|
162
|
-
body = {
|
163
|
-
source_location: [::Rails.root.join(file).to_s, line],
|
164
|
-
verb: route.verb,
|
165
|
-
path: route.path.spec.to_s,
|
166
|
-
}
|
203
|
+
file, _, line = source_location.rpartition(":")
|
167
204
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
205
|
+
{
|
206
|
+
source_location: [::Rails.root.join(file).to_s, line],
|
207
|
+
verb: route.verb,
|
208
|
+
path: route.path.spec.to_s,
|
209
|
+
}
|
172
210
|
end
|
173
211
|
|
174
212
|
# Older versions of Rails don't support `route_source_locations`.
|
@@ -182,74 +220,48 @@ module RubyLsp
|
|
182
220
|
end
|
183
221
|
|
184
222
|
match_data = name.match(/^(.+)(_path|_url)$/)
|
185
|
-
return
|
223
|
+
return unless match_data
|
186
224
|
|
187
225
|
key = match_data[1]
|
188
226
|
|
189
227
|
# A token could match the _path or _url pattern, but not be an actual route.
|
190
228
|
route = ::Rails.application.routes.named_routes.get(key)
|
191
|
-
return
|
192
|
-
|
193
|
-
{
|
194
|
-
result: {
|
195
|
-
location: ::Rails.root.join(route.source_location).to_s,
|
196
|
-
},
|
197
|
-
}
|
198
|
-
rescue => e
|
199
|
-
{ error: e.full_message(highlight: false) }
|
229
|
+
return unless route&.source_location
|
230
|
+
|
231
|
+
{ location: ::Rails.root.join(route.source_location).to_s }
|
200
232
|
end
|
201
233
|
else
|
202
234
|
def route_location(name)
|
203
|
-
|
235
|
+
nil
|
204
236
|
end
|
205
237
|
end
|
206
238
|
|
207
239
|
def resolve_database_info_from_model(model_name)
|
208
240
|
const = ActiveSupport::Inflector.safe_constantize(model_name)
|
209
|
-
unless active_record_model?(const)
|
210
|
-
return {
|
211
|
-
result: nil,
|
212
|
-
}
|
213
|
-
end
|
241
|
+
return unless active_record_model?(const)
|
214
242
|
|
215
243
|
info = {
|
216
|
-
|
217
|
-
|
218
|
-
primary_keys: Array(const.primary_key),
|
219
|
-
},
|
244
|
+
columns: const.columns.map { |column| [column.name, column.type, column.default, column.null] },
|
245
|
+
primary_keys: Array(const.primary_key),
|
220
246
|
}
|
221
247
|
|
222
248
|
if ActiveRecord::Tasks::DatabaseTasks.respond_to?(:schema_dump_path)
|
223
|
-
info[:
|
224
|
-
ActiveRecord::Tasks::DatabaseTasks.schema_dump_path(const.connection.pool.db_config)
|
225
|
-
|
249
|
+
info[:schema_file] = ActiveRecord::Tasks::DatabaseTasks.schema_dump_path(const.connection.pool.db_config)
|
226
250
|
end
|
251
|
+
|
227
252
|
info
|
228
|
-
rescue => e
|
229
|
-
{ error: e.full_message(highlight: false) }
|
230
253
|
end
|
231
254
|
|
232
255
|
def resolve_association_target(params)
|
233
256
|
const = ActiveSupport::Inflector.safe_constantize(params[:model_name])
|
234
|
-
unless active_record_model?(const)
|
235
|
-
return {
|
236
|
-
result: nil,
|
237
|
-
}
|
238
|
-
end
|
257
|
+
return unless active_record_model?(const)
|
239
258
|
|
240
259
|
association_klass = const.reflect_on_association(params[:association_name].intern).klass
|
241
|
-
|
242
260
|
source_location = Object.const_source_location(association_klass.to_s)
|
243
261
|
|
244
|
-
{
|
245
|
-
result: {
|
246
|
-
location: source_location.first + ":" + source_location.second.to_s,
|
247
|
-
},
|
248
|
-
}
|
262
|
+
{ location: source_location.first + ":" + source_location.second.to_s }
|
249
263
|
rescue NameError
|
250
|
-
|
251
|
-
result: nil,
|
252
|
-
}
|
264
|
+
nil
|
253
265
|
end
|
254
266
|
|
255
267
|
def active_record_model?(const)
|
@@ -282,6 +294,14 @@ module RubyLsp
|
|
282
294
|
|
283
295
|
{ message: stdout, status: status.exitstatus }
|
284
296
|
end
|
297
|
+
|
298
|
+
def load_routes
|
299
|
+
with_notification_error_handling("initial_load_routes") do
|
300
|
+
# Load routes if they haven't been loaded yet (see https://github.com/rails/rails/pull/51614).
|
301
|
+
routes_reloader = ::Rails.application.routes_reloader
|
302
|
+
routes_reloader.execute_unless_loaded if routes_reloader&.respond_to?(:execute_unless_loaded)
|
303
|
+
end
|
304
|
+
end
|
285
305
|
end
|
286
306
|
end
|
287
307
|
end
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-lsp-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.28
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-01-07 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: ruby-lsp
|
@@ -16,20 +15,20 @@ dependencies:
|
|
16
15
|
requirements:
|
17
16
|
- - ">="
|
18
17
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
18
|
+
version: 0.23.0
|
20
19
|
- - "<"
|
21
20
|
- !ruby/object:Gem::Version
|
22
|
-
version: 0.
|
21
|
+
version: 0.24.0
|
23
22
|
type: :runtime
|
24
23
|
prerelease: false
|
25
24
|
version_requirements: !ruby/object:Gem::Requirement
|
26
25
|
requirements:
|
27
26
|
- - ">="
|
28
27
|
- !ruby/object:Gem::Version
|
29
|
-
version: 0.
|
28
|
+
version: 0.23.0
|
30
29
|
- - "<"
|
31
30
|
- !ruby/object:Gem::Version
|
32
|
-
version: 0.
|
31
|
+
version: 0.24.0
|
33
32
|
description: A Ruby LSP addon that adds extra editor functionality for Rails applications
|
34
33
|
email:
|
35
34
|
- ruby@shopify.com
|
@@ -43,6 +42,7 @@ files:
|
|
43
42
|
- lib/ruby-lsp-rails.rb
|
44
43
|
- lib/ruby_lsp/ruby_lsp_rails/addon.rb
|
45
44
|
- lib/ruby_lsp/ruby_lsp_rails/code_lens.rb
|
45
|
+
- lib/ruby_lsp/ruby_lsp_rails/completion.rb
|
46
46
|
- lib/ruby_lsp/ruby_lsp_rails/definition.rb
|
47
47
|
- lib/ruby_lsp/ruby_lsp_rails/document_symbol.rb
|
48
48
|
- lib/ruby_lsp/ruby_lsp_rails/hover.rb
|
@@ -53,7 +53,6 @@ files:
|
|
53
53
|
- lib/ruby_lsp/ruby_lsp_rails/support/associations.rb
|
54
54
|
- lib/ruby_lsp/ruby_lsp_rails/support/callbacks.rb
|
55
55
|
- lib/ruby_lsp/ruby_lsp_rails/support/location_builder.rb
|
56
|
-
- lib/ruby_lsp_rails/railtie.rb
|
57
56
|
- lib/ruby_lsp_rails/version.rb
|
58
57
|
- lib/tasks/ruby_lsp_rails_tasks.rake
|
59
58
|
homepage: https://github.com/Shopify/ruby-lsp-rails
|
@@ -65,7 +64,6 @@ metadata:
|
|
65
64
|
source_code_uri: https://github.com/Shopify/ruby-lsp-rails
|
66
65
|
changelog_uri: https://github.com/Shopify/ruby-lsp-rails/releases
|
67
66
|
documentation_uri: https://shopify.github.io/ruby-lsp/rails-add-on.html
|
68
|
-
post_install_message:
|
69
67
|
rdoc_options: []
|
70
68
|
require_paths:
|
71
69
|
- lib
|
@@ -80,8 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
80
78
|
- !ruby/object:Gem::Version
|
81
79
|
version: '0'
|
82
80
|
requirements: []
|
83
|
-
rubygems_version: 3.
|
84
|
-
signing_key:
|
81
|
+
rubygems_version: 3.6.2
|
85
82
|
specification_version: 4
|
86
83
|
summary: A Ruby LSP addon for Rails
|
87
84
|
test_files: []
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# typed: strict
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require "rails"
|
5
|
-
|
6
|
-
module RubyLsp
|
7
|
-
module Rails
|
8
|
-
class Railtie < ::Rails::Railtie
|
9
|
-
config.ruby_lsp_rails = ActiveSupport::OrderedOptions.new
|
10
|
-
|
11
|
-
initializer "ruby_lsp_rails.setup" do |_app|
|
12
|
-
config.after_initialize do |_app|
|
13
|
-
unless config.ruby_lsp_rails.server.nil?
|
14
|
-
ActiveSupport::Deprecation.new.warn("The `ruby_lsp_rails.server` configuration option is no longer " \
|
15
|
-
"needed and will be removed in a future release.")
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|