# -*- coding: utf-8 -*- # # Copyright (c) 2020 by Ferus Castor # # 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 . # # Select a buffer from dmenu, rofi, or fzf-tmux # Screenshot with fzf-tmux: https://seirdy.one/misc/buffer-dmenu-tmux.png # To call externally (IE: from i3), enable weechat fifo and run: # $ echo "core.weechat */buffer_dmenu" >> $(find ~/.weechat -type p) # # Optionally requires i3-py [py2] (or i3ipc [py3]) to focus weechat in i3 # # History: # 2021-03-19, Seirdy # version 0.2.1: add support for fzf-tmux # 2020-06-08, Ferus # version 0.2: drop support of py2 and fix error when closing # dmenu/rofi with no choice selected # 2020-02024, Seirdy # version 0.1.2: py3-ok # 2017-05-03, Ferus # version 0.1.1: fix argument error for config_set_plugin # 2016-05-01, Ferus # version 0.1: initial release - requires WeeChat ≥ 0.3.7 # TODO: # Option to remove certain buffer types # Implement `focus` for other window managers # if buffer == currentbuffer: switch to previous buffer # pylint: disable=I0011,W0603,W1401 SCRIPT_NAME = "buffer_dmenu" SCRIPT_AUTHOR = "Ferus " SCRIPT_VERSION = "0.2.1" SCRIPT_LICENSE = "GPL3" SCRIPT_DESC = ( "List buffers in dmenu (or rofi/fzf-tmux), changes active window to selected buffer" ) SCRIPT_COMMAND = "buffer_dmenu" import os import subprocess try: import weechat as w except ImportError as e: print("This script must be run under WeeChat.") exit(1) try: import i3ipc as i3 have_i3 = True except ImportError as e: have_i3 = False settings = { "launcher": ("dmenu", "launcher to use (supported: dmenu, rofi, fzf_tmux)"), "focus": ( "false", "whether to immediately focus the terminal after selecting buffer", ), "focus.wm": ("i3", "wm focus logic to use (supported: i3)"), "dmenu.command": ("dmenu -b -i -l 20", "command used to call dmenu"), "rofi.command": ( "rofi -p '# ' -dmenu -lines 10 -columns 8 -auto-select -mesg 'Pick a buffer to jump to:'", "command used to call rofi", ), "fzf_tmux.command": ( "sed -e \"s/b'//\" -e s#\\\\n#\\n#g | fzf-tmux -w 40 -h 70%", "command used to call fzf-tmux", ), "title.regex": ("WeeChat \d+\.\d+", "regex used to match weechat's title window"), } def check_dmenu(): devnull = open(os.devnull) retcode = subprocess.call(["which", "dmenu"], stdout=devnull, stderr=devnull) return True if retcode == 0 else False def check_rofi(): devnull = open(os.devnull) retcode = subprocess.call(["which", "rofi"], stdout=devnull, stderr=devnull) return True if retcode == 0 else False def check_fzf_tmux(): devnull = open(os.devnull) retcode = subprocess.call(["which", "fzf-tmux"], stdout=devnull, stderr=devnull) return True if retcode == 0 else False def get_launcher(): launcher = w.config_get_plugin("launcher") command = None if launcher == "dmenu": if check_dmenu(): command = w.config_get_plugin("dmenu.command") elif launcher == "rofi": if check_rofi(): command = w.config_get_plugin("rofi.command") elif launcher == "fzf_tmux": if check_fzf_tmux(): command = w.config_get_plugin("fzf_tmux.command") return command def launch(options): launcher = get_launcher() if launcher: call(launcher, options) return True def focus(): if w.config_string_to_boolean(w.config_get_plugin("focus")): if w.config_get_plugin("focus.wm") == "i3": focus_i3() def focus_i3(): if have_i3: regex = w.config_get_plugin("title.regex") i3conn = i3.Connection() weechat = i3conn.get_tree().find_named(regex)[0] weechat.command("focus") def call(command, options): options = "\n".join(options) w.hook_process_hashtable( "sh", {"arg1": "-c", "arg2": 'echo "{0}" | {1}'.format(options, command)}, 10 * 1000, "launch_process_cb", "", ) process_output = "" def launch_process_cb(data, command, rc, out, err): global process_output if out == "" or ":" not in out: return w.WEECHAT_RC_ERROR process_output += out if int(rc) >= 0: selected = process_output.strip("\n") _, name = selected.split(":") process_output = "" switch_to_buffer(name) focus() return w.WEECHAT_RC_OK def get_open_buffers(): buffers = [] infolist = w.infolist_get("buffer", "", "") if infolist: while w.infolist_next(infolist): name = w.infolist_string(infolist, "name") number = w.infolist_integer(infolist, "number") _ = "{0}:{1}".format(number, name) buffers.append(_) w.infolist_free(infolist) return buffers def get_hotlist_buffers(): buffers = [] infolist = w.infolist_get("hotlist", "", "") if infolist: while w.infolist_next(infolist): number = w.infolist_integer(infolist, "buffer_number") buffer = w.infolist_pointer(infolist, "buffer_pointer") name = w.buffer_get_string(buffer, "name") _ = "{0}:{1}".format(number, name) buffers.append(_) w.infolist_free(infolist) return buffers def switch_to_buffer(buffer_name): w.command("", "/buffer {0}".format(buffer_name)) def dmenu_cmd_cb(data, buffer, args): """ Command /buffers_dmenu """ if args == "hotlist": buffers = get_hotlist_buffers() else: buffers = get_open_buffers() if not launch(buffers): return w.WEECHAT_RC_ERROR return w.WEECHAT_RC_OK if w.register( SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, "", "" ): version = w.info_get("version_number", "") or 0 for option, value in settings.items(): if not w.config_is_set_plugin(option): w.config_set_plugin(option, value[0]) if int(version) >= 0x00030500: w.config_set_desc_plugin( option, '{0} (default: "{1}")'.format(value[1], value[0]) ) w.hook_command( SCRIPT_COMMAND, "Show a list of all buffers in dmenu", "[hotlist]", " hotlist: shows hotlist buffers only\n" "\n" "To call externally (IE: from i3), enable weechat fifo and run:\n" " $ echo 'core.weechat */buffer_dmenu' >> $(find ~/.weechat -type p)\n" "\n" "To focus the terminal containing WeeChat for the following WM:\n" " i3: requires i3ipc from pip3\n", "", "dmenu_cmd_cb", "", )