我理想的是能够做的是扫描一堆隐含导入一堆函数的文件Test::Most.我想显式导入文件中的函数.所以基本上我会检查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();
Run Code Online (Sandbox Code Playgroud)
我的脚本输出如下.您会注意到该文档似乎没有被更改:
# 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.
Run Code Online (Sandbox Code Playgroud)
# 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);
}
'';
}
Run Code Online (Sandbox Code Playgroud)
"非重要令牌"类似于空白或评论.
您正尝试在顶层插入单个重要标记(在语句之后).这是不允许的.
你必须建立一个完整的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;
Run Code Online (Sandbox Code Playgroud)
但让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;
Run Code Online (Sandbox Code Playgroud)
注意:使用$new_element->remove而不仅仅是$new_element至关重要.您需要$new_element从其旧的包含文档中分离,否则临时PPI::Document实例的销毁将清除所有子元素,包括已添加到的元素$doc.