Populate dotfiles repository

This commit is contained in:
2019-03-24 15:03:56 +01:00
commit 9aae1daa1c
158 changed files with 19135 additions and 0 deletions

View File

@@ -0,0 +1,95 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Arnaud Renevier <arno@renevier.net>
#
# > updated by kbdkode <kbdkode@protonmail.com>
#
# 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/>.
SCRIPT_NAME = "autoconnect"
SCRIPT_AUTHOR = "arno <arno@renevier.net>"
SCRIPT_VERSION = "0.3.2"
SCRIPT_LICENSE = "GPL3"
SCRIPT_DESC = "reopens servers and channels opened last time weechat closed"
SCRIPT_COMMAND = "autoconnect"
try:
import weechat
except:
print "This script must be run under WeeChat."
print "Get WeeChat now at: http://www.weechat.org/"
quit()
weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, "", "")
quitting = False
def get_autojoin_channels(server):
channels = weechat.config_string(weechat.config_get("irc.server.%s.autojoin" % (server,)))
return set(channels.split(',')) if channels else set()
def update_autojoin_channels(server, channels):
channels = ','.join(channels) if channels else "null"
weechat.command("", "/mute /set irc.server.%s.autojoin %s" % (server, channels))
def joinpart_cb(data, signal, signal_data):
server = signal.split(',')[0]
if weechat.info_get("irc_nick_from_host", signal_data) != weechat.info_get("irc_nick", server):
# nick which has joined is not our current nick
return weechat.WEECHAT_RC_OK
autojoin_channels = get_autojoin_channels(server)
if signal.endswith("irc_in2_JOIN"):
weechat.command("", "/mute /set irc.server.%s.autoconnect on" % (server,))
# get all channels joined (without passphrases)
chans = [j.split()[0].strip().lstrip(':') for j in signal_data.split(None, 2)[2].split(',')]
autojoin_channels.add(','.join(chans))
elif signal.endswith("irc_in2_PART"):
channel = signal_data.split(' PART ')[1].split()[0]
autojoin_channels.discard(channel)
update_autojoin_channels(server, autojoin_channels)
weechat.command("", "/save irc")
return weechat.WEECHAT_RC_OK
def disconnect_cb(data, signal, signal_data):
global quitting
if not quitting:
server = signal_data.split(',')[0]
weechat.command("", "/mute /set irc.server.%s.autoconnect null" % (server,))
weechat.command("", "/mute /set irc.server.%s.autojoin null" % (server,))
weechat.command("", "/mute /save irc")
return weechat.WEECHAT_RC_OK
def quit_cb(data, signal, signal_data):
global quitting
quitting = True
return weechat.WEECHAT_RC_OK
weechat.hook_signal("quit", "quit_cb", "")
weechat.hook_signal("*,irc_in2_join", "joinpart_cb", "")
weechat.hook_signal("*,irc_in2_part", "joinpart_cb", "")
weechat.hook_signal("irc_server_disconnected", "disconnect_cb", "")

View File

@@ -0,0 +1 @@
../autoconnect.py

View File

@@ -0,0 +1 @@
../autosort.py

View File

@@ -0,0 +1 @@
../colorize_nicks.py

View File

@@ -0,0 +1 @@
../go.py

View File

@@ -0,0 +1 @@
../urlgrab.py

View File

@@ -0,0 +1 @@
../zerotab.py

View File

