Skip to content

Commit 5998de8

Browse files
Add scope to list of symbols (#333)
Add scope to list of symbols
1 parent 042537b commit 5998de8

File tree

3 files changed

+92
-3
lines changed

3 files changed

+92
-3
lines changed

lib/ruby_lsp/ruby_lsp_rails/document_symbol.rb

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,22 @@ class DocumentSymbol
2121
end
2222
def initialize(response_builder, dispatcher)
2323
@response_builder = response_builder
24-
25-
dispatcher.register(self, :on_call_node_enter)
24+
@namespace_stack = T.let([], T::Array[String])
25+
26+
dispatcher.register(
27+
self,
28+
:on_call_node_enter,
29+
:on_class_node_enter,
30+
:on_class_node_leave,
31+
:on_module_node_enter,
32+
:on_module_node_leave,
33+
)
2634
end
2735

2836
sig { params(node: Prism::CallNode).void }
2937
def on_call_node_enter(node)
38+
return if @namespace_stack.empty?
39+
3040
content = extract_test_case_name(node)
3141

3242
if content
@@ -45,15 +55,45 @@ def on_call_node_enter(node)
4555
when *Support::Callbacks::ALL, "validate"
4656
handle_all_arg_types(node, T.must(message))
4757
when "validates", "validates!", "validates_each", "belongs_to", "has_one", "has_many",
48-
"has_and_belongs_to_many", "attr_readonly"
58+
"has_and_belongs_to_many", "attr_readonly", "scope"
4959
handle_symbol_and_string_arg_types(node, T.must(message))
5060
when "validates_with"
5161
handle_class_arg_types(node, T.must(message))
5262
end
5363
end
5464

65+
sig { params(node: Prism::ClassNode).void }
66+
def on_class_node_enter(node)
67+
add_to_namespace_stack(node)
68+
end
69+
70+
sig { params(node: Prism::ClassNode).void }
71+
def on_class_node_leave(node)
72+
remove_from_namespace_stack(node)
73+
end
74+
75+
sig { params(node: Prism::ModuleNode).void }
76+
def on_module_node_enter(node)
77+
add_to_namespace_stack(node)
78+
end
79+
80+
sig { params(node: Prism::ModuleNode).void }
81+
def on_module_node_leave(node)
82+
remove_from_namespace_stack(node)
83+
end
84+
5585
private
5686

87+
sig { params(node: T.any(Prism::ClassNode, Prism::ModuleNode)).void }
88+
def add_to_namespace_stack(node)
89+
@namespace_stack << node.constant_path.slice
90+
end
91+
92+
sig { params(node: T.any(Prism::ClassNode, Prism::ModuleNode)).void }
93+
def remove_from_namespace_stack(node)
94+
@namespace_stack.delete(node.constant_path.slice)
95+
end
96+
5797
sig { params(node: Prism::CallNode, message: String).void }
5898
def handle_all_arg_types(node, message)
5999
block = node.block

test/dummy/app/models/user.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ class User < ApplicationRecord
44
before_create :foo, -> () {}
55
validates :name, presence: true
66
has_one :profile
7+
scope :adult, -> { where(age: 18..) }
8+
9+
attr_readonly :last_name
710

811
private
912

test/ruby_lsp_rails/document_symbol_test.rb

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,52 @@ class FooModel < ApplicationRecord
168168
assert_equal("attr_readonly :foo", response[0].children[0].name)
169169
end
170170

171+
test "correctly handles scope in a class" do
172+
response = generate_document_symbols_for_source(<<~RUBY)
173+
class FooModel < ApplicationRecord
174+
scope :bar, ->{ where(b: 2).order(:c) }
175+
scope :foo, ->{ where(a: 1).order(:b) }
176+
end
177+
RUBY
178+
179+
assert_equal(1, response.size)
180+
assert_equal("FooModel", response[0].name)
181+
assert_equal(2, response[0].children.size)
182+
assert_equal("scope :bar", response[0].children[0].name)
183+
assert_equal("scope :foo", response[0].children[1].name)
184+
end
185+
186+
test "correctly handles scope in a concern" do
187+
response = generate_document_symbols_for_source(<<~RUBY)
188+
module Scopable
189+
extend ActiveSupport::Concern
190+
191+
included do
192+
scope :bar, ->{ where(b: 2).order(:c) }
193+
scope :foo, ->{ where(a: 1).order(:b) }
194+
end
195+
end
196+
RUBY
197+
198+
assert_equal(1, response.size)
199+
assert_equal("Scopable", response[0].name)
200+
assert_equal(2, response[0].children.size)
201+
assert_equal("scope :bar", response[0].children[0].name)
202+
assert_equal("scope :foo", response[0].children[1].name)
203+
end
204+
205+
test "ignores scope in a routes file" do
206+
response = generate_document_symbols_for_source(<<~RUBY)
207+
Rails.application.routes.draw do
208+
scope '/admin' do
209+
resources :users
210+
end
211+
end
212+
RUBY
213+
214+
assert_equal(0, response.size)
215+
end
216+
171217
test "correctly handles model callbacks with multiple string arguments" do
172218
response = generate_document_symbols_for_source(<<~RUBY)
173219
class FooModel < ApplicationRecord

0 commit comments

Comments
 (0)