Here's what I've learned since posting snippet browser.
There are numerous improvements based on bbfu's comments,
in that thread, but lots of things done for which I hardly know the
consequences. Criticism (as well as generosity) would be
appreciated. (Trying the READMORE tag - here goes...nothing?)
It's more like a full program now - and I'm pleased to
say, it took less time to write this, than it did to write the
original version (thanks to obsessively reading
all of you ).
N.B. again, that this probably only runs on Linux. Also,
as in the previous version, as written it requires a $SANDBOX environment
variable, to designate a subdirectory under $HOME.
Thank you for indulging a beginner using you
all as a sounding board.
New stuff revised Sat Jan 20 at 04:16
- cleaned up a few things following tilly's advice below (for which I'm very grateful).
- Improved subroutines for color.
- Change menu colors.
- You can now specify the editor, and a subroutine called
which looks it up in your $PATH. Defaults to "less"
if you enter something bogus, by calling which recursively.
- Has a menued console interface - with color!
- The menu is hideable.
- Arbitrarily select snippets by designating an array index.
- Change the Sandbox directory interactively (using
a hidden option: type "SANDBOX".)
- Display a snippet to the screen or
- execute the snippet from the menu.
#!/usr/bin/perl -w
use strict;
my $EDITOR = 'elvis';
my $index = @ARGV ? shift(@ARGV) :0;
my $clear = 1;
my $hide_help = 0;
my $homedir = $ENV{HOME};
my $sandbox = $ENV{SANDBOX};
my $STUDYPATH = "$homedir/$sandbox";
$EDITOR = which($EDITOR);
my $color = colorize('GREEN');
my $reset = colorize('reset');
while (1){
my @files = glob "$STUDYPATH/*";
system ('clear') unless $clear == 0;
$clear++;
$index =~ s/[A-Za-z]/0/g;
$index = 0 if $index < (0-$#files)-1 || $index > $#files;
printf ( "%50s <- (%5s)\n", $files[$index], $index);
print "
",$color,"[C]",$reset,"hange editor. Currently $EDITOR
",$color,"[E]",$reset,"dit to open the snippet in an editor
",$color,"[G]",$reset,"oto to select file index: -", ($#files+1),"
+ to $#files
",$color,"[H]",$reset,"elp to (un)hide this menu
",$color,"[N]",$reset,"ext to skip this snippet
",$color,"[R]",$reset,"edo to revisit the previous snippet
co",$color,"[L]",$reset,"or to change the menu item color
",$color,"[Q]",$reset,"UIT to exit
",$color,"[T]",$reset,"ext to change the menu text color
e",$color,"[X]",$reset,"ecute
Any other key +",$color," <ENTER> ",$reset,"to print out the snipp
+et
-> "
unless $hide_help==1;
my $input = <>;
if ($input =~ /^N/i){
++$index;
next;
}elsif($input =~ /^G/i){
print "enter a number from -",$#files+1," to $#files
-> ";
$index = <>;
chomp $index;
}elsif($input =~ /^H/i){
$hide_help = $hide_help?0:1;
}elsif($input =~ /^C/i){
$EDITOR = <>;
chomp $EDITOR;
$EDITOR = which ($EDITOR);
# redo STUDY;
}elsif($input =~ /^R/i){
--$index;
}elsif($input =~ /^L/i){
print "change menu item color to: ";
my $newcolor = <>;
chomp $newcolor;
$color = colorize($newcolor);
}elsif($input =~ /^T/i){
print "change menu text color to: ";
my $textcolor = <>;
chomp $textcolor;
$reset = colorize($textcolor);
}elsif($input =~ /^(SANDBOX)/i){
print "$homedir/";
my $sandbox = <>;
chomp $sandbox;
$STUDYPATH = change_snippet_dir($sandbox);
}elsif($input =~ /^E/i){
system ($EDITOR,$files[$index]);
}elsif($input =~ /^X/i){
do $files[$index];
print "\n";
$clear = 0;
}elsif($input =~ /^Q/i){
last;
}else{
$clear=0;
open (SCRIPT,"< $files[$index]")
or warn "can't open file: $!";
print while <SCRIPT>;
}
}
sub which{
my $editor = shift;
foreach (my @PATH = split(":",$ENV{PATH}))
{
if (-x "$_/$editor")
{
return "$_/$editor";
last;
}
}
which ('less') if not -x "$editor";
}
sub colorize{
my $a = shift;
my $newcolor;
eval "use Term::ANSIColor 'color'";
return if $@;
for (my @valid_colors = split /,/ => qq(
blue bold,yellow bold,reset,
green bold,red bold,white,
bold blue,bold yellow,bold white,
bold green,bold red,
red,green,yellow,blue))
{
s/^\s+//s;
if ( $a =~ /($_)/i)
{
return $newcolor = color($_);
}
}
}
sub change_snippet_dir{
my $sandbox = shift;
my $STUDYPATH = "$homedir/$sandbox";
-d $STUDYPATH ?
return $STUDYPATH:
return "$ENV{HOME}/$ENV{SANDBOX}";
}
Re (tilly) 1: newer snippet browser
by tilly (Archbishop) on Jan 20, 2001 at 19:08 UTC
|
use strict;
use Config;
use vars qw(%Config);
print "My default pager is $Config{pager}\n";
That will make your code substantially more customizable.
And if someone doesn't have less on their system (such
do exist) they won't go directly into hard recursion.
In the same vein I suggest having the editor be not just
hard-coded but specifiable with an environment variable
and possibly also with a command-line option. I usually
use Getopt::Std for the latter. I would do the same
with the color.
Speaking of the color, it is good that you handle the
case where you cannot load colors, but I would suggest
a brief message. Also I am wondering at your attempt to
dictate valid colors. Why not just throw the color and
then trap errors? Like this:
sub colorize {
# Autoload here
eval {require Term::ANSIColor};
if ($@ =~ /locate Term::ANSIColor/) {
print "Cannot locate Term::ANSIColor.\n",
"Color support disabled\n";
return;
}
elsif ($@) {
die $@;
}
my $color = shift;
my $escape_code = eval {color($color)};
if ($@) {
print "Invalid color '$color'\n";
return color('reset');
}
else {
return $escape_code;
}
}
Next, why is your change_snippet_dir
function inside your loop? Putting it there doesn't
do anything useful.
Another detail. Should the sandbox be a directory with
spaces in the path name, your glob is going to run into
problems with shell semantics. I would suggest going
with opendir and readdir - less danger of breakage.
Now a bigger change. Your main loop is very simple in
concept. However you now have a manual syncronization
job between printing your menu and writing your loop. This
is going to be a problem. Instead I would suggest that
you take a page off of Code that writes code and write the stuff
currently in the body of your loop as structured text.
Extract that into the real loop, then eval it.
I would strongly recommend that if you go this way you test
your eval for $@, and if there are errors dump the full
text of the code you created.
Alternately you can write the information for the menu
and the handlers in two hashes, then run an automatic
check at load that the two data structures are in sync.
Then you could, say, dispatch to anonymous subroutines
like this:
my ($case, $data) =(split /\s/, $input, 2;
$case = lc($case);
if (exists $handler{$case}) {
$handler{$case}->($data);
}
else {
do_default($input);
}
There are also ways to combine the two approaches. One of
the nicest is to use the fact that nested Perl data
structures already look a lot like a structured data
format. Take a look at the output of Data::Dumper
to see what I mean. (I prefer $Data::Dumper::Indent set
to 1, YMMV.) So you can have something like this:
@cases = [
# etc
{
case => 'l',
desc => 'co_or to change the menu item color',
func => sub {
print "change menu item color to: ";
my $newcolor=<>;
chomp $newcolor;
$color=colorize($newcolor);
},
},
# etc
};
Think you can reconstruct your loop from that? :-)
Any way you cut it, a long-term maintainance problem is
going to be keeping the description in the menu in sync
with what the loop does. If your program will (and you
know it will) remain small, then solving that is unneeded
work. But if there is a good chance it will grow or change
over time, restructuring so that the information that
belongs together will stay together is worthwhile.
Also a point that is too often forgotten is that while the
work may not have been needed, being agressive about doing
this kind of thing on small projects stretches you so that
you have the skills when you need it later... | [reply] [d/l] [select] |
(tye)Re: newer snippet browser
by tye (Sage) on Jan 20, 2001 at 22:54 UTC
|
#!/usr/bin/perl -w
=head Changes
Be more consistant with your spacing.
I don't see the use for which() since you are just duplicating code th
+at
the kernel will do but less portably (under Win32, you need to spl
+it
on ';', not on ':').
Used a more "standard" method for picking an editor.
Since you only have one enclosing loop, you don't need the STUDY: labe
+l,
but I don't mind you keeping it since it makes it easy to find tha
+t loop.
On non-Unix systems, $ENV{HOME} may not be set.
Declaring subs in the middle of code just makes them hard to find.
Added a default for $ENV{SANDBOX}
s/[A-Za-z]/0/g more efficient as tr/A-Za-z/0/
Allow easy highlight with a color other than 'GREEN'
Replace repeated highlighting code with a subroutine
Treat $hide_help and $clear as Booleans instead of integers
Your first "next" doesn't do anything (I just commented it out)
Your first "redo" doesn't do anything (commented out)
Don't have a [my] variable with the same name as a global, STUDYPATH
close SCRIPT when you are done with it
=cut
use strict;
use Term::ANSIColor 'color';
my $EDITOR = $ENV{VISUAL} || $ENV{EDITOR} || $ENV{EDIT}
or $^O =~ /^MSWin/ ? 'notepad' : 'vi';
my $index = @ARGV ? shift(@ARGV) :0;
my $clear = 1;
my $hide_help = 0;
my $highlight = $ENV{STUDYCOLOR} || 'GREEN';
my $homedir = $ENV{HOME} || '.';
my $sandbox = $ENV{SANDBOX} || 'sandbox';
my $STUDYPATH = "$homedir/$sandbox";
sub change_snippet_dir{
my $sandbox= shift;
my $path= "$homedir/$sandbox";
if( -d $path ) {
$STUDYPATH= $path;
}
}
sub highlight {
return color($highlight), @_, color('reset');
}
STUDY:
while (1) {
my @files= glob "$STUDYPATH/*";
if( $clear ) {
system ('clear');
} else {
$clear++;
}
$index =~ tr/A-Za-z/0/;
$index= 0 if $index < (0-$#files)-1 || $index > $#files;
printf( "%50s <- (%5s)\n", $files[$index], $index );
print "
",highlight("[C]"),"HANGE editor. Currently $EDITOR
",highlight("[E]"),"DIT to open the snippet in an editor
",highlight("[G]"),"oTo to select file index: -", ($#files+1)," to
+ $#files
",highlight("[H]"),"ELP to (un)hide this menu
",highlight("[N]"),"EXT to skip this snippet
",highlight("[R]"),"EDO to revisit the previous snippet
",highlight("[Q]"),"UIT to exit
e",highlight("[X]"),"ecute
Any other key +",highlight(" [ENTER] "),"to print out the snippet
-> "
unless $hide_help;
my $input = <>;
if ($input =~ /^N/i){
++$index;
# next; This doesn't do anything
}elsif($input =~ /^G/i){
print "enter a number from -",$#files+1," to $#files
-> ";
$index = <>;
chomp $index;
}elsif($input =~ /^H/i){
$hide_help= ! $hide_help;
}elsif($input =~ /^C/i){
$EDITOR = <>;
chomp $EDITOR;
# redo STUDY;
}elsif($input =~ /^R/i){
--$index;
}elsif($input =~ /^(SANDBOX)/i){
print "$homedir/";
my $sandbox=<>;
chomp $sandbox;
change_snippet_dir($sandbox);
}elsif($input =~ /^E/i){
system( $EDITOR, $files[$index] );
}elsif($input =~ /^X/i){
do $files[$index];
print "\n";
$clear= 0;
}elsif($input =~ /^Q/i){
last;
}else{
$clear= 0;
open (SCRIPT,"< $files[$index]")
or warn "can't open file: $!";
print while <SCRIPT>;
close SCRIPT;
}
}
-
tye
(but my friends call me "Tye") | [reply] [d/l] |
|
This is just terrific stuff, tye. Like tilly's criticisms, I understand what you wrote,
and I can agree with everything you said. I'm grateful
that you both took so much time to make detailed comments.
I'll try to make good use
of your advice, not just in improving this simple program but
in thinking more carefully about portability, maintainability,
and choosing the appropriate data structures.
Hopefully the light will come up, little by little, and
you'll see some smarter programs from me over time!
mkmcconn
| [reply] |
|
|