rubik 0.3.1 → 0.3.2

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.
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "output"
4
+ require_relative "diagnostic_reporter"
5
+ require_relative "fuzzy_matcher"
6
+ require_relative "truncator"
7
+
8
+ module Rubik
9
+ module Tools
10
+ module SymbolToolSupport
11
+ def self.string_argument(args, primary, aliases: [], label: primary)
12
+ return [nil, ToolOutput.error("Tool arguments must be an object.")] unless args.is_a?(Hash)
13
+
14
+ values = [primary, *aliases].flat_map do |key|
15
+ [key.to_s, key.to_sym].uniq.filter_map { |actual| args[actual] if args.key?(actual) }
16
+ end
17
+ values.compact!
18
+ return [nil, nil] if values.empty?
19
+ return [nil, ToolOutput.error("#{label} must be a string.")] unless values.all?(String)
20
+
21
+ [values.first, nil]
22
+ end
23
+
24
+ def self.boolean_argument(args, key, label: key)
25
+ return [nil, ToolOutput.error("Tool arguments must be an object.")] unless args.is_a?(Hash)
26
+
27
+ values = [key.to_s, key.to_sym].uniq.filter_map { |actual| args[actual] if args.key?(actual) }
28
+ values.compact!
29
+ return [false, nil] if values.empty?
30
+ return [nil, ToolOutput.error("#{label} must be a boolean.")] unless values.all? { |value| [true, false].include?(value) }
31
+
32
+ [values.first, nil]
33
+ end
34
+
35
+ def self.warmup_output(graph)
36
+ return nil unless graph.respond_to?(:warmup_status)
37
+
38
+ status = graph.warmup_status
39
+ return nil unless status
40
+
41
+ state = status[:state] || status["state"]
42
+ if [:warming, "warming"].include?(state)
43
+ indexed = status[:indexed] || status["indexed"]
44
+ total = status[:total] || status["total"]
45
+ progress = if indexed.is_a?(Numeric) && total.is_a?(Numeric)
46
+ " (#{indexed}/#{total} files indexed)"
47
+ else
48
+ ""
49
+ end
50
+ ToolOutput.error("AST index warming; retry this request shortly#{progress}.")
51
+ elsif [:failed, "failed"].include?(state)
52
+ ToolOutput.error("AST index warmup failed; retry after running '!context scan'.")
53
+ end
54
+ end
55
+
56
+ def self.rank_matches(matches, query, graph)
57
+ symbol_map = graph.respond_to?(:symbols) ? graph.symbols : nil
58
+ qualified = symbol_map.respond_to?(:qualified_symbols) ? symbol_map.qualified_symbols : {}
59
+ keys_by_id = {}.compare_by_identity
60
+ qualified.sort_by { |key, _symbol| key }.each do |(key, symbol)|
61
+ keys_by_id[symbol] ||= key
62
+ end
63
+
64
+ matches.each_with_index.sort_by do |symbol, index|
65
+ name = symbol.name.to_s.downcase
66
+ lowered_query = query.downcase
67
+ rank = if name == lowered_query
68
+ 0
69
+ elsif name.start_with?(lowered_query)
70
+ 1
71
+ else
72
+ 2
73
+ end
74
+ key = keys_by_id[symbol] || "#{symbol.file}##{symbol.name}"
75
+ [rank, key, index]
76
+ end.map(&:first)
77
+ end
78
+
79
+ def self.render_search(query, matches)
80
+ shown_count = [matches.length, 50].min
81
+ loop do
82
+ omitted = matches.length - shown_count
83
+ lines = ["Found #{matches.length} matching symbol(s) for '#{query}':"]
84
+ matches.first(shown_count).each do |sym|
85
+ lines << "• #{sym.kind.to_s.upcase} #{sym.name} (#{sym.file}:#{sym.line_start}-#{sym.line_end})"
86
+ lines << " Signature: #{sym.signature}"
87
+ lines << " Calls: #{sym.calls.join(', ')}"
88
+ end
89
+ lines << "... #{omitted} additional matches omitted." if omitted.positive?
90
+
91
+ truncated = Truncator.truncate(lines.join("\n"))
92
+ return truncated[:text] if !truncated[:truncated] || shown_count <= 1
93
+
94
+ shown_count -= 1
95
+ end
96
+ end
97
+
98
+ def self.fuzzy_candidate_names(graph)
99
+ symbols = graph.respond_to?(:symbols) ? graph.symbols : nil
100
+ return [] unless symbols
101
+
102
+ names = if symbols.respond_to?(:values)
103
+ symbols.values.filter_map { |sym| sym.name if sym.respond_to?(:name) }.uniq
104
+ else
105
+ []
106
+ end
107
+ return names unless names.empty?
108
+
109
+ symbols.respond_to?(:keys) ? symbols.keys : []
110
+ end
111
+ end
112
+ end
113
+ end
data/lib/rubik/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rubik
4
- VERSION = "0.3.1"
4
+ VERSION = "0.3.2"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubik
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Özgür Ulukır
@@ -249,6 +249,7 @@ files:
249
249
  - lib/rubik/ast/fallback_cleaner.rb
250
250
  - lib/rubik/ast/go_extractor.rb
251
251
  - lib/rubik/ast/graph.rb
252
+ - lib/rubik/ast/graph_extensions.rb
252
253
  - lib/rubik/ast/python_extractor.rb
253
254
  - lib/rubik/ast/ruby_extractor.rb
254
255
  - lib/rubik/ast/rust_extractor.rb
@@ -757,6 +758,7 @@ files:
757
758
  - lib/rubik/tools/registry.rb
758
759
  - lib/rubik/tools/result.rb
759
760
  - lib/rubik/tools/services.rb
761
+ - lib/rubik/tools/symbol_tool_support.rb
760
762
  - lib/rubik/tools/truncator.rb
761
763
  - lib/rubik/tools/uri_resolver.rb
762
764
  - lib/rubik/ui.rb