File Coverage

lib/App/PRT/Command/ReplaceToken.pm
Criterion Covered Total %
statement 93 93 100.0
branch 28 32 87.5
condition 8 9 88.8
subroutine 15 15 100.0
pod 0 7 0.0
total 144 156 92.3


line stmt bran cond sub pod time code
1             package App::PRT::Command::ReplaceToken;
2 3     3   2304 use strict;
  3         4  
  3         77  
3 3     3   11 use warnings;
  3         3  
  3         77  
4 3     3   1125 use PPI;
  3         175109  
  3         1872  
5              
6             sub new {
7 33     33 0 32756 my ($class) = @_;
8 33         174 bless {
9             source_tokens => undef,
10             destination_tokens => undef,
11             statement_in => undef,
12             }, $class;
13             }
14              
15             # parse arguments from CLI
16             # arguments:
17             # @arguments
18             # returns:
19             # @rest_arguments
20             sub parse_arguments {
21 9     9 0 76 my ($self, @arguments) = @_;
22              
23 9 100       30 die "source and destination tokens required" unless @arguments >= 2;
24              
25 8         18 $self->register(shift @arguments => shift @arguments);
26              
27 8 100 100     45 if (@arguments >= 2 && $arguments[0] eq '--in-statement') {
28 1         2 shift @arguments;
29 1         3 $self->set_replace_only_statement_which_has_token(shift @arguments);
30             }
31              
32 8         21 @arguments;
33             }
34              
35             # register a replacing rule
36             # arguments:
37             # $source: source token
38             # $destination: destinationination token
39             # discussions:
40             # should consider utf-8 flag ?
41             sub register {
42 30     30 0 1429 my ($self, $source, $destination) = @_;
43              
44 30         30 my $source_tokens = do {
45 30         207 my $document = PPI::Document::Fragment->new(\$source);
46 30         17152 [ map { $_->content } @{ $document->find('PPI::Token') } ];
  64         6472  
  30         85  
47             };
48              
49 30         820 my $destination_tokens = do {
50 30         182 my $document = PPI::Document::Fragment->new(\$destination);
51 30         13259 [ map { $_->content } @{ $document->find('PPI::Token') } ];
  68         6219  
  30         88  
52             };
53              
54 30         744 $self->{source_tokens} = $source_tokens;
55 30         175 $self->{destination_tokens} = $destination_tokens;
56             }
57              
58             sub set_replace_only_statement_which_has_token {
59 5     5 0 13 my ($self, $in) = @_;
60              
61 5         12 $self->{statement_in} = $in;
62             }
63              
64             sub replace_only_statement_which_has_token {
65 223     223 0 1299 my ($self) = @_;
66              
67 223         396 $self->{statement_in};
68             }
69              
70             sub source_tokens {
71 922     922 0 7191 my ($self) = @_;
72              
73 922         1047 $self->{source_tokens};
74             }
75              
76             sub destination_tokens {
77 23     23 0 12947 my ($self) = @_;
78              
79 23         101 $self->{destination_tokens};
80             }
81              
82             # refactor a file
83             # argumensts:
84             # $file: filename for refactoring
85             sub execute {
86 21     21   474 my ($self, $file) = @_;
87              
88 21 100       56 return unless defined $self->source_tokens;
89              
90 20         57 my $document = PPI::Document->new($file);
91              
92             # When parse failed
93 20 100       85784 return unless $document;
94              
95 18         25 my $replaced = 0;
96 18 100       50 if (defined $self->replace_only_statement_which_has_token) {
97 3         8 $replaced += $self->_replace_in_statement($document);
98             } else {
99 15         43 $replaced += $self->_replace_all($document);
100             }
101              
102 18 100       92 $document->save($file) if $replaced;
103             }
104              
105             sub _replace_all {
106 15     15   20 my ($self, $document) = @_;
107              
108 15         38 my $tokens = $document->find('PPI::Token');
109              
110 15 50       20099 return 0 unless $tokens;
111              
112 15         23 my $replaced = 0;
113              
114 15         29 for my $token (@$tokens) {
115 834         773 $replaced += $self->_try_replace($token);
116             }
117              
118 15         43 $replaced;
119             }
120              
121             sub _try_replace {
122 891     891   617 my ($self, $token) = @_;
123 891         825 my @matched = $self->_match($token);
124 891 100       1531 return 0 unless @matched;
125 13         16 my $first = shift @matched;
126 13         10 $first->set_content(join '', @{$self->destination_tokens});
  13         25  
127 13         64 $_->set_content('') for @matched; # removing `(` will delete (...). So set empty content.
128 13         57 1;
129             }
130              
131             sub _match {
132 891     891   566 my ($self, $token) = @_;
133              
134 891         444 my @matched;
135              
136 891         498 for my $source (@{$self->source_tokens}) {
  891         753  
137 936 100 66     1941 if ($token->content eq $source) {
    100 100        
138 53         128 push @matched, $token;
139 53         141 $token = $token->next_token;
140             } elsif (@matched && $token->next_token && ref $token eq 'PPI::Token::Whitespace') {
141             # ignore white spaces of source
142 5         120 push @matched, $token;
143 5         8 $token = $token->next_token;
144 5         78 redo;
145             } else {
146 878         3368 return;
147             }
148             }
149 13         444 return @matched;
150             }
151              
152             sub _replace_in_statement {
153 3     3   4 my ($self, $document) = @_;
154              
155 3         7 my $statements = $document->find('PPI::Statement');
156              
157 3 50       5775 return 0 unless $statements;
158              
159 3         3 my $replaced = 0;
160              
161 3         8 for my $statement (@$statements) {
162 42 100       74 next if ref $statement eq 'PPI::Statement::Sub';
163 40 50       48 next if ref $statement eq 'PPI::Statement::Compound';
164 40 50       83 next if $statement->schild(0)->content eq 'do';
165              
166 40         340 my $found = 0;
167 40         69 my $tokens = $statement->find('PPI::Token');
168 40         8386 for my $token (@$tokens) {
169 201 100       265 if ($token->content eq $self->replace_only_statement_which_has_token) {
170 6         6 $found++;
171 6         7 last;
172             }
173             }
174 40 100       74 next unless $found;
175              
176 6         8 for my $token (@$tokens) {
177 57         59 $replaced += $self->_try_replace($token);
178             }
179             }
180              
181 3         7 $replaced;
182             }
183              
184             1;