diff --git a/lib/ruby_lsp/ruby_lsp_rails/completion.rb b/lib/ruby_lsp/ruby_lsp_rails/completion.rb index 377ce5af..450c28ed 100644 --- a/lib/ruby_lsp/ruby_lsp_rails/completion.rb +++ b/lib/ruby_lsp/ruby_lsp_rails/completion.rb @@ -28,73 +28,49 @@ def initialize(client, response_builder, node_context, dispatcher, uri) sig { params(node: Prism::CallNode).void } def on_call_node_enter(node) - if node.name == :where - handle_active_record_where_completions(call_node: node) - elsif @node_context.call_node && @node_context.call_node&.name == :where - handle_active_record_where_completions(call_node: T.must(@node_context.call_node), current_node: node) + if @node_context.call_node && @node_context.call_node&.name == :where + handle_active_record_where_completions(node) end end private - sig { params(call_node: Prism::CallNode, current_node: T.nilable(Prism::CallNode)).void } - def handle_active_record_where_completions(call_node:, current_node: nil) - receiver = call_node.receiver + sig { params(node: Prism::CallNode).void } + def handle_active_record_where_completions(node) + receiver = T.must(@node_context.call_node).receiver return if receiver.nil? return unless receiver.is_a?(Prism::ConstantReadNode) resolved_class = @client.model(receiver.name.to_s) return if resolved_class.nil? - arguments = call_node.arguments&.arguments + arguments = T.must(@node_context.call_node).arguments&.arguments existing_args = T.let({}, T::Hash[String, T::Boolean]) if arguments&.is_a?(Array) - return if current_node && current_node_is_argument_value?(current_node: current_node, arguments: arguments) + return if node_is_argument_value?(node: node, arguments: arguments) existing_args = get_call_node_arguments(arguments: arguments) end resolved_class[:columns].each do |column| - next if current_node && !column[0].start_with?(current_node.name.to_s) + next unless column[0].start_with?(node.name.to_s) next if existing_args[column[0]] - if current_node - location = current_node.location - range = Interface::Range.new( - start: Interface::Position.new( - line: location.start_line - 1, - character: location.start_column, - ), - end: Interface::Position.new(line: location.end_line - 1, character: location.end_column), - ) - else - location = call_node.location - # unclear how to calculate location in this scenario - range = Interface::Range.new( - start: Interface::Position.new( - line: location.start_line - 1, - character: 0, - ), - end: Interface::Position.new(line: location.end_line - 1, character: 0), - ) - - end - @response_builder << Interface::CompletionItem.new( label: column[0], filter_text: column[0], label_details: Interface::CompletionItemLabelDetails.new( description: "Filter #{receiver.name} records by #{column[0]}", ), - text_edit: Interface::TextEdit.new(range: range, new_text: "#{column[0]}: "), + text_edit: Interface::TextEdit.new(range: range_from_location(node.location), new_text: "#{column[0]}: "), kind: Constant::CompletionItemKind::FIELD, ) end end - sig { params(current_node: Prism::Node, arguments: T::Array[Prism::Node]).returns(T::Boolean) } - def current_node_is_argument_value?(current_node:, arguments:) + sig { params(node: Prism::Node, arguments: T::Array[Prism::Node]).returns(T::Boolean) } + def node_is_argument_value?(node:, arguments:) arguments.any? do |argument| next unless argument.is_a?(Prism::KeywordHashNode) @@ -103,7 +79,7 @@ def current_node_is_argument_value?(current_node:, arguments:) value = e.value if value.is_a?(Prism::CallNode) - value == current_node + value == node end end end diff --git a/test/ruby_lsp_rails/completion_test.rb b/test/ruby_lsp_rails/completion_test.rb index 589bbf4d..5840d147 100644 --- a/test/ruby_lsp_rails/completion_test.rb +++ b/test/ruby_lsp_rails/completion_test.rb @@ -6,50 +6,10 @@ module RubyLsp module Rails class CompletionTest < ActiveSupport::TestCase - # This test uses node as the call node with name where - test "recognizes Active Record .where call on an Active Record model using (" do - response = generate_completions_for_source(<<~RUBY, { line: 1, character: 11 }) - # 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: 1, 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 }) + response = generate_completions_for_source(<<~RUBY, { line: 1, character: 30 }) # typed: false - User.where(id: + User.where(id:, first_name:, f RUBY assert_equal(0, response.size)