File Coverage

lib/Devel/Trepan/Interface/User.pm
Criterion Covered Total %
statement 84 124 67.7
branch 18 56 32.1
condition 2 17 11.7
subroutine 18 25 72.0
pod 0 11 0.0
total 122 233 52.3


line stmt bran cond sub pod time code
1             # -*- coding: utf-8 -*-
2             # Copyright (C) 2011-2012, 2014-2016 Rocky Bernstein <rocky@cpan.org>
3             # Interface when communicating with the user.
4              
5 13     13   50180 use warnings; no warnings 'redefine';
  13     13   40  
  13         524  
  13         80  
  13         32  
  13         489  
6 13     13   95 use Exporter;
  13         35  
  13         520  
7              
8 13     13   77 use rlib '../../..';
  13         33  
  13         113  
9              
10             package Devel::Trepan::Interface::User;
11 13     13   5274 use vars qw(@EXPORT @ISA);
  13         37  
  13         890  
12              
13 13     13   4423 use if !@ISA, Devel::Trepan::Util; # qw(hash_merge YN);
  13         114  
  13         102  
14 13     13   2583 use if !@ISA, Devel::Trepan::IO::Input;
  13         36  
  13         72  
15 13     13   2502 use if !@ISA, Devel::Trepan::Interface;
  13         40  
  13         107  
16              
17             @ISA = qw(Devel::Trepan::Interface Exporter);
18 13     13   1028 use strict;
  13         32  
  13         586  
19             # Interface when communicating with the user.
20              
21 13         8227 use constant DEFAULT_USER_OPTS => {
22              
23             readline => # Try to use Term::ReadLine?
24             $Devel::Trepan::IO::Input::HAVE_TERM_READLINE,
25              
26             # The below are only used if we want and have readline support.
27             # See method Trepan::term_readline below.
28             histsize => 256, # Use gdb's default setting
29             file_history => '.trepanpl_hist', # where history file lives
30             # Note a directory will
31             # be appended
32             history_save => 1 # do we save the history?
33 13     13   77 };
  13         34  
34              
35             sub new
36             {
37 13     13   512 my($class, $inp, $out, $opts) = @_;
38 13         97 $opts = hash_merge($opts, DEFAULT_USER_OPTS);
39 13         133 my $self = Devel::Trepan::Interface->new($inp, $out, $opts);
40 13         95 $self->{opts} = $opts;
41 13         44 bless $self, $class;
42 13 50 66     119 if ($inp && $inp->isa('Devel::Trepan::IO:InputBase')) {
43 0         0 $self->{input} = $inp;
44             } else {
45             $self->{input} = Devel::Trepan::IO::Input->new($inp,
46             {readline => $opts->{readline}})
47 13         93 }
48 13 50       510 if ($self->{input}{term_readline}) {
49 13 50       179 if ($self->{opts}{complete}) {
50 0         0 my $attribs = $inp->{readline}->Attribs;
51 0         0 $attribs->{attempted_completion_function} = $self->{opts}{complete};
52             }
53 13         163 $self->read_history();
54             }
55 13         141 return $self;
56             }
57              
58             sub add_history($$)
59             {
60 0     0 0 0 my ($self, $command) = @_;
61             return unless (($self->{input}{readline})
62 0 0 0     0 and $self->{input}{readline}->can('add_history'));
63 0         0 $self->{input}{readline}->add_history($command) ;
64              
65 0 0       0 if ($self->can('add_history_time')) {
66 0         0 my $now = localtime;
67 0         0 $self->{input}{readline}->add_history_time($now);
68             }
69              
70             # Having problems with setting destroy to write history.
71             # So write it after each add. Ugh.
72             # Use Term::ReadLine::Gnu name WriteHistory, since Gnu doesn't have
73             # write_history().
74 0 0       0 $self->{input}{readline}->WriteHistory($self->{histfile}, $command)
75             if $self->can('WriteHistory');
76             }
77              
78             sub remove_history($;$)
79             {
80 8     8 0 20 my ($self, $which) = @_;
81 8 50       23 $which = -1 unless defined($which);
82 8 50       49 return unless ($self->{input}{readline});
83 8 50       52 if ($self->{input}{readline}->can("where_history")) {
84 0         0 my $where_history = $self->{input}{readline}->where_history();
85 0 0       0 $which = $where_history unless defined $which;
86             }
87             $self->{input}{readline}->remove_history($which) if
88 8 50       61 $self->{input}{readline}->can("remove_history");
89             }
90              
91             sub is_closed($)
92             {
93 0     0 0 0 my($self) = shift;
94 0 0       0 $self->{input}->is_eof && $self->{output}->is_eof;
95             }
96              
97             # Called when a dangerous action is about to be done, to make
98             # sure it's okay. Expect a yes/no answer to `prompt' which is printed,
99             # suffixed with a question mark and the default value. The user
100             # response converted to a boolean is returned.
101             # FIXME: make common routine for this and server.rb
102             sub confirm($$$) {
103 10     10 0 5842 my($self, $prompt, $default) = @_;
104 10 100       31 my $default_str = $default ? 'Y/n' : 'N/y';
105 10         13 my $response;
106 10         20 while (1) {
107 10         85 $response = $self->readline(sprintf '%s (%s) ', $prompt, $default_str);
108 10 50       86 return $default if $self->{input}->is_eof;
109 10         25 chomp($response);
110 10 100       31 return $default if $response eq '';
111 8         52 ($response = lc(unpack("A*", $response))) =~ s/^\s+//;
112             # We don't catch "Yes, I'm sure" or "NO!", but I leave that
113             # as an exercise for the reader.
114 8 50       151 last if grep(/^${response}$/, @Devel::Trepan::Util::YN);
115 0         0 $self->msg( "Please answer 'yes' or 'no'. Try again.");
116             }
117 8         34 $self->remove_history;
118 8         315 return grep(/^${response}$/, YES);
119             }
120              
121 13     13   99 use File::Spec;
  13         32  
  13         6582  
