module PluginCatalog
Constants
- DEFAULT_MIN_SCORE
- DEFAULT_PLUGINS_DIR
- EXCLUDED_COMMAND_PREFIXES
- LOCATION_HINTS
- TOPIC_SYNONYMS
Public Instance Methods
Source
# File lib/plugin_catalog.rb, line 139 def excluded_command?(command) root = command.to_s.split(/\s+/, 2).first.to_s.downcase EXCLUDED_COMMAND_PREFIXES.any? { |prefix| root == prefix || root.start_with?("#{prefix} ") } end
Source
# File lib/plugin_catalog.rb, line 67 def existing_command_for(text, plugins_dir: Help::PLUGINS_DIR, min_score: DEFAULT_MIN_SCORE) entry = find_match(text, plugins_dir: plugins_dir, min_score: min_score) return nil unless entry location = extract_location_hint(text) command = entry[:command].to_s.split(/\s+/, 2).first args = location || "" usage = args.empty? ? "!#{command}" : "!#{command} #{args}" { command: command, args: args, usage: usage, description: entry[:description].to_s.strip, plugin: entry[:plugin].to_s } end
Source
# File lib/plugin_catalog.rb, line 62 def extract_location_hint(text) normalized = normalize_text(text) LOCATION_HINTS.find { |place| normalized.include?(place) } end
Source
# File lib/plugin_catalog.rb, line 51 def find_match(text, plugins_dir: Help::PLUGINS_DIR, min_score: DEFAULT_MIN_SCORE) normalized = normalize_text(text) return nil if normalized.empty? best = list_entries(plugins_dir: plugins_dir) .map { |entry| [entry, score_entry(entry, normalized)] } .select { |_entry, score| score >= min_score } .max_by { |_entry, score| score } best&.first end
Source
# File lib/plugin_catalog.rb, line 47 def list_entries(plugins_dir: Help::PLUGINS_DIR) Help.list_commands(plugins_dir: plugins_dir).reject { |entry| excluded_command?(entry[:command]) } end
Source
# File lib/plugin_catalog.rb, line 131 def normalize_text(text) text.to_s .downcase .gsub(/[^a-z0-9\s]/, " ") .squeeze(" ") .strip end
Source
# File lib/plugin_catalog.rb, line 120 def query_tokens(text) normalize_text(text) .split(/\s+/) .reject { |token| token.length < 3 || stop_word?(token) } .uniq end
Source
# File lib/plugin_catalog.rb, line 85 def score_entry(entry, normalized_text) tokens = query_tokens(normalized_text) return 0 if tokens.empty? haystack = searchable_text(entry) tokens.sum { |token| token_weight(token, haystack) } end
Source
# File lib/plugin_catalog.rb, line 93 def searchable_text(entry) [ entry[:command], entry[:description], entry[:plugin], synonyms_for_command(entry[:command]) ].join(" ").downcase end
Source
# File lib/plugin_catalog.rb, line 127 def stop_word?(token) %w[the and for with make build plugin plugins create add new from that this about into].include?(token) end
Source
# File lib/plugin_catalog.rb, line 102 def synonyms_for_command(command) root = command.to_s.split(/\s+/, 2).first TOPIC_SYNONYMS.fetch(root, TOPIC_SYNONYMS.values.flatten.select { |word| word == root }) end
Source
# File lib/plugin_catalog.rb, line 107 def token_weight(token, haystack) return 2 if haystack.include?(token) related = TOPIC_SYNONYMS[token] || [] return 2 if related.any? { |word| haystack.include?(word) } TOPIC_SYNONYMS.each do |_topic, words| return 1 if words.include?(token) && words.any? { |word| haystack.include?(word) } end 0 end