diff --git a/lib/ruby_lsp/ruby_lsp_rails/addon.rb b/lib/ruby_lsp/ruby_lsp_rails/addon.rb index f791478f..948b25b4 100644 --- a/lib/ruby_lsp/ruby_lsp_rails/addon.rb +++ b/lib/ruby_lsp/ruby_lsp_rails/addon.rb @@ -16,7 +16,6 @@ require_relative "definition" require_relative "rails_test_style" require_relative "completion" -require_relative "indexing_enhancement" module RubyLsp module Rails diff --git a/lib/ruby_lsp/ruby_lsp_rails/indexing_enhancement.rb b/lib/ruby_lsp/ruby_lsp_rails/indexing_enhancement.rb deleted file mode 100644 index 95f0ccf5..00000000 --- a/lib/ruby_lsp/ruby_lsp_rails/indexing_enhancement.rb +++ /dev/null @@ -1,96 +0,0 @@ -# typed: strict -# frozen_string_literal: true - -module RubyLsp - module Rails - class IndexingEnhancement < RubyIndexer::Enhancement - # @override - #: (Prism::CallNode call_node) -> void - def on_call_node_enter(call_node) - owner = @listener.current_owner - return unless owner - - case call_node.name - when :extend - handle_concern_extend(owner, call_node) - when :has_one, :has_many, :belongs_to, :has_and_belongs_to_many - handle_association(owner, call_node) - # for `class_methods do` blocks within concerns - when :class_methods - handle_class_methods(owner, call_node) - end - end - - # @override - #: (Prism::CallNode call_node) -> void - def on_call_node_leave(call_node) - if call_node.name == :class_methods && call_node.block - @listener.pop_namespace_stack - end - end - - private - - #: (RubyIndexer::Entry::Namespace owner, Prism::CallNode call_node) -> void - def handle_association(owner, call_node) - arguments = call_node.arguments&.arguments - return unless arguments - - name_arg = arguments.first - - name = case name_arg - when Prism::StringNode - name_arg.content - when Prism::SymbolNode - name_arg.value - end - - return unless name - - loc = name_arg.location - - # Reader - reader_signatures = [RubyIndexer::Entry::Signature.new([])] - @listener.add_method(name, loc, reader_signatures) - - # Writer - writer_signatures = [ - RubyIndexer::Entry::Signature.new([RubyIndexer::Entry::RequiredParameter.new(name: name.to_sym)]), - ] - @listener.add_method("#{name}=", loc, writer_signatures) - end - - #: (RubyIndexer::Entry::Namespace owner, Prism::CallNode call_node) -> void - def handle_concern_extend(owner, call_node) - arguments = call_node.arguments&.arguments - return unless arguments - - arguments.each do |node| - next unless node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode) - - module_name = node.full_name - next unless module_name == "ActiveSupport::Concern" - - @listener.register_included_hook do |index, base| - class_methods_name = "#{owner.name}::ClassMethods" - - if index.indexed?(class_methods_name) - singleton = index.existing_or_new_singleton_class(base.name) - singleton.mixin_operations << RubyIndexer::Entry::Include.new(class_methods_name) - end - end - rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError, - Prism::ConstantPathNode::MissingNodesInConstantPathError - # Do nothing - end - end - - #: (RubyIndexer::Entry::Namespace owner, Prism::CallNode call_node) -> void - def handle_class_methods(owner, call_node) - return unless call_node.block - - @listener.add_module("ClassMethods", call_node.location, call_node.location) - end - end - end -end diff --git a/test/ruby_lsp_rails/indexing_enhancement_test.rb b/test/ruby_lsp_rails/indexing_enhancement_test.rb deleted file mode 100644 index 257b27a2..00000000 --- a/test/ruby_lsp_rails/indexing_enhancement_test.rb +++ /dev/null @@ -1,126 +0,0 @@ -# typed: true -# frozen_string_literal: true - -require "test_helper" - -module RubyLsp - module Rails - class IndexingEnhancementTest < ActiveSupport::TestCase - class << self - # For these tests, it's convenient to have the index fully populated with Rails information, but we don't have - # to re-index on every single example or that will be too slow - def populated_index - @index ||= begin - index = RubyIndexer::Index.new - index.index_all - index - end - end - end - - def setup - @index = self.class.populated_index - @indexable_path = URI::Generic.from_path(path: "/fake.rb") - end - - def teardown - # Prevent state leaking between tests - @index.delete(@indexable_path) - @index.instance_variable_set(:@ancestors, {}) - end - - test "ClassMethods module inside concerns are automatically extended" do - @index.index_single(@indexable_path, <<~RUBY) - module Auditable - extend ActiveSupport::Concern - - module ClassMethods - def all_verified; end - end - end - - class Post - include Auditable - end - RUBY - - ancestors = @index.linearized_ancestors_of("Post::") - - assert_includes(ancestors, "Auditable::ClassMethods") - refute_nil(@index.resolve_method("all_verified", "Post::")) - end - - test "class_methods blocks inside concerns are automatically extended via a ClassMethods module" do - @index.index_single(@indexable_path, <<~RUBY) - module Auditable - extend ActiveSupport::Concern - - class_methods do - def all_verified; end - end - end - - class Post - include Auditable - end - RUBY - - ancestors = @index.linearized_ancestors_of("Post::") - - assert_includes(ancestors, "Auditable::ClassMethods") - refute_nil(@index.resolve_method("all_verified", "Post::")) - end - - test "ignores `class_methods` calls without a block" do - @index.index_single(@indexable_path, <<~RUBY) - module Auditable - extend ActiveSupport::Concern - - class_methods - end - - class Post - include Auditable - end - RUBY - - ancestors = @index.linearized_ancestors_of("Post::") - - refute_includes(ancestors, "Auditable::ClassMethods") - end - - test "associations" do - @index.index_single(@indexable_path, <<~RUBY) - class Post < ActiveRecord::Base - has_one :content - belongs_to :author - has_many :comments - has_and_belongs_to_many :tags - end - RUBY - - assert_declaration_on_line("content", "Post", 2) - assert_declaration_on_line("content=", "Post", 2) - - assert_declaration_on_line("author", "Post", 3) - assert_declaration_on_line("author=", "Post", 3) - - assert_declaration_on_line("comments", "Post", 4) - assert_declaration_on_line("comments=", "Post", 4) - - assert_declaration_on_line("tags", "Post", 5) - assert_declaration_on_line("tags=", "Post", 5) - end - - private - - def assert_declaration_on_line(method_name, class_name, line) - association_entries = @index.resolve_method(method_name, class_name) - refute_nil(association_entries) - - association = association_entries.first - assert_equal(line, association.location.start_line) - end - end - end -end