我理想地想要做的是扫描一堆文件,这些文件通过Test::Most
隐式地导入了一堆函数。我想在文件中显式导入函数。因此,基本上,我将检查use
语句以查看它们是否已经存在,如果不存在,我想为所涉及的函数添加一个附加的use语句。例如,如果文件中有use Test::Differences qw( eq_or_diff );
,但没有eq_or_diff
,我可能会添加use Test::Differences
。会变得有些复杂,但这是基本思想。
作为概念证明,我试图仅在现有脚本中添加一个单词,但我无法弄清楚。 insert_after()
成功返回true。我只得到一个false
值,但看不到任何有关为何无法添加该行的调试信息。
use strict;
use warnings;
use PPI::Document ();
use PPI::Token::Word ();
use Test::More;
my $script = <<'EOF';
use strict;
use warnings;
use DateTime ();
use Git::Helpers qw( checkout_root );
use LWP::UserAgent ();
my $foo = 'bar';
EOF
my $doc = PPI::Document->new( \$script );
my $includes = $doc->find('PPI::Statement::Include');
my @use = grep { $_->type eq 'use' } @{$includes};
my $second_last = $use[-2];
diag 'Trying to insert after ' . $second_last->module;
my $word = PPI::Token::Word->new('use');
isa_ok( $word, 'PPI::Element', 'word is an Element' );
isa_ok( $second_last, 'PPI::Element', 'use is an Element' );
ok( $second_last->insert_after($word), 'word inserted' );
diag $doc->serialize;
done_testing();
我的脚本的输出如下。您会注意到该文档似乎没有被更改:
# trying to insert after Git::Helpers
ok 1 - 'word is an Element' isa 'PPI::Element'
ok 2 - 'use is an Element' isa 'PPI::Element'
not ok 3 - word inserted
# failed test 'word inserted'
# at so.pl line 31.
# use strict;
# use warnings;
#
# use DateTime ();
# use Git::Helpers qw( checkout_root );
# use LWP::UserAgent ();
#
# my $foo = 'bar';
1..3
# looks like you failed 1 test of 3.
答案 0 :(得分:6)
# As above, you can insert a statement, or a non-significant token
sub insert_after {
my $self = shift;
my $Element = _INSTANCE(shift, 'PPI::Element') or return undef;
if ( $Element->isa('PPI::Statement') ) {
return $self->__insert_after($Element);
} elsif ( $Element->isa('PPI::Token') and ! $Element->significant ) {
return $self->__insert_after($Element);
}
'';
}
“非重要令牌”类似于空格或注释。
您正在尝试在顶层(声明之后)插入单个重要的令牌。那是不允许的。
您必须构建完整的PPI::Statement::Include
元素。
这是一些(相当丑陋的)概念验证代码:
# ...
diag 'Trying to insert after ' . $second_last->module;
{
my $insertion_point = $second_last;
for my $new_element (
do {
my $synthetic_use = PPI::Statement::Include->new;
for my $child (
PPI::Token::Word->new('use'),
PPI::Token::Whitespace->new(' '),
PPI::Token::Word->new('Test::Differences'),
PPI::Token::Whitespace->new(' '),
PPI::Token::Quote::Single->new("'eq_or_diff'"),
PPI::Token::Structure->new(';'),
) {
ok $synthetic_use->add_element($child);
}
$synthetic_use
},
PPI::Token::Whitespace->new("\n"),
) {
ok $insertion_point->insert_after($new_element);
}
}
diag $doc->serialize;
但是让PPI解析给定的片段并仅使用这些对象要容易得多:
diag 'Trying to insert after ' . $second_last->module;
{
my $insertion_point = $second_last;
for my $new_element (
reverse PPI::Document->new(\ "\nuse Test::Differences qw( eq_or_diff );")->elements
) {
ok $insertion_point->insert_after($new_element->remove);
}
}
diag $doc->serialize;
当心:使用$new_element->remove
而非仅使用$new_element
是至关重要的。您需要将$new_element
与旧的文档分离,因为否则,临时PPI::Document
实例的破坏将清除所有子元素,包括已经添加到$doc
的子元素。