Improve description searching and add a cache.
Closes Homebrew/homebrew#42281. Signed-off-by: Mike McQuaid <mike@mikemcquaid.com>
This commit is contained in:
parent
c4ceaabfc1
commit
8374374168
@ -195,6 +195,18 @@ _brew_deps ()
|
|||||||
__brew_complete_formulae
|
__brew_complete_formulae
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_brew_desc ()
|
||||||
|
{
|
||||||
|
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
case "$cur" in
|
||||||
|
--*)
|
||||||
|
__brewcomp "--search --name --description"
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
__brew_complete_formulae
|
||||||
|
}
|
||||||
|
|
||||||
_brew_doctor () {
|
_brew_doctor () {
|
||||||
local cur="${COMP_WORDS[COMP_CWORD]}"
|
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
__brewcomp "$(brew doctor --list-checks)"
|
__brewcomp "$(brew doctor --list-checks)"
|
||||||
@ -599,6 +611,7 @@ _brew ()
|
|||||||
cleanup) _brew_cleanup ;;
|
cleanup) _brew_cleanup ;;
|
||||||
create) _brew_create ;;
|
create) _brew_create ;;
|
||||||
deps) _brew_deps ;;
|
deps) _brew_deps ;;
|
||||||
|
desc) _brew_desc ;;
|
||||||
doctor|dr) _brew_doctor ;;
|
doctor|dr) _brew_doctor ;;
|
||||||
diy|configure) _brew_diy ;;
|
diy|configure) _brew_diy ;;
|
||||||
fetch) _brew_fetch ;;
|
fetch) _brew_fetch ;;
|
||||||
|
|||||||
@ -40,6 +40,7 @@ _1st_arguments=(
|
|||||||
'config:show homebrew and system configuration'
|
'config:show homebrew and system configuration'
|
||||||
'create:create a new formula'
|
'create:create a new formula'
|
||||||
'deps:list dependencies and dependants of a formula'
|
'deps:list dependencies and dependants of a formula'
|
||||||
|
'desc:display a description of a formula'
|
||||||
'doctor:audits your installation for common issues'
|
'doctor:audits your installation for common issues'
|
||||||
'edit:edit a formula'
|
'edit:edit a formula'
|
||||||
'fetch:download formula resources to the cache'
|
'fetch:download formula resources to the cache'
|
||||||
@ -95,7 +96,7 @@ if (( CURRENT == 1 )); then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
case "$words[1]" in
|
case "$words[1]" in
|
||||||
install|reinstall|audit|home|homepage|log|info|abv|uses|cat|deps|edit|options|switch)
|
install|reinstall|audit|home|homepage|log|info|abv|uses|cat|deps|desc|edit|options|switch)
|
||||||
_brew_all_formulae
|
_brew_all_formulae
|
||||||
_wanted formulae expl 'all formulae' compadd -a formulae ;;
|
_wanted formulae expl 'all formulae' compadd -a formulae ;;
|
||||||
list|ls)
|
list|ls)
|
||||||
|
|||||||
40
Library/Homebrew/cmd/desc.rb
Normal file
40
Library/Homebrew/cmd/desc.rb
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
require "descriptions"
|
||||||
|
require "cmd/search"
|
||||||
|
|
||||||
|
module Homebrew
|
||||||
|
def desc
|
||||||
|
if ARGV.options_only.empty?
|
||||||
|
if ARGV.named.empty?
|
||||||
|
raise FormulaUnspecifiedError
|
||||||
|
exit
|
||||||
|
end
|
||||||
|
results = Descriptions.named(ARGV.formulae.map(&:full_name))
|
||||||
|
else
|
||||||
|
if ARGV.options_only.count != 1
|
||||||
|
odie "Pick one, and only one, of -s/--search, -n/--name, or -d/--description."
|
||||||
|
end
|
||||||
|
|
||||||
|
search_arg = ARGV.options_only.first
|
||||||
|
|
||||||
|
search_type = case search_arg
|
||||||
|
when '-s', '--search'
|
||||||
|
:either
|
||||||
|
when '-n', '--name'
|
||||||
|
:name
|
||||||
|
when '-d', '--description'
|
||||||
|
:desc
|
||||||
|
else
|
||||||
|
odie "Unrecognized option '#{search_arg}'."
|
||||||
|
end
|
||||||
|
|
||||||
|
if arg = ARGV.named.first
|
||||||
|
regex = Homebrew::query_regexp(arg)
|
||||||
|
results = Descriptions.search(regex, search_type)
|
||||||
|
else
|
||||||
|
odie "You must provide a search term."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
results.print unless results.nil?
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -3,6 +3,7 @@ require "blacklist"
|
|||||||
require "utils"
|
require "utils"
|
||||||
require "thread"
|
require "thread"
|
||||||
require "official_taps"
|
require "official_taps"
|
||||||
|
require 'descriptions'
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
SEARCH_ERROR_QUEUE = Queue.new
|
SEARCH_ERROR_QUEUE = Queue.new
|
||||||
@ -23,11 +24,7 @@ module Homebrew
|
|||||||
elsif ARGV.include? "--desc"
|
elsif ARGV.include? "--desc"
|
||||||
query = ARGV.next
|
query = ARGV.next
|
||||||
rx = query_regexp(query)
|
rx = query_regexp(query)
|
||||||
Formula.each do |formula|
|
Descriptions.search(rx, :desc).print
|
||||||
if formula.desc =~ rx
|
|
||||||
puts "#{Tty.white}#{formula.full_name}:#{Tty.reset} #{formula.desc}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elsif ARGV.empty?
|
elsif ARGV.empty?
|
||||||
puts_columns Formula.full_names
|
puts_columns Formula.full_names
|
||||||
elsif ARGV.first =~ HOMEBREW_TAP_FORMULA_REGEX
|
elsif ARGV.first =~ HOMEBREW_TAP_FORMULA_REGEX
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
require "tap"
|
require "tap"
|
||||||
|
require "descriptions"
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
def tap
|
def tap
|
||||||
@ -41,6 +42,7 @@ module Homebrew
|
|||||||
|
|
||||||
formula_count = tap.formula_files.size
|
formula_count = tap.formula_files.size
|
||||||
puts "Tapped #{formula_count} formula#{plural(formula_count, "e")} (#{tap.path.abv})"
|
puts "Tapped #{formula_count} formula#{plural(formula_count, "e")} (#{tap.path.abv})"
|
||||||
|
Descriptions.cache_formulae(tap.formula_names)
|
||||||
|
|
||||||
if !clone_target && tap.private?
|
if !clone_target && tap.private?
|
||||||
puts <<-EOS.undent
|
puts <<-EOS.undent
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
require "cmd/tap" # for tap_args
|
require "cmd/tap" # for tap_args
|
||||||
|
require "descriptions"
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
def untap
|
def untap
|
||||||
@ -13,6 +14,7 @@ module Homebrew
|
|||||||
tap.unpin if tap.pinned?
|
tap.unpin if tap.pinned?
|
||||||
|
|
||||||
formula_count = tap.formula_files.size
|
formula_count = tap.formula_files.size
|
||||||
|
Descriptions.uncache_formulae(tap.formula_names)
|
||||||
tap.path.rmtree
|
tap.path.rmtree
|
||||||
tap.path.dirname.rmdir_if_possible
|
tap.path.dirname.rmdir_if_possible
|
||||||
puts "Untapped #{formula_count} formula#{plural(formula_count, "e")}"
|
puts "Untapped #{formula_count} formula#{plural(formula_count, "e")}"
|
||||||
|
|||||||
@ -2,6 +2,7 @@ require "cmd/tap"
|
|||||||
require "formula_versions"
|
require "formula_versions"
|
||||||
require "migrator"
|
require "migrator"
|
||||||
require "formulary"
|
require "formulary"
|
||||||
|
require "descriptions"
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
def update
|
def update
|
||||||
@ -100,6 +101,7 @@ module Homebrew
|
|||||||
puts "Updated Homebrew from #{master_updater.initial_revision[0, 8]} to #{master_updater.current_revision[0, 8]}."
|
puts "Updated Homebrew from #{master_updater.initial_revision[0, 8]} to #{master_updater.current_revision[0, 8]}."
|
||||||
report.dump
|
report.dump
|
||||||
end
|
end
|
||||||
|
Descriptions.update_cache(report)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
151
Library/Homebrew/descriptions.rb
Normal file
151
Library/Homebrew/descriptions.rb
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
require "formula"
|
||||||
|
require "csv"
|
||||||
|
|
||||||
|
class Descriptions
|
||||||
|
CACHE_FILE = HOMEBREW_CACHE + "desc_cache"
|
||||||
|
|
||||||
|
def self.cache
|
||||||
|
@cache || self.load_cache
|
||||||
|
end
|
||||||
|
|
||||||
|
# If the cache file exists, load it into, and return, a hash; otherwise,
|
||||||
|
# return nil.
|
||||||
|
def self.load_cache
|
||||||
|
if CACHE_FILE.exist?
|
||||||
|
@cache = {}
|
||||||
|
CSV.foreach(CACHE_FILE) { |name, desc| @cache[name] = desc }
|
||||||
|
@cache
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Write the cache to disk after ensuring the existence of the containing
|
||||||
|
# directory.
|
||||||
|
def self.save_cache
|
||||||
|
HOMEBREW_CACHE.mkpath
|
||||||
|
CSV.open(CACHE_FILE, 'w') do |csv|
|
||||||
|
@cache.each do |name, desc|
|
||||||
|
csv << [name, desc]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create a hash mapping all formulae to their descriptions;
|
||||||
|
# save it for future use.
|
||||||
|
def self.generate_cache
|
||||||
|
@cache = {}
|
||||||
|
Formula.map do |f|
|
||||||
|
@cache[f.full_name] = f.desc
|
||||||
|
end
|
||||||
|
self.save_cache
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return true if the cache exists, and neither Homebrew nor any of the Taps
|
||||||
|
# repos were updated more recently than it was.
|
||||||
|
def self.cache_fresh?
|
||||||
|
if CACHE_FILE.exist?
|
||||||
|
cache_date = File.mtime(CACHE_FILE)
|
||||||
|
|
||||||
|
ref_master = ".git/refs/heads/master"
|
||||||
|
master = HOMEBREW_REPOSITORY/ref_master
|
||||||
|
|
||||||
|
last_update = (master.exist? ? File.mtime(master) : Time.at(0))
|
||||||
|
|
||||||
|
Dir.glob(HOMEBREW_LIBRARY/"Taps/**"/ref_master).each do |repo|
|
||||||
|
repo_mtime = File.mtime(repo)
|
||||||
|
last_update = repo_mtime if repo_mtime > last_update
|
||||||
|
end
|
||||||
|
last_update <= cache_date
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create the cache if it doesn't already exist.
|
||||||
|
def self.ensure_cache
|
||||||
|
self.generate_cache unless self.cache_fresh? && self.cache
|
||||||
|
end
|
||||||
|
|
||||||
|
# Take a {Report}, as generated by cmd/update.rb.
|
||||||
|
# Unless the cache file exists, do nothing.
|
||||||
|
# If it does exist, but the Report is empty, just touch the cache file.
|
||||||
|
# Otherwise, use the report to update the cache.
|
||||||
|
def self.update_cache(report)
|
||||||
|
if CACHE_FILE.exist?
|
||||||
|
if report.empty?
|
||||||
|
FileUtils.touch CACHE_FILE
|
||||||
|
else
|
||||||
|
renamings = report.select_formula(:R)
|
||||||
|
alterations = report.select_formula(:A) + report.select_formula(:M) +
|
||||||
|
renamings.map(&:last)
|
||||||
|
self.cache_formulae(alterations, :save => false)
|
||||||
|
self.uncache_formulae(report.select_formula(:D) +
|
||||||
|
renamings.map(&:first))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Given an array of formula names, add them and their descriptions to the
|
||||||
|
# cache. Save the updated cache to disk, unless explicitly told not to.
|
||||||
|
def self.cache_formulae(formula_names, options = { :save => true })
|
||||||
|
if self.cache
|
||||||
|
formula_names.each { |name| @cache[name] = Formula[name].desc }
|
||||||
|
self.save_cache if options[:save]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Given an array of formula names, remove them and their descriptions from
|
||||||
|
# the cache. Save the updated cache to disk, unless explicitly told not to.
|
||||||
|
def self.uncache_formulae(formula_names, options = { :save => true })
|
||||||
|
if self.cache
|
||||||
|
formula_names.each { |name| @cache.delete(name) }
|
||||||
|
self.save_cache if options[:save]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Given an array of formula names, return a {Descriptions} object mapping
|
||||||
|
# those names to their descriptions.
|
||||||
|
def self.named(names)
|
||||||
|
self.ensure_cache
|
||||||
|
|
||||||
|
results = {}
|
||||||
|
unless names.empty?
|
||||||
|
results = names.inject({}) do |accum, name|
|
||||||
|
accum[name] = @cache[name]
|
||||||
|
accum
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
new(results)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Given a regex, find all formulae whose specified fields contain a match.
|
||||||
|
def self.search(regex, field = :either)
|
||||||
|
self.ensure_cache
|
||||||
|
|
||||||
|
results = case field
|
||||||
|
when :name
|
||||||
|
@cache.select { |name, _| name =~ regex }
|
||||||
|
when :desc
|
||||||
|
@cache.select { |_, desc| desc =~ regex }
|
||||||
|
when :either
|
||||||
|
@cache.select { |name, desc| (name =~ regex) || (desc =~ regex) }
|
||||||
|
end
|
||||||
|
|
||||||
|
results = Hash[results] if RUBY_VERSION <= "1.8.7"
|
||||||
|
|
||||||
|
new(results)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create an actual instance.
|
||||||
|
def initialize(descriptions)
|
||||||
|
@descriptions = descriptions
|
||||||
|
end
|
||||||
|
|
||||||
|
# Take search results -- a hash mapping formula names to descriptions -- and
|
||||||
|
# print them.
|
||||||
|
def print
|
||||||
|
blank = "#{Tty.yellow}[no description]#{Tty.reset}"
|
||||||
|
@descriptions.keys.sort.each do |name|
|
||||||
|
description = @descriptions[name] || blank
|
||||||
|
puts "#{Tty.white}#{name}:#{Tty.reset} #{description}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -126,6 +126,16 @@ Note that these flags should only appear after a command.
|
|||||||
type dependencies, pass `--skip-build`. Similarly, pass `--skip-optional`
|
type dependencies, pass `--skip-build`. Similarly, pass `--skip-optional`
|
||||||
to skip `:optional` dependencies.
|
to skip `:optional` dependencies.
|
||||||
|
|
||||||
|
* `desc` <formula>:
|
||||||
|
Display <formula>'s name and one-line description.
|
||||||
|
|
||||||
|
* `desc [-s|-n|-d] <pattern>`:
|
||||||
|
Search both name and description (`-s`), just the names (`-n`), or just the
|
||||||
|
descriptions (`-d`) for `<pattern>`. `<pattern>` is by default interpreted
|
||||||
|
as a literal string; if flanked by slashes, it is instead interpreted as a
|
||||||
|
regular expression. Formula descriptions are cached; the cache is created on
|
||||||
|
the first search, making that search slower than subsequent ones.
|
||||||
|
|
||||||
* `diy [--name=<name>] [--version=<version>]`:
|
* `diy [--name=<name>] [--version=<version>]`:
|
||||||
Automatically determine the installation prefix for non-Homebrew software.
|
Automatically determine the installation prefix for non-Homebrew software.
|
||||||
|
|
||||||
|
|||||||
@ -255,8 +255,8 @@ def puts_columns(items, star_items = [])
|
|||||||
# determine the best width to display for different console sizes
|
# determine the best width to display for different console sizes
|
||||||
console_width = `/bin/stty size`.chomp.split(" ").last.to_i
|
console_width = `/bin/stty size`.chomp.split(" ").last.to_i
|
||||||
console_width = 80 if console_width <= 0
|
console_width = 80 if console_width <= 0
|
||||||
longest = items.sort_by(&:length).last
|
max_len = items.reduce(0) { |max, item| l = item.length ; l > max ? l : max }
|
||||||
optimal_col_width = (console_width.to_f / (longest.length + 2).to_f).floor
|
optimal_col_width = (console_width.to_f / (max_len + 2).to_f).floor
|
||||||
cols = optimal_col_width > 1 ? optimal_col_width : 1
|
cols = optimal_col_width > 1 ? optimal_col_width : 1
|
||||||
|
|
||||||
IO.popen("/usr/bin/pr -#{cols} -t -w#{console_width}", "w") { |io| io.puts(items) }
|
IO.popen("/usr/bin/pr -#{cols} -t -w#{console_width}", "w") { |io| io.puts(items) }
|
||||||
|
|||||||
@ -128,6 +128,12 @@ If \fB\-\-installed\fR is passed, show dependencies for all installed formulae\.
|
|||||||
By default, \fBdeps\fR shows dependencies for \fIformulae\fR\. To skip the \fB:build\fR type dependencies, pass \fB\-\-skip\-build\fR\. Similarly, pass \fB\-\-skip\-optional\fR to skip \fB:optional\fR dependencies\.
|
By default, \fBdeps\fR shows dependencies for \fIformulae\fR\. To skip the \fB:build\fR type dependencies, pass \fB\-\-skip\-build\fR\. Similarly, pass \fB\-\-skip\-optional\fR to skip \fB:optional\fR dependencies\.
|
||||||
.
|
.
|
||||||
.IP "\(bu" 4
|
.IP "\(bu" 4
|
||||||
|
\fBdesc\fR \fIformula\fR: Display \fIformula\fR\'s name and one\-line description\.
|
||||||
|
.
|
||||||
|
.IP "\(bu" 4
|
||||||
|
\fBdesc [\-s|\-n|\-d] <pattern>\fR: Search both name and description (\fB\-s\fR), just the names (\fB\-n\fR), or just the descriptions (\fB\-d\fR) for \fB<pattern>\fR\. \fB<pattern>\fR is by default interpreted as a literal string; if flanked by slashes, it is instead interpreted as a regular expression\. Formula descriptions are cached; the cache is created on the first search, making that search slower than subsequent ones\.
|
||||||
|
.
|
||||||
|
.IP "\(bu" 4
|
||||||
\fBdiy [\-\-name=<name>] [\-\-version=<version>]\fR: Automatically determine the installation prefix for non\-Homebrew software\.
|
\fBdiy [\-\-name=<name>] [\-\-version=<version>]\fR: Automatically determine the installation prefix for non\-Homebrew software\.
|
||||||
.
|
.
|
||||||
.IP
|
.IP
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user