ruby-lsp-rails 0.3.3 → 0.3.4

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: 0c6faaac7ecd3824bd041d7273207db6c8611306ef0a33d7d02eb42cc8e931c0
4
- data.tar.gz: 26ee8d231aab1512d52547056838f42a194265645fceb80dc9c02d77eba0e286
3
+ metadata.gz: 463c37a3e7c9fef9c7b5433d2ef9519f41a29867a0089fe6508fc701a6cf1871
4
+ data.tar.gz: 0f3e7a164235e85b610f9e0855a0d57e9a25125999ff1c15ed31acc54880cad2
5
5
  SHA512:
6
- metadata.gz: e6d4711e5a31d9a59b4648e693796ab1398c04abe9b00e55627a6815b989751b8764d0c33a26679a4530aeadf8900d0f7d0f59b0dd0ab55fb68365be831f2199
7
- data.tar.gz: e55de08bde09f2b065ec8820ac29f831ec094efdb23eb8ce930f488c851ad3b4e183ef7f0dafca5d16d511ffa87ae0be20bba4c903a0d4b6d9b2dee2532df369
6
+ metadata.gz: 85cbe926939cac7051877d168de8164099265329ccb0cbe4ff6dbe9d1508ae64ce52b168a816d6cd538c79c384f04a1873654b692c98592cb032bc9c1470f318
7
+ data.tar.gz: c1023803f769de1d701788a85e17906b9626aa9fee89a7d36cabb849826ca68021c00402d8a3d7043cb3128ac7966195fb9ef5b0714109fff96ad907359d7ed5
data/README.md CHANGED
@@ -16,7 +16,8 @@ There is no need to add the gem to your bundle.
16
16
 
17
17
  * Hover over an ActiveRecord model to reveal its schema.
18
18
  * Run or debug a test by clicking on the code lens which appears above the test class, or an individual test.
19
- * Navigate to validations, callbacks and test cases using your editor's "Go to Symbol" feature, or outline view.
19
+ * Navigate to associations, validations, callbacks and test cases using your editor's "Go to Symbol" feature, or outline view.
20
+ * Jump to the definition of callbacks using your editor's "Go to Definition" feature.
20
21
 
21
22
  ## Documentation
22
23
 
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.generator = "snapper"
26
27
  rdoc.options.push("--copy-files", "misc")
27
28
  rdoc.options.push("--copy-files", "LICENSE.txt")
28
29
  end
@@ -10,6 +10,7 @@ module RubyLsp
10
10
  # - [Hover](rdoc-ref:RubyLsp::Rails::Hover)
11
11
  # - [CodeLens](rdoc-ref:RubyLsp::Rails::CodeLens)
12
12
  # - [DocumentSymbol](rdoc-ref:RubyLsp::Rails::DocumentSymbol)
13
+ # - [Definition](rdoc-ref:RubyLsp::Rails::Definition)
13
14
  module Rails
14
15
  end
15
16
  end
@@ -4,10 +4,12 @@
4
4
  require "ruby_lsp/addon"
5
5
 
6
6
  require_relative "support/active_support_test_case_helper"
7
+ require_relative "support/callbacks"
7
8
  require_relative "runner_client"
8
9
  require_relative "hover"
9
10
  require_relative "code_lens"
10
11
  require_relative "document_symbol"
12
+ require_relative "definition"
11
13
 
12
14
  module RubyLsp
13
15
  module Rails
@@ -23,8 +25,10 @@ module RubyLsp
23
25
  @client = T.let(NullClient.new, RunnerClient)
24
26
  end
25
27
 
26
- sig { override.params(message_queue: Thread::Queue).void }
27
- def activate(message_queue)
28
+ sig { override.params(global_state: GlobalState, message_queue: Thread::Queue).void }
29
+ def activate(global_state, message_queue)
30
+ @global_state = T.let(global_state, T.nilable(RubyLsp::GlobalState))
31
+ $stderr.puts("Activating Ruby LSP Rails addon v#{VERSION}")
28
32
  # Start booting the real client in a background thread. Until this completes, the client will be a NullClient
29
33
  Thread.new { @client = RunnerClient.create_client }
30
34
  end
@@ -43,6 +47,8 @@ module RubyLsp
43
47
  ).void
44
48
  end
45
49
  def create_code_lens_listener(response_builder, uri, dispatcher)
