diff options
Diffstat (limited to 'scripts/typofix.pl')
| -rw-r--r-- | scripts/typofix.pl | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/scripts/typofix.pl b/scripts/typofix.pl new file mode 100644 index 0000000..49b312a --- /dev/null +++ b/scripts/typofix.pl @@ -0,0 +1,165 @@ +# typofix.pl - when someone uses s/foo/bar typofixing, this script really +# goes and modifies the original text on screen. + +use strict; +use warnings; +use Irssi qw( + settings_get_str settings_get_bool + settings_add_str settings_add_bool + signal_add signal_stop +); +use Irssi 20140701; +use Irssi::TextUI; +use Algorithm::Diff 'sdiff'; + +our $VERSION = '1.12'; # 4be3787e5717715 +our %IRSSI = ( + authors => 'Juerd (first version: Timo Sirainen, additions by: Qrczak)', + contact => 'tss@iki.fi, juerd@juerd.nl, qrczak@knm.org.pl', + name => 'Typofix', + description => 'When someone uses s/foo/bar/, this really modifies the text', + license => 'Same as Irssi', + url => 'http://juerd.nl/irssi/', + changed => 'Sat Jun 28 16:24:26 CEST 2014', + upgrade_info => '/set typofix_modify_string %wold%gnew%n', + NOTE1 => 'you need irssi 0.8.17' +); + +# /SET typofix_modify_string [fixed] - append string after replaced text +# /SET typofix_hide_replace NO - hide the s/foo/bar/ line +# (J) /SET typofix_format - format with "old" and "new" in it + + +my $chars = '/|;:\'"_=+*&^%$#@!~,.?-'; +my $regex = qq{(?x-sm: # "s/foo/bar/i # oops" + \\s* # Optional whitespace + s # Substitution operator s + ([$chars]) # Delimiter / + ( (?: \\\\. | (?!\\1). )* ) # Pattern foo + # Backslash plus any char, or a single non-delimiter char + \\1 # Delimiter / + ( (?: \\\\. | (?!\\1). )* ) # Replacementstring bar + \\1? # Optional delimiter / + ([a-z]*) # Modifiers i + \\s* # Optional whitespace + (.?) # Don't hide if there's more # oops +)}; +my $irssi_mumbo = qr/\cD[`-i]|\cD[&-@\xff]./; +my $irssi_mumbo_no_partial = qr/(?<!\cD)(?<!\cD[&-@\xff])/; + +sub replace { + my ($window, $nick, $from, $to, $opt, $screen) = @_; + + my $view = $window->view(); + my $line = $screen ? $view->{bottom_startline} : $view->{startline}; + + my $last_line; + (my $copy = $from) =~ s/\^|^/^.*\\b$nick\\b.*?\\s.*?/; + while ($line) { + my $text = $line->get_text(0); + eval { + $last_line = $line + if ($line->{info}{level} & (MSGLEVEL_PUBLIC | MSGLEVEL_MSGS)) && + $text !~ /$regex/o && $text =~ /$copy/; + 1 + } or return; + $line = $line->next(); + } + return 0 if (!$last_line); + my $text = $last_line->get_text(1); + + # variables and case insensitivity + $from = "(?i:$from)" if $opt =~ /i/; + $to = quotemeta $to; + $to =~ s{\\\\\\(.)|\\(.)([1-9])?}{ + if (defined $1) { + "\\$1" + } elsif (defined $3 && ($2 eq "\\" || $2 eq "\$")) { + "\$$3" + } else { + "\\$2".($3//"") + } }ge; + + # text replacing + $text =~ s/(.*(?:\b|$irssi_mumbo)$irssi_mumbo_no_partial$nick(?:\b|$irssi_mumbo).*?\s)//; + my $pre = $1; + $text =~ s/$irssi_mumbo//g; + my $format = settings_get_str('typofix_format'); + $format =~ s/old/\0\cA/; + $format =~ s/new/\0\cB/; + $format =~ s/%/\0\cC/g; + + my $old = $text; + eval " \$text =~ s/\$from/$to/".($opt =~ /g/ ? "g" : "")." ; 1 " + or Irssi::print "Typofix warning: $@", return 0; + my $new = ''; + my $diff = Algorithm::Diff->new([split//,$old],[split//,$text]); + while ($diff->Next()) { + local $" = ''; + if (my @it = $diff->Same()) { + $new .= "@it"; + } + else { + my %r = ("\cA" => [ $diff->Items(1) ], + "\cB" => [ $diff->Items(2) ]); + my $format_st = $format; + $format_st =~ s/\0([\cA\cB])/@{$r{$1}}/g; + $new .= $format_st; + } + } + s/%/%%/g for $pre, $new; + s/\0\cC/%/g for $new; + $text = $pre . $new . settings_get_str('typofix_modify_string'); + + my $bottom = $view->{bottom}; + my $info = $last_line->{info}; + $window->print_after($last_line, $info->{level}, $text, $info->{time}); + $view->remove_line($last_line); + $window->command('^scrollback end') if $bottom && !$window->view->{bottom}; + $view->redraw(); + + return 1; +} + +sub event_privmsg { + my ($server, $data, $nick, $address) = @_; + my ($target, $text) = $data =~ /^(\S*)\s:(.*)/ or return; + + return unless $text =~ /^$regex/o; + my ($from, $to, $opt, $extra) = ($2, $3, $4, $5); + + my $hide = settings_get_bool('typofix_hide_replace') && !$extra; + + my $ischannel = $server->ischannel($target); + my $level = $ischannel ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS; + + $target = $nick unless $ischannel; + my $window = $server->window_find_closest($target, $level); + + signal_stop() if (replace($window, $nick, $from, $to, $opt, 0) && $hide); +} + +sub event_own_public { + my ($server, $text, $target) = @_; + + return unless $text =~ /^$regex/o; + my ($from, $to, $opt, $extra) = ($2, $3, $4, $5); + + my $hide = settings_get_bool('typofix_hide_replace') && !$extra; + $hide = 0 if settings_get_bool('typofix_own_no_hide'); + + my $level = $server->ischannel($target) ? MSGLEVEL_MSGS : MSGLEVEL_PUBLIC; + my $window = $server->window_find_closest($target, $level); + + signal_stop() if (replace($window, $server->{nick}, $from, $to, $opt, 0) && $hide); +} + +settings_add_str ('typofix', 'typofix_modify_string', ' [fixed]'); +settings_add_str ('typofix', 'typofix_format', '%rold%gnew%n'); +settings_add_bool('typofix', 'typofix_hide_replace', 0); +settings_add_bool('typofix', 'typofix_own_no_hide', 0); + +signal_add { + 'event privmsg' => \&event_privmsg, + 'message own_public' => \&event_own_public +}; |
