Skip to content

Commit

Permalink
Bug fixes + finish writing unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ChallaHalla committed Dec 11, 2024
1 parent b5a1117 commit 12955db
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 6 deletions.
25 changes: 19 additions & 6 deletions lib/ruby_lsp/ruby_lsp_rails/completion.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ class Completion
sig do
override.params(
client: RunnerClient,
response_builder: ResponseBuilders::CollectionResponseBuilder[T.any(
Interface::Location, Interface::LocationLink
)],
response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem],
node_context: NodeContext,
dispatcher: Prism::Dispatcher,
uri: URI::Generic,
Expand All @@ -30,7 +28,7 @@ def initialize(client, response_builder, node_context, dispatcher, uri)

sig { params(node: Prism::CallNode).void }
def on_call_node_enter(node)
if @node_context&.call_node&.name == :where
if node.name == :where
handle_active_record_where_completions(node)
end
end
Expand All @@ -39,15 +37,30 @@ def on_call_node_enter(node)

sig { params(node: Prism::CallNode).void }
def handle_active_record_where_completions(node)
resolved_class = @client.model(T.must(@node_context.call_node).receiver&.name)
receiver = node.receiver
return unless receiver

resolved_class = @client.model(receiver.name.to_s)
return if resolved_class.nil?

arguments_node = node.arguments
existing_args = {}
if arguments_node
arguments_node.arguments.each do |arg|
arg.elements.each do |a|
existing_args[a.key.unescaped] = true
end
end
end

resolved_class[:columns].each do |column|
next if existing_args[column[0]]

@response_builder << Interface::CompletionItem.new(
label: column[0],
filter_text: column[0],
label_details: Interface::CompletionItemLabelDetails.new(
description: "Filter #{T.must(@node_context.call_node).receiver.name} records by #{column[0]}",
description: "Filter #{node.receiver.name} records by #{column[0]}",
),
text_edit: Interface::TextEdit.new(range: 0, new_text: "#{column[0]}: "),
kind: Constant::CompletionItemKind::FIELD,
Expand Down
94 changes: 94 additions & 0 deletions test/ruby_lsp_rails/completion_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# typed: true
# frozen_string_literal: true

require "test_helper"

module RubyLsp
module Rails
class CompletionTest < ActiveSupport::TestCase
test "recognizes Active Record .where call on an Active Record model when cursor is on (" do
response = generate_completions_for_source(<<~RUBY, { line: 2, character: 10 })
# typed: false
User.where(
RUBY

columns = [
"id",
"first_name",
"last_name",
"age",
"created_at",
"updated_at",
"country_id",
"active",
]
assert_equal(columns.size, response.size)

columns.each_with_index do |column, i|
assert_equal(column, response[i].label)
assert_equal(column, response[i].filter_text)
end
end

test "Does not suggest column if it already exists within .where as an arg and parantheses are not closed" do
response = generate_completions_for_source(<<~RUBY, { line: 2, character: 28 })
# typed: false
User.where(id:, first_name:,
RUBY

columns = [ "last_name", "age", "created_at", "updated_at", "country_id", "active" ]
assert_equal(columns.size, response.size)

columns.each_with_index do |column, i|
assert_equal(column, response[i].label)
assert_equal(column, response[i].filter_text)
end
end

test "Does not suggest column if it already exists within .where as an arg and parantheses are closed" do
response = generate_completions_for_source(<<~RUBY, { line: 2, character: 28 })
# typed: false
User.where(id:, first_name:, )
RUBY

columns = [ "last_name", "age", "created_at", "updated_at", "country_id", "active" ]
assert_equal(columns.size, response.size)

columns.each_with_index do |column, i|
assert_equal(column, response[i].label)
assert_equal(column, response[i].filter_text)
end
end

test "Does not provide suggestions when typing argument values" do
response = generate_completions_for_source(<<~RUBY, { line: 1, character: 14})
# typed: false
User.where(id:
RUBY

assert_equal(0, response.size)
end


private

def generate_completions_for_source(source, position)
with_server(source) do |server, uri|
sleep(0.1) while RubyLsp::Addon.addons.first.instance_variable_get(:@rails_runner_client).is_a?(NullClient)

server.process_message(
id: 1,
method: "textDocument/completion",
params: { textDocument: { uri: uri }, position: position },
)

result = pop_result(server)
result.response
end
end
end
end
end

0 comments on commit 12955db

Please sign in to comment.