50
+ return unless T.must(@global_state).test_library == "rails"
51
+
46
52
  CodeLens.new(response_builder, uri, dispatcher)
47
53
  end
48
54
 
@@ -50,12 +56,11 @@ module RubyLsp
50
56
  override.params(
51
57
  response_builder: ResponseBuilders::Hover,
52
58
  nesting: T::Array[String],
53
- index: RubyIndexer::Index,
54
59
  dispatcher: Prism::Dispatcher,
55
60
  ).void
56
61
  end
57
- def create_hover_listener(response_builder, nesting, index, dispatcher)
58
- Hover.new(@client, response_builder, nesting, index, dispatcher)
62
+ def create_hover_listener(response_builder, nesting, dispatcher)
63
+ Hover.new(@client, response_builder, nesting, T.must(@global_state), dispatcher)
59
64
  end
60
65
 
61
66
  sig do
@@ -68,6 +73,19 @@ module RubyLsp
68
73
  DocumentSymbol.new(response_builder, dispatcher)
69
74
  end
70
75
 
76
+ sig do
77
+ override.params(
78
+ response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::Location],
79
+ uri: URI::Generic,
80
+ nesting: T::Array[String],
81
+ dispatcher: Prism::Dispatcher,
82
+ ).void
83
+ end
84
+ def create_definition_listener(response_builder, uri, nesting, dispatcher)
85
+ index = T.must(@global_state).index
86
+ Definition.new(response_builder, nesting, index, dispatcher)
87
+ end
88
+
71
89
  sig { params(changes: T::Array[{ uri: String, type: Integer }]).void }
72
90
  def workspace_did_change_watched_files(changes)
73
91
  if changes.any? do |change|
@@ -0,0 +1,87 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module Rails
6
+ # ![Definition demo](../../definition.gif)
7
+ #
8
+ # The [definition
9
+ # request](https://microsoft.github.io/language-server-protocol/specification#textDocument_definition) jumps to the
10
+ # definition of the symbol under the cursor.
11
+ #
12
+ # Currently supported targets:
13
+ # - Callbacks
14
+ #
15
+ # # Example
16
+ #
17
+ # ```ruby
18
+ # before_action :foo # <- Go to definition on this symbol will jump to the method if it is defined in the same class
19
+ # ```
20
+ class Definition
21
+ extend T::Sig
22
+ include Requests::Support::Common
23
+
24
+ sig do
25
+ params(
26
+ response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::Location],
27
+ nesting: T::Array[String],
28
+ index: RubyIndexer::Index,
29
+ dispatcher: Prism::Dispatcher,
30
+ ).void
31
+ end
32
+ def initialize(response_builder, nesting, index, dispatcher)
33
+ @response_builder = response_builder
34
+ @nesting = nesting
35
+ @index = index
36
+
37
+ dispatcher.register(self, :on_call_node_enter)
38
+ end
39
+
40
+ sig { params(node: Prism::CallNode).void }
41
+ def on_call_node_enter(node)
42
+ return unless self_receiver?(node)
43
+
44
+ message = node.message
45
+
46
+ return unless message && Support::Callbacks::ALL.include?(message)
47
+
48
+ arguments = node.arguments&.arguments
49
+ return unless arguments&.any?
50
+
51
+ arguments.each do |argument|
52
+ name = case argument
53
+ when Prism::SymbolNode
54
+ argument.value
55
+ when Prism::StringNode
56
+ argument.content
57
+ end
58
+
59
+ next unless name
60
+
61
+ collect_definitions(name)
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ sig { params(name: String).void }
68
+ def collect_definitions(name)
69
+ methods = @index.resolve_method(name, @nesting.join("::"))
70
+ return unless methods
71
+
72
+ methods.each do |target_method|
73
+ location = target_method.location
74
+ file_path = target_method.file_path
75
+
76
+ @response_builder << Interface::Location.new(
77
+ uri: URI::Generic.from_path(path: file_path).to_s,
78
+ range: Interface::Range.new(
79
+ start: Interface::Position.new(line: location.start_line - 1, character: location.start_column),
80
+ end: Interface::Position.new(line: location.end_line - 1, character: location.end_column),
81
+ ),
82
+ )
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -6,69 +6,13 @@ module RubyLsp
6
6
  # ![Document Symbol demo](../../document_symbol.gif)
7
7
  #
8
8
  # The [document symbol](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentSymbol)
