ruby-lsp-rails 0.3.13 → 0.3.15

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e2543aa7d39f0a6b231112f9ddb365692c7e8d06eb14681da551ab55fd8f5a4d
4
- data.tar.gz: e466ccd6d90934e083e638efdcedfb5d6b8f85fbce70b139e1542eeb2ab8a139
3
+ metadata.gz: 8864e641f204eaafb608b703228bd98d5a2234413f48ee1d1232f277b7385144
4
+ data.tar.gz: 3db424bb7e32fbd1a1dce231d56b9d98d92e23b0c9ef70f56cbab86b878f9133
5
5
  SHA512:
6
- metadata.gz: 8f7c82462682d93fd6b944c2705c8b62b424b4abc3636d1f2540eeee1ab9f7e2896eb77d338697bb65a0a64752529085f38e49bd5a75bfc668fa9d31dd5b0d10
7
- data.tar.gz: a8978fdebb07ba8b76dd10a725b3b3c736961f103cefa806fbc0302b494516e0a196105bf7fac9d114c963a62c8a706959d53f1509a4ed343e2e9d24633c4372
6
+ metadata.gz: 3820483222f6b082c3f939e9b797a71e775776fd3e5a3d9422a99ab1c3af992dce883a69781cf4b2a07e8492c87e3054a351df4f22c398fd12cb20c6a4472ff8
7
+ data.tar.gz: 72e7715cefc65926b7d8011933e0152f90ee896b6530cea5edec0b41c70be701ae55619ea9c73dbb60e4becaf73df9f48ce122448ab2f6688204eda1d85edfb8
data/README.md CHANGED
@@ -2,15 +2,7 @@
2
2
 
3
3
  # Ruby LSP Rails
4
4
 
