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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 89ca540ebc5d2ae79689ed9eb4c4ad9a89cea5b66991adecb4de8262f8d50867
4
- data.tar.gz: c1d5b39a3a772b1c4079376491361f67cb0dc2853a484e43ceca18b370798f1b
3
+ metadata.gz: 5fa041a7ffe7e916e1f3715500d21a9dea2a3a6ce60990be8cf4e7578f704702
4
+ data.tar.gz: '0779f87c520ddc1edddf0448c96d596194982d5b87fa5c380bb3a1132500bd9e'
5
5
  SHA512:
6
- metadata.gz: cbb72f614d4355c2aa7f4d96b6c5f9babbd61565c7aa6e571c1f33096b5ed8154bda34cb20c9a55523bf5b9a628ec79f7901d0a04d71ceef992cd9930bd62f19
7
- data.tar.gz: 3e2288b97ef3f4028060ff754b069bf5aa457a8429edce1e9177c75307d605a872b87eced63184cbda4578e17ffc5b39c2529ac0317d968e0a75d83e364a7b43
6
+ metadata.gz: 397847759b6c959511602d276eee22f7e89959c5858d840cf8398b874a39ef6ccfeec8d876e9c0539d5cc0bc0cce77cb1e297b7fabc30c9a4562bfb7e85ddb7b
7
+ data.tar.gz: 5c823067473c8b784a4868bf696b4374735b548eb8eb9341fb887dc78028e9223aeaac7172c621c624d0e8a3de221b50e101e908177626f9c316797e8ce2de70
data/exe/hind CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
- require "hind"
3
- require "hind/cli"
2
+ # frozen_string_literal: true
3
+
4
+ require 'hind'
5
+ require 'hind/cli'
4
6
 
5
7
  Hind::CLI.start(ARGV)
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 StandardError => e
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 StandardError => e
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
- if exclude_patterns
82
- exclude_patterns.each do |exclude|
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 StandardError => e
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, "SCIP generation not yet implemented"
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 StandardError => e
132
+ rescue => e
137
133
  abort "Error loading config file: #{e.message}"
138
134
  end
139
135
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Hind
2
4
  module LSIF
3
5
  class Edge
@@ -12,7 +14,7 @@ module Hind
12
14
  @document = document
13
15
  end
14
16
 
15
- def to_json
17
+ def to_json(*_args)
16
18
  json = {
17
19
  id: @id,
18
20
  type: 'edge',
@@ -91,7 +91,7 @@ module Hind
91
91
  }
92
92
  })
93
93
 
94
- @global_state.project_id = emit_vertex('project', { kind: 'ruby' })
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
- if ranges&.any?
114
- emit_edge('contains', @document_id, ranges)
115
- end
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.gsub('\\', '/')
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] = { file: file_path, range_id: range_id }
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] << { file: file_path, range_id: range_id }
28
+ @references[qualified_name] << {file: file_path, range_id: range_id}
27
29
  end
28
30
  end
29
31
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Hind
2
4
  module LSIF
3
5
  class Vertex
@@ -9,7 +11,7 @@ module Hind
9
11
  @data = data
10
12
  end
11
13
 
12
- def to_json
14
+ def to_json(*_args)
13
15
  json = {
14
16
  id: @id,
15
17
  type: 'vertex',
@@ -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('::').last.downcase}"
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
- # Add hover information
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
- # Additional visitor methods...
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
- require_relative "lsif/global_state"
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Hind
2
4
  class Parser
3
5
  def initialize(code)
@@ -7,6 +9,7 @@ module Hind
7
9
  def parse
8
10
  result = Prism.parse(@code)
9
11
  raise "Parse error: #{result.errors}" unless result.success?
12
+
10
13
  result.value
11
14
  end
12
15
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Hind
2
4
  module SCIP
3
5
  class Generator
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Hind
2
4
  module SCIP
3
5
  class Visitor
data/lib/hind/scip.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Hind
2
4
  module SCIP
3
5
  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.4"
4
+ VERSION = '0.1.5'
5
5
  end
data/lib/hind.rb CHANGED
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "hind/version"
4
- require_relative "hind/lsif"
5
- require_relative "hind/scip"
6
- require_relative "hind/parser"
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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hind
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aboobacker MK