9
- # request allows users to navigate between ActiveSupport test cases with VS Code's "Go to Symbol" feature.
9
+ # request allows users to navigate between associations, validations, callbacks and ActiveSupport test cases with
10
+ # VS Code's "Go to Symbol" feature.
10
11
  class DocumentSymbol
11
12
  extend T::Sig
12
13
  include Requests::Support::Common
13
14
  include ActiveSupportTestCaseHelper
14
15
 
15
- MODEL_CALLBACKS = T.let(
16
- [
17
- "before_validation",
18
- "after_validation",
19
- "before_save",
20
- "around_save",
21
- "after_save",
22
- "before_create",
23
- "around_create",
24
- "after_create",
25
- "after_commit",
26
- "after_rollback",
27
- "before_update",
28
- "around_update",
29
- "after_update",
30
- "before_destroy",
31
- "around_destroy",
32
- "after_destroy",
33
- "after_initialize",
34
- "after_find",
35
- "after_touch",
36
- ].freeze,
37
- T::Array[String],
38
- )
39
-
40
- CONTROLLER_CALLBACKS = T.let(
41
- [
42
- "after_action",
43
- "append_after_action",
44
- "append_around_action",
45
- "append_before_action",
46
- "around_action",
47
- "before_action",
48
- "prepend_after_action",
49
- "prepend_around_action",
50
- "prepend_before_action",
51
- "skip_after_action",
52
- "skip_around_action",
53
- "skip_before_action",
54
- ].freeze,
55
- T::Array[String],
56
- )
57
-
58
- JOB_CALLBACKS = T.let(
59
- [
60
- "after_enqueue",
61
- "after_perform",
62
- "around_enqueue",
63
- "around_perform",
64
- "before_enqueue",
65
- "before_perform",
66
- ].freeze,
67
- T::Array[String],
68
- )
69
-
70
- CALLBACKS = T.let((MODEL_CALLBACKS + CONTROLLER_CALLBACKS + JOB_CALLBACKS).freeze, T::Array[String])
71
-
72
16
  sig do
73
17
  params(
74
18
  response_builder: ResponseBuilders::DocumentSymbol,
@@ -98,9 +42,9 @@ module RubyLsp
98
42
 
99
43
  message = node.message
100
44
  case message
101
- when *CALLBACKS, "validate"
45
+ when *Support::Callbacks::ALL, "validate"
102
46
  handle_all_arg_types(node, T.must(message))
103
- when "validates", "validates!", "validates_each"
47
+ when "validates", "validates!", "validates_each", "belongs_to", "has_one", "has_many", "has_and_belongs_to_many"
104
48
  handle_symbol_and_string_arg_types(node, T.must(message))
105
49
  when "validates_with"
106
50
  handle_class_arg_types(node, T.must(message))
@@ -115,7 +59,7 @@ module RubyLsp
115
59
 
116
60
  if block
117
61
  append_document_symbol(
118
- name: "#{message}(<anonymous>)",
62
+ name: "#{message} <anonymous>",
119
63
  range: range_from_location(node.location),
120
64
  selection_range: range_from_location(block.location),
121
65
  )
@@ -132,7 +76,7 @@ module RubyLsp
132
76
  next unless name
133
77
 
134
78
  append_document_symbol(
135
- name: "#{message}(#{name})",
79
+ name: "#{message} :#{name}",
136
80
  range: range_from_location(argument.location),
137
81
  selection_range: range_from_location(T.must(argument.value_loc)),
138
82
  )
@@ -141,13 +85,13 @@ module RubyLsp
141
85
  next if name.empty?
142
86
 
143
87
  append_document_symbol(
144
- name: "#{message}(#{name})",
88
+ name: "#{message} :#{name}",
145
89
  range: range_from_location(argument.location),
146
90
  selection_range: range_from_location(argument.content_loc),
147
91
  )
148
92
  when Prism::LambdaNode
149
93
  append_document_symbol(
150
- name: "#{message}(<anonymous>)",
94
+ name: "#{message} <anonymous>",
151
95
  range: range_from_location(node.location),
152
96
  selection_range: range_from_location(argument.location),
153
97
  )
@@ -156,12 +100,12 @@ module RubyLsp
156
100
 
157
101
  arg_receiver = argument.receiver
158
102
 
159
- name = arg_receiver.name if arg_receiver.is_a?(Prism::ConstantReadNode)
160
- name = arg_receiver.full_name if arg_receiver.is_a?(Prism::ConstantPathNode)
103
+ name = arg_receiver.full_name if arg_receiver.is_a?(Prism::ConstantReadNode) ||
104
+ arg_receiver.is_a?(Prism::ConstantPathNode)
161
105
  next unless name
162
106
 
163
107
  append_document_symbol(
164
- name: "#{message}(#{name})",
108
+ name: "#{message} #{name}",
165
109
  range: range_from_location(argument.location),
166
110
  selection_range: range_from_location(argument.location),
167
111
  )
@@ -170,7 +114,7 @@ module RubyLsp
170
114
  next if name.empty?
171
115
 
172
116
  append_document_symbol(
173
- name: "#{message}(#{name})",
117
+ name: "#{message} #{name}",
174
118
  range: range_from_location(argument.location),
175
119
  selection_range: range_from_location(argument.location),
176
120
  )
@@ -190,7 +134,7 @@ module RubyLsp
190
134
  next unless name
191
135
 
192
136
  append_document_symbol(
193
- name: "#{message}(#{name})",
137
+ name: "#{message} :#{name}",
194
138
  range: range_from_location(argument.location),
195
139
  selection_range: range_from_location(T.must(argument.value_loc)),
196
140
  )
@@ -199,7 +143,7 @@ module RubyLsp
199
143
  next if name.empty?
200
144
 
201
145
  append_document_symbol(
202
- name: "#{message}(#{name})",
146
+ name: "#{message} :#{name}",
203
147
  range: range_from_location(argument.location),
204
148
  selection_range: range_from_location(argument.content_loc),
205
149
  )
@@ -219,7 +163,7 @@ module RubyLsp
219
163
  next if name.empty?
220
164
 
221
165
  append_document_symbol(
222
- name: "#{message}(#{name})",
166
+ name: "#{message} #{name}",
223
167
  range: range_from_location(argument.location),
224
168
  selection_range: range_from_location(argument.location),
225
169
  )
@@ -25,15 +25,15 @@ module RubyLsp
25
25
  client: RunnerClient,
26
26
  response_builder: ResponseBuilders::Hover,
27
27
  nesting: T::Array[String],
28
- index: RubyIndexer::Index,
28
+ global_state: GlobalState,
29
29
  dispatcher: Prism::Dispatcher,
30
30
  ).void
