Merge branch 'topic/dzil'
[p5sagit/Devel-REPL.git] / lib / Devel / REPL / Plugin / CompletionDriver / INC.pm
CommitLineData
1716b200 1use strict;
2use warnings;
f1f5a418 3package Devel::REPL::Plugin::CompletionDriver::INC;
4use Devel::REPL::Plugin;
5use File::Next;
6use File::Spec;
aa8b7647 7use namespace::autoclean;
f1f5a418 8
3a400715 9sub BEFORE_PLUGIN {
10 my $self = shift;
11 $self->load_plugin('Completion');
12}
6631e15c 13
f1f5a418 14around complete => sub {
15 my $orig = shift;
16 my ($self, $text, $document) = @_;
17
18 my $last = $self->last_ppi_element($document, 'PPI::Statement::Include');
19
20 return $orig->(@_)
21 unless $last->isa('PPI::Statement::Include');
22
23 my @elements = $last->children;
24 shift @elements; # use or require
25
26 # too late for us to care, they're completing on something like
27 # use List::Util qw(m
28 # OR they just have "use " and are tab completing. we'll spare them the flood
29 return $orig->(@_)
30 if @elements != 1;
31
32 my $package = shift @elements;
33 my $outsep = '::';
c5cdacc2 34 my $insep = "::";
f1f5a418 35 my $keep_extension = 0;
c5cdacc2 36 my $prefix = '';
37
38 # require "Foo/Bar.pm" -- not supported yet, ->string doesn't work for
39 # partially completed elements
40 #if ($package->isa('PPI::Token::Quote'))
41 #{
42 # # we need to strip off the leading quote and stash it
43 # $package = $package->string;
44 # my $start = index($package->quote, $package);
45 # $prefix = substr($package->quote, 0, $start);
46
47 # # we're completing something like: require "Foo/Bar.pm"
48 # $outsep = $insep = '/';
49 # $keep_extension = 1;
50 #}
51 if ($package =~ /'/)
f1f5a418 52 {
b0489a7c 53 # the goofball is using the ancient ' package sep, we'll humor him
c5cdacc2 54 $outsep = "'";
b0489a7c 55 $insep = "'|::";
f1f5a418 56 }
57
58 my @directories = split $insep, $package;
59
60 # split drops trailing fields
61 push @directories, '' if $package =~ /(?:$insep)$/;
62 my $final = pop @directories;
63 my $final_re = qr/^\Q$final/;
64
65 my @found;
66
16d29e42 67 # most VCSes don't litter every single fucking directory with garbage. if you
68 # know of any other, just stick them in here. noone wants to complete
69 # Devel::REPL::Plugin::.svn
c5cdacc2 70 my %ignored =
71 (
72 '.' => 1,
73 '..' => 1,
74 '.svn' => 1,
75 );
76
16d29e42 77 # this will take a directory and add to @found all of the possible matches
6c3218fe 78 my $add_recursively;
79 $add_recursively = sub {
80 my ($path, $iteration, @more) = @_;
ce00c3c0 81 opendir((my $dirhandle), $path) || return;
c5cdacc2 82 for (grep { !$ignored{$_} } readdir $dirhandle)
6c3218fe 83 {
6c3218fe 84 my $match = $_;
c5cdacc2 85
86 # if this is the first time around, we need respect whatever the user had
87 # at the very end when he pressed tab
88 next if $iteration == 0 && $match !~ $final_re;
89
6c3218fe 90 my $fullmatch = File::Spec->rel2abs($match, $path);
91 if (-d $fullmatch)
92 {
93 $add_recursively->($fullmatch, $iteration + 1, @more, $match);
94 }
95 else
96 {
97 $match =~ s/\..*// unless $keep_extension;
c5cdacc2 98 push @found, join '', $prefix,
99 join $outsep, @directories, @more, $match;
6c3218fe 100 }
101 }
102 };
103
afc8677b 104 # look through all of
f1f5a418 105 INC: for (@INC)
106 {
107 my $path = $_;
16d29e42 108
109 # match all of the fragments they have, so "use Moose::Meta::At<tab>"
110 # will only begin looking in ../Moose/Meta/
f1f5a418 111 for my $subdir (@directories)
112 {
113 $path = File::Spec->catdir($path, $subdir);
114 -d $path or next INC;
115 }
116
6c3218fe 117 $add_recursively->($path, 0);
f1f5a418 118 }
119
120 return $orig->(@_), @found;
121};
122
1231;
124
cfd1094b 125__END__
126
127=head1 NAME
128
129Devel::REPL::Plugin::CompletionDriver::INC - Complete module names in use and require
130
30b459d4 131=head1 AUTHOR
132
133Shawn M Moore, C<< <sartak at gmail dot com> >>
134
cfd1094b 135=cut
136