@@ -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,43 @@ 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+ db_configs = @client . db_configs
71+ return if db_configs . nil?
72+
73+ db_config = db_configs . find do |config |
74+ config [ :migrations_paths ] . any? do |path |
75+ File . join ( @client . rails_root , path ) == File . dirname ( @path )
76+ end
77+ end
78+ return if db_config . nil?
79+
80+ range = range_from_location ( node . location )
81+
82+ @index . method_completion_candidates ( node . message , db_config [ :adapter_class ] ) . each do |entry |
83+ next unless entry . public?
84+
85+ entry_name = entry . name
86+ owner_name = entry . owner &.name
87+
88+ label_details = Interface ::CompletionItemLabelDetails . new (
89+ description : entry . file_name ,
90+ detail : entry . decorated_parameters ,
91+ )
92+ @response_builder << Interface ::CompletionItem . new (
93+ label : entry_name ,
94+ filter_text : entry_name ,
95+ label_details : label_details ,
96+ text_edit : Interface ::TextEdit . new ( range : range , new_text : entry_name ) ,
97+ kind : Constant ::CompletionItemKind ::METHOD ,
98+ data : {
99+ owner_name : owner_name ,
100+ } ,
101+ )
102+ end
103+ end
104+
65105 #: (arguments: Array[Prism::Node]) -> Hash[String, Prism::Node]
66106 def index_call_node_args ( arguments :)
67107 indexed_call_node_args = { }
@@ -79,6 +119,28 @@ def index_call_node_args(arguments:)
79119 end
80120 indexed_call_node_args
81121 end
122+
123+ # Checks that we're on instance level of a `ActiveRecord::Migration` subclass.
124+ #
125+ #: -> bool
126+ def active_record_migration?
127+ nesting_nodes = @node_context . instance_variable_get ( :@nesting_nodes ) . reverse
128+ class_node = nesting_nodes . find { |node | node . is_a? ( Prism ::ClassNode ) }
129+ return false unless class_node
130+
131+ superclass = class_node . superclass
132+ return false unless superclass . is_a? ( Prism ::CallNode )
133+
134+ receiver = superclass . receiver
135+ return false unless receiver . is_a? ( Prism ::ConstantPathNode )
136+ return false unless receiver . slice == "ActiveRecord::Migration"
137+
138+ def_node = nesting_nodes . find { |n | n . is_a? ( Prism ::DefNode ) }
139+ return false if def_node . receiver
140+
141+ true
142+ end
143+
82144 end
83145 end
84146end
0 commit comments