diff --git a/README.md b/README.md index 8116bb3..326fbb1 100644 --- a/README.md +++ b/README.md @@ -215,7 +215,6 @@ docker attach weechat # Detach with CTRL-p CTRL-q /set plugins.var.perl.highmon.first_run false /set plugins.var.perl.highmon.short_names on /set plugins.var.perl.keepnick.default_enable 1 -/set plugins.var.perl.multiline.weechat_paste_fix off ``` --- diff --git a/scripts/perl/multiline.pl b/scripts/perl/multiline.pl deleted file mode 100644 index d8cb032..0000000 --- a/scripts/perl/multiline.pl +++ /dev/null @@ -1,798 +0,0 @@ -use strict; use warnings; -$INC{'Encode/ConfigLocal.pm'}=1; -require Encode; -use utf8; - -# multiline.pl is written by Nei -# and licensed under the under GNU General Public License v3 -# or any later version - -# to read the following docs, you can use "perldoc multiline.pl" - -=head1 NAME - -multiline - Multi-line edit box for WeeChat (weechat edition) - -=head1 DESCRIPTION - -multiline will draw a multi-line edit box to your WeeChat window so -that when you hit the return key, you can first compose a complete -multi-line message before sending it all at once. - -Furthermore, if you have multi-line pastes then you can edit them -before sending out all the lines. - -=head1 USAGE - -make a key binding to send the finished message: - - /key bind meta-s /input return - -then you can send the multi-line message with Alt+S - -=head1 SETTINGS - -the settings are usually found in the - - plugins.var.perl.multiline - -namespace, that is, type - - /set plugins.var.perl.multiline.* - -to see them and - - /set plugins.var.perl.multiline.SETTINGNAME VALUE - -to change a setting C to a new value C. Finally, - - /unset plugins.var.perl.multiline.SETTINGNAME - -will reset a setting to its default value. - -the following settings are available: - -=head2 char - -character(s) which should be displayed to indicate end of line - -=head2 tab - -character(s) which should be displayed instead of Tab key character - -=head2 lead_linebreak - -if turned on, multi-line messages always start on a new line - -=head2 modify_keys - -if turned on, cursor keys are modified so that they respect line -boundaries instead of treating the whole multi-line message as a -single line - -=head2 magic - -indicator displayed when message will be sent soon - -=head2 magic_enter_time - -delay after pressing enter before sending automatically (in ms), or 0 -to disable - -=head2 magic_paste_only - -only use multi-line messages for multi-line pastes (multi-line on -enter is disabled by this) - -=head2 paste_lock - -time-out to detect pastes (disable the weechat built-in paste -detection if you want to use this) - -=head2 send_empty - -set to on to automatically disregard enter key on empty line - -=head2 hide_magic_nl - -whether the new line inserted by magic enter key will be hidden - -=head2 weechat_paste_fix - -disable ctrl-j binding when paste is detected to stop silly weechat -sending out pastes without allowing to edit them - -=head2 ipl - -this setting controls override of ctrl-m (enter key) by script. Turn -it off if you don't want multiline.pl to set and re-set the key binding. - -=head1 FUNCTION DESCRIPTION - -for full pod documentation, filter this script with - - perl -pE' - (s/^## (.*?) -- (.*)/=head2 $1\n\n$2\n\n=over\n/ and $o=1) or - s/^## (.*?) - (.*)/=item I<$1>\n\n$2\n/ or - (s/^## (.*)/=back\n\n$1\n\n=cut\n/ and $o=0,1) or - ($o and $o=0,1 and s/^sub /=back\n\n=cut\n\nsub /)' - -=cut - -use constant SCRIPT_NAME => 'multiline'; -our $VERSION = '0.6.4'; -weechat::register(SCRIPT_NAME, - 'Nei ', # Author - $VERSION, - 'GPL3', # License - 'Multi-line edit box', # Description - 'stop_multiline', '') || return; -sub SCRIPT_FILE() { - my $infolistptr = weechat::infolist_get('perl_script', '', SCRIPT_NAME); - my $filename = weechat::infolist_string($infolistptr, 'filename') if weechat::infolist_next($infolistptr); - weechat::infolist_free($infolistptr); - return $filename unless @_; -} - -{ -package Nlib; -# this is a weechat perl library -use strict; use warnings; no warnings 'redefine'; - -## get_key_name -- get key name valid for any weechat version -## $key_name - name of key as valid in weechat 4.0.0; note: only supports keys used in this file, not any key -## $secondary_code - for keys with multiple codes, set to 1 to return the second code instead of the first -## returns key name unchanged if weechat >= 4.0.0; key name translated to old name if weechat < 4.0.0 -sub get_key_name { - my $key_name = shift; - my $secondary_code = shift; - return $key_name if (weechat::info_get('version_number', '') || 0) >= 0x04000000; - return "ctrl-H" if $key_name eq "backspace" && $secondary_code; - return "ctrl-?" if $key_name eq "backspace" && !$secondary_code; - return "meta2-A" if $key_name eq "up"; - return "meta2-B" if $key_name eq "down"; - $key_name =~ s/ctrl-(\w+)/ctrl-\U$1/; - return $key_name; -} - -## i2h -- copy weechat infolist content into perl hash -## $infolist - name of the infolist in weechat -## $ptr - pointer argument (infolist dependend) -## @args - arguments to the infolist (list dependend) -## $fields - string of ref type "fields" if only certain keys are needed (optional) -## returns perl list with perl hashes for each infolist entry -sub i2h { - my %i2htm = (i => 'integer', s => 'string', p => 'pointer', b => 'buffer', t => 'time'); - local *weechat::infolist_buffer = sub { '(not implemented)' }; - my ($infolist, $ptr, @args) = @_; - $ptr ||= ""; - my $fields = ref $args[-1] eq 'fields' ? ${ pop @args } : undef; - my $infptr = weechat::infolist_get($infolist, $ptr, do { local $" = ','; "@args" }); - my @infolist; - while (weechat::infolist_next($infptr)) { - my @fields = map { - my ($t, $v) = split ':', $_, 2; - bless \$v, $i2htm{$t}; - } - split ',', - ($fields || weechat::infolist_fields($infptr)); - push @infolist, +{ do { - my (%list, %local, @local); - map { - my $fn = 'weechat::infolist_'.ref $_; - my $r = do { no strict 'refs'; &$fn($infptr, $$_) }; - if ($$_ =~ /^localvar_name_(\d+)$/) { - $local[$1] = $r; - () - } - elsif ($$_ =~ /^(localvar)_value_(\d+)$/) { - $local{$local[$2]} = $r; - $1 => \%local - } - elsif ($$_ =~ /(.*?)((?:_\d+)+)$/) { - my ($key, $idx) = ($1, $2); - my @idx = split '_', $idx; shift @idx; - my $target = \$list{$key}; - for my $x (@idx) { - my $o = 1; - if ($key eq 'key' or $key eq 'key_command') { - $o = 0; - } - if ($x-$o < 0) { - local $" = '|'; - weechat::print('',"list error: $target/$$_/$key/$x/$idx/@idx(@_)"); - $o = 0; - } - $target = \$$target->[$x-$o] - } - $$target = $r; - - $key => $list{$key} - } - else { - $$_ => $r - } - } @fields - } }; - } - weechat::infolist_free($infptr); - !wantarray && @infolist ? \@infolist : @infolist -} - -## hdh -- hdata helper -## $_[0] - arg pointer or hdata list name -## $_[1] - hdata name -## $_[2..$#_] - hdata variable name -## $_[-1] - hashref with key/value to update (optional) -## returns value of hdata, and hdata name in list ctx, or number of variables updated -sub hdh { - if (@_ > 1 && $_[0] !~ /^0x/ && $_[0] !~ /^\d+$/) { - my $arg = shift; - unshift @_, weechat::hdata_get_list(weechat::hdata_get($_[0]), $arg); - } - while (@_ > 2) { - my ($arg, $name, $var) = splice @_, 0, 3; - my $hdata = weechat::hdata_get($name); - unless (ref $var eq 'HASH') { - $var =~ s/!(.*)/weechat::hdata_get_string($hdata, $1)/e; - (my $plain_var = $var) =~ s/^\d+\|//; - my $type = weechat::hdata_get_var_type_string($hdata, $plain_var); - if ($type eq 'pointer') { - my $name = weechat::hdata_get_var_hdata($hdata, $var); - unshift @_, $name if $name; - } - - my $fn = "weechat::hdata_$type"; - unshift @_, do { no strict 'refs'; - &$fn($hdata, $arg, $var) }; - } - else { - return weechat::hdata_update($hdata, $arg, $var); - } - } - wantarray ? @_ : $_[0] -} - -use Pod::Select qw(); -use Pod::Simple::TextContent; - -## get_desc_from_pod -- return setting description from pod documentation -## $file - filename with pod -## $setting - name of setting -## returns description as text -sub get_desc_from_pod { - my $file = shift; - return unless -s $file; - my $setting = shift; - - open my $pod_sel, '>', \my $ss; - Pod::Select::podselect({ - -output => $pod_sel, - -sections => ["SETTINGS/$setting"]}, $file); - - my $pt = new Pod::Simple::TextContent; - $pt->output_string(\my $ss_f); - $pt->parse_string_document($ss); - - my ($res) = $ss_f =~ /^\s*\Q$setting\E\s+(.*)\s*/; - $res -} - -## get_settings_from_pod -- retrieve all settings in settings section of pod -## $file - file with pod -## returns list of all settings -sub get_settings_from_pod { - my $file = shift; - return unless -s $file; - - open my $pod_sel, '>', \my $ss; - Pod::Select::podselect({ - -output => $pod_sel, - -sections => ["SETTINGS//!.+"]}, $file); - - $ss =~ /^=head2\s+(.*)\s*$/mg -} - -## mangle_man_for_wee -- turn man output into weechat codes -## @_ - list of grotty lines that should be turned into weechat attributes -## returns modified lines and modifies lines in-place -sub mangle_man_for_wee { - for (@_) { - s/_\x08(.)/weechat::color('underline').$1.weechat::color('-underline')/ge; - s/(.)\x08\1/weechat::color('bold').$1.weechat::color('-bold')/ge; - } - wantarray ? @_ : $_[0] -} - -## read_manpage -- read a man page in weechat window -## $file - file with pod -## $name - buffer name -sub read_manpage { - my $caller_package = (caller)[0]; - my $file = shift; - my $name = shift; - - if (my $obuf = weechat::buffer_search('perl', "man $name")) { - eval qq{ - package $caller_package; - weechat::buffer_close(\$obuf); - }; - } - - my @wee_keys = Nlib::i2h('key'); - my @keys; - - my $winptr = weechat::current_window(); - my ($wininfo) = Nlib::i2h('window', $winptr); - my $buf = weechat::buffer_new("man $name", '', '', '', ''); - return weechat::WEECHAT_RC_OK unless $buf; - - my $width = $wininfo->{chat_width}; - --$width if $wininfo->{chat_width} < $wininfo->{width} || ($wininfo->{width_pct} < 100 && (grep { $_->{y} == $wininfo->{y} } Nlib::i2h('window'))[-1]{x} > $wininfo->{x}); - $width -= 2; # when prefix is shown - - weechat::buffer_set($buf, 'time_for_each_line', 0); - eval qq{ - package $caller_package; - weechat::buffer_set(\$buf, 'display', 'auto'); - }; - die $@ if $@; - - @keys = map { $_->{key} } - grep { $_->{command} eq '/input history_previous' || - $_->{command} eq '/input history_global_previous' } @wee_keys; - @keys = Nlib::get_key_name('up') unless @keys; - weechat::buffer_set($buf, "key_bind_$_", '/window scroll -1') for @keys; - - @keys = map { $_->{key} } - grep { $_->{command} eq '/input history_next' || - $_->{command} eq '/input history_global_next' } @wee_keys; - @keys = Nlib::get_key_name('down') unless @keys; - weechat::buffer_set($buf, "key_bind_$_", '/window scroll +1') for @keys; - - weechat::buffer_set($buf, 'key_bind_ ', '/window page_down'); - - @keys = map { $_->{key} } - grep { $_->{command} eq '/input delete_previous_char' } @wee_keys; - @keys = (Nlib::get_key_name('backspace'), Nlib::get_key_name('backspace', 1)) unless @keys; - weechat::buffer_set($buf, "key_bind_$_", '/window page_up') for @keys; - - weechat::buffer_set($buf, 'key_bind_g', '/window scroll_top'); - weechat::buffer_set($buf, 'key_bind_G', '/window scroll_bottom'); - - weechat::buffer_set($buf, 'key_bind_q', '/buffer close'); - - weechat::print($buf, " \t".mangle_man_for_wee($_)) # weird bug with \t\t showing nothing? - for `pod2man \Q$file\E 2>/dev/null | GROFF_NO_SGR=1 nroff -mandoc -rLL=${width}n -rLT=${width}n -Tutf8 2>/dev/null`; - weechat::command($buf, '/window scroll_top'); - - unless (hdh($buf, 'buffer', 'lines', 'lines_count') > 0) { - weechat::print($buf, weechat::prefix('error').$_) - for "Unfortunately, your @{[weechat::color('underline')]}nroff". - "@{[weechat::color('-underline')]} command did not produce". - " any output.", - "Working pod2man and nroff commands are required for the ". - "help viewer to work.", - "In the meantime, please use the command ", '', - "\tperldoc $file", '', - "on your shell instead in order to read the manual.", - "Thank you and sorry for the inconvenience." - } -} - -1 -} - -our $MAGIC_ENTER_TIMER; -our $MAGIC_LOCK; -our $MAGIC_LOCK_TIMER; -our $WEECHAT_PASTE_FIX_CTRLJ_CMD; -our $INPUT_CHANGED_EATER_FLAG; -our $IGNORE_INPUT_CHANGED; -our $IGNORE_INPUT_CHANGED2; - -use constant INPUT_NL => '/input insert \x0a'; -use constant INPUT_MAGIC => '/input magic_enter'; -our $KEY_RET = Nlib::get_key_name('ctrl-m'); -our $NL = "\x0a"; - -init_multiline(); - -my $magic_enter_cancel_dynamic = 1; -my $paste_undo_start_ignore_dynamic = 0; -my $input_changed_eater_dynamic = 0; -my $multiline_complete_fix_dynamic = 1; - -sub magic_enter_cancel_dynamic { $magic_enter_cancel_dynamic ? &magic_enter_cancel : weechat::WEECHAT_RC_OK } -sub paste_undo_start_ignore_dynamic { $paste_undo_start_ignore_dynamic ? &paste_undo_start_ignore : weechat::WEECHAT_RC_OK } -sub input_changed_eater_dynamic { $input_changed_eater_dynamic ? &input_changed_eater : weechat::WEECHAT_RC_OK } -sub multiline_complete_fix_dynamic { $multiline_complete_fix_dynamic ? &multiline_complete_fix : weechat::WEECHAT_RC_OK } - -weechat::hook_config('plugins.var.perl.'.SCRIPT_NAME.'.*', 'default_options', ''); -weechat::hook_modifier('input_text_display_with_cursor', 'multiline_display', ''); -weechat::hook_command_run('/help '.SCRIPT_NAME, 'help_cmd', ''); -weechat::hook_command_run(INPUT_MAGIC, 'magic_enter', ''); -weechat::hook_signal('input_text_*', 'magic_enter_cancel_dynamic', ''); -weechat::hook_command_run('/input *', 'paste_undo_start_ignore_dynamic', ''); -weechat::hook_signal('2000|input_text_changed', 'input_changed_eater_dynamic', ''); -weechat::hook_signal('key_pressed', 'magic_lock_hatch', ''); -# we need lower than default priority here or the first character is separated -weechat::hook_signal('500|input_text_changed', 'paste_undo_hack', '') - # can only do this on weechat 0.4.0 - if (weechat::info_get('version_number', '') || 0) >= 0x00040000; -weechat::hook_command_run("1500|/input complete*", 'multiline_complete_fix_dynamic', 'complete*'); -weechat::hook_command_run("1500|/input delete_*", 'multiline_complete_fix_dynamic', 'delete_*'); -weechat::hook_command_run("1500|/input move_*", 'multiline_complete_fix_dynamic', 'move_*'); - -sub _stack_depth { - my $depth = -1; - 1 while caller(++$depth); - $depth; -} - -## multiline_display -- show multi-lines on display of input string -## () - modifier handler -## $_[2] - buffer pointer -## $_[3] - input string -## returns modified input string -sub multiline_display { - Encode::_utf8_on($_[3]); - Encode::_utf8_on(my $nl = weechat::config_get_plugin('char') || ' '); - Encode::_utf8_on(my $tab = weechat::config_get_plugin('tab')); - my $cb = weechat::current_buffer() eq $_[2] && $MAGIC_ENTER_TIMER; - if ($cb) { - $_[3] =~ s/$NL\x19b#/\x19b#/ if weechat::config_string_to_boolean(weechat::config_get_plugin('hide_magic_nl')); - } - if ($_[3] =~ s/$NL/$nl\x0d/g) { - $_[3] =~ s/\A/ \x0d/ if weechat::config_string_to_boolean(weechat::config_get_plugin('lead_linebreak')); - } - $_[3] =~ s/\x09/$tab/g if $tab; - if ($cb) { - Encode::_utf8_on(my $magic = weechat::config_get_plugin('magic')); - $_[3] =~ s/\Z/$magic/ if $magic; - } - $_[3] -} - -## lock_timer_exp -- expire the magic lock timer -sub lock_timer_exp { - if ($MAGIC_LOCK_TIMER) { - weechat::unhook($MAGIC_LOCK_TIMER); - $MAGIC_LOCK_TIMER = undef; - } - weechat::WEECHAT_RC_OK -} - -## paste_undo_stop_ignore -- unset ignore2 flag -sub paste_undo_stop_ignore { - $IGNORE_INPUT_CHANGED2 = undef; - weechat::WEECHAT_RC_OK -} - -## paste_undo_start_ignore -- set ignore2 flag when /input is received so to allow /input undo/redo -## () - command_run handler -## $_[2] - command that was called -sub paste_undo_start_ignore { - return weechat::WEECHAT_RC_OK if $IGNORE_INPUT_CHANGED; - return weechat::WEECHAT_RC_OK if $_[2] =~ /insert/; - $IGNORE_INPUT_CHANGED2 = 1; - weechat::WEECHAT_RC_OK -} - -## paste_undo_hack -- fix up undo stack when paste is detected by calling /input undo -## () - signal handler -## $_[2] - buffer pointer -sub paste_undo_hack { - return weechat::WEECHAT_RC_OK if $IGNORE_INPUT_CHANGED; - return paste_undo_stop_ignore() if $IGNORE_INPUT_CHANGED2; - if ($MAGIC_LOCK > 0 && get_lock_enabled()) { - signall_ignore_input_changed(1); - $paste_undo_start_ignore_dynamic = 1; - - Encode::_utf8_on(my $input = weechat::buffer_get_string($_[2], 'input')); - my $pos = weechat::buffer_get_integer($_[2], 'input_pos'); - - weechat::command($_[2], '/input undo') for 1..2; - - weechat::buffer_set($_[2], 'input', $input); - weechat::buffer_set($_[2], 'input_pos', $pos); - - $paste_undo_start_ignore_dynamic = 0; - signall_ignore_input_changed(0); - } - weechat::WEECHAT_RC_OK -} - -## input_changed_eater -- suppress input_text_changed signal on new weechats -## () - signal handler -sub input_changed_eater { - $INPUT_CHANGED_EATER_FLAG = undef; - weechat::WEECHAT_RC_OK_EAT -} - -## signall_ignore_input_changed -- use various methods to "ignore" input_text_changed signal -## $_[0] - start ignore or stop ignore -sub signall_ignore_input_changed { - if ($_[0]) { - weechat::hook_signal_send('input_flow_free', weechat::WEECHAT_HOOK_SIGNAL_INT, 1); - $input_changed_eater_dynamic = 1; - $IGNORE_INPUT_CHANGED = 1; - weechat::buffer_set('', 'completion_freeze', '1'); - } - else { - weechat::buffer_set('', 'completion_freeze', '0'); - $IGNORE_INPUT_CHANGED = undef; - $input_changed_eater_dynamic = 0; - weechat::hook_signal_send('input_flow_free', weechat::WEECHAT_HOOK_SIGNAL_INT, 0); - } -} - -## multiline_complete_fix -- add per line /input handling for completion, movement and deletion -## () - command_run handler -## $_[0] - original bound data -## $_[1] - buffer pointer -## $_[2] - original command -sub multiline_complete_fix { - $magic_enter_cancel_dynamic = 0; - $multiline_complete_fix_dynamic = 0; - if ($_[2] =~ s/_message$/_line/ || !weechat::config_string_to_boolean(weechat::config_get_plugin('modify_keys'))) { - weechat::command($_[1], $_[2]); - } - else { - signall_ignore_input_changed(1); - Encode::_utf8_on(my $input = weechat::buffer_get_string($_[1], 'input')); - my $pos = weechat::buffer_get_integer($_[1], 'input_pos'); - if ($pos && $_[2] =~ /(?:previous|beginning_of)_/ && (substr $input, $pos-1, 1) eq $NL) { - substr $input, $pos-1, 1, "\0" - } - elsif ($pos < length $input && $_[2] =~ /(?:next|end_of)_/ && (substr $input, $pos, 1) eq $NL) { - substr $input, $pos, 1, "\0" - } - my @lines = $pos ? (split /$NL/, (substr $input, 0, $pos), -1) : ''; - my @after = $pos < length $input ? (split /$NL/, (substr $input, $pos), -1) : ''; - $lines[-1] =~ s/\0$/$NL/; - $after[0] =~ s/^\0/$NL/; - my ($p1, $p2) = (pop @lines, shift @after); - weechat::buffer_set($_[1], 'input', $p1.$p2); - weechat::buffer_set($_[1], 'input_pos', length $p1); - - $magic_enter_cancel_dynamic = 1; - $INPUT_CHANGED_EATER_FLAG = 1; - weechat::command($_[1], $_[2]); - my $changed_later = !$INPUT_CHANGED_EATER_FLAG; - magic_enter_cancel() if $changed_later; - $magic_enter_cancel_dynamic = 0; - - Encode::_utf8_on(my $p = weechat::buffer_get_string($_[1], 'input')); - $pos = weechat::buffer_get_integer($_[1], 'input_pos'); - weechat::command($_[1], '/input undo') if @lines || @after; - weechat::command($_[1], '/input undo'); - weechat::buffer_set($_[1], 'input', join $NL, @lines, $p, @after); - weechat::buffer_set($_[1], 'input_pos', $pos+length join $NL, @lines, ''); - - signall_ignore_input_changed(0); - weechat::hook_signal_send('input_text_changed', weechat::WEECHAT_HOOK_SIGNAL_POINTER, $_[1]) if $changed_later; - } - $multiline_complete_fix_dynamic = 1; - $magic_enter_cancel_dynamic = 1; - weechat::WEECHAT_RC_OK_EAT -} - -## help_cmd -- show multi-line script documentation -## () - command_run handler -sub help_cmd { - Nlib::read_manpage(SCRIPT_FILE, SCRIPT_NAME); - weechat::WEECHAT_RC_OK_EAT -} - -## get_lock_time -- gets timeout for paste detection according to setting -## returns timeout (at least 1) -sub get_lock_time { - my $lock_time = weechat::config_get_plugin('paste_lock'); - $lock_time = 1 unless $lock_time =~ /^\d+$/ && $lock_time; - $lock_time -} - -## get_lock_enabled -- checks whether the paste detection lock is enabled -## returns bool -sub get_lock_enabled { - my $lock = weechat::config_get_plugin('paste_lock'); - $lock = weechat::config_string_to_boolean($lock) - unless $lock =~ /^\d+$/; - $lock -} - -## magic_lock_hatch -- set a timer for paste detection -## () - signal handler -sub magic_lock_hatch { - lock_timer_exp(); - $MAGIC_LOCK_TIMER = weechat::hook_timer(get_lock_time(), 0, 1, 'lock_timer_exp', ''); - weechat::WEECHAT_RC_OK -} - -## magic_unlock -- reduce the lock added by paste detection -## () - timer handler -sub magic_unlock { - if ($MAGIC_LOCK_TIMER) { - weechat::hook_timer(get_lock_time(), 0, 1, 'magic_unlock', ''); - } - else { - --$MAGIC_LOCK; - if (!$MAGIC_LOCK && $WEECHAT_PASTE_FIX_CTRLJ_CMD) { - do_key_bind(Nlib::get_key_name('ctrl-j'), $WEECHAT_PASTE_FIX_CTRLJ_CMD); - $WEECHAT_PASTE_FIX_CTRLJ_CMD = undef; - } - } - weechat::WEECHAT_RC_OK -} - -## get_magic_enter_time -- get timeout for auto-sending messages according to config -## returns timeout -sub get_magic_enter_time { - my $magic_enter = weechat::config_get_plugin('magic_enter_time'); - $magic_enter = 1000 * weechat::config_string_to_boolean($magic_enter) - unless $magic_enter =~ /^\d+$/; - $magic_enter -} - -## magic_enter -- receive enter key and do magic things: set up a timer for sending the message, add newline -## () - command_run handler -## $_[1] - buffer pointer -sub magic_enter { - Encode::_utf8_on(my $input = weechat::buffer_get_string($_[1], 'input')); - if (!length $input && weechat::config_string_to_boolean(weechat::config_get_plugin('send_empty'))) { - weechat::command($_[1], '/input return'); - } - else { - magic_enter_cancel(); - weechat::command($_[1], INPUT_NL); - - unless (get_lock_enabled() && $MAGIC_LOCK) { - if (weechat::config_string_to_boolean(weechat::config_get_plugin('magic_paste_only')) && - $input !~ /$NL/) { - magic_enter_send($_[1]); - } - elsif (my $magic_enter = get_magic_enter_time()) { - $MAGIC_ENTER_TIMER = weechat::hook_timer($magic_enter, 0, 1, 'magic_enter_send', $_[1]); - } - } - } - weechat::WEECHAT_RC_OK_EAT -} - -## magic_enter_send -- actually send enter key when triggered by magic_enter, remove preceding newline -## $_[0] - buffer pointer -## sending is delayed by 1ms to circumvent crash bug in api -sub magic_enter_send { - magic_enter_cancel(); - weechat::command($_[0], '/input delete_previous_char'); - weechat::command($_[0], '/wait 1ms /input return'); - weechat::WEECHAT_RC_OK -} - -## magic_enter_cancel -- cancel the timer for automatic sending of message, for example when more text was added, increase the paste lock for paste detection when used as signal handler -## () - signal handler when @_ is set -sub magic_enter_cancel { - if ($MAGIC_ENTER_TIMER) { - weechat::unhook($MAGIC_ENTER_TIMER); - $MAGIC_ENTER_TIMER = undef; - } - if ($MAGIC_LOCK_TIMER && @_) { - if (!$MAGIC_LOCK && !$WEECHAT_PASTE_FIX_CTRLJ_CMD && - weechat::config_string_to_boolean(weechat::config_get_plugin('weechat_paste_fix'))) { - ($WEECHAT_PASTE_FIX_CTRLJ_CMD) = get_key_command(Nlib::get_key_name('ctrl-j')); - $WEECHAT_PASTE_FIX_CTRLJ_CMD = '-' unless defined $WEECHAT_PASTE_FIX_CTRLJ_CMD; - do_key_bind(Nlib::get_key_name('ctrl-j'), '-'); - } - if ($MAGIC_LOCK < 1) { - my $lock_time = get_lock_time(); - ++$MAGIC_LOCK; - weechat::hook_timer(get_lock_time(), 0, 1, 'magic_unlock', ''); - } - } - weechat::WEECHAT_RC_OK -} - -## need_magic_enter -- check if magic enter keybinding is needed according to config settings -## returns bool -sub need_magic_enter { - weechat::config_string_to_boolean(weechat::config_get_plugin('send_empty')) || get_magic_enter_time() || - weechat::config_string_to_boolean(weechat::config_get_plugin('magic_paste_only')) -} - -## do_key_bind -- mute execute a key binding, or unbind if $_[-1] is '-' -## @_ - arguments to /key bind -sub do_key_bind { - if ($_[-1] eq '-') { - pop; - weechat::command('', "/mute /key unbind @_"); - } - elsif ($_[-1] eq '!') { - pop; - weechat::command('', "/mute /key reset @_"); - } - else { - weechat::command('', "/mute /key bind @_"); - } -} - -{ my %keys; -## get_key_command -- get the command bound to a key -## $_[0] - key in weechat syntax -## returns the command - sub get_key_command { - unless (exists $keys{$_[0]}) { - ($keys{$_[0]}) = - map { $_->{command} } grep { $_->{key} eq $_[0] } - Nlib::i2h('key') - } - $keys{$_[0]} - } -} - -## default_options -- set up default option values on start and when unset -## () - config handler if @_ is set -sub default_options { - my %defaults = ( - char => '↩', - tab => '──▶▏', - magic => '‼', - ipl => 'on', - lead_linebreak => 'on', - modify_keys => 'on', - send_empty => 'on', - magic_enter_time => '1000', - paste_lock => '1', - magic_paste_only => 'off', - hide_magic_nl => 'on', - weechat_paste_fix => 'on', - ); - unless (weechat::config_is_set_plugin('ipl')) { - if (my $bar = weechat::bar_search('input')) { - weechat::bar_set($bar, $_, '0') for 'size', 'size_max'; - } - } - for (keys %defaults) { - weechat::config_set_plugin($_, $defaults{$_}) - unless weechat::config_is_set_plugin($_); - } - do_key_bind($KEY_RET, INPUT_NL) - if weechat::config_string_to_boolean(weechat::config_get_plugin('ipl')); - my ($enter_key) = get_key_command($KEY_RET); - if (need_magic_enter()) { - do_key_bind($KEY_RET, INPUT_MAGIC) - if defined $enter_key && $enter_key eq INPUT_NL; - } - else { - do_key_bind($KEY_RET, INPUT_NL) - if defined $enter_key && $enter_key eq INPUT_MAGIC; - } - weechat::WEECHAT_RC_OK -} - -sub init_multiline { - $MAGIC_LOCK = -1; - default_options(); - my $sf = SCRIPT_FILE; - for (Nlib::get_settings_from_pod($sf)) { - weechat::config_set_desc_plugin($_, Nlib::get_desc_from_pod($sf, $_)); - } - weechat::WEECHAT_RC_OK -} - -sub stop_multiline { - magic_enter_cancel(); - if (need_magic_enter()) { - my ($enter_key) = get_key_command($KEY_RET); - do_key_bind($KEY_RET, INPUT_NL) - if defined $enter_key && $enter_key eq INPUT_MAGIC; - } - if ($WEECHAT_PASTE_FIX_CTRLJ_CMD) { - do_key_bind(Nlib::get_key_name('ctrl-j'), $WEECHAT_PASTE_FIX_CTRLJ_CMD); - $WEECHAT_PASTE_FIX_CTRLJ_CMD = undef; - } - if (weechat::config_string_to_boolean(weechat::config_get_plugin('ipl'))) { - do_key_bind($KEY_RET, '!'); - } - weechat::WEECHAT_RC_OK -}