ruby-lsp-rails 0.2.9 → 0.2.10
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 +0 -2
- data/lib/ruby_lsp/ruby_lsp_rails/addon.rb +8 -6
- data/lib/ruby_lsp/ruby_lsp_rails/code_lens.rb +14 -14
- data/lib/ruby_lsp/ruby_lsp_rails/hover.rb +24 -33
- data/lib/ruby_lsp/ruby_lsp_rails/runner_client.rb +84 -0
- data/lib/ruby_lsp/ruby_lsp_rails/server.rb +87 -0
- data/lib/ruby_lsp_rails/rack_app.rb +1 -1
- data/lib/ruby_lsp_rails/version.rb +1 -1
- metadata +9 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 41272ca8c9212dc08dc09beeef481c282656946e182e0c62c72585ecc6cfe658
|
4
|
+
data.tar.gz: 0dc3c80ab252123291ab7b77a02b22fff1f27dd5bc07eb49ea51dfdbff4310f2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb753c4c3a4282faeefbd254840be186969179e6eaff83320a79e6fd5d2149e4d35acd68f7c38d8bd436e07b233f5e3cebf553c53317576333b83c4b81582dc0
|
7
|
+
data.tar.gz: 881b3d77f931f890ed68a12b478b7762ea491165e70c4cd61af078eccdeb139c28d9f9eed83406b7fa01927e6b0ea3a07993e0d73ae4c0eede146c6fb124a4aa
|
data/README.md
CHANGED
@@ -36,8 +36,6 @@ end
|
|
36
36
|
1. Start your Rails server
|
37
37
|
1. Hover over an ActiveRecord model to see its details
|
38
38
|
|
39
|
-
Nested models (e.g. `Admin::User`) are not yet supported.
|
40
|
-
|
41
39
|
### Documentation
|
42
40
|
|
43
41
|
See the [documentation](https://shopify.github.io/ruby-lsp-rails) for more in-depth details about the
|
@@ -28,23 +28,25 @@ module RubyLsp
|
|
28
28
|
# Creates a new CodeLens listener. This method is invoked on every CodeLens request
|
29
29
|
sig do
|
30
30
|
override.params(
|
31
|
+
response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens],
|
31
32
|
uri: URI::Generic,
|
32
33
|
dispatcher: Prism::Dispatcher,
|
33
|
-
).
|
34
|
+
).void
|
34
35
|
end
|
35
|
-
def create_code_lens_listener(uri, dispatcher)
|
36
|
-
CodeLens.new(uri, dispatcher)
|
36
|
+
def create_code_lens_listener(response_builder, uri, dispatcher)
|
37
|
+
CodeLens.new(response_builder, uri, dispatcher)
|
37
38
|
end
|
38
39
|
|
39
40
|
sig do
|
40
41
|
override.params(
|
42
|
+
response_builder: ResponseBuilders::Hover,
|
41
43
|
nesting: T::Array[String],
|
42
44
|
index: RubyIndexer::Index,
|
43
45
|
dispatcher: Prism::Dispatcher,
|
44
|
-
).
|
46
|
+
).void
|
45
47
|
end
|
46
|
-
def create_hover_listener(nesting, index, dispatcher)
|
47
|
-
Hover.new(client, nesting, index, dispatcher)
|
48
|
+
def create_hover_listener(response_builder, nesting, index, dispatcher)
|
49
|
+
Hover.new(client, response_builder, nesting, index, dispatcher)
|
48
50
|
end
|
49
51
|
|
50
52
|
sig { override.returns(String) }
|
@@ -32,26 +32,26 @@ module RubyLsp
|
|
32
32
|
# ````
|
33
33
|
#
|
34
34
|
# The code lenses will be displayed above the class and above each test method.
|
35
|
-
class CodeLens
|
35
|
+
class CodeLens
|
36
36
|
extend T::Sig
|
37
|
-
|
37
|
+
include Requests::Support::Common
|
38
38
|
|
39
|
-
ResponseType = type_member { { fixed: T::Array[::RubyLsp::Interface::CodeLens] } }
|
40
39
|
BASE_COMMAND = "bin/rails test"
|
41
40
|
|
42
|
-
sig
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
41
|
+
sig do
|
42
|
+
params(
|
43
|
+
response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens],
|
44
|
+
uri: URI::Generic,
|
45
|
+
dispatcher: Prism::Dispatcher,
|
46
|
+
).void
|
47
|
+
end
|
48
|
+
def initialize(response_builder, uri, dispatcher)
|
49
|
+
@response_builder = response_builder
|
48
50
|
@path = T.let(uri.to_standardized_path, T.nilable(String))
|
49
51
|
@group_id = T.let(1, Integer)
|
50
52
|
@group_id_stack = T.let([], T::Array[Integer])
|
51
53
|
|
52
54
|
dispatcher.register(self, :on_call_node_enter, :on_class_node_enter, :on_def_node_enter, :on_class_node_leave)
|
53
|
-
|
54
|
-
super(dispatcher)
|
55
55
|
end
|
56
56
|
|
57
57
|
sig { params(node: Prism::CallNode).void }
|
@@ -131,7 +131,7 @@ module RubyLsp
|
|
131
131
|
grouping_data = { group_id: @group_id_stack.last, kind: kind }
|
132
132
|
grouping_data[:id] = @group_id if kind == :group
|
133
133
|
|
134
|
-
@
|
134
|
+
@response_builder << create_code_lens(
|
135
135
|
node,
|
136
136
|
title: "Run",
|
137
137
|
command_name: "rubyLsp.runTest",
|
@@ -139,7 +139,7 @@ module RubyLsp
|
|
139
139
|
data: { type: "test", **grouping_data },
|
140
140
|
)
|
141
141
|
|
142
|
-
@
|
142
|
+
@response_builder << create_code_lens(
|
143
143
|
node,
|
144
144
|
title: "Run In Terminal",
|
145
145
|
command_name: "rubyLsp.runTestInTerminal",
|
@@ -147,7 +147,7 @@ module RubyLsp
|
|
147
147
|
data: { type: "test_in_terminal", **grouping_data },
|
148
148
|
)
|
149
149
|
|
150
|
-
@
|
150
|
+
@response_builder << create_code_lens(
|
151
151
|
node,
|
152
152
|
title: "Debug",
|
153
153
|
command_name: "rubyLsp.debugTest",
|
@@ -16,28 +16,22 @@ module RubyLsp
|
|
16
16
|
# User.all
|
17
17
|
# # ^ hovering here will show information about the User model
|
18
18
|
# ```
|
19
|
-
class Hover
|
19
|
+
class Hover
|
20
20
|
extend T::Sig
|
21
|
-
|
22
|
-
|
23
|
-
ResponseType = type_member { { fixed: T.nilable(::RubyLsp::Interface::Hover) } }
|
24
|
-
|
25
|
-
sig { override.returns(ResponseType) }
|
26
|
-
attr_reader :_response
|
21
|
+
include Requests::Support::Common
|
27
22
|
|
28
23
|
sig do
|
29
24
|
params(
|
30
25
|
client: RailsClient,
|
26
|
+
response_builder: ResponseBuilders::Hover,
|
31
27
|
nesting: T::Array[String],
|
32
28
|
index: RubyIndexer::Index,
|
33
29
|
dispatcher: Prism::Dispatcher,
|
34
30
|
).void
|
35
31
|
end
|
36
|
-
def initialize(client, nesting, index, dispatcher)
|
37
|
-
super(dispatcher)
|
38
|
-
|
39
|
-
@_response = T.let(nil, ResponseType)
|
32
|
+
def initialize(client, response_builder, nesting, index, dispatcher)
|
40
33
|
@client = client
|
34
|
+
@response_builder = response_builder
|
41
35
|
@nesting = nesting
|
42
36
|
@index = index
|
43
37
|
dispatcher.register(self, :on_constant_path_node_enter, :on_constant_read_node_enter, :on_call_node_enter)
|
@@ -49,16 +43,10 @@ module RubyLsp
|
|
49
43
|
return unless entries
|
50
44
|
|
51
45
|
name = T.must(entries.first).name
|
52
|
-
content = +""
|
53
|
-
column_info = generate_column_content(name)
|
54
|
-
content << column_info if column_info
|
55
46
|
|
56
|
-
|
57
|
-
content << urls.join("\n\n") unless urls.empty?
|
58
|
-
return if content.empty?
|
47
|
+
generate_column_content(name)
|
59
48
|
|
60
|
-
|
61
|
-
@_response = RubyLsp::Interface::Hover.new(range: range_from_location(node.location), contents: contents)
|
49
|
+
generate_rails_document_link_hover(name, node.location)
|
62
50
|
end
|
63
51
|
|
64
52
|
sig { params(node: Prism::ConstantReadNode).void }
|
@@ -66,11 +54,7 @@ module RubyLsp
|
|
66
54
|
entries = @index.resolve(node.name.to_s, @nesting)
|
67
55
|
return unless entries
|
68
56
|
|
69
|
-
|
70
|
-
return unless content
|
71
|
-
|
72
|
-
contents = RubyLsp::Interface::MarkupContent.new(kind: "markdown", value: content)
|
73
|
-
@_response = RubyLsp::Interface::Hover.new(range: range_from_location(node.location), contents: contents)
|
57
|
+
generate_column_content(T.must(entries.first).name)
|
74
58
|
end
|
75
59
|
|
76
60
|
sig { params(node: Prism::CallNode).void }
|
@@ -80,30 +64,37 @@ module RubyLsp
|
|
80
64
|
|
81
65
|
return unless message_value && message_loc
|
82
66
|
|
83
|
-
|
67
|
+
generate_rails_document_link_hover(message_value, message_loc)
|
84
68
|
end
|
85
69
|
|
86
70
|
private
|
87
71
|
|
88
|
-
sig { params(name: String).
|
72
|
+
sig { params(name: String).void }
|
89
73
|
def generate_column_content(name)
|
90
74
|
model = @client.model(name)
|
91
75
|
return if model.nil?
|
92
76
|
|
93
77
|
schema_file = model[:schema_file]
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
78
|
+
|
79
|
+
@response_builder.push(
|
80
|
+
"[Schema](#{URI::Generic.build(scheme: "file", path: schema_file)})",
|
81
|
+
category: :links,
|
82
|
+
) if schema_file
|
83
|
+
|
84
|
+
@response_builder.push(
|
85
|
+
model[:columns].map do |name, type|
|
86
|
+
"**#{name}**: #{type}\n"
|
87
|
+
end.join("\n"),
|
88
|
+
category: :documentation,
|
89
|
+
)
|
98
90
|
end
|
99
91
|
|
100
|
-
sig { params(name: String, location: Prism::Location).
|
92
|
+
sig { params(name: String, location: Prism::Location).void }
|
101
93
|
def generate_rails_document_link_hover(name, location)
|
102
94
|
urls = Support::RailsDocumentClient.generate_rails_document_urls(name)
|
103
95
|
return if urls.empty?
|
104
96
|
|
105
|
-
|
106
|
-
RubyLsp::Interface::Hover.new(range: range_from_location(location), contents: contents)
|
97
|
+
@response_builder.push(urls.join("\n\n"), category: :links)
|
107
98
|
end
|
108
99
|
end
|
109
100
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "json"
|
5
|
+
require "open3"
|
6
|
+
|
7
|
+
# NOTE: We should avoid printing to stderr since it causes problems. We never read the standard error pipe
|
8
|
+
# from the client, so it will become full and eventually hang or crash.
|
9
|
+
# Instead, return a response with an `error` key.
|
10
|
+
|
11
|
+
module RubyLsp
|
12
|
+
module Rails
|
13
|
+
class RunnerClient
|
14
|
+
extend T::Sig
|
15
|
+
|
16
|
+
sig { void }
|
17
|
+
def initialize
|
18
|
+
stdin, stdout, stderr, wait_thread = Open3.popen3(
|
19
|
+
"bin/rails",
|
20
|
+
"runner",
|
21
|
+
"#{__dir__}/server.rb",
|
22
|
+
"start",
|
23
|
+
)
|
24
|
+
@stdin = T.let(stdin, IO)
|
25
|
+
@stdout = T.let(stdout, IO)
|
26
|
+
@stderr = T.let(stderr, IO)
|
27
|
+
@wait_thread = T.let(wait_thread, Process::Waiter)
|
28
|
+
@stdin.binmode # for Windows compatibility
|
29
|
+
@stdout.binmode # for Windows compatibility
|
30
|
+
end
|
31
|
+
|
32
|
+
sig { params(name: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
|
33
|
+
def model(name)
|
34
|
+
make_request("model", name: name)
|
35
|
+
end
|
36
|
+
|
37
|
+
sig { void }
|
38
|
+
def shutdown
|
39
|
+
send_notification("shutdown")
|
40
|
+
Thread.pass while @wait_thread.alive?
|
41
|
+
[@stdin, @stdout, @stderr].each(&:close)
|
42
|
+
end
|
43
|
+
|
44
|
+
sig { returns(T::Boolean) }
|
45
|
+
def stopped?
|
46
|
+
[@stdin, @stdout, @stderr].all?(&:closed?) && !@wait_thread.alive?
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
sig { params(request: T.untyped, params: T.untyped).returns(T.untyped) }
|
52
|
+
def make_request(request, params = nil)
|
53
|
+
send_message(request, params)
|
54
|
+
read_response
|
55
|
+
end
|
56
|
+
|
57
|
+
sig { params(request: T.untyped, params: T.untyped).void }
|
58
|
+
def send_message(request, params = nil)
|
59
|
+
message = { method: request }
|
60
|
+
message[:params] = params if params
|
61
|
+
json = message.to_json
|
62
|
+
|
63
|
+
@stdin.write("Content-Length: #{json.length}\r\n\r\n", json)
|
64
|
+
end
|
65
|
+
|
66
|
+
alias_method :send_notification, :send_message
|
67
|
+
|
68
|
+
sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
|
69
|
+
def read_response
|
70
|
+
headers = @stdout.gets("\r\n\r\n")
|
71
|
+
raw_response = @stdout.read(T.must(headers)[/Content-Length: (\d+)/i, 1].to_i)
|
72
|
+
|
73
|
+
response = JSON.parse(T.must(raw_response), symbolize_names: true)
|
74
|
+
|
75
|
+
if response[:error]
|
76
|
+
warn("Ruby LSP Rails error: " + response[:error])
|
77
|
+
return
|
78
|
+
end
|
79
|
+
|
80
|
+
response.fetch(:result)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "sorbet-runtime"
|
5
|
+
require "json"
|
6
|
+
|
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
|
+
module RubyLsp
|
23
|
+
module Rails
|
24
|
+
class Server
|
25
|
+
VOID = Object.new
|
26
|
+
|
27
|
+
extend T::Sig
|
28
|
+
|
29
|
+
sig { params(model_name: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
|
30
|
+
def resolve_database_info_from_model(model_name)
|
31
|
+
const = ActiveSupport::Inflector.safe_constantize(model_name)
|
32
|
+
unless const && const < ActiveRecord::Base && !const.abstract_class?
|
33
|
+
return {
|
34
|
+
result: nil,
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
schema_file = ActiveRecord::Tasks::DatabaseTasks.schema_dump_path(const.connection.pool.db_config)
|
39
|
+
|
40
|
+
{
|
41
|
+
result: {
|
42
|
+
columns: const.columns.map { |column| [column.name, column.type] },
|
43
|
+
schema_file: ::Rails.root + schema_file,
|
44
|
+
},
|
45
|
+
}
|
46
|
+
rescue => e
|
47
|
+
{
|
48
|
+
error: e.message,
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
sig { void }
|
53
|
+
def start
|
54
|
+
$stdin.sync = true
|
55
|
+
$stdout.sync = true
|
56
|
+
|
57
|
+
running = T.let(true, T::Boolean)
|
58
|
+
|
59
|
+
while running
|
60
|
+
headers = $stdin.gets("\r\n\r\n")
|
61
|
+
request = $stdin.read(headers[/Content-Length: (\d+)/i, 1].to_i)
|
62
|
+
|
63
|
+
json = JSON.parse(request, symbolize_names: true)
|
64
|
+
request_method = json.fetch(:method)
|
65
|
+
params = json[:params]
|
66
|
+
|
67
|
+
response = case request_method
|
68
|
+
when "shutdown"
|
69
|
+
running = false
|
70
|
+
VOID
|
71
|
+
when "model"
|
72
|
+
resolve_database_info_from_model(params.fetch(:name))
|
73
|
+
else
|
74
|
+
VOID
|
75
|
+
end
|
76
|
+
|
77
|
+
next if response == VOID
|
78
|
+
|
79
|
+
json_response = response.to_json
|
80
|
+
$stdout.write("Content-Length: #{json_response.length}\r\n\r\n#{json_response}")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
RubyLsp::Rails::Server.new.start if ARGV.first == "start"
|
@@ -31,7 +31,7 @@ module RubyLsp
|
|
31
31
|
def resolve_database_info_from_model(model_name)
|
32
32
|
const = ActiveSupport::Inflector.safe_constantize(model_name)
|
33
33
|
|
34
|
-
if const && const < ActiveRecord::Base
|
34
|
+
if const && const < ActiveRecord::Base && !const.abstract_class?
|
35
35
|
begin
|
36
36
|
schema_file = ActiveRecord::Tasks::DatabaseTasks.schema_dump_path(const.connection.pool.db_config)
|
37
37
|
rescue => e
|
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.2.
|
4
|
+
version: 0.2.10
|
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-02-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|
@@ -58,20 +58,20 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.
|
61
|
+
version: 0.14.0
|
62
62
|
- - "<"
|
63
63
|
- !ruby/object:Gem::Version
|
64
|
-
version: 0.
|
64
|
+
version: 0.15.0
|
65
65
|
type: :runtime
|
66
66
|
prerelease: false
|
67
67
|
version_requirements: !ruby/object:Gem::Requirement
|
68
68
|
requirements:
|
69
69
|
- - ">="
|
70
70
|
- !ruby/object:Gem::Version
|
71
|
-
version: 0.
|
71
|
+
version: 0.14.0
|
72
72
|
- - "<"
|
73
73
|
- !ruby/object:Gem::Version
|
74
|
-
version: 0.
|
74
|
+
version: 0.15.0
|
75
75
|
- !ruby/object:Gem::Dependency
|
76
76
|
name: sorbet-runtime
|
77
77
|
requirement: !ruby/object:Gem::Requirement
|
@@ -101,6 +101,8 @@ files:
|
|
101
101
|
- lib/ruby_lsp/ruby_lsp_rails/code_lens.rb
|
102
102
|
- lib/ruby_lsp/ruby_lsp_rails/hover.rb
|
103
103
|
- lib/ruby_lsp/ruby_lsp_rails/rails_client.rb
|
104
|
+
- lib/ruby_lsp/ruby_lsp_rails/runner_client.rb
|
105
|
+
- lib/ruby_lsp/ruby_lsp_rails/server.rb
|
104
106
|
- lib/ruby_lsp/ruby_lsp_rails/support/rails_document_client.rb
|
105
107
|
- lib/ruby_lsp_rails/rack_app.rb
|
106
108
|
- lib/ruby_lsp_rails/railtie.rb
|
@@ -129,7 +131,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
129
131
|
- !ruby/object:Gem::Version
|
130
132
|
version: '0'
|
131
133
|
requirements: []
|
132
|
-
rubygems_version: 3.5.
|
134
|
+
rubygems_version: 3.5.6
|
133
135
|
signing_key:
|
134
136
|
specification_version: 4
|
135
137
|
summary: A Ruby LSP addon for Rails
|