File Coverage

blib/lib/Text/Snippet.pm
Criterion Covered Total %
statement 61 62 98.3
branch 9 12 75.0
condition 1 2 50.0
subroutine 16 16 100.0
pod 3 3 100.0
total 90 95 94.7


line stmt bran cond sub pod time code
1             package Text::Snippet;
2             BEGIN {
3 8     8   222091 $Text::Snippet::VERSION = '0.04';
4             }
5              
6             # ABSTRACT: TextMate-like snippet functionality
7              
8 8     8   224 use warnings;
  8         20  
  8         309  
9 8     8   50 use strict;
  8         18  
  8         362  
10 8     8   58321 use Text::Balanced qw(extract_bracketed extract_multiple);
  8         376025  
  8         1087  
11 8     8   11365 use Text::Snippet::TabStop::Parser;
  8         31  
  8         270  
12 8     8   22180 use Text::Snippet::TabStop::Cursor;
  8         28156  
  8         308  
13 8     8   61 use Scalar::Util qw(blessed);
  8         14  
  8         421  
14 8     8   42 use Carp qw(croak);
  8         16  
  8         4417  
15              
16              
17             sub _new {
18 24     24   39 my $class = shift;
19 24 50       124 my $args = ref($_[0]) ? shift : {@_};
20 24         135 my $self = {
21             chunks => [],
22             tab_stops => [],
23             %$args
24             };
25 24 50       122 croak "no src attribute specified" unless defined($self->{src});
26 24         282 return bless $self, $class;
27             }
28             sub parse {
29 24     24 1 38755 my $class = shift;
30 24         46 my $source = shift;
31             my @raw = extract_multiple( $source, [ { Simple => qr/\$\d+/ },
32 73     73   13239 { Curly => sub { extract_bracketed( $_[0], '{}', '\$(?=\{\d)' ) } },
33 24         404 { Plain => qr/[^\$]+/ },
34             ], undef, 1);
35              
36 24         3167 my %tab_stop_cache;
37             my @chunks;
38 24         58 foreach my $c (@raw) {
39 100 100       229 if ( ref($c) eq 'Plain' ) {
40 54         142 push( @chunks, $$c );
41             } else {
42              
43             # the leading $ gets stripped on these by extract_bracketed...
44 46 100       130 $$c = '$' . $$c if(ref($c) eq 'Curly');
45              
46 46         217 my $t = Text::Snippet::TabStop::Parser->parse( $$c );
47              
48 46 100       314 if ( exists( $tab_stop_cache{ $t->index } ) ) {
49 11         80 $t->parent($tab_stop_cache{ $t->index });
50             } else {
51 35         110 $tab_stop_cache{ $t->index } = $t;
52             }
53 46         100 push( @chunks, $t );
54             }
55             }
56              
57 24         109 my @tab_stops = map { $tab_stop_cache{$_} } sort { $a <=> $b } keys %tab_stop_cache;
  35         99  
  14         72  
58              
59 24 50       78 if ( exists( $tab_stop_cache{'0'} ) ) {
60             # put the zero-th tab stop on the end of the array
61 0         0 push( @tab_stops, shift(@tab_stops) );
62             } else {
63             # append the implicit zero-th tab stop on the end of the array
64 24         95 my $implicit = Text::Snippet::TabStop::Parser->parse( '$0' );
65 24         40 push( @tab_stops, $implicit );
66 24         90 push( @chunks, $implicit );
67             }
68              
69 24         96 my %params = (
70             src => $source,
71             chunks => \@chunks,
72             tab_stops => \@tab_stops,
73             );
74 24         114 return $class->_new(%params);
75             }
76              
77              
78              
79 8     8   60 use overload '""' => sub { shift->to_string }, fallback => 1;
  8     6   16  
  8         121  
  6         806  
80              
81             sub to_string {
82 24     24 1 828 my $self = shift;
83 24   50     49 return join( '', @{ $self->chunks } ) || '';
84             }
85              
86 8     8   7771 use Class::XSAccessor getters => { src => 'src', tab_stops => 'tab_stops', chunks => 'chunks' };
  8         16  
  8         73  
87              
88              
89             sub cursor {
90 3     3 1 14 my $self = shift;
91 3         20 return Text::Snippet::TabStop::Cursor->new( snippet => $self );
92             }
93              
94              
95             1;
96              
97             __END__