hind 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/exe/hind +4 -2
- data/lib/hind/cli.rb +11 -15
- data/lib/hind/lsif/edge.rb +3 -1
- data/lib/hind/lsif/generator.rb +7 -6
- data/lib/hind/lsif/global_state.rb +4 -2
- data/lib/hind/lsif/vertex.rb +3 -1
- data/lib/hind/lsif/visitor.rb +208 -3
- data/lib/hind/lsif.rb +6 -5
- data/lib/hind/parser.rb +3 -0
- data/lib/hind/scip/generator.rb +2 -0
- data/lib/hind/scip/visitor.rb +2 -0
- data/lib/hind/scip.rb +2 -0
- data/lib/hind/version.rb +1 -1
- data/lib/hind.rb +4 -4
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5fa041a7ffe7e916e1f3715500d21a9dea2a3a6ce60990be8cf4e7578f704702
|
4
|
+
data.tar.gz: '0779f87c520ddc1edddf0448c96d596194982d5b87fa5c380bb3a1132500bd9e'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 397847759b6c959511602d276eee22f7e89959c5858d840cf8398b874a39ef6ccfeec8d876e9c0539d5cc0bc0cce77cb1e297b7fabc30c9a4562bfb7e85ddb7b
|
7
|
+
data.tar.gz: 5c823067473c8b784a4868bf696b4374735b548eb8eb9341fb887dc78028e9223aeaac7172c621c624d0e8a3de221b50e101e908177626f9c316797e8ce2de70
|
data/exe/hind
CHANGED
data/lib/hind/cli.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'thor'
|
2
4
|
require 'json'
|
3
5
|
require 'pathname'
|
@@ -26,7 +28,7 @@ module Hind
|
|
26
28
|
begin
|
27
29
|
generate_lsif(files, options)
|
28
30
|
say "\nLSIF data has been written to: #{options[:output]}", :green if options[:verbose]
|
29
|
-
rescue
|
31
|
+
rescue => e
|
30
32
|
abort "Error generating LSIF: #{e.message}"
|
31
33
|
end
|
32
34
|
end
|
@@ -49,7 +51,7 @@ module Hind
|
|
49
51
|
begin
|
50
52
|
generate_scip(files, options)
|
51
53
|
say "\nSCIP data has been written to: #{options[:output]}", :green if options[:verbose]
|
52
|
-
rescue
|
54
|
+
rescue => e
|
53
55
|
abort "Error generating SCIP: #{e.message}"
|
54
56
|
end
|
55
57
|
end
|
@@ -66,9 +68,7 @@ module Hind
|
|
66
68
|
end
|
67
69
|
|
68
70
|
def validate_output_file(output, force)
|
69
|
-
if File.exist?(output) && !force
|
70
|
-
abort "Error: Output file '#{output}' already exists. Use --force to overwrite."
|
71
|
-
end
|
71
|
+
abort "Error: Output file '#{output}' already exists. Use --force to overwrite." if File.exist?(output) && !force
|
72
72
|
|
73
73
|
# Ensure output directory exists
|
74
74
|
FileUtils.mkdir_p(File.dirname(output))
|
@@ -78,10 +78,8 @@ module Hind
|
|
78
78
|
pattern = File.join(directory, glob)
|
79
79
|
files = Dir.glob(pattern)
|
80
80
|
|
81
|
-
|
82
|
-
|
83
|
-
files.reject! { |f| File.fnmatch?(exclude, f) }
|
84
|
-
end
|
81
|
+
exclude_patterns&.each do |exclude|
|
82
|
+
files.reject! { |f| File.fnmatch?(exclude, f) }
|
85
83
|
end
|
86
84
|
|
87
85
|
files
|
@@ -94,9 +92,7 @@ module Hind
|
|
94
92
|
|
95
93
|
File.open(options[:output], 'w') do |output_file|
|
96
94
|
files.each do |file|
|
97
|
-
if options[:verbose]
|
98
|
-
say "Processing file: #{file}", :cyan
|
99
|
-
end
|
95
|
+
say "Processing file: #{file}", :cyan if options[:verbose]
|
100
96
|
|
101
97
|
relative_path = Pathname.new(file).relative_path_from(Pathname.new(options[:directory])).to_s
|
102
98
|
|
@@ -115,7 +111,7 @@ module Hind
|
|
115
111
|
vertex_id = output.last[:id].to_i + 1
|
116
112
|
output_file.puts(output.map(&:to_json).join("\n"))
|
117
113
|
initial = false
|
118
|
-
rescue
|
114
|
+
rescue => e
|
119
115
|
warn "Warning: Failed to process file '#{file}': #{e.message}"
|
120
116
|
next
|
121
117
|
end
|
@@ -124,7 +120,7 @@ module Hind
|
|
124
120
|
end
|
125
121
|
|
126
122
|
def generate_scip(files, options)
|
127
|
-
raise NotImplementedError,
|
123
|
+
raise NotImplementedError, 'SCIP generation not yet implemented'
|
128
124
|
# Similar to generate_lsif but using SCIP generator
|
129
125
|
end
|
130
126
|
|
@@ -133,7 +129,7 @@ module Hind
|
|
133
129
|
|
134
130
|
begin
|
135
131
|
YAML.load_file(config_path) || {}
|
136
|
-
rescue
|
132
|
+
rescue => e
|
137
133
|
abort "Error loading config file: #{e.message}"
|
138
134
|
end
|
139
135
|
end
|
data/lib/hind/lsif/edge.rb
CHANGED
data/lib/hind/lsif/generator.rb
CHANGED
@@ -91,7 +91,7 @@ module Hind
|
|
91
91
|
}
|
92
92
|
})
|
93
93
|
|
94
|
-
@global_state.project_id = emit_vertex('project', {
|
94
|
+
@global_state.project_id = emit_vertex('project', {kind: 'ruby'})
|
95
95
|
end
|
96
96
|
|
97
97
|
def setup_document
|
@@ -110,9 +110,9 @@ module Hind
|
|
110
110
|
file_path = File.join(@metadata[:projectRoot], @metadata[:uri])
|
111
111
|
ranges = @global_state.ranges[file_path]
|
112
112
|
|
113
|
-
|
114
|
-
|
115
|
-
|
113
|
+
return unless ranges&.any?
|
114
|
+
|
115
|
+
emit_edge('contains', @document_id, ranges)
|
116
116
|
end
|
117
117
|
|
118
118
|
def update_cross_file_references
|
@@ -150,15 +150,16 @@ module Hind
|
|
150
150
|
def valid_in_v?(in_v)
|
151
151
|
return false unless in_v
|
152
152
|
return in_v.any? if in_v.is_a?(Array)
|
153
|
+
|
153
154
|
true
|
154
155
|
end
|
155
156
|
|
156
157
|
def edge_document(label)
|
157
|
-
label == 'item' ? @document_id : nil
|
158
|
+
(label == 'item') ? @document_id : nil
|
158
159
|
end
|
159
160
|
|
160
161
|
def path_to_uri(path)
|
161
|
-
normalized_path = path.
|
162
|
+
normalized_path = path.tr('\\', '/')
|
162
163
|
normalized_path = normalized_path.sub(%r{^file://}, '')
|
163
164
|
absolute_path = File.expand_path(normalized_path)
|
164
165
|
"file://#{absolute_path}"
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Hind
|
2
4
|
module LSIF
|
3
5
|
class GlobalState
|
@@ -18,12 +20,12 @@ module Hind
|
|
18
20
|
end
|
19
21
|
|
20
22
|
def add_definition(qualified_name, file_path, range_id)
|
21
|
-
@definitions[qualified_name] = {
|
23
|
+
@definitions[qualified_name] = {file: file_path, range_id: range_id}
|
22
24
|
end
|
23
25
|
|
24
26
|
def add_reference(qualified_name, file_path, range_id)
|
25
27
|
@references[qualified_name] ||= []
|
26
|
-
@references[qualified_name] << {
|
28
|
+
@references[qualified_name] << {file: file_path, range_id: range_id}
|
27
29
|
end
|
28
30
|
end
|
29
31
|
end
|
data/lib/hind/lsif/vertex.rb
CHANGED
data/lib/hind/lsif/visitor.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# lib/hind/lsif/visitor.rb
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
1
4
|
module Hind
|
2
5
|
module LSIF
|
3
6
|
class Visitor
|
@@ -9,7 +12,7 @@ module Hind
|
|
9
12
|
def visit(node)
|
10
13
|
return unless node
|
11
14
|
|
12
|
-
method_name = "visit_#{node.class.name.split(
|
15
|
+
method_name = "visit_#{node.class.name.split("::").last.downcase}"
|
13
16
|
if respond_to?(method_name)
|
14
17
|
send(method_name, node)
|
15
18
|
else
|
@@ -22,6 +25,7 @@ module Hind
|
|
22
25
|
end
|
23
26
|
|
24
27
|
def visit_defnode(node)
|
28
|
+
# Handle method definitions
|
25
29
|
method_name = node.name.to_s
|
26
30
|
qualified_name = current_scope_name.empty? ? method_name : "#{current_scope_name}##{method_name}"
|
27
31
|
|
@@ -33,7 +37,7 @@ module Hind
|
|
33
37
|
@generator.emit_edge('textDocument/definition', result_set_id, def_result_id)
|
34
38
|
@generator.emit_edge('item', def_result_id, [range_id], 'definitions')
|
35
39
|
|
36
|
-
#
|
40
|
+
# Generate method signature for hover
|
37
41
|
sig = []
|
38
42
|
sig << "def #{qualified_name}"
|
39
43
|
sig << "(#{node.parameters.slice})" if node.parameters
|
@@ -51,13 +55,214 @@ module Hind
|
|
51
55
|
visit_children(node)
|
52
56
|
end
|
53
57
|
|
54
|
-
|
58
|
+
def visit_classnode(node)
|
59
|
+
@current_scope.push(node.constant_path.slice)
|
60
|
+
class_name = current_scope_name
|
61
|
+
|
62
|
+
range_id = @generator.create_range(node.location, node.location)
|
63
|
+
result_set_id = @generator.emit_vertex('resultSet')
|
64
|
+
@generator.emit_edge('next', range_id, result_set_id)
|
65
|
+
|
66
|
+
def_result_id = @generator.emit_vertex('definitionResult')
|
67
|
+
@generator.emit_edge('textDocument/definition', result_set_id, def_result_id)
|
68
|
+
@generator.emit_edge('item', def_result_id, [range_id], 'definitions')
|
69
|
+
|
70
|
+
# Generate hover with inheritance info
|
71
|
+
hover = []
|
72
|
+
class_def = "class #{class_name}"
|
73
|
+
class_def += " < #{node.superclass.slice}" if node.superclass
|
74
|
+
|
75
|
+
hover << if node.superclass
|
76
|
+
"#{class_def}\n\nInherits from: #{node.superclass.slice}"
|
77
|
+
else
|
78
|
+
class_def
|
79
|
+
end
|
80
|
+
|
81
|
+
hover_id = @generator.emit_vertex('hoverResult', {
|
82
|
+
contents: [{
|
83
|
+
language: 'ruby',
|
84
|
+
value: hover.join("\n")
|
85
|
+
}]
|
86
|
+
})
|
87
|
+
@generator.emit_edge('textDocument/hover', result_set_id, hover_id)
|
88
|
+
|
89
|
+
@generator.add_to_global_state(class_name, result_set_id, range_id)
|
90
|
+
|
91
|
+
# Handle inheritance
|
92
|
+
visit_inheritance(node.superclass) if node.superclass
|
93
|
+
|
94
|
+
visit_children(node)
|
95
|
+
@current_scope.pop
|
96
|
+
end
|
97
|
+
|
98
|
+
def visit_modulenode(node)
|
99
|
+
@current_scope.push(node.constant_path.slice)
|
100
|
+
module_name = current_scope_name
|
101
|
+
|
102
|
+
range_id = @generator.create_range(node.location, node.location)
|
103
|
+
result_set_id = @generator.emit_vertex('resultSet')
|
104
|
+
@generator.emit_edge('next', range_id, result_set_id)
|
105
|
+
|
106
|
+
def_result_id = @generator.emit_vertex('definitionResult')
|
107
|
+
@generator.emit_edge('textDocument/definition', result_set_id, def_result_id)
|
108
|
+
@generator.emit_edge('item', def_result_id, [range_id], 'definitions')
|
109
|
+
|
110
|
+
hover_id = @generator.emit_vertex('hoverResult', {
|
111
|
+
contents: [{
|
112
|
+
language: 'ruby',
|
113
|
+
value: "module #{module_name}"
|
114
|
+
}]
|
115
|
+
})
|
116
|
+
@generator.emit_edge('textDocument/hover', result_set_id, hover_id)
|
117
|
+
|
118
|
+
@generator.add_to_global_state(module_name, result_set_id, range_id)
|
119
|
+
|
120
|
+
visit_children(node)
|
121
|
+
@current_scope.pop
|
122
|
+
end
|
123
|
+
|
124
|
+
def visit_callnode(node)
|
125
|
+
return unless node.name && node.location
|
126
|
+
|
127
|
+
method_name = node.name.to_s
|
128
|
+
qualified_names = []
|
129
|
+
|
130
|
+
# Try with current scope first
|
131
|
+
qualified_names << "#{current_scope_name}##{method_name}" unless current_scope_name.empty?
|
132
|
+
|
133
|
+
# Try with receiver's type if available
|
134
|
+
if node.receiver
|
135
|
+
case node.receiver
|
136
|
+
when Prism::ConstantReadNode
|
137
|
+
qualified_names << "#{node.receiver.name}##{method_name}"
|
138
|
+
when Prism::CallNode
|
139
|
+
# Handle method chaining
|
140
|
+
qualified_names << "#{node.receiver.name}##{method_name}" if node.receiver.name
|
141
|
+
when Prism::InstanceVariableReadNode
|
142
|
+
# Handle instance variable calls
|
143
|
+
qualified_names << "#{current_scope_name}##{method_name}" if current_scope_name
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Try as a standalone method
|
148
|
+
qualified_names << method_name
|
149
|
+
|
150
|
+
# Add references for matching qualified names
|
151
|
+
qualified_names.each do |qualified_name|
|
152
|
+
next unless @generator.global_state.result_sets[qualified_name]
|
153
|
+
|
154
|
+
range_id = @generator.create_range(node.location, node.location)
|
155
|
+
@generator.global_state.add_reference(qualified_name, @generator.metadata[:uri], range_id)
|
156
|
+
@generator.emit_edge('next', range_id, @generator.global_state.result_sets[qualified_name])
|
157
|
+
break # Stop after finding first match
|
158
|
+
end
|
159
|
+
|
160
|
+
visit_children(node)
|
161
|
+
end
|
162
|
+
|
163
|
+
def visit_constantreadnode(node)
|
164
|
+
return unless node.name
|
165
|
+
|
166
|
+
constant_name = node.name.to_s
|
167
|
+
qualified_name = @current_scope.empty? ? constant_name : "#{current_scope_name}::#{constant_name}"
|
168
|
+
|
169
|
+
return unless @generator.global_state.result_sets[qualified_name]
|
170
|
+
|
171
|
+
range_id = @generator.create_range(node.location, node.location)
|
172
|
+
@generator.global_state.add_reference(qualified_name, @generator.metadata[:uri], range_id)
|
173
|
+
@generator.emit_edge('next', range_id, @generator.global_state.result_sets[qualified_name])
|
174
|
+
end
|
175
|
+
|
176
|
+
def visit_constantwritenode(node)
|
177
|
+
return unless node.name
|
178
|
+
|
179
|
+
constant_name = node.name.to_s
|
180
|
+
qualified_name = @current_scope.empty? ? constant_name : "#{current_scope_name}::#{constant_name}"
|
181
|
+
|
182
|
+
range_id = @generator.create_range(node.location, node.location)
|
183
|
+
result_set_id = @generator.emit_vertex('resultSet')
|
184
|
+
@generator.emit_edge('next', range_id, result_set_id)
|
185
|
+
|
186
|
+
def_result_id = @generator.emit_vertex('definitionResult')
|
187
|
+
@generator.emit_edge('textDocument/definition', result_set_id, def_result_id)
|
188
|
+
@generator.emit_edge('item', def_result_id, [range_id], 'definitions')
|
189
|
+
|
190
|
+
hover_id = @generator.emit_vertex('hoverResult', {
|
191
|
+
contents: [{
|
192
|
+
language: 'ruby',
|
193
|
+
value: "#{qualified_name} = ..."
|
194
|
+
}]
|
195
|
+
})
|
196
|
+
@generator.emit_edge('textDocument/hover', result_set_id, hover_id)
|
197
|
+
|
198
|
+
@generator.add_to_global_state(qualified_name, result_set_id, range_id)
|
199
|
+
|
200
|
+
visit_children(node)
|
201
|
+
end
|
202
|
+
|
203
|
+
def visit_instancevariablereadnode(node)
|
204
|
+
return unless node.name && current_scope_name
|
205
|
+
|
206
|
+
var_name = node.name.to_s
|
207
|
+
qualified_name = "#{current_scope_name}##{var_name}"
|
208
|
+
|
209
|
+
return unless @generator.global_state.result_sets[qualified_name]
|
210
|
+
|
211
|
+
range_id = @generator.create_range(node.location, node.location)
|
212
|
+
@generator.global_state.add_reference(qualified_name, @generator.metadata[:uri], range_id)
|
213
|
+
@generator.emit_edge('next', range_id, @generator.global_state.result_sets[qualified_name])
|
214
|
+
end
|
215
|
+
|
216
|
+
def visit_instancevariablewritenode(node)
|
217
|
+
return unless node.name && current_scope_name
|
218
|
+
|
219
|
+
var_name = node.name.to_s
|
220
|
+
qualified_name = "#{current_scope_name}##{var_name}"
|
221
|
+
|
222
|
+
range_id = @generator.create_range(node.location, node.location)
|
223
|
+
result_set_id = @generator.emit_vertex('resultSet')
|
224
|
+
@generator.emit_edge('next', range_id, result_set_id)
|
225
|
+
|
226
|
+
def_result_id = @generator.emit_vertex('definitionResult')
|
227
|
+
@generator.emit_edge('textDocument/definition', result_set_id, def_result_id)
|
228
|
+
@generator.emit_edge('item', def_result_id, [range_id], 'definitions')
|
229
|
+
|
230
|
+
hover_id = @generator.emit_vertex('hoverResult', {
|
231
|
+
contents: [{
|
232
|
+
language: 'ruby',
|
233
|
+
value: "Instance variable #{var_name} in #{current_scope_name}"
|
234
|
+
}]
|
235
|
+
})
|
236
|
+
@generator.emit_edge('textDocument/hover', result_set_id, hover_id)
|
237
|
+
|
238
|
+
@generator.add_to_global_state(qualified_name, result_set_id, range_id)
|
239
|
+
|
240
|
+
visit_children(node)
|
241
|
+
end
|
55
242
|
|
56
243
|
private
|
57
244
|
|
58
245
|
def current_scope_name
|
59
246
|
@current_scope.join('::')
|
60
247
|
end
|
248
|
+
|
249
|
+
def visit_inheritance(node)
|
250
|
+
case node
|
251
|
+
when Prism::ConstantReadNode, Prism::ConstantPathNode
|
252
|
+
range_id = @generator.create_range(node.location, node.location)
|
253
|
+
qualified_name = case node
|
254
|
+
when Prism::ConstantReadNode
|
255
|
+
node.name.to_s
|
256
|
+
when Prism::ConstantPathNode
|
257
|
+
node.slice
|
258
|
+
end
|
259
|
+
|
260
|
+
return unless @generator.global_state.result_sets[qualified_name]
|
261
|
+
|
262
|
+
@generator.global_state.add_reference(qualified_name, @generator.metadata[:uri], range_id)
|
263
|
+
@generator.emit_edge('next', range_id, @generator.global_state.result_sets[qualified_name])
|
264
|
+
end
|
265
|
+
end
|
61
266
|
end
|
62
267
|
end
|
63
268
|
end
|
data/lib/hind/lsif.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
-
|
2
|
-
require_relative "lsif/edge"
|
3
|
-
require_relative "lsif/generator"
|
4
|
-
require_relative "lsif/vertex"
|
5
|
-
require_relative "lsif/visitor"
|
1
|
+
# frozen_string_literal: true
|
6
2
|
|
3
|
+
require_relative 'lsif/global_state'
|
4
|
+
require_relative 'lsif/edge'
|
5
|
+
require_relative 'lsif/generator'
|
6
|
+
require_relative 'lsif/vertex'
|
7
|
+
require_relative 'lsif/visitor'
|
7
8
|
|
8
9
|
module Hind
|
9
10
|
module LSIF
|
data/lib/hind/parser.rb
CHANGED
data/lib/hind/scip/generator.rb
CHANGED
data/lib/hind/scip/visitor.rb
CHANGED
data/lib/hind/scip.rb
CHANGED
data/lib/hind/version.rb
CHANGED
data/lib/hind.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
require_relative
|
6
|
-
require_relative
|
3
|
+
require_relative 'hind/version'
|
4
|
+
require_relative 'hind/lsif'
|
5
|
+
require_relative 'hind/scip'
|
6
|
+
require_relative 'hind/parser'
|
7
7
|
|
8
8
|
module Hind
|
9
9
|
class Error < StandardError; end
|