5
- Ruby LSP Rails is a [Ruby LSP](https://github.com/Shopify/ruby-lsp) addon for extra Rails editor features, such as:
6
-
7
- * Hover over an ActiveRecord model to reveal its schema.
8
- * Run or debug a test by clicking on the code lens which appears above the test class, or an individual test.
9
- * Navigate to associations, validations, callbacks and test cases using your editor's "Go to Symbol" feature, or outline view.
10
- * Jump to the definition of callbacks using your editor's "Go to Definition" feature.
11
- * Jump to the declaration of a route.
12
- * Code Lens allowing fast-forwarding or rewinding of migrations.
13
- * Code Lens showing the path that a route action corresponds to.
5
+ Ruby LSP Rails is a [Ruby LSP](https://github.com/Shopify/ruby-lsp) addon for extra [Rails editor features](https://shopify.github.io/ruby-lsp-rails/FEATURES_md.html).
14
6
 
15
7
  ## Installation
16
8
 
@@ -22,7 +14,7 @@ There is no need to add the gem to your bundle.
22
14
  ## Documentation
23
15
 
24
16
  See the [documentation](https://shopify.github.io/ruby-lsp-rails) for more in-depth details about the
25
- [supported features](https://shopify.github.io/ruby-lsp-rails/RubyLsp/Rails.html).
17
+ [supported features](https://shopify.github.io/ruby-lsp-rails/FEATURES_md.html).
26
18
 
27
19
  ## How Runtime Introspection Works
28
20
 
data/Rakefile CHANGED
@@ -23,6 +23,7 @@ RDoc::Task.new do |rdoc|
23
23
  rdoc.rdoc_files.include("*.md", "lib/**/*.rb")
24
24
  rdoc.rdoc_dir = "docs"
25
25
  rdoc.markup = "markdown"
26
+ rdoc.title = "Ruby LSP Rails"
26
27
  rdoc.generator = "snapper"
27
28
  rdoc.options.push("--copy-files", "misc")
28
29
  rdoc.options.push("--copy-files", "LICENSE.txt")
@@ -26,23 +26,27 @@ module RubyLsp
26
26
 
27
27
  # We first initialize the client as a NullClient, so that we can start the server in a background thread. Until
28
28
  # the real client is initialized, features that depend on it will not be blocked by using the NullClient
29
- @client = T.let(NullClient.new, RunnerClient)
29
+ @rails_runner_client = T.let(NullClient.new, RunnerClient)
30
+ @global_state = T.let(nil, T.nilable(GlobalState))
30
31
  end
31
32
 
33
+ sig { returns(RunnerClient) }
34
+ attr_reader :rails_runner_client
35
+
32
36
  sig { override.params(global_state: GlobalState, message_queue: Thread::Queue).void }
33
37
  def activate(global_state, message_queue)
34
- @global_state = T.let(global_state, T.nilable(RubyLsp::GlobalState))
38
+ @global_state = global_state
35
39
  $stderr.puts("Activating Ruby LSP Rails addon v#{VERSION}")
36
40
  # Start booting the real client in a background thread. Until this completes, the client will be a NullClient
37
- Thread.new { @client = RunnerClient.create_client }
41
+ Thread.new { @rails_runner_client = RunnerClient.create_client }
38
42
  register_additional_file_watchers(global_state: global_state, message_queue: message_queue)
39
43
 
40
- T.must(@global_state).index.register_enhancement(IndexingEnhancement.new)
44
+ @global_state.index.register_enhancement(IndexingEnhancement.new)
41
45
  end
42
46
 
43
47
  sig { override.void }
44
48
  def deactivate
45
- @client.shutdown
49
+ @rails_runner_client.shutdown
46
50
  end
47
51
 
48
52
  # Creates a new CodeLens listener. This method is invoked on every CodeLens request
@@ -54,7 +58,7 @@ module RubyLsp
54
58
  ).void
55
59
  end
56
60
  def create_code_lens_listener(response_builder, uri, dispatcher)
57
- CodeLens.new(@client, T.must(@global_state), response_builder, uri, dispatcher)
61
+ CodeLens.new(@rails_runner_client, T.must(@global_state), response_builder, uri, dispatcher)
58
62
  end
59
63
 
60
64
  sig do
@@ -65,7 +69,7 @@ module RubyLsp
65
69
  ).void
66
70
  end
67
71
  def create_hover_listener(response_builder, node_context, dispatcher)
68
- Hover.new(@client, response_builder, node_context, T.must(@global_state), dispatcher)
72
+ Hover.new(@rails_runner_client, response_builder, node_context, T.must(@global_state), dispatcher)
69
73
  end
70
74
 
71
75
  sig do
@@ -90,7 +94,7 @@ module RubyLsp
90
94
  end
91
95
  def create_definition_listener(response_builder, uri, node_context, dispatcher)
92
96
  index = T.must(@global_state).index
93
- Definition.new(@client, response_builder, node_context, index, dispatcher)
97
+ Definition.new(@rails_runner_client, response_builder, node_context, index, dispatcher)
94
98
  end
95
99
 
96
100
  sig { params(changes: T::Array[{ uri: String, type: Integer }]).void }
@@ -98,7 +102,7 @@ module RubyLsp
98
102
  if changes.any? do |change|
99
103
  change[:uri].end_with?("db/schema.rb") || change[:uri].end_with?("structure.sql")
100
104
  end
101
- @client.trigger_reload
105
+ @rails_runner_client.trigger_reload
102
106
  end
103
107
  end
104
108
 
@@ -138,22 +138,23 @@ module RubyLsp
138
138
  class_name = node.constant_path.slice
139
139
  superclass_name = node.superclass&.slice
140
140
 
141
+ # We need to use a stack because someone could define a nested class
142
+ # inside a controller. When we exit that nested class declaration, we are
143
+ # back in a controller context. This part is used in other places in the LSP
144
+ @constant_name_stack << [class_name, superclass_name]
145
+
141
146
  if class_name.end_with?("Test")
142
- command = "#{test_command} #{@path}"
147
+ fully_qualified_name = @constant_name_stack.map(&:first).join("::")
148
+ command = "#{test_command} #{@path} --name \"/#{Shellwords.escape(fully_qualified_name)}(#|::)/\""
143
149
  add_test_code_lens(node, name: class_name, command: command, kind: :group)
144
150
  @group_id_stack.push(@group_id)
145
151
  @group_id += 1
146
152
  end
147
153
 
148
- if superclass_name&.start_with?("ActiveRecord::Migration")
154
+ if @path && superclass_name&.start_with?("ActiveRecord::Migration")
149
155
  command = "#{migrate_command} VERSION=#{migration_version}"
150
156
  add_migrate_code_lens(node, name: class_name, command: command)
151
157
  end
152
-
153
- # We need to use a stack because someone could define a nested class
154
- # inside a controller. When we exit that nested class declaration, we are
155
- # back in a controller context. This part is used in other places in the LSP
156
- @constant_name_stack << [class_name, superclass_name]
157
158
  end
158
159
 
159
160
  sig { params(node: Prism::ClassNode).void }
@@ -151,7 +151,8 @@ module RubyLsp
151
151
  selection_range: range_from_location(argument.location),
152
152
  )
153
153
  when Prism::ConstantReadNode, Prism::ConstantPathNode
154
- name = argument.full_name
154
+ name = constant_name(argument)
155
+ next unless name
155
156
  next if name.empty?
156
157
 
157
158
  append_document_symbol(
@@ -63,8 +63,11 @@ module RubyLsp
63
63
  @stdout = T.let(stdout, IO)
64
64
  @stderr = T.let(stderr, IO)
65
65
  @wait_thread = T.let(wait_thread, Process::Waiter)
66
- @stdin.binmode # for Windows compatibility
67
- @stdout.binmode # for Windows compatibility
66
+
67
+ # We set binmode for Windows compatibility
68
+ @stdin.binmode
69
+ @stdout.binmode
70
+ @stderr.binmode
68
71
 
69
72
  $stderr.puts("Ruby LSP Rails booting server")
70
73
  count = 0
@@ -158,8 +161,6 @@ module RubyLsp
158
161
  [@stdin, @stdout, @stderr].all?(&:closed?) && !@wait_thread.alive?
159
162
  end
160
163
 
161
- private
162
-
163
164
  sig do
164
165
  params(
165
166
  request: String,
@@ -171,6 +172,12 @@ module RubyLsp
171
172
  read_response
172
173
  end
173
174
 
175
+ # Notifications are like messages, but one-way, with no response sent back.
176
+ sig { params(request: String, params: T.nilable(T::Hash[Symbol, T.untyped])).void }
177
+ def send_notification(request, params = nil) = send_message(request, params)
178
+
179
+ private
180
+
174
181
  sig { overridable.params(request: String, params: T.nilable(T::Hash[Symbol, T.untyped])).void }
175
182
  def send_message(request, params = nil)
176
183
  message = { method: request }
@@ -184,10 +191,6 @@ module RubyLsp
184
191
  # The server connection died
185
192
  end
186
193
 
187
- # Notifications are like messages, but one-way, with no response sent back.
188
- sig { params(request: String, params: T.nilable(T::Hash[Symbol, T.untyped])).void }
189
- def send_notification(request, params = nil) = send_message(request, params)
190
-
191
194
  sig { overridable.returns(T.nilable(T::Hash[Symbol, T.untyped])) }
192
195
  def read_response
193
196
  raw_response = @mutex.synchronize do
@@ -12,10 +12,22 @@ module RubyLsp
12
12
  VOID = Object.new
13
13
 
14
14
  def initialize
15
- $stdin.sync = true
16
- $stdout.sync = true
17
- $stdin.binmode
18
- $stdout.binmode
15
+ # Grab references to the original pipes so that we can change the default output device further down
16
+ @stdin = $stdin
17
+ @stdout = $stdout
18
+ @stderr = $stderr
19
+ @stdin.sync = true
20
+ @stdout.sync = true
21
+ @stderr.sync = true
22
+ @stdin.binmode
23
+ @stdout.binmode
24
+ @stderr.binmode
25
+
26
+ # # Set the default output device to be $stderr. This means that using `puts` by itself will default to printing
27
+ # # to $stderr and only explicit `$stdout.puts` will go to $stdout. This reduces the chance that output coming
28
+ # # from the Rails app will be accidentally sent to the client
29
+ $> = $stderr
30
+
19
31
  @running = true
20
32
  end
21
33
 
@@ -25,18 +37,18 @@ module RubyLsp
25
37
  routes_reloader.execute_unless_loaded if routes_reloader&.respond_to?(:execute_unless_loaded)
26
38
 
27
39
  initialize_result = { result: { message: "ok", root: ::Rails.root.to_s } }.to_json
28
- $stdout.write("Content-Length: #{initialize_result.length}\r\n\r\n#{initialize_result}")
40
+ @stdout.write("Content-Length: #{initialize_result.length}\r\n\r\n#{initialize_result}")
29
41
 
30
42
  while @running
31
- headers = $stdin.gets("\r\n\r\n")
32
- json = $stdin.read(headers[/Content-Length: (\d+)/i, 1].to_i)
43
+ headers = @stdin.gets("\r\n\r\n")
44
+ json = @stdin.read(headers[/Content-Length: (\d+)/i, 1].to_i)
33
45
 
34
46
  request = JSON.parse(json, symbolize_names: true)
35
47
  response = execute(request.fetch(:method), request[:params])
36
48
  next if response == VOID
37
49
 
38
50
  json_response = response.to_json
39
- $stdout.write("Content-Length: #{json_response.length}\r\n\r\n#{json_response}")
51
+ @stdout.write("Content-Length: #{json_response.length}\r\n\r\n#{json_response}")
40
52
  end
41
53
  end
42
54
 
@@ -11,18 +11,18 @@ module RubyLsp
11
11
  sig { params(location_string: String).returns(Interface::Location) }
12
12
  def line_location_from_s(location_string)
13
13
  *file_parts, line = location_string.split(":")
14
-
15
- raise ArgumentError, "Invalid location string given" unless file_parts
14
+ raise ArgumentError, "Invalid location string given" if file_parts.empty?
16
15
 
17
16
  # On Windows, file paths will look something like `C:/path/to/file.rb:123`. Only the last colon is the line
18
17
  # number and all other parts compose the file path
19
18
  file_path = file_parts.join(":")
19
+ line_as_number = line ? Integer(line.to_i) - 1 : 0
20
20
 
21
21
  Interface::Location.new(
22
22
  uri: URI::Generic.from_path(path: file_path).to_s,
23
23
  range: Interface::Range.new(
24
- start: Interface::Position.new(line: Integer(line) - 1, character: 0),
25
- end: Interface::Position.new(line: Integer(line) - 1, character: 0),
24
+ start: Interface::Position.new(line: line_as_number, character: 0),
25
+ end: Interface::Position.new(line: line_as_number, character: 0),
26
26
  ),
27
27
  )
28
28
  end
@@ -3,6 +3,6 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Rails
6
- VERSION = "0.3.13"
6
+ VERSION = "0.3.15"
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.3.13
4
+ version: 0.3.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-15 00:00:00.000000000 Z
11
+ date: 2024-09-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-lsp
@@ -80,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
80
80
  - !ruby/object:Gem::Version
81
81
  version: '0'
82
82
  requirements: []
83
- rubygems_version: 3.5.17
83
+ rubygems_version: 3.5.18
84
84
  signing_key:
85
85
  specification_version: 4
86
86
  summary: A Ruby LSP addon for Rails