| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package App::Rakubrew::Shell; | 
| 2 | 3 |  |  | 3 |  | 63532 | use strict; | 
|  | 3 |  |  |  |  | 13 |  | 
|  | 3 |  |  |  |  | 76 |  | 
| 3 | 3 |  |  | 3 |  | 14 | use warnings; | 
|  | 3 |  |  |  |  | 6 |  | 
|  | 3 |  |  |  |  | 59 |  | 
| 4 | 3 |  |  | 3 |  | 44 | use 5.010; | 
|  | 3 |  |  |  |  | 9 |  | 
| 5 | 3 |  |  | 3 |  | 369 | use File::Spec::Functions qw(catdir catfile updir splitpath); | 
|  | 3 |  |  |  |  | 722 |  | 
|  | 3 |  |  |  |  | 162 |  | 
| 6 | 3 |  |  | 3 |  | 449 | use Try::Tiny; | 
|  | 3 |  |  |  |  | 1677 |  | 
|  | 3 |  |  |  |  | 146 |  | 
| 7 | 3 |  |  | 3 |  | 716 | use App::Rakubrew::Tools; | 
|  | 3 |  |  |  |  | 5 |  | 
|  | 3 |  |  |  |  | 215 |  | 
| 8 | 3 |  |  | 3 |  | 372 | use App::Rakubrew::Variables; | 
|  | 3 |  |  |  |  | 8 |  | 
|  | 3 |  |  |  |  | 376 |  | 
| 9 | 3 |  |  | 3 |  | 487 | use App::Rakubrew::VersionHandling; | 
|  | 3 |  |  |  |  | 7 |  | 
|  | 3 |  |  |  |  | 323 |  | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | # Turn on substring-based command line completion where possible contrary to the | 
| 12 |  |  |  |  |  |  | # "start of the line completion". I.e., to visualize the difference, 'ver' | 
| 13 |  |  |  |  |  |  | # string would result in the following command candidates: | 
| 14 |  |  |  |  |  |  | #   SUBSTRING_COMPLETION==1 -> version versions rakubrew-version | 
| 15 |  |  |  |  |  |  | #   SUBSTRING_COMPLETION==0 -> version versions | 
| 16 | 3 |  |  | 3 |  | 17 | use constant SUBSTRING_COMPLETION => 1; | 
|  | 3 |  |  |  |  | 6 |  | 
|  | 3 |  |  |  |  | 4787 |  | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | my $shell_hook; | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | sub initialize { | 
| 21 | 7 |  |  | 7 | 0 | 2840 | my $class = shift; | 
| 22 | 7 |  |  |  |  | 12 | my $shell = shift; | 
| 23 |  |  |  |  |  |  |  | 
| 24 | 7 | 50 | 33 |  |  | 20 | if (!shell_exists('Dummy self', $shell) || $shell eq 'auto') { | 
| 25 | 0 |  |  |  |  | 0 | $shell = detect_shell(); | 
| 26 |  |  |  |  |  |  | } | 
| 27 |  |  |  |  |  |  |  | 
| 28 | 7 |  |  |  |  | 310 | eval "require App::Rakubrew::Shell::$shell"; | 
| 29 | 7 | 50 |  |  |  | 23 | if ($@) { | 
| 30 | 0 |  |  |  |  | 0 | die "Loading shell hook failed: " . $@; | 
| 31 |  |  |  |  |  |  | } | 
| 32 | 7 |  |  |  |  | 47 | $shell_hook = bless {}, "App::Rakubrew::Shell::$shell"; | 
| 33 | 7 |  |  |  |  | 16 | return $shell_hook; | 
| 34 |  |  |  |  |  |  | } | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | sub detect_shell { | 
| 37 | 0 | 0 |  | 0 | 0 | 0 | if ($^O =~ /win32/i) { | 
| 38 |  |  |  |  |  |  | # https://stackoverflow.com/a/8547234 | 
| 39 | 0 |  |  |  |  | 0 | my $psmodpath = $ENV{PSMODULEPATH}; | 
| 40 | 0 |  |  |  |  | 0 | my $userprofile = $ENV{USERPROFILE}; | 
| 41 | 0 | 0 |  |  |  | 0 | if (index($psmodpath, $userprofile) == 0) { | 
| 42 | 0 |  |  |  |  | 0 | return 'PowerShell'; | 
| 43 |  |  |  |  |  |  | } | 
| 44 |  |  |  |  |  |  | else { | 
| 45 | 0 |  |  |  |  | 0 | return 'Cmd'; | 
| 46 |  |  |  |  |  |  | } | 
| 47 |  |  |  |  |  |  | } | 
| 48 |  |  |  |  |  |  | else { | 
| 49 | 0 |  | 0 |  |  | 0 | my $shell = $ENV{'SHELL'} || '/bin/bash'; | 
| 50 | 0 |  |  |  |  | 0 | $shell = (splitpath( $shell))[2]; | 
| 51 | 0 |  |  |  |  | 0 | $shell =~ s/[^a-z]+$//; # remove version numbers | 
| 52 |  |  |  |  |  |  |  | 
| 53 |  |  |  |  |  |  | # tcsh claims it's csh on FreeBSD. Try to detect that. | 
| 54 | 0 | 0 | 0 |  |  | 0 | if ($shell eq 'csh' && $ENV{'tcsh'}) { | 
| 55 | 0 |  |  |  |  | 0 | $shell = 'tcsh'; | 
| 56 |  |  |  |  |  |  | } | 
| 57 |  |  |  |  |  |  |  | 
| 58 | 0 |  |  |  |  | 0 | $shell = ucfirst $shell; | 
| 59 |  |  |  |  |  |  |  | 
| 60 | 0 | 0 |  |  |  | 0 | if (!shell_exists('Dummy self', $shell)) { | 
| 61 | 0 |  |  |  |  | 0 | $shell = 'Sh'; | 
| 62 |  |  |  |  |  |  | } | 
| 63 |  |  |  |  |  |  |  | 
| 64 | 0 |  |  |  |  | 0 | return $shell; | 
| 65 |  |  |  |  |  |  | } | 
| 66 |  |  |  |  |  |  | } | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | sub get { | 
| 69 | 0 |  |  | 0 | 0 | 0 | my $self = shift; | 
| 70 | 0 |  |  |  |  | 0 | return $shell_hook; | 
| 71 |  |  |  |  |  |  | } | 
| 72 |  |  |  |  |  |  |  | 
| 73 |  |  |  |  |  |  | sub shell_exists { | 
| 74 | 7 |  |  | 7 | 0 | 10 | my $self = shift; | 
| 75 | 7 |  |  |  |  | 9 | my $shell = shift; | 
| 76 |  |  |  |  |  |  |  | 
| 77 | 7 |  |  |  |  | 321 | eval "require App::Rakubrew::Shell::$shell"; | 
| 78 | 7 | 50 |  |  |  | 62 | return $@ ? 0 : 1; | 
| 79 |  |  |  |  |  |  | } | 
| 80 |  |  |  |  |  |  |  | 
| 81 |  |  |  |  |  |  | sub print_shellmod_code { | 
| 82 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 83 | 0 |  |  |  |  |  | my @params = @_; | 
| 84 | 0 |  | 0 |  |  |  | my $command = shift(@params) // ''; | 
| 85 | 0 |  |  |  |  |  | my $mode = get_brew_mode(1); | 
| 86 | 0 |  |  |  |  |  | my $version; | 
| 87 |  |  |  |  |  |  |  | 
| 88 | 0 | 0 |  |  |  |  | my $sep = $^O =~ /win32/i ? ';' : ':'; | 
| 89 |  |  |  |  |  |  |  | 
| 90 | 0 | 0 | 0 |  |  |  | if ($command eq 'shell' && @params) { | 
|  |  | 0 | 0 |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 91 | 0 |  |  |  |  |  | $version = $params[0]; | 
| 92 | 0 | 0 |  |  |  |  | if ($params[0] eq '--unset') { | 
|  |  | 0 |  |  |  |  |  | 
| 93 | 0 |  |  |  |  |  | say $self->get_shell_unsetter_code(); | 
| 94 |  |  |  |  |  |  | } | 
| 95 |  |  |  |  |  |  | elsif (! is_version_broken($params[0])) { | 
| 96 | 0 |  |  |  |  |  | say $self->get_shell_setter_code($params[0]); | 
| 97 |  |  |  |  |  |  | } | 
| 98 |  |  |  |  |  |  | } | 
| 99 |  |  |  |  |  |  | elsif ($command eq 'mode' && $mode eq 'shim') { # just switched to shim mode | 
| 100 | 0 |  |  |  |  |  | my $path = $ENV{PATH}; | 
| 101 | 0 |  |  |  |  |  | $path = $self->clean_path($path); | 
| 102 | 0 |  |  |  |  |  | $path = $shim_dir . $sep . $path; | 
| 103 | 0 |  |  |  |  |  | say $self->get_path_setter_code($path); | 
| 104 |  |  |  |  |  |  | } | 
| 105 |  |  |  |  |  |  | elsif ($mode eq 'env') { | 
| 106 | 0 |  |  |  |  |  | $version = get_version(); | 
| 107 |  |  |  |  |  |  | } | 
| 108 |  |  |  |  |  |  |  | 
| 109 | 0 | 0 |  |  |  |  | if ($mode eq 'env') { | 
| 110 | 0 |  |  |  |  |  | my $path = $ENV{PATH}; | 
| 111 | 0 |  |  |  |  |  | $path = $self->clean_path($path); | 
| 112 |  |  |  |  |  |  |  | 
| 113 | 0 | 0 |  |  |  |  | if ($version ne 'system') { | 
| 114 | 0 | 0 |  |  |  |  | if ($version eq '--unset') { | 
| 115 |  |  |  |  |  |  | # Get version ignoring the still set shell version. | 
| 116 | 0 |  |  |  |  |  | $version = get_version('shell'); | 
| 117 |  |  |  |  |  |  | } | 
| 118 | 0 | 0 |  |  |  |  | return if is_version_broken($version); | 
| 119 | 0 |  |  |  |  |  | $path = join($sep, get_bin_paths($version), $path); | 
| 120 |  |  |  |  |  |  | } | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | # In env mode several commands require changing PATH, so we just always | 
| 123 |  |  |  |  |  |  | # construct a new PATH and see if it's different. | 
| 124 | 0 | 0 |  |  |  |  | if ($path ne $ENV{PATH}) { | 
| 125 | 0 |  |  |  |  |  | say $self->get_path_setter_code($path); | 
| 126 |  |  |  |  |  |  | } | 
| 127 |  |  |  |  |  |  | } | 
| 128 |  |  |  |  |  |  | } | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | sub clean_path { | 
| 131 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 132 | 0 |  |  |  |  |  | my $path = shift; | 
| 133 | 0 |  |  |  |  |  | my $also_clean_path = shift; | 
| 134 |  |  |  |  |  |  |  | 
| 135 | 0 | 0 |  |  |  |  | my $sep = $^O =~ /win32/i ? ';' : ':'; | 
| 136 |  |  |  |  |  |  |  | 
| 137 | 0 |  |  |  |  |  | my @paths; | 
| 138 | 0 |  |  |  |  |  | for my $version (get_versions()) { | 
| 139 | 0 | 0 |  |  |  |  | next if $version eq 'system'; | 
| 140 | 0 | 0 |  |  |  |  | next if is_version_broken($version); | 
| 141 |  |  |  |  |  |  | try { | 
| 142 | 0 |  |  | 0 |  |  | push @paths, get_bin_paths($version); | 
| 143 |  |  |  |  |  |  | } | 
| 144 |  |  |  | 0 |  |  | catch { | 
| 145 |  |  |  |  |  |  | # Version is broken. So it's likely not in path anyways. | 
| 146 |  |  |  |  |  |  | # -> ignore it | 
| 147 | 0 |  |  |  |  |  | }; | 
| 148 |  |  |  |  |  |  | } | 
| 149 | 0 |  |  |  |  |  | push @paths, $versions_dir; | 
| 150 | 0 |  |  |  |  |  | push @paths, $shim_dir; | 
| 151 | 0 | 0 |  |  |  |  | push @paths, $also_clean_path if $also_clean_path; | 
| 152 | 0 |  |  |  |  |  | @paths = map { "\Q$_\E" } @paths; | 
|  | 0 |  |  |  |  |  |  | 
| 153 | 0 |  |  |  |  |  | my $paths_regex = join "|", @paths; | 
| 154 |  |  |  |  |  |  |  | 
| 155 | 0 |  |  |  |  |  | my $old_path; | 
| 156 | 0 |  |  |  |  |  | do { | 
| 157 | 0 |  |  |  |  |  | $old_path = $path; | 
| 158 | 0 |  |  |  |  |  | $path =~ s/^($paths_regex)[^$sep]*$//g; | 
| 159 | 0 |  |  |  |  |  | $path =~ s/^($paths_regex)[^$sep]*$sep//g; | 
| 160 | 0 |  |  |  |  |  | $path =~ s/$sep($paths_regex)[^$sep]*$//g; | 
| 161 | 0 |  |  |  |  |  | $path =~ s/$sep($paths_regex)[^$sep]*$sep/$sep/g; | 
| 162 |  |  |  |  |  |  | } until $path eq $old_path; | 
| 163 | 0 |  |  |  |  |  | return $path; | 
| 164 |  |  |  |  |  |  | } | 
| 165 |  |  |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | # Strips out all elements in arguments array up to and including $bre_name | 
| 167 |  |  |  |  |  |  | # command.  The first argument is index where the completion should look for the | 
| 168 |  |  |  |  |  |  | # word to be completed. | 
| 169 |  |  |  |  |  |  | sub strip_executable { | 
| 170 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 171 | 0 |  |  |  |  |  | my $index = shift; | 
| 172 |  |  |  |  |  |  |  | 
| 173 | 0 |  |  |  |  |  | my $cmd_pos = 0; | 
| 174 | 0 |  |  |  |  |  | foreach my $word (@_) { | 
| 175 | 0 |  |  |  |  |  | ++$cmd_pos; | 
| 176 | 0 |  |  |  |  |  | --$index; | 
| 177 | 0 | 0 |  |  |  |  | last if $word =~ /(^|\W)$brew_name$/; | 
| 178 |  |  |  |  |  |  | } | 
| 179 | 0 |  |  |  |  |  | return ($index, @_[$cmd_pos..$#_]) | 
| 180 |  |  |  |  |  |  | } | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  | =pod | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | Returns a list of completion candidates. | 
| 185 |  |  |  |  |  |  | This function takes two parameters: | 
| 186 |  |  |  |  |  |  |  | 
| 187 |  |  |  |  |  |  | =over 4 | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | =item * Index of the word to complete, 0-based. If C<-1> is passed then list of all commands is returned. | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | =item * A list of words already entered | 
| 192 |  |  |  |  |  |  |  | 
| 193 |  |  |  |  |  |  | =back | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | =cut | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  | sub _filter_candidates { | 
| 198 | 0 |  |  | 0 |  |  | my $self = shift; | 
| 199 | 0 |  |  |  |  |  | my $seed = shift; | 
| 200 |  |  |  |  |  |  | return | 
| 201 |  |  |  |  |  |  | # If a shell preserves ordering then put the prefix-mathing candidates first. I.e. for 'ver' 'version' would | 
| 202 |  |  |  |  |  |  | # precede 'rakudo-version' | 
| 203 | 0 |  |  |  |  |  | sort { index($a, $seed) cmp index($b, $seed) } | 
| 204 |  |  |  |  |  |  | grep { | 
| 205 | 0 |  |  |  |  |  | my $pos = index($_, $seed); | 
|  | 0 |  |  |  |  |  |  | 
| 206 | 0 |  |  |  |  |  | SUBSTRING_COMPLETION ? $pos >= 0 : $pos == 0 | 
| 207 |  |  |  |  |  |  | } @_ | 
| 208 |  |  |  |  |  |  | } | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | sub get_completions { | 
| 211 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 212 | 0 |  |  |  |  |  | my ($index, @words) = @_; | 
| 213 |  |  |  |  |  |  |  | 
| 214 | 0 |  |  |  |  |  | my @commands = qw(version current versions list global switch shell local nuke unregister rehash available list-available build register build-zef download exec which whence mode self-upgrade triple test home rakubrew-version); | 
| 215 |  |  |  |  |  |  |  | 
| 216 | 0 | 0 | 0 |  |  |  | if ($index <= 0) { # if @words is empty then $index == -1 | 
|  |  | 0 | 0 |  |  |  |  | 
|  |  | 0 | 0 |  |  |  |  | 
|  |  | 0 | 0 |  |  |  |  | 
|  |  | 0 | 0 |  |  |  |  | 
|  |  | 0 | 0 |  |  |  |  | 
|  |  | 0 | 0 |  |  |  |  | 
|  |  | 0 | 0 |  |  |  |  | 
|  |  | 0 | 0 |  |  |  |  | 
| 217 | 0 | 0 | 0 |  |  |  | my $candidate = $index < 0 || !$words[0] ? '' : $words[0]; | 
| 218 | 0 |  |  |  |  |  | my @c = $self->_filter_candidates($candidate, @commands, 'help'); | 
| 219 | 0 |  |  |  |  |  | return @c; | 
| 220 |  |  |  |  |  |  | } | 
| 221 |  |  |  |  |  |  | elsif($index == 1 && ($words[0] eq 'global' || $words[0] eq 'switch' || $words[0] eq 'shell' || $words[0] eq 'local' || $words[0] eq 'nuke' || $words[0] eq 'test')) { | 
| 222 | 0 |  |  |  |  |  | my @versions = get_versions(); | 
| 223 | 0 | 0 |  |  |  |  | push @versions, 'all'     if $words[0] eq 'test'; | 
| 224 | 0 | 0 |  |  |  |  | push @versions, '--unset' if $words[0] eq 'shell'; | 
| 225 | 0 |  | 0 |  |  |  | my $candidate = $words[1] // ''; | 
| 226 | 0 |  |  |  |  |  | return $self->_filter_candidates($candidate, @versions); | 
| 227 |  |  |  |  |  |  | } | 
| 228 |  |  |  |  |  |  | elsif($index == 1 && $words[0] eq 'build') { | 
| 229 | 0 |  | 0 |  |  |  | my $candidate = $words[1] // ''; | 
| 230 | 0 |  |  |  |  |  | return $self->_filter_candidates($candidate, (App::Rakubrew::Variables::available_backends(), 'all')); | 
| 231 |  |  |  |  |  |  | } | 
| 232 |  |  |  |  |  |  | elsif($index == 2 && $words[0] eq 'build') { | 
| 233 | 0 | 0 |  |  |  |  | my @installed = map { if ($_ =~ /^\Q$words[1]\E-(.*)$/) {$1} else { () } } get_versions(); | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 234 | 0 |  |  |  |  |  | my @installables = grep({ my $able = $_; !grep({ $able eq $_ } @installed) } App::Rakubrew::Build::available_rakudos()); | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 235 | 0 |  | 0 |  |  |  | my $candidate = $words[2] // ''; | 
| 236 | 0 |  |  |  |  |  | return $self->_filter_candidates($candidate, @installables); | 
| 237 |  |  |  |  |  |  | } | 
| 238 |  |  |  |  |  |  | elsif($index == 1 && $words[0] eq 'download') { | 
| 239 | 0 |  | 0 |  |  |  | my $candidate = $words[1] // ''; | 
| 240 | 0 |  |  |  |  |  | return $self->_filter_candidates($candidate, ('moar')); | 
| 241 |  |  |  |  |  |  | } | 
| 242 |  |  |  |  |  |  | elsif($index == 2 && $words[0] eq 'download') { | 
| 243 | 0 | 0 |  |  |  |  | my @installed = map { if ($_ =~ /^\Q$words[1]\E-(.*)$/) {$1} else { () } } get_versions(); | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 244 | 0 |  |  |  |  |  | my @installables = map { $_->{ver} } App::Rakubrew::Download::available_precomp_archives(); | 
|  | 0 |  |  |  |  |  |  | 
| 245 | 0 |  |  |  |  |  | @installables = grep { my $able = $_; !grep({ $able eq $_ } @installed) } @installables; | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 246 | 0 |  | 0 |  |  |  | my $candidate = $words[2] // ''; | 
| 247 | 0 |  |  |  |  |  | return $self->_filter_candidates($candidate, @installables); | 
| 248 |  |  |  |  |  |  | } | 
| 249 |  |  |  |  |  |  | elsif($index == 1 && $words[0] eq 'mode') { | 
| 250 | 0 |  |  |  |  |  | my @modes = qw(env shim); | 
| 251 | 0 |  | 0 |  |  |  | my $candidate = $words[2] // ''; | 
| 252 | 0 |  |  |  |  |  | return $self->_filter_candidates($candidate, @modes); | 
| 253 |  |  |  |  |  |  | } | 
| 254 |  |  |  |  |  |  | elsif($index == 2 && $words[0] eq 'register') { | 
| 255 | 0 |  |  |  |  |  | my @completions; | 
| 256 |  |  |  |  |  |  |  | 
| 257 | 0 |  |  |  |  |  | my $path = $words[2]; | 
| 258 | 0 |  |  |  |  |  | my ($volume, $directories, $file) = splitpath($path); | 
| 259 | 0 |  |  |  |  |  | $path = catdir($volume, $directories, $file); # Normalize the path | 
| 260 | 0 |  |  |  |  |  | my $basepath = catdir($volume, $directories); | 
| 261 | 0 | 0 |  |  |  |  | opendir(my $dh, $basepath) or return ''; | 
| 262 | 0 |  |  |  |  |  | while (my $entry = readdir $dh) { | 
| 263 | 0 |  |  |  |  |  | my $candidate = catdir($basepath, $entry); | 
| 264 | 0 | 0 |  |  |  |  | next if $entry =~ /^\./; | 
| 265 | 0 | 0 |  |  |  |  | next if substr($candidate, 0, length($path)) ne $path; | 
| 266 | 0 | 0 |  |  |  |  | next if !-d $candidate; | 
| 267 | 0 | 0 | 0 |  |  |  | $candidate .= '/' if length($candidate) > 0 && substr($candidate, -1) ne '/'; | 
| 268 | 0 |  |  |  |  |  | push @completions, $candidate; | 
| 269 |  |  |  |  |  |  | } | 
| 270 | 0 |  |  |  |  |  | closedir $dh; | 
| 271 | 0 |  |  |  |  |  | return @completions; | 
| 272 |  |  |  |  |  |  | } | 
| 273 |  |  |  |  |  |  | elsif($index == 1 && $words[0] eq 'help') { | 
| 274 | 0 |  | 0 |  |  |  | my $candidate = $words[1] // ''; | 
| 275 | 0 |  |  |  |  |  | my @topics = @commands; | 
| 276 | 0 |  |  |  |  |  | push @topics, '--verbose'; | 
| 277 | 0 |  |  |  |  |  | return $self->_filter_candidates($candidate, @topics); | 
| 278 |  |  |  |  |  |  | } | 
| 279 |  |  |  |  |  |  | } | 
| 280 |  |  |  |  |  |  |  | 
| 281 |  |  |  |  |  |  | 1; |