-
Notifications
You must be signed in to change notification settings - Fork 7
/
file-completition.pl
174 lines (135 loc) · 4.19 KB
/
file-completition.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
use common::sense;
use Xchat;
use File::Spec;
use Cwd;
#remove the working directory from the path if possible
my $remove_working_dir = 1;
my $configdir = Xchat::get_info 'configdir';
my $addondir = File::Spec->catfile($configdir, 'addons');
my $workingdir = File::Spec->canonpath(getcwd);
#paths to look into for files
my @paths = (
$addondir,
);
#commands to use a single tab on
my @commands = (
'load',
);
#commands for which to not prefix the path for
my @filename_only = (
'reload',
'unload',
'script update',
);
#how many files to cycle through
my $limit = 10;
Xchat::register 'File Completition', '1.02', 'Completes filenames with Shift-Tab, or just Tab for /load, /unload or /reload commands';
Xchat::hook_print 'Key Press', \&key_press;
my $complete = { };
sub key_press {
my ($key, $modifier) = @{ $_[0] };
my $context = Xchat::get_context;
my $text = Xchat::get_info 'inputbox';
my $regex = do {
my $commands_str = join '|', @commands, @filename_only;
qr/(?:$commands_str)/io;
};
#on shift-tab or a normal tab if the texts starts with /load, /unload or /reload
if ($modifier == 1 && $key == 65056 || $key == 65289 && $modifier == 0 && $text =~ /^\/$regex /) {
my $input = Xchat::get_info 'inputbox';
#text to match without any spaces
my ($command, $match) = $text =~ /(?:^\/($regex))?(?:[^ ]*? *(.*))/;
my $pos = $-[2]; #position before $match
#if there's no context that means that tab-cycling hasn't started yet
if (!exists $complete->{ $context }) {
#remove quotation marks
$match =~ s/^"//;
$match =~ s/"$//;
my ($volume, $path, $match) = File::Spec->splitpath($match);
my $path = File::Spec->catpath($volume, $path);
$complete->{ $context } = {
'index' => 0,
'items' => [ sort { fc $a->{'name'} cmp fc $b->{'name'} } contents(length $path ? $path : @paths) ],
'match' => $match,
'input' => $input,
};
}
#match whatever the user supplied from the beginning
my @matches = grep {
$_->{'name'} =~ /^\Q$complete->{ $context }{'match'}/i
} @{ $complete->{ $context }{'items'} };
#print the matches if they're above the limit
if (@matches > $limit) {
Xchat::print join ' | ', map { $_->{'name'} } @matches;
}
#make sure there are matches
elsif (@matches) {
#if we cycled through everything, reset the text to what it was
if ($complete->{ $context }{'index'} > $#matches) {
$complete->{ $context }{'index'} = 0;
set_text($complete->{ $context }{'input'});
return Xchat::EAT_XCHAT;
}
my $before = substr($input, 0, $pos); #whatever is before $match
my $filename = do {
my $path = $matches[ $complete->{ $context }{'index'} ]{'path'};
my $name = $matches[ $complete->{ $context }{'index'} ]{'name'};
#File::Spec->abs2rel(File::Spec->catfile($path, "$name"));
File::Spec->catfile($path, "$name");
};
#remove working dir
$filename =~ s/^\Q$workingdir\E[\\\/]?//i if $remove_working_dir;
#remove path from specified commands
if (grep { fc $_ eq fc $command} @filename_only) {
$filename = (File::Spec->splitpath($filename))[2];
}
#add quotes if needed
$filename = "\"$filename\"" if $filename =~ / /;
set_text("$before$filename");
$complete->{ $context }{'index'}++;
}
return Xchat::EAT_XCHAT;
}
#if the text is changed, consider tab cycling to have ended
Xchat::hook_timer 0, sub {
delete $complete->{ $context } if fc $text ne fc Xchat::get_info 'inputbox';
return Xchat::REMOVE;
};
return Xchat::EAT_NONE;
}
sub contents {
my @paths = @_;
my @items;
for (@paths) {
$_ = File::Spec->canonpath($_);
#prefix current directory
if (!File::Spec->file_name_is_absolute($_)) {
$_ = File::Spec->catfile(File::Spec->curdir(), $_);
}
if (opendir my $dh, $_) {
for my $item (readdir $dh) {
next if $item eq '.' || $item eq '..';
push @items, {
'name' => $item,
'path' => $_,
};
}
closedir $dh;
}
#display the errors if any
else {
Xchat::print "Can't open $_: $!";
next;
}
}
return @items;
}
sub set_text {
my ($text) = @_;
my $result = Xchat::command "settext $text";
if ($result) {
my $offset = $text =~ /"$/ ? -1 : 0;
Xchat::command 'setcursor ' . ($offset + length $text);
}
return $result;
}