@@ -0,0 +1,923 @@
# -*- 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.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.3'
SCRIPT_LICENSE = 'GPL3'
SCRIPT_DESC = 'Flexible automatic (or manual) buffer sorting based on eval expressions.'
config = None
hooks = []
timer = None
# 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 dictonary 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 seting manually.'.format(key, type(value)))
return {}
return parsed
class Config:
''' The autosort configuration. '''
default_rules = json.dumps([
'${core_first}',
'${irc_last}',
'${buffer.plugin.name}',
'${irc_raw_first}',
'${if:${plugin}==irc?${server}}',
'${if:${plugin}==irc?${info:autosort_order,${type},server,*,channel,private}}',
'${if:${plugin}==irc?${hashless_name}}',
'${buffer.full_name}',
])
default_helpers = json.dumps({
'core_first': '${if:${buffer.full_name}!=core.weechat}',
'irc_first': '${if:${buffer.plugin.name}!=irc}',
'irc_last': '${if:${buffer.plugin.name}==irc}',
'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,#,,${buffer.name}}',
})
default_signal_delay = 5
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_on_config = True
self.__case_sensitive = None
self.__rules = None
self.__helpers = None
self.__signals = None
self.__signal_delay = 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, '', '', '', '', '', '', '', '', '', '')
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_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)
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_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, 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 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():
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)
def command_sort(buffer, command, args):
''' Sort the buffers and print a confirmation. '''
start = perf_counter()
do_sort()
elapsed = perf_counter() - start
log("Finished sorting buffers in {0:.4f} seconds.".format(elapsed))
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 evalutaion 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 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_signal(*args, **kwargs):
global timer
''' Called whenever the buffer list changes. '''
if timer is not None:
weechat.unhook(timer)
timer = None
weechat.hook_timer(config.signal_delay, 0, 1, "on_timeout", "")
return weechat.WEECHAT_RC_OK
def on_timeout(pointer, remaining_calls):
global timer
timer = None
do_sort()
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:
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, 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:
arg, args = parse_arg(args)
if arg is None: break
result.append(arg)
i += 1
return result, args
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}# 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.
{*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}# Replacing substrings{reset}
There is no default method for replacing text inside eval expressions. However,
autosort adds a `replace` info hook that can be used inside eval expressions:
{cyan}${{info:autosort_replace,from,to,text}}{reset}
For example, to strip all hashes from a buffer name, you could write:
{cyan}${{info:autosort_replace,#,,${{buffer.name}}}}{reset}
You can escape commas and backslashes inside the arguments by prefixing them with
a backslash.
{*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}
{*white}/set {cyan}buffers.look.indenting{reset} {brown}on{reset}
The first setting allows server buffers to be sorted independently, which is
needed to create a hierarchical tree view of the server and channel buffers.
The second one indents channel and private buffers in the buffer list of the
`{*default}buffers.pl{reset}` script.
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 occurences of `from` with `to` in the string `text`.'
info_replace_arguments = 'from,to,text'
info_order_description = (
'Get a zero padded index of a value in a list of possible values.'
'If the value is not found, the index for `*` is returned.'
'If there is no `*` in the list, the highest index + 1 is returned.'
)
info_order_arguments = 'value,first,second,third,...'
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_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()

View File

@@ -0,0 +1,400 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2010 by xt <xt@bash.no>
#
# 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/>.
#
# This script colors nicks in IRC channels in the actual message
# not just in the prefix section.
#
#
# History:
# 2018-04-06: Joey Pabalinas <joeypabalinas@gmail.com>
# version 26: fix freezes with too many nicks in one line
# 2018-03-18: nils_2
# version 25: fix unable to run function colorize_config_reload_cb()
# 2017-06-20: lbeziaud <louis.beziaud@ens-rennes.fr>
# version 24: colorize utf8 nicks
# 2017-03-01, arza <arza@arza.us>
# version 23: don't colorize nicklist group names
# 2016-05-01, Simmo Saan <simmo.saan@gmail.com>
# version 22: invalidate cached colors on hash algorithm change
# 2015-07-28, xt
# version 21: fix problems with nicks with commas in them
# 2015-04-19, xt
# version 20: fix ignore of nicks in URLs
# 2015-04-18, xt
# version 19: new option ignore nicks in URLs
# 2015-03-03, xt
# version 18: iterate buffers looking for nicklists instead of servers
# 2015-02-23, holomorph
# version 17: fix coloring in non-channel buffers (#58)
# 2014-09-17, holomorph
# version 16: use weechat config facilities
# clean unused, minor linting, some simplification
# 2014-05-05, holomorph
# version 15: fix python2-specific re.search check
# 2013-01-29, nils_2
# version 14: make script compatible with Python 3.x
# 2012-10-19, ldvx
# version 13: Iterate over every word to prevent incorrect colorization of
# nicks. Added option greedy_matching.
# 2012-04-28, ldvx
# version 12: added ignore_tags to avoid colorizing nicks if tags are present
# 2012-01-14, nesthib
# version 11: input_text_display hook and modifier to colorize nicks in input bar
# 2010-12-22, xt
# version 10: hook config option for updating blacklist
# 2010-12-20, xt
# version 0.9: hook new config option for weechat 0.3.4
# 2010-11-01, nils_2
# version 0.8: hook_modifier() added to communicate with rainbow_text
# 2010-10-01, xt
# version 0.7: changes to support non-irc-plugins
# 2010-07-29, xt
# version 0.6: compile regexp as per patch from Chris quigybo@hotmail.com
# 2010-07-19, xt
# version 0.5: fix bug with incorrect coloring of own nick
# 2010-06-02, xt
# version 0.4: update to reflect API changes
# 2010-03-26, xt
# version 0.3: fix error with exception
# 2010-03-24, xt
# version 0.2: use ignore_channels when populating to increase performance.
# 2010-02-03, xt
# version 0.1: initial (based on ruby script by dominikh)
#
# Known issues: nicks will not get colorized if they begin with a character
# such as ~ (which some irc networks do happen to accept)
import weechat
import re
w = weechat
SCRIPT_NAME = "colorize_nicks"
SCRIPT_AUTHOR = "xt <xt@bash.no>"
SCRIPT_VERSION = "26"
SCRIPT_LICENSE = "GPL"
SCRIPT_DESC = "Use the weechat nick colors in the chat area"
# Based on the recommendations in RFC 7613. A valid nick is composed
# of anything but " ,*?.!@".
VALID_NICK = r'([@~&!%+-])?([^\s,\*?\.!@]+)'
valid_nick_re = re.compile(VALID_NICK)
ignore_channels = []
ignore_nicks = []
# Dict with every nick on every channel with its color as lookup value
colored_nicks = {}
CONFIG_FILE_NAME = "colorize_nicks"
# config file and options
colorize_config_file = ""
colorize_config_option = {}
def colorize_config_init():
'''
Initialization of configuration file.
Sections: look.
'''
global colorize_config_file, colorize_config_option
colorize_config_file = weechat.config_new(CONFIG_FILE_NAME,
"", "")
if colorize_config_file == "":
return
# section "look"
section_look = weechat.config_new_section(
colorize_config_file, "look", 0, 0, "", "", "", "", "", "", "", "", "", "")
if section_look == "":
weechat.config_free(colorize_config_file)
return
colorize_config_option["blacklist_channels"] = weechat.config_new_option(
colorize_config_file, section_look, "blacklist_channels",
"string", "Comma separated list of channels", "", 0, 0,
"", "", 0, "", "", "", "", "", "")
colorize_config_option["blacklist_nicks"] = weechat.config_new_option(
colorize_config_file, section_look, "blacklist_nicks",
"string", "Comma separated list of nicks", "", 0, 0,
"so,root", "so,root", 0, "", "", "", "", "", "")
colorize_config_option["min_nick_length"] = weechat.config_new_option(
colorize_config_file, section_look, "min_nick_length",
"integer", "Minimum length nick to colorize", "",
2, 20, "", "", 0, "", "", "", "", "", "")
colorize_config_option["colorize_input"] = weechat.config_new_option(
colorize_config_file, section_look, "colorize_input",
"boolean", "Whether to colorize input", "", 0,
0, "off", "off", 0, "", "", "", "", "", "")
colorize_config_option["ignore_tags"] = weechat.config_new_option(
colorize_config_file, section_look, "ignore_tags",
"string", "Comma separated list of tags to ignore; i.e. irc_join,irc_part,irc_quit", "", 0, 0,
"", "", 0, "", "", "", "", "", "")
colorize_config_option["greedy_matching"] = weechat.config_new_option(
colorize_config_file, section_look, "greedy_matching",
"boolean", "If off, then use lazy matching instead", "", 0,
0, "on", "on", 0, "", "", "", "", "", "")
colorize_config_option["match_limit"] = weechat.config_new_option(
colorize_config_file, section_look, "match_limit",
"integer", "Fall back to lazy matching if greedy matches exceeds this number", "",
20, 1000, "", "", 0, "", "", "", "", "", "")
colorize_config_option["ignore_nicks_in_urls"] = weechat.config_new_option(
colorize_config_file, section_look, "ignore_nicks_in_urls",
"boolean", "If on, don't colorize nicks inside URLs", "", 0,
0, "off", "off", 0, "", "", "", "", "", "")
def colorize_config_read():
''' Read configuration file. '''
global colorize_config_file
return weechat.config_read(colorize_config_file)
def colorize_nick_color(nick, my_nick):
''' Retrieve nick color from weechat. '''
if nick == my_nick:
return w.color(w.config_string(w.config_get('weechat.color.chat_nick_self')))
else:
return w.info_get('irc_nick_color', nick)
def colorize_cb(data, modifier, modifier_data, line):
''' Callback that does the colorizing, and returns new line if changed '''
global ignore_nicks, ignore_channels, colored_nicks
full_name = modifier_data.split(';')[1]
channel = '.'.join(full_name.split('.')[1:])
buffer = w.buffer_search('', full_name)
# Check if buffer has colorized nicks
if buffer not in colored_nicks:
return line
if channel and channel in ignore_channels:
return line
min_length = w.config_integer(colorize_config_option['min_nick_length'])
reset = w.color('reset')
# Don't colorize if the ignored tag is present in message
tags_line = modifier_data.rsplit(';')
if len(tags_line) >= 3:
tags_line = tags_line[2].split(',')
for i in w.config_string(colorize_config_option['ignore_tags']).split(','):
if i in tags_line:
return line
for words in valid_nick_re.findall(line):
nick = words[1]
# Check that nick is not ignored and longer than minimum length
if len(nick) < min_length or nick in ignore_nicks:
continue
# If the matched word is not a known nick, we try to match the
# word without its first or last character (if not a letter).
# This is necessary as "foo:" is a valid nick, which could be
# adressed as "foo::".
if nick not in colored_nicks[buffer]:
if not nick[-1].isalpha() and not nick[0].isalpha():
if nick[1:-1] in colored_nicks[buffer]:
nick = nick[1:-1]
elif not nick[0].isalpha():
if nick[1:] in colored_nicks[buffer]:
nick = nick[1:]
elif not nick[-1].isalpha():
if nick[:-1] in colored_nicks[buffer]:
nick = nick[:-1]
# Check that nick is in the dictionary colored_nicks
if nick in colored_nicks[buffer]:
nick_color = colored_nicks[buffer][nick]
try:
# Let's use greedy matching. Will check against every word in a line.
if w.config_boolean(colorize_config_option['greedy_matching']):
cnt = 0
limit = w.config_integer(colorize_config_option['match_limit'])
for word in line.split():
cnt += 1
assert cnt < limit
# if cnt > limit:
# raise RuntimeError('Exceeded colorize_nicks.look.match_limit.');
if w.config_boolean(colorize_config_option['ignore_nicks_in_urls']) and \
word.startswith(('http://', 'https://')):
continue
if nick in word:
# Is there a nick that contains nick and has a greater lenght?
# If so let's save that nick into var biggest_nick
biggest_nick = ""
for i in colored_nicks[buffer]:
cnt += 1
assert cnt < limit
if nick in i and nick != i and len(i) > len(nick):
if i in word:
# If a nick with greater len is found, and that word
# also happens to be in word, then let's save this nick
biggest_nick = i
# If there's a nick with greater len, then let's skip this
# As we will have the chance to colorize when biggest_nick
# iterates being nick.
if len(biggest_nick) > 0 and biggest_nick in word:
pass
elif len(word) < len(biggest_nick) or len(biggest_nick) == 0:
new_word = word.replace(nick, '%s%s%s' % (nick_color, nick, reset))
line = line.replace(word, new_word)
# Switch to lazy matching
else:
raise AssertionError
except AssertionError:
# Let's use lazy matching for nick
nick_color = colored_nicks[buffer][nick]
# The two .? are in case somebody writes "nick:", "nick,", etc
# to address somebody
regex = r"(\A|\s).?(%s).?(\Z|\s)" % re.escape(nick)
match = re.search(regex, line)
if match is not None:
new_line = line[:match.start(2)] + nick_color+nick+reset + line[match.end(2):]
line = new_line
return line
def colorize_input_cb(data, modifier, modifier_data, line):
''' Callback that does the colorizing in input '''
global ignore_nicks, ignore_channels, colored_nicks
min_length = w.config_integer(colorize_config_option['min_nick_length'])
if not w.config_boolean(colorize_config_option['colorize_input']):
return line
buffer = w.current_buffer()
# Check if buffer has colorized nicks
if buffer not in colored_nicks:
return line
channel = w.buffer_get_string(buffer, 'name')
if channel and channel in ignore_channels:
return line
reset = w.color('reset')
for words in valid_nick_re.findall(line):
nick = words[1]
# Check that nick is not ignored and longer than minimum length
if len(nick) < min_length or nick in ignore_nicks:
continue
if nick in colored_nicks[buffer]:
nick_color = colored_nicks[buffer][nick]
line = line.replace(nick, '%s%s%s' % (nick_color, nick, reset))
return line
def populate_nicks(*args):
''' Fills entire dict with all nicks weechat can see and what color it has
assigned to it. '''
global colored_nicks
colored_nicks = {}
buffers = w.infolist_get('buffer', '', '')
while w.infolist_next(buffers):
buffer_ptr = w.infolist_pointer(buffers, 'pointer')
my_nick = w.buffer_get_string(buffer_ptr, 'localvar_nick')
nicklist = w.infolist_get('nicklist', buffer_ptr, '')
while w.infolist_next(nicklist):
if buffer_ptr not in colored_nicks:
colored_nicks[buffer_ptr] = {}
if w.infolist_string(nicklist, 'type') != 'nick':
continue
nick = w.infolist_string(nicklist, 'name')
nick_color = colorize_nick_color(nick, my_nick)
colored_nicks[buffer_ptr][nick] = nick_color
w.infolist_free(nicklist)
w.infolist_free(buffers)
return w.WEECHAT_RC_OK
def add_nick(data, signal, type_data):
''' Add nick to dict of colored nicks '''
global colored_nicks
# Nicks can have , in them in some protocols
splitted = type_data.split(',')
pointer = splitted[0]
nick = ",".join(splitted[1:])
if pointer not in colored_nicks:
colored_nicks[pointer] = {}
my_nick = w.buffer_get_string(pointer, 'localvar_nick')
nick_color = colorize_nick_color(nick, my_nick)
colored_nicks[pointer][nick] = nick_color
return w.WEECHAT_RC_OK
def remove_nick(data, signal, type_data):
''' Remove nick from dict with colored nicks '''
global colored_nicks
# Nicks can have , in them in some protocols
splitted = type_data.split(',')
pointer = splitted[0]
nick = ",".join(splitted[1:])
if pointer in colored_nicks and nick in colored_nicks[pointer]:
del colored_nicks[pointer][nick]
return w.WEECHAT_RC_OK
def update_blacklist(*args):
''' Set the blacklist for channels and nicks. '''
global ignore_channels, ignore_nicks
ignore_channels = w.config_string(colorize_config_option['blacklist_channels']).split(',')
ignore_nicks = w.config_string(colorize_config_option['blacklist_nicks']).split(',')
return w.WEECHAT_RC_OK
if __name__ == "__main__":
if w.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE,
SCRIPT_DESC, "", ""):
colorize_config_init()
colorize_config_read()
# Run once to get data ready
update_blacklist()
populate_nicks()
w.hook_signal('nicklist_nick_added', 'add_nick', '')
w.hook_signal('nicklist_nick_removed', 'remove_nick', '')
w.hook_modifier('weechat_print', 'colorize_cb', '')
# Hook config for changing colors
w.hook_config('weechat.color.chat_nick_colors', 'populate_nicks', '')
w.hook_config('weechat.look.nick_color_hash', 'populate_nicks', '')
# Hook for working togheter with other scripts (like colorize_lines)
w.hook_modifier('colorize_nicks', 'colorize_cb', '')
# Hook for modifying input
w.hook_modifier('250|input_text_display', 'colorize_input_cb', '')
# Hook for updating blacklist (this could be improved to use fnmatch)
weechat.hook_config('%s.look.blacklist*' % SCRIPT_NAME, 'update_blacklist', '')

View File

@@ -0,0 +1,561 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2009-2014 Sébastien Helleu <flashcode@flashtux.org>
# Copyright (C) 2010 m4v <lambdae2@gmail.com>
# Copyright (C) 2011 stfn <stfnmd@googlemail.com>
#
# 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/>.
#
#
# History:
#
# 2017-04-01, Sébastien Helleu <flashcode@flashtux.org>:
# version 2.5: add option "buffer_number"
# 2017-03-02, Sébastien Helleu <flashcode@flashtux.org>:
# version 2.4: fix syntax and indentation error
# 2017-02-25, Simmo Saan <simmo.saan@gmail.com>
# version 2.3: fix fuzzy search breaking buffer number search display
# 2016-01-28, ylambda <ylambda@koalabeast.com>
# version 2.2: add option "fuzzy_search"
# 2015-11-12, nils_2 <weechatter@arcor.de>
# version 2.1: fix problem with buffer short_name "weechat", using option
# "use_core_instead_weechat", see:
# https://github.com/weechat/weechat/issues/574
# 2014-05-12, Sébastien Helleu <flashcode@flashtux.org>:
# version 2.0: add help on options, replace option "sort_by_activity" by
# "sort" (add sort by name and first match at beginning of
# name and by number), PEP8 compliance
# 2012-11-26, Nei <anti.teamidiot.de>
# version 1.9: add auto_jump option to automatically go to buffer when it
# is uniquely selected
# 2012-09-17, Sébastien Helleu <flashcode@flashtux.org>:
# version 1.8: fix jump to non-active merged buffers (jump with buffer name
# instead of number)
# 2012-01-03 nils_2 <weechatter@arcor.de>
# version 1.7: add option "use_core_instead_weechat"
# 2012-01-03, Sébastien Helleu <flashcode@flashtux.org>:
# version 1.6: make script compatible with Python 3.x
# 2011-08-24, stfn <stfnmd@googlemail.com>:
# version 1.5: /go with name argument jumps directly to buffer
# Remember cursor position in buffer input
# 2011-05-31, Elián Hanisch <lambdae2@gmail.com>:
# version 1.4: Sort list of buffers by activity.
# 2011-04-25, Sébastien Helleu <flashcode@flashtux.org>:
# version 1.3: add info "go_running" (used by script input_lock.rb)
# 2010-11-01, Sébastien Helleu <flashcode@flashtux.org>:
# version 1.2: use high priority for hooks to prevent conflict with other
# plugins/scripts (WeeChat >= 0.3.4 only)
# 2010-03-25, Elián Hanisch <lambdae2@gmail.com>:
# version 1.1: use a space to match the end of a string
# 2009-11-16, Sébastien Helleu <flashcode@flashtux.org>:
# version 1.0: add new option to display short names
# 2009-06-15, Sébastien Helleu <flashcode@flashtux.org>:
# version 0.9: fix typo in /help go with command /key
# 2009-05-16, Sébastien Helleu <flashcode@flashtux.org>:
# version 0.8: search buffer by number, fix bug when window is split
# 2009-05-03, Sébastien Helleu <flashcode@flashtux.org>:
# version 0.7: eat tab key (do not complete input, just move buffer
# pointer)
# 2009-05-02, Sébastien Helleu <flashcode@flashtux.org>:
# version 0.6: sync with last API changes
# 2009-03-22, Sébastien Helleu <flashcode@flashtux.org>:
# version 0.5: update modifier signal name for input text display,
# fix arguments for function string_remove_color
# 2009-02-18, Sébastien Helleu <flashcode@flashtux.org>:
# version 0.4: do not hook command and init options if register failed
# 2009-02-08, Sébastien Helleu <flashcode@flashtux.org>:
# version 0.3: case insensitive search for buffers names
# 2009-02-08, Sébastien Helleu <flashcode@flashtux.org>:
# version 0.2: add help about Tab key
# 2009-02-08, Sébastien Helleu <flashcode@flashtux.org>:
# version 0.1: initial release
#
"""
Quick jump to buffers.
(this script requires WeeChat 0.3.0 or newer)
"""
from __future__ import print_function
SCRIPT_NAME = 'go'
SCRIPT_AUTHOR = 'Sébastien Helleu <flashcode@flashtux.org>'
SCRIPT_VERSION = '2.5'
SCRIPT_LICENSE = 'GPL3'
SCRIPT_DESC = 'Quick jump to buffers'
SCRIPT_COMMAND = 'go'
IMPORT_OK = True
try:
import weechat
except ImportError:
print('This script must be run under WeeChat.')
print('Get WeeChat now at: http://www.weechat.org/')
IMPORT_OK = False
import re
# script options
SETTINGS = {
'color_number': (
'yellow,magenta',
'color for buffer number (not selected)'),
'color_number_selected': (
'yellow,red',
'color for selected buffer number'),
'color_name': (
'black,cyan',
'color for buffer name (not selected)'),
'color_name_selected': (
'black,brown',
'color for a selected buffer name'),
'color_name_highlight': (
'red,cyan',
'color for highlight in buffer name (not selected)'),
'color_name_highlight_selected': (
'red,brown',
'color for highlight in a selected buffer name'),
'message': (
'Go to: ',
'message to display before list of buffers'),
'short_name': (
'off',
'display and search in short names instead of buffer name'),
'sort': (
'number,beginning',
'comma-separated list of keys to sort buffers '
'(the order is important, sorts are performed in the given order): '
'name = sort by name (or short name), ',
'hotlist = sort by hotlist order, '
'number = first match a buffer number before digits in name, '
'beginning = first match at beginning of names (or short names); '
'the default sort of buffers is by numbers'),
'use_core_instead_weechat': (
'off',
'use name "core" instead of "weechat" for core buffer'),
'auto_jump': (
'off',
'automatically jump to buffer when it is uniquely selected'),
'fuzzy_search': (
'off',
'search buffer matches using approximation'),
'buffer_number': (
'on',
'display buffer number'),
}
# hooks management
HOOK_COMMAND_RUN = {
'input': ('/input *', 'go_command_run_input'),
'buffer': ('/buffer *', 'go_command_run_buffer'),
'window': ('/window *', 'go_command_run_window'),
}
hooks = {}
# input before command /go (we'll restore it later)
saved_input = ''
saved_input_pos = 0
# last user input (if changed, we'll update list of matching buffers)
old_input = None
# matching buffers
buffers = []
buffers_pos = 0
def go_option_enabled(option):
"""Checks if a boolean script option is enabled or not."""
return weechat.config_string_to_boolean(weechat.config_get_plugin(option))
def go_info_running(data, info_name, arguments):
"""Returns "1" if go is running, otherwise "0"."""
return '1' if 'modifier' in hooks else '0'
def go_unhook_one(hook):
"""Unhook something hooked by this script."""
global hooks
if hook in hooks:
weechat.unhook(hooks[hook])
del hooks[hook]
def go_unhook_all():
"""Unhook all."""
go_unhook_one('modifier')
for hook in HOOK_COMMAND_RUN:
go_unhook_one(hook)
def go_hook_all():
"""Hook command_run and modifier."""
global hooks
priority = ''
version = weechat.info_get('version_number', '') or 0
# use high priority for hook to prevent conflict with other plugins/scripts
# (WeeChat >= 0.3.4 only)
if int(version) >= 0x00030400:
priority = '2000|'
for hook, value in HOOK_COMMAND_RUN.items():
if hook not in hooks:
hooks[hook] = weechat.hook_command_run(
'%s%s' % (priority, value[0]),
value[1], '')
if 'modifier' not in hooks:
hooks['modifier'] = weechat.hook_modifier(
'input_text_display_with_cursor', 'go_input_modifier', '')
def go_start(buf):
"""Start go on buffer."""
global saved_input, saved_input_pos, old_input, buffers_pos
go_hook_all()
saved_input = weechat.buffer_get_string(buf, 'input')
saved_input_pos = weechat.buffer_get_integer(buf, 'input_pos')
weechat.buffer_set(buf, 'input', '')
old_input = None
buffers_pos = 0
def go_end(buf):
"""End go on buffer."""
global saved_input, saved_input_pos, old_input
go_unhook_all()
weechat.buffer_set(buf, 'input', saved_input)
weechat.buffer_set(buf, 'input_pos', str(saved_input_pos))
old_input = None
def go_match_beginning(buf, string):
"""Check if a string matches the beginning of buffer name/short name."""
if not string:
return False
esc_str = re.escape(string)
if re.search(r'^#?' + esc_str, buf['name']) \
or re.search(r'^#?' + esc_str, buf['short_name']):
return True
return False
def go_match_fuzzy(name, string):
"""Check if string matches name using approximation."""
if not string:
return False
name_len = len(name)
string_len = len(string)
if string_len > name_len:
return False
if name_len == string_len:
return name == string
# Attempt to match all chars somewhere in name
prev_index = -1
for i, char in enumerate(string):
index = name.find(char, prev_index+1)
if index == -1:
return False
prev_index = index
return True
def go_now(buf, args):
"""Go to buffer specified by args."""
listbuf = go_matching_buffers(args)
if not listbuf:
return
# prefer buffer that matches at beginning (if option is enabled)
if 'beginning' in weechat.config_get_plugin('sort').split(','):
for index in range(len(listbuf)):
if go_match_beginning(listbuf[index], args):
weechat.command(buf,
'/buffer ' + str(listbuf[index]['full_name']))
return
# jump to first buffer in matching buffers by default
weechat.command(buf, '/buffer ' + str(listbuf[0]['full_name']))
def go_cmd(data, buf, args):
"""Command "/go": just hook what we need."""
global hooks
if args:
go_now(buf, args)
elif 'modifier' in hooks:
go_end(buf)
else:
go_start(buf)
return weechat.WEECHAT_RC_OK
def go_matching_buffers(strinput):
"""Return a list with buffers matching user input."""
global buffers_pos
listbuf = []
if len(strinput) == 0:
buffers_pos = 0
strinput = strinput.lower()
infolist = weechat.infolist_get('buffer', '', '')
while weechat.infolist_next(infolist):
short_name = weechat.infolist_string(infolist, 'short_name')
if go_option_enabled('short_name'):
name = weechat.infolist_string(infolist, 'short_name')
else:
name = weechat.infolist_string(infolist, 'name')
if name == 'weechat' \
and go_option_enabled('use_core_instead_weechat') \
and weechat.infolist_string(infolist, 'plugin_name') == 'core':
name = 'core'
number = weechat.infolist_integer(infolist, 'number')
full_name = weechat.infolist_string(infolist, 'full_name')
if not full_name:
full_name = '%s.%s' % (
weechat.infolist_string(infolist, 'plugin_name'),
weechat.infolist_string(infolist, 'name'))
pointer = weechat.infolist_pointer(infolist, 'pointer')
matching = name.lower().find(strinput) >= 0
if not matching and strinput[-1] == ' ':
matching = name.lower().endswith(strinput.strip())
if not matching and go_option_enabled('fuzzy_search'):
matching = go_match_fuzzy(name.lower(), strinput)
if not matching and strinput.isdigit():
matching = str(number).startswith(strinput)
if len(strinput) == 0 or matching:
listbuf.append({
'number': number,
'short_name': short_name,
'name': name,
'full_name': full_name,
'pointer': pointer,
})
weechat.infolist_free(infolist)
# sort buffers
hotlist = []
infolist = weechat.infolist_get('hotlist', '', '')
while weechat.infolist_next(infolist):
hotlist.append(
weechat.infolist_pointer(infolist, 'buffer_pointer'))
weechat.infolist_free(infolist)
last_index_hotlist = len(hotlist)
def _sort_name(buf):
"""Sort buffers by name (or short name)."""
return buf['name']
def _sort_hotlist(buf):
"""Sort buffers by hotlist order."""
try:
return hotlist.index(buf['pointer'])
except ValueError:
# not in hotlist, always last.
return last_index_hotlist
def _sort_match_number(buf):
"""Sort buffers by match on number."""
return 0 if str(buf['number']) == strinput else 1
def _sort_match_beginning(buf):
"""Sort buffers by match at beginning."""
return 0 if go_match_beginning(buf, strinput) else 1
funcs = {
'name': _sort_name,
'hotlist': _sort_hotlist,
'number': _sort_match_number,
'beginning': _sort_match_beginning,
}
for key in weechat.config_get_plugin('sort').split(','):
if key in funcs:
listbuf = sorted(listbuf, key=funcs[key])
if not strinput:
index = [i for i, buf in enumerate(listbuf)
if buf['pointer'] == weechat.current_buffer()]
if index:
buffers_pos = index[0]
return listbuf
def go_buffers_to_string(listbuf, pos, strinput):
"""Return string built with list of buffers found (matching user input)."""
string = ''
strinput = strinput.lower()
for i in range(len(listbuf)):
selected = '_selected' if i == pos else ''
buffer_name = listbuf[i]['name']
index = buffer_name.lower().find(strinput)
if index >= 0:
index2 = index + len(strinput)
name = '%s%s%s%s%s' % (
buffer_name[:index],
weechat.color(weechat.config_get_plugin(
'color_name_highlight' + selected)),
buffer_name[index:index2],
weechat.color(weechat.config_get_plugin(
'color_name' + selected)),
buffer_name[index2:])
elif go_option_enabled("fuzzy_search") and \
go_match_fuzzy(buffer_name.lower(), strinput):
name = ""
prev_index = -1
for char in strinput.lower():
index = buffer_name.lower().find(char, prev_index+1)
if prev_index < 0:
name += buffer_name[:index]
name += weechat.color(weechat.config_get_plugin(
'color_name_highlight' + selected))
if prev_index >= 0 and index > prev_index+1:
name += weechat.color(weechat.config_get_plugin(
'color_name' + selected))
name += buffer_name[prev_index+1:index]
name += weechat.color(weechat.config_get_plugin(
'color_name_highlight' + selected))
name += buffer_name[index]
prev_index = index
name += weechat.color(weechat.config_get_plugin(
'color_name' + selected))
name += buffer_name[prev_index+1:]
else:
name = buffer_name
string += ' '
if go_option_enabled('buffer_number'):
string += '%s%s' % (
weechat.color(weechat.config_get_plugin(
'color_number' + selected)),
str(listbuf[i]['number']))
string += '%s%s%s' % (
weechat.color(weechat.config_get_plugin(
'color_name' + selected)),
name,
weechat.color('reset'))
return ' ' + string if string else ''
def go_input_modifier(data, modifier, modifier_data, string):
"""This modifier is called when input text item is built by WeeChat.
This is commonly called after changes in input or cursor move: it builds
a new input with prefix ("Go to:"), and suffix (list of buffers found).
"""
global old_input, buffers, buffers_pos
if modifier_data != weechat.current_buffer():
return ''
names = ''
new_input = weechat.string_remove_color(string, '')
new_input = new_input.lstrip()
if old_input is None or new_input != old_input:
old_buffers = buffers
buffers = go_matching_buffers(new_input)
if buffers != old_buffers and len(new_input) > 0:
if len(buffers) == 1 and go_option_enabled('auto_jump'):
weechat.command(modifier_data, '/wait 1ms /input return')
buffers_pos = 0
old_input = new_input
names = go_buffers_to_string(buffers, buffers_pos, new_input.strip())
return weechat.config_get_plugin('message') + string + names
def go_command_run_input(data, buf, command):
"""Function called when a command "/input xxx" is run."""
global buffers, buffers_pos
if command == '/input search_text' or command.find('/input jump') == 0:
# search text or jump to another buffer is forbidden now
return weechat.WEECHAT_RC_OK_EAT
elif command == '/input complete_next':
# choose next buffer in list
buffers_pos += 1
if buffers_pos >= len(buffers):
buffers_pos = 0
weechat.hook_signal_send('input_text_changed',
weechat.WEECHAT_HOOK_SIGNAL_STRING, '')
return weechat.WEECHAT_RC_OK_EAT
elif command == '/input complete_previous':
# choose previous buffer in list
buffers_pos -= 1
if buffers_pos < 0:
buffers_pos = len(buffers) - 1
weechat.hook_signal_send('input_text_changed',
weechat.WEECHAT_HOOK_SIGNAL_STRING, '')
return weechat.WEECHAT_RC_OK_EAT
elif command == '/input return':
# switch to selected buffer (if any)
go_end(buf)
if len(buffers) > 0:
weechat.command(
buf, '/buffer ' + str(buffers[buffers_pos]['full_name']))
return weechat.WEECHAT_RC_OK_EAT
return weechat.WEECHAT_RC_OK
def go_command_run_buffer(data, buf, command):
"""Function called when a command "/buffer xxx" is run."""
return weechat.WEECHAT_RC_OK_EAT
def go_command_run_window(data, buf, command):
"""Function called when a command "/window xxx" is run."""
return weechat.WEECHAT_RC_OK_EAT
def go_unload_script():
"""Function called when script is unloaded."""
go_unhook_all()
return weechat.WEECHAT_RC_OK
def go_main():
"""Entry point."""
if not weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION,
SCRIPT_LICENSE, SCRIPT_DESC,
'go_unload_script', ''):
return
weechat.hook_command(
SCRIPT_COMMAND,
'Quick jump to buffers', '[name]',
'name: directly jump to buffer by name (without argument, list is '
'displayed)\n\n'
'You can bind command to a key, for example:\n'
' /key bind meta-g /go\n\n'
'You can use completion key (commonly Tab and shift-Tab) to select '
'next/previous buffer in list.',
'%(buffers_names)',
'go_cmd', '')
# set default settings
version = weechat.info_get('version_number', '') or 0
for option, value in SETTINGS.items():
if not weechat.config_is_set_plugin(option):
weechat.config_set_plugin(option, value[0])
if int(version) >= 0x00030500:
weechat.config_set_desc_plugin(
option, '%s (default: "%s")' % (value[1], value[0]))
weechat.hook_info('go_running',
'Return "1" if go is running, otherwise "0"',
'',
'go_info_running', '')
if __name__ == "__main__" and IMPORT_OK:
go_main()

