File Coverage

blib/lib/Gherkin/Line.pm
Criterion Covered Total %
statement 40 98 40.8
branch 5 30 16.6
condition 5 9 55.5
subroutine 12 16 75.0
pod 0 8 0.0
total 62 161 38.5


line stmt bran cond sub pod time code
1             package Gherkin::Line;
2             $Gherkin::Line::VERSION = '39.0.0';
3 2     2   9 use strict;
  2         4  
  2         60  
4 2     2   6 use warnings;
  2         4  
  2         97  
5              
6 2         19 use Class::XSAccessor accessors =>
7 2     2   9 [ qw/line_text line_number indent _trimmed_line_text _tag_error/, ];
  2         2  
8              
9 2     2   1005 use Gherkin::Exceptions;
  2         4  
  2         2375  
10              
11             sub new {
12 38     38 0 76 my ( $class, $options ) = @_;
13 38         84 my $self = bless $options, $class;
14              
15 38   66     163 $self->{'_trimmed_line_text'} ||= $self->_build__trimmed_line_text;
16 38   66     181 $self->{'indent'} ||= $self->_build_indent;
17              
18 38         101 return $self;
19             }
20              
21             sub _build__trimmed_line_text {
22 38     38   57 my $self = shift;
23 38         124 my $trimmed = $self->line_text;
24 38 50       237 $trimmed =~ s/^\s+// if defined $trimmed;
25 38         176 return $trimmed;
26             }
27              
28             sub _build_indent {
29 38     38   60 my $self = shift;
30 38         235 return length( $self->line_text ) - length( $self->_trimmed_line_text );
31             }
32              
33             sub get_rest_trimmed {
34 21     21 0 43 my ( $self, $from ) = @_;
35 21         106 my $rest = substr( $self->_trimmed_line_text, $from );
36 21         117 $rest =~ s/^\s*//;
37 21         205 $rest =~ s/\s*$//;
38 21         60 return $rest;
39             }
40              
41             sub get_line_text {
42 5     5 0 12 my ( $self, $indent_to_remove ) = @_;
43 5 50       14 $indent_to_remove = -1 unless defined $indent_to_remove;
44              
45 5 50 33     20 if ( $indent_to_remove < 0 or $indent_to_remove > $self->indent ) {
46 5         62 return $self->_trimmed_line_text;
47             } else {
48 0         0 return substr( $self->line_text, $indent_to_remove );
49             }
50             }
51              
52             sub is_empty {
53 24     24 0 44 my $self = shift;
54 24         131 return !$self->_trimmed_line_text;
55             }
56              
57             sub startswith {
58 264     264 0 463 my ( $self, $prefix ) = @_;
59 264 50       616 return unless defined $self->_trimmed_line_text;
60 264         1163 return !index( $self->_trimmed_line_text, $prefix );
61             }
62              
63             sub startswith_title_keyword {
64 75     75 0 137 my ( $self, $prefix ) = @_;
65 75 50       191 return unless defined $self->_trimmed_line_text;
66 75         307 return !index( $self->_trimmed_line_text, $prefix . ':' );
67             }
68              
69             sub _split_table_cells_iterator {
70 0     0     my ( $self, $row ) = @_;
71 0           my $col = 0;
72 0           my $first_cell = 1;
73              
74             return sub {
75 0     0     my $cell = '';
76 0           my $start_col = $col + 1 + $first_cell;
77              
78 0           while (1) {
79 0 0         ( $row =~ s/^(.)// ) || return;
80 0           my $char = $1;
81 0           $col += 1;
82 0 0         if ( $char eq '|' ) {
    0          
    0          
83 0 0         if ($first_cell) {
84 0           $first_cell = 0;
85             } else {
86 0           return ( $cell, $start_col );
87             }
88             } elsif ( $char eq "\\" ) {
89 0 0         if ($row =~ s/^(.)//) {
90 0           $col += 1;
91 0           $cell .= '\\' . $1;
92             } else {
93 0           $cell .= '\\';
94             }
95             } elsif ( defined $char ) {
96 0           $cell .= $char;
97             } else {
98 0           die "WHAT?";
99             }
100             }
101             }
102 0           }
103              
104             my %unescape_map = ( '\\\\' => '\\', '\\|' => '|', '\\n' => "\n" );
105              
106             sub table_cells {
107 0     0 0   my ($self) = @_;
108 0           my $cells = [];
109 0           my $text = $self->_trimmed_line_text;
110 0           $text =~ s/^\s*//;
111 0           $text =~ s/\s*$//;
112              
113 0           my $i = $self->_split_table_cells_iterator($text);
114 0           while (1) {
115 0           my ( $cell, $col ) = $i->();
116 0 0         last unless defined $col;
117              
118 0           my $stripped_cell = $cell;
119 0           $stripped_cell =~ s/^\s+//;
120 0           my $cell_indent = length($cell) - length($stripped_cell);
121 0           $stripped_cell =~ s/\s+$//;
122 0           $stripped_cell =~ s/(\\\\|\\\||\\n)/$unescape_map{$1}/g;
123             push(
124 0           @{$cells},
  0            
125             {
126             column => $col + $self->indent + $cell_indent,
127             text => $stripped_cell
128             }
129             );
130             }
131              
132 0           return $cells;
133             }
134              
135             sub tags {
136 0     0 0   my $self = shift;
137 0           my $column = $self->indent + 1;
138 0           my $items_line = $self->_trimmed_line_text;
139 0           $items_line =~ s/\s+(#.*)?$//;
140              
141 0           my @tags;
142             my @errors;
143 0           my @items = split( /@/, $items_line );
144 0           shift(@items); # Blank first item
145              
146 0           for my $untrimmed (@items) {
147 0           my $item = $untrimmed;
148 0           $item =~ s/^\s*//;
149 0           $item =~ s/\s*$//;
150 0 0         next if length($item) == 0;
151              
152 0 0         if ($item !~ /^\S+$/) {
153 0           push @errors,
154             Gherkin::Exceptions::SingleParser->new(
155             detailed_message => 'A tag may not contain whitespace',
156             location => {
157             line => $self->line_number,
158             column => $column,
159             },
160             );
161             }
162 0           push @tags, {
163             column => $column,
164             text => '@' . $item,
165             };
166              
167 0           $column += length($untrimmed) + 1;
168             }
169              
170 0           my $err;
171 0 0         if (@errors) {
172 0           $err = Gherkin::Exceptions::CompositeParser->new(@errors);
173             }
174              
175 0           return (\@tags, $err);
176             }
177              
178             1;