Skip to content

Commit badda27

Browse files
authored
Handle namespaces for showing column information (#171)
* Handle namespaces for showing column information * Add namespaced model for testing purposes
1 parent e0c6793 commit badda27

File tree

7 files changed

+141
-19
lines changed

7 files changed

+141
-19
lines changed

lib/ruby_lsp/ruby_lsp_rails/addon.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def create_code_lens_listener(uri, emitter, message_queue)
4646
).returns(T.nilable(Listener[T.nilable(Interface::Hover)]))
4747
end
4848
def create_hover_listener(nesting, index, emitter, message_queue)
49-
Hover.new(client, emitter, message_queue)
49+
Hover.new(client, nesting, index, emitter, message_queue)
5050
end
5151

5252
sig { override.returns(String) }

lib/ruby_lsp/ruby_lsp_rails/hover.rb

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,33 +25,53 @@ class Hover < ::RubyLsp::Listener
2525
sig { override.returns(ResponseType) }
2626
attr_reader :_response
2727

28-
sig { params(client: RailsClient, emitter: RubyLsp::EventEmitter, message_queue: Thread::Queue).void }
29-
def initialize(client, emitter, message_queue)
28+
sig do
29+
params(
30+
client: RailsClient,
31+
nesting: T::Array[String],
32+
index: RubyIndexer::Index,
33+
emitter: RubyLsp::EventEmitter,
34+
message_queue: Thread::Queue,
35+
).void
36+
end
37+
def initialize(client, nesting, index, emitter, message_queue)
3038
super(emitter, message_queue)
3139

3240
@_response = T.let(nil, ResponseType)
3341
@client = client
42+
@nesting = nesting
43+
@index = index
3444
emitter.register(self, :on_constant_path, :on_constant_read, :on_call)
3545
end
3646

3747
sig { params(node: YARP::ConstantPathNode).void }
3848
def on_constant_path(node)
39-
@_response = generate_rails_document_link_hover(node.slice, node.location)
49+
entries = @index.resolve(node.slice, @nesting)
50+
return unless entries
51+
52+
name = T.must(entries.first).name
53+
content = +""
54+
column_info = generate_column_content(name)
55+
content << column_info if column_info
56+
57+
urls = Support::RailsDocumentClient.generate_rails_document_urls(name)
58+
content << urls.join("\n\n") unless urls.empty?
59+
return if content.empty?
60+
61+
contents = RubyLsp::Interface::MarkupContent.new(kind: "markdown", value: content)
62+
@_response = RubyLsp::Interface::Hover.new(range: range_from_location(node.location), contents: contents)
4063
end
4164

4265
sig { params(node: YARP::ConstantReadNode).void }
4366
def on_constant_read(node)
44-
model = @client.model(node.name.to_s)
45-
return if model.nil?
67+
entries = @index.resolve(node.name.to_s, @nesting)
68+
return unless entries
69+
70+
content = generate_column_content(T.must(entries.first).name)
71+
return unless content
4672

47-
schema_file = model[:schema_file]
48-
content = +""
49-
if schema_file
50-
content << "[Schema](#{URI::Generic.build(scheme: "file", path: schema_file)})\n\n"
51-
end
52-
content << model[:columns].map { |name, type| "**#{name}**: #{type}\n" }.join("\n")
5373
contents = RubyLsp::Interface::MarkupContent.new(kind: "markdown", value: content)
54-
@_response = RubyLsp::Interface::Hover.new(range: range_from_node(node), contents: contents)
74+
@_response = RubyLsp::Interface::Hover.new(range: range_from_location(node.location), contents: contents)
5575
end
5676

5777
sig { params(node: YARP::CallNode).void }
@@ -66,6 +86,18 @@ def on_call(node)
6686

6787
private
6888

89+
sig { params(name: String).returns(T.nilable(String)) }
90+
def generate_column_content(name)
91+
model = @client.model(name)
92+
return if model.nil?
93+
94+
schema_file = model[:schema_file]
95+
content = +""
96+
content << "[Schema](#{URI::Generic.build(scheme: "file", path: schema_file)})\n\n" if schema_file
97+
content << model[:columns].map { |name, type| "**#{name}**: #{type}\n" }.join("\n")
98+
content
99+
end
100+
69101
sig { params(name: String, location: YARP::Location).returns(T.nilable(Interface::Hover)) }
70102
def generate_rails_document_link_hover(name, location)
71103
urls = Support::RailsDocumentClient.generate_rails_document_urls(name)

test/dummy/app/models/blog/post.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# typed: true
2+
# frozen_string_literal: true
3+
4+
module Blog
5+
class Post < ApplicationRecord
6+
self.table_name = "posts"
7+
end
8+
end
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class CreatePosts < ActiveRecord::Migration[7.2]
2+
def change
3+
create_table :posts do |t|
4+
t.string :title
5+
t.text :body
6+
7+
t.timestamps
8+
end
9+
end
10+
end

