| 
									
										
										
										
											2020-10-10 14:16:11 +02:00
										 |  |  | # typed: false | 
					
						
							| 
									
										
										
										
											2019-04-19 15:38:03 +09:00
										 |  |  | # frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-05 15:39:09 +02:00
										 |  |  | require "searchable" | 
					
						
							| 
									
										
										
										
											2018-10-13 08:22:51 -07:00
										 |  |  | require "description_cache_store" | 
					
						
							| 
									
										
										
										
											2018-06-05 15:39:09 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-02 20:49:14 +02:00
										 |  |  | module Homebrew | 
					
						
							| 
									
										
										
										
											2020-08-19 07:02:45 +02:00
										 |  |  |   # Helper module for searching formulae or casks. | 
					
						
							|  |  |  |   # | 
					
						
							|  |  |  |   # @api private | 
					
						
							| 
									
										
										
										
											2018-06-02 20:49:14 +02:00
										 |  |  |   module Search | 
					
						
							| 
									
										
										
										
											2022-12-19 19:27:48 -08:00
										 |  |  |     module_function | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def query_regexp(query) | 
					
						
							| 
									
										
										
										
											2021-02-12 18:33:37 +05:30
										 |  |  |       if (m = query.match(%r{^/(.*)/$})) | 
					
						
							| 
									
										
										
										
											2018-06-02 20:49:14 +02:00
										 |  |  |         Regexp.new(m[1]) | 
					
						
							|  |  |  |       else | 
					
						
							| 
									
										
										
										
											2018-06-05 15:39:09 +02:00
										 |  |  |         query | 
					
						
							| 
									
										
										
										
											2018-06-02 20:49:14 +02:00
										 |  |  |       end | 
					
						
							|  |  |  |     rescue RegexpError | 
					
						
							|  |  |  |       raise "#{query} is not a valid regex." | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-19 19:27:48 -08:00
										 |  |  |     def search_descriptions(string_or_regex, args, search_type: :desc) | 
					
						
							| 
									
										
										
										
											2022-12-17 10:03:41 -08:00
										 |  |  |       both = !args.formula? && !args.cask? | 
					
						
							|  |  |  |       eval_all = args.eval_all? || Homebrew::EnvConfig.eval_all? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if args.formula? || both | 
					
						
							|  |  |  |         ohai "Formulae" | 
					
						
							|  |  |  |         CacheStoreDatabase.use(:descriptions) do |db| | 
					
						
							|  |  |  |           cache_store = DescriptionCacheStore.new(db) | 
					
						
							|  |  |  |           Descriptions.search(string_or_regex, search_type, cache_store, eval_all).print | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |       return if !args.cask? && !both | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       puts if both | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       ohai "Casks" | 
					
						
							|  |  |  |       CacheStoreDatabase.use(:cask_descriptions) do |db| | 
					
						
							|  |  |  |         cache_store = CaskDescriptionCacheStore.new(db) | 
					
						
							|  |  |  |         Descriptions.search(string_or_regex, search_type, cache_store, eval_all).print | 
					
						
							| 
									
										
										
										
											2018-10-13 08:22:51 -07:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2018-06-18 16:09:13 +02:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-19 19:27:48 -08:00
										 |  |  |     def search_taps(query, silent: false) | 
					
						
							| 
									
										
										
										
											2018-06-13 07:49:01 +02:00
										 |  |  |       if query.match?(Regexp.union(HOMEBREW_TAP_FORMULA_REGEX, HOMEBREW_TAP_CASK_REGEX)) | 
					
						
							|  |  |  |         _, _, query = query.split("/", 3) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-06 13:24:55 +02:00
										 |  |  |       results = { formulae: [], casks: [] } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-05 15:44:50 +01:00
										 |  |  |       return results if Homebrew::EnvConfig.no_github_api? | 
					
						
							| 
									
										
										
										
											2018-06-02 20:49:14 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       unless silent | 
					
						
							|  |  |  |         # Use stderr to avoid breaking parsed output | 
					
						
							|  |  |  |         $stderr.puts Formatter.headline("Searching taps on GitHub...", color: :blue) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-06 13:24:55 +02:00
										 |  |  |       matches = begin | 
					
						
							|  |  |  |         GitHub.search_code( | 
					
						
							| 
									
										
										
										
											2018-11-02 17:18:07 +00:00
										 |  |  |           user:      "Homebrew", | 
					
						
							|  |  |  |           path:      ["Formula", "Casks", "."], | 
					
						
							|  |  |  |           filename:  query, | 
					
						
							| 
									
										
										
										
											2018-06-06 13:24:55 +02:00
										 |  |  |           extension: "rb", | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2021-02-15 21:48:21 +05:30
										 |  |  |       rescue GitHub::API::Error => e | 
					
						
							| 
									
										
										
										
											2019-04-04 19:49:06 +01:00
										 |  |  |         opoo "Error searching on GitHub: #{e}\n" | 
					
						
							| 
									
										
										
										
											2020-11-10 00:11:22 +11:00
										 |  |  |         nil | 
					
						
							| 
									
										
										
										
											2018-06-06 13:24:55 +02:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2018-06-02 20:49:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-10 00:11:22 +11:00
										 |  |  |       return results if matches.blank? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-06 13:24:55 +02:00
										 |  |  |       matches.each do |match| | 
					
						
							| 
									
										
										
										
											2018-06-02 20:49:14 +02:00
										 |  |  |         name = File.basename(match["path"], ".rb") | 
					
						
							|  |  |  |         tap = Tap.fetch(match["repository"]["full_name"]) | 
					
						
							|  |  |  |         full_name = "#{tap.name}/#{name}" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-09 12:20:58 +02:00
										 |  |  |         next if tap.installed? | 
					
						
							| 
									
										
										
										
											2018-06-06 13:24:55 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if match["path"].start_with?("Casks/") | 
					
						
							|  |  |  |           results[:casks] = [*results[:casks], full_name].sort | 
					
						
							| 
									
										
										
										
											2018-06-02 20:49:14 +02:00
										 |  |  |         else | 
					
						
							| 
									
										
										
										
											2018-06-06 13:24:55 +02:00
										 |  |  |           results[:formulae] = [*results[:formulae], full_name].sort | 
					
						
							| 
									
										
										
										
											2018-06-02 20:49:14 +02:00
										 |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2018-06-06 13:24:55 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       results | 
					
						
							| 
									
										
										
										
											2018-06-02 20:49:14 +02:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-19 19:27:48 -08:00
										 |  |  |     def search_formulae(string_or_regex) | 
					
						
							| 
									
										
										
										
											2018-06-13 07:49:01 +02:00
										 |  |  |       if string_or_regex.is_a?(String) && string_or_regex.match?(HOMEBREW_TAP_FORMULA_REGEX) | 
					
						
							|  |  |  |         return begin | 
					
						
							|  |  |  |           [Formulary.factory(string_or_regex).name] | 
					
						
							|  |  |  |         rescue FormulaUnavailableError | 
					
						
							|  |  |  |           [] | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-02 20:49:14 +02:00
										 |  |  |       aliases = Formula.alias_full_names | 
					
						
							| 
									
										
										
										
											2018-06-05 10:55:00 +02:00
										 |  |  |       results = (Formula.full_names + aliases) | 
					
						
							| 
									
										
										
										
											2018-06-05 15:39:09 +02:00
										 |  |  |                 .extend(Searchable) | 
					
						
							|  |  |  |                 .search(string_or_regex) | 
					
						
							| 
									
										
										
										
											2018-06-05 10:55:00 +02:00
										 |  |  |                 .sort | 
					
						
							| 
									
										
										
										
											2018-06-02 20:49:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-23 00:03:11 -04:00
										 |  |  |       results |= Formula.fuzzy_search(string_or_regex).map { |n| Formulary.factory(n).full_name } | 
					
						
							| 
									
										
										
										
											2021-06-19 00:19:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-02 20:49:14 +02:00
										 |  |  |       results.map do |name| | 
					
						
							| 
									
										
										
										
											2018-06-07 17:57:26 +02:00
										 |  |  |         formula, canonical_full_name = begin | 
					
						
							|  |  |  |           f = Formulary.factory(name) | 
					
						
							|  |  |  |           [f, f.full_name] | 
					
						
							| 
									
										
										
										
											2018-06-02 20:49:14 +02:00
										 |  |  |         rescue | 
					
						
							| 
									
										
										
										
											2018-06-07 17:57:26 +02:00
										 |  |  |           [nil, name] | 
					
						
							| 
									
										
										
										
											2018-06-02 20:49:14 +02:00
										 |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Ignore aliases from results when the full name was also found | 
					
						
							|  |  |  |         next if aliases.include?(name) && results.include?(canonical_full_name) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-07 17:57:26 +02:00
										 |  |  |         if formula&.any_version_installed? | 
					
						
							| 
									
										
										
										
											2018-06-02 20:49:14 +02:00
										 |  |  |           pretty_installed(name) | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |           name | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end.compact | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2018-06-07 14:42:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-19 19:27:48 -08:00
										 |  |  |     def search_casks(string_or_regex) | 
					
						
							| 
									
										
										
										
											2022-12-17 09:27:33 -08:00
										 |  |  |       if string_or_regex.is_a?(String) && string_or_regex.match?(HOMEBREW_TAP_CASK_REGEX) | 
					
						
							|  |  |  |         return begin | 
					
						
							|  |  |  |           [Cask::CaskLoader.load(string_or_regex).token] | 
					
						
							|  |  |  |         rescue Cask::CaskUnavailableError | 
					
						
							|  |  |  |           [] | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       cask_tokens = Tap.flat_map(&:cask_tokens).map do |c| | 
					
						
							|  |  |  |         c.sub(%r{^homebrew/cask.*/}, "") | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-23 13:32:54 -05:00
										 |  |  |       if !Tap.fetch("homebrew/cask").installed? && Homebrew::EnvConfig.install_from_api? | 
					
						
							|  |  |  |         cask_tokens += Homebrew::API::Cask.all_casks.keys | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-17 09:27:33 -08:00
										 |  |  |       results = cask_tokens.extend(Searchable) | 
					
						
							|  |  |  |                            .search(string_or_regex) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       results += DidYouMean::SpellChecker.new(dictionary: cask_tokens) | 
					
						
							|  |  |  |                                          .correct(string_or_regex) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       results.sort.map do |name| | 
					
						
							|  |  |  |         cask = Cask::CaskLoader.load(name) | 
					
						
							|  |  |  |         if cask.installed? | 
					
						
							|  |  |  |           pretty_installed(cask.full_name) | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |           cask.full_name | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end.uniq | 
					
						
							| 
									
										
										
										
											2018-06-07 14:42:58 +02:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2022-12-10 12:59:06 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-19 19:27:48 -08:00
										 |  |  |     def search_names(query, string_or_regex, args) | 
					
						
							| 
									
										
										
										
											2022-12-13 20:38:00 -08:00
										 |  |  |       both = !args.formula? && !args.cask? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-10 12:59:06 -08:00
										 |  |  |       remote_results = search_taps(query, silent: true) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-13 20:38:00 -08:00
										 |  |  |       all_formulae = if args.formula? || both | 
					
						
							|  |  |  |         search_formulae(string_or_regex) + remote_results[:formulae] | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         [] | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2022-12-10 12:59:06 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-13 20:38:00 -08:00
										 |  |  |       all_casks = if args.cask? || both | 
					
						
							|  |  |  |         search_casks(string_or_regex) + remote_results[:casks] | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         [] | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2022-12-10 12:59:06 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-12 19:51:18 -08:00
										 |  |  |       [all_formulae, all_casks] | 
					
						
							| 
									
										
										
										
											2022-12-10 12:59:06 -08:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2018-06-02 20:49:14 +02:00
										 |  |  |   end | 
					
						
							|  |  |  | end |