ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/perl/matcher
Revision: 1.5
Committed: Fri Aug 19 23:02:46 2011 UTC (12 years, 8 months ago) by sf-tpope
Branch: MAIN
Changes since 1.4: +12 -7 lines
Log Message:
Consume button release events in matcher

File Contents

# User Rev Content
1 root 1.1 #! perl
2    
3     # Author: Tim Pope <rxvt-unicodeNOSPAM@tpope.info>
4    
5     my $url =
6     qr{
7 tpope 1.2 (?:https?://|ftp://|news://|mailto:|file://|\bwww\.)
8     [a-zA-Z0-9\-\@;\/?:&=%\$_.+!*\x27,~#]*
9     (
10     \([a-zA-Z0-9\-\@;\/?:&=%\$_.+!*\x27,~#]*\)| # Allow a pair of matched parentheses
11     [a-zA-Z0-9\-\@;\/?:&=%\$_+*~] # exclude some trailing characters (heuristic)
12     )+
13 root 1.1 }x;
14    
15 tpope 1.4 sub on_user_command {
16     my ($self, $cmd) = @_;
17     if($cmd =~ s/^matcher\b//) {
18     $self->most_recent;
19     }
20     ()
21     }
22    
23     sub most_recent {
24     my ($self) = shift;
25     my $row = $self->nrow;
26     my @exec;
27     while($row-- > $self->top_row) {
28     #my $line = $self->line ($row);
29     #my $text = $line->t;
30     @exec = $self->command_for($row);
31     last if(@exec);
32     }
33     if(@exec) {
34     return $self->exec_async (@exec);
35     }
36     ()
37     }
38 tpope 1.2
39 root 1.1 sub my_resource {
40     my $self = shift;
41 root 1.3 $self->x_resource ("$self->{name}.$_[0]");
42 root 1.1 }
43    
44 tpope 1.2 # turn a rendition spec in the resource into a sub that implements it on $_
45     sub parse_rend {
46     my ($self, $str) = @_;
47     my ($mask, $fg, $bg, $failed) = $str ? urxvt::rend2mask($str)
48     : (urxvt::RS_Uline, undef, undef, []);
49     warn "Failed to parse rendition string: " . join(',', @$failed) if @$failed;
50     my @rend;
51     push @rend, sub { $_ |= $mask } if $mask;
52     push @rend, sub { $_ = urxvt::SET_FGCOLOR($_, $fg) } if defined $fg;
53     push @rend, sub { $_ = urxvt::SET_BGCOLOR($_, $bg) } if defined $bg;
54     sub {
55     for my $s ( @rend ) { &$s };
56     }
57     }
58    
59 root 1.1 sub on_start {
60     my ($self) = @_;
61    
62     ($self->{name} = __PACKAGE__) =~ s/.*:://;
63     $self->{name} =~ tr/_/-/;
64     $self->{launcher} = $self->my_resource("launcher") ||
65     $self->x_resource("urlLauncher") ||
66     "sensible-browser";
67    
68     $self->{button} = 2;
69     $self->{state} = 0;
70     if($self->{argv}[0] || $self->my_resource("button")) {
71     my @mods = split('', $self->{argv}[0] || $self->my_resource("button"));
72     for my $mod (@mods) {
73     if($mod =~ /^\d+$/) {
74     $self->{button} = $mod;
75     } elsif($mod eq "C") {
76     $self->{state} |= urxvt::ControlMask;
77     } elsif($mod eq "S") {
78     $self->{state} |= urxvt::ShiftMask;
79     } elsif($mod eq "M") {
80     $self->{state} |= $self->ModMetaMask;
81     } elsif($mod ne "-" && $mod ne " ") {
82     warn("$mod is invalid in $self->{name}<$self->{argv}[0]>\n");
83     }
84     }
85     }
86    
87     my @defaults = ($url);
88     my @matchers;
89     for (my $idx = 0; defined (my $res = $self->my_resource("pattern.$idx") || $defaults[$idx]); $idx++) {
90     $res = $self->locale_decode ($res);
91     utf8::encode $res;
92     my $launcher = $self->my_resource("launcher.$idx");
93     $launcher =~ s/\$&|\$\{&\}/\${0}/g if ($launcher);
94 tpope 1.2 my $rend = $self->parse_rend($self->my_resource("rend.$idx"));
95     unshift @matchers, [qr($res)x,$launcher,$rend];
96 root 1.1 }
97     $self->{matchers} = \@matchers;
98    
99     ()
100     }
101    
102     sub on_line_update {
103     my ($self, $row) = @_;
104    
105     # fetch the line that has changed
106     my $line = $self->line ($row);
107     my $text = $line->t;
108     my $i = 0;
109    
110     # find all urls (if any)
111     for my $matcher (@{$self->{matchers}}) {
112     while ($text =~ /$matcher->[0]/g) {
113 tpope 1.2 #print "$&\n";
114 root 1.1 my $rend = $line->r;
115    
116     # mark all characters as underlined. we _must_ not toggle underline,
117     # as we might get called on an already-marked url.
118 tpope 1.2 &{$matcher->[2]}
119 root 1.1 for @{$rend}[ $-[0] .. $+[0] - 1];
120    
121     $line->r ($rend);
122     }
123     }
124    
125     ()
126     }
127    
128     sub valid_button {
129     my ($self, $event) = @_;
130     my $mask = $self->ModLevel3Mask | $self->ModMetaMask
131     | urxvt::ShiftMask | urxvt::ControlMask;
132     return ($event->{button} == $self->{button} &&
133     ($event->{state} & $mask) == $self->{state});
134     }
135    
136     sub command_for {
137     my ($self, $row, $col) = @_;
138     my $line = $self->line ($row);
139     my $text = $line->t;
140    
141     for my $matcher (@{$self->{matchers}}) {
142     my $launcher = $matcher->[1] || $self->{launcher};
143     while (($text =~ /$matcher->[0]/g)) {
144     my $match = $&;
145     my @begin = @-;
146     my @end = @+;
147 tpope 1.2 if (!defined($col) || ($-[0] <= $col && $+[0] >= $col)) {
148 root 1.1 if ($launcher !~ /\$/) {
149     return ($launcher,$match);
150     } else {
151     # It'd be nice to just access a list like ($&,$1,$2...),
152     # but alas, m//g behaves differently in list context.
153     my @exec = map { s/\$(\d+)|\$\{(\d+)\}/
154     substr($text,$begin[$1||$2],$end[$1||$2]-$begin[$1||$2])
155     /egx; $_ } split(/\s+/, $launcher);
156     return @exec;
157     }
158     }
159     }
160     }
161    
162     ()
163     }
164    
165     sub on_button_press {
166     my ($self, $event) = @_;
167 sf-tpope 1.5 if($self->valid_button($event)
168     && (my @exec = $self->command_for($event->{row},$event->{col}))) {
169 root 1.1 $self->{row} = $event->{row};
170     $self->{col} = $event->{col};
171 sf-tpope 1.5 $self->{cmd} = \@exec;
172     return 1;
173 root 1.1 } else {
174     delete $self->{row};
175     delete $self->{col};
176 sf-tpope 1.5 delete $self->{cmd};
177 root 1.1 }
178    
179     ()
180     }
181    
182     sub on_button_release {
183     my ($self, $event) = @_;
184    
185     my $row = delete $self->{row};
186     my $col = delete $self->{col};
187 sf-tpope 1.5 my $cmd = delete $self->{cmd};
188 root 1.1
189 sf-tpope 1.5 return if !defined $row;
190    
191     if($row == $event->{row} && abs($col-$event->{col}) < 2
192     && join("\x00", @$cmd) eq join("\x00", $self->command_for($row,$col))) {
193 root 1.1 if($self->valid_button($event)) {
194    
195 sf-tpope 1.5 $self->exec_async (@$cmd);
196 root 1.1
197     }
198     }
199    
200 sf-tpope 1.5 1;
201 root 1.1 }
202    
203     # vim:set sw=3 sts=3 et: