@@ -7,11 +7,13 @@ class Completion
77 include Requests ::Support ::Common
88
99 # @override
10- #: (RunnerClient client, ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem] response_builder, NodeContext node_context, Prism::Dispatcher dispatcher, URI::Generic uri) -> void
11- def initialize ( client , response_builder , node_context , dispatcher , uri )
10+ #: (RunnerClient client, ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem] response_builder, NodeContext node_context, RubyIndexer::Index index, Prism::Dispatcher dispatcher, URI::Generic uri) -> void
11+ def initialize ( client , response_builder , node_context , index , dispatcher , uri )
1212 @response_builder = response_builder
1313 @client = client
1414 @node_context = node_context
15+ @index = index
16+ @path = uri . to_standardized_path #: String?
1517 dispatcher . register (
1618 self ,
1719 :on_call_node_enter ,
@@ -21,11 +23,12 @@ def initialize(client, response_builder, node_context, dispatcher, uri)
2123 #: (Prism::CallNode node) -> void
2224 def on_call_node_enter ( node )
2325 call_node = @node_context . call_node
24- return unless call_node
26+ receiver = call_node &. receiver
2527
26- receiver = call_node . receiver
27- if call_node . name == :where && receiver . is_a? ( Prism ::ConstantReadNode )
28+ if call_node &.name == :where && receiver . is_a? ( Prism ::ConstantReadNode )
2829 handle_active_record_where_completions ( node : node , receiver : receiver )
30+ elsif active_record_migration?
31+ handle_active_record_migration_completions ( node : node )
2932 end
3033 end
3134
@@ -62,6 +65,45 @@ def handle_active_record_where_completions(node:, receiver:)
6265 end
6366 end
6467
68+ #: (node: Prism::CallNode) -> void
69+ def handle_active_record_migration_completions ( node :)
70+ return if @path . nil?
71+
72+ db_configs = @client . db_configs
73+ return if db_configs . nil?
74+
75+ db_config = db_configs . values . find do |config |
76+ config [ :migrations_paths ] . any? do |path |
77+ File . join ( @client . rails_root , path ) == File . dirname ( @path )
78+ end
79+ end
80+ return if db_config . nil?
81+
82+ range = range_from_location ( node . location )
83+
84+ @index . method_completion_candidates ( node . message , db_config [ :adapter_class ] ) . each do |entry |
85+ next unless entry . public?
86+
87+ entry_name = entry . name
88+ owner_name = entry . owner &.name
89+
90+ label_details = Interface ::CompletionItemLabelDetails . new (
91+ description : entry . file_name ,
92+ detail : entry . decorated_parameters ,
93+ )
94+ @response_builder << Interface ::CompletionItem . new (
95+ label : entry_name ,
96+ filter_text : entry_name ,
97+ label_details : label_details ,
98+ text_edit : Interface ::TextEdit . new ( range : range , new_text : entry_name ) ,
99+ kind : Constant ::CompletionItemKind ::METHOD ,
100+ data : {
101+ owner_name : owner_name ,
102+ } ,
103+ )
104+ end
105+ end
106+
65107 #: (arguments: Array[Prism::Node]) -> Hash[String, Prism::Node]
66108 def index_call_node_args ( arguments :)
67109 indexed_call_node_args = { }
@@ -79,6 +121,28 @@ def index_call_node_args(arguments:)
79121 end
80122 indexed_call_node_args
81123 end
124+
125+ # Checks that we're on instance level of a `ActiveRecord::Migration` subclass.
126+ #
127+ #: -> bool
128+ def active_record_migration?
129+ nesting_nodes = @node_context . instance_variable_get ( :@nesting_nodes ) . reverse
130+ class_node = nesting_nodes . find { |node | node . is_a? ( Prism ::ClassNode ) }
131+ return false unless class_node
132+
133+ superclass = class_node . superclass
134+ return false unless superclass . is_a? ( Prism ::CallNode )
135+
136+ receiver = superclass . receiver
137+ return false unless receiver . is_a? ( Prism ::ConstantPathNode )
138+ return false unless receiver . slice == "ActiveRecord::Migration"
139+
140+ def_node = nesting_nodes . find { |n | n . is_a? ( Prism ::DefNode ) }
141+ return false if def_node . receiver
142+
143+ true
144+ end
145+
82146 end
83147 end
84148end
0 commit comments