View File

@@ -0,0 +1,699 @@
# -*- coding: utf-8 -*-
#
# UrlGrab, for weechat version >= 0.3.0
#
# Listens to all channels for URLs, collects them in a list, and launches
# them in your favourite web server on the local host or a remote server.
# Copies url to X11 clipboard via xsel
# (http://www.vergenet.net/~conrad/software/xsel)
#
# Usage:
#
# The /url command provides access to all UrlGrab functions. Run
# '/help url' for complete command usage.
#
# In general, use '/url list' to list the entire url list for the current
# channel, and '/url <n>' to launch the nth url in the list. For
# example, to launch the first (and most-recently added) url in the list,
# you would run '/url 1'
#
# From the server window, you must specify a specific channel for the
# list and launch commands, for example:
# /url list weechat
# /url 3 weechat
#
# Configuration:
#
# The '/url set' command lets you get and set the following options:
#
# historysize
# The maximum number of URLs saved per channel. Default is 10
#
# method
# Must be one of 'local' or 'remote' - Defines how URLs are launched by
# the script. If 'local', the script will run 'localcmd' on the host.
# If 'remote', the script will run 'remotessh remotehost remotecmd' on
# the local host which should normally use ssh to connect to another
# host and run the browser command there.
#
# localcmd
# The command to run on the local host to launch URLs in 'local' mode.
# The string '%s' will be replaced with the URL. The default is
# 'firefox %s'.
#
# remotessh
# The command (and arguments) used to connect to the remote host for
# 'remote' mode. The default is 'ssh -x' which will connect as the
# current username via ssh and disable X11 forwarding.
#
# remotehost
# The remote host to which we will connect in 'remote' mode. For ssh,
# this can just be a hostname or 'user@host' to specify a username
# other than your current login name. The default is 'localhost'.
#
# remotecmd
# The command to execute on the remote host for 'remote' mode. The
# default is 'bash -c "DISPLAY=:0.0 firefox '%s'"' Which runs bash, sets
# up the environment to display on the remote host's main X display,
# and runs firefox. As with 'localcmd', the string '%s' will be
# replaced with the URL.
#
# cmdoutput
# The file where the command output (if any) is saved. Overwritten
# each time you launch a new URL. Default is ~/.weechat/urllaunch.log
#
# default
# The command that will be run if no arguemnts to /url are given.
# Default is show
#
# Requirements:
#
# - Designed to run with weechat version 0.3 or better.
# http://www.weechat.org/
#
# Acknowlegements:
#
# - Based on an earlier version called 'urlcollector.py' by 'kolter' of
# irc.freenode.net/#weechat Honestly, I just cleaned up the code a bit and
# made the settings a little more useful (to me).
#
# - With changes by Leonid Evdokimov (weechat at darkk dot net another dot ru):
# http://darkk.net.ru/weechat/urlgrab.py
# v1.1: added better handling of dead zombie-childs
# added parsing of private messages
# added default command setting
# added parsing of scrollback buffers on load
# v1.2: `historysize` was ignored
#
# - With changes by ExclusivE (exclusive_tm at mail dot ru):
# v1.3: X11 clipboard support
#
# - V1.4 Just ported it over to weechat 0.2.7 drubin AT smartcube dot co dot za
# - V1.5 1) I created a logging feature for urls, Time, Date, buffer, and url.
# 2) Added selectable urls support, similar to the iset plugin (Thanks FlashCode)
# 3) Colors/formats are configuarable.
# 4) browser now uses hook_process (Please test with remote clients)
# 5) Added /url open http://url.com functionality
# 6) Changed urls detection to use regexpressions so should be much better
# Thanks to xt of #weechat bassed on on urlbar.py
# - V1.6 FlashCode <flashcode@flashtux.org>: Increase timeout for hook_process
# (from 1 second to 1 minute)
# - V1.7 FlashCode <flashcode@flashtux.org>: Update WeeChat site
# - V1.8 drubin <drubin [at] smartcube . co.za>:
# - Changed remote cmd to be single option
# - Added scrolling on up and down arrow keys for /url show
# - Changed remotecmd to include options with public/private keys password auth doesn't work
# - V1.9 Specimen <spinifer [at] gmail . com>:
# - Changed the default command when /url is run with no arguments to 'show'
# - Removed '/url help' command, because /help <command> is the standard way
# - V2.0 Xilov: replace "/url help" by "/help url"
# - V2.1 nand: Changed default: firefox %s to firefox '%s' (localcmd)
# - V2.2 Sébastien Helleu <flashcode@flashtux.org>: fix reload of config file
# - V2.3 nand: Allowed trailing )s for unmatched (s in URLs
# - V2.4 nand: Escaped URLs via URL-encoding instead of shell escaping, fixes '
# - V2.5 nand: Fixed some URLs that got incorrectly mangled by escaping
# - V2.6 nesthib: Fixed escaping of "="
# Added missing quotes in default parameter (firefox '%s')
# Removed the mix of tabs and spaces in the file indentation
# - V2.7 dobbymoodge <john.w.lamb [at] gmail . com>
# ( https://github.com/dobbymoodge/ ):
# - Added 'copycmd' setting, users can set command to pipe into
# for '/url copy'
# - V2.8 Simmo Saan <simmo.saan@gmail.com>:
# - Changed print hook to ignore filtered lines
# - V2.9 Dominik Heidler <dominik@heidler.eu>:
# - Updated script for python3 support (now python2 and 3 are both supported)
# - V3.0 Sébastien Helleu <flashcode@flashtux.org>:
# - Fix python 3 compatibility (replace "has_key" by "in")
#
# Copyright (C) 2005 David Rubin <drubin AT smartcube dot co dot za>
#
# 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 2
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
# USA.
#
from __future__ import print_function
import sys
import os
try:
import weechat
import_ok = True
except:
print("This script must be run under WeeChat.")
print("Get WeeChat now at: http://www.weechat.org/")
import_ok = False
import subprocess
import time
try:
from urllib import quote
except ImportError:
from urllib.parse import quote
import re
try:
from UserDict import UserDict
except ImportError:
from collections import UserDict
octet = r'(?:2(?:[0-4]\d|5[0-5])|1\d\d|\d{1,2})'
ipAddr = r'%s(?:\.%s){3}' % (octet, octet)
# Base domain regex off RFC 1034 and 1738
label = r'[0-9a-z][-0-9a-z]*[0-9a-z]?'
domain = r'%s(?:\.%s)*\.[a-z][-0-9a-z]*[a-z]?' % (label, label)
urlRe = re.compile(r'(\w+://(?:%s|%s)(?::\d+)?(?:/[^\]>\s]*)?)' % (domain, ipAddr), re.I)
SCRIPT_NAME = "urlgrab"
SCRIPT_AUTHOR = "David Rubin <drubin [At] smartcube [dot] co [dot] za>"
SCRIPT_VERSION = "3.0"
SCRIPT_LICENSE = "GPL"
SCRIPT_DESC = "Url functionality Loggin, opening of browser, selectable links"
CONFIG_FILE_NAME= "urlgrab"
SCRIPT_COMMAND = "url"
def stripParens(url):
return dropChar(')', url.count(')') - url.count('('), url[::-1])[::-1]
def dropChar(c, n, xs):
if n == 0 or xs == []:
return xs
elif xs[0] == c:
return dropChar(c, n-1, xs[1:])
else:
return xs
def urlGrabPrint(message):
bufferd=weechat.current_buffer()
if urlGrabSettings['output_main_buffer'] == 1 :
weechat.prnt("","[%s] %s" % ( SCRIPT_NAME, message ) )
else :
weechat.prnt(bufferd,"[%s] %s" % ( SCRIPT_NAME, message ) )
def hashBufferName(bufferp):
if not weechat.buffer_get_string(bufferp, "short_name"):
bufferd = weechat.buffer_get_string(bufferp, "name")
else:
bufferd = weechat.buffer_get_string(bufferp, "short_name")
return bufferd
def ug_config_reload_cb(data, config_file):
""" Reload configuration file. """
return weechat.config_reload(config_file)
class UrlGrabSettings(UserDict):
def __init__(self):
UserDict.__init__(self)
self.data = {}
self.config_file = weechat.config_new(CONFIG_FILE_NAME,
"ug_config_reload_cb", "")
if not self.config_file:
return
section_color = weechat.config_new_section(
self.config_file, "color", 0, 0, "", "", "", "", "", "",
"", "", "", "")
section_default = weechat.config_new_section(
self.config_file, "default", 0, 0, "", "", "", "", "", "",
"", "", "", "")
self.data['color_buffer']=weechat.config_new_option(
self.config_file, section_color,
"color_buffer", "color", "Color to display buffer name", "", 0, 0,
"red", "red", 0, "", "", "", "", "", "")
self.data['color_url']=weechat.config_new_option(
self.config_file, section_color,
"color_url", "color", "Color to display urls", "", 0, 0,
"blue", "blue", 0, "", "", "", "", "", "")
self.data['color_time']=weechat.config_new_option(
self.config_file, section_color,
"color_time", "color", "Color to display time", "", 0, 0,
"cyan", "cyan", 0, "", "", "", "", "", "")
self.data['color_buffer_selected']=weechat.config_new_option(
self.config_file, section_color,
"color_buffer_selected", "color",
"Color to display buffer selected name", "", 0, 0, "red", "red",
0, "", "", "", "", "", "")
self.data['color_url_selected']=weechat.config_new_option(
self.config_file, section_color,
"color_url_selected", "color", "Color to display url selected",
"", 0, 0, "blue", "blue", 0, "", "", "", "", "", "")
self.data['color_time_selected']=weechat.config_new_option(
self.config_file, section_color,
"color_time_selected", "color", "Color to display tim selected",
"", 0, 0, "cyan", "cyan", 0, "", "", "", "", "", "")
self.data['color_bg_selected']=weechat.config_new_option(
self.config_file, section_color,
"color_bg_selected", "color", "Background for selected row", "", 0, 0,
"green", "green", 0, "", "", "", "", "", "")
self.data['historysize']=weechat.config_new_option(
self.config_file, section_default,
"historysize", "integer", "Max number of urls to store per buffer",
"", 0, 999, "10", "10", 0, "", "", "", "", "", "")
self.data['method']=weechat.config_new_option(
self.config_file, section_default,
"method", "string", """Where to launch URLs
If 'local', runs %localcmd%.
If 'remote' runs the following command:
'%remodecmd%'""", "", 0, 0,
"local", "local", 0, "", "", "", "", "", "")
self.data['copycmd']=weechat.config_new_option(
self.config_file, section_default,
"copycmd", "string",
"Command to pipe into for 'url copy'. "
"E.g. to copy into the CLIPBOARD buffer "
"instead of PRIMARY, you can use 'xsel -b "
"-i' here.", "", 0, 0,
"xsel -i", "xsel -i", 0, "", "", "", "", "", "")
self.data['localcmd']=weechat.config_new_option(
self.config_file, section_default,
"localcmd", "string", """Local command to execute""", "", 0, 0,
"firefox '%s'", "firefox '%s'", 0, "", "", "", "", "", "")
remotecmd="ssh -x localhost -i ~/.ssh/id_rsa -C \"export DISPLAY=\":0.0\" && firefox '%s'\""
self.data['remotecmd']=weechat.config_new_option(
self.config_file, section_default,
"remotecmd", "string", remotecmd, "", 0, 0,
remotecmd, remotecmd, 0, "", "", "", "", "", "")
self.data['url_log']=weechat.config_new_option(
self.config_file, section_default,
"url_log", "string", """log location""", "", 0, 0,
"~/.weechat/urls.log", "~/.weechat/urls.log", 0, "", "", "", "", "", "")
self.data['time_format']=weechat.config_new_option(
self.config_file, section_default,
"time_format", "string", """TIme format""", "", 0, 0,
"%H:%M:%S", "%H:%M:%S", 0, "", "", "", "", "", "")
self.data['output_main_buffer']=weechat.config_new_option(
self.config_file, section_default,
"output_main_buffer", "boolean",
"""Print text to main buffer or current one""", "", 0, 0, "1", "1",
0, "", "", "", "", "", "")
weechat.config_read(self.config_file)
def __getitem__(self, key):
if key == "historysize":
return weechat.config_integer(self.data[key])
elif key == 'output_main_buffer':
return weechat.config_boolean(self.data[key])
#elif key.startswith('color'):
# return weechat.config_color(self.data[key])
else:
return weechat.config_string(self.data[key])
def prnt(self, name, verbose = True):
weechat.prnt( ""," %s = %s" % (name.ljust(11), self.data[name]) )
def prntall(self):
for key in self.names():
self.prnt(key, verbose = False)
def createCmd(self, url):
str =""
if self['method'] == 'remote':
str = self['remotecmd'] % url
else:
str = self['localcmd'] % url
return str
class UrlGrabber:
def __init__(self, historysize):
# init
self.urls = {}
self.globalUrls = []
self.historysize = 5
# control
self.setHistorysize(historysize)
def setHistorysize(self, count):
if count > 1:
self.historysize = count
def getHistorysize(self):
return self.historysize
def addUrl(self, bufferp,url ):
global urlGrabSettings
self.globalUrls.insert(0,{"buffer":bufferp,
"url":url, "time":time.strftime(urlGrabSettings["time_format"])})
#Log urls only if we have set a log path.
if urlGrabSettings['url_log']:
try :
index = self.globalUrls[0]
logfile = os.path.expanduser(urlGrabSettings['url_log'])
dout = open(logfile, "a")
dout.write("%s %s %s\n" % (index['time'],
index['buffer'], index['url']))
dout.close()
except :
print("failed to log url check that %s is valid path" % urlGrabSettings['url_log'])
pass
# check for buffer
if not bufferp in self.urls:
self.urls[bufferp] = []
# add url
if url in self.urls[bufferp]:
self.urls[bufferp].remove(url)
self.urls[bufferp].insert(0, url)
# removing old urls
while len(self.urls[bufferp]) > self.historysize:
self.urls[bufferp].pop()
def hasIndex( self, bufferp, index ):
return bufferp in self.urls and \
len(self.url[bufferp]) >= index
def hasBuffer( self, bufferp ):
return bufferp in self.urls
def getUrl(self, bufferp, index):
url = ""
if bufferp in self.urls:
if len(self.urls[bufferp]) >= index:
url = self.urls[bufferp][index-1]
return url
def prnt(self, buff):
found = True
if buff in self.urls:
if len(self.urls[buff]) > 0:
i = 1
for url in self.urls[buff]:
urlGrabPrint("--> " + str(i) + " : " + url)
i += 1
else:
found = False
elif buff == "*":
for b in self.urls.keys():
self.prnt(b)
else:
found = False
if not found:
urlGrabPrint(buff + ": no entries")
def urlGrabCheckMsgline(bufferp, message, isdisplayed):
global urlGrab, max_buffer_length
if not message or isdisplayed == 0:
return
# Ignore output from 'tinyurl.py' and our selfs
if ( message.startswith( "[AKA] http://tinyurl.com" ) or
message.startswith("[urlgrab]") ):
return
# Check for URLs
for url in urlRe.findall(message):
urlGrab.addUrl(bufferp,stripParens(url))
if max_buffer_length < len(bufferp):
max_buffer_length = len(bufferp)
if urlgrab_buffer:
refresh()
def urlGrabCheck(data, bufferp, uber_empty, tagsn, isdisplayed, ishilight, prefix, message):
urlGrabCheckMsgline(hashBufferName(bufferp), message, isdisplayed)
return weechat.WEECHAT_RC_OK
def urlGrabCopy(bufferd, index):
global urlGrab
if bufferd == "":
urlGrabPrint( "No current channel, you must activate one" )
elif not urlGrab.hasBuffer(bufferd):
urlGrabPrint("No URL found - Invalid channel")
else:
if index <= 0:
urlGrabPrint("No URL found - Invalid index")
return
url = urlGrab.getUrl(bufferd,index)
if url == "":
urlGrabPrint("No URL found - Invalid index")
else:
try:
pipe = os.popen(urlGrabSettings['copycmd'],"w")
pipe.write(url)
pipe.close()
urlGrabPrint("Url: %s gone to clipboard." % url)
except:
urlGrabPrint("Url: %s failed to copy to clipboard." % url)
def urlGrabOpenUrl(url):
global urlGrab, urlGrabSettings
argl = urlGrabSettings.createCmd( quote(url, '/:#%?&+=') )
weechat.hook_process(argl,60000, "ug_open_cb", "")
def ug_open_cb(data, command, code, out, err):
#print out
#print err
return weechat.WEECHAT_RC_OK
def urlGrabOpen(bufferd, index):
global urlGrab, urlGrabSettings
if bufferd == "":
urlGrabPrint( "No current channel, you must specify one" )
elif not urlGrab.hasBuffer(bufferd) :
urlGrabPrint("No URL found - Invalid channel")
else:
if index <= 0:
urlGrabPrint("No URL found - Invalid index")
return
url = urlGrab.getUrl(bufferd,index)
if url == "":
urlGrabPrint("No URL found - Invalid index")
else:
urlGrabPrint("loading %s %sly" % (url, urlGrabSettings["method"]))
urlGrabOpenUrl (url)
def urlGrabList( args ):
global urlGrab
if len(args) == 0:
buf = hashBufferName(weechat.current_buffer())
else:
buf = args[0]
if buf == "" or buf == "all":
buf = "*"
urlGrab.prnt(buf)
def urlGrabMain(data, bufferp, args):
if args[0:2] == "**":
keyEvent(data, bufferp, args[2:])
return weechat.WEECHAT_RC_OK
bufferd = hashBufferName(bufferp)
largs = args.split(" ")
#strip spaces
while '' in largs:
largs.remove('')
while ' ' in largs:
largs.remove(' ')
if len(largs) == 0 or largs[0] == "show":
if not urlgrab_buffer:
init()
refresh()
weechat.buffer_set(urlgrab_buffer, "display", "1")
elif largs[0] == 'open' and len(largs) == 2:
urlGrabOpenUrl(largs[1])
elif largs[0] == 'list':
urlGrabList( largs[1:] )
elif largs[0] == 'copy':
if len(largs) > 1:
no = int(largs[1])
urlGrabCopy(bufferd, no)
else:
urlGrabCopy(bufferd,1)
else:
try:
no = int(largs[0])
if len(largs) > 1:
urlGrabOpen(largs[1], no)
else:
urlGrabOpen(bufferd, no)
except ValueError:
#not a valid number so try opening it as a url..
for url in urlRe.findall(largs[0]):
urlGrabOpenUrl(stripParens(url))
urlGrabPrint( "Unknown command '%s'. Try '/help url' for usage" % largs[0])
return weechat.WEECHAT_RC_OK
def buffer_input(*kwargs):
return weechat.WEECHAT_RC_OK
def buffer_close(*kwargs):
global urlgrab_buffer
urlgrab_buffer = None
return weechat.WEECHAT_RC_OK
def keyEvent (data, bufferp, args):
global urlGrab , urlGrabSettings, urlgrab_buffer, current_line
if args == "refresh":
refresh()
elif args == "up":
if current_line > 0:
current_line = current_line -1
refresh_line (current_line + 1)
refresh_line (current_line)
ugCheckLineOutsideWindow()
elif args == "down":
if current_line < len(urlGrab.globalUrls) - 1:
current_line = current_line +1
refresh_line (current_line - 1)
refresh_line (current_line)
ugCheckLineOutsideWindow()
elif args == "scroll_top":
temp_current = current_line
current_line = 0
refresh_line (temp_current)
refresh_line (current_line)
weechat.command(urlgrab_buffer, "/window scroll_top")
pass
elif args == "scroll_bottom":
temp_current = current_line
current_line = len(urlGrab.globalUrls)
refresh_line (temp_current)
refresh_line (current_line)
weechat.command(urlgrab_buffer, "/window scroll_bottom")
elif args == "enter":
if urlGrab.globalUrls[current_line]:
urlGrabOpenUrl (urlGrab.globalUrls[current_line]['url'])
def refresh_line (y):
global urlGrab , urlGrabSettings, urlgrab_buffer, current_line, max_buffer_length
#Print format Time(space)buffer(max4 spaces, but lined up)url
format = "%%s%%s %%s%%-%ds%%s%%s" % (max_buffer_length+4)
#non selected colors
color_buffer = urlGrabSettings["color_buffer"]
color_url = urlGrabSettings["color_url"]
color_time =urlGrabSettings["color_time"]
#selected colors
color_buffer_selected = urlGrabSettings["color_buffer_selected"]
color_url_selected = urlGrabSettings["color_url_selected"]
color_time_selected = urlGrabSettings["color_time_selected"]
color_bg_selected = urlGrabSettings["color_bg_selected"]
color1 = color_time
color2 = color_buffer
color3 = color_url
#If this line is selected we change the colors.
if y == current_line:
color1 = "%s,%s" % (color_time_selected, color_bg_selected)
color2 = "%s,%s" % (color_buffer_selected, color_bg_selected)
color3 = "%s,%s" % (color_url_selected, color_bg_selected)
color1 = weechat.color(color1)
color2 = weechat.color(color2)
color3 = weechat.color(color3)
text = format % (color1,
urlGrab.globalUrls[y]['time'],
color2,
urlGrab.globalUrls[y]['buffer'],
color3,
urlGrab.globalUrls[y]['url'] )
weechat.prnt_y(urlgrab_buffer,y,text)
def ugCheckLineOutsideWindow():
global urlGrab , urlGrabSettings, urlgrab_buffer, current_line, max_buffer_length
if (urlgrab_buffer):
infolist = weechat.infolist_get("window", "", "current")
if (weechat.infolist_next(infolist)):
start_line_y = weechat.infolist_integer(infolist, "start_line_y")
chat_height = weechat.infolist_integer(infolist, "chat_height")
if(start_line_y > current_line):
weechat.command(urlgrab_buffer, "/window scroll -%i" %(start_line_y - current_line))
elif(start_line_y <= current_line - chat_height):
weechat.command(urlgrab_buffer, "/window scroll +%i"%(current_line - start_line_y - chat_height + 1))
weechat.infolist_free(infolist)
def refresh():
global urlGrab
y=0
for x in urlGrab.globalUrls:
refresh_line (y)
y += 1
def init():
global urlGrab , urlGrabSettings, urlgrab_buffer
if not urlgrab_buffer:
urlgrab_buffer = weechat.buffer_new("urlgrab", "buffer_input", "", "buffer_close", "")
if urlgrab_buffer:
weechat.buffer_set(urlgrab_buffer, "type", "free")
weechat.buffer_set(urlgrab_buffer, "key_bind_ctrl-R", "/url **refresh")
weechat.buffer_set(urlgrab_buffer, "key_bind_meta2-A", "/url **up")
weechat.buffer_set(urlgrab_buffer, "key_bind_meta2-B", "/url **down")
weechat.buffer_set(urlgrab_buffer, "key_bind_meta-ctrl-J", "/url **enter")
weechat.buffer_set(urlgrab_buffer, "key_bind_meta-ctrl-M", "/url **enter")
weechat.buffer_set(urlgrab_buffer, "key_bind_meta-meta2-1./~", "/url **scroll_top")
weechat.buffer_set(urlgrab_buffer, "key_bind_meta-meta2-4~", "/url **scroll_bottom")
weechat.buffer_set(urlgrab_buffer, "title","Lists the urls in the applications")
weechat.buffer_set(urlgrab_buffer, "display", "1")
def completion_urls_cb(data, completion_item, bufferp, completion):
""" Complete with URLS, for command '/url'. """
global urlGrab
bufferd = hashBufferName( bufferp)
for url in urlGrab.globalUrls :
if url['buffer'] == bufferd:
weechat.hook_completion_list_add(completion, url['url'], 0, weechat.WEECHAT_LIST_POS_SORT)
return weechat.WEECHAT_RC_OK
def ug_unload_script():
""" Function called when script is unloaded. """
global urlGrabSettings
weechat.config_write(urlGrabSettings.config_file)
return weechat.WEECHAT_RC_OK
#Main stuff
if ( import_ok and
weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION,
SCRIPT_LICENSE,SCRIPT_DESC, "ug_unload_script", "") ):
urlgrab_buffer = None
current_line = 0
max_buffer_length = 0
urlGrabSettings = UrlGrabSettings()
urlGrab = UrlGrabber( urlGrabSettings['historysize'])
weechat.hook_print("", "", "", 1, "urlGrabCheck", "")
weechat.hook_command(SCRIPT_COMMAND,
"Url Grabber",
"[open <url> | <url> | show | copy [n] | [n] | list]",
"open or <url>: opens the url\n"
"show: Opens the select buffer to allow for url selection\n"
"copy: Copies the nth url to the system clipboard\n"
"list: Lists the urls in the current buffer\n",
"open %(urlgrab_urls) || %(urlgrab_urls) || "
"copy || show || list",
"urlGrabMain", "")
weechat.hook_completion("urlgrab_urls", "list of URLs",
"completion_urls_cb", "")
else:
print("failed to load weechat")

