Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

podwatch: POD previewing tool

by adrianh (Chancellor)
on Jan 17, 2003 at 23:21 UTC ( [id://227856]=sourcecode: print w/replies, xml ) Need Help??
Category: Utility Scripts
Author/Contact Info Adrian Howard
/msg adrianh
Description:

When I started writing POD documents I got bored with previewing things with perldoc. Every time I made an edit I had to start up perldoc again, find where I had made the change, only to discover it wasn't quite right. Repeat until done.

To make this a little easier I threw together podwatch. A (very basic) POD viewer that tracks changes to the source file. When the source changes podwatch reads it in again, and moves to the place where the output changed.

Now I edit POD in one window, with podwatch running in another. Instant feedback whenever I hit save.

Hope you find it useful.

#! /usr/bin/perl

# podwatch FILE
#
# adrianh@quietstars.com
# 20020520
#
# Copyright 2002-2003 Adrian Howard, All Rights Reserved.
#
# This program is free software; you can redistribute it 
# and/or modify it under the same terms as Perl itself.
#
# 20020522
# - now moves changed line to the top on refresh
# - refactored move_by out from command subs
# - added help
# 20030117
# - tided up formatting a bit
# - added beep
# 20030118
# - added ReadMode 0 on quit (thanks to castaway)

use strict;
use warnings;
use Term::ReadKey;
use Pod::Text;
use IO::String;

$|=1;
ReadMode 3;
my $Filename = $ARGV[-1];

my $Help = <<'END_HELP';

===================================

SUMMARY OF KEYS

return  -   forward one line

space   -   forward one page
b       -   back one page

g       -   go to first line in file
G       -   go to last line in file

/       -   search

r       -   refresh page

q       -   quit

===================================

END_HELP

my $Status_line = "(%5d) $Filename - h:elp q:uit";
my $Line_num = 0;
my $Modification_time = -1;
my $Page_length = -1;
my $Column_num = -1;
my @Pod = ();
my $Search = '';

my %Command = (
    "undef" => \&beep,
    "\n"    => \&next_line,
    " "     => \&next_page,
    "b"     => \&prev_page,
    "g"     => \&end,
    "G"     => \&top,
    "/"     => \&search,
    "r"     => \&refresh,
    "h"     => \&help,
    "q"     => \&quit,
);

while (1) {
    refresh() if changed();
    my $key = ReadKey 0.25;
    next unless $key;
    &{$Command{$key} || $Command{'undef'}};
};

###########

sub beep { 
    print "\a"
};

sub next_line   { move_by(+1) };
sub next_page   { move_by(+$Page_length) };
sub prev_page   { move_by(-$Page_length) };
sub top         { move_by(-$Line_num) };
sub end         { move_by($#Pod) };

sub help { 
    clear_status();
    print $Help;
    print status_line();
};

sub search {
    clear_status();
    print "/";
    ReadMode 0;
    my $input = <STDIN>;
    ReadMode 3;
    chomp($input);
    $Search = $input eq '' ? $Search : $input;
    my $final_line_num = $Line_num;
    foreach my $n ($Line_num+1 .. $#Pod) {
        if (eval {$Pod[$n] =~ /$Search/}) {
            $final_line_num = $n;
            last;
        };
    };
    beep() if $Line_num == $final_line_num;
    inc_line($final_line_num-$Line_num);
    show_page();
};

sub quit {
    print "\n";
    ReadMode 0;
    exit(0);
};

###########

sub clear_status {
    print "\r", " " x length(status_line()), "\r";
};

sub status_line {
    return(substr(sprintf($Status_line, $Line_num+1), 0, $Column_num))
+;
};

sub last_line {
    return($Line_num + $Page_length - 1);
};

sub print_line {
    my $line_num = shift;
    my $line = $Pod[$line_num];
    $line = "" unless defined($line);
    print "\r$line";
    my $status_line = status_line();
    my $overlap = length($status_line) - length($line);
    print " " x $overlap if $overlap > 0;
    print "\n", $status_line;
};

sub inc_line($) {
    my $n = shift;
    my $start_line = $Line_num;
    $Line_num += $n;
    if ($Line_num < 0) {
        $Line_num = 0
    } elsif ($Line_num > $#Pod){
        $Line_num = $#Pod;
    };
    return($start_line != $Line_num);
}

sub show_page {
    print_line($_) foreach ($Line_num..last_line());
};

sub move_by {
    my $n = shift;
    if ($n < 0 || $n >= $Page_length) {
        show_page() if inc_line($n);
    } else {
        foreach (1..$n) {
            return unless inc_line(1);
            print_line( last_line() ) 
        };
    };
};

###########

sub modification_time {
    my $filename = shift;
    return( (stat $filename)[9] || die "$filename ($!)\n" );
};

sub page_size {
    my ($cols, $rows) = GetTerminalSize *STDOUT{IO};
    die "cannot determine terminal size" unless $cols && $rows;
    return($cols, $rows-1);
};

sub changed {
    my ($cols, $rows) = page_size();
    return(
        $Modification_time != modification_time($Filename) 
        || $Page_length != $rows || $Column_num != $cols
    );
};

sub fetch_pod {
    my $filename = shift;
    my $pod = IO::String->new;
    open(POD, $filename) or die "$Filename ($!)\n";
    Pod::Text->new(
        width => ($Column_num > 76 ? 76 : $Column_num)
    )->parse_from_filehandle(*POD{IO}, $pod);
    close(POD);
    return(${$pod->string_ref});
};

sub refresh {
    ($Column_num, $Page_length) = page_size();
    $Modification_time = modification_time($Filename);
    my @old_pod = @Pod; 
    @Pod = split(/\n/, fetch_pod($Filename));
    LINE: foreach my $n (0..$#Pod) {
        my ($line, $old_line) = ($Pod[$n], $old_pod[$n]);
        unless (defined($old_line) && $line eq $old_line) {
            $Line_num = $n if ($Line_num > $n || $n > last_line());
            last LINE;
        };
    };
    inc_line(0);
    show_page();
    beep();
};
Replies are listed 'Best First'.
Re: podwatch: POD previewing tool
by castaway (Parson) on Jan 18, 2003 at 13:53 UTC
    Very nice :)
    Can I suggest a
    ReadMode 0;
    in the quit sub? It messes up my terminal else..
    C.
      Good point. Fixed. Thanks.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: sourcecode [id://227856]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (3)
As of 2024-04-24 17:55 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found