hind 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,31 +1,14 @@
1
- # lib/hind/lsif/visitor.rb
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module Hind
5
4
  module LSIF
6
- class Visitor
5
+ class Visitor < Prism::Visitor
7
6
  def initialize(generator)
8
7
  @generator = generator
9
8
  @current_scope = []
10
9
  end
11
10
 
12
- def visit(node)
13
- return unless node
14
-
15
- method_name = "visit_#{node.class.name.split("::").last.downcase}"
16
- if respond_to?(method_name)
17
- send(method_name, node)
18
- else
19
- visit_children(node)
20
- end
21
- end
22
-
23
- def visit_children(node)
24
- node.child_nodes.each { |child| visit(child) if child }
25
- end
26
-
27
- def visit_defnode(node)
28
- # Handle method definitions
11
+ def visit_def_node(node)
29
12
  method_name = node.name.to_s
30
13
  qualified_name = current_scope_name.empty? ? method_name : "#{current_scope_name}##{method_name}"
31
14
 
@@ -52,10 +35,10 @@ module Hind
52
35
 
53
36
  @generator.add_to_global_state(qualified_name, result_set_id, range_id)
54
37
 
55
- visit_children(node)
38
+ super
56
39
  end
57
40
 
58
- def visit_classnode(node)
41
+ def visit_class_node(node)
59
42
  @current_scope.push(node.constant_path.slice)
60
43
  class_name = current_scope_name
61
44
 
@@ -91,11 +74,11 @@ module Hind
91
74
  # Handle inheritance
92
75
  visit_inheritance(node.superclass) if node.superclass
93
76
 
94
- visit_children(node)
77
+ super
95
78
  @current_scope.pop
96
79
  end
97
80
 
98
- def visit_modulenode(node)
81
+ def visit_module_node(node)
99
82
  @current_scope.push(node.constant_path.slice)
100
83
  module_name = current_scope_name
101
84
 
@@ -117,11 +100,11 @@ module Hind
117
100
 
118
101
  @generator.add_to_global_state(module_name, result_set_id, range_id)
119
102
 
120
- visit_children(node)
103
+ super
121
104
  @current_scope.pop
122
105
  end
123
106
 
124
- def visit_callnode(node)
107
+ def visit_call_node(node)
125
108
  return unless node.name && node.location
126
109
 
127
110
  method_name = node.name.to_s
@@ -132,13 +115,13 @@ module Hind
132
115
 
133
116
  # Try with receiver's type if available
134
117
  if node.receiver
135
- case node.receiver
136
- when Prism::ConstantReadNode
118
+ case node.receiver.type
119
+ when :constant_read
137
120
  qualified_names << "#{node.receiver.name}##{method_name}"
138
- when Prism::CallNode
121
+ when :call
139
122
  # Handle method chaining
140
123
  qualified_names << "#{node.receiver.name}##{method_name}" if node.receiver.name
141
- when Prism::InstanceVariableReadNode
124
+ when :instance_variable_read
142
125
  # Handle instance variable calls
143
126
  qualified_names << "#{current_scope_name}##{method_name}" if current_scope_name
144
127
  end
@@ -157,10 +140,10 @@ module Hind
157
140
  break # Stop after finding first match
158
141
  end
159
142
 
160
- visit_children(node)
143
+ super
161
144
  end
162
145
 
163
- def visit_constantreadnode(node)
146
+ def visit_constant_read_node(node)
164
147
  return unless node.name
165
148
 
166
149
  constant_name = node.name.to_s
@@ -171,9 +154,11 @@ module Hind
171
154
  range_id = @generator.create_range(node.location, node.location)
172
155
  @generator.global_state.add_reference(qualified_name, @generator.metadata[:uri], range_id)
173
156
  @generator.emit_edge('next', range_id, @generator.global_state.result_sets[qualified_name])
157
+
158
+ super
174
159
  end
175
160
 
176
- def visit_constantwritenode(node)
161
+ def visit_constant_write_node(node)
177
162
  return unless node.name
178
163
 
179
164
  constant_name = node.name.to_s
@@ -197,10 +182,10 @@ module Hind
197
182
 
