diff --git a/.iterm2/imgcat b/.iterm2/imgcat new file mode 100755 index 0000000..15acf4d --- /dev/null +++ b/.iterm2/imgcat @@ -0,0 +1,157 @@ +#!/bin/bash + +# tmux requires unrecognized OSC sequences to be wrapped with DCS tmux; +# ST, and for all ESCs in to be replaced with ESC ESC. It +# only accepts ESC backslash for ST. We use TERM instead of TMUX because TERM +# gets passed through ssh. +function print_osc() { + if [[ $TERM == screen* ]]; then + printf "\033Ptmux;\033\033]" + else + printf "\033]" + fi +} + +# More of the tmux workaround described above. +function print_st() { + if [[ $TERM == screen* ]]; then + printf "\a\033\\" + else + printf "\a" + fi +} + +function load_version() { + if [ -z ${IMGCAT_BASE64_VERSION+x} ]; then + IMGCAT_BASE64_VERSION=$(base64 --version 2>&1) + export IMGCAT_BASE64_VERSION + fi +} + +function b64_encode() { + load_version + if [[ $IMGCAT_BASE64_VERSION =~ GNU ]]; then + # Disable line wrap + base64 -w0 + else + base64 + fi +} + +function b64_decode() { + load_version + if [[ $IMGCAT_BASE64_VERSION =~ fourmilab ]]; then + BASE64ARG=-d + elif [[ $IMGCAT_BASE64_VERSION =~ GNU ]]; then + BASE64ARG=-di + else + BASE64ARG=-D + fi + base64 $BASE64ARG +} + +# print_image filename inline base64contents print_filename +# filename: Filename to convey to client +# inline: 0 or 1 +# base64contents: Base64-encoded contents +# print_filename: If non-empty, print the filename +# before outputting the image +function print_image() { + print_osc + printf '1337;File=' + if [[ -n $1 ]]; then + printf "name=%s;" "$(printf "%s" "$1" | b64_encode)" + fi + + printf "%s" "$3" | b64_decode | wc -c | awk '{printf "size=%d",$1}' + printf ";inline=%s" "$2" + printf ":" + printf "%s" "$3" + print_st + printf '\n' + if [[ -n $4 ]]; then + echo "$1" + fi +} + +function error() { + echo "ERROR: $*" 1>&2 +} + +function show_help() { + echo "Usage: imgcat [-p] filename ..." 1>&2 + echo " or: cat filename | imgcat" 1>&2 +} + +function check_dependency() { + if ! (builtin command -V "$1" >/dev/null 2>&1); then + echo "imgcat: missing dependency: can't find $1" 1>&2 + exit 1 + fi +} + +## Main + +if [ -t 0 ]; then + has_stdin=f +else + has_stdin=t +fi + +# Show help if no arguments and no stdin. +if [ $has_stdin = f ] && [ $# -eq 0 ]; then + show_help + exit +fi + +check_dependency awk +check_dependency base64 +check_dependency wc + +# Look for command line flags. +while [ $# -gt 0 ]; do + case "$1" in + -h | --h | --help) + show_help + exit + ;; + -p | --p | --print) + print_filename=1 + ;; + -u | --u | --url) + check_dependency curl + encoded_image=$(curl -s "$2" | b64_encode) || ( + error "No such file or url $2" + exit 2 + ) + has_stdin=f + print_image "$2" 1 "$encoded_image" "$print_filename" + set -- "${@:1:1}" "-u" "${@:3}" + if [ "$#" -eq 2 ]; then + exit + fi + ;; + -*) + error "Unknown option flag: $1" + show_help + exit 1 + ;; + *) + if [ -r "$1" ]; then + has_stdin=f + print_image "$1" 1 "$(b64_encode <"$1")" "$print_filename" + else + error "imgcat: $1: No such file or directory" + exit 2 + fi + ;; + esac + shift +done + +# Read and print stdin +if [ $has_stdin = t ]; then + print_image "" 1 "$(cat | b64_encode)" "" +fi + +exit 0 diff --git a/.iterm2/imgls b/.iterm2/imgls new file mode 100755 index 0000000..bf424b2 --- /dev/null +++ b/.iterm2/imgls @@ -0,0 +1,80 @@ +#!/bin/bash +# tmux requires unrecognized OSC sequences to be wrapped with DCS tmux; +# ST, and for all ESCs in to be replaced with ESC ESC. It +# only accepts ESC backslash for ST. +function print_osc() { + if [ x"$TERM" = "xscreen" ] ; then + printf "\033Ptmux;\033\033]" + else + printf "\033]" + fi +} + +function check_dependency() { + if ! (builtin command -V "$1" > /dev/null 2>& 1); then + echo "imgcat: missing dependency: can't find $1" 1>& 2 + exit 1 + fi +} + +# More of the tmux workaround described above. +function print_st() { + if [ x"$TERM" = "xscreen" ] ; then + printf "\a\033\\" + else + printf "\a" + fi +} + +function list_file() { + fn=$1 + test -f "$fn" || return 0 + dims=$(php -r 'if (!is_file($argv[1])) exit(1); $a = getimagesize($argv[1]); if ($a==FALSE) exit(1); else { echo $a[0] . "x" .$a[1]; exit(0); }' -- "$fn") + rc=$? + if [[ $rc == 0 ]] ; then + print_osc + printf '1337;File=name='`echo -n "$fn" | base64`";" + wc -c -- "$fn" | awk '{printf "size=%d",$1}' + printf ";inline=1;height=3;width=3;preserveAspectRatio=true" + printf ":" + base64 < "$fn" + print_st + if [ x"$TERM" == "xscreen" ] ; then + # This works in plain-old tmux but does the wrong thing in iTerm2's tmux + # integration mode. tmux doesn't know that the cursor moves when the + # image code is sent, while iTerm2 does. I had to pick one, since + # integration mode is undetectable, so I picked the failure mode that at + # least produces useful output (there is just too much whitespace in + # integration mode). This could be fixed by not moving the cursor while + # in integration mode. A better fix would be for tmux to interpret the + # image sequence, though. + # + # tl;dr: If you use tmux in integration mode, replace this with the printf + # from the else clause. + printf '\033[4C\033[Bx' + else + printf '\033[A' + fi + echo -n "$dims " + ls -ld -- "$fn" + else + ls -ld -- "$fn" + fi +} + +check_dependency php +check_dependency base64 +check_dependency wc + +if [ $# -eq 0 ]; then + for fn in * + do + list_file "$fn" + done < <(ls -ls) +else + for fn in "$@" + do + list_file "$fn" + done +fi + diff --git a/.iterm2/it2api b/.iterm2/it2api new file mode 100755 index 0000000..dff3bd0 --- /dev/null +++ b/.iterm2/it2api @@ -0,0 +1,722 @@ +#!/usr/bin/env python3.7 + +import argparse +import asyncio +import iterm2 +import logging +import re +import sys +import traceback + +async def list_sessions(connection, args): + a = await iterm2.async_get_app(connection) + for w in a.terminal_windows: + for t in w.tabs: + sessions = t.sessions + for s in sessions: + print(s.pretty_str(), end='') + print("") + print("Buried sessions:") + for s in a.buried_sessions: + print(s.pretty_str(), end='') + +async def show_hierarchy(connection, args): + a = await iterm2.async_get_app(connection) + print(a.pretty_str()) + +async def send_text(connection, args): + a = await iterm2.async_get_app(connection) + s = a.get_session_by_id(args.session) + await s.async_send_text(args.text) + +async def create_tab(connection, args): + a = await iterm2.async_get_app(connection) + if args.window is not None: + window_id = args.window + try: + window = next(window for window in a.terminal_windows if window.window_id == window_id) + tab = await window.async_create_tab(profile=args.profile, command=args.command, index=args.index) + except: + print("bad window id {}".format(window_id)) + sys.exit(1) + else: + window = await iterm2.Window.async_create(connection, profile=args.profile, command=args.command) + if not window: + return + tab = window.tabs[0] + session = tab.sessions[0] + print(session.pretty_str()) + +async def split_pane(connection, args): + a = await iterm2.async_get_app(connection) + s = a.get_session_by_id(args.session) + session = await s.async_split_pane(vertical=args.vertical, before=args.before, profile=args.profile) + print(session.pretty_str()) + +async def get_buffer(connection, args): + a = await iterm2.async_get_app(connection) + s = a.get_session_by_id(args.session) + contents = await s.async_get_screen_contents() + for i in range(contents.number_of_lines): + line = contents.line(i) + print(line.string) + +async def get_prompt(connection, args): + a = await iterm2.async_get_app(connection) + s = a.get_session_by_id(args.session) + result = await iterm2.async_get_last_prompt(connection, s.session_id) + print("working_directory: \"{}\"".format(result.working_directory)) + print("command: \"{}\"".format(result.command)) + +def profile_property_type_map(): + map = { + "allow_title_reporting": "bool", + "allow_title_setting": "bool", + "ambiguous_double_width": "bool", + "ansi_0_color": "color", + "ansi_10_color": "color", + "ansi_11_color": "color", + "ansi_12_color": "color", + "ansi_13_color": "color", + "ansi_14_color": "color", + "ansi_15_color": "color", + "ansi_1_color": "color", + "ansi_2_color": "color", + "ansi_3_color": "color", + "ansi_4_color": "color", + "ansi_5_color": "color", + "ansi_6_color": "color", + "ansi_7_color": "color", + "ansi_8_color": "color", + "ansi_9_color": "color", + "answerback_string": "str", + "application_keypad_allowed": "bool", + "ascii_anti_aliased": "bool", + "ascii_ligatures": "bool", + "background_color": "color", + "background_image_is_tiled": "bool", + "badge_color": "color", + "badge_text": "str", + "blend": "float", + "blink_allowed": "bool", + "blinking_cursor": "bool", + "blur": "float", + "blur_radius": "float", + "bm_growl": "bool", + "bold_color": "color", + "character_encoding": "int", + "close_sessions_on_end": "bool", + "cursor_boost": "float", + "cursor_color": "color", + "cursor_guide_color": "color", + "cursor_text_color": "color", + "cursor_type": "int", + "disable_printing": "bool", + "disable_smcup_rmcup": "bool", + "disable_window_resizing": "bool", + "flashing_bell": "bool", + "foreground_color": "color", + "horizontal_spacing": "float", + "idle_code": "int", + "idle_period": "float", + "link_color": "color", + "minimum_contrast": "float", + "mouse_reporting": "bool", + "mouse_reporting_allow_mouse_wheel": "bool", + "name": "str", + "non_ascii_anti_aliased": "bool", + "non_ascii_ligatures": "bool", + "only_the_default_bg_color_uses_transparency": "bool", + "left_option_key_sends": "int", + "place_prompt_at_first_column": "bool", + "prompt_before_closing": "bool", + "reduce_flicker": "bool", + "right_option_key_sends": "int", + "scrollback_in_alternate_screen": "bool", + "scrollback_lines": "int", + "scrollback_with_status_bar": "bool", + "selected_text_color": "color", + "selection_color": "color", + "send_bell_alert": "bool", + "send_code_when_idle": "bool", + "send_idle_alert": "bool", + "send_new_output_alert": "bool", + "send_session_ended_alert": "bool", + "send_terminal_generated_alerts": "bool", + "session_close_undo_timeout": "float", + "show_mark_indicators": "bool", + "silence_bell": "bool", + "smart_cursor_color": "color", + "smart_cursor_color": "color", + "sync_title": "str", + "tab_color": "color", + "thin_strokes": "int", + "transparency": "float", + "underline_color": "color", + "unicode_normalization": "int", + "unicode_version": "int", + "unlimited_scrollback": "bool", + "use_bold_font": "bool", + "use_bright_bold": "bool", + "use_cursor_guide": "bool", + "use_italic_font": "bool", + "use_non_ascii_font": "bool", + "use_tab_color": "bool", + "use_underline_color": "bool", + "vertical_spacing": "float", + "visual_bell": "bool", + "triggers": "dict", + "smart_selection_rules": "list", + "semantic_history": "dict", + "automatic_profile_switching_rules": "list", + "advanced_working_directory_window_setting": "string", + "advanced_working_directory_window_directory": "string", + "advanced_working_directory_tab_setting": "string", + "advanced_working_directory_tab_directory": "string", + "advanced_working_directory_pane_setting": "string", + "advanced_working_directory_pane_directory": "string", + "normal_font": "string", + "non_ascii_font": "string", + "background_image_location": "string", + "key_mappings": "dict", + "touchbar_mappings": "dict" } + return map + +def profile_properties(): + return list(profile_property_type_map().keys()) + +def profile_property_type(key): + return profile_property_type_map()[key] + +async def get_profile_property(connection, args): + a = await iterm2.async_get_app(connection) + s = a.get_session_by_id(args.session) + profile = await s.async_get_profile() + if args.keys is not None: + keys = args.keys.split(",") + else: + keys = profile_properties() + for prop in keys: + fname = prop + value = getattr(profile, fname) + print("{}: {}".format(prop, value)) + +def encode_property_value(key, value): + type = profile_property_type(key) + if type == "bool": + assert value == "true" or value == "false" + return value == "true" + elif type == "str": + return value + elif type == "float": + return float(value) + elif type == "int": + return int(value) + elif type == "dict" or type == "list": + class TypeNotSupportedException(Exception): Pass + raise TypeNotSupportedException("this property's type is not supported") + elif type == "color": + # Accepted values look like: "(0,0,0,255 sRGB)" + regex = r"\(([0-9]+), *([0-9]+), *([0-9]+), *([0-9]+) *([A-Za-z]+)\)" + match = re.search(regex, value) + assert match is not None + return iterm2.Color( + float(match.group(1)), + float(match.group(2)), + float(match.group(3)), + float(match.group(4)), + iterm2.ColorSpace(match.group(5))) + +async def set_profile_property(connection, args): + a = await iterm2.async_get_app(connection) + s = a.get_session_by_id(args.session) + + encoded_value = encode_property_value(args.key, args.value) + profile = await s.async_get_profile() + fname = "async_set_" + args.key + f = getattr(profile, fname) + await f(encoded_value) + +async def read(connection, args): + a = await iterm2.async_get_app(connection) + s = a.get_session_by_id(args.session) + if args.mode == "char": + async with iterm2.KeystrokeMonitor(connection) as mon: + keystroke = await mon.async_get() + print(keystroke) + elif args.mode == "line": + async with s.get_keystroke_reader() as reader: + eol = False + line = "" + while not eol: + k = await reader.get() + for e in k: + c = e.characters + if c == "\r" or c == "\n": + eol = True + break + line += c + + print(line) + +async def get_window_property(connection, args): + a = await iterm2.async_get_app(connection) + w = a.get_window_by_id(args.id) + if w is None: + print("bad window ID") + else: + if args.name == "frame": + frame = await w.async_get_frame() + print("{},{},{},{}".format(frame.origin.x,frame.origin.y,frame.size.width,frame.size.height)) + elif args.name == "fullscreen": + print(await w.async_get_fullscreen(connection)) + +async def set_window_property(connection, args): + a = await iterm2.async_get_app(connection) + w = a.get_window_by_id(args.id) + if w is None: + print("bad window ID") + else: + if args.name == "frame": + parts = args.value.split(",") + frame = iterm2.Frame(iterm2.Point(int(parts[0]), int(parts[1])), iterm2.Size(int(parts[2]), int(parts[3]))) + await w.async_set_frame(frame) + elif args.name == "fullscreen": + await w.async_set_fullscreen(args.value == "true") + +async def inject(connection, args): + a = await iterm2.async_get_app(connection) + s = a.get_session_by_id(args.session) + if s is None: + print("bad session ID") + else: + await s.async_inject(args.data.encode()) + +async def activate(connection, args): + a = await iterm2.async_get_app(connection) + if args.mode == "session": + s = a.get_session_by_id(args.id) + if s is None: + print("bad session ID") + else: + await s.async_activate() + elif args.mode == "tab": + t = a.get_tab_by_id(args.id) + if t is None: + print("bad tab ID") + else: + await t.async_select() + elif args.mode == "window": + w = a.get_window_by_id(args.id) + if w is None: + print("bad window ID") + else: + await w.async_activate() + +async def activate_app(connection, args): + a = await iterm2.async_get_app(connection) + await a.async_activate(raise_all_windows=args.raise_all_windows, ignoring_other_apps=args.ignoring_other_apps) + +async def set_variable(connection, args): + a = await iterm2.async_get_app(connection) + if args.session: + s = a.get_session_by_id(args.session) + if s is None: + print("bad session ID") + return + await s.async_set_variable(args.name, args.value) + elif args.tab: + t = a.get_tab_by_id(args.tab) + if t is None: + print("bad tab ID") + return + await t.async_set_variable(args.name, args.value) + else: + await a.async_set_variable(args.name, args.value) + +async def get_variable(connection, args): + a = await iterm2.async_get_app(connection) + if args.session: + s = a.get_session_by_id(args.session) + if s is None: + print("bad session ID") + return + value = await s.async_get_variable(args.name) + print(value) + elif args.tab: + t = a.get_tab_by_id(args.tab) + if t is None: + print("bad tab ID") + return + value = await t.async_get_variable(args.name) + print(value) + else: + value = await a.async_get_variable(args.name) + print(value) + +async def list_variables(connection, args): + a = await iterm2.async_get_app(connection) + if args.session: + s = a.get_session_by_id(args.session) + if s is None: + print("bad session ID") + return + value = await s.async_get_variable("*") + for name in value: + print(name) + elif args.tab: + t = a.get_tab_by_id(args.tab) + if t is None: + print("bad tab ID") + return + value = await t.async_get_variable("*") + for name in value: + print(name) + else: + value = await a.async_get_variable("*") + for name in value: + print(name) + +async def saved_arrangement(connection, args): + if args.window is not None: + a = await iterm2.async_get_app(connection) + w = a.get_window_by_id(args.window) + if w is None: + print("bad window ID") + return + if args.action == "save": + await w.async_save_window_as_arrangement(args.name) + elif args.action == "restore": + await w.async_restore_window_arrangement(args.name) + else: + if args.action == "save": + await iterm2.Arrangement.async_save(connection, args.name) + elif args.action == "restore": + await iterm2.Arrangement.async_restore(connection, args.name) + +async def show_focus(connection, args): + a = await iterm2.async_get_app(connection) + if a.app_active: + print("App is active") + w = a.current_terminal_window + print("Key window: {}".format(w.window_id)) + print("") + for w in a.terminal_windows: + t = a.get_tab_by_id(w.selected_tab_id) + print("Selected tab in {}: {}".format(w.window_id, t.tab_id)) + s = a.get_session_by_id(t.active_session_id) + print(" Active session is: {}".format(s.pretty_str())) + +async def list_profiles(connection, args): + guids = args.guids.split(",") if args.guids is not None else None + properties = args.properties.split(",") if args.properties is not None else None + profiles = await iterm2.PartialProfile.async_query(connection, guids=guids, properties=properties) + for profile in profiles: + keys = list(profile.all_properties.keys()) + keys.sort() + for k in keys: + v = profile.all_properties[k] + print("{}: {}".format(k, v)) + print("") + +async def set_grid_size(connection, args): + a = await iterm2.async_get_app(connection) + s = a.get_session_by_id(args.session) + await s.async_set_grid_size(iterm2.Size(args.width, args.height)) + +async def list_tmux_connections(connection, args): + connections = await iterm2.async_get_tmux_connections(connection) + for connection in connections: + print("Connection ID: {}\nOwning session: {}".format(connection.connection_id, connection.owning_session)) + +async def send_tmux_command(connection, args): + connections = await iterm2.async_get_tmux_connections(connection) + ids = [] + for connection in connections: + if connection.connection_id == args.connection_id: + print(await connection.async_send_command(args.command)) + return; + ids.append(connection.connection_id) + print("No connection with id {} found. Have: {}".format(args.connection_id, ", ".join(ids))) + +async def set_tmux_window_visible(connection, args): + connections = await iterm2.async_get_tmux_connections(connection) + ids = [] + for connection in connections: + if connection.connection_id == args.connection_id: + await connection.async_set_tmux_window_visible(args.window_id, args.visible) + return; + ids.append(connection.connection_id) + print("No connection with id {} found. Have: {}".format(args.connection_id, ", ".join(ids))) + +async def sort_tabs(connection, args): + app = await iterm2.async_get_app(connection) + for w in app.terminal_windows: + tabs = w.tabs + for t in tabs: + t.tab_name = await t.async_get_variable("currentSession.session.name") + def tab_name(t): + return t.tab_name + sorted_tabs = sorted(tabs, key=tab_name) + await w.async_set_tabs(sorted_tabs) + +async def list_color_presets(connection, args): + presets = await iterm2.ColorPreset.async_get_list(connection) + for preset in presets: + print(preset) + +async def set_color_preset(connection, args): + preset = await iterm2.ColorPreset.async_get(connection, args.preset) + profiles = await iterm2.PartialProfile.async_query(connection, properties=['Guid', 'Name']) + for partial in profiles: + if partial.name == args.profile: + profile = await partial.async_get_full_profile() + await profile.async_set_color_preset(preset) + +async def monitor_variable(connection, args): + if args.session: + scope = iterm2.VariableScopes.SESSION + identifier = args.session + elif args.tab: + scope = iterm2.VariableScopes.TAB + identifier = args.tab + elif args.window: + scope = iterm2.VariableScopes.WINDOW + identifier = args.window + elif args.app: + scope = iterm2.VariableScopes.APP + identifier = '' + else: + assert False + async with iterm2.VariableMonitor(connection, scope, args.name, identifier) as monitor: + value = await monitor.async_get() + print(f"New value: {value}") + +async def monitor_focus(connection, args): + async with iterm2.FocusMonitor(connection) as monitor: + update = await monitor.async_get_next_update() + print("Update: {}".format(update)) + +async def set_cursor_color(connection, args): + a = await iterm2.async_get_app(connection) + s = a.get_session_by_id(args.session) + partial = iterm2.LocalWriteOnlyProfile() + r, g, b = list(map(int, args.color.split(","))) + c = iterm2.Color(r, g, b) + partial.set_cursor_color(c) + await s.async_set_profile_properties(partial) + +async def monitor_screen(connection, args): + a = await iterm2.async_get_app(connection) + s = a.get_session_by_id(args.session) + async with s.get_screen_streamer() as streamer: + done = False + while not done: + contents = await streamer.async_get() + for i in range(contents.number_of_lines): + line = contents.line(i) + if args.query in line.string: + return + +async def show_selection(connection, args): + a = await iterm2.async_get_app(connection) + s = a.get_session_by_id(args.session) + selection = await s.async_get_selection() + for sub in selection.subSelections: + print("Sub selection: {}".format(await sub.async_get_string(connection, s.session_id))) + print("Text: {}".format(await selection.async_get_string(connection, s.session_id, s.grid_size.width))) + +def make_parser(): + parser = argparse.ArgumentParser(description='iTerm2 CLI') + subparsers = parser.add_subparsers(help='Commands') + + list_sessions_parser = subparsers.add_parser("list-sessions", help="List sessions") + list_sessions_parser.set_defaults(func=list_sessions) + + show_hierarchy_parser = subparsers.add_parser("show-hierarchy", help="Show all windows, tabs, and sessions") + show_hierarchy_parser.set_defaults(func=show_hierarchy) + + send_text_parser = subparsers.add_parser("send-text", help="Send text as though the user had typed it") + send_text_parser.add_argument('session', type=str, help='Session ID') + send_text_parser.add_argument("text", type=str, help='Text to send') + send_text_parser.set_defaults(func=send_text) + + create_tab_parser = subparsers.add_parser("create-tab", help="Create a new tab or window") + create_tab_parser.add_argument('--profile', type=str, nargs='?', help='Profile name') + create_tab_parser.add_argument('--window', type=str, nargs='?', help='Window ID') + create_tab_parser.add_argument('--index', type=int, nargs='?', help='Desired tab index') + create_tab_parser.add_argument('--command', type=str, nargs='?', help='Command') + create_tab_parser.set_defaults(func=create_tab) + + split_pane_parser = subparsers.add_parser("split-pane", help="Split a pane into two") + split_pane_parser.add_argument('session', type=str, help='Session ID') + split_pane_parser.add_argument('--vertical', action='store_true', help='Split vertically?', default=False) + split_pane_parser.add_argument('--before', action='store_true', help='Spilt left or above target', default=False) + split_pane_parser.add_argument('--profile', type=str, nargs='?', help='Profile name') + split_pane_parser.set_defaults(func=split_pane) + + get_buffer_parser = subparsers.add_parser("get-buffer", help="Get screen contents") + get_buffer_parser.add_argument("session", type=str, help="Session ID") + get_buffer_parser.set_defaults(func=get_buffer) + + get_prompt_parser = subparsers.add_parser("get-prompt", help="Get info about prompt, if available. Gives either the current prompt or the last prompt if a command is being run. Requires shell integration for prompt detection.") + get_prompt_parser.add_argument("session", type=str, help="Session ID") + get_prompt_parser.set_defaults(func=get_prompt) + + get_profile_property_parser = subparsers.add_parser("get-profile-property", help="Get a session's profile settings") + get_profile_property_parser.add_argument("session", type=str, help="Session ID") + get_profile_property_parser.add_argument("keys", type=str, nargs='?', help="Comma separated keys. Omit to get all. Valid keys are: " + ", ".join(profile_properties())) + get_profile_property_parser.set_defaults(func=get_profile_property) + + set_profile_parser = subparsers.add_parser("set-profile-property", help="Set a session's profile setting") + set_profile_parser.add_argument("session", type=str, help="Session ID") + set_profile_parser.add_argument("key", type=str, help="Key to set. Valid keys are: " + ", ".join(profile_properties())) + set_profile_parser.add_argument("value", type=str, help="New value.") + set_profile_parser.set_defaults(func=set_profile_property) + + read_parser = subparsers.add_parser("read", help="Wait for a input.") + read_parser.add_argument("session", type=str, help="Session ID") + read_parser.add_argument("mode", type=str, help="What to read", choices=[ "char", "line" ]) + read_parser.set_defaults(func=read) + + get_window_property_parser = subparsers.add_parser("get-window-property", help="Get a property of a window") + get_window_property_parser.add_argument("id", type=str, help="Window ID") + get_window_property_parser.add_argument("name", type=str, help="Property name", choices=["frame", "fullscreen"]) + get_window_property_parser.set_defaults(func=get_window_property) + + set_window_property_parser = subparsers.add_parser("set-window-property", help="Set a property of a window") + set_window_property_parser.add_argument("id", type=str, help="Window ID") + set_window_property_parser.add_argument("name", type=str, help="Property name", choices=["frame", "fullscreen"]) + set_window_property_parser.add_argument("value", type=str, help="New value. For frame: x,y,width,height; for fullscreen: true or false") + set_window_property_parser.set_defaults(func=set_window_property) + + inject_parser = subparsers.add_parser("inject", help="Inject a string as though it were program output") + inject_parser.add_argument("session", type=str, help="Session ID") + inject_parser.add_argument("data", type=str, help="Data to inject") + inject_parser.set_defaults(func=inject) + + activate_parser = subparsers.add_parser("activate", help="Activate a session, tab, or window.") + activate_parser.add_argument("mode", type=str, help="What kind of object to activate", choices=["session", "tab", "window"]) + activate_parser.add_argument("id", type=str, help="ID of object to activate") + activate_parser.set_defaults(func=activate) + + activate_app_parser = subparsers.add_parser("activate-app", help="Activate the app") + activate_app_parser.add_argument('--raise_all_windows', action='store_true', help='Raise all windows?', default=False) + activate_app_parser.add_argument('--ignoring_other_apps', action='store_true', help='Activate ignoring other apps (may steal focus)', default=False) + activate_app_parser.set_defaults(func=activate_app) + + set_variable_parser = subparsers.add_parser("set-variable", help="Set a user-defined variable in a session. See Badges documentation for details.") + set_variable_parser.add_argument("--session", type=str, nargs='?', help="Session ID") + set_variable_parser.add_argument("--tab", type=str, nargs='?', help="Tab ID") + set_variable_parser.add_argument("name", type=str, help="Variable name. Starts with \"user.\"") + set_variable_parser.add_argument("value", type=str, help="New value") + set_variable_parser.set_defaults(func=set_variable) + + get_variable_parser = subparsers.add_parser("get-variable", help="Get a variable in a session. See Badges documentation for details.") + get_variable_parser.add_argument("--session", type=str, nargs='?', help="Session ID") + get_variable_parser.add_argument("--tab", type=str, nargs='?', help="Tab ID") + get_variable_parser.add_argument("name", type=str, help="Variable name. Starts with \"user.\"") + get_variable_parser.set_defaults(func=get_variable) + + list_variables_parser = subparsers.add_parser("list-variables", help="Lists variable names available in a session.") + list_variables_parser.add_argument("--session", type=str, nargs='?', help="Session ID") + list_variables_parser.add_argument("--tab", type=str, nargs='?', help="Tab ID") + list_variables_parser.set_defaults(func=list_variables) + + saved_arrangement_parser = subparsers.add_parser("saved-arrangement", help="Saves and restores window arrangements") + saved_arrangement_parser.add_argument("action", type=str, help="Action to perform", choices=["save", "restore"]) + saved_arrangement_parser.add_argument("name", type=str, help="Arrangement name") + saved_arrangement_parser.add_argument('--window', type=str, nargs='?', help='Window ID to save/restore to') + saved_arrangement_parser.set_defaults(func=saved_arrangement) + + show_focus_parser = subparsers.add_parser("show-focus", help="Show active windows, tabs, and panes") + show_focus_parser.set_defaults(func=show_focus) + + list_profiles_parser = subparsers.add_parser("list-profiles", help="List profiles") + list_profiles_parser.add_argument("--guids", type=str, nargs='?', help="Comma-delimited list of profiles to list. Omit to get all of them.") + list_profiles_parser.add_argument("--properties", type=str, nargs='?', help="Comma-delimited list of properties to request. Omit to get all of them.") + list_profiles_parser.set_defaults(func=list_profiles) + + set_grid_size_parser = subparsers.add_parser("set-grid-size", help="Set size of session") + set_grid_size_parser.add_argument("session", type=str, help="Session ID") + set_grid_size_parser.add_argument("width", type=int, help="Width in columns") + set_grid_size_parser.add_argument("height", type=int, help="Height in rows") + set_grid_size_parser.set_defaults(func=set_grid_size) + + list_tmux_connections_parser = subparsers.add_parser("list-tmux-connections", help="List tmux integration connections") + list_tmux_connections_parser.set_defaults(func=list_tmux_connections) + + send_tmux_command_parser = subparsers.add_parser("send-tmux-command", help="Send a tmux command to a tmux integration connection") + send_tmux_command_parser.add_argument("connection_id", type=str, help="tmux connection ID") + send_tmux_command_parser.add_argument("command", type=str, help="Command to send") + send_tmux_command_parser.set_defaults(func=send_tmux_command) + + set_tmux_window_visible_parser = subparsers.add_parser("set-tmux-window-visible", help="Show or hide a tmux integration window (represented as a tab in iTerm2)") + set_tmux_window_visible_parser.add_argument("connection_id", type=str, help="tmux connection ID") + set_tmux_window_visible_parser.add_argument("window_id", type=str, help="tmux window ID (number)") + set_tmux_window_visible_parser.add_argument('--visible', dest='visible', action='store_true') + set_tmux_window_visible_parser.add_argument('--no-visible', dest='visible', action='store_false') + set_tmux_window_visible_parser.set_defaults(visible=True) + set_tmux_window_visible_parser.set_defaults(func=set_tmux_window_visible) + + sort_tabs_parser = subparsers.add_parser("sort-tabs", help="Sort tabs alphabetically by name") + sort_tabs_parser.set_defaults(func=sort_tabs) + + list_color_presets_parser = subparsers.add_parser("list-color-presets", help="Lists names of color presets") + list_color_presets_parser.set_defaults(func=list_color_presets) + + set_color_preset_parser = subparsers.add_parser("set-color-preset", help="Lists names of color presets") + set_color_preset_parser.add_argument("profile", type=str, help="Profile name") + set_color_preset_parser.add_argument("preset", type=str, help="Color preset name") + set_color_preset_parser.set_defaults(func=set_color_preset) + + monitor_variable_parser = subparsers.add_parser("monitor-variable", help="Monitor changes to a variable") + monitor_variable_parser.add_argument("name", type=str, help="variable name") + monitor_variable_parser.add_argument('--session', type=str, nargs='?', help='Session ID for the variable scope') + monitor_variable_parser.add_argument('--tab', type=str, nargs='?', help='Tab ID for the variable scope') + monitor_variable_parser.add_argument('--window', type=str, nargs='?', help='Window ID for the variable scope') + monitor_variable_parser.add_argument('--app', action='store_true', help='App scope', default=False) + monitor_variable_parser.set_defaults(func=monitor_variable) + + monitor_focus_parser = subparsers.add_parser("monitor-focus", help="Monitor changes to focus") + monitor_focus_parser.set_defaults(func=monitor_focus) + + set_cursor_color_parser = subparsers.add_parser("set-cursor-color", help="Set cursor color") + set_cursor_color_parser.add_argument("session", type=str, help="Session ID") + set_cursor_color_parser.add_argument("color", type=str, help="Color as red,green,blue where each value is in 0-255") + set_cursor_color_parser.set_defaults(func=set_cursor_color) + + monitor_screen_parser = subparsers.add_parser("monitor-screen", help="Monitor screen contents") + monitor_screen_parser.add_argument("session", type=str, help="Session ID") + monitor_screen_parser.add_argument("query", type=str, help="Stop when this text is seen") + monitor_screen_parser.set_defaults(func=monitor_screen) + + show_selection_parser = subparsers.add_parser("show-selection", help="Shows the selected text in a session") + show_selection_parser.add_argument("session", type=str, help="Session ID") + show_selection_parser.set_defaults(func=show_selection) + + return parser + +def main(argv): + logging.basicConfig() + + parser = make_parser() + args = parser.parse_args(argv[1:]) + if "func" not in args: + print(parser.format_help()) + raise argparse.ArgumentTypeError('Missing command') + + async def wrapper(connection): + try: + await args.func(connection, args) + except Exception as e: + print(traceback.format_exc()) + + iterm2.run_until_complete(wrapper) + +if __name__ == "__main__": + try: + main(sys.argv) + except Exception as e: + print(traceback.format_exc()) + sys.exit(1) diff --git a/.iterm2/it2attention b/.iterm2/it2attention new file mode 100755 index 0000000..6947a7e --- /dev/null +++ b/.iterm2/it2attention @@ -0,0 +1,82 @@ +#!/bin/bash + +# tmux requires unrecognized OSC sequences to be wrapped with DCS tmux; +# ST, and for all ESCs in to be replaced with ESC ESC. It +# only accepts ESC backslash for ST. +function print_osc() { + if [[ $TERM == screen* ]] ; then + printf "\033Ptmux;\033\033]" + else + printf "\033]" + fi +} + +# More of the tmux workaround described above. +function print_st() { + if [[ $TERM == screen* ]] ; then + printf "\a\033\\" + else + printf "\a" + fi +} + +function show_help() { + echo "Usage:" 1>& 2 + echo " $(basename $0) start" 1>& 2 + echo " Begin bouncing the dock icon if another app is active" 1>& 2 + echo " $(basename $0) stop" 1>& 2 + echo " Stop bouncing the dock icon if another app is active" 1>& 2 + echo " $(basename $0) once" 1>& 2 + echo " Bounce the dock icon once if another app is active" 1>& 2 + echo " $(basename $0) fireworks" 1>& 2 + echo " Show an explosion animation at the cursor" 1>& 2 +} + +function start_bounce() { + print_osc + printf "1337;RequestAttention=1" + print_st +} + +function stop_bounce() { + print_osc + printf "1337;RequestAttention=0" + print_st +} + +function bounce_once() { + print_osc + printf "1337;RequestAttention=once" + print_st +} + +function fireworks() { + print_osc + printf "1337;RequestAttention=fireworks" + print_st +} + +## Main +if [[ $# == 0 ]] +then + show_help + exit 1 +fi + +if [[ $1 == start ]] +then + start_bounce +elif [[ $1 == stop ]] +then + stop_bounce +elif [[ $1 == once ]] +then + bounce_once +elif [[ $1 == fireworks ]] +then + fireworks +else + show_help + exit 1 +fi + diff --git a/.iterm2/it2check b/.iterm2/it2check new file mode 100755 index 0000000..39acf4e --- /dev/null +++ b/.iterm2/it2check @@ -0,0 +1,81 @@ +#!/bin/bash +# Make sure stdin and stdout are a tty. +if [ ! -t 0 ] ; then + exit 1 +fi +if [ ! -t 1 ] ; then + exit 1 +fi + +# Read some bytes from stdin. Pass the number of bytes to read as the first argument. +function read_bytes() +{ + numbytes=$1 + dd bs=1 count=$numbytes 2>/dev/null +} + +function read_dsr() { + # Reading response to DSR. + dsr="" + spam=$(read_bytes 2) + byte=$(read_bytes 1) + while [ "${byte}" != "n" ]; do + dsr=${dsr}${byte} + byte=$(read_bytes 1) + done + echo ${dsr} +} + +# Extract the terminal name from DSR 1337 +function terminal { + echo -n "$1" | sed -e 's/ .*//' +} + +# Extract the version number from DSR 1337 +function version { + echo -n "$1" | sed -e 's/.* //' +} + +trap clean_up EXIT +_STTY=$(stty -g) ## Save current terminal setup + +function clean_up() { + stty "$_STTY" ## Restore terminal settings +} + +# Enter raw mode and turn off echo so the terminal and I can chat quietly. +stty -echo -icanon raw + +# Support for the extension first appears in this version of iTerm2: +MIN_VERSION=2.9.20160304 +if [ $# -eq 1 ]; then + MIN_VERSION=$1 +fi + +# Send iTerm2-proprietary code. Other terminals ought to ignore it (but are +# free to use it respectfully). The response won't start with a 0 so we can +# distinguish it from the response to DSR 5. It should contain the terminal's +# name followed by a space followed by its version number and be terminated +# with an n. +echo -n '' + +# Report device status. Responds with esc [ 0 n. All terminals support this. We +# do this because if the terminal will not respond to iTerm2's custom escape +# sequence, we can still read from stdin without blocking indefinitely. +echo -n '' + +version_string=$(read_dsr) +if [ "${version_string}" != "0" -a "${version_string}" != "3" ]; then + # Already read DSR 1337. Read DSR 5 and throw it away. + dsr=$(read_dsr) +else + # Terminal didn't respond to the DSR 1337. The response we read is from DSR 5. + version_string="" +fi + +# Extract the terminal name and version number from the response. +version=$(version "${version_string}") +term=$(terminal "${version_string}") + +# Check if they match what we're looking for. This becomes the return code of the script. +test "$term" = ITERM2 -a \( "$version" \> "$MIN_VERSION" -o "$version" = "$MIN_VERSION" \) diff --git a/.iterm2/it2copy b/.iterm2/it2copy new file mode 100755 index 0000000..e045952 --- /dev/null +++ b/.iterm2/it2copy @@ -0,0 +1,78 @@ +#!/bin/bash + +trap clean_up EXIT +trap clean_up INT + +inosc=0 + +function clean_up() { + if [[ $inosc == 1 ]] + then + print_st + fi +} + +function show_help() { + echo "Usage: $(basename $0)" 1>& 2 + echo " Copies to clipboard from standard input" 1>& 2 + echo " $(basename $0) filename" 1>& 2 + echo " Copies to clipboard from file" 1>& 2 +} + +# tmux requires unrecognized OSC sequences to be wrapped with DCS tmux; +# ST, and for all ESCs in to be replaced with ESC ESC. It +# only accepts ESC backslash for ST. +function print_osc() { + if [[ $TERM == screen* ]] ; then + printf "\033Ptmux;\033\033]" + else + printf "\033]" + fi +} + +# More of the tmux workaround described above. +function print_st() { + if [[ $TERM == screen* ]] ; then + printf "\a\033\\" + else + printf "\a" + fi +} + +# Look for command line flags. +while [ $# -gt 0 ]; do + case "$1" in + -h|--h|--help) + show_help + exit + ;; + -*) + error "Unknown option flag: $1" + show_help + exit 1 + ;; + *) + if [ -r "$1" ] ; then + data=$(base64 < "$1") + print_osc + inosc=1 + printf '1337;Copy=:%s' "$data" + print_st + inosc=0 + exit 0 + else + error "it2copy: $1: No such file or directory" + exit 2 + fi + ;; + esac + shift +done + +data=$(base64) +print_osc +inosc=1 +printf '1337;Copy=:%s' "$data" +print_st +inosc=0 + diff --git a/.iterm2/it2dl b/.iterm2/it2dl new file mode 100755 index 0000000..8be4462 --- /dev/null +++ b/.iterm2/it2dl @@ -0,0 +1,36 @@ +#!/bin/bash +if [ $# -lt 1 ]; then + echo "Usage: $(basename $0) file ..." + exit 1 +fi + +function load_version() { + if [ -z ${IT2DL_BASE64_VERSION+x} ]; then + export IT2DL_BASE64_VERSION=$(base64 --version 2>&1) + fi +} + +function b64_encode() { + load_version + if [[ "$IT2DL_BASE64_VERSION" =~ GNU ]]; then + # Disable line wrap + base64 -w0 + else + base64 + fi +} + + +for fn in "$@" +do + if [ -r "$fn" ] ; then + [ -d "$fn" ] && { echo "$fn is a directory"; continue; } + printf '\033]1337;File=name=%s;' $(echo -n "$fn" | b64_encode) + wc -c "$fn" | awk '{printf "size=%d",$1}' + printf ":" + base64 < "$fn" + printf '\a' + else + echo File $fn does not exist or is not readable. + fi +done diff --git a/.iterm2/it2getvar b/.iterm2/it2getvar new file mode 100755 index 0000000..94c4795 --- /dev/null +++ b/.iterm2/it2getvar @@ -0,0 +1,95 @@ +#!/bin/bash + +# tmux requires unrecognized OSC sequences to be wrapped with DCS tmux; +# ST, and for all ESCs in to be replaced with ESC ESC. It +# only accepts ESC backslash for ST. +function print_osc() { + if [[ $TERM == screen* ]] ; then printf "\033Ptmux;\033\033]" + else + printf "\033]" >& 2 + fi +} + +# More of the tmux workaround described above. +function print_st() { + if [[ $TERM == screen* ]] ; then + printf "\a\033\\" >& 2 + else + printf "\a" >& 2 + fi +} + +function show_help() { + echo "Usage:" 1>& 2 + echo " $(basename $0) name" 1>& 2 +} + +# Read some bytes from stdin. Pass the number of bytes to read as the first argument. +function read_bytes() { + numbytes=$1 + dd bs=1 count=$numbytes 2>/dev/null +} + +# read_until c +# Returns bytes read from stdin up to but not including the fist one equal to c +function read_until() { + result="" + while : + do + b=$(read_bytes 1) + if [[ $b == $1 ]] + then + echo "$result" + return + fi + result="$result$b" + done +} + +## Main +if [[ $# != 1 ]] +then + show_help + exit 1 +fi + +if ! test -t 1 +then + echo "Standard error not a terminal" + exit 1 +fi + +trap clean_up EXIT +_STTY=$(stty -g) ## Save current terminal setup + +function clean_up() { + stty "$_STTY" ## Restore terminal settings +} + +# Enter raw mode and turn off echo so the terminal and I can chat quietly. +stty -echo -icanon raw + +print_osc +printf "1337;ReportVariable=%s" "$(printf "%s" "$1" | base64)" >& 2 +print_st + +VERSION=$(base64 --version 2>&1) +if [[ "$VERSION" =~ fourmilab ]]; then + BASE64ARG=-d +elif [[ "$VERSION" =~ GNU ]]; then + BASE64ARG=-di +else + BASE64ARG=-D +fi + +ignore=$(read_bytes 1) +name=$(read_until ) +re='^]1337;ReportVariable=(.*)' +if [[ $name =~ $re ]] +then + printf "%s" $(base64 $BASE64ARG <<< ${BASH_REMATCH[1]}) + exit 0 +else + exit 1 +fi + diff --git a/.iterm2/it2git b/.iterm2/it2git new file mode 100755 index 0000000..2d3805a --- /dev/null +++ b/.iterm2/it2git @@ -0,0 +1,117 @@ +#!/bin/bash +# This script sets iTerm2 user-defined variables describing the state of the git +# repo in the current directory. +# +# For more information on the status bar, see: +# https://www.iterm2.com/3.3/documentation-status-bar.html +# +# Installation instructions for this script: +# +# bash: Place this in .bashrc. +# -------------------------------------------------------------------------------------- +# function iterm2_print_user_vars() { +# it2git +# } + +# fish: Place this in ~/.config/fish/config.fish after the line +# "source ~/.iterm2_shell_integration.fish". +# -------------------------------------------------------------------------------------- +# function iterm2_print_user_vars +# it2git +# end + +# tcsh: Place this in .tcshrc +# -------------------------------------------------------------------------------------- +# alias get_current_branch "bash -c '((git branch 2> /dev/null) | grep \* | cut -c3-)'" +# alias _iterm2_user_defined_vars 'it2git' + +# zsh:Place this in .zshrc after "source /Users/georgen/.iterm2_shell_integration.zsh". +# -------------------------------------------------------------------------------------- +# iterm2_print_user_vars() { +# it2git +# } + +GIT_BINARY=/usr/bin/git + +dirty() { + # Outputs "dirty" or "clean" + OUTPUT=$("$GIT_BINARY" status --porcelain --ignore-submodules -unormal 2>/dev/null) + if (($?)); then + echo "clean" + return + fi + if [ -z "$OUTPUT" ]; then + echo "clean" + else + echo "dirty" + fi +} + +counts() { + OUTPUT=$("$GIT_BINARY" rev-list --left-right --count HEAD...@'{u}' 2>/dev/null) + if (($?)); then + echo "error" + return + fi + echo "$OUTPUT" +} + +branch() { + OUTPUT=$("$GIT_BINARY" symbolic-ref -q --short HEAD 2>/dev/null || git rev-parse --short HEAD 2>/dev/null) + if (($?)); then + return + fi + echo "$OUTPUT" +} + +adds() { + "${GIT_BINARY}" ls-files --others --exclude-standard | wc -l +} + +deletes() { + "${GIT_BINARY}" ls-files --deleted --exclude-standard | wc -l +} +function iterm2_begin_osc { + printf "\033]" +} + +function iterm2_end_osc { + printf "\007" +} + +function iterm2_set_user_var() { + iterm2_begin_osc + printf "1337;SetUserVar=%s=%s" "$1" $(printf "%s" "$2" | base64 | tr -d '\n') + iterm2_end_osc +} + +git_poll () { + DIRECTORY="$1" + cd "$DIRECTORY" + DIRTY=$(dirty) + COUNTS=$(counts) + PUSH_COUNT=$(cut -f1 <<< "$COUNTS") + PULL_COUNT=$(cut -f2 <<< "$COUNTS") + BRANCH=$(branch) + + iterm2_set_user_var gitBranch "$BRANCH" + iterm2_set_user_var gitDirty "$DIRTY" + iterm2_set_user_var gitPushCount "$PUSH_COUNT" + iterm2_set_user_var gitPullCount "$PULL_COUNT" + iterm2_set_user_var gitAdds "$ADDS" + iterm2_set_user_var gitDeletes "$DELETES" +} + +"$GIT_BINARY" rev-parse --git-dir 2>/dev/null >/dev/null +if (($?)); then + iterm2_set_user_var gitBranch "" + iterm2_set_user_var gitDirty "" + iterm2_set_user_var gitPushCount "" + iterm2_set_user_var gitPullCount "" + iterm2_set_user_var gitAdds "" + iterm2_set_user_var gitDeletes "" +else + git_poll "$PWD" +fi + + diff --git a/.iterm2/it2setcolor b/.iterm2/it2setcolor new file mode 100755 index 0000000..7871d6f --- /dev/null +++ b/.iterm2/it2setcolor @@ -0,0 +1,116 @@ +#!/bin/bash +open=0 + +# tmux requires unrecognized OSC sequences to be wrapped with DCS tmux; +# ST, and for all ESCs in to be replaced with ESC ESC. It +# only accepts ESC backslash for ST. +function print_osc() { + if [[ $TERM == screen* ]] ; then + printf "\033Ptmux;\033\033]" + else + printf "\033]" + fi +} + +# More of the tmux workaround described above. +function print_st() { + if [[ $TERM == screen* ]] ; then + printf "\a\033\\" + else + printf "\a" + fi +} + +# set_color key rgb +function set_color() { + print_osc + printf '1337;SetColors=%s=%s' "$1" "$2" + print_st +} + +function error() { + echo "ERROR: $*" 1>&2 +} + +function show_help() { + if [ $open = 1 ]; then + print_st + fi + echo "Usage" + echo "" + echo "1) To set a specific color to an RGB value:" + echo " it2setcolor name color [name color...]" 1>& 2 + echo "For example:" + echo " it2setcolor fg fff" + echo "" + echo "name is one of:" + echo " fg bg bold link selbg selfg curbg curfg underline tab" + echo " black red green yellow blue magenta cyan white" + echo " br_black br_red br_green br_yellow br_blue br_magenta br_cyan br_white" + echo "" + echo "color is of the format:" + echo " RGB (three hex digits, like fff)" + echo " RRGGBB (six hex digits, like f0f0f0)" + echo " cs:RGB (cs is a color space name)" + echo " cs:RRGGBB (cs is a color space name)" + echo "" + echo " The color space names accepted in the color are:" + echo " srgb (sRGB, the default if none is specified)" + echo " rgb (Apple's old device-independent colorspace)" + echo " p3 (Apple's fancy large-gamut colorspace)" + echo "" + echo "2) To switch to a named color preset:" + echo " it2setcolor preset name" + echo "For example:" + echo " it2setcolor preset 'Light Background'" + echo "" + echo "3) To reset the current tab's color to the system default:" + echo " it2setcolor tab default" +} + +# Show help if no arguments and no stdin. +if [ $# -eq 0 ]; then + show_help + exit +fi + +# Look for command line flags. +while [ $# -gt 0 ]; do + case "$1" in + -h|--h|--help) + show_help + exit + ;; + -*) + error "Unknown option flag: $1" + show_help + exit 1 + ;; + *) + if [ $# -lt 2 ]; then + show_help + exit + fi + if [ $open = 0 ]; then + open=1 + print_osc + printf '1337;SetColors=' + else + printf "," + fi + # name is not checked for validity because we'd like this to work with future colors, too. + printf "%s=%s" "$1" "$2" + shift + ;; + esac + shift +done + +if [ $open = 1 ]; then + print_st +else + show_help +fi + +exit 0 + diff --git a/.iterm2/it2setkeylabel b/.iterm2/it2setkeylabel new file mode 100755 index 0000000..c8320e4 --- /dev/null +++ b/.iterm2/it2setkeylabel @@ -0,0 +1,123 @@ +#!/bin/bash + +# tmux requires unrecognized OSC sequences to be wrapped with DCS tmux; +# ST, and for all ESCs in to be replaced with ESC ESC. It +# only accepts ESC backslash for ST. +function print_osc() { + if [[ $TERM == screen* ]] ; then + printf "\033Ptmux;\033\033]" + else + printf "\033]" + fi +} + +# More of the tmux workaround described above. +function print_st() { + if [[ $TERM == screen* ]] ; then + printf "\a\033\\" + else + printf "\a" + fi +} + +function show_help() { + echo "Usage:" 1>& 2 + echo " $(basename $0) set Fn Label" 1>& 2 + echo " Where n is a value from 1 to 20" 1>& 2 + echo " $(basename $0) set status Label" 1>& 2 + echo " Sets the touch bar status button's label" 1>& 2 + echo " $(basename $0) push [name]" 1>& 2 + echo " Saves the current labels with an optional name. Resets labels to their default value, unless name begins with a "." character." 1>& 2 + echo " $(basename $0) pop [name]" 1>& 2 + echo " If name is given, all key labels up to and including the one with the matching name are popped." 1>& 2 + echo "" 1>& 2 + echo "Example:" 1>& 2 + echo "#!/bin/bash" 1>& 2 + echo "# Wrapper script for mc that sets function key labels" 1>& 2 + echo "NAME=mc_\$RANDOM" 1>& 2 + echo "# Save existing labels" 1>& 2 + echo "$(basename $0) push \$NAME" 1>& 2 + echo "$(basename $0) set F1 Help" 1>& 2 + echo "$(basename $0) set F2 Menu" 1>& 2 + echo "$(basename $0) set F3 View" 1>& 2 + echo "$(basename $0) set F4 Edit" 1>& 2 + echo "$(basename $0) set F5 Copy" 1>& 2 + echo "$(basename $0) set F6 Move" 1>& 2 + echo "$(basename $0) set F7 Mkdir" 1>& 2 + echo "$(basename $0) set F8 Del" 1>& 2 + echo "$(basename $0) set F9 Menu" 1>& 2 + echo "$(basename $0) set F10 Quit" 1>& 2 + echo "$(basename $0) set F11 Menu" 1>& 2 + echo "$(basename $0) set F13 View" 1>& 2 + echo "$(basename $0) set F14 Edit" 1>& 2 + echo "$(basename $0) set F15 Copy" 1>& 2 + echo "$(basename $0) set F16 Move" 1>& 2 + echo "$(basename $0) set F17 Find" 1>& 2 + echo "mc" 1>& 2 + echo "# Restore labels to their previous state" 1>& 2 + echo "$(basename $0) pop \$NAME" 1>& 2 +} + +## Main +if [[ $# == 0 ]] +then + show_help + exit 1 +fi + +if [[ $1 == set ]] +then + if [[ $# != 3 ]] + then + show_help + exit 1 + fi + print_osc + printf "1337;SetKeyLabel=%s=%s" "$2" "$3" + print_st +elif [[ $1 == push ]] +then + if [[ $# == 1 ]] + then + print_osc + printf "1337;PushKeyLabels" + print_st + elif [[ $# == 2 ]] + then + if [[ $2 == "" ]] + then + echo "Name must not be empty" 1>& 2 + exit 1 + fi + print_osc + printf "1337;PushKeyLabels=%s" "$2" + print_st + else + show_help + exit 1 + fi +elif [[ $1 == pop ]] +then + if [[ $# == 1 ]] + then + print_osc + printf "1337;PopKeyLabels" + print_st + elif [[ $# == 2 ]] + then + if [[ $2 == "" ]] + then + echo "Name must not be empty" 1>& 2 + exit 1 + fi + print_osc + printf "1337;PopKeyLabels=%s" "$2" + print_st + else + show_help + exit 1 + fi +else + show_help + exit 1 +fi diff --git a/.iterm2/it2ul b/.iterm2/it2ul new file mode 100755 index 0000000..5aae8df --- /dev/null +++ b/.iterm2/it2ul @@ -0,0 +1,98 @@ +#!/bin/bash + +trap clean_up EXIT +_STTY=$(stty -g) ## Save current terminal setup +stty -echo ## Turn off echo + +function clean_up() { + stty "$_STTY" ## Restore terminal settings +} + +function show_help() { + echo "Usage: $(basename $0) [destination [tar flags]]" 1>& 2 + echo " If given, the destination specifies the directory to place downloaded files." + echo " Further options are passed through to tar. See your system's manpage for tar for details." +} + +function bad_input() { + echo "Bad input: %1" 1>& 2 + exit 1 +} + +function die() { + echo "Fatal error: $1" 1>& 2 + exit 1 +} + +function read_base64_stanza() { + value="" + while read line; + do + if [ "$line" == "" ]; then + break + fi + printf "%s" "$line" + done +} + +function decode() { + VERSION=$(base64 --version 2>&1) + if [[ "$VERSION" =~ fourmilab ]]; then + BASE64ARG=-d + elif [[ "$VERSION" =~ GNU ]]; then + BASE64ARG=-di + else + BASE64ARG=-D + fi + + base64 "$BASE64ARG" <<< "$1" +} + +# tmux requires unrecognized OSC sequences to be wrapped with DCS tmux; +# ST, and for all ESCs in to be replaced with ESC ESC. It +# only accepts ESC backslash for ST. +function print_osc() { + if [[ $TERM == screen* ]] ; then + printf "\033Ptmux;\033\033]" + else + printf "\033]" + fi +} + +# More of the tmux workaround described above. +function print_st() { + if [[ $TERM == screen* ]] ; then + printf "\a\033\\" + else + printf "\a" + fi +} + +function send_request_for_upload() { + print_osc + printf '1337;RequestUpload=format=tgz' "" + print_st +} + +location="$PWD" +if [[ $# > 0 ]] +then + location="$1" + shift +fi + +send_request_for_upload +read status + +if [[ $status == ok ]] +then + data=$(read_base64_stanza) + clean_up + decode "$data" | tar -x -z -C "$location" -f - $* 1>& 2 +elif [[ $status == abort ]] +then + echo "Upload aborted" 1>& 2 +else + die "Unknown status: $status" 1>& 2 +fi + diff --git a/.iterm2/it2universion b/.iterm2/it2universion new file mode 100755 index 0000000..771c067 --- /dev/null +++ b/.iterm2/it2universion @@ -0,0 +1,104 @@ +#!/bin/bash + +# tmux requires unrecognized OSC sequences to be wrapped with DCS tmux; +# ST, and for all ESCs in to be replaced with ESC ESC. It +# only accepts ESC backslash for ST. +function print_osc() { + if [[ $TERM == screen* ]] ; then + printf "\033Ptmux;\033\033]" + else + printf "\033]" + fi +} + +# More of the tmux workaround described above. +function print_st() { + if [[ $TERM == screen* ]] ; then + printf "\a\033\\" + else + printf "\a" + fi +} + +function show_help() { + echo "Usage:" 1>& 2 + echo " $(basename $0) set 8" 1>& 2 + echo " $(basename $0) set 9" 1>& 2 + echo " $(basename $0) push [name]" 1>& 2 + echo " Saves the current version with an optional name." 1>& 2 + echo " $(basename $0) pop [name]" 1>& 2 + echo " If name is given, all versions up to and including the one with the matching name are popped." 1>& 2 +} + +function set_version() { + print_osc + printf "1337;UnicodeVersion=$1" + print_st +} + +function push_version() { + print_osc + printf "1337;UnicodeVersion=push $1" + print_st +} + +function pop_version() { + print_osc + printf "1337;UnicodeVersion=pop $1" + print_st +} + +## Main +if [[ $# == 0 ]] +then + show_help + exit 1 +fi + +if [[ $1 == set ]] +then + if [[ $# != 2 ]] + then + show_help + exit 1 + fi + set_version $2 +elif [[ $1 == push ]] +then + if [[ $# == 1 ]] + then + push_version "" + elif [[ $# == 2 ]] + then + if [[ $2 == "" ]] + then + echo "Name must not be empty" 1>& 2 + exit 1 + fi + push_version "$2" + else + show_help + exit 1 + fi +elif [[ $1 == pop ]] +then + if [[ $# == 1 ]] + then + pop_version "" + elif [[ $# == 2 ]] + then + if [[ $2 == "" ]] + then + echo "Name must not be empty" 1>& 2 + exit 1 + fi + pop_version "$2" + else + show_help + exit 1 + fi +else + show_help + exit 1 +fi + diff --git a/.iterm2_shell_integration.zsh b/.iterm2_shell_integration.zsh new file mode 100755 index 0000000..9ee6e9b --- /dev/null +++ b/.iterm2_shell_integration.zsh @@ -0,0 +1,170 @@ +# 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. + +if [[ -o interactive ]]; then + if [ "${ITERM_ENABLE_SHELL_INTEGRATION_WITH_TMUX-}""$TERM" != "screen" -a "${ITERM_SHELL_INTEGRATION_INSTALLED-}" = "" -a "$TERM" != linux -a "$TERM" != dumb ]; then + ITERM_SHELL_INTEGRATION_INSTALLED=Yes + ITERM2_SHOULD_DECORATE_PROMPT="1" + # Indicates start of command output. Runs just before command executes. + iterm2_before_cmd_executes() { + printf "\033]133;C;\007" + } + + iterm2_set_user_var() { + printf "\033]1337;SetUserVar=%s=%s\007" "$1" $(printf "%s" "$2" | base64 | tr -d '\n') + } + + # Users can write their own version of this method. It should call + # iterm2_set_user_var but not produce any other output. + # e.g., iterm2_set_user_var currentDirectory $PWD + # Accessible in iTerm2 (in a badge now, elsewhere in the future) as + # \(user.currentDirectory). + whence -v iterm2_print_user_vars > /dev/null 2>&1 + if [ $? -ne 0 ]; then + iterm2_print_user_vars() { + true + } + fi + + iterm2_print_state_data() { + local _iterm2_hostname="${iterm2_hostname-}" + if [ -z "${iterm2_hostname:-}" ]; then + _iterm2_hostname=$(hostname -f 2>/dev/null) + fi + printf "\033]1337;RemoteHost=%s@%s\007" "$USER" "${_iterm2_hostname-}" + printf "\033]1337;CurrentDir=%s\007" "$PWD" + iterm2_print_user_vars + } + + # Report return code of command; runs after command finishes but before prompt + iterm2_after_cmd_executes() { + printf "\033]133;D;%s\007" "$STATUS" + iterm2_print_state_data + } + + # Mark start of prompt + iterm2_prompt_mark() { + printf "\033]133;A\007" + } + + # Mark end of prompt + iterm2_prompt_end() { + printf "\033]133;B\007" + } + + # There are three possible paths in life. + # + # 1) A command is entered at the prompt and you press return. + # The following steps happen: + # * iterm2_preexec is invoked + # * PS1 is set to ITERM2_PRECMD_PS1 + # * ITERM2_SHOULD_DECORATE_PROMPT is set to 1 + # * The command executes (possibly reading or modifying PS1) + # * iterm2_precmd is invoked + # * ITERM2_PRECMD_PS1 is set to PS1 (as modified by command execution) + # * PS1 gets our escape sequences added to it + # * zsh displays your prompt + # * You start entering a command + # + # 2) You press ^C while entering a command at the prompt. + # The following steps happen: + # * (iterm2_preexec is NOT invoked) + # * iterm2_precmd is invoked + # * iterm2_before_cmd_executes is called since we detected that iterm2_preexec was not run + # * (ITERM2_PRECMD_PS1 and PS1 are not messed with, since PS1 already has our escape + # sequences and ITERM2_PRECMD_PS1 already has PS1's original value) + # * zsh displays your prompt + # * You start entering a command + # + # 3) A new shell is born. + # * PS1 has some initial value, either zsh's default or a value set before this script is sourced. + # * iterm2_precmd is invoked + # * ITERM2_SHOULD_DECORATE_PROMPT is initialized to 1 + # * ITERM2_PRECMD_PS1 is set to the initial value of PS1 + # * PS1 gets our escape sequences added to it + # * Your prompt is shown and you may begin entering a command. + # + # Invariants: + # * ITERM2_SHOULD_DECORATE_PROMPT is 1 during and just after command execution, and "" while the prompt is + # shown and until you enter a command and press return. + # * PS1 does not have our escape sequences during command execution + # * After the command executes but before a new one begins, PS1 has escape sequences and + # ITERM2_PRECMD_PS1 has PS1's original value. + iterm2_decorate_prompt() { + # This should be a raw PS1 without iTerm2's stuff. It could be changed during command + # execution. + ITERM2_PRECMD_PS1="$PS1" + ITERM2_SHOULD_DECORATE_PROMPT="" + + # Add our escape sequences just before the prompt is shown. + # Use ITERM2_SQUELCH_MARK for people who can't mdoify PS1 directly, like powerlevel9k users. + # This is gross but I had a heck of a time writing a correct if statetment for zsh 5.0.2. + local PREFIX="" + if [[ $PS1 == *"$(iterm2_prompt_mark)"* ]]; then + PREFIX="" + elif [[ "${ITERM2_SQUELCH_MARK-}" != "" ]]; then + PREFIX="" + else + PREFIX="%{$(iterm2_prompt_mark)%}" + fi + PS1="$PREFIX$PS1%{$(iterm2_prompt_end)%}" + } + + iterm2_precmd() { + local STATUS="$?" + if [ -z "${ITERM2_SHOULD_DECORATE_PROMPT-}" ]; then + # You pressed ^C while entering a command (iterm2_preexec did not run) + iterm2_before_cmd_executes + fi + + iterm2_after_cmd_executes "$STATUS" + + if [ -n "$ITERM2_SHOULD_DECORATE_PROMPT" ]; then + iterm2_decorate_prompt + fi + } + + # This is not run if you press ^C while entering a command. + iterm2_preexec() { + # Set PS1 back to its raw value prior to executing the command. + PS1="$ITERM2_PRECMD_PS1" + ITERM2_SHOULD_DECORATE_PROMPT="1" + iterm2_before_cmd_executes + } + + # If hostname -f is slow on your system set iterm2_hostname prior to + # sourcing this script. We know it is fast on macOS so we don't cache + # it. That lets us handle the hostname changing like when you attach + # to a VPN. + if [ -z "${iterm2_hostname-}" ]; then + if [ "$(uname)" != "Darwin" ]; then + iterm2_hostname=`hostname -f 2>/dev/null` + # Some flavors of BSD (i.e. NetBSD and OpenBSD) don't have the -f option. + if [ $? -ne 0 ]; then + iterm2_hostname=`hostname` + fi + fi + fi + + [[ -z ${precmd_functions-} ]] && precmd_functions=() + precmd_functions=($precmd_functions iterm2_precmd) + + [[ -z ${preexec_functions-} ]] && preexec_functions=() + preexec_functions=($preexec_functions iterm2_preexec) + + iterm2_print_state_data + printf "\033]1337;ShellIntegrationVersion=12;shell=zsh\007" + fi +fi +alias imgcat=${HOME}/.iterm2/imgcat;alias imgls=${HOME}/.iterm2/imgls;alias it2api=${HOME}/.iterm2/it2api;alias it2attention=${HOME}/.iterm2/it2attention;alias it2check=${HOME}/.iterm2/it2check;alias it2copy=${HOME}/.iterm2/it2copy;alias it2dl=${HOME}/.iterm2/it2dl;alias it2getvar=${HOME}/.iterm2/it2getvar;alias it2git=${HOME}/.iterm2/it2git;alias it2setcolor=${HOME}/.iterm2/it2setcolor;alias it2setkeylabel=${HOME}/.iterm2/it2setkeylabel;alias it2ul=${HOME}/.iterm2/it2ul;alias it2universion=${HOME}/.iterm2/it2universion diff --git a/.zsh_aliases b/.zsh_aliases index 90cb35e..d4d14ab 100644 --- a/.zsh_aliases +++ b/.zsh_aliases @@ -160,7 +160,11 @@ alias sscrot='grim -g "$(slurp)" "desktop-$(date +"%Y%m%d%H%M").png"' alias sway='dbus-launch --sh-syntax --exit-with-session /usr/bin/sway' alias gnome='XDG_SESSION_TYPE=wayland dbus-run-session gnome-session' alias minecraft='QT_QPA_PLATFORM= multimc' -alias xclip='tee >(wl-copy) | wl-copy -p' +if [ "$IS_MAC" = 0 ]; then + alias xclip='tee >(wl-copy) | wl-copy -p' +else + alias xclip='pbcopy' +fi alias lzd='docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock -v ~/.config/lazydocker:/.config/jesseduffield/lazydocker lazyteam/lazydocker' alias wavogg='for f in *.wav; do ffmpeg -i "$f" -c:a libvorbis -q:a 4 "${f/%wav/ogg}"; done' alias rustscan='docker run -it --rm --name rustscan rustscan/rustscan:alpine' diff --git a/Library/Preferences/com.googlecode.iterm2.plist b/Library/Preferences/com.googlecode.iterm2.plist index ce332e6..77974b9 100644 Binary files a/Library/Preferences/com.googlecode.iterm2.plist and b/Library/Preferences/com.googlecode.iterm2.plist differ