View File

@@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
#
# Script Name: Zerotab.py
# Script Author: Lucian Adamson <lucian.adamson@yahoo.com>
# Script License: GPL
# Alternate Contact: Freenode IRC nick i686
#
# 2013-01-27, Nils Görs <weechatter@arcor.de>:
# version 1.5: make script compatible with Python 3.x
# 2011-09-20, Nils Görs <weechatter@arcor.de>:
# version 1.4: fixed: latest nick from join/part messages were used.
# 2010-12-04, Sebastien Helleu <flashcode@flashtux.org>:
# version 1.3: use tag "nick_xxx" (WeeChat >= 0.3.4 only)
# 2010-08-03, Sebastien Helleu <flashcode@flashtux.org>:
# version 1.2: fix bug with nick prefixes (op/halfop/..)
# 2010-08-03, Sebastien Helleu <flashcode@flashtux.org>:
# version 1.1: fix bug with self nick
SCRIPT_NAME='zerotab'
SCRIPT_AUTHOR='Lucian Adamson <lucian.adamson@yahoo.com>'
SCRIPT_VERSION='1.5'
SCRIPT_LICENSE='GPL'
SCRIPT_DESC='Will tab complete the last nick in channel without typing anything first. This is good for rapid conversations.'
import_ok=True
try:
import weechat, re
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
latest_speaker={}
weechat_version=0
def my_completer(data, buffer, command):
global latest_speaker
str_input = weechat.buffer_get_string(weechat.current_buffer(), "input")
if command == "/input complete_next" and str_input == '':
nick = latest_speaker.get(buffer, "")
if nick != "":
weechat.command(buffer, "/input insert " + nick)
return weechat.WEECHAT_RC_OK
def hook_print_cb(data, buffer, date, tags, displayed, highlight, prefix, message):
global latest_speaker
alltags = tags.split(',')
if 'notify_message' in alltags:
nick = None
if int(weechat_version) >= 0x00030400:
# in version >= 0.3.4, there is a tag "nick_xxx" for each message
for tag in alltags:
if tag.startswith('nick_'):
nick = tag[5:]
break
else:
# in older versions, no tag, so extract nick from printed message
# this is working, except for irc actions (/me ...)
nick = prefix
if re.match('^[@%+~*&!-]', nick):
nick = nick[1:]
if nick:
local_nick = weechat.buffer_get_string(buffer, "localvar_nick")
if nick != local_nick:
latest_speaker[buffer] = nick
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_print("", "", "", 1, "hook_print_cb", "")
weechat.hook_command_run('/input complete*', 'my_completer', '')