File Coverage

blib/lib/Gherkin/Line.pm
Criterion Covered Total %
statement 40 96 41.6
branch 5 30 16.6
condition 5 9 55.5
subroutine 12 16 75.0
pod 0 8 0.0
total 62 159 38.9


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