Term-EditorEdit-0.0016000755000765000024 011604637310 13610 5ustar00robstaff000000000000README000644000765000024 523211604637310 14552 0ustar00robstaff000000000000Term-EditorEdit-0.0016NAME Term::EditorEdit - Edit a document via $EDITOR VERSION version 0.0016 SYNOPSIS use Term::EditorEdit; # $VISUAL or $EDITOR is invoked $document = Term::EditorEdit->edit( document => <<_END_ ); Apple Banana Cherry _END_ With post-processing: $document = Term::EditorEdit->edit( document => $document, process => sub { my $edit = shift; my $document = $edit->document; if ( document_is_invalid() ) { # The retry method will return out of ->process immediately (via die) $edit->retry } # Whatever is returned from the processor will be returned via ->edit return $document; } ); With an "out-of-band" instructional preamble: $document = <<_END_ # Delete everything but the fruit you like: --- Apple Banana Cherry _END_ # After the edit, only the text following the first '---' will be returned $content = Term::EditorEdit->edit( separator => '---', document => $document, ); DESCRIPTION Term::EditorEdit is a tool for prompting the user to edit a piece of text via $VISUAL or $EDITOR and return the result In addition to just editing a document, this module can distinguish between a document preamble and document content, giving you a way to provide "out-of-bound" information to whoever is editing. Once an edit is complete, only the content (whatever was below the preamble) is returned USAGE $result = Term::EditorEdit->edit( ... ) Takes the following parameters: document The document to edit (required) separator The string to use as a line separator dividing content from the preamble process A code reference that will be called once an edit is complete. Within process, you can check the document, preamble, and content. You can also have the user retry the edit. Whatever is returned from the code will be what is returned from the ->edit call Returns the edited document (or content if a separator was specified) or the result of the "process" argument (if supplied) SEE ALSO Term::CallEditor AUTHOR Robert Krimen COPYRIGHT AND LICENSE This software is copyright (c) 2011 by Robert Krimen. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Changes000644000765000024 115311604637310 15163 0ustar00robstaff000000000000Term-EditorEdit-0.0016TODO: - Test for edit-on-file-swap 0.0016 Tuesday July 05 09:40:55 PDT 2011: - Added better error reporting 0.0015 Tuesday October 19 14:04:46 PDT 2010: - Added graceful recovery on editor exception 0.0014 Monday October 18 10:17:29 PDT 2010: - Added file => support 0.0013 Thursday October 14 12:20:47 PDT 2010: - Make sure to 'use IO::File' and check for error 0.0012 Tuesday October 12 19:36:32 PDT 2010: - Safer read from tmp edit (?), encountered weird issue with vim 0.0011 Thursday June 17 21:01:00 PDT 2010: - Tighter joining (on empty preamble) 0.0010: - Initial release META.yml000644000765000024 77411604637310 15131 0ustar00robstaff000000000000Term-EditorEdit-0.0016--- abstract: 'Edit a document via $EDITOR' author: - 'Robert Krimen ' build_requires: Test::Most: 0 configure_requires: ExtUtils::MakeMaker: 6.30 dynamic_config: 0 generated_by: 'Dist::Zilla version 4.200008, CPAN::Meta::Converter version 2.110930' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: Term-EditorEdit requires: Any::Moose: 0 File::Temp: 0 IO::File: 0 Text::Clip: 0 Try::Tiny: 0 version: 0.0016 MANIFEST000644000765000024 21611604637310 15000 0ustar00robstaff000000000000Term-EditorEdit-0.0016Changes MANIFEST META.yml Makefile.PL README bin/editor-edit lib/Term/EditorEdit.pm lib/Term/EditorEdit/Edit.pm t/01-basic.t t/02-edit-read.t Makefile.PL000644000765000024 217111604637310 15643 0ustar00robstaff000000000000Term-EditorEdit-0.0016 use strict; use warnings; use ExtUtils::MakeMaker 6.30; my %WriteMakefileArgs = ( 'ABSTRACT' => 'Edit a document via $EDITOR', 'AUTHOR' => 'Robert Krimen ', 'BUILD_REQUIRES' => { 'Test::Most' => '0' }, 'CONFIGURE_REQUIRES' => { 'ExtUtils::MakeMaker' => '6.30' }, 'DISTNAME' => 'Term-EditorEdit', 'EXE_FILES' => [ 'bin/editor-edit' ], 'LICENSE' => 'perl', 'NAME' => 'Term::EditorEdit', 'PREREQ_PM' => { 'Any::Moose' => '0', 'File::Temp' => '0', 'IO::File' => '0', 'Text::Clip' => '0', 'Try::Tiny' => '0' }, 'VERSION' => '0.0016', 'test' => { 'TESTS' => 't/*.t' } ); unless ( eval { ExtUtils::MakeMaker->VERSION(6.56) } ) { my $br = delete $WriteMakefileArgs{BUILD_REQUIRES}; my $pp = $WriteMakefileArgs{PREREQ_PM}; for my $mod ( keys %$br ) { if ( exists $pp->{$mod} ) { $pp->{$mod} = $br->{$mod} if $br->{$mod} > $pp->{$mod}; } else { $pp->{$mod} = $br->{$mod}; } } } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; WriteMakefile(%WriteMakefileArgs); t000755000765000024 011604637310 13774 5ustar00robstaff000000000000Term-EditorEdit-0.001601-basic.t000644000765000024 176311604637310 15626 0ustar00robstaff000000000000Term-EditorEdit-0.0016/t#!/usr/bin/env perl use strict; use warnings; use Test::Most 'no_plan'; use Term::EditorEdit; use Term::EditorEdit::Edit; my ( $edit, $document ); $document = <<_END_; A B c D _END_ $edit = Term::EditorEdit::Edit->new( file => 0, document => $document ); is( $edit->document, $document ); is( $edit->content, $document ); is( $edit->preamble, undef ); is( $edit->join( undef, $document ), $document ); is( $edit->join( "1\n # 2\n3", "$document\n" ), <<_END_ ); 1\n # 2\n3 $document _END_ $edit->separator( '---' ); is( $edit->join( "1\n # 2\n3", "$document\n" ), <<_END_ ); 1\n # 2\n3 --- $document _END_ is( $edit->preamble, undef ); $edit->_initial_preamble( "1\n # 2\n 3" ); $edit->preamble_from_initial( "Xyzzy\n # Apple, Banana\n\n" ); is( $edit->preamble, "Xyzzy\n # Apple, Banana\n\n1\n # 2\n 3\n" ); throws_ok( sub { $edit->retry }, qr/^\s*__Term_EditorEdit_retry__\s*$/ ); $document = <<_END_; --- A B C _END_ is( $edit->join( $edit->split( $document ) ), <<_END_ ) --- A B C _END_ bin000755000765000024 011604637310 14301 5ustar00robstaff000000000000Term-EditorEdit-0.0016editor-edit000755000765000024 20111604637310 16550 0ustar00robstaff000000000000Term-EditorEdit-0.0016/bin#!/usr/bin/env perl use strict; use warnings; use Term::EditorEdit; Term::EditorEdit->edit( document => <<_END_ ); Xyzzy _END_ 02-edit-read.t000644000765000024 74411604637310 16362 0ustar00robstaff000000000000Term-EditorEdit-0.0016/t#!/usr/bin/env perl use strict; use warnings; use Test::Most; use Term::EditorEdit; use Term::EditorEdit::Edit; use IO::File; my ( $edit, $document ); $document = <<_END_; A B c D _END_ $Term::EditorEdit::Edit::Test_edit = sub { my $tmp = shift; my $tmpw = IO::File->new( $tmp->filename, 'w' ); $tmpw->print( "Xyzzy\n" ); $tmpw->flush; $tmpw->close; }; $document = Term::EditorEdit->edit( document => $document ); is( $document, "Xyzzy\n" ); done_testing; Term000755000765000024 011604637310 15206 5ustar00robstaff000000000000Term-EditorEdit-0.0016/libEditorEdit.pm000644000765000024 771011604637310 17744 0ustar00robstaff000000000000Term-EditorEdit-0.0016/lib/Termpackage Term::EditorEdit; BEGIN { $Term::EditorEdit::VERSION = '0.0016'; } # ABSTRACT: Edit a document via $EDITOR # prompt_Yn, prompt_yN use strict; use warnings; use Any::Moose; use Carp; use File::Temp; use Term::EditorEdit::Edit; sub EDITOR { return $ENV{VISUAL} || $ENV{EDITOR}; } our $__singleton__; sub __singleton__ { return $__singleton__ ||=__PACKAGE__->new; } sub edit_file { my $self = shift; my $file = shift; die "*** Missing editor (No \$VISUAL or \$EDITOR)\n" unless my $editor = $self->EDITOR; my $rc = system $editor, $file; unless ( $rc == 0 ) { my ($exit_value, $signal, $core_dump); $exit_value = $? >> 8; $signal = $? & 127; $core_dump = $? & 128; die "Error during edit ($editor): exit value($exit_value), signal($signal), core_dump($core_dump): $!"; } } sub edit { my $self = shift; $self = $self->__singleton__ unless blessed $self; my %given = @_; # carp "Ignoring remaining arguments: @_" if @_; my $document = delete $given{document}; $document = '' unless defined $document; my $file = delete $given{file}; $file = $self->tmp unless defined $file; my $edit = Term::EditorEdit::Edit->new( editor => $self, file => $file, document => $document, %given, # process, split, ... ); return $edit->edit; } sub tmp { return File::Temp->new( unlink => 1 ) } 1; __END__ =pod =head1 NAME Term::EditorEdit - Edit a document via $EDITOR =head1 VERSION version 0.0016 =head1 SYNOPSIS use Term::EditorEdit; # $VISUAL or $EDITOR is invoked $document = Term::EditorEdit->edit( document => <<_END_ ); Apple Banana Cherry _END_ With post-processing: $document = Term::EditorEdit->edit( document => $document, process => sub { my $edit = shift; my $document = $edit->document; if ( document_is_invalid() ) { # The retry method will return out of ->process immediately (via die) $edit->retry } # Whatever is returned from the processor will be returned via ->edit return $document; } ); With an "out-of-band" instructional preamble: $document = <<_END_ # Delete everything but the fruit you like: --- Apple Banana Cherry _END_ # After the edit, only the text following the first '---' will be returned $content = Term::EditorEdit->edit( separator => '---', document => $document, ); =head1 DESCRIPTION Term::EditorEdit is a tool for prompting the user to edit a piece of text via C<$VISUAL> or C<$EDITOR> and return the result In addition to just editing a document, this module can distinguish between a document preamble and document content, giving you a way to provide "out-of-bound" information to whoever is editing. Once an edit is complete, only the content (whatever was below the preamble) is returned =head1 USAGE =head2 $result = Term::EditorEdit->edit( ... ) Takes the following parameters: document The document to edit (required) separator The string to use as a line separator dividing content from the preamble process A code reference that will be called once an edit is complete. Within process, you can check the document, preamble, and content. You can also have the user retry the edit. Whatever is returned from the code will be what is returned from the ->edit call Returns the edited document (or content if a separator was specified) or the result of the C argument (if supplied) =head1 SEE ALSO L =head1 AUTHOR Robert Krimen =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2011 by Robert Krimen. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut EditorEdit000755000765000024 011604637310 17242 5ustar00robstaff000000000000Term-EditorEdit-0.0016/lib/TermEdit.pm000644000765000024 1360411604637310 20650 0ustar00robstaff000000000000Term-EditorEdit-0.0016/lib/Term/EditorEditpackage Term::EditorEdit::Edit; use strict; use warnings; use Any::Moose; use Text::Clip; use Try::Tiny; use IO::File; our $EDITOR = 'Term::EditorEdit'; our $RETRY = "__Term_EditorEdit_retry__\n"; our $Test_edit; #has editor => qw/ is ro required 1 weak_ref 1 /; has process => qw/ is ro isa Maybe[CodeRef] /; has separator => qw/ is rw /; has file => qw/ is ro required 1 /; has document => qw/ is rw isa Str required 1 /; has $_ => reader => $_, writer => "_$_", isa => 'Str' for qw/ initial_document /; has preamble => qw/ is rw isa Maybe[Str] /; has $_ => reader => $_, writer => "_$_", isa => 'Maybe[Str]' for qw/ initial_preamble /; has content => qw/ is rw isa Str /; has $_ => reader => $_, writer => "_$_", isa => 'Str' for qw/ initial_content /; sub BUILD { my $self = shift; my $document = $self->document; $self->_initial_document( $document ); my ( $preamble, $content ) = $self->split( $document ); $self->preamble( $preamble ); $self->_initial_preamble( $preamble ); $self->content( $content ); $self->_initial_content( $content ); } sub edit { my $self = shift; my $file = $self->file; my $tmp; if ( blessed $file ) { if ( $file->isa( 'IO::Handle' ) ) { $tmp = $file; } elsif ( $file->isa( 'Path::Class::File' ) ) { $tmp = $file->open( 'w' ) or die "Unable to open $file: $!"; } else { die "Invalid file: $file"; } } else { $file = '' unless defined $file; if ( ref $file ) { die "Invalid file: $file"; } elsif ( length $file ) { $tmp = IO::File->new( $file, 'w' ) or die "Unable to open $file: $!"; } else { die "Missing file"; } } $tmp->autoflush( 1 ); while ( 1 ) { $tmp->seek( 0, 0 ) or die "Unable to seek on tmp ($tmp): $!"; $tmp->truncate( 0 ) or die "Unable to truncate on tmp ($tmp): $!"; $tmp->print( $self->join( $self->preamble, $self->content ) ); if ( $Test_edit ) { $Test_edit->( $tmp ); } else { try { $EDITOR->edit_file( $tmp->filename ); } catch { my $error = $_[0]; warn "$error"; warn "*** There was an error editing ", $tmp->filename, "\n"; while ( 1 ) { print STDERR "Do you want to (c)ontinue, (a)bort, or (s)ave? "; my $input = ; chomp $input; die $error unless defined $input; if ( 0 ) { } elsif ( $input eq 'c' ) { last; } elsif ( $input eq 'a' ) { die $error; } elsif ( $input eq 's' ) { my $save; unless ( $save = File::Temp->new( dir => '.', template => 'TermEditorEdit.XXXXXX', unlink => 0 ) ) { warn "Unable to create temporary file: $!" and next; } my $tmp_filename = $tmp->filename; my $tmpr; unless ( $tmpr = IO::File->new( $tmp_filename, 'r' ) ) { warn "Unable to open ($tmp_filename): $!" and next; } $save->print( join '', <$tmpr> ); $save->close; warn "Saved to: ", $save->filename, " ", ( -s $save->filename ), "\n"; } else { warn "I don't understand ($input)\n"; } } }; } my $document; { my $filename = $tmp->filename; my $tmpr = IO::File->new( $filename, 'r' ) or die "Unable to open ($filename): $!"; $document = join '', <$tmpr>; $tmpr->close; undef $tmpr; } $self->document( $document ); my ( $preamble, $content ) = $self->split( $document ); $self->preamble( $preamble ); $self->content( $content ); if ( my $process = $self->process ) { my ( @result, $retry ); try { @result = $process->( $self ); } catch { die $_ unless $_ eq $RETRY; $retry = 1; }; next if $retry; return $result[0] if defined $result[0]; } return $content; } } sub first_line_blank { my $self = shift; return $self->document =~ m/\A\s*$/m; } sub line0_blank { return $_[0]->first_line_blank } sub preamble_from_initial { my $self = shift; my @preamble; for my $part ( "$_[0]", $self->initial_preamble ) { next unless defined $part; chomp $part; push @preamble, $part; } $self->preamble( join "\n", @preamble, '' ) if @preamble; } sub retry { my $self = shift; die $RETRY; } sub split { my $self = shift; my $document = shift; return ( undef, $document ) unless my $separator = $self->separator; die "Invalid separator ($separator)" if ref $separator; if ( my $mark = Text::Clip->new( data => $document )->find( qr/^\s*$separator\s*$/m ) ) { return ( $mark->preceding, $mark->remaining ); } return ( undef, $document ); } sub join { my $self = shift; my $preamble = shift; my $content = shift; return $content unless defined $preamble; chomp $preamble; my $separator = $self->separator; unless ( defined $separator ) { return $content unless length $preamble; return join "\n", $preamble, $content; } return join "\n", $separator, $content unless length $preamble; return join "\n", $preamble, $separator, $content; } 1;