source: perl/lib/BarnOwl/Completion.pm @ fe7ece2

release-1.6release-1.7release-1.8release-1.9
Last change on this file since fe7ece2 was 880311d, checked in by Nelson Elhage <nelhage@mit.edu>, 12 years ago
Completion: Allow completers to provide partial completion.
  • Property mode set to 100644
File size: 4.1 KB
Line 
1use warnings;
2use strict;
3
4=head1 NAME
5
6BarnOwl::Completion
7
8=head1 DESCRIPTION
9
10Hooks for tab-completion support in BarnOwl.
11
12=cut
13
14package BarnOwl::Completion;
15
16use BarnOwl::Completion::Context;
17use BarnOwl::Editwin qw(save_excursion text_before_point text_after_point
18                        point_move replace_region);
19
20use List::Util qw(min first);
21
22our %completers = ();
23
24sub do_complete {
25    my $cmd = shift;
26    my $before = text_before_point();
27    my $after  = text_after_point();
28    BarnOwl::debug("Completing: $before-|-$after");
29    my $ctx = BarnOwl::Completion::Context->new($before, $after);
30
31    my @words = get_completions($ctx);
32    return unless @words;
33    my $prefix = common_prefix(map {completion_value($_)} @words);
34
35    if($prefix) {
36        insert_completion($ctx, $prefix,
37                          scalar @words == 1 && completion_done($words[0]));
38    }
39
40    if(scalar @words > 1) {
41        show_completions(@words);
42    } else {
43        BarnOwl::message('');
44    }
45}
46
47=head1 COMPLETIONS
48
49A COMPLETION is either a simple string, or a reference to an array
50containing two or more values.
51
52In the former case, the string use used for both the text to display,
53as well as the result of the completion, and is assumed to be a full
54completion.
55
56An arrayref completion consists of
57
58    [$display_text, $replacement_value[, $completion_done] ].
59
60$display_text will be printed in the case of ambiguous completions,
61$replacement_value will be used to substitute the value in. If there
62is only a single completion for a given word, a space will be appended
63after the completion iff $completion_done is true (or missing).
64
65=cut
66
67sub completion_text {
68    my $c = shift;
69    return $c unless ref($c) eq 'ARRAY';
70    return $c->[0];
71}
72
73sub completion_value {
74    my $c = shift;
75    return $c unless ref($c) eq 'ARRAY';
76    return $c->[1];
77}
78
79sub completion_done {
80    my $c = shift;
81    return 1 if ref($c) ne 'ARRAY' or @$c < 3;
82    return $c->[2];
83}
84
85sub insert_completion {
86    my $ctx = shift;
87    my $completion = BarnOwl::quote(completion_value(shift));
88    my $done = shift;
89
90    if($done) {
91        $completion .= " ";
92    }
93
94    save_excursion {
95        point_move($ctx->word_start - $ctx->point);
96        BarnOwl::Editwin::set_mark();
97        point_move($ctx->word_end - $ctx->word_start);
98        replace_region($completion);
99    };
100    if(!length($ctx->words->[$ctx->word])) {
101        point_move(length($completion));
102    }
103}
104
105sub show_completions {
106    my @words = @_;
107    my $all = BarnOwl::quote(map {completion_text($_)} @words);
108    my $width = BarnOwl::getnumcols();
109    if (length($all) > $width-1) {
110        $all = substr($all, 0, $width-4) . "...";
111    }
112    BarnOwl::message($all);
113}
114
115sub common_prefix {
116    my @words = @_;
117    my $len   = min(map {length($_)} @words);
118    my $pfx = '';
119    for my $i (1..$len) {
120        $pfx = substr($words[0], 0, $i);
121        if(first {substr($_, 0, $i) ne $pfx} @words) {
122            $pfx = substr($pfx, 0, $i-1);
123            last;
124        }
125    }
126
127    return $pfx;
128}
129
130
131sub get_completions {
132    my $ctx = shift;
133    if($ctx->word == 0) {
134        return complete_command($ctx->words->[0]);
135    } else {
136        my $cmd = $ctx->words->[0];
137        my $word = $ctx->words->[$ctx->word];
138        if(exists($completers{$cmd})) {
139            return grep {completion_value($_) =~ m{^\Q$word\E}} $completers{$cmd}->($ctx);
140        }
141        return;
142    }
143}
144
145sub complete_command {
146    my $cmd = shift;
147    $cmd = "" unless defined($cmd);
148    return grep {$_ =~ m{^\Q$cmd\E}} @BarnOwl::all_commands;
149}
150
151sub register_completer {
152    my $cmd = shift;
153    my $completer = shift;
154    $completers{$cmd} = $completer;
155}
156
157sub load_completers {
158    opendir(my $dh, BarnOwl::get_data_dir() . "/" . "lib/BarnOwl/Complete/") or return;
159    while(my $name = readdir($dh)) {
160        next if $name =~ m{^\.};
161        next unless $name =~ m{[.]pm$};
162        $name =~ s{[.]pm$}{};
163        eval "use BarnOwl::Complete::$name";
164        if($@) {
165            BarnOwl::error("Loading completion module $name:\n$@\n");
166        }
167    }
168}
169
170$BarnOwl::Hooks::startup->add("BarnOwl::Completion::load_completers");
171
1721;
Note: See TracBrowser for help on using the repository browser.