31
31
  end
32
- def initialize(client, response_builder, nesting, index, dispatcher)
32
+ def initialize(client, response_builder, nesting, global_state, dispatcher)
33
33
  @client = client
34
34
  @response_builder = response_builder
35
35
  @nesting = nesting
36
- @index = index
36
+ @index = T.let(global_state.index, RubyIndexer::Index)
37
37
  dispatcher.register(self, :on_constant_path_node_enter, :on_constant_read_node_enter, :on_call_node_enter)
38
38
  end
39
39
 
@@ -30,6 +30,9 @@ module RubyLsp
30
30
 
31
31
  class InitializationError < StandardError; end
32
32
  class IncompleteMessageError < StandardError; end
33
+ class EmptyMessageError < StandardError; end
34
+
35
+ MAX_RETRIES = 5
33
36
 
34
37
  extend T::Sig
35
38
 
@@ -59,7 +62,16 @@ module RubyLsp
59
62
  @stdout.binmode # for Windows compatibility
60
63
 
61
64
  $stderr.puts("Ruby LSP Rails booting server")
62
- read_response
65
+ count = 0
66
+
67
+ begin
68
+ count += 1
69
+ read_response
70
+ rescue EmptyMessageError
71
+ $stderr.puts("Ruby LSP Rails is retrying initialize (#{count})")
72
+ retry if count < MAX_RETRIES
73
+ end
74
+
63
75
  $stderr.puts("Finished booting Ruby LSP Rails server")
64
76
 
65
77
  unless ENV["RAILS_ENV"] == "test"
@@ -134,7 +146,10 @@ module RubyLsp
134
146
  headers = @stdout.gets("\r\n\r\n")
135
147
  raise IncompleteMessageError unless headers
136
148
 
137
- raw_response = @stdout.read(headers[/Content-Length: (\d+)/i, 1].to_i)
149
+ content_length = headers[/Content-Length: (\d+)/i, 1].to_i
150
+ raise EmptyMessageError if content_length.zero?
151
+
152
+ raw_response = @stdout.read(content_length)
138
153
  response = JSON.parse(T.must(raw_response), symbolize_names: true)
