ruby-lsp-rails 0.3.5 → 0.3.6
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 +2 -3
- data/lib/ruby_lsp/ruby_lsp_rails/addon.rb +29 -1
- data/lib/ruby_lsp/ruby_lsp_rails/code_lens.rb +12 -5
- data/lib/ruby_lsp/ruby_lsp_rails/definition.rb +42 -3
- data/lib/ruby_lsp/ruby_lsp_rails/document_symbol.rb +44 -3
- data/lib/ruby_lsp/ruby_lsp_rails/hover.rb +3 -2
- data/lib/ruby_lsp/ruby_lsp_rails/runner_client.rb +18 -7
- data/lib/ruby_lsp/ruby_lsp_rails/server.rb +46 -31
- data/lib/ruby_lsp/ruby_lsp_rails/support/rails_document_client.rb +10 -3
- data/lib/ruby_lsp_rails/version.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 65c6258c3267f6d1d080e056f4561d61cf8f9bb93059dec1c0b70086fa0ce540
|
|
4
|
+
data.tar.gz: 536aac52752c432fc3c18d76c66151c099533849abc8968aafe832524e25303b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3b527faec645912d5c49aef3d4d2bb094a789410283ddcebcc9be168f703807831a5a5f2561a88dcecf5c4e3d894c0d2ccf9fe617688f09462e88ab7ae1a2ee8
|
|
7
|
+
data.tar.gz: b080811bd5697ae68573df2fd6d922dec47e2e16b5264cd86b707f73cab9689fffa858851da2ebed7a819930fbc148234db4de5c4a15dc2e4fffbec3e4fdfbcb
|
data/README.md
CHANGED
|
@@ -6,6 +6,7 @@ Ruby LSP Rails is a [Ruby LSP](https://github.com/Shopify/ruby-lsp) addon for ex
|
|
|
6
6
|
* Run or debug a test by clicking on the code lens which appears above the test class, or an individual test.
|
|
7
7
|
* Navigate to associations, validations, callbacks and test cases using your editor's "Go to Symbol" feature, or outline view.
|
|
8
8
|
* Jump to the definition of callbacks using your editor's "Go to Definition" feature.
|
|
9
|
+
* Jump to the declaration of a route.
|
|
9
10
|
|
|
10
11
|
## Installation
|
|
11
12
|
|
|
@@ -36,9 +37,7 @@ When extension is stopped (e.g. by quitting the editor), the server instance is
|
|
|
36
37
|
|
|
37
38
|
## Contributing
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the
|
|
41
|
-
[Contributor Covenant](https://github.com/Shopify/ruby-lsp-rails/blob/main/CODE_OF_CONDUCT.md) code of conduct.
|
|
40
|
+
See [CONTRIBUTING.md](https://github.com/Shopify/ruby-lsp-rails/blob/main/CONTRIBUTING.md)
|
|
42
41
|
|
|
43
42
|
## License
|
|
44
43
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
require "ruby_lsp/addon"
|
|
5
5
|
|
|
6
|
+
require_relative "../../ruby_lsp_rails/version"
|
|
6
7
|
require_relative "support/active_support_test_case_helper"
|
|
7
8
|
require_relative "support/callbacks"
|
|
8
9
|
require_relative "runner_client"
|
|
@@ -31,6 +32,7 @@ module RubyLsp
|
|
|
31
32
|
$stderr.puts("Activating Ruby LSP Rails addon v#{VERSION}")
|
|
32
33
|
# Start booting the real client in a background thread. Until this completes, the client will be a NullClient
|
|
33
34
|
Thread.new { @client = RunnerClient.create_client }
|
|
35
|
+
register_additional_file_watchers(global_state: global_state, message_queue: message_queue)
|
|
34
36
|
end
|
|
35
37
|
|
|
36
38
|
sig { override.void }
|
|
@@ -83,7 +85,7 @@ module RubyLsp
|
|
|
83
85
|
end
|
|
84
86
|
def create_definition_listener(response_builder, uri, nesting, dispatcher)
|
|
85
87
|
index = T.must(@global_state).index
|
|
86
|
-
Definition.new(response_builder, nesting, index, dispatcher)
|
|
88
|
+
Definition.new(@client, response_builder, nesting, index, dispatcher)
|
|
87
89
|
end
|
|
88
90
|
|
|
89
91
|
sig { params(changes: T::Array[{ uri: String, type: Integer }]).void }
|
|
@@ -95,6 +97,32 @@ module RubyLsp
|
|
|
95
97
|
end
|
|
96
98
|
end
|
|
97
99
|
|
|
100
|
+
sig { params(global_state: GlobalState, message_queue: Thread::Queue).void }
|
|
101
|
+
def register_additional_file_watchers(global_state:, message_queue:)
|
|
102
|
+
return unless global_state.supports_watching_files
|
|
103
|
+
|
|
104
|
+
message_queue << Request.new(
|
|
105
|
+
id: "ruby-lsp-rails-file-watcher",
|
|
106
|
+
method: "client/registerCapability",
|
|
107
|
+
params: Interface::RegistrationParams.new(
|
|
108
|
+
registrations: [
|
|
109
|
+
Interface::Registration.new(
|
|
110
|
+
id: "workspace/didChangeWatchedFilesRails",
|
|
111
|
+
method: "workspace/didChangeWatchedFiles",
|
|
112
|
+
register_options: Interface::DidChangeWatchedFilesRegistrationOptions.new(
|
|
113
|
+
watchers: [
|
|
114
|
+
Interface::FileSystemWatcher.new(
|
|
115
|
+
glob_pattern: "**/*structure.sql",
|
|
116
|
+
kind: Constant::WatchKind::CREATE | Constant::WatchKind::CHANGE | Constant::WatchKind::DELETE,
|
|
117
|
+
),
|
|
118
|
+
],
|
|
119
|
+
),
|
|
120
|
+
),
|
|
121
|
+
],
|
|
122
|
+
),
|
|
123
|
+
)
|
|
124
|
+
end
|
|
125
|
+
|
|
98
126
|
sig { override.returns(String) }
|
|
99
127
|
def name
|
|
100
128
|
"Ruby LSP Rails"
|
|
@@ -42,8 +42,6 @@ module RubyLsp
|
|
|
42
42
|
include Requests::Support::Common
|
|
43
43
|
include ActiveSupportTestCaseHelper
|
|
44
44
|
|
|
45
|
-
BASE_COMMAND = "bin/rails test"
|
|
46
|
-
|
|
47
45
|
sig do
|
|
48
46
|
params(
|
|
49
47
|
response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens],
|
|
@@ -67,7 +65,7 @@ module RubyLsp
|
|
|
67
65
|
return unless content
|
|
68
66
|
|
|
69
67
|
line_number = node.location.start_line
|
|
70
|
-
command = "#{
|
|
68
|
+
command = "#{test_command} #{@path}:#{line_number}"
|
|
71
69
|
add_test_code_lens(node, name: content, command: command, kind: :example)
|
|
72
70
|
end
|
|
73
71
|
|
|
@@ -77,7 +75,7 @@ module RubyLsp
|
|
|
77
75
|
method_name = node.name.to_s
|
|
78
76
|
if method_name.start_with?("test_")
|
|
79
77
|
line_number = node.location.start_line
|
|
80
|
-
command = "#{
|
|
78
|
+
command = "#{test_command} #{@path}:#{line_number}"
|
|
81
79
|
add_test_code_lens(node, name: method_name, command: command, kind: :example)
|
|
82
80
|
end
|
|
83
81
|
end
|
|
@@ -86,7 +84,7 @@ module RubyLsp
|
|
|
86
84
|
def on_class_node_enter(node)
|
|
87
85
|
class_name = node.constant_path.slice
|
|
88
86
|
if class_name.end_with?("Test")
|
|
89
|
-
command = "#{
|
|
87
|
+
command = "#{test_command} #{@path}"
|
|
90
88
|
add_test_code_lens(node, name: class_name, command: command, kind: :group)
|
|
91
89
|
@group_id_stack.push(@group_id)
|
|
92
90
|
@group_id += 1
|
|
@@ -103,6 +101,15 @@ module RubyLsp
|
|
|
103
101
|
|
|
104
102
|
private
|
|
105
103
|
|
|
104
|
+
sig { returns(String) }
|
|
105
|
+
def test_command
|
|
106
|
+
if Gem.win_platform?
|
|
107
|
+
"ruby bin/rails test"
|
|
108
|
+
else
|
|
109
|
+
"bin/rails test"
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
106
113
|
sig { params(node: Prism::Node, name: String, command: String, kind: Symbol).void }
|
|
107
114
|
def add_test_code_lens(node, name:, command:, kind:)
|
|
108
115
|
return unless @path
|
|
@@ -11,25 +11,35 @@ module RubyLsp
|
|
|
11
11
|
#
|
|
12
12
|
# Currently supported targets:
|
|
13
13
|
# - Callbacks
|
|
14
|
+
# - Named routes (e.g. `users_path`)
|
|
14
15
|
#
|
|
15
16
|
# # Example
|
|
16
17
|
#
|
|
17
18
|
# ```ruby
|
|
18
19
|
# before_action :foo # <- Go to definition on this symbol will jump to the method if it is defined in the same class
|
|
19
20
|
# ```
|
|
21
|
+
#
|
|
22
|
+
# Notes for named routes:
|
|
23
|
+
# - It is available only in Rails 7.1 or newer.
|
|
24
|
+
# - Route may be defined across multiple files, e.g. using `draw`, rather than in `routes.rb`.
|
|
25
|
+
# - Routes won't be found if not defined for the Rails development environment.
|
|
26
|
+
# - If using `constraints`, the route can only be found if the constraints are met.
|
|
27
|
+
# - Changes to routes won't be picked up until the server is restarted.
|
|
20
28
|
class Definition
|
|
21
29
|
extend T::Sig
|
|
22
30
|
include Requests::Support::Common
|
|
23
31
|
|
|
24
32
|
sig do
|
|
25
33
|
params(
|
|
34
|
+
client: RunnerClient,
|
|
26
35
|
response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::Location],
|
|
27
36
|
nesting: T::Array[String],
|
|
28
37
|
index: RubyIndexer::Index,
|
|
29
38
|
dispatcher: Prism::Dispatcher,
|
|
30
39
|
).void
|
|
31
40
|
end
|
|
32
|
-
def initialize(response_builder, nesting, index, dispatcher)
|
|
41
|
+
def initialize(client, response_builder, nesting, index, dispatcher)
|
|
42
|
+
@client = client
|
|
33
43
|
@response_builder = response_builder
|
|
34
44
|
@nesting = nesting
|
|
35
45
|
@index = index
|
|
@@ -43,8 +53,19 @@ module RubyLsp
|
|
|
43
53
|
|
|
44
54
|
message = node.message
|
|
45
55
|
|
|
46
|
-
return unless message
|
|
56
|
+
return unless message
|
|
57
|
+
|
|
58
|
+
if Support::Callbacks::ALL.include?(message)
|
|
59
|
+
handle_callback(node)
|
|
60
|
+
elsif message.end_with?("_path") || message.end_with?("_url")
|
|
61
|
+
handle_route(node)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
47
64
|
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
sig { params(node: Prism::CallNode).void }
|
|
68
|
+
def handle_callback(node)
|
|
48
69
|
arguments = node.arguments&.arguments
|
|
49
70
|
return unless arguments&.any?
|
|
50
71
|
|
|
@@ -62,7 +83,25 @@ module RubyLsp
|
|
|
62
83
|
end
|
|
63
84
|
end
|
|
64
85
|
|
|
65
|
-
|
|
86
|
+
sig { params(node: Prism::CallNode).void }
|
|
87
|
+
def handle_route(node)
|
|
88
|
+
result = @client.route_location(T.must(node.message))
|
|
89
|
+
return unless result
|
|
90
|
+
|
|
91
|
+
*file_parts, line = result.fetch(:location).split(":")
|
|
92
|
+
|
|
93
|
+
# On Windows, file paths will look something like `C:/path/to/file.rb:123`. Only the last colon is the line
|
|
94
|
+
# number and all other parts compose the file path
|
|
95
|
+
file_path = file_parts.join(":")
|
|
96
|
+
|
|
97
|
+
@response_builder << Interface::Location.new(
|
|
98
|
+
uri: URI::Generic.from_path(path: file_path).to_s,
|
|
99
|
+
range: Interface::Range.new(
|
|
100
|
+
start: Interface::Position.new(line: Integer(line) - 1, character: 0),
|
|
101
|
+
end: Interface::Position.new(line: Integer(line) - 1, character: 0),
|
|
102
|
+
),
|
|
103
|
+
)
|
|
104
|
+
end
|
|
66
105
|
|
|
67
106
|
sig { params(name: String).void }
|
|
68
107
|
def collect_definitions(name)
|
|
@@ -21,12 +21,22 @@ module RubyLsp
|
|
|
21
21
|
end
|
|
22
22
|
def initialize(response_builder, dispatcher)
|
|
23
23
|
@response_builder = response_builder
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
@namespace_stack = T.let([], T::Array[String])
|
|
25
|
+
|
|
26
|
+
dispatcher.register(
|
|
27
|
+
self,
|
|
28
|
+
:on_call_node_enter,
|
|
29
|
+
:on_class_node_enter,
|
|
30
|
+
:on_class_node_leave,
|
|
31
|
+
:on_module_node_enter,
|
|
32
|
+
:on_module_node_leave,
|
|
33
|
+
)
|
|
26
34
|
end
|
|
27
35
|
|
|
28
36
|
sig { params(node: Prism::CallNode).void }
|
|
29
37
|
def on_call_node_enter(node)
|
|
38
|
+
return if @namespace_stack.empty?
|
|
39
|
+
|
|
30
40
|
content = extract_test_case_name(node)
|
|
31
41
|
|
|
32
42
|
if content
|
|
@@ -44,15 +54,46 @@ module RubyLsp
|
|
|
44
54
|
case message
|
|
45
55
|
when *Support::Callbacks::ALL, "validate"
|
|
46
56
|
handle_all_arg_types(node, T.must(message))
|
|
47
|
-
when "validates", "validates!", "validates_each", "belongs_to", "has_one", "has_many",
|
|
57
|
+
when "validates", "validates!", "validates_each", "belongs_to", "has_one", "has_many",
|
|
58
|
+
"has_and_belongs_to_many", "attr_readonly", "scope"
|
|
48
59
|
handle_symbol_and_string_arg_types(node, T.must(message))
|
|
49
60
|
when "validates_with"
|
|
50
61
|
handle_class_arg_types(node, T.must(message))
|
|
51
62
|
end
|
|
52
63
|
end
|
|
53
64
|
|
|
65
|
+
sig { params(node: Prism::ClassNode).void }
|
|
66
|
+
def on_class_node_enter(node)
|
|
67
|
+
add_to_namespace_stack(node)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
sig { params(node: Prism::ClassNode).void }
|
|
71
|
+
def on_class_node_leave(node)
|
|
72
|
+
remove_from_namespace_stack(node)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
sig { params(node: Prism::ModuleNode).void }
|
|
76
|
+
def on_module_node_enter(node)
|
|
77
|
+
add_to_namespace_stack(node)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
sig { params(node: Prism::ModuleNode).void }
|
|
81
|
+
def on_module_node_leave(node)
|
|
82
|
+
remove_from_namespace_stack(node)
|
|
83
|
+
end
|
|
84
|
+
|
|
54
85
|
private
|
|
55
86
|
|
|
87
|
+
sig { params(node: T.any(Prism::ClassNode, Prism::ModuleNode)).void }
|
|
88
|
+
def add_to_namespace_stack(node)
|
|
89
|
+
@namespace_stack << node.constant_path.slice
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
sig { params(node: T.any(Prism::ClassNode, Prism::ModuleNode)).void }
|
|
93
|
+
def remove_from_namespace_stack(node)
|
|
94
|
+
@namespace_stack.delete(node.constant_path.slice)
|
|
95
|
+
end
|
|
96
|
+
|
|
56
97
|
sig { params(node: Prism::CallNode, message: String).void }
|
|
57
98
|
def handle_all_arg_types(node, message)
|
|
58
99
|
block = node.block
|
|
@@ -77,13 +77,14 @@ module RubyLsp
|
|
|
77
77
|
schema_file = model[:schema_file]
|
|
78
78
|
|
|
79
79
|
@response_builder.push(
|
|
80
|
-
"[Schema](#{URI::Generic.
|
|
80
|
+
"[Schema](#{URI::Generic.from_path(path: schema_file)})",
|
|
81
81
|
category: :links,
|
|
82
82
|
) if schema_file
|
|
83
83
|
|
|
84
84
|
@response_builder.push(
|
|
85
85
|
model[:columns].map do |name, type|
|
|
86
|
-
"
|
|
86
|
+
primary_key_suffix = " (PK)" if model[:primary_keys].include?(name)
|
|
87
|
+
"**#{name}**: #{type}#{primary_key_suffix}\n"
|
|
87
88
|
end.join("\n"),
|
|
88
89
|
category: :documentation,
|
|
89
90
|
)
|
|
@@ -46,14 +46,15 @@ module RubyLsp
|
|
|
46
46
|
Process.setsid
|
|
47
47
|
rescue Errno::EPERM
|
|
48
48
|
# If we can't set the session ID, continue
|
|
49
|
+
rescue NotImplementedError
|
|
50
|
+
# setpgrp() may be unimplemented on some platform
|
|
51
|
+
# https://github.com/Shopify/ruby-lsp-rails/issues/348
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
stdin, stdout, stderr, wait_thread = Bundler.with_original_env do
|
|
55
|
+
Open3.popen3("bundle", "exec", "rails", "runner", "#{__dir__}/server.rb", "start")
|
|
49
56
|
end
|
|
50
57
|
|
|
51
|
-
stdin, stdout, stderr, wait_thread = Open3.popen3(
|
|
52
|
-
"bin/rails",
|
|
53
|
-
"runner",
|
|
54
|
-
"#{__dir__}/server.rb",
|
|
55
|
-
"start",
|
|
56
|
-
)
|
|
57
58
|
@stdin = T.let(stdin, IO)
|
|
58
59
|
@stdout = T.let(stdout, IO)
|
|
59
60
|
@stderr = T.let(stderr, IO)
|
|
@@ -79,7 +80,9 @@ module RubyLsp
|
|
|
79
80
|
if @wait_thread.alive?
|
|
80
81
|
$stderr.puts("Ruby LSP Rails is force killing the server")
|
|
81
82
|
sleep(0.5) # give the server a bit of time if we already issued a shutdown notification
|
|
82
|
-
|
|
83
|
+
|
|
84
|
+
# Windows does not support the `TERM` signal, so we're forced to use `KILL` here
|
|
85
|
+
Process.kill(T.must(Signal.list["KILL"]), @wait_thread.pid)
|
|
83
86
|
end
|
|
84
87
|
end
|
|
85
88
|
end
|
|
@@ -95,6 +98,14 @@ module RubyLsp
|
|
|
95
98
|
nil
|
|
96
99
|
end
|
|
97
100
|
|
|
101
|
+
sig { params(name: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
|
|
102
|
+
def route_location(name)
|
|
103
|
+
make_request("route_location", name: name)
|
|
104
|
+
rescue IncompleteMessageError
|
|
105
|
+
$stderr.puts("Ruby LSP Rails failed to get route location: #{@stderr.read}")
|
|
106
|
+
nil
|
|
107
|
+
end
|
|
108
|
+
|
|
98
109
|
sig { void }
|
|
99
110
|
def trigger_reload
|
|
100
111
|
$stderr.puts("Reloading Rails application")
|
|
@@ -1,24 +1,8 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: false
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
require "sorbet-runtime"
|
|
5
4
|
require "json"
|
|
6
5
|
|
|
7
|
-
begin
|
|
8
|
-
T::Configuration.default_checked_level = :never
|
|
9
|
-
# Suppresses call validation errors
|
|
10
|
-
T::Configuration.call_validation_error_handler = ->(*) {}
|
|
11
|
-
# Suppresses errors caused by T.cast, T.let, T.must, etc.
|
|
12
|
-
T::Configuration.inline_type_error_handler = ->(*) {}
|
|
13
|
-
# Suppresses errors caused by incorrect parameter ordering
|
|
14
|
-
T::Configuration.sig_validation_error_handler = ->(*) {}
|
|
15
|
-
rescue
|
|
16
|
-
# Need this rescue so that if another gem has
|
|
17
|
-
# already set the checked level by the time we
|
|
18
|
-
# get to it, we don't fail outright.
|
|
19
|
-
nil
|
|
20
|
-
end
|
|
21
|
-
|
|
22
6
|
# NOTE: We should avoid printing to stderr since it causes problems. We never read the standard error pipe from the
|
|
23
7
|
# client, so it will become full and eventually hang or crash. Instead, return a response with an `error` key.
|
|
24
8
|
|
|
@@ -27,16 +11,14 @@ module RubyLsp
|
|
|
27
11
|
class Server
|
|
28
12
|
VOID = Object.new
|
|
29
13
|
|
|
30
|
-
extend T::Sig
|
|
31
|
-
|
|
32
|
-
sig { void }
|
|
33
14
|
def initialize
|
|
34
15
|
$stdin.sync = true
|
|
35
16
|
$stdout.sync = true
|
|
36
|
-
|
|
17
|
+
$stdin.binmode
|
|
18
|
+
$stdout.binmode
|
|
19
|
+
@running = true
|
|
37
20
|
end
|
|
38
21
|
|
|
39
|
-
sig { void }
|
|
40
22
|
def start
|
|
41
23
|
initialize_result = { result: { message: "ok" } }.to_json
|
|
42
24
|
$stdout.write("Content-Length: #{initialize_result.length}\r\n\r\n#{initialize_result}")
|
|
@@ -54,22 +36,18 @@ module RubyLsp
|
|
|
54
36
|
end
|
|
55
37
|
end
|
|
56
38
|
|
|
57
|
-
sig do
|
|
58
|
-
params(
|
|
59
|
-
request: String,
|
|
60
|
-
params: T.nilable(T::Hash[Symbol, T.untyped]),
|
|
61
|
-
).returns(T.any(Object, T::Hash[Symbol, T.untyped]))
|
|
62
|
-
end
|
|
63
39
|
def execute(request, params)
|
|
64
40
|
case request
|
|
65
41
|
when "shutdown"
|
|
66
42
|
@running = false
|
|
67
43
|
VOID
|
|
68
44
|
when "model"
|
|
69
|
-
resolve_database_info_from_model(
|
|
45
|
+
resolve_database_info_from_model(params.fetch(:name))
|
|
70
46
|
when "reload"
|
|
71
47
|
::Rails.application.reloader.reload!
|
|
72
48
|
VOID
|
|
49
|
+
when "route_location"
|
|
50
|
+
route_location(params.fetch(:name))
|
|
73
51
|
else
|
|
74
52
|
VOID
|
|
75
53
|
end
|
|
@@ -79,10 +57,37 @@ module RubyLsp
|
|
|
79
57
|
|
|
80
58
|
private
|
|
81
59
|
|
|
82
|
-
|
|
60
|
+
# Older versions of Rails don't support `route_source_locations`.
|
|
61
|
+
# We also check that it's enabled.
|
|
62
|
+
if ActionDispatch::Routing::Mapper.respond_to?(:route_source_locations) &&
|
|
63
|
+
ActionDispatch::Routing::Mapper.route_source_locations
|
|
64
|
+
def route_location(name)
|
|
65
|
+
match_data = name.match(/^(.+)(_path|_url)$/)
|
|
66
|
+
return { result: nil } unless match_data
|
|
67
|
+
|
|
68
|
+
key = match_data[1]
|
|
69
|
+
|
|
70
|
+
# A token could match the _path or _url pattern, but not be an actual route.
|
|
71
|
+
route = ::Rails.application.routes.named_routes.get(key)
|
|
72
|
+
return { result: nil } unless route&.source_location
|
|
73
|
+
|
|
74
|
+
{
|
|
75
|
+
result: {
|
|
76
|
+
location: ::Rails.root.join(route.source_location).to_s,
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
rescue => e
|
|
80
|
+
{ error: e.full_message(highlight: false) }
|
|
81
|
+
end
|
|
82
|
+
else
|
|
83
|
+
def route_location(name)
|
|
84
|
+
{ result: nil }
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
83
88
|
def resolve_database_info_from_model(model_name)
|
|
84
89
|
const = ActiveSupport::Inflector.safe_constantize(model_name)
|
|
85
|
-
unless
|
|
90
|
+
unless active_record_model?(const)
|
|
86
91
|
return {
|
|
87
92
|
result: nil,
|
|
88
93
|
}
|
|
@@ -91,6 +96,7 @@ module RubyLsp
|
|
|
91
96
|
info = {
|
|
92
97
|
result: {
|
|
93
98
|
columns: const.columns.map { |column| [column.name, column.type] },
|
|
99
|
+
primary_keys: Array(const.primary_key),
|
|
94
100
|
},
|
|
95
101
|
}
|
|
96
102
|
|
|
@@ -103,6 +109,15 @@ module RubyLsp
|
|
|
103
109
|
rescue => e
|
|
104
110
|
{ error: e.full_message(highlight: false) }
|
|
105
111
|
end
|
|
112
|
+
|
|
113
|
+
def active_record_model?(const)
|
|
114
|
+
!!(
|
|
115
|
+
const &&
|
|
116
|
+
defined?(ActiveRecord) &&
|
|
117
|
+
ActiveRecord::Base > const && # We do this 'backwards' in case the class overwrites `<`
|
|
118
|
+
!const.abstract_class?
|
|
119
|
+
)
|
|
120
|
+
end
|
|
106
121
|
end
|
|
107
122
|
end
|
|
108
123
|
end
|
|
@@ -66,18 +66,25 @@ module RubyLsp
|
|
|
66
66
|
private def build_search_index
|
|
67
67
|
return unless RAILTIES_VERSION
|
|
68
68
|
|
|
69
|
-
$stderr.puts("Fetching Rails
|
|
69
|
+
$stderr.puts("Fetching search index for Rails documentation")
|
|
70
70
|
|
|
71
|
-
response = Net::HTTP.get_response(
|
|
71
|
+
response = Net::HTTP.get_response(
|
|
72
|
+
URI("#{RAILS_DOC_HOST}/v#{RAILTIES_VERSION}/js/search_index.js"),
|
|
73
|
+
{ "User-Agent" => "ruby-lsp-rails/#{RubyLsp::Rails::VERSION}" },
|
|
74
|
+
)
|
|
72
75
|
|
|
73
76
|
body = case response
|
|
74
77
|
when Net::HTTPSuccess
|
|
78
|
+
$stderr.puts("Finished fetching search index for Rails documentation")
|
|
75
79
|
response.body
|
|
76
80
|
when Net::HTTPRedirection
|
|
77
81
|
# If the version's doc is not found, e.g. Rails main, it'll be redirected
|
|
78
82
|
# In this case, we just fetch the latest doc
|
|
79
83
|
response = Net::HTTP.get_response(URI("#{RAILS_DOC_HOST}/js/search_index.js"))
|
|
80
|
-
|
|
84
|
+
if response.is_a?(Net::HTTPSuccess)
|
|
85
|
+
$stderr.puts("Finished fetching search index for Rails documentation")
|
|
86
|
+
response.body
|
|
87
|
+
end
|
|
81
88
|
else
|
|
82
89
|
$stderr.puts("Response failed: #{response.inspect}")
|
|
83
90
|
nil
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
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.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shopify
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2024-
|
|
11
|
+
date: 2024-05-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ruby-lsp
|
|
@@ -16,7 +16,7 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 0.16.
|
|
19
|
+
version: 0.16.5
|
|
20
20
|
- - "<"
|
|
21
21
|
- !ruby/object:Gem::Version
|
|
22
22
|
version: 0.17.0
|
|
@@ -26,7 +26,7 @@ dependencies:
|
|
|
26
26
|
requirements:
|
|
27
27
|
- - ">="
|
|
28
28
|
- !ruby/object:Gem::Version
|
|
29
|
-
version: 0.16.
|
|
29
|
+
version: 0.16.5
|
|
30
30
|
- - "<"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
32
|
version: 0.17.0
|
|
@@ -91,7 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
91
91
|
- !ruby/object:Gem::Version
|
|
92
92
|
version: '0'
|
|
93
93
|
requirements: []
|
|
94
|
-
rubygems_version: 3.5.
|
|
94
|
+
rubygems_version: 3.5.9
|
|
95
95
|
signing_key:
|
|
96
96
|
specification_version: 4
|
|
97
97
|
summary: A Ruby LSP addon for Rails
|