122              
123             # Read a saved Readline history file into Readline. The history
124             # file will be created if it doesn't already exist.
125             # Much of this code follows what's done in ruby-debug.
126             sub read_history($)
127             {
128 13     13 0 56 my $self = shift;
129 13         55 my %opts = %{$self->{opts}};
  13         235  
130 13 50       124 unless ($self->{histfile}) {
131 13   0     138 my $dirname = $ENV{'HOME'} || $ENV{'HOMEPATH'} || glob('~');
132 13         425 $self->{histfile} = File::Spec->catfile($dirname, $opts{file_history});
133             }
134 13 50       136 my $histsize = $ENV{'HISTSIZE'} ? $ENV{'HISTSIZE'} : $opts{histsize};
135 13 50       110 $self->{histsize} = $histsize unless defined $self->{histsize};
136 13 50       550 if ( -f $self->{histfile} ) {
137             $self->{input}{readline}->StifleHistory($self->{histsize}) if
138 0 0       0 $self->{input}{readline}->can("StifleHistory");
139             $self->{input}{readline}->ReadHistory($self->{histfile}) if
140 0 0       0 $self->{input}{readline}->can("ReadHistory");
141             }
142             }
143              
144             sub is_interactive($)
145             {
146 0     0 0 0 my $self = shift;
147 0         0 $self->{input}->is_interactive;
148             }
149              
150             sub rl_filename_list($$)
151             {
152 0     0 0 0 my ($self,$prefix) = @_;
153 0         0 $self->{input}->rl_filename_list($prefix);
154             }
155              
156             sub has_completion($)
157             {
158 26     26 0 67 my $self = shift;
159 26         166 $self->{input}{term_readline};
160             }
161              
162             sub want_term_readline($)
163             {
164 0     0 0 0 my $self = shift;
165 0 0       0 defined($self->{opts}{readline}) && $self->{input}{term_readline};
166             }
167              
168             # read a debugger command
169             sub read_command($;$) {
170 0     0 0 0 my($self, $prompt) = @_;
171 0 0       0 $prompt = '(trepanpl) ' unless defined $prompt;
172 0         0 my $last = $self->readline($prompt);
173 0         0 my $line = '';
174 0         0 $prompt .= '>> '; # continuation
175 0   0     0 $last ||= '';
176 0   0     0 while ($last && '\\' eq substr($last, -1)) {
177 0         0 $line .= substr($last, 0, -1) . "\n";
178 0         0 $last = $self->readline($prompt);
179             }
180 0 0       0 $line .= $last if defined $last;
181 0         0 return $line;
182             }
183              
184             sub readline($;$) {
185 0     0   0 my($self, $prompt) = @_;
186 0         0 $self->{output}->flush;
187 0 0       0 if ($self->want_term_readline) {
188 0         0 $self->{input}->readline($prompt);
189             } else {
190 0 0 0     0 $self->{output}->write($prompt) if defined($prompt) && $prompt;
191 0         0 $self->{input}->readline;
192             }
193             }
194              
195             sub set_completion($$$)
196             {
197 13     13 0 62 my ($self, $completion_fn, $list_completion_fn) = @_;
198 13 50       55 return unless $self->has_completion;
199 13         142 my $attribs = $self->{input}{readline}->Attribs;
200              
201             # Silence "used only once warnings" inside ReadLine::Term::Perl.
202 13     13   102 no warnings 'once';
  13         32  
  13         5787  
203 13         95 $readline::rl_completion_entry_function = undef;
204 13         46 $readline::rl_attempted_completion_function = undef;
205              
206 13         206 $attribs->{completion_entry_function} = $list_completion_fn;
207              
208             # For Term:ReadLine::Gnu
209 13         384 $attribs->{attempted_completion_function} = $completion_fn;
210              
211             # For Term::ReadLine::Perl
212 13         172 $readline::rl_completion_function = $completion_fn;
213 13         64 $attribs->{completion_function} = $completion_fn;
214             }
215              
216             # Demo
217             unless (caller) {
218             my $intf = Devel::Trepan::Interface::User->new;
219             $intf->msg("Hi, there!");
220             $intf->errmsg("Houston, we have a problem here!");
221             $intf->errmsg(['Two', 'lines']);
222             printf "Is interactive: %s\n", ($intf->is_interactive ? "yes" : "no");
223             printf "Has completion: %s\n", ($intf->has_completion ? "yes" : "no");
224              
225             my $save_term_readline = $HAVE_TERM_READLINE;
226             foreach my $term (qw(Gnu Perl5)) {
227             $HAVE_TERM_READLINE = $term;
228             my $path='./';
229             my @files = $intf->rl_filename_list($path);
230             printf "term: %s, path: %s\n", $term, $path;
231             foreach my $file (@files) {
232             print "\t$file\n";
233             }
234             }
235             $HAVE_TERM_READLINE = $save_term_readline;
236              
237             if (scalar(@ARGV) > 0 && $intf->is_interactive) {
238             my $line = $intf->readline("Type something: ");
239             if ($intf->is_input_eof) {
240             $intf->msg("No input, got EOF\n");
241             } else {
242             $intf->msg("You typed: $line");
243             }
244             $intf->msg(sprintf "input EOF is now: %d", $intf->{input}->is_eof);
245             unless ($intf->{input}->is_eof) {
246             $intf->msg("Now we in read a command");
247             my $line = $intf->read_command("Type a command something: ");
248             if ($intf->is_input_eof) {
249             $intf->msg("No input, got EOF");
250             } else {
251             $intf->msg("You typed: $line");
252             }
253             unless ($intf->is_input_eof) {
254             $line = $intf->confirm("Are you sure", 0);
255             chomp($line);
256             $intf->msg("you typed: ${line}");
257             $intf->msg(sprintf "eof is now: %d", $intf->{input}->is_eof);
258             $line = $intf->confirm("Really sure", 0);
259             $intf->msg("you typed: $line");
260             $intf->msg(sprintf "eof is now: %d", $intf->{input}->is_eof);
261             }
262             }
263             }
264             printf "User interface closed?: %d\n", $intf->is_closed;
265             $intf->close;
266             # Note STDOUT is closed
267             printf STDERR "User interface closed?: %d\n", $intf->is_closed;
268              
269             }
270              
271             1;