198
183
  @generator.add_to_global_state(qualified_name, result_set_id, range_id)
199
184
 
200
- visit_children(node)
185
+ super
201
186
  end
202
187
 
203
- def visit_instancevariablereadnode(node)
188
+ def visit_instance_variable_read_node(node)
204
189
  return unless node.name && current_scope_name
205
190
 
206
191
  var_name = node.name.to_s
@@ -211,9 +196,11 @@ module Hind
211
196
  range_id = @generator.create_range(node.location, node.location)
212
197
  @generator.global_state.add_reference(qualified_name, @generator.metadata[:uri], range_id)
213
198
  @generator.emit_edge('next', range_id, @generator.global_state.result_sets[qualified_name])
199
+
200
+ super
214
201
  end
215
202
 
216
- def visit_instancevariablewritenode(node)
203
+ def visit_instance_variable_write_node(node)
217
204
  return unless node.name && current_scope_name
218
205
 
219
206
  var_name = node.name.to_s
@@ -237,7 +224,7 @@ module Hind
237
224
 
238
225
  @generator.add_to_global_state(qualified_name, result_set_id, range_id)
239
226
 
240
- visit_children(node)
227
+ super
241
228
  end
242
229
 
243
230
  private
@@ -247,13 +234,13 @@ module Hind
247
234
  end
248
235
 
249
236
  def visit_inheritance(node)
250
- case node
251
- when Prism::ConstantReadNode, Prism::ConstantPathNode
237
+ case node.type
238
+ when :constant_read_node, :constant_path_node
252
239
  range_id = @generator.create_range(node.location, node.location)
253
- qualified_name = case node
254
- when Prism::ConstantReadNode
240
+ qualified_name = case node.type
241
+ when :constant_read_node
255
242
  node.name.to_s
256
- when Prism::ConstantPathNode
243
+ when :constant_path_node
257
244
  node.slice
258
245
  end
259
246
 
