helm-perldoc を使ってみたりしていて気付いたのだけど、Perlでインストールされているモジュールたちからperldoc
でドキュメント引くためのリストを作るのってけっこう難しい。。
シェルコマンドで
@INC
以下にある*.pmファイル、もしくは*.podファイルから生成。@INCそれぞれからファイルを検索する。カレントディレクトリ.
が含まれてしまうのは困るので除外する。
$ perl -e '$_ ne "." and print "$_\n" for @INC' | xargs -I{} find {} -name '*.pm' -o -name '*.pod'
というかんじでファイルは列挙できるけど、ここからパッケージ名とか抽出していくのが厳しい。全部フルパスで得られるけど探索元がそれぞれ違ったりするから一気に削る、みたいにはできないし。
perl-completion.el では列挙したファイルそれぞれにgrepをかけてpackage ****;
の宣言部分を抜き出したりしていたみたい。しかしそれだとpackage宣言されていないpodだけのものとかが抜けてしまうのですよね…。
Perlスクリプトで
ExtUtils::Installed
を使って列挙する方法。
- modulesメソッドで一覧を取得できる。カレントディレクトリを除く場合は
skip_cwd
を指定。
#!/usr/bin/env perl use strict; use warnings; use ExtUtils::Installed; for my $module (ExtUtils::Installed->new(skip_cwd => 1)->modules) { print $module, "\n"; }
で、あっさりモジュール名一覧が取れる…のだけど、これは所謂ディストリビューション名だけになり、それに含まれる各関連パッケージが含まれていなかったりする("Amon2"だけ取得できて"Amon2::Web"とかは含まれない、など)。
なので、各ディストリビューションから.packlist
に記述されているファイルたちも取得したい。
use ExtUtils::Installed; my $ei = ExtUtils::Installed->new(skip_cwd => 1); my @files = (); for my $module ($ei->modules) { push @files, $ei->files($module); } for my $file (@files) { print "$file\n"; }
こんなかんじにすると、関連するファイルたちをすべて取得できる。しかしこれだと不要なものもたくさん含まれるので、拡張子で適当にフィルタリングすることにする。
for my $file (@files) { next unless $file =~ /\.(?:pm|pods)$/; print "$file\n"; }
で、結局フルパスで出てくるのはどうにかしたいので、@INCと一致する部分を削る。@INCをつなげた正規表現で…
my @target_inc = grep { $_ ne '.' } @INC; my $re_target_inc = '(' . join('|', map { quotemeta } @target_inc) . ')'; my $ei = ExtUtils::Installed->new(skip_cwd => 1); my @files = (); for my $module ($ei->modules) { push @files, $ei->files($module); } for my $file (@files) { next unless $file =~ /\.(?:pm|pods)$/; $file =~ s!${re_target_inc}/?!!; print "$file\n"; }
ちょっと無理矢理感はあるけど、これでだいたい削れるので、あとはseparatorを置換したり拡張子を除去したりすればOK。
というわけでperldoc
の対象リストの列挙はこんなかんじになるかな、と。
#!/usr/bin/env perl use strict; use warnings; use utf8; use ExtUtils::Installed; my @target_inc = grep { $_ ne '.' } @INC; my $re_target_inc = '(' . join('|', map { quotemeta } @target_inc) . ')'; my $ei = ExtUtils::Installed->new(skip_cwd => 1); my @files = (); for my $module ($ei->modules) { push @files, $ei->files($module); } for my $file (@files) { next unless $file =~ /\.(?:pm|pods)$/; $file =~ s!${re_target_inc}/?!!; $file =~ s!/!::!g; $file =~ s!\.[^\.]+$!!; print "$file\n"; }
ワンライナーで
やるなら、こんなかんじかな。
perl -MExtUtils::Installed -e 'my $i = ExtUtils::Installed->new(skip_cwd => 1); print "$_\n" for sort map { s!^(@{[ join("|", map { quotemeta } grep { not /^\./ } @INC) ]})/?!!; s!/!::!g; s!\.(pm|pod)$!!; $_ } grep { /\.(pm|pod)$/ } map { $i->files($_) } $i->modules'
もっと簡単でキレイな方法ないのかなー。