#!/usr/bin/env python3 from dataclasses import dataclass from typing import List, Dict from subprocess import run, PIPE def strip_comments(line: str): if (line[0] == ';'): return "" elif (comment_start := line.find("#")) >= 0: line = line[:comment_start] return line.rstrip() def parse_keybind(lines: List[str]): keybind = lines[0] commands = [] end = len(lines) - 1 for i, line in enumerate(lines[1:]): if line.startswith(" "): commands.append(line) else: end = i break end += 1 return keybind, commands, lines[end:] def parse_all_keybinds(lines: list[str]) -> Dict[str, list[str]]: binds = dict() while len(lines) != 0: binding, commands, lines = parse_keybind(lines) binds[binding] = commands return binds def get_config_data(filepath: str): with open(filepath, "r") as fp: lines = fp.readlines() lines = map(strip_comments, lines) lines = filter(lambda x : x != "", lines) bindings = parse_all_keybinds(list(lines)) return bindings def dmenu(prompt: str, items: list) -> str: return run(["dmenu", "-p", "Choose binding: "], input="\n".join(items), capture_output=True, text=True).stdout.strip() def get_keybind(keybinds: Dict[str, list[str]]) -> str: return dmenu("Choose binding: ", list(keybinds.keys())).replace("\n", "") def get_command(keybinds: Dict[str, list[str]]) -> str: reverse_map = dict() for key in keybinds: value = keybinds[key] if reverse_map.get(value) is not None: continue reverse_map[value] = key return dmenu("Choose binding: ", list(reverse_map.keys())).replace("\n", "") bindings = get_config_data("/home/oreo/.config/sxhkd/sxhkdrc") choice = get_command(bindings) print(choice, bindings[choice])