test/dummy/db/schema.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,14 @@
1010
#
1111
# It's strongly recommended that you check this file into your version control system.
1212

13-
ActiveRecord::Schema[7.0].define(version: 2023_03_30_202955) do
13+
ActiveRecord::Schema[7.2].define(version: 2023_10_19_180159) do
14+
create_table "posts", force: :cascade do |t|
15+
t.string "title"
16+
t.text "body"
17+
t.datetime "created_at", null: false
18+
t.datetime "updated_at", null: false
19+
end
20+
1421
create_table "users", force: :cascade do |t|
1522
t.string "first_name"
1623
t.string "last_name"

test/ruby_lsp_rails/hover_test.rb

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,54 @@ class HoverTest < ActiveSupport::TestCase
3737
stub_http_request("200", expected_response.to_json)
3838
@client.stubs(check_if_server_is_running!: true)
3939

40-
response = hover_on_source("User", { line: 0, character: 0 })
40+
response = hover_on_source(<<~RUBY, { line: 3, character: 0 })
41+
class User < ApplicationRecord
42+
end
43+
44+
User
45+
RUBY
46+
47+
assert_equal(<<~CONTENT, response.contents.value)
48+
[Schema](file://#{@client.root}/db/schema.rb)
49+
50+
**id**: integer
51+
52+
**first_name**: string
53+
54+
**last_name**: string
55+
56+
**age**: integer
57+
58+
**created_at**: datetime
59+
60+
**updated_at**: datetime
61+
CONTENT
62+
end
63+
64+
test "return column information for namespaced models" do
65+
expected_response = {
66+
schema_file: "#{@client.root}/db/schema.rb",
67+
columns: [
68+
["id", "integer"],
69+
["first_name", "string"],
70+
["last_name", "string"],
71+
["age", "integer"],
72+
["created_at", "datetime"],
73+
["updated_at", "datetime"],
74+
],
75+
}
76+
77+
stub_http_request("200", expected_response.to_json)
78+
@client.stubs(check_if_server_is_running!: true)
79+
80+
response = hover_on_source(<<~RUBY, { line: 4, character: 6 })
81+
module Blog
82+
class User < ApplicationRecord
83+
end
84+
end
85+
86+
Blog::User
87+
RUBY
4188

4289
assert_equal(<<~CONTENT, response.contents.value)
4390
[Schema](file://#{@client.root}/db/schema.rb)
@@ -65,7 +112,12 @@ class HoverTest < ActiveSupport::TestCase
65112
stub_http_request("200", expected_response.to_json)
66113
@client.stubs(check_if_server_is_running!: true)
67114

68-
response = hover_on_source("User", { line: 0, character: 0 })
115+
response = hover_on_source(<<~RUBY, { line: 3, character: 0 })
116+
class User < ApplicationRecord
117+
end
118+
119+
User
120+
RUBY
69121

70122
assert_includes(
71123
response.contents.value,
@@ -82,7 +134,12 @@ class HoverTest < ActiveSupport::TestCase
82134
stub_http_request("200", expected_response.to_json)
83135
@client.stubs(check_if_server_is_running!: true)
84136

85-
response = hover_on_source("User", { line: 0, character: 0 })
137+
response = hover_on_source(<<~RUBY, { line: 3, character: 0 })
138+
class User < ApplicationRecord
139+
end
140+
141+
User
142+
RUBY
86143

87144
refute_match(/Schema/, response.contents.value)
88145
end
@@ -116,7 +173,11 @@ class HoverTest < ActiveSupport::TestCase
116173
end
117174

118175
test "shows documentation for Rails constants" do
119-
value = hover_on_source("ActiveRecord::Base", { line: 0, character: 14 }).contents.value
176+
value = hover_on_source(<<~RUBY, { line: 2, character: 14 }).contents.value
177+
class ActiveRecord::Base
178+
end
179+
ActiveRecord::Base
180+
RUBY
120181

121182
assert_match(/\[Rails Document: `ActiveRecord::Base`\]/, value)
122183
assert_match(%r{\(https://api\.rubyonrails\.org/.*Base\.html\)}, value)
@@ -129,7 +190,11 @@ def hover_on_source(source, position)
129190
store = RubyLsp::Store.new
130191
store.set(uri: uri, source: source, version: 1)
131192

132-
response = RubyLsp::Executor.new(store, @message_queue).execute(
193+
executor = RubyLsp::Executor.new(store, @message_queue)
194+
executor.instance_variable_get(:@index).index_single(
195+
RubyIndexer::IndexablePath.new(nil, T.must(uri.to_standardized_path)), source
196+
)
197+
response = executor.execute(
133198
{
134199
method: "textDocument/hover",
135200
params: {

0 commit comments

Comments
 (0)