ruby-lsp-rails 0.2.9 → 0.2.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 54789b1f5da58f025ec88cfcff5cf95f800ae68af9dc409d31a4cdf6307e8286
4
- data.tar.gz: c03bd0623133606a7ba2290198939fb7de0071bdcc2e52bea30efdb3ed1fc7e2
3
+ metadata.gz: 41272ca8c9212dc08dc09beeef481c282656946e182e0c62c72585ecc6cfe658
4
+ data.tar.gz: 0dc3c80ab252123291ab7b77a02b22fff1f27dd5bc07eb49ea51dfdbff4310f2
5
5
  SHA512:
6
- metadata.gz: 7426ceebeabe03877d10b49fcb05b88c435d968fdcfd300d898768289aff80dfac7936c604d43f46de58eaa1649c5047ae9f7f0edbdb708e86b260cb52827591
7
- data.tar.gz: 52c94c5e2c2fe0c37e1fd3e807805ad2b80d5749903366a4dc41ff73885b150f8db03dd2a2154a9364e63b0eb36bf065cf0795eb5a19b801c201e8d5e93d40df
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
- ).returns(T.nilable(Listener[T::Array[Interface::CodeLens]]))
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
- ).returns(T.nilable(Listener[T.nilable(Interface::Hover)]))
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 < ::RubyLsp::Listener
35
+ class CodeLens
36
36
  extend T::Sig
37
- extend T::Generic
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 { override.returns(ResponseType) }
43
- attr_reader :_response
44
-
45
- sig { params(uri: URI::Generic, dispatcher: Prism::Dispatcher).void }
46
- def initialize(uri, dispatcher)
47
- @_response = T.let([], ResponseType)
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
- @_response << create_code_lens(
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
- @_response << create_code_lens(
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
- @_response << create_code_lens(
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 < ::RubyLsp::Listener
19
+ class Hover
20
20
  extend T::Sig
21
- extend T::Generic
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
- urls = Support::RailsDocumentClient.generate_rails_document_urls(name)
57
- content << urls.join("\n\n") unless urls.empty?
58
- return if content.empty?
47
+ generate_column_content(name)
59
48
 
60
- contents = RubyLsp::Interface::MarkupContent.new(kind: "markdown", value: content)
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
- content = generate_column_content(T.must(entries.first).name)
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
- @_response = generate_rails_document_link_hover(message_value, message_loc)
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).returns(T.nilable(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
- content = +""
95
- content << "[Schema](#{URI::Generic.build(scheme: "file", path: schema_file)})\n\n" if schema_file
96
- content << model[:columns].map { |name, type| "**#{name}**: #{type}\n" }.join("\n")
97
- content
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).returns(T.nilable(Interface::Hover)) }
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
- contents = RubyLsp::Interface::MarkupContent.new(kind: "markdown", value: urls.join("\n\n"))
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
@@ -3,6 +3,6 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Rails
6
- VERSION = "0.2.9"
6
+ VERSION = "0.2.10"
7
7
  end
8
8
  end
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.9
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-01-18 00:00:00.000000000 Z
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.13.0
61
+ version: 0.14.0
62
62
  - - "<"
63
63
  - !ruby/object:Gem::Version
64
- version: 0.14.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.13.0
71
+ version: 0.14.0
72
72
  - - "<"
73
73
  - !ruby/object:Gem::Version
74
- version: 0.14.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.4
134
+ rubygems_version: 3.5.6
133
135
  signing_key:
134
136
  specification_version: 4
135
137
  summary: A Ruby LSP addon for Rails