@@ -0,0 +1,239 @@
1
+ # lib/hind/lsif/visitors/declaration_visitor.rb
2
+ # frozen_string_literal: true
3
+
4
+ module Hind
5
+ module LSIF
6
+ class DeclarationVisitor < Prism::Visitor
7
+ attr_reader :current_scope
8
+
9
+ def initialize(generator, file_path)
10
+ @generator = generator
11
+ @file_path = file_path
12
+ @current_scope = []
13
+ @current_visibility = :public
14
+ @visibility_stack = []
15
+ @in_singleton_class = false
16
+ end
17
+
18
+ def visit_class_node(node)
19
+ @current_scope.push(node.constant_path.slice)
20
+ class_name = current_scope_name
21
+
22
+ # Register class declaration
23
+ @generator.register_declaration({
24
+ type: :class,
25
+ name: class_name,
26
+ node: node,
27
+ scope: @current_scope[0..-2].join('::'),
28
+ superclass: node.superclass&.slice
29
+ })
30
+
31
+ # Process the class body with proper scope and visibility
32
+ push_visibility(:public)
33
+ super
34
+ pop_visibility
35
+ @current_scope.pop
36
+ end
37
+
38
+ def visit_module_node(node)
39
+ @current_scope.push(node.constant_path.slice)
40
+ module_name = current_scope_name
41
+
42
+ @generator.register_declaration({
43
+ type: :module,
44
+ name: module_name,
45
+ node: node,
46
+ scope: @current_scope[0..-2].join('::')
47
+ })
48
+
49
+ push_visibility(:public)
50
+ super
51
+ pop_visibility
52
+ @current_scope.pop
53
+ end
54
+
55
+ def visit_def_node(node)
56
+ method_name = node.name.to_s
57
+ # Use '#' for instance methods
58
+ qualified_name = @in_singleton_class ?
59
+ "#{current_scope_name}.#{method_name}" :
60
+ "#{current_scope_name}##{method_name}"
61
+
62
+ @generator.register_declaration({
63
+ type: :method,
64
+ name: qualified_name,
65
+ node: node,
66
+ scope: current_scope_name,
67
+ visibility: current_visibility,
68
+ params: node.parameters&.slice,
69
+ instance_method: !@in_singleton_class
70
+ })
71
+
72
+ super
73
+ end
74
+
75
+ def visit_constant_write_node(node)
76
+ return unless node.name
77
+
78
+ constant_name = node.name.to_s
79
+ qualified_name = @current_scope.empty? ? constant_name : "#{current_scope_name}::#{constant_name}"
80
+
81
+ @generator.register_declaration({
82
+ type: :constant,
83
+ name: qualified_name,
84
+ node: node,
85
+ scope: current_scope_name
86
+ })
87
+
88
+ super
89
+ end
90
+
91
+ def visit_singleton_class_node(node)
92
+ if node.expression.is_a?(Prism::SelfNode)
93
+ # class << self
94
+ @in_singleton_class = true
95
+ super
96
+ @in_singleton_class = false
97
+ else
98
+ # Process regular singleton class
99
+ super
100
+ end
101
+ end
102
+
103
+ def visit_call_node(node)
104
+ method_name = node.name.to_s
105
+ case method_name
106
+ when 'private', 'protected', 'public'
107
+ handle_visibility_method(node)
108
+ when 'attr_reader', 'attr_writer', 'attr_accessor'
109
+ handle_attribute_method(node)
110
+ end
111
+
112
+ super
113
+ end
114
+
115
+ def visit_class_variable_write_node(node)
116
+ return unless node.name
117
+
118
+ var_name = node.name.to_s
119
+ qualified_name = "#{current_scope_name}::#{var_name}"
120
+
121
+ @generator.register_declaration({
122
+ type: :class_variable,
123
+ name: qualified_name,
124
+ node: node,
125
+ scope: current_scope_name
126
+ })
127
+
128
+ super
129
+ end
130
+
131
+ def visit_instance_variable_write_node(node)
132
+ return unless node.name && current_scope_name
133
+
134
+ var_name = node.name.to_s
135
+ qualified_name = "#{current_scope_name}##{var_name}"
136
+
137
+ @generator.register_declaration({
138
+ type: :instance_variable,
139
+ name: qualified_name,
140
+ node: node,
141
+ scope: current_scope_name
142
+ })
143
+
144
+ super
145
+ end
146
+
147
+ private
148
+
149
+ def current_scope_name
150
+ @current_scope.join('::')
151
+ end
152
+
153
+ def current_visibility
154
+ @current_visibility
155
+ end
156
+
157
+ def push_visibility(visibility)
158
+ @visibility_stack.push(@current_visibility)
159
+ @current_visibility = visibility
160
+ end
161
+
162
+ def pop_visibility
163
+ @current_visibility = @visibility_stack.pop || :public
164
+ end
165
+
166
+ def handle_visibility_method(node)
167
+ visibility = node.name.to_sym
168
+
169
+ if node.arguments&.arguments&.empty?
170
+ # Global visibility change
171
+ @current_visibility = visibility
172
+ elsif node.arguments
173
+ # Per-method visibility change
174
+ node.arguments.arguments.each do |arg|
175
+ next unless arg.is_a?(Prism::SymbolNode) || arg.is_a?(Prism::StringNode)
176
+
177
+ method_name = arg.value.to_s
178
+ qualified_name = "#{current_scope_name}##{method_name}"
179
+
180
+ if @generator.global_state.has_declaration?(qualified_name)
181
+ @generator.global_state.get_declaration(qualified_name)[:visibility] = visibility
182
+ end
183
+ end
184
+ end
185
+ end
186
+
187
+ def handle_attribute_method(node)
188
+ return unless node.arguments
189
+
190
+ attr_type = node.name.to_s
191
+ node.arguments.arguments.each do |arg|
192
+ next unless arg.respond_to?(:value)
193
+
194
+ attr_name = arg.value.to_s
195
+ register_attribute(attr_name, attr_type)
196
+ end
197
+ end
198
+
199
+ def register_attribute(attr_name, attr_type)
200
+ base_name = "#{current_scope_name}##{attr_name}"
201
+
202
+ # Register reader method if applicable
203
+ if %w[attr_reader attr_accessor].include?(attr_type)
204
+ @generator.register_declaration({
205
+ type: :method,
206
+ name: base_name,
207
+ node: nil, # Synthetic node
208
+ scope: current_scope_name,
209
+ visibility: current_visibility,
210
+ synthetic: true,
211
+ kind: :reader
212
+ })
213
+ end
214
+
215
+ # Register writer method if applicable
216
+ if %w[attr_writer attr_accessor].include?(attr_type)
217
+ @generator.register_declaration({
218
+ type: :method,
219
+ name: "#{base_name}=",
220
+ node: nil, # Synthetic node
221
+ scope: current_scope_name,
222
+ visibility: current_visibility,
223
+ synthetic: true,
224
+ kind: :writer
225
+ })
226
+ end
227
+
228
+ # Register the instance variable
229
+ @generator.register_declaration({
230
+ type: :instance_variable,
231
+ name: "@#{attr_name}",
232
+ node: nil, # Synthetic node
233
+ scope: current_scope_name,
234
+ synthetic: true
235
+ })
236
+ end
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,221 @@
1
+ # lib/hind/lsif/visitors/reference_visitor.rb
2
+ # frozen_string_literal: true
3
+
4
+ module Hind
5
+ module LSIF
6
+ class ReferenceVisitor < Prism::Visitor
7
+ attr_reader :current_scope
8
+
9
+ def initialize(generator, file_path)
10
+ @generator = generator
11
+ @file_path = file_path
12
+ @current_scope = []
13
+ end
14
+
15
+ # Method calls
16
+ def visit_call_node(node)
17
+ return unless node.name && node.location
18
+
19
+ method_name = node.name.to_s
20
+ qualified_names = generate_qualified_names_for_call(node)
21
+
22
+ qualified_names.each do |qualified_name|
23
+ @generator.register_reference({
24
+ type: :method,
25
+ name: qualified_name,
26
+ node: node,
27
+ scope: current_scope_name,
28
+ call_type: :instance_method
29
+ })
30
+ end
31
+
32
+ super
33
+ end
34
+
35
+ # Class/module references
36
+ def visit_constant_read_node(node)
37
+ return unless node.name
38
+
39
+ constant_name = node.name.to_s
40
+ qualified_name = @current_scope.empty? ? constant_name : "#{current_scope_name}::#{constant_name}"
41
+
42
+ @generator.register_reference({
43
+ type: :constant,
44
+ name: qualified_name,
45
+ node: node,
46
+ scope: current_scope_name
47
+ })
48
+
49
+ super
50
+ end
51
+
52
+ # Constant path references (e.g., A::B::C)
53
+ def visit_constant_path_node(node)
54
+ qualified_name = node.slice
55
+
56
+ @generator.register_reference({
57
+ type: :constant,
58
+ name: qualified_name,
59
+ node: node,
60
+ scope: current_scope_name
61
+ })
62
+
63
+ super
64
+ end
65
+
66
+ # Instance variable references
67
+ def visit_instance_variable_read_node(node)
68
+ return unless node.name && current_scope_name
69
+
70
+ var_name = node.name.to_s
71
+ qualified_name = "#{current_scope_name}##{var_name}"
72
+
73
+ @generator.register_reference({
74
+ type: :instance_variable,
75
+ name: qualified_name,
76
+ node: node,
77
+ scope: current_scope_name
78
+ })
79
+
80
+ super
81
+ end
82
+
83
+ # Class variable references
84
+ def visit_class_variable_read_node(node)
85
+ return unless node.name && current_scope_name
86
+
87
+ var_name = node.name.to_s
88
+ qualified_name = "#{current_scope_name}::#{var_name}"
89
+
90
+ @generator.register_reference({
91
+ type: :class_variable,
92
+ name: qualified_name,
93
+ node: node,
94
+ scope: current_scope_name
95
+ })
96
+
97
+ super
98
+ end
99
+
100
+ # Singleton method calls (class methods)
101
+ def visit_constant_path_call_node(node)
102
+ return unless node.name
103
+
104
+ method_name = node.name.to_s
105
+ receiver_name = node.receiver.slice
106
+ qualified_name = "#{receiver_name}.#{method_name}"
107
+
108
+ @generator.register_reference({
109
+ type: :method,
110
+ name: qualified_name,
111
+ node: node,
112
+ scope: current_scope_name,
113
+ call_type: :class_method
114
+ })
115
+
116
+ super
117
+ end
118
+
119
+ # Super method calls
120
+ def visit_super_node(node)
121
+ return unless current_scope_name
122
+
123
+ # Extract current method name from scope
124
+ current_method = current_method_name
125
+ return unless current_method
126
+
127
+ # Try to find the superclass method
128
+ if in_class_scope?
129
+ superclass = find_superclass
130
+ if superclass
131
+ qualified_name = "#{superclass}##{current_method}"
132
+
133
+ @generator.register_reference({
134
+ type: :method,
135
+ name: qualified_name,
136
+ node: node,
137
+ scope: current_scope_name,
138
+ call_type: :super
139
+ })
140
+ end
141
+ end
142
+
143
+ super
144
+ end
145
+
146
+ # Track class/module scope
147
+ def visit_class_node(node)
148
+ @current_scope.push(node.constant_path.slice)
149
+ super
150
+ @current_scope.pop
151
+ end
152
+
153
+ def visit_module_node(node)
154
+ @current_scope.push(node.constant_path.slice)
155
+ super
156
+ @current_scope.pop
157
+ end
158
+
159
+ private
160
+
161
+ def current_scope_name
162
+ @current_scope.join('::')
163
+ end
164
+
165
+ def generate_qualified_names_for_call(node)
166
+ qualified_names = []
167
+ method_name = node.name.to_s
168
+
169
+ # Try with current scope first
170
+ qualified_names << "#{current_scope_name}##{method_name}" unless current_scope_name.empty?
171
+
172
+ # Try with receiver's type if available
173
+ if node.receiver
174
+ case node.receiver
175
+ when Prism::ConstantReadNode
176
+ qualified_names << "#{node.receiver.name}##{method_name}"
177
+ when Prism::ConstantPathNode
178
+ qualified_names << "#{node.receiver.slice}##{method_name}"
179
+ when Prism::CallNode
180
+ # Method chaining - try both instance and class methods
181
+ if node.receiver.name
182
+ qualified_names << "#{node.receiver.name}##{method_name}"
183
+ qualified_names << "#{node.receiver.name}.#{method_name}"
184
+ end
185
+ when Prism::InstanceVariableReadNode
186
+ # Instance variable calls - try current class context
187
+ qualified_names << "#{current_scope_name}##{method_name}" if current_scope_name
188
+ end
189
+ end
190
+
191
+ # Try as a standalone method
192
+ qualified_names << method_name
193
+
194
+ # Add potential class method variant
195
+ qualified_names << "#{current_scope_name}.#{method_name}" unless current_scope_name.empty?
196
+
197
+ qualified_names.uniq
198
+ end
199
+
200
+ def current_method_name
201
+ # Try to find the nearest method node in the AST
202
+ # This is a simplified version - you might need to enhance this
203
+ # based on your specific needs
204
+ "current_method"
205
+ end
206
+
207
+ def in_class_scope?
208
+ # Check if we're currently in a class definition
209
+ !@current_scope.empty? && @generator.global_state.declarations[@current_scope.last]&.[](:type) == :class
210
+ end
211
+
212
+ def find_superclass
213
+ return unless in_class_scope?
214
+
215
+ current_class = @current_scope.last
216
+ class_declaration = @generator.global_state.declarations[current_class]
217
+ class_declaration&.[](:superclass)
218
+ end
219
+ end
220
+ end
221
+ end
data/lib/hind/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hind
4
- VERSION = '0.1.5'
4
+ VERSION = '0.1.6'
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hind
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aboobacker MK
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-02-09 00:00:00.000000000 Z
10
+ date: 2025-02-10 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: prism
@@ -112,6 +112,8 @@ files:
112
112
  - lib/hind/lsif/global_state.rb
113
113
  - lib/hind/lsif/vertex.rb
114
114
  - lib/hind/lsif/visitor.rb
115
+ - lib/hind/lsif/visitors/declaration_visitor.rb
116
+ - lib/hind/lsif/visitors/reference_visitor.rb
115
117
  - lib/hind/parser.rb
116
118
  - lib/hind/scip.rb
117
119
  - lib/hind/scip/generator.rb