+package App::Zealc::Command::query;
+
+use 5.014000;
+use strict;
+use warnings;
+
+our $VERSION = '0.000_001';
+
+use App::Zealc '-command';
+
+use IO::Prompter;
+use File::Slurp qw/write_file/;
+use File::Temp qw//;
+use File::Which;
+use HTML::FormatText;
+use Term::Pager;
+
+use Encode qw/encode/;
+
+# These set --format=html and --with=something
+use constant BROWSERS => [ qw/www-browser lynx links2 elinks w3m x-www-browser firefox/ ];
+my @BROWSERS = map { [$_, "Shorthand for --format=html --with=$_", {implies => {with => $_, format => 'html'}}] } @{BROWSERS()};
+
+# These set -format=something
+use constant FORMATTERS => [
+ [lynxdump => 'lynx -dump -nolist'],
+ [links2dump => 'links2 -dump'],
+ [elinksdump => 'elinks -dump -eval "set document.dump.references = 0" -eval "set document.dump.numbering = 0"'],
+];
+my @FORMATTERS = map {
+ my ($name, $format) = @$_;
+ [$name, "Shorthand for --format='$format'", {implies => {format => $format}}]
+} @{FORMATTERS()};
+
+# These set --with=something
+use constant PAGERS => [ qw/pager less most more pg/ ];
+my @PAGERS = map { [$_, "Shorthand for --with=$_", {implies => {with => $_}}] } @{PAGERS()};
+
+sub opt_spec {(
+ ['with|w=s', 'Open with this program'],
+ ['format|f=s', 'Convert html to this format'],
+
+ [], @BROWSERS,
+ [], @FORMATTERS,
+ [], @PAGERS,
+)}
+
+sub usage_desc { "%c query %o term" };
+
+sub validate_args {
+ my ($self, $opts, $args) = @_;
+ my @args = @$args;
+ $self->usage_error("No query specified") unless @args;
+ $self->usage_error("Too many arguments") if @args > 1;
+ $opts->{with} //= $self->app->config->{with};
+ $opts->{format} //= $self->app->config->{format};
+
+ # If output is not a tty, do not choose browsers/pagers
+ $opts->{with} //= '' unless -t select;
+
+ # Try to default to a browser
+ if (!defined $opts->{with} && !defined $opts->{format}) {
+ for my $browser (@{BROWSERS()}) {
+ warn "Trying to use $browser as a browser\n" if $self->app->verbose;
+ next unless which $browser;
+ $opts->{with} = $browser;
+ $opts->{format} = 'html';
+ last
+ }
+ }
+
+ # If no browsers were found, try to choose a formatter
+ unless (defined $opts->{format}) {
+ for my $formatter (map { $_->[1] } @{FORMATTERS()}) {
+ my ($exec) = $formatter =~ /^(\w+)/s;
+ warn "Trying to use $exec ($formatter) as a formatter\n" if $self->app->verbose;
+ next unless which $exec;
+ $opts->{format} = $formatter;
+ last
+ }
+ }
+
+ # If no browsers were found, try to choose a pager
+ unless (defined $opts->{with}) {
+ for my $pager (@{PAGERS()}) {
+ warn "Trying to use $pager as a pager\n" if $self->app->verbose;
+ next unless which $pager;
+ $opts->{with} = $pager;
+ last
+ }
+ }
+
+ # Default to HTML::FormatText and the internal pager
+ $opts->{format} //= 'text';
+ $opts->{with} //= '';
+}
+
+sub html_to_format {
+ my ($html, $format) = @_;
+ return $html if $format eq 'html';
+ return HTML::FormatText->format_string($html) if $format eq 'text';
+ my $tmp = File::Temp->new(TEMPLATE => 'zealcXXXX', SUFFIX => '.html');
+ write_file $tmp, $html;
+ my $fn = $tmp->filename;
+ `$format $fn`
+}
+
+sub show_document {
+ my ($self, $doc, %opts) = @_;
+ my $html = $doc->fetch;
+
+ say "Format: $opts{format}" if $self->app->verbose;
+ say "Open with: ", $opts{with} // 'internal pager' if $self->app->verbose;;
+
+ my $text = encode 'UTF-8', html_to_format $html, $opts{format};
+
+ if ($opts{with}) {
+ my $tmp = File::Temp->new(TEMPLATE => 'zealcXXXX', SUFFIX => '.html');
+ write_file $tmp, $text;
+ system $opts{with} . ' ' . $tmp->filename;
+ } elsif (!-t select) {
+ say $text
+ } else {
+ my ($rows, $cols) = split ' ', `stty size`;
+ my $pager = Term::Pager->new(text => $text, rows => $rows, cols => $cols);
+ $pager->more;
+ }
+}
+
+sub execute {
+ my ($self, $opts, $args) = @_;
+ my %opts = %$opts;
+ my ($query) = @$args;
+ my $zeal = $self->app->zeal;
+ my @results = $zeal->query($query);
+ @results = $zeal->query("$query%") unless @results;
+ die "No results found for $query\n" unless @results;
+
+ my $doc = $results[0];
+ if (@results > 1 && -t select) {
+ my @args = '-single';
+ @args = '-number' if @results > 52;
+ my %menu = map {
+ my $ds = $results[$_]->docset->name;
+ $results[$_]->name . " ($ds)" => $_
+ } 0 .. $#results;
+ $doc = prompt 'Choose a document', -menu => \%menu, '-stdio', @args;
+ return unless $doc;
+ $doc = $results[$doc];
+ }
+
+ $self->show_document($doc, %opts);
+}
+
+1;
+__END__
+
+=encoding utf-8
+
+=head1 NAME
+
+App::Zealc::Command::query - view the documentation for a term
+
+=head1 SYNOPSIS
+
+ zealc query perldoc
+ # displays documentation for perldoc
+
+ zealc query --w3m perl:%Spec
+ # finds File::Spec and perlpodspec
+
+=head1 DESCRIPTION
+
+The query command displays the documentation for a given term. The
+term is a SQL LIKE pattern. If no documents are found, the search is
+retried after appending a % to the term. If multiple documents are
+found, the user will be prompted to choose one of them.
+
+A SQL LIKE pattern is similar to a shell glob. The "%" character
+matches zero or more characters (like "*" in a shell glob or ".*" in a
+regex) and "_" matches exactly one character (like "?" in a shell glob
+or "." in a regex). Matching is case-insensitive.
+
+=head1 SEE ALSO
+
+L<zealc>, L<Zeal>
+
+=head1 AUTHOR
+
+Marius Gavrilescu, E<lt>marius@ieval.roE<gt>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2015 by Marius Gavrilescu
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself, either Perl version 5.20.1 or,
+at your option, any later version of Perl 5 you may have available.
+
+
+=cut