139
154
 
140
155
  if response[:error]
@@ -0,0 +1,67 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module Rails
6
+ module Support
7
+ module Callbacks
8
+ MODELS = T.let(
9
+ [
10
+ "before_validation",
11
+ "after_validation",
12
+ "before_save",
13
+ "around_save",
14
+ "after_save",
15
+ "before_create",
16
+ "around_create",
17
+ "after_create",
18
+ "after_commit",
19
+ "after_rollback",
20
+ "before_update",
21
+ "around_update",
22
+ "after_update",
23
+ "before_destroy",
24
+ "around_destroy",
25
+ "after_destroy",
26
+ "after_initialize",
27
+ "after_find",
28
+ "after_touch",
29
+ ].freeze,
30
+ T::Array[String],
31
+ )
32
+
33
+ CONTROLLERS = T.let(
34
+ [
35
+ "after_action",
36
+ "append_after_action",
37
+ "append_around_action",
38
+ "append_before_action",
39
+ "around_action",
40
+ "before_action",
41
+ "prepend_after_action",
42
+ "prepend_around_action",
43
+ "prepend_before_action",
44
+ "skip_after_action",
45
+ "skip_around_action",
46
+ "skip_before_action",
47
+ ].freeze,
48
+ T::Array[String],
49
+ )
50
+
51
+ JOBS = T.let(
52
+ [
53
+ "after_enqueue",
54
+ "after_perform",
55
+ "around_enqueue",
56
+ "around_perform",
57
+ "before_enqueue",
58
+ "before_perform",
59
+ ].freeze,
60
+ T::Array[String],
61
+ )
62
+
63
+ ALL = T.let((MODELS + CONTROLLERS + JOBS).freeze, T::Array[String])
64
+ end
65
+ end
66
+ end
67
+ end
@@ -3,6 +3,6 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Rails
6
- VERSION = "0.3.3"
6
+ VERSION = "0.3.4"
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.3
4
+ version: 0.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-03-14 00:00:00.000000000 Z
11
+ date: 2024-04-04 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.14.2
61
+ version: 0.16.0
62
62
  - - "<"
63
63
  - !ruby/object:Gem::Version
64
- version: 0.15.0
64
+ version: 0.17.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.14.2
71
+ version: 0.16.0
72
72
  - - "<"
73
73
  - !ruby/object:Gem::Version
74
- version: 0.15.0
74
+ version: 0.17.0
75
75
  - !ruby/object:Gem::Dependency
76
76
  name: sorbet-runtime
77
77
  requirement: !ruby/object:Gem::Requirement
@@ -99,11 +99,13 @@ files:
99
99
  - lib/ruby-lsp-rails.rb
100
100
  - lib/ruby_lsp/ruby_lsp_rails/addon.rb
101
101
  - lib/ruby_lsp/ruby_lsp_rails/code_lens.rb
102
+ - lib/ruby_lsp/ruby_lsp_rails/definition.rb
102
103
  - lib/ruby_lsp/ruby_lsp_rails/document_symbol.rb
103
104
  - lib/ruby_lsp/ruby_lsp_rails/hover.rb
104
105
  - lib/ruby_lsp/ruby_lsp_rails/runner_client.rb
105
106
  - lib/ruby_lsp/ruby_lsp_rails/server.rb
106
107
  - lib/ruby_lsp/ruby_lsp_rails/support/active_support_test_case_helper.rb
108
+ - lib/ruby_lsp/ruby_lsp_rails/support/callbacks.rb
107
109
  - lib/ruby_lsp/ruby_lsp_rails/support/rails_document_client.rb
108
110
  - lib/ruby_lsp_rails/railtie.rb
109
111
  - lib/ruby_lsp_rails/version.rb
@@ -124,14 +126,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
124
126
  requirements:
125
127
  - - ">="
126
128
  - !ruby/object:Gem::Version
127
- version: '0'
129
+ version: 3.0.0
128
130
  required_rubygems_version: !ruby/object:Gem::Requirement
129
131
  requirements:
130
132
  - - ">="
131
133
  - !ruby/object:Gem::Version
132
134
  version: '0'
133
135
  requirements: []
134
- rubygems_version: 3.5.6
136
+ rubygems_version: 3.5.7
135
137
  signing_key:
136
138
  specification_version: 4
137
139
  summary: A Ruby LSP addon for Rails