ruby-lsp-rails 0.3.3 → 0.3.4

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: 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