File Coverage

blib/lib/App/PS1.pm
Criterion Covered Total %
statement 21 127 16.5
branch 0 50 0.0
condition 0 25 0.0
subroutine 7 15 46.6
pod 8 8 100.0
total 36 225 16.0


line stmt bran cond sub pod time code
1             package App::PS1;
2              
3             # Created on: 2011-06-21 09:47:36
4             # Create by: Ivan Wills
5             # $Id$
6             # $Revision$, $HeadURL$, $Date$
7             # $Revision$, $Source$, $Date$
8              
9 1     1   81358 use strict;
  1         3  
  1         32  
10 1     1   6 use warnings;
  1         3  
  1         32  
11 1     1   5 use Carp qw/cluck/;
  1         2  
  1         76  
12 1     1   702 use Data::Dumper qw/Dumper/;
  1         7841  
  1         100  
13 1     1   634 use English qw/ -no_match_vars /;
  1         4213  
  1         9  
14 1     1   1276 use Term::ANSIColor;
  1         9220  
  1         152  
15 1     1   16 use base qw/Class::Accessor::Fast/;
  1         3  
  1         818  
16              
17             eval { require Term::Colour256 };
18             my $t256 = !$EVAL_ERROR;
19              
20             our $VERSION = 0.08;
21              
22             __PACKAGE__->mk_accessors(qw/ ps1 cols plugins bw low exit parts safe theme verbose/);
23              
24             my %theme = (
25             default => {
26             # name Low Colour Hi Colour
27             background => [ 'black' , 'on_52' ],
28             marker => [ 'black' , 246 ],
29             up_time => [ 'yellow', 'yellow' ],
30             up_label => [ 'black' , 'black' ],
31             branch => [ 'cyan' , 'cyan' ],
32             branch_label => [ 'black' , 'black' ],
33             date => [ 'red' , 'red' ],
34             face_happy => [ 'green' , 46 ],
35             face_sad => [ 'red' , 202 ],
36             dir_name => [ 'white' , 'white' ],
37             dir_label => [ 'black' , 'black' ],
38             dir_size => [ 'cyan' , 'cyan' ],
39             },
40             green => {
41             # name Low Colour Hi Colour
42             background => [ 'on_green', 'on_22' ],
43             marker => [ 'black' , 246 ],
44             up_time => [ 'yellow' , 'yellow' ],
45             up_label => [ 'black' , 'black' ],
46             branch => [ 'white' , 190 ],
47             branch_label => [ 'black' , 'black' ],
48             date => [ 'red' , 9 ],
49             face_happy => [ 'green' , 46 ],
50             face_sad => [ 'red' , 202 ],
51             dir_name => [ 'blue' , 21 ],
52             dir_label => [ 'black' , 'black' ],
53             dir_size => [ 'cyan' , 33 ],
54             },
55             blue => {
56             # name Low Colour Hi Colour
57             background => [ 'on_blue' , 'on_30' ],
58             marker => [ 'black' , 236 ],
59             up_time => [ 'yellow' , 'yellow' ],
60             up_label => [ 'black' , 'black' ],
61             branch => [ 'white' , 190 ],
62             branch_label => [ 'black' , 'black' ],
63             date => [ 'red' , 52 ],
64             face_happy => [ 'green' , 46 ],
65             face_sad => [ 'red' , 52 ],
66             dir_name => [ 'blue' , 21 ],
67             dir_label => [ 'black' , 'black' ],
68             dir_size => [ 'green' , 46 ],
69             },
70             );
71              
72             sub new {
73 0     0 1   my ($class, $params) = @_;
74 0           my $self = $class->SUPER::new($params);
75              
76 0 0         $self->safe( $ENV{UNICODE_UNSAFE} ) if !defined $self->safe;
77 0 0         $self->theme("default") if !defined $self->theme;
78              
79 0   0       $theme{ $self->theme } ||= {};
80 0           for my $name ( keys %{ $theme{ $self->theme } } ) {
  0            
81 0           my $env = $ENV{ 'APP_PS1_' . uc $name };
82 0 0         if ($env) {
83 0           $theme{ $self->theme }{$name} = [ ( $env ) x 2 ];
84             }
85             }
86              
87 0           return $self;
88             }
89              
90             sub sum(@) { ## no critic
91 0     0 1   my $i = 0;
92 0   0       $i += $_ || 0 for (@_);
93 0           return $i;
94             }
95              
96             sub cmd_prompt {
97 0     0 1   my ($self) = @_;
98 0           my $out = '';
99 0           $self->parts([]);
100              
101 0           for my $param ( split /;/, $self->ps1 ) {
102 0           my ( $plugin, $options ) = split /(?=[{])/, $param;
103 0 0         next if $plugin !~ /^[a-z]+$/;
104 0 0         next if !$self->load($plugin);
105              
106 0           $options = $self->parse_options($options, $plugin);
107 0           my ($text, $size) = eval { $self->$plugin($options) };
  0            
108              
109 0 0         if ($size) {
110 0           push @{$self->parts}, [ $text, $size ];
  0            
111             }
112             }
113              
114 0           my $total = $self->parts_size;
115 0           my $spare = $self->cols - $total;
116 0           my $spare_size = $spare / ( @{$self->parts} - 1 );
  0            
117              
118 0   0       while ($spare < 0 || $spare_size < 0) {
119 0           pop @{$self->parts};
  0            
120 0 0         if ( @{$self->parts} == 1 ) {
  0            
121 0           $total = $self->parts_size;
122 0           $spare = $self->cols - $total;
123 0           $spare_size = $spare;
124 0           last;
125             }
126 0           $total = $self->parts_size;
127 0           $spare = $self->cols - $total;
128 0 0         $spare_size = ( @{$self->parts} - 1 ) ? $spare / ( @{$self->parts} - 1 ) : 0;
  0            
  0            
129             }
130              
131 0 0         if ( $total <= $self->cols ) {
132 0           my $line = '';
133 0           my $extra = 0;
134 0           for my $i ( 0 .. @{$self->parts} - 2 ) {
  0            
135 0 0         my $div_first = $i ? 2 : 1;
136 0 0         my $div_second = $i == @{$self->parts} - 2 ? 1 : 2;
  0            
137 0           my $spaces;
138 0 0         if ( $total < $self->cols / 2 ) {
139 0           $spaces = ( $self->cols / ( @{$self->parts} - 1 ) - $self->parts->[$i][0] / $div_first - $self->parts->[$i + 1][0] / $div_second );
  0            
140             }
141             else {
142 0           $spaces = $spare_size;
143 0           $spare -= $spare_size - ( $spaces - int $spaces );
144             }
145 0           $extra += $spaces - int $spaces;
146              
147 0           $line .= $self->parts->[$i][1];
148 0           $line .= ' ' x $spaces;
149 0 0         if ( $extra > 1 ) {
150 0           $line .= ' ' x $extra;
151 0           $spare -= int $extra;
152 0           $extra = $extra - int $extra;
153             }
154             }
155 0 0         if ( $extra > 0.1 ) {
156 0           $line .= ' ';
157             }
158 0           $line .= $self->parts->[-1][1];
159              
160 0   0       my $colour = $ENV{APP_PS1_BACKGROUND} || 52;
161 0           $out = $self->colour('background') . $line . "\e[0m\n";
162             }
163              
164 0           return $out;
165             }
166              
167             sub parse_options {
168 0     0 1   my ($self, $options_txt, $name) = @_;
169              
170 0 0         return {} if !$options_txt;
171              
172 0           require JSON::XS;
173              
174 0           my $options = eval { JSON::XS::decode_json($options_txt) };
  0            
175 0           my $error = $@;
176              
177 0 0 0       if ($error && $self->verbose) {
178 0           cluck "Error reading $name\'s options ($options_txt)! $error\n";
179             }
180              
181 0   0       return $options || {};
182             }
183              
184             sub parts_size {
185 0     0 1   my ($self) = @_;
186 0           return sum map { $_->[0] } @{$self->parts};
  0            
  0            
187             }
188              
189             sub load {
190 0     0 1   my ($self, $plugin) = @_;
191              
192 0 0         $self->plugins({}) if !$self->plugins;
193              
194 0 0         return 1 if $self->plugins->{$plugin};
195              
196 0           my $module = 'App::PS1::Plugin::' . ucfirst $plugin;
197 0           my $file = 'App/PS1/Plugin/' . ( ucfirst $plugin ) . '.pm';
198 0           eval { require $file };
  0            
199 0 0         warn $@ if $@;
200 0 0         return 0 if $@;
201              
202 0           push @App::PS1::ISA, $module;
203              
204 0           return $self->plugins->{$plugin} = 1;
205             }
206              
207             sub surround {
208 0     0 1   my ($self, $count, $text) = @_;
209              
210 0 0 0       return if !defined $text || !$count;
211              
212 0 0         my $left = $self->safe ? '≺' : '<';
213 0 0         my $right = $self->safe ? '≻' : '>';
214              
215 0           $count += 2;
216 0           $text = $self->colour('marker') . "$left$text" . $self->colour('marker') . $right;
217 0           return ($count, $text);
218             }
219              
220             sub colour {
221 0     0 1   my ($self, $name) = @_;
222 0   0       my $colour = $theme{$self->theme}{$name} || [];
223             return
224 0 0 0       $self->bw || !$colour ? ''
    0 0        
225             : $t256 && !$self->low ? Term::Colour256::color($colour->[1])
226             : Term::ANSIColor::color($colour->[0]);
227             }
228              
229             1;
230              
231             __END__
232              
233             =head1 NAME
234              
235             App::PS1 - Module to load PS1 status line elements
236              
237             =head1 VERSION
238              
239             This documentation refers to App::PS1 version 0.08.
240              
241             =head1 SYNOPSIS
242              
243             # in your ~/.bashrc file
244             export APP_PS1='face;branch;date;direcory;perl;node;ruby;uptime'
245             export PS1="\[\`app-ps1 -e\$?\`\]\n\u@\h \\\$ "
246              
247             =head1 DESCRIPTION
248              
249             This is the engine for the C<app-ps1> command.
250              
251             =head1 SUBROUTINES/METHODS
252              
253             =head3 C<new ( $param_hash )>
254              
255             Param: C<ps1> Str What plugins to show on the prompt
256             Param: C<low> Bool Use low (16 bit colour)
257             Param: C<bw> Bool Don't use any colour (black and white)
258             Param: C<theme> Str Use colour theme
259             Param: C<exit> Int The last program's exit code
260             Param: C<cols> Int The number of columns wide to assume the terminal is
261              
262             Return: App::PS1 - A new object
263              
264             Description:
265              
266             =head3 C<sum ( @list )>
267              
268             Adds the values in list and returns the result.
269              
270             =head3 C<cmd_prompt ()>
271              
272             Display the command prompt
273              
274             =head3 C<parts_size ()>
275              
276             calculate the size of the prompt parts
277              
278             =head3 C<load ()>
279              
280             Load plugins
281              
282             =head3 C<surround ()>
283              
284             Surround the text with brackets
285              
286             =head3 C<colour ($name)>
287              
288             Get the theme colour for C<$name>
289              
290             =head3 C<parse_options ($options)>
291              
292             Parses the JSON $options txt.
293              
294             =head1 DIAGNOSTICS
295              
296             =head1 CONFIGURATION AND ENVIRONMENT
297              
298             Lots of environment variables are used to configure the command prompt
299              
300             =over 4
301              
302             =item C<$APP_PS1>
303              
304             Sets the elements to be displayed (overridden by C<--ps1>)
305              
306             Default 'face;branch;date;directory;uptime',
307              
308             =item C<$APP_PS1_THEME>
309              
310             Sets the colour theme for the prompt
311              
312             =over 4
313              
314             =item *
315              
316             default
317              
318             =item *
319              
320             green
321              
322             =item *
323              
324             blue
325              
326             =back
327              
328             Default 'default',
329              
330             =item C<$PS1_COLS>
331              
332             If L<Term::Size::Any> is not installed you can configure the width of your
333             screen by setting this parameter.
334              
335             Default 90,
336              
337             =item C<$UNICODE_UNSAFE>
338              
339             If set to a true value this will allow UTF8 characters to be used displaying
340             the prompt
341              
342             Default not set
343              
344             =item C<$APP_PS1_BACKGROUND>
345              
346             Set the line's background colour
347              
348             Default 52
349              
350             =back
351              
352             =head1 DEPENDENCIES
353              
354             =head1 INCOMPATIBILITIES
355              
356             =head1 BUGS AND LIMITATIONS
357              
358             There are no known bugs in this module.
359              
360             Please report problems to Ivan Wills (ivan.wills@gmail.com).
361              
362             Patches are welcome.
363              
364             =head1 AUTHOR
365              
366             Ivan Wills - (ivan.wills@gmail.com)
367              
368             =head1 LICENSE AND COPYRIGHT
369              
370             Copyright (c) 2011 Ivan Wills (14 Mullion Close, Hornsby Heights, NSW, Australia 2077)
371             All rights reserved.
372              
373             This module is free software; you can redistribute it and/or modify it under
374             the same terms as Perl itself. See L<perlartistic>. This program is
375             distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
376             without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
377             PARTICULAR PURPOSE.
378              
379             =cut