# HG changeset patch # User Steve Losh # Date 1629733878 14400 # Node ID 7023c500dd7a5afc52c7d9ee2bac36b5b58a2bba # Parent d4ff2322e2aba6459b4a203d3ceae943e5bd8d01 More diff -r d4ff2322e2ab -r 7023c500dd7a gitconfig --- a/gitconfig Tue Aug 17 15:20:33 2021 -0400 +++ b/gitconfig Mon Aug 23 11:51:18 2021 -0400 @@ -8,6 +8,7 @@ excludesfile = ~/.gitignore [alias] + mainbranch = "!f() { git branch | grep -P ' (master|main)$' | sed -Ee 's/.* //'; }; f" tags = tag -l branches = branch -a remotes = remote -v @@ -33,9 +34,6 @@ ci = commit cm = commit -m - rom = rebase origin/master - rec = rebase --continue - d = diff di = diff --cached dl = "!f() { git diff \"$@\" | nvim -R -c 'set ft=diff' -; }; f" @@ -84,7 +82,7 @@ fuu = "!sh -c 'git fu && git uu' -" fuo = "!sh -c 'git fo && git uo' -" - fuom = "!sh -c 'git co master && git fo && git uo' -" + fuom = "!sh -c 'git co $(git mainbranch) && git fo && git uo' -" addremove = !git add . && git add -u addrem = !git addremove diff -r d4ff2322e2ab -r 7023c500dd7a lispwords --- a/lispwords Tue Aug 17 15:20:33 2021 -0400 +++ b/lispwords Mon Aug 23 11:51:18 2021 -0400 @@ -121,6 +121,7 @@ ; stumpwm (3 defcommand) +(1 when-let-window) ; adopt (2 define-interface) @@ -140,3 +141,4 @@ ; boots (1 event-case) + diff -r d4ff2322e2ab -r 7023c500dd7a psqlrc --- a/psqlrc Tue Aug 17 15:20:33 2021 -0400 +++ b/psqlrc Mon Aug 23 11:51:18 2021 -0400 @@ -10,3 +10,4 @@ \setenv PAGER less \setenv LESS -iS + diff -r d4ff2322e2ab -r 7023c500dd7a stumpwmrc --- a/stumpwmrc Tue Aug 17 15:20:33 2021 -0400 +++ b/stumpwmrc Mon Aug 23 11:51:18 2021 -0400 @@ -7,7 +7,7 @@ ;;;; Config ------------------------------------------------------------------- (set-prefix-key (kbd "C-space")) -(redirect-all-output (data-dir-file "debug" "log")) +(defvar *redirected* (redirect-all-output (data-dir-file "debug" "log"))) (setf *mouse-focus-policy* :click *message-window-gravity* :center @@ -144,6 +144,10 @@ "Find and return a fresh list of all windows that match `query` under `window-match-p`." (remove-if-not (lambda (w) (window-match-p query w)) (all-windows))) +(defmacro when-let-window ((symbol title-query) &body body) + `(when-let ((,symbol (find-window `(:title ,(ppcre:create-scanner ,title-query))))) + ,@body)) + ;;;; Posture ------------------------------------------------------------------ (defparameter *posture-thread* nil) @@ -402,10 +406,17 @@ (run-shell-command (format nil "mark ~A" thing))) (defcommand toggle-zoom-mute () () - (when-let ((win (find-window `(:title ,(ppcre:create-scanner "^Zoom Meeting.*"))))) + (when-let-window (win "^Zoom Meeting.*") (focus-window win t) ; Zoom stupidly won't accept the shortcut unless it's in focus (meta (kbd "M-a")))) +(defcommand end-zoom () () + (when-let-window (win "^Zoom Meeting.*") + (kill-window win)) + (sleep 2) + (when-let-window (win "^Zoom -.*") + (kill-window win))) + ;;;; Terminal Fonts ----------------------------------------------------------- (defcommand reload-terminal-font-size () @@ -473,8 +484,8 @@ (run-or-raise "gcontrol" '(:class "Gnome-control-center"))) (defcommand zoom () () - (when-let ((window (find-window `(:title ,(ppcre:create-scanner "^Zoom Meeting.*"))))) - (focus-window window t))) + (when-let-window (w "^Zoom Meeting.*") + (focus-window w t))) ;;;; Timers ------------------------------------------------------------------- @@ -547,6 +558,7 @@ ("H-o" "files") ("H-z" "zoom") ("XF86Launch8" "toggle-zoom-mute") + ("H-XF86Launch8" "end-zoom") ("H-q" "exec lock-screen") ("H-y" "screenshot") ("H-g" "gcontrol") diff -r d4ff2322e2ab -r 7023c500dd7a weechat/alias.conf --- a/weechat/alias.conf Tue Aug 17 15:20:33 2021 -0400 +++ b/weechat/alias.conf Mon Aug 23 11:51:18 2021 -0400 @@ -1,10 +1,10 @@ # -# weechat -- alias.conf +# WeeChat -- alias.conf # # WARNING: It is NOT recommended to edit this file by hand, # especially if WeeChat is running. # -# Use /set or similar command to change settings in WeeChat. +# Use commands like /set or /fset to change settings in WeeChat. # # For more info, see: https://weechat.org/doc/quickstart # diff -r d4ff2322e2ab -r 7023c500dd7a weechat/autosort.conf --- a/weechat/autosort.conf Tue Aug 17 15:20:33 2021 -0400 +++ b/weechat/autosort.conf Mon Aug 23 11:51:18 2021 -0400 @@ -1,18 +1,24 @@ # -# weechat -- autosort.conf +# WeeChat -- autosort.conf # # WARNING: It is NOT recommended to edit this file by hand, # especially if WeeChat is running. # -# Use /set or similar command to change settings in WeeChat. +# Use commands like /set or /fset to change settings in WeeChat. # # For more info, see: https://weechat.org/doc/quickstart # [sorting] case_sensitive = off -group_irc = on +debug_log = off replacements = "[]" rules = "[["core", 0], ["irc", 2], ["*", 1], ["irc.irc_raw", 0], ["irc.server", 1]]" +signal_delay = 5 signals = "buffer_opened buffer_merged buffer_unmerged buffer_renamed" +sort_limit = 100 sort_on_config_change = on + +[v3] +helpers = "{"core_first": "${if:${buffer.full_name}!=core.weechat}", "irc_raw_first": "${if:${buffer.full_name}!=irc.irc_raw}", "irc_raw_last": "${if:${buffer.full_name}==irc.irc_raw}", "hashless_name": "${info:autosort_replace,#,,${info:autosort_escape,${buffer.name}}}", "script_or_plugin": "${if:${script_name}?${script_name}:${plugin}}"}" +rules = "["${core_first}", "${info:autosort_order,${info:autosort_escape,${script_or_plugin}},core,*,irc,bitlbee,matrix,slack}", "${script_or_plugin}", "${irc_raw_first}", "${server}", "${info:autosort_order,${type},server,*,channel,private}", "${hashless_name}", "${buffer.full_name}"]" diff -r d4ff2322e2ab -r 7023c500dd7a weechat/buflist.conf --- a/weechat/buflist.conf Tue Aug 17 15:20:33 2021 -0400 +++ b/weechat/buflist.conf Mon Aug 23 11:51:18 2021 -0400 @@ -1,10 +1,10 @@ # -# weechat -- buflist.conf +# WeeChat -- buflist.conf # # WARNING: It is NOT recommended to edit this file by hand, # especially if WeeChat is running. # -# Use /set or similar command to change settings in WeeChat. +# Use commands like /set or /fset to change settings in WeeChat. # # For more info, see: https://weechat.org/doc/quickstart # @@ -21,6 +21,7 @@ nick_prefix_empty = on signals_refresh = "" sort = "number,-active" +use_items = 1 [format] buffer = "${format_number}${indent}${format_nick_prefix}${color_hotlist}${format_name}" @@ -37,3 +38,4 @@ name = "${name}" nick_prefix = "${color_nick_prefix}${nick_prefix}" number = "${color:green}${number}${if:${number_displayed}?.: }" +tls_version = " ${color:default}(${if:${tls_version}==TLS1.3?${color:green}:${if:${tls_version}==TLS1.2?${color:yellow}:${color:red}}}${translate:${tls_version}}${color:default})" diff -r d4ff2322e2ab -r 7023c500dd7a weechat/charset.conf --- a/weechat/charset.conf Tue Aug 17 15:20:33 2021 -0400 +++ b/weechat/charset.conf Mon Aug 23 11:51:18 2021 -0400 @@ -1,10 +1,10 @@ # -# weechat -- charset.conf +# WeeChat -- charset.conf # # WARNING: It is NOT recommended to edit this file by hand, # especially if WeeChat is running. # -# Use /set or similar command to change settings in WeeChat. +# Use commands like /set or /fset to change settings in WeeChat. # # For more info, see: https://weechat.org/doc/quickstart # diff -r d4ff2322e2ab -r 7023c500dd7a weechat/exec.conf --- a/weechat/exec.conf Tue Aug 17 15:20:33 2021 -0400 +++ b/weechat/exec.conf Mon Aug 23 11:51:18 2021 -0400 @@ -1,10 +1,10 @@ # -# weechat -- exec.conf +# WeeChat -- exec.conf # # WARNING: It is NOT recommended to edit this file by hand, # especially if WeeChat is running. # -# Use /set or similar command to change settings in WeeChat. +# Use commands like /set or /fset to change settings in WeeChat. # # For more info, see: https://weechat.org/doc/quickstart # @@ -12,6 +12,7 @@ [command] default_options = "" purge_delay = 0 +shell = "${env:SHELL}" [color] flag_finished = lightred diff -r d4ff2322e2ab -r 7023c500dd7a weechat/fifo.conf --- a/weechat/fifo.conf Tue Aug 17 15:20:33 2021 -0400 +++ b/weechat/fifo.conf Mon Aug 23 11:51:18 2021 -0400 @@ -1,10 +1,10 @@ # -# weechat -- fifo.conf +# WeeChat -- fifo.conf # # WARNING: It is NOT recommended to edit this file by hand, # especially if WeeChat is running. # -# Use /set or similar command to change settings in WeeChat. +# Use commands like /set or /fset to change settings in WeeChat. # # For more info, see: https://weechat.org/doc/quickstart # diff -r d4ff2322e2ab -r 7023c500dd7a weechat/fset.conf --- a/weechat/fset.conf Tue Aug 17 15:20:33 2021 -0400 +++ b/weechat/fset.conf Mon Aug 23 11:51:18 2021 -0400 @@ -1,15 +1,16 @@ # -# weechat -- fset.conf +# WeeChat -- fset.conf # # WARNING: It is NOT recommended to edit this file by hand, # especially if WeeChat is running. # -# Use /set or similar command to change settings in WeeChat. +# Use commands like /set or /fset to change settings in WeeChat. # # For more info, see: https://weechat.org/doc/quickstart # [look] +auto_refresh = "*" auto_unmark = off condition_catch_set = "${count} >= 1" export_help_default = on diff -r d4ff2322e2ab -r 7023c500dd7a weechat/logger.conf --- a/weechat/logger.conf Tue Aug 17 15:20:33 2021 -0400 +++ b/weechat/logger.conf Mon Aug 23 11:51:18 2021 -0400 @@ -1,10 +1,10 @@ # -# weechat -- logger.conf +# WeeChat -- logger.conf # # WARNING: It is NOT recommended to edit this file by hand, # especially if WeeChat is running. # -# Use /set or similar command to change settings in WeeChat. +# Use commands like /set or /fset to change settings in WeeChat. # # For more info, see: https://weechat.org/doc/quickstart # @@ -19,6 +19,7 @@ [file] auto_log = on +color_lines = off flush_delay = 120 fsync = off info_lines = off diff -r d4ff2322e2ab -r 7023c500dd7a weechat/python.conf --- a/weechat/python.conf Tue Aug 17 15:20:33 2021 -0400 +++ b/weechat/python.conf Mon Aug 23 11:51:18 2021 -0400 @@ -1,10 +1,10 @@ # -# weechat -- python.conf +# WeeChat -- python.conf # # WARNING: It is NOT recommended to edit this file by hand, # especially if WeeChat is running. # -# Use /set or similar command to change settings in WeeChat. +# Use commands like /set or /fset to change settings in WeeChat. # # For more info, see: https://weechat.org/doc/quickstart # diff -r d4ff2322e2ab -r 7023c500dd7a weechat/python/autoload/autosort.py --- a/weechat/python/autoload/autosort.py Tue Aug 17 15:20:33 2021 -0400 +++ b/weechat/python/autoload/autosort.py Mon Aug 23 11:51:18 2021 -0400 @@ -1,860 +1,1 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2013-2014 Maarten de Vries -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -# -# Autosort automatically keeps your buffers sorted and grouped by server. -# You can define your own sorting rules. See /help autosort for more details. -# -# http://github.com/de-vri.es/weechat-autosort -# - -# -# Changelog: -# 2.5: -# * Fix handling unicode buffer names. -# * Add hint to set irc.look.server_buffer to independent and buffers.look.indenting to on. -# 2.4: -# * Make script python3 compatible. -# 2.3: -# * Fix sorting items without score last (regressed in 2.2). -# 2.2: -# * Add configuration option for signals that trigger a sort. -# * Add command to manually trigger a sort (/autosort sort). -# * Add replacement patterns to apply before sorting. -# 2.1: -# * Fix some minor style issues. -# 2.0: -# * Allow for custom sort rules. -# - - -import weechat -import re -import json - -SCRIPT_NAME = 'autosort' -SCRIPT_AUTHOR = 'Maarten de Vries ' -SCRIPT_VERSION = '2.5' -SCRIPT_LICENSE = 'GPL3' -SCRIPT_DESC = 'Automatically or manually keep your buffers sorted and grouped by server.' - - -config = None -hooks = [] - -class HumanReadableError(Exception): - pass - - -def parse_int(arg, arg_name = 'argument'): - ''' Parse an integer and provide a more human readable error. ''' - arg = arg.strip() - try: - return int(arg) - except ValueError: - raise HumanReadableError('Invalid {0}: expected integer, got "{1}".'.format(arg_name, arg)) - - -class Pattern: - ''' A simple glob-like pattern for matching buffer names. ''' - - def __init__(self, pattern): - ''' Construct a pattern from a string. ''' - escaped = False - char_class = 0 - chars = '' - regex = '' - for c in pattern: - if escaped and char_class: - escaped = False - chars += re.escape(c) - elif escaped: - escaped = False - regex += re.escape(c) - elif c == '\\': - escaped = True - elif c == '*' and not char_class: - regex += '[^.]*' - elif c == '?' and not char_class: - regex += '[^.]' - elif c == '[' and not char_class: - char_class = 1 - chars = '' - elif c == '^' and char_class and not chars: - chars += '^' - elif c == ']' and char_class and chars not in ('', '^'): - char_class = False - regex += '[' + chars + ']' - elif c == '-' and char_class: - chars += '-' - elif char_class: - chars += re.escape(c) - else: - regex += re.escape(c) - - if char_class: - raise ValueError("unmatched opening '['") - if escaped: - raise ValueError("unexpected trailing '\\'") - - self.regex = re.compile('^' + regex + '$') - self.pattern = pattern - - def match(self, input): - ''' Match the pattern against a string. ''' - return self.regex.match(input) - - -class FriendlyList(object): - ''' A list with human readable errors. ''' - - def __init__(self): - self.__data = [] - - def raw(self): - return self.__data - - def append(self, value): - ''' Add a rule to the list. ''' - self.__data.append(value) - - def insert(self, index, value): - ''' Add a rule to the list. ''' - if not 0 <= index <= len(self): raise HumanReadableError('Index out of range: expected an integer in the range [0, {0}], got {1}.'.format(len(self), index)) - self.__data.insert(index, value) - - def pop(self, index): - ''' Remove a rule from the list and return it. ''' - if not 0 <= index < len(self): raise HumanReadableError('Index out of range: expected an integer in the range [0, {0}), got {1}.'.format(len(self), index)) - return self.__data.pop(index) - - def move(self, index_a, index_b): - ''' Move a rule to a new position in the list. ''' - self.insert(index_b, self.pop(index_a)) - - def swap(self, index_a, index_b): - ''' Swap two elements in the list. ''' - self[index_a], self[index_b] = self[index_b], self[index_a] - - def __len__(self): - return len(self.__data) - - def __getitem__(self, index): - if not 0 <= index < len(self): raise HumanReadableError('Index out of range: expected an integer in the range [0, {0}), got {1}.'.format(len(self), index)) - return self.__data[index] - - def __setitem__(self, index, value): - if not 0 <= index < len(self): raise HumanReadableError('Index out of range: expected an integer in the range [0, {0}), got {1}.'.format(len(self), index)) - self.__data[index] = value - - def __iter__(self): - return iter(self.__data) - - -class RuleList(FriendlyList): - ''' A list of rules to test buffer names against. ''' - rule_regex = re.compile(r'^(.*)=\s*([+-]?[^=]*)$') - - def __init__(self, rules): - ''' Construct a RuleList from a list of rules. ''' - super(RuleList, self).__init__() - for rule in rules: self.append(rule) - - def get_score(self, name, rules): - ''' Get the sort score of a partial name according to a rule list. ''' - for rule in self: - if rule[0].match(name): return rule[1] - return 999999999 - - def encode(self): - ''' Encode the rules for storage. ''' - return json.dumps(list(map(lambda x: (x[0].pattern, x[1]), self))) - - @staticmethod - def decode(blob): - ''' Parse rules from a string blob. ''' - result = [] - - try: - decoded = json.loads(blob) - except ValueError: - log('Invalid rules: expected JSON encoded list of pairs, got "{0}".'.format(blob)) - return [], 0 - - for rule in decoded: - # Rules must be a pattern,score pair. - if len(rule) != 2: - log('Invalid rule: expected (pattern, score), got "{0}". Rule ignored.'.format(rule)) - continue - - # Rules must have a valid pattern. - try: - pattern = Pattern(rule[0]) - except ValueError as e: - log('Invalid pattern: {0} in "{1}". Rule ignored.'.format(e, rule[0])) - continue - - # Rules must have a valid score. - try: - score = int(rule[1]) - except ValueError as e: - log('Invalid score: expected an integer, got "{0}". Rule ignored.'.format(score)) - continue - - result.append((pattern, score)) - - return RuleList(result) - - @staticmethod - def parse_rule(arg): - ''' Parse a rule argument. ''' - arg = arg.strip() - match = RuleList.rule_regex.match(arg) - if not match: - raise HumanReadableError('Invalid rule: expected " = ", got "{0}".'.format(arg)) - - pattern = match.group(1).strip() - try: - pattern = Pattern(pattern) - except ValueError as e: - raise HumanReadableError('Invalid pattern: {0} in "{1}".'.format(e, pattern)) - - score = parse_int(match.group(2), 'score') - return (pattern, score) - - -def decode_replacements(blob): - ''' Decode a replacement list encoded as JSON. ''' - result = FriendlyList() - try: - decoded = json.loads(blob) - except ValueError: - log('Invalid replacement list: expected JSON encoded list of pairs, got "{0}".'.format(blob)) - return [], 0 - - for replacement in decoded: - # Replacements must be a (string, string) pair. - if len(replacement) != 2: - log('Invalid replacement pattern: expected (pattern, replacement), got "{0}". Replacement ignored.'.format(rule)) - continue - result.append(replacement) - - return result - - -def encode_replacements(replacements): - ''' Encode a list of replacement patterns as JSON. ''' - return json.dumps(replacements.raw()) - - -class Config: - ''' The autosort configuration. ''' - - default_rules = json.dumps([ - ('core', 0), - ('irc', 2), - ('*', 1), - - ('irc.irc_raw', 0), - ('irc.server', 1), - ]) - - default_replacements = '[]' - default_signals = 'buffer_opened buffer_merged buffer_unmerged buffer_renamed' - - def __init__(self, filename): - ''' Initialize the configuration. ''' - - self.filename = filename - self.config_file = weechat.config_new(self.filename, '', '') - self.sorting_section = None - - self.case_sensitive = False - self.group_irc = True - self.rules = [] - self.replacements = [] - self.signals = [] - self.sort_on_config = True - - self.__case_sensitive = None - self.__group_irc = None - self.__rules = None - self.__replacements = None - self.__signals = None - self.__sort_on_config = None - - if not self.config_file: - log('Failed to initialize configuration file "{0}".'.format(self.filename)) - return - - self.sorting_section = weechat.config_new_section(self.config_file, 'sorting', False, False, '', '', '', '', '', '', '', '', '', '') - - if not self.sorting_section: - log('Failed to initialize section "sorting" of configuration file.') - weechat.config_free(self.config_file) - return - - self.__case_sensitive = weechat.config_new_option( - self.config_file, self.sorting_section, - 'case_sensitive', 'boolean', - 'If this option is on, sorting is case sensitive.', - '', 0, 0, 'off', 'off', 0, - '', '', '', '', '', '' - ) - - self.__group_irc = weechat.config_new_option( - self.config_file, self.sorting_section, - 'group_irc', 'boolean', - 'If this option is on, the script pretends that IRC channel/private buffers are renamed to "irc.server.{network}.{channel}" rather than "irc.{network}.{channel}".' + - 'This ensures that these buffers are grouped with their respective server buffer.', - '', 0, 0, 'on', 'on', 0, - '', '', '', '', '', '' - ) - - self.__rules = weechat.config_new_option( - self.config_file, self.sorting_section, - 'rules', 'string', - 'An ordered list of sorting rules encoded as JSON. See /help autosort for commands to manipulate these rules.', - '', 0, 0, Config.default_rules, Config.default_rules, 0, - '', '', '', '', '', '' - ) - - self.__replacements = weechat.config_new_option( - self.config_file, self.sorting_section, - 'replacements', 'string', - 'An ordered list of replacement patterns to use on buffer name components, encoded as JSON. See /help autosort for commands to manipulate these replacements.', - '', 0, 0, Config.default_replacements, Config.default_replacements, 0, - '', '', '', '', '', '' - ) - - self.__signals = weechat.config_new_option( - self.config_file, self.sorting_section, - 'signals', 'string', - 'The signals that will cause autosort to resort your buffer list. Seperate signals with spaces.', - '', 0, 0, Config.default_signals, Config.default_signals, 0, - '', '', '', '', '', '' - ) - - self.__sort_on_config = weechat.config_new_option( - self.config_file, self.sorting_section, - 'sort_on_config_change', 'boolean', - 'Decides if the buffer list should be sorted when autosort configuration changes.', - '', 0, 0, 'on', 'on', 0, - '', '', '', '', '', '' - ) - - if weechat.config_read(self.config_file) != weechat.WEECHAT_RC_OK: - log('Failed to load configuration file.') - - if weechat.config_write(self.config_file) != weechat.WEECHAT_RC_OK: - log('Failed to write configuration file.') - - self.reload() - - def reload(self): - ''' Load configuration variables. ''' - - self.case_sensitive = weechat.config_boolean(self.__case_sensitive) - self.group_irc = weechat.config_boolean(self.__group_irc) - - rules_blob = weechat.config_string(self.__rules) - replacements_blob = weechat.config_string(self.__replacements) - signals_blob = weechat.config_string(self.__signals) - - self.rules = RuleList.decode(rules_blob) - self.replacements = decode_replacements(replacements_blob) - self.signals = signals_blob.split() - self.sort_on_config = weechat.config_boolean(self.__sort_on_config) - - def save_rules(self, run_callback = True): - ''' Save the current rules to the configuration. ''' - weechat.config_option_set(self.__rules, RuleList.encode(self.rules), run_callback) - - def save_replacements(self, run_callback = True): - ''' Save the current replacement patterns to the configuration. ''' - weechat.config_option_set(self.__replacements, encode_replacements(self.replacements), run_callback) - - -def pad(sequence, length, padding = None): - ''' Pad a list until is has a certain length. ''' - return sequence + [padding] * max(0, (length - len(sequence))) - - -def log(message, buffer = 'NULL'): - weechat.prnt(buffer, 'autosort: {0}'.format(message)) - - -def get_buffers(): - ''' Get a list of all the buffers in weechat. ''' - buffers = [] - - buffer_list = weechat.infolist_get('buffer', '', '') - - while weechat.infolist_next(buffer_list): - name = weechat.infolist_string (buffer_list, 'full_name') - number = weechat.infolist_integer(buffer_list, 'number') - - # Buffer is merged with one we already have in the list, skip it. - if number <= len(buffers): - continue - buffers.append(name) - - weechat.infolist_free(buffer_list) - return buffers - - -def preprocess(buffer, config): - ''' - Preprocess a buffers names. - ''' - if not config.case_sensitive: - buffer = buffer.lower() - - for replacement in config.replacements: - buffer = buffer.replace(replacement[0], replacement[1]) - - buffer = buffer.split('.') - if config.group_irc and len(buffer) >= 2 and buffer[0] == 'irc' and buffer[1] not in ('server', 'irc_raw'): - buffer.insert(1, 'server') - - return buffer - - -def buffer_sort_key(rules): - ''' Create a sort key function for a buffer list from a rule list. ''' - def key(buffer): - result = [] - name = '' - for word in preprocess(buffer.decode('utf-8'), config): - name += ('.' if name else '') + word - result.append((rules.get_score(name, rules), word)) - return result - - return key - - -def apply_buffer_order(buffers): - ''' Sort the buffers in weechat according to the order in the input list. ''' - for i, buffer in enumerate(buffers): - weechat.command('', '/buffer swap {0} {1}'.format(buffer, i + 1)) - - -def split_args(args, expected, optional = 0): - ''' Split an argument string in the desired number of arguments. ''' - split = args.split(' ', expected - 1) - if (len(split) < expected): - raise HumanReadableError('Expected at least {0} arguments, got {1}.'.format(expected, len(split))) - return split[:-1] + pad(split[-1].split(' ', optional), optional + 1, '') - - -def command_sort(buffer, command, args): - ''' Sort the buffers and print a confirmation. ''' - on_buffers_changed() - log("Finished sorting buffers.", buffer) - return weechat.WEECHAT_RC_OK - - -def command_rule_list(buffer, command, args): - ''' Show the list of sorting rules. ''' - output = 'Sorting rules:\n' - for i, rule in enumerate(config.rules): - output += ' {0}: {1} = {2}\n'.format(i, rule[0].pattern, rule[1]) - if not len(config.rules): - output += ' No sorting rules configured.\n' - log(output, buffer) - - return weechat.WEECHAT_RC_OK - - -def command_rule_add(buffer, command, args): - ''' Add a rule to the rule list. ''' - rule = RuleList.parse_rule(args) - - config.rules.append(rule) - config.save_rules() - command_rule_list(buffer, command, '') - - return weechat.WEECHAT_RC_OK - - -def command_rule_insert(buffer, command, args): - ''' Insert a rule at the desired position in the rule list. ''' - index, rule = split_args(args, 2) - index = parse_int(index, 'index') - rule = RuleList.parse_rule(rule) - - config.rules.insert(index, rule) - config.save_rules() - command_rule_list(buffer, command, '') - return weechat.WEECHAT_RC_OK - - -def command_rule_update(buffer, command, args): - ''' Update a rule in the rule list. ''' - index, rule = split_args(args, 2) - index = parse_int(index, 'index') - rule = RuleList.parse_rule(rule) - - config.rules[index] = rule - config.save_rules() - command_rule_list(buffer, command, '') - return weechat.WEECHAT_RC_OK - - -def command_rule_delete(buffer, command, args): - ''' Delete a rule from the rule list. ''' - index = args.strip() - index = parse_int(index, 'index') - - config.rules.pop(index) - config.save_rules() - command_rule_list(buffer, command, '') - return weechat.WEECHAT_RC_OK - - -def command_rule_move(buffer, command, args): - ''' Move a rule to a new position. ''' - index_a, index_b = split_args(args, 2) - index_a = parse_int(index_a, 'index') - index_b = parse_int(index_b, 'index') - - config.rules.move(index_a, index_b) - config.save_rules() - command_rule_list(buffer, command, '') - return weechat.WEECHAT_RC_OK - - -def command_rule_swap(buffer, command, args): - ''' Swap two rules. ''' - index_a, index_b = split_args(args, 2) - index_a = parse_int(index_a, 'index') - index_b = parse_int(index_b, 'index') - - config.rules.swap(index_a, index_b) - config.save_rules() - command_rule_list(buffer, command, '') - return weechat.WEECHAT_RC_OK - - -def command_replacement_list(buffer, command, args): - ''' Show the list of sorting rules. ''' - output = 'Replacement patterns:\n' - for i, pattern in enumerate(config.replacements): - output += ' {0}: {1} -> {2}\n'.format(i, pattern[0], pattern[1]) - if not len(config.replacements): - output += ' No replacement patterns configured.' - log(output, buffer) - - return weechat.WEECHAT_RC_OK - - -def command_replacement_add(buffer, command, args): - ''' Add a rule to the rule list. ''' - pattern, replacement = split_args(args, 1, 1) - - config.replacements.append((pattern, replacement)) - config.save_replacements() - command_replacement_list(buffer, command, '') - - return weechat.WEECHAT_RC_OK - - -def command_replacement_insert(buffer, command, args): - ''' Insert a rule at the desired position in the rule list. ''' - index, pattern, replacement = split_args(args, 2, 1) - index = parse_int(index, 'index') - - config.replacements.insert(index, (pattern, replacement)) - config.save_replacements() - command_replacement_list(buffer, command, '') - return weechat.WEECHAT_RC_OK - - -def command_replacement_update(buffer, command, args): - ''' Update a rule in the rule list. ''' - index, pattern, replacement = split_args(args, 2, 1) - index = parse_int(index, 'index') - - config.replacements[index] = (pattern, replacement) - config.save_replacements() - command_replacement_list(buffer, command, '') - return weechat.WEECHAT_RC_OK - - -def command_replacement_delete(buffer, command, args): - ''' Delete a rule from the rule list. ''' - index = args.strip() - index = parse_int(index, 'index') - - config.replacements.pop(index) - config.save_replacements() - command_replacement_list(buffer, command, '') - return weechat.WEECHAT_RC_OK - - -def command_replacement_move(buffer, command, args): - ''' Move a rule to a new position. ''' - index_a, index_b = split_args(args, 2) - index_a = parse_int(index_a, 'index') - index_b = parse_int(index_b, 'index') - - config.replacements.move(index_a, index_b) - config.save_replacements() - command_replacement_list(buffer, command, '') - return weechat.WEECHAT_RC_OK - - -def command_replacement_swap(buffer, command, args): - ''' Swap two rules. ''' - index_a, index_b = split_args(args, 2) - index_a = parse_int(index_a, 'index') - index_b = parse_int(index_b, 'index') - - config.replacements.swap(index_a, index_b) - config.save_replacements() - command_replacement_list(buffer, command, '') - return weechat.WEECHAT_RC_OK - - - - -def call_command(buffer, command, args, subcommands): - ''' Call a subccommand from a dictionary. ''' - subcommand, tail = pad(args.split(' ', 1), 2, '') - subcommand = subcommand.strip() - if (subcommand == ''): - child = subcommands.get(' ') - else: - command = command + [subcommand] - child = subcommands.get(subcommand) - - if isinstance(child, dict): - return call_command(buffer, command, tail, child) - elif callable(child): - return child(buffer, command, tail) - - log('{0}: command not found'.format(' '.join(command))) - return weechat.WEECHAT_RC_ERROR - - -def on_buffers_changed(*args, **kwargs): - ''' Called whenever the buffer list changes. ''' - buffers = get_buffers() - buffers.sort(key=buffer_sort_key(config.rules)) - apply_buffer_order(buffers) - return weechat.WEECHAT_RC_OK - - -def on_config_changed(*args, **kwargs): - ''' Called whenever the configuration changes. ''' - config.reload() - - # Unhook all signals and hook the new ones. - for hook in hooks: - weechat.unhook(hook) - for signal in config.signals: - hooks.append(weechat.hook_signal(signal, 'on_buffers_changed', '')) - - if config.sort_on_config: - on_buffers_changed() - - return weechat.WEECHAT_RC_OK - - -def on_autosort_command(data, buffer, args): - ''' Called when the autosort command is invoked. ''' - try: - return call_command(buffer, ['/autosort'], args, { - ' ': command_sort, - 'sort': command_sort, - - 'rules': { - ' ': command_rule_list, - 'list': command_rule_list, - 'add': command_rule_add, - 'insert': command_rule_insert, - 'update': command_rule_update, - 'delete': command_rule_delete, - 'move': command_rule_move, - 'swap': command_rule_swap, - }, - 'replacements': { - ' ': command_replacement_list, - 'list': command_replacement_list, - 'add': command_replacement_add, - 'insert': command_replacement_insert, - 'update': command_replacement_update, - 'delete': command_replacement_delete, - 'move': command_replacement_move, - 'swap': command_replacement_swap, - }, - 'sort': on_buffers_changed, - }) - except HumanReadableError as e: - log(e, buffer) - return weechat.WEECHAT_RC_ERROR - - -command_description = r''' -NOTE: For the best effect, you may want to consider setting the option irc.look.server_buffer to independent and buffers.look.indenting to on. - -# Commands - -## Miscellaneous -/autosort sort -Manually trigger the buffer sorting. - - -## Sorting rules - -/autosort rules list -Print the list of sort rules. - -/autosort rules add = -Add a new rule at the end of the list. - -/autosort rules insert = -Insert a new rule at the given index in the list. - -/autosort rules update = -Update a rule in the list with a new pattern and score. - -/autosort rules delete -Delete a rule from the list. - -/autosort rules move -Move a rule from one position in the list to another. - -/autosort rules swap -Swap two rules in the list - - -## Replacement patterns - -/autosort replacements list -Print the list of replacement patterns. - -/autosort replacements add -Add a new replacement pattern at the end of the list. - -/autosort replacements insert -Insert a new replacement pattern at the given index in the list. - -/autosort replacements update -Update a replacement pattern in the list. - -/autosort replacements delete -Delete a replacement pattern from the list. - -/autosort replacements move -Move a replacement pattern from one position in the list to another. - -/autosort replacements swap -Swap two replacement pattern in the list - - -# Introduction -Autosort is a weechat script to automatically keep your buffers sorted. -The sort order can be customized by defining your own sort rules, -but the default should be sane enough for most people. -It can also group IRC channel/private buffers under their server buffer if you like. - -Autosort first turns buffer names into a list of their components by splitting on them on the period character. -For example, the buffer name "irc.server.freenode" is turned into ['irc', 'server', 'freenode']. -The list of buffers is then lexicographically sorted. - -To facilitate custom sort orders, it is possible to assign a score to each component individually before the sorting is done. -Any name component that did not get a score assigned will be sorted after those that did receive a score. -Components are always sorted on their score first and on their name second. -Lower scores are sorted first. - -## Automatic or manual sorting -By default, autosort will automatically sort your buffer list whenever a buffer is opened, merged, unmerged or renamed. -This should keep your buffers sorted in almost all situations. -However, you may wish to change the list of signals that cause your buffer list to be sorted. -Simply edit the "autosort.sorting.signals" option to add or remove any signal you like. -If you remove all signals you can still sort your buffers manually with the "/autosort sort" command. -To prevent all automatic sorting, "autosort.sorting.sort_on_config_change" should also be set to off. - -## Grouping IRC buffers -In weechat, IRC channel/private buffers are named "irc..<#channel>", -and IRC server buffers are named "irc.server.". -This does not work very well with lexicographical sorting if you want all buffers for one network grouped together. -That is why autosort comes with the "autosort.sorting.group_irc" option, -which secretly pretends IRC channel/private buffers are called "irc.server..<#channel>". -The buffers are not actually renamed, autosort simply pretends they are for sorting purposes. - -## Replacement patterns -Sometimes you may want to ignore some characters for sorting purposes. -On Freenode for example, you may wish to ignore the difference between channels starting with a double or a single hash sign. -To do so, simply add a replacement pattern that replaces ## with # with the following command: -/autosort replacements add ## # - -Replacement patterns do not support wildcards or special characters at the moment. - -## Sort rules -You can assign scores to name components by defining sort rules. -The first rule that matches a component decides the score. -Further rules are not examined. -Sort rules use the following syntax: - = - -You can use the "/autosort rules" command to show and manipulate the list of sort rules. - - -Allowed special characters in the glob patterns are: - -Pattern | Meaning ---------|-------- -* | Matches a sequence of any characters except for periods. -? | Matches a single character, but not a period. -[a-z] | Matches a single character in the given regex-like character class. -[^ab] | A negated regex-like character class. -\* | A backslash escapes the next characters and removes its special meaning. -\\ | A literal backslash. - - -## Example -As an example, consider the following rule list: -0: core = 0 -1: irc = 2 -2: * = 1 - -3: irc.server.*.#* = 1 -4: irc.server.*.* = 0 - -Rule 0 ensures the core buffer is always sorted first. -Rule 1 sorts IRC buffers last and rule 2 puts all remaining buffers in between the two. - -Rule 3 and 4 would make no sense with the group_irc option off. -With the option on though, these rules will sort private buffers before regular channel buffers. -Rule 3 matches channel buffers and assigns them a higher score, -while rule 4 matches the buffers that remain and assigns them a lower score. -The same effect could also be achieved with a single rule: -irc.server.*.[^#]* = 0 -''' - -command_completion = 'sort||rules list|add|insert|update|delete|move|swap||replacements list|add|insert|update|delete|move|swap' - - -if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, "", ""): - config = Config('autosort') - - weechat.hook_config('autosort.*', 'on_config_changed', '') - weechat.hook_command('autosort', command_description, '', '', command_completion, 'on_autosort_command', 'NULL') - on_config_changed() +../autosort.py \ No newline at end of file diff -r d4ff2322e2ab -r 7023c500dd7a weechat/python/autoload/brows.py --- a/weechat/python/autoload/brows.py Tue Aug 17 15:20:33 2021 -0400 +++ b/weechat/python/autoload/brows.py Mon Aug 23 11:51:18 2021 -0400 @@ -15,7 +15,7 @@ try: import weechat except ImportError: - print 'This is a weechat script, what are you doing, run it in weechat, jesus' + print('This is a weechat script, what are you doing, run it in weechat, jesus') import_ok = False weechat_version = 0 diff -r d4ff2322e2ab -r 7023c500dd7a weechat/python/autoload/colon_complete.py --- a/weechat/python/autoload/colon_complete.py Tue Aug 17 15:20:33 2021 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -SCRIPT_NAME='coloncomplete' -SCRIPT_AUTHOR='Steve Losh ' -SCRIPT_VERSION='1.0' -SCRIPT_LICENSE='MIT/X11' -SCRIPT_DESC='Add a colon after nick completion when all the previous words in the input are also nicks.' - -EXTRA_NICKS = ['all', 'backend', 'clojerks', 'ops', 'support'] - -import_ok=True - -try: - import weechat -except ImportError: - print 'This script must be run under WeeChat' - print 'You can obtain a copy of WeeChat, for free, at http://www.weechat.org' - import_ok=False - -weechat_version=0 - -def get_nicks(buffer, prefix=''): - channel = weechat.buffer_get_string(buffer, 'localvar_channel') - server = weechat.buffer_get_string(buffer, 'localvar_server') - prefix = prefix.lower() - - matches = [] - - infolist = weechat.infolist_get('irc_nick', '', '%s,%s' % (server, channel)) - while weechat.infolist_next(infolist): - nick = weechat.infolist_string(infolist, 'name') - if nick != 'localhost' and nick.lower().startswith(prefix): - matches.append(nick) - weechat.infolist_free(infolist) - - for nick in EXTRA_NICKS: - if nick.lower().startswith(prefix): - matches.append(nick) - - return matches - -def completer(data, buffer, command): - cb = weechat.current_buffer() - if command == "/input complete_next": - line = weechat.buffer_get_string(cb, "input") - words = line.split(' ') - prefix = words[-1] - if prefix and words and all([s.endswith(':') for s in words[:-1] if s]): - nicks = get_nicks(cb, prefix) - if len(nicks) == 1: - for _ in range(len(prefix)): - weechat.command(buffer, "/input delete_previous_char") - weechat.command(buffer, "/input insert " + nicks[-1] + ":\\x20") - elif len(nicks) > 1: - l = min(len(nick) for nick in nicks) - for i in range(len(prefix), l): - if len(set(nick[i] for nick in nicks)) > 1: - break - else: - weechat.command(buffer, "/input insert " + nicks[0][i]) - - for nick in nicks: - weechat.prnt(cb, "==> " + nick) - return weechat.WEECHAT_RC_OK_EAT - - return weechat.WEECHAT_RC_OK - -if __name__ == "__main__" and import_ok: - if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, "", ""): - weechat_version = weechat.info_get("version_number", "") or 0 - weechat.hook_command_run('/input complete*', 'completer', '') diff -r d4ff2322e2ab -r 7023c500dd7a weechat/python/autoload/editor.py --- a/weechat/python/autoload/editor.py Tue Aug 17 15:20:33 2021 -0400 +++ b/weechat/python/autoload/editor.py Mon Aug 23 11:51:18 2021 -0400 @@ -16,7 +16,7 @@ try: import weechat except ImportError: - print 'This is a weechat script, what are you doing, run it in weechat, jesus' + print('This is a weechat script, what are you doing, run it in weechat, jesus') import_ok = False weechat_version = 0 @@ -33,9 +33,8 @@ # Reopen, because most editors do atomic write-tmp+rename saves which # fucks with Python here. tf.file.close() - tf = file(tf.name) - - return tf.read() + with open(tf.name) as tf2: + return tf2.read() def editor(data, buffer, args): suffix = args or "tmp" diff -r d4ff2322e2ab -r 7023c500dd7a weechat/python/autoload/quotes.py --- a/weechat/python/autoload/quotes.py Tue Aug 17 15:20:33 2021 -0400 +++ b/weechat/python/autoload/quotes.py Mon Aug 23 11:51:18 2021 -0400 @@ -18,7 +18,7 @@ try: import weechat except ImportError: - print 'This is a weechat script, what are you doing, run it in weechat, jesus' + print('This is a weechat script, what are you doing, run it in weechat, jesus') import_ok = False weechat_version = 0 diff -r d4ff2322e2ab -r 7023c500dd7a weechat/python/autoload/sanitize_jira.py --- a/weechat/python/autoload/sanitize_jira.py Tue Aug 17 15:20:33 2021 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -import re, weechat, subprocess - -SCRIPT_NAME = 'sanitize_jira' -SCRIPT_AUTHOR = 'Steve Losh ' -SCRIPT_VERSION = '0.0.1' -SCRIPT_LICENSE = 'MIT' -SCRIPT_DESC = 'clean up the garbage jirabot sends to channels into something readable' - -weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, '', '') - -weechat.hook_line('*', '', 'nick_Jira_Cloud', 'sanitize_jira', '') - -first_line_re = re.compile( - r'(?Phttps://[^/]+/browse/[^?]+)[?]atlOrigin=[^ ]+ [(](?P.+)[)]' -) - -detail_line_re = re.compile( - r'''Status: \x1a\x01[*](?P<status>[^*]+)[*]\x1b\x01.*Type: \x1a\x01[*](?P<type>[^*]+)[*]\x1b\x01.*Assignee: \x1a\x01[*](?P<assignee>[^*]+)[*]\x1b\x01.*Priority: \x1a\x01[*](?P<priority>[^*]+)[*]\x1b\x01''' -) - -def sanitize_jira(data, line): - if 'sign up for an Atlassian account to view this link' in line['message']: - return {'message': ' '} - - m = first_line_re.search(line['message']) - if m: - return {'message': '%s | %s' % (m.group('title'), m.group('link'))} - - m = detail_line_re.search(line['message']) - if m: - return {'message': '%s / %s / %s' % (m.group('type'), m.group('status'), m.group('assignee'))} - - return {} diff -r d4ff2322e2ab -r 7023c500dd7a weechat/python/autosort.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/weechat/python/autosort.py Mon Aug 23 11:51:18 2021 -0400 @@ -0,0 +1,1075 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013-2017 Maarten de Vries <maarten@de-vri.es> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# +# Autosort automatically keeps your buffers sorted and grouped by server. +# You can define your own sorting rules. See /help autosort for more details. +# +# https://github.com/de-vri-es/weechat-autosort +# + +# +# Changelog: +# 3.9: +# * Remove `buffers.pl` from recommended settings. +# 3,8: +# * Fix relative sorting on script name in default rules. +# * Document a useful property of stable sort algorithms. +# 3.7: +# * Make default rules work with bitlbee, matrix and slack. +# 3.6: +# * Add more documentation on provided info hooks. +# 3.5: +# * Add ${info:autosort_escape,...} to escape arguments for other info hooks. +# 3.4: +# * Fix rate-limit of sorting to prevent high CPU load and lock-ups. +# * Fix bug in parsing empty arguments for info hooks. +# * Add debug_log option to aid with debugging. +# * Correct a few typos. +# 3.3: +# * Fix the /autosort debug command for unicode. +# * Update the default rules to work better with Slack. +# 3.2: +# * Fix python3 compatiblity. +# 3.1: +# * Use colors to format the help text. +# 3.0: +# * Switch to evaluated expressions for sorting. +# * Add `/autosort debug` command. +# * Add ${info:autosort_replace,from,to,text} to replace substrings in sort rules. +# * Add ${info:autosort_order,value,first,second,third} to ease writing sort rules. +# * Make tab completion context aware. +# 2.8: +# * Fix compatibility with python 3 regarding unicode handling. +# 2.7: +# * Fix sorting of buffers with spaces in their name. +# 2.6: +# * Ignore case in rules when doing case insensitive sorting. +# 2.5: +# * Fix handling unicode buffer names. +# * Add hint to set irc.look.server_buffer to independent and buffers.look.indenting to on. +# 2.4: +# * Make script python3 compatible. +# 2.3: +# * Fix sorting items without score last (regressed in 2.2). +# 2.2: +# * Add configuration option for signals that trigger a sort. +# * Add command to manually trigger a sort (/autosort sort). +# * Add replacement patterns to apply before sorting. +# 2.1: +# * Fix some minor style issues. +# 2.0: +# * Allow for custom sort rules. +# + + +import json +import math +import re +import sys +import time +import weechat + +SCRIPT_NAME = 'autosort' +SCRIPT_AUTHOR = 'Maarten de Vries <maarten@de-vri.es>' +SCRIPT_VERSION = '3.9' +SCRIPT_LICENSE = 'GPL3' +SCRIPT_DESC = 'Flexible automatic (or manual) buffer sorting based on eval expressions.' + + +config = None +hooks = [] +signal_delay_timer = None +sort_limit_timer = None +sort_queued = False + + +# Make sure that unicode, bytes and str are always available in python2 and 3. +# For python 2, str == bytes +# For python 3, str == unicode +if sys.version_info[0] >= 3: + unicode = str + +def ensure_str(input): + ''' + Make sure the given type if the correct string type for the current python version. + That means bytes for python2 and unicode for python3. + ''' + if not isinstance(input, str): + if isinstance(input, bytes): + return input.encode('utf-8') + if isinstance(input, unicode): + return input.decode('utf-8') + return input + + +if hasattr(time, 'perf_counter'): + perf_counter = time.perf_counter +else: + perf_counter = time.clock + +def casefold(string): + if hasattr(string, 'casefold'): return string.casefold() + # Fall back to lowercasing for python2. + return string.lower() + +def list_swap(values, a, b): + values[a], values[b] = values[b], values[a] + +def list_move(values, old_index, new_index): + values.insert(new_index, values.pop(old_index)) + +def list_find(collection, value): + for i, elem in enumerate(collection): + if elem == value: return i + return None + +class HumanReadableError(Exception): + pass + +def parse_int(arg, arg_name = 'argument'): + ''' Parse an integer and provide a more human readable error. ''' + arg = arg.strip() + try: + return int(arg) + except ValueError: + raise HumanReadableError('Invalid {0}: expected integer, got "{1}".'.format(arg_name, arg)) + +def decode_rules(blob): + parsed = json.loads(blob) + if not isinstance(parsed, list): + log('Malformed rules, expected a JSON encoded list of strings, but got a {0}. No rules have been loaded. Please fix the setting manually.'.format(type(parsed))) + return [] + + for i, entry in enumerate(parsed): + if not isinstance(entry, (str, unicode)): + log('Rule #{0} is not a string but a {1}. No rules have been loaded. Please fix the setting manually.'.format(i, type(entry))) + return [] + + return parsed + +def decode_helpers(blob): + parsed = json.loads(blob) + if not isinstance(parsed, dict): + log('Malformed helpers, expected a JSON encoded dictionary but got a {0}. No helpers have been loaded. Please fix the setting manually.'.format(type(parsed))) + return {} + + for key, value in parsed.items(): + if not isinstance(value, (str, unicode)): + log('Helper "{0}" is not a string but a {1}. No helpers have been loaded. Please fix setting manually.'.format(key, type(value))) + return {} + return parsed + +class Config: + ''' The autosort configuration. ''' + + default_rules = json.dumps([ + '${core_first}', + '${info:autosort_order,${info:autosort_escape,${script_or_plugin}},core,*,irc,bitlbee,matrix,slack}', + '${script_or_plugin}', + '${irc_raw_first}', + '${server}', + '${info:autosort_order,${type},server,*,channel,private}', + '${hashless_name}', + '${buffer.full_name}', + ]) + + default_helpers = json.dumps({ + 'core_first': '${if:${buffer.full_name}!=core.weechat}', + 'irc_raw_first': '${if:${buffer.full_name}!=irc.irc_raw}', + 'irc_raw_last': '${if:${buffer.full_name}==irc.irc_raw}', + 'hashless_name': '${info:autosort_replace,#,,${info:autosort_escape,${buffer.name}}}', + 'script_or_plugin': '${if:${script_name}?${script_name}:${plugin}}', + }) + + default_signal_delay = 5 + default_sort_limit = 100 + + default_signals = 'buffer_opened buffer_merged buffer_unmerged buffer_renamed' + + def __init__(self, filename): + ''' Initialize the configuration. ''' + + self.filename = filename + self.config_file = weechat.config_new(self.filename, '', '') + self.sorting_section = None + self.v3_section = None + + self.case_sensitive = False + self.rules = [] + self.helpers = {} + self.signals = [] + self.signal_delay = Config.default_signal_delay, + self.sort_limit = Config.default_sort_limit, + self.sort_on_config = True + self.debug_log = False + + self.__case_sensitive = None + self.__rules = None + self.__helpers = None + self.__signals = None + self.__signal_delay = None + self.__sort_limit = None + self.__sort_on_config = None + self.__debug_log = None + + if not self.config_file: + log('Failed to initialize configuration file "{0}".'.format(self.filename)) + return + + self.sorting_section = weechat.config_new_section(self.config_file, 'sorting', False, False, '', '', '', '', '', '', '', '', '', '') + self.v3_section = weechat.config_new_section(self.config_file, 'v3', False, False, '', '', '', '', '', '', '', '', '', '') + + if not self.sorting_section: + log('Failed to initialize section "sorting" of configuration file.') + weechat.config_free(self.config_file) + return + + self.__case_sensitive = weechat.config_new_option( + self.config_file, self.sorting_section, + 'case_sensitive', 'boolean', + 'If this option is on, sorting is case sensitive.', + '', 0, 0, 'off', 'off', 0, + '', '', '', '', '', '' + ) + + weechat.config_new_option( + self.config_file, self.sorting_section, + 'rules', 'string', + 'Sort rules used by autosort v2.x and below. Not used by autosort anymore.', + '', 0, 0, '', '', 0, + '', '', '', '', '', '' + ) + + weechat.config_new_option( + self.config_file, self.sorting_section, + 'replacements', 'string', + 'Replacement patterns used by autosort v2.x and below. Not used by autosort anymore.', + '', 0, 0, '', '', 0, + '', '', '', '', '', '' + ) + + self.__rules = weechat.config_new_option( + self.config_file, self.v3_section, + 'rules', 'string', + 'An ordered list of sorting rules encoded as JSON. See /help autosort for commands to manipulate these rules.', + '', 0, 0, Config.default_rules, Config.default_rules, 0, + '', '', '', '', '', '' + ) + + self.__helpers = weechat.config_new_option( + self.config_file, self.v3_section, + 'helpers', 'string', + 'A dictionary helper variables to use in the sorting rules, encoded as JSON. See /help autosort for commands to manipulate these helpers.', + '', 0, 0, Config.default_helpers, Config.default_helpers, 0, + '', '', '', '', '', '' + ) + + self.__signals = weechat.config_new_option( + self.config_file, self.sorting_section, + 'signals', 'string', + 'A space separated list of signals that will cause autosort to resort your buffer list.', + '', 0, 0, Config.default_signals, Config.default_signals, 0, + '', '', '', '', '', '' + ) + + self.__signal_delay = weechat.config_new_option( + self.config_file, self.sorting_section, + 'signal_delay', 'integer', + 'Delay in milliseconds to wait after a signal before sorting the buffer list. This prevents triggering many times if multiple signals arrive in a short time. It can also be needed to wait for buffer localvars to be available.', + '', 0, 1000, str(Config.default_signal_delay), str(Config.default_signal_delay), 0, + '', '', '', '', '', '' + ) + + self.__sort_limit = weechat.config_new_option( + self.config_file, self.sorting_section, + 'sort_limit', 'integer', + 'Minimum delay in milliseconds to wait after sorting before signals can trigger a sort again. This is effectively a rate limit on sorting. Keeping signal_delay low while setting this higher can reduce excessive sorting without a long initial delay.', + '', 0, 1000, str(Config.default_sort_limit), str(Config.default_sort_limit), 0, + '', '', '', '', '', '' + ) + + self.__sort_on_config = weechat.config_new_option( + self.config_file, self.sorting_section, + 'sort_on_config_change', 'boolean', + 'Decides if the buffer list should be sorted when autosort configuration changes.', + '', 0, 0, 'on', 'on', 0, + '', '', '', '', '', '' + ) + + self.__debug_log = weechat.config_new_option( + self.config_file, self.sorting_section, + 'debug_log', 'boolean', + 'If enabled, print more debug messages. Not recommended for normal usage.', + '', 0, 0, 'off', 'off', 0, + '', '', '', '', '', '' + ) + + if weechat.config_read(self.config_file) != weechat.WEECHAT_RC_OK: + log('Failed to load configuration file.') + + if weechat.config_write(self.config_file) != weechat.WEECHAT_RC_OK: + log('Failed to write configuration file.') + + self.reload() + + def reload(self): + ''' Load configuration variables. ''' + + self.case_sensitive = weechat.config_boolean(self.__case_sensitive) + + rules_blob = weechat.config_string(self.__rules) + helpers_blob = weechat.config_string(self.__helpers) + signals_blob = weechat.config_string(self.__signals) + + self.rules = decode_rules(rules_blob) + self.helpers = decode_helpers(helpers_blob) + self.signals = signals_blob.split() + self.signal_delay = weechat.config_integer(self.__signal_delay) + self.sort_limit = weechat.config_integer(self.__sort_limit) + self.sort_on_config = weechat.config_boolean(self.__sort_on_config) + self.debug_log = weechat.config_boolean(self.__debug_log) + + def save_rules(self, run_callback = True): + ''' Save the current rules to the configuration. ''' + weechat.config_option_set(self.__rules, json.dumps(self.rules), run_callback) + + def save_helpers(self, run_callback = True): + ''' Save the current helpers to the configuration. ''' + weechat.config_option_set(self.__helpers, json.dumps(self.helpers), run_callback) + + +def pad(sequence, length, padding = None): + ''' Pad a list until is has a certain length. ''' + return sequence + [padding] * max(0, (length - len(sequence))) + +def log(message, buffer = 'NULL'): + weechat.prnt(buffer, 'autosort: {0}'.format(message)) + +def debug(message, buffer = 'NULL'): + if config.debug_log: + weechat.prnt(buffer, 'autosort: debug: {0}'.format(message)) + +def get_buffers(): + ''' Get a list of all the buffers in weechat. ''' + hdata = weechat.hdata_get('buffer') + buffer = weechat.hdata_get_list(hdata, "gui_buffers"); + + result = [] + while buffer: + number = weechat.hdata_integer(hdata, buffer, 'number') + result.append((number, buffer)) + buffer = weechat.hdata_pointer(hdata, buffer, 'next_buffer') + return hdata, result + +class MergedBuffers(list): + """ A list of merged buffers, possibly of size 1. """ + def __init__(self, number): + super(MergedBuffers, self).__init__() + self.number = number + +def merge_buffer_list(buffers): + ''' + Group merged buffers together. + The output is a list of MergedBuffers. + ''' + if not buffers: return [] + result = {} + for number, buffer in buffers: + if number not in result: result[number] = MergedBuffers(number) + result[number].append(buffer) + return result.values() + +def sort_buffers(hdata, buffers, rules, helpers, case_sensitive): + for merged in buffers: + for buffer in merged: + name = weechat.hdata_string(hdata, buffer, 'name') + + return sorted(buffers, key=merged_sort_key(rules, helpers, case_sensitive)) + +def buffer_sort_key(rules, helpers, case_sensitive): + ''' Create a sort key function for a list of lists of merged buffers. ''' + def key(buffer): + extra_vars = {} + for helper_name, helper in sorted(helpers.items()): + expanded = weechat.string_eval_expression(helper, {"buffer": buffer}, {}, {}) + extra_vars[helper_name] = expanded if case_sensitive else casefold(expanded) + result = [] + for rule in rules: + expanded = weechat.string_eval_expression(rule, {"buffer": buffer}, extra_vars, {}) + result.append(expanded if case_sensitive else casefold(expanded)) + return result + + return key + +def merged_sort_key(rules, helpers, case_sensitive): + buffer_key = buffer_sort_key(rules, helpers, case_sensitive) + def key(merged): + best = None + for buffer in merged: + this = buffer_key(buffer) + if best is None or this < best: best = this + return best + return key + +def apply_buffer_order(buffers): + ''' Sort the buffers in weechat according to the given order. ''' + for i, buffer in enumerate(buffers): + weechat.buffer_set(buffer[0], "number", str(i + 1)) + +def split_args(args, expected, optional = 0): + ''' Split an argument string in the desired number of arguments. ''' + split = args.split(' ', expected - 1) + if (len(split) < expected): + raise HumanReadableError('Expected at least {0} arguments, got {1}.'.format(expected, len(split))) + return split[:-1] + pad(split[-1].split(' ', optional), optional + 1, '') + +def do_sort(verbose = False): + start = perf_counter() + + hdata, buffers = get_buffers() + buffers = merge_buffer_list(buffers) + buffers = sort_buffers(hdata, buffers, config.rules, config.helpers, config.case_sensitive) + apply_buffer_order(buffers) + + elapsed = perf_counter() - start + if verbose: + log("Finished sorting buffers in {0:.4f} seconds.".format(elapsed)) + else: + debug("Finished sorting buffers in {0:.4f} seconds.".format(elapsed)) + +def command_sort(buffer, command, args): + ''' Sort the buffers and print a confirmation. ''' + do_sort(True) + return weechat.WEECHAT_RC_OK + +def command_debug(buffer, command, args): + hdata, buffers = get_buffers() + buffers = merge_buffer_list(buffers) + + # Show evaluation results. + log('Individual evaluation results:') + start = perf_counter() + key = buffer_sort_key(config.rules, config.helpers, config.case_sensitive) + results = [] + for merged in buffers: + for buffer in merged: + fullname = weechat.hdata_string(hdata, buffer, 'full_name') + results.append((fullname, key(buffer))) + elapsed = perf_counter() - start + + for fullname, result in results: + fullname = ensure_str(fullname) + result = [ensure_str(x) for x in result] + log('{0}: {1}'.format(fullname, result)) + log('Computing evaluation results took {0:.4f} seconds.'.format(elapsed)) + + return weechat.WEECHAT_RC_OK + +def command_rule_list(buffer, command, args): + ''' Show the list of sorting rules. ''' + output = 'Sorting rules:\n' + for i, rule in enumerate(config.rules): + output += ' {0}: {1}\n'.format(i, rule) + if not len(config.rules): + output += ' No sorting rules configured.\n' + log(output ) + + return weechat.WEECHAT_RC_OK + + +def command_rule_add(buffer, command, args): + ''' Add a rule to the rule list. ''' + config.rules.append(args) + config.save_rules() + command_rule_list(buffer, command, '') + + return weechat.WEECHAT_RC_OK + + +def command_rule_insert(buffer, command, args): + ''' Insert a rule at the desired position in the rule list. ''' + index, rule = split_args(args, 2) + index = parse_int(index, 'index') + + config.rules.insert(index, rule) + config.save_rules() + command_rule_list(buffer, command, '') + return weechat.WEECHAT_RC_OK + + +def command_rule_update(buffer, command, args): + ''' Update a rule in the rule list. ''' + index, rule = split_args(args, 2) + index = parse_int(index, 'index') + + config.rules[index] = rule + config.save_rules() + command_rule_list(buffer, command, '') + return weechat.WEECHAT_RC_OK + + +def command_rule_delete(buffer, command, args): + ''' Delete a rule from the rule list. ''' + index = args.strip() + index = parse_int(index, 'index') + + config.rules.pop(index) + config.save_rules() + command_rule_list(buffer, command, '') + return weechat.WEECHAT_RC_OK + + +def command_rule_move(buffer, command, args): + ''' Move a rule to a new position. ''' + index_a, index_b = split_args(args, 2) + index_a = parse_int(index_a, 'index') + index_b = parse_int(index_b, 'index') + + list_move(config.rules, index_a, index_b) + config.save_rules() + command_rule_list(buffer, command, '') + return weechat.WEECHAT_RC_OK + + +def command_rule_swap(buffer, command, args): + ''' Swap two rules. ''' + index_a, index_b = split_args(args, 2) + index_a = parse_int(index_a, 'index') + index_b = parse_int(index_b, 'index') + + list_swap(config.rules, index_a, index_b) + config.save_rules() + command_rule_list(buffer, command, '') + return weechat.WEECHAT_RC_OK + + +def command_helper_list(buffer, command, args): + ''' Show the list of helpers. ''' + output = 'Helper variables:\n' + + width = max(map(lambda x: len(x) if len(x) <= 30 else 0, config.helpers.keys())) + + for name, expression in sorted(config.helpers.items()): + output += ' {0:>{width}}: {1}\n'.format(name, expression, width=width) + if not len(config.helpers): + output += ' No helper variables configured.' + log(output) + + return weechat.WEECHAT_RC_OK + + +def command_helper_set(buffer, command, args): + ''' Add/update a helper to the helper list. ''' + name, expression = split_args(args, 2) + + config.helpers[name] = expression + config.save_helpers() + command_helper_list(buffer, command, '') + + return weechat.WEECHAT_RC_OK + +def command_helper_delete(buffer, command, args): + ''' Delete a helper from the helper list. ''' + name = args.strip() + + del config.helpers[name] + config.save_helpers() + command_helper_list(buffer, command, '') + return weechat.WEECHAT_RC_OK + + +def command_helper_rename(buffer, command, args): + ''' Rename a helper to a new position. ''' + old_name, new_name = split_args(args, 2) + + try: + config.helpers[new_name] = config.helpers[old_name] + del config.helpers[old_name] + except KeyError: + raise HumanReadableError('No such helper: {0}'.format(old_name)) + config.save_helpers() + command_helper_list(buffer, command, '') + return weechat.WEECHAT_RC_OK + + +def command_helper_swap(buffer, command, args): + ''' Swap two helpers. ''' + a, b = split_args(args, 2) + try: + config.helpers[b], config.helpers[a] = config.helpers[a], config.helpers[b] + except KeyError as e: + raise HumanReadableError('No such helper: {0}'.format(e.args[0])) + + config.helpers.swap(index_a, index_b) + config.save_helpers() + command_helper_list(buffer, command, '') + return weechat.WEECHAT_RC_OK + +def call_command(buffer, command, args, subcommands): + ''' Call a subcommand from a dictionary. ''' + subcommand, tail = pad(args.split(' ', 1), 2, '') + subcommand = subcommand.strip() + if (subcommand == ''): + child = subcommands.get(' ') + else: + command = command + [subcommand] + child = subcommands.get(subcommand) + + if isinstance(child, dict): + return call_command(buffer, command, tail, child) + elif callable(child): + return child(buffer, command, tail) + + log('{0}: command not found'.format(' '.join(command))) + return weechat.WEECHAT_RC_ERROR + +def on_signal(data, signal, signal_data): + global signal_delay_timer + global sort_queued + + # If the sort limit timeout is started, we're in the hold-off time after sorting, just queue a sort. + if sort_limit_timer is not None: + if sort_queued: + debug('Signal {0} ignored, sort limit timeout is active and sort is already queued.'.format(signal)) + else: + debug('Signal {0} received but sort limit timeout is active, sort is now queued.'.format(signal)) + sort_queued = True + return weechat.WEECHAT_RC_OK + + # If the signal delay timeout is started, a signal was recently received, so ignore this signal. + if signal_delay_timer is not None: + debug('Signal {0} ignored, signal delay timeout active.'.format(signal)) + return weechat.WEECHAT_RC_OK + + # Otherwise, start the signal delay timeout. + debug('Signal {0} received, starting signal delay timeout of {1} ms.'.format(signal, config.signal_delay)) + weechat.hook_timer(config.signal_delay, 0, 1, "on_signal_delay_timeout", "") + return weechat.WEECHAT_RC_OK + +def on_signal_delay_timeout(pointer, remaining_calls): + """ Called when the signal_delay_timer triggers. """ + global signal_delay_timer + global sort_limit_timer + global sort_queued + + signal_delay_timer = None + + # If the sort limit timeout was started, we're still in the no-sort period, so just queue a sort. + if sort_limit_timer is not None: + debug('Signal delay timeout expired, but sort limit timeout is active, sort is now queued.') + sort_queued = True + return weechat.WEECHAT_RC_OK + + # Time to sort! + debug('Signal delay timeout expired, starting sort.') + do_sort() + + # Start the sort limit timeout if not disabled. + if config.sort_limit > 0: + debug('Starting sort limit timeout of {0} ms.'.format(config.sort_limit)) + sort_limit_timer = weechat.hook_timer(config.sort_limit, 0, 1, "on_sort_limit_timeout", "") + + return weechat.WEECHAT_RC_OK + +def on_sort_limit_timeout(pointer, remainin_calls): + """ Called when de sort_limit_timer triggers. """ + global sort_limit_timer + global sort_queued + + # If no signal was received during the timeout, we're done. + if not sort_queued: + debug('Sort limit timeout expired without receiving a signal.') + sort_limit_timer = None + return weechat.WEECHAT_RC_OK + + # Otherwise it's time to sort. + debug('Signal received during sort limit timeout, starting queued sort.') + do_sort() + sort_queued = False + + # Start the sort limit timeout again if not disabled. + if config.sort_limit > 0: + debug('Starting sort limit timeout of {0} ms.'.format(config.sort_limit)) + sort_limit_timer = weechat.hook_timer(config.sort_limit, 0, 1, "on_sort_limit_timeout", "") + + return weechat.WEECHAT_RC_OK + + +def apply_config(): + # Unhook all signals and hook the new ones. + for hook in hooks: + weechat.unhook(hook) + for signal in config.signals: + hooks.append(weechat.hook_signal(signal, 'on_signal', '')) + + if config.sort_on_config: + debug('Sorting because configuration changed.') + do_sort() + +def on_config_changed(*args, **kwargs): + ''' Called whenever the configuration changes. ''' + config.reload() + apply_config() + + return weechat.WEECHAT_RC_OK + +def parse_arg(args): + if not args: return '', None + + result = '' + escaped = False + for i, c in enumerate(args): + if not escaped: + if c == '\\': + escaped = True + continue + elif c == ',': + return result, args[i+1:] + result += c + escaped = False + return result, None + +def parse_args(args, max = None): + result = [] + i = 0 + while max is None or i < max: + i += 1 + arg, args = parse_arg(args) + if arg is None: break + result.append(arg) + if args is None: break + return result, args + +def on_info_escape(pointer, name, arguments): + result = '' + for c in arguments: + if c == '\\': + result += '\\\\' + elif c == ',': + result += '\\,' + else: + result +=c + return result + +def on_info_replace(pointer, name, arguments): + arguments, rest = parse_args(arguments, 3) + if rest or len(arguments) < 3: + log('usage: ${{info:{0},old,new,text}}'.format(name)) + return '' + old, new, text = arguments + + return text.replace(old, new) + +def on_info_order(pointer, name, arguments): + arguments, rest = parse_args(arguments) + if len(arguments) < 1: + log('usage: ${{info:{0},value,first,second,third,...}}'.format(name)) + return '' + + value = arguments[0] + keys = arguments[1:] + if not keys: return '0' + + # Find the value in the keys (or '*' if we can't find it) + result = list_find(keys, value) + if result is None: result = list_find(keys, '*') + if result is None: result = len(keys) + + # Pad result with leading zero to make sure string sorting works. + width = int(math.log10(len(keys))) + 1 + return '{0:0{1}}'.format(result, width) + + +def on_autosort_command(data, buffer, args): + ''' Called when the autosort command is invoked. ''' + try: + return call_command(buffer, ['/autosort'], args, { + ' ': command_sort, + 'sort': command_sort, + 'debug': command_debug, + + 'rules': { + ' ': command_rule_list, + 'list': command_rule_list, + 'add': command_rule_add, + 'insert': command_rule_insert, + 'update': command_rule_update, + 'delete': command_rule_delete, + 'move': command_rule_move, + 'swap': command_rule_swap, + }, + 'helpers': { + ' ': command_helper_list, + 'list': command_helper_list, + 'set': command_helper_set, + 'delete': command_helper_delete, + 'rename': command_helper_rename, + 'swap': command_helper_swap, + }, + }) + except HumanReadableError as e: + log(e) + return weechat.WEECHAT_RC_ERROR + +def add_completions(completion, words): + for word in words: + weechat.hook_completion_list_add(completion, word, 0, weechat.WEECHAT_LIST_POS_END) + +def autosort_complete_rules(words, completion): + if len(words) == 0: + add_completions(completion, ['add', 'delete', 'insert', 'list', 'move', 'swap', 'update']) + if len(words) == 1 and words[0] in ('delete', 'insert', 'move', 'swap', 'update'): + add_completions(completion, map(str, range(len(config.rules)))) + if len(words) == 2 and words[0] in ('move', 'swap'): + add_completions(completion, map(str, range(len(config.rules)))) + if len(words) == 2 and words[0] in ('update'): + try: + add_completions(completion, [config.rules[int(words[1])]]) + except KeyError: pass + except ValueError: pass + else: + add_completions(completion, ['']) + return weechat.WEECHAT_RC_OK + +def autosort_complete_helpers(words, completion): + if len(words) == 0: + add_completions(completion, ['delete', 'list', 'rename', 'set', 'swap']) + elif len(words) == 1 and words[0] in ('delete', 'rename', 'set', 'swap'): + add_completions(completion, sorted(config.helpers.keys())) + elif len(words) == 2 and words[0] == 'swap': + add_completions(completion, sorted(config.helpers.keys())) + elif len(words) == 2 and words[0] == 'rename': + add_completions(completion, sorted(config.helpers.keys())) + elif len(words) == 2 and words[0] == 'set': + try: + add_completions(completion, [config.helpers[words[1]]]) + except KeyError: pass + return weechat.WEECHAT_RC_OK + +def on_autosort_complete(data, name, buffer, completion): + cmdline = weechat.buffer_get_string(buffer, "input") + cursor = weechat.buffer_get_integer(buffer, "input_pos") + prefix = cmdline[:cursor] + words = prefix.split()[1:] + + # If the current word isn't finished yet, + # ignore it for coming up with completion suggestions. + if prefix[-1] != ' ': words = words[:-1] + + if len(words) == 0: + add_completions(completion, ['debug', 'helpers', 'rules', 'sort']) + elif words[0] == 'rules': + return autosort_complete_rules(words[1:], completion) + elif words[0] == 'helpers': + return autosort_complete_helpers(words[1:], completion) + return weechat.WEECHAT_RC_OK + +command_description = r'''{*white}# General commands{reset} + +{*white}/autosort {brown}sort{reset} +Manually trigger the buffer sorting. + +{*white}/autosort {brown}debug{reset} +Show the evaluation results of the sort rules for each buffer. + + +{*white}# Sorting rule commands{reset} + +{*white}/autosort{brown} rules list{reset} +Print the list of sort rules. + +{*white}/autosort {brown}rules add {cyan}<expression>{reset} +Add a new rule at the end of the list. + +{*white}/autosort {brown}rules insert {cyan}<index> <expression>{reset} +Insert a new rule at the given index in the list. + +{*white}/autosort {brown}rules update {cyan}<index> <expression>{reset} +Update a rule in the list with a new expression. + +{*white}/autosort {brown}rules delete {cyan}<index> +Delete a rule from the list. + +{*white}/autosort {brown}rules move {cyan}<index_from> <index_to>{reset} +Move a rule from one position in the list to another. + +{*white}/autosort {brown}rules swap {cyan}<index_a> <index_b>{reset} +Swap two rules in the list + + +{*white}# Helper variable commands{reset} + +{*white}/autosort {brown}helpers list +Print the list of helper variables. + +{*white}/autosort {brown}helpers set {cyan}<name> <expression> +Add or update a helper variable with the given name. + +{*white}/autosort {brown}helpers delete {cyan}<name> +Delete a helper variable. + +{*white}/autosort {brown}helpers rename {cyan}<old_name> <new_name> +Rename a helper variable. + +{*white}/autosort {brown}helpers swap {cyan}<name_a> <name_b> +Swap the expressions of two helper variables in the list. + + +{*white}# Info hooks{reset} +Autosort comes with a number of info hooks to add some extra functionality to regular weechat eval strings. +Info hooks can be used in eval strings in the form of {cyan}${{info:some_hook,arguments}}{reset}. + +Commas and backslashes in arguments to autosort info hooks (except for {cyan}${{info:autosort_escape}}{reset}) must be escaped with a backslash. + +{*white}${{info:{brown}autosort_replace{white},{cyan}pattern{white},{cyan}replacement{white},{cyan}source{white}}}{reset} +Replace all occurrences of {cyan}pattern{reset} with {cyan}replacement{reset} in the string {cyan}source{reset}. +Can be used to ignore certain strings when sorting by replacing them with an empty string. + +For example: {cyan}${{info:autosort_replace,cat,dog,the dog is meowing}}{reset} expands to "the cat is meowing". + +{*white}${{info:{brown}autosort_order{white},{cyan}value{white},{cyan}option0{white},{cyan}option1{white},{cyan}option2{white},{cyan}...{white}}} +Generate a zero-padded number that corresponds to the index of {cyan}value{reset} in the list of options. +If one of the options is the special value {brown}*{reset}, then any value not explicitly mentioned will be sorted at that position. +Otherwise, any value that does not match an option is assigned the highest number available. +Can be used to easily sort buffers based on a manual sequence. + +For example: {cyan}${{info:autosort_order,${{server}},freenode,oftc,efnet}}{reset} will sort freenode before oftc, followed by efnet and then any remaining servers. +Alternatively, {cyan}${{info:autosort_order,${{server}},freenode,oftc,*,efnet}}{reset} will sort any unlisted servers after freenode and oftc, but before efnet. + +{*white}${{info:{brown}autosort_escape{white},{cyan}text{white}}}{reset} +Escape commas and backslashes in {cyan}text{reset} by prepending them with a backslash. +This is mainly useful to pass arbitrary eval strings as arguments to other autosort info hooks. +Otherwise, an eval string that expands to something with a comma would be interpreted as multiple arguments. + +For example, it can be used to safely pass buffer names to {cyan}${{info:autosort_replace}}{reset} like so: +{cyan}${{info:autosort_replace,##,#,${{info:autosort_escape,${{buffer.name}}}}}}{reset}. + + +{*white}# Description +Autosort is a weechat script to automatically keep your buffers sorted. The sort +order can be customized by defining your own sort rules, but the default should +be sane enough for most people. It can also group IRC channel/private buffers +under their server buffer if you like. + +Autosort uses a stable sorting algorithm, meaning that you can manually move buffers +to change their relative order, if they sort equal with your rule set. + +{*white}# Sort rules{reset} +Autosort evaluates a list of eval expressions (see {*default}/help eval{reset}) and sorts the +buffers based on evaluated result. Earlier rules will be considered first. Only +if earlier rules produced identical results is the result of the next rule +considered for sorting purposes. + +You can debug your sort rules with the `{*default}/autosort debug{reset}` command, which will +print the evaluation results of each rule for each buffer. + +{*brown}NOTE:{reset} The sort rules for version 3 are not compatible with version 2 or vice +versa. You will have to manually port your old rules to version 3 if you have any. + +{*white}# Helper variables{reset} +You may define helper variables for the main sort rules to keep your rules +readable. They can be used in the main sort rules as variables. For example, +a helper variable named `{cyan}foo{reset}` can be accessed in a main rule with the +string `{cyan}${{foo}}{reset}`. + +{*white}# Automatic or manual sorting{reset} +By default, autosort will automatically sort your buffer list whenever a buffer +is opened, merged, unmerged or renamed. This should keep your buffers sorted in +almost all situations. However, you may wish to change the list of signals that +cause your buffer list to be sorted. Simply edit the `{cyan}autosort.sorting.signals{reset}` +option to add or remove any signal you like. + +If you remove all signals you can still sort your buffers manually with the +`{*default}/autosort sort{reset}` command. To prevent all automatic sorting, the option +`{cyan}autosort.sorting.sort_on_config_change{reset}` should also be disabled. + +{*white}# Recommended settings +For the best visual effect, consider setting the following options: + {*white}/set {cyan}irc.look.server_buffer{reset} {brown}independent{reset} + +This setting allows server buffers to be sorted independently, which is +needed to create a hierarchical tree view of the server and channel buffers. + +If you are using the {*default}buflist{reset} plugin you can (ab)use Unicode to draw a tree +structure with the following setting (modify to suit your need): + {*white}/set {cyan}buflist.format.indent {brown}"${{color:237}}${{if:${{buffer.next_buffer.local_variables.type}}=~^(channel|private)$?├─:└─}}"{reset} +''' + +command_completion = '%(plugin_autosort) %(plugin_autosort) %(plugin_autosort) %(plugin_autosort) %(plugin_autosort)' + +info_replace_description = ( + 'Replace all occurrences of `pattern` with `replacement` in the string `source`. ' + 'Can be used to ignore certain strings when sorting by replacing them with an empty string. ' + 'See /help autosort for examples.' +) +info_replace_arguments = 'pattern,replacement,source' + +info_order_description = ( + 'Generate a zero-padded number that corresponds to the index of `value` in the list of options. ' + 'If one of the options is the special value `*`, then any value not explicitly mentioned will be sorted at that position. ' + 'Otherwise, any value that does not match an option is assigned the highest number available. ' + 'Can be used to easily sort buffers based on a manual sequence. ' + 'See /help autosort for examples.' +) +info_order_arguments = 'value,first,second,third,...' + +info_escape_description = ( + 'Escape commas and backslashes in `text` by prepending them with a backslash. ' + 'This is mainly useful to pass arbitrary eval strings as arguments to other autosort info hooks. ' + 'Otherwise, an eval string that expands to something with a comma would be interpreted as multiple arguments.' + 'See /help autosort for examples.' +) +info_escape_arguments = 'text' + + +if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, "", ""): + config = Config('autosort') + + colors = { + 'default': weechat.color('default'), + 'reset': weechat.color('reset'), + 'black': weechat.color('black'), + 'red': weechat.color('red'), + 'green': weechat.color('green'), + 'brown': weechat.color('brown'), + 'yellow': weechat.color('yellow'), + 'blue': weechat.color('blue'), + 'magenta': weechat.color('magenta'), + 'cyan': weechat.color('cyan'), + 'white': weechat.color('white'), + '*default': weechat.color('*default'), + '*black': weechat.color('*black'), + '*red': weechat.color('*red'), + '*green': weechat.color('*green'), + '*brown': weechat.color('*brown'), + '*yellow': weechat.color('*yellow'), + '*blue': weechat.color('*blue'), + '*magenta': weechat.color('*magenta'), + '*cyan': weechat.color('*cyan'), + '*white': weechat.color('*white'), + } + + weechat.hook_config('autosort.*', 'on_config_changed', '') + weechat.hook_completion('plugin_autosort', '', 'on_autosort_complete', '') + weechat.hook_command('autosort', command_description.format(**colors), '', '', command_completion, 'on_autosort_command', '') + weechat.hook_info('autosort_escape', info_escape_description, info_escape_arguments, 'on_info_escape', '') + weechat.hook_info('autosort_replace', info_replace_description, info_replace_arguments, 'on_info_replace', '') + weechat.hook_info('autosort_order', info_order_description, info_order_arguments, 'on_info_order', '') + + apply_config() diff -r d4ff2322e2ab -r 7023c500dd7a weechat/python/colon_complete.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/weechat/python/colon_complete.py Mon Aug 23 11:51:18 2021 -0400 @@ -0,0 +1,69 @@ +SCRIPT_NAME='coloncomplete' +SCRIPT_AUTHOR='Steve Losh <steve@stevelosh.com>' +SCRIPT_VERSION='1.0' +SCRIPT_LICENSE='MIT/X11' +SCRIPT_DESC='Add a colon after nick completion when all the previous words in the input are also nicks.' + +EXTRA_NICKS = ['all', 'backend', 'clojerks', 'ops', 'support'] + +import_ok=True + +try: + import weechat +except ImportError: + print 'This script must be run under WeeChat' + print 'You can obtain a copy of WeeChat, for free, at http://www.weechat.org' + import_ok=False + +weechat_version=0 + +def get_nicks(buffer, prefix=''): + channel = weechat.buffer_get_string(buffer, 'localvar_channel') + server = weechat.buffer_get_string(buffer, 'localvar_server') + prefix = prefix.lower() + + matches = [] + + infolist = weechat.infolist_get('irc_nick', '', '%s,%s' % (server, channel)) + while weechat.infolist_next(infolist): + nick = weechat.infolist_string(infolist, 'name') + if nick != 'localhost' and nick.lower().startswith(prefix): + matches.append(nick) + weechat.infolist_free(infolist) + + for nick in EXTRA_NICKS: + if nick.lower().startswith(prefix): + matches.append(nick) + + return matches + +def completer(data, buffer, command): + cb = weechat.current_buffer() + if command == "/input complete_next": + line = weechat.buffer_get_string(cb, "input") + words = line.split(' ') + prefix = words[-1] + if prefix and words and all([s.endswith(':') for s in words[:-1] if s]): + nicks = get_nicks(cb, prefix) + if len(nicks) == 1: + for _ in range(len(prefix)): + weechat.command(buffer, "/input delete_previous_char") + weechat.command(buffer, "/input insert " + nicks[-1] + ":\\x20") + elif len(nicks) > 1: + l = min(len(nick) for nick in nicks) + for i in range(len(prefix), l): + if len(set(nick[i] for nick in nicks)) > 1: + break + else: + weechat.command(buffer, "/input insert " + nicks[0][i]) + + for nick in nicks: + weechat.prnt(cb, "==> " + nick) + return weechat.WEECHAT_RC_OK_EAT + + return weechat.WEECHAT_RC_OK + +if __name__ == "__main__" and import_ok: + if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, "", ""): + weechat_version = weechat.info_get("version_number", "") or 0 + weechat.hook_command_run('/input complete*', 'completer', '') diff -r d4ff2322e2ab -r 7023c500dd7a weechat/python/sanitize_jira.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/weechat/python/sanitize_jira.py Mon Aug 23 11:51:18 2021 -0400 @@ -0,0 +1,33 @@ +import re, weechat, subprocess + +SCRIPT_NAME = 'sanitize_jira' +SCRIPT_AUTHOR = 'Steve Losh <steve@stevelosh.com>' +SCRIPT_VERSION = '0.0.1' +SCRIPT_LICENSE = 'MIT' +SCRIPT_DESC = 'clean up the garbage jirabot sends to channels into something readable' + +weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, '', '') + +weechat.hook_line('*', '', 'nick_Jira_Cloud', 'sanitize_jira', '') + +first_line_re = re.compile( + r'(?P<link>https://[^/]+/browse/[^?]+)[?]atlOrigin=[^ ]+ [(](?P<title>.+)[)]' +) + +detail_line_re = re.compile( + r'''Status: \x1a\x01[*](?P<status>[^*]+)[*]\x1b\x01.*Type: \x1a\x01[*](?P<type>[^*]+)[*]\x1b\x01.*Assignee: \x1a\x01[*](?P<assignee>[^*]+)[*]\x1b\x01.*Priority: \x1a\x01[*](?P<priority>[^*]+)[*]\x1b\x01''' +) + +def sanitize_jira(data, line): + if 'sign up for an Atlassian account to view this link' in line['message']: + return {'message': ' '} + + m = first_line_re.search(line['message']) + if m: + return {'message': '%s | %s' % (m.group('title'), m.group('link'))} + + m = detail_line_re.search(line['message']) + if m: + return {'message': '%s / %s / %s' % (m.group('type'), m.group('status'), m.group('assignee'))} + + return {} diff -r d4ff2322e2ab -r 7023c500dd7a weechat/relay.conf --- a/weechat/relay.conf Tue Aug 17 15:20:33 2021 -0400 +++ b/weechat/relay.conf Mon Aug 23 11:51:18 2021 -0400 @@ -1,10 +1,10 @@ # -# weechat -- relay.conf +# WeeChat -- relay.conf # # WARNING: It is NOT recommended to edit this file by hand, # especially if WeeChat is running. # -# Use /set or similar command to change settings in WeeChat. +# Use commands like /set or /fset to change settings in WeeChat. # # For more info, see: https://weechat.org/doc/quickstart # @@ -27,14 +27,20 @@ [network] allow_empty_password = off allowed_ips = "" +auth_timeout = 60 bind_address = "" clients_purge_delay = 0 compression_level = 6 ipv6 = on max_clients = 5 +nonce_size = 16 password = "" +password_hash_algo = "*" +password_hash_iterations = 100000 ssl_cert_key = "%h/ssl/relay.pem" ssl_priorities = "NORMAL:-VERS-SSL3.0" +totp_secret = "" +totp_window = 0 websocket_allowed_origins = "" [irc] @@ -45,4 +51,9 @@ backlog_tags = "irc_privmsg" backlog_time_format = "[%H:%M] " +[weechat] +commands = "" + [port] + +[path] diff -r d4ff2322e2ab -r 7023c500dd7a weechat/ruby.conf --- a/weechat/ruby.conf Tue Aug 17 15:20:33 2021 -0400 +++ b/weechat/ruby.conf Mon Aug 23 11:51:18 2021 -0400 @@ -1,10 +1,10 @@ # -# weechat -- ruby.conf +# WeeChat -- ruby.conf # # WARNING: It is NOT recommended to edit this file by hand, # especially if WeeChat is running. # -# Use /set or similar command to change settings in WeeChat. +# Use commands like /set or /fset to change settings in WeeChat. # # For more info, see: https://weechat.org/doc/quickstart # diff -r d4ff2322e2ab -r 7023c500dd7a weechat/script.conf --- a/weechat/script.conf Tue Aug 17 15:20:33 2021 -0400 +++ b/weechat/script.conf Mon Aug 23 11:51:18 2021 -0400 @@ -1,10 +1,10 @@ # -# weechat -- script.conf +# WeeChat -- script.conf # # WARNING: It is NOT recommended to edit this file by hand, # especially if WeeChat is running. # -# Use /set or similar command to change settings in WeeChat. +# Use commands like /set or /fset to change settings in WeeChat. # # For more info, see: https://weechat.org/doc/quickstart # @@ -50,6 +50,7 @@ [scripts] autoload = on cache_expire = 60 +download_enabled = on download_timeout = 30 hold = "" path = "%h/script" diff -r d4ff2322e2ab -r 7023c500dd7a weechat/trigger.conf --- a/weechat/trigger.conf Tue Aug 17 15:20:33 2021 -0400 +++ b/weechat/trigger.conf Mon Aug 23 11:51:18 2021 -0400 @@ -1,10 +1,10 @@ # -# weechat -- trigger.conf +# WeeChat -- trigger.conf # # WARNING: It is NOT recommended to edit this file by hand, # especially if WeeChat is running. # -# Use /set or similar command to change settings in WeeChat. +# Use commands like /set or /fset to change settings in WeeChat. # # For more info, see: https://weechat.org/doc/quickstart # diff -r d4ff2322e2ab -r 7023c500dd7a weechat/urlgrab.conf --- a/weechat/urlgrab.conf Tue Aug 17 15:20:33 2021 -0400 +++ b/weechat/urlgrab.conf Mon Aug 23 11:51:18 2021 -0400 @@ -1,10 +1,10 @@ # -# weechat -- urlgrab.conf +# WeeChat -- urlgrab.conf # # WARNING: It is NOT recommended to edit this file by hand, # especially if WeeChat is running. # -# Use /set or similar command to change settings in WeeChat. +# Use commands like /set or /fset to change settings in WeeChat. # # For more info, see: https://weechat.org/doc/quickstart # diff -r d4ff2322e2ab -r 7023c500dd7a weechat/weechat.conf --- a/weechat/weechat.conf Tue Aug 17 15:20:33 2021 -0400 +++ b/weechat/weechat.conf Mon Aug 23 11:51:18 2021 -0400 @@ -1,10 +1,10 @@ # -# weechat -- weechat.conf +# WeeChat -- weechat.conf # # WARNING: It is NOT recommended to edit this file by hand, # especially if WeeChat is running. # -# Use /set or similar command to change settings in WeeChat. +# Use commands like /set or /fset to change settings in WeeChat. # # For more info, see: https://weechat.org/doc/quickstart # @@ -35,6 +35,7 @@ buffer_search_regex = off buffer_search_where = prefix_message buffer_time_format = "%H:%M" +buffer_time_same = "" color_basic_force_bold = off color_inactive_buffer = off color_inactive_message = on @@ -71,6 +72,7 @@ hotlist_sort = group_time_asc hotlist_suffix = "" hotlist_unique_numbers = on +hotlist_update_on_buffer_switch = on input_cursor_scroll = 20 input_share = none input_share_overwrite = off @@ -89,6 +91,7 @@ mouse_timer_delay = 100 nick_color_force = "" nick_color_hash = djb2 +nick_color_hash_salt = "" nick_color_stop_chars = "_|[" nick_prefix = "" nick_suffix = "" @@ -111,6 +114,7 @@ prefix_network = "--" prefix_quit = "✘" prefix_same_nick = "" +prefix_same_nick_middle = "" prefix_suffix = "|" quote_nick_prefix = "<" quote_nick_suffix = ">" @@ -118,6 +122,7 @@ read_marker = line read_marker_always_show = on read_marker_string = "─" +read_marker_update_on_buffer_switch = on save_config_on_exit = off save_config_with_fsync = off save_layout_on_exit = all @@ -231,7 +236,8 @@ [network] connection_timeout = 60 -gnutls_ca_file = "/etc/ssl/certs/ca-certificates.crt" +gnutls_ca_system = on +gnutls_ca_user = "" gnutls_handshake_timeout = 30 proxy_curl = "" @@ -242,8 +248,16 @@ path = "%h/plugins" save_config_on_unload = on +[signal] +sighup = "${if:${info:weechat_headless}?/reload:/quit -yes}" +sigquit = "/quit -yes" +sigterm = "/quit -yes" +sigusr1 = "" +sigusr2 = "" + [bar] buffers.color_bg = default +buffers.color_bg_inactive = default buffers.color_delim = default buffers.color_fg = default buffers.conditions = "" @@ -258,6 +272,7 @@ buffers.size_max = 0 buffers.type = root buflist.color_bg = default +buflist.color_bg_inactive = default buflist.color_delim = default buflist.color_fg = default buflist.conditions = "" @@ -272,6 +287,7 @@ buflist.size_max = 25 buflist.type = root fset.color_bg = default +fset.color_bg_inactive = default fset.color_delim = cyan fset.color_fg = default fset.conditions = "${buffer.full_name} == fset.fset" @@ -286,6 +302,7 @@ fset.size_max = 3 fset.type = window input.color_bg = default +input.color_bg_inactive = default input.color_delim = green input.color_fg = default input.conditions = "" @@ -300,6 +317,7 @@ input.size_max = 0 input.type = window nicklist.color_bg = default +nicklist.color_bg_inactive = default nicklist.color_delim = cyan nicklist.color_fg = default nicklist.conditions = "nicklist" @@ -314,6 +332,7 @@ nicklist.size_max = 0 nicklist.type = window status.color_bg = green +status.color_bg_inactive = default status.color_delim = 0 status.color_fg = 0 status.conditions = "" @@ -328,6 +347,7 @@ status.size_max = 0 status.type = window title.color_bg = green +title.color_bg_inactive = default title.color_delim = cyan title.color_fg = 16 title.conditions = "" @@ -345,6 +365,11 @@ [layout] [notify] +python.slack.10xgenomics.&cloud-alerts-pagerduty = highlight +python.slack.10xgenomics.&cloud-sumo-prod-support-alerts = highlight +python.slack.10xgenomics.&lacework-10xdev = highlight +python.slack.10xgenomics.&lacework-10xprod = highlight +python.slack.10xgenomics.&testing1234 = highlight [filter] irc_smart = on;*;irc_smart_filter;* @@ -381,6 +406,8 @@ ctrl-Y = "/input clipboard_paste" meta-meta-OP = "/bar scroll buflist * b" meta-meta-OQ = "/bar scroll buflist * e" +meta-meta2-11~ = "/bar scroll buflist * b" +meta-meta2-12~ = "/bar scroll buflist * e" meta-meta2-1~ = "/window scroll_top" meta-meta2-23~ = "/bar scroll nicklist * yb" meta-meta2-24~ = "/bar scroll nicklist * ye" @@ -406,6 +433,7 @@ meta-< = "/input jump_previously_visited_buffer" meta-= = "/filter toggle" meta-> = "/input jump_next_visited_buffer" +meta-B = "/buflist toggle" meta-OA = "/input history_global_previous" meta-OB = "/input history_global_next" meta-OC = "/input move_next_word" @@ -418,6 +446,10 @@ meta-Ob = "/input history_global_next" meta-Oc = "/input move_next_word" meta-Od = "/input move_previous_word" +meta2-11^ = "/bar scroll buflist * -100%" +meta2-11~ = "/bar scroll buflist * -100%" +meta2-12^ = "/bar scroll buflist * +100%" +meta2-12~ = "/bar scroll buflist * +100%" meta2-15~ = "/bar scroll nicklist * y-100%" meta2-17~ = "/bar scroll nicklist * y+100%" meta2-18~ = "/window -1" @@ -426,8 +458,12 @@ meta2-1;3B = "/buffer +1" meta2-1;3C = "/buffer +1" meta2-1;3D = "/buffer -1" +meta2-1;3P = "/bar scroll buflist * b" +meta2-1;3Q = "/bar scroll buflist * e" meta2-1;5A = "/input history_global_previous" meta2-1;5B = "/input history_global_next" +meta2-1;5P = "/bar scroll buflist * -100%" +meta2-1;5Q = "/bar scroll buflist * +100%" meta2-1;9A = "/buffer move -1" meta2-1;9B = "/buffer move +1" meta2-1~ = "/input move_beginning_of_line" @@ -601,6 +637,11 @@ meta2-B = "/cursor move down" meta2-C = "/cursor move right" meta2-D = "/cursor move left" +@chat(python.*):D = "hsignal:slack_cursor_delete" +@chat(python.*):L = "hsignal:slack_cursor_linkarchive" +@chat(python.*):M = "hsignal:slack_cursor_message" +@chat(python.*):R = "hsignal:slack_cursor_reply" +@chat(python.*):T = "hsignal:slack_cursor_thread" @item(buffer_nicklist):K = "/window ${_window_number};/kickban ${nick}" @item(buffer_nicklist):b = "/window ${_window_number};/ban ${nick}" @item(buffer_nicklist):k = "/window ${_window_number};/kick ${nick}" @@ -622,6 +663,7 @@ @chat(fset.fset):button2* = "hsignal:fset_mouse" @chat(fset.fset):wheeldown = "/fset -down 5" @chat(fset.fset):wheelup = "/fset -up 5" +@chat(python.*):button2 = "hsignal:slack_mouse" @chat(script.scripts):button1 = "/window ${_window_number};/script go ${_chat_line_y}" @chat(script.scripts):button2 = "/window ${_window_number};/script go ${_chat_line_y};/script installremove -q ${script_name_with_extension}" @chat(script.scripts):wheeldown = "/script down 5" diff -r d4ff2322e2ab -r 7023c500dd7a weechat/xfer.conf --- a/weechat/xfer.conf Tue Aug 17 15:20:33 2021 -0400 +++ b/weechat/xfer.conf Mon Aug 23 11:51:18 2021 -0400 @@ -1,10 +1,10 @@ # -# weechat -- xfer.conf +# WeeChat -- xfer.conf # # WARNING: It is NOT recommended to edit this file by hand, # especially if WeeChat is running. # -# Use /set or similar command to change settings in WeeChat. +# Use commands like /set or /fset to change settings in WeeChat. # # For more info, see: https://weechat.org/doc/quickstart # @@ -31,7 +31,8 @@ own_ip = "" port_range = "" send_ack = on -speed_limit = 0 +speed_limit_recv = 0 +speed_limit_send = 0 timeout = 300 [file] @@ -43,5 +44,6 @@ auto_resume = on convert_spaces = on download_path = "%h/xfer" +download_temporary_suffix = ".part" upload_path = "~" use_nick_in_filename = on