--- a/.hgsub Sun May 18 14:59:11 2025 -0400
+++ b/.hgsub Wed Aug 27 16:19:24 2025 -0400
@@ -2,6 +2,7 @@
mercurial/templates = [hg]ssh://hg.stevelosh.com/repos/mercurial-cli-templates
vim/bundle/abolish = [git]https://github.com/tpope/vim-abolish
vim/bundle/ack = [git]https://github.com/mileszs/ack.vim
+vim/bundle/ansible = [git]https://github.com/pearofducks/ansible-vim
vim/bundle/badwolf = [hg]ssh://hg.stevelosh.com/repos/badwolf/
vim/bundle/boxdraw = [git]https://github.com/gyim/vim-boxdraw
vim/bundle/clam = [hg]ssh://hg.stevelosh.com/repos/clam.vim/
@@ -18,6 +19,8 @@
vim/bundle/markdown = [git]https://github.com/sjl/vim-markdown
vim/bundle/neoformat = [git]https://github.com/sbdchd/neoformat
vim/bundle/nerdtree = [git]https://github.com/scrooloose/nerdtree
+vim/bundle/nextflow-vim = [git]https://github.com/sjl/nextflow-vim
+vim/bundle/nvim-r = [git]https://github.com/jalvesaq/Nvim-R
vim/bundle/paredit = [git]https://github.com/kovisoft/paredit
vim/bundle/pgsql = [git]https://github.com/exu/pgsql.vim
vim/bundle/python-mode = [git]https://github.com/klen/python-mode
@@ -28,11 +31,13 @@
vim/bundle/sexp = [git]https://github.com/guns/vim-sexp
vim/bundle/shellcheck = [git]https://github.com/itspriddle/vim-shellcheck
vim/bundle/simpylfold = [git]https://github.com/tmhedberg/SimpylFold
+vim/bundle/singularity-syntax = [git]https://github.com/rbberger/vim-singularity-syntax
vim/bundle/splice = [hg]ssh://hg.stevelosh.com/repos/splice.vim
vim/bundle/strftimedammit = [hg]ssh://hg.stevelosh.com/repos/strftimedammit.vim/
vim/bundle/surround = [git]https://github.com/tpope/vim-surround
vim/bundle/swig = [git]https://github.com/vim-scripts/SWIG-syntax
vim/bundle/targets = [git]https://github.com/wellle/targets.vim
+vim/bundle/ultisnips = [git]https://github.com/SirVer/ultisnips
vim/bundle/vim-go = [git]https://github.com/fatih/vim-go
vim/bundle/vimtex = [git]https://github.com/lervag/vimtex
vim/bundle/vlime = [git]https://github.com/sjl/vlime
--- a/.hgsubstate Sun May 18 14:59:11 2025 -0400
+++ b/.hgsubstate Wed Aug 27 16:19:24 2025 -0400
@@ -1,24 +1,27 @@
16aa2def1d5b91f2f38c0bf50a955d28559c7ebe mercurial/hg-prompt
1fc4a9fbead7e0acc4c828b346f3be2658ec3df9 mercurial/templates
-b6a8b49e2173ba5a1b34d00e68e0ed8addac3ebd vim/bundle/abolish
-a16a9b63eb85cc0960a7f25c54647ac1f99f3360 vim/bundle/ack
-599e1bb1aee98e563132553cf8b7bc32cb402b75 vim/bundle/badwolf
-24f6d94dd03ba0fdc703265fe281f70cf2b45ba6 vim/bundle/boxdraw
+dcbfe065297d31823561ba787f51056c147aa682 vim/bundle/abolish
+36e40f9ec91bdbf6f1adf408522a73a6925c3042 vim/bundle/ack
+3329367d2e5f90d203c8d340c53fa83c60a1ad41 vim/bundle/ansible
+a93aaf7f4247858f5e483dc98001a878a5233761 vim/bundle/badwolf
+b7f789f305b1c5b0b4623585e0f10adb417f2966 vim/bundle/boxdraw
b542a7bc4d9bc5da8fb12e110fe7975131fb57a4 vim/bundle/clam
-8295187ea1210138c0b171d8e3ec3569936f4c1a vim/bundle/commentary
-c6d1fc5e58d689bfb104ff336aeb89d9ef1b48e2 vim/bundle/ctrlp
+e87cd90dc09c2a203e13af9704bd0ef79303d755 vim/bundle/commentary
+7c972cb19c8544c681ca345c64ec39e04f4651cc vim/bundle/ctrlp
38487bbec8ba50834e257940b357de03991fa8f9 vim/bundle/delimitmate
755554bb3c44944f70f4b2048acf0c69261782ac vim/bundle/fugitive
127d706f2def96876605e6bd5d366c973cb8e406 vim/bundle/gdl
7fcea1a08423da3012aac87f5224738c85d212a1 vim/bundle/gnupg
0d57b080f9fae8573c688b6679b31eb1666edc4c vim/bundle/gnuplot
1d84591fff04caebab75cba2294fc3843f0a4a29 vim/bundle/gundo
-fccd580f5f11d576169ee347907c9fbd77af410a vim/bundle/html5
+485f2cd62162c81e56d8604b4c630f0b5ef69d1f vim/bundle/html5
dd84369d731bcb8feee0901cbb9b63a2b219bf28 vim/bundle/javascript
e2d7fcd682a461a3951e8b5067cc8a0083e75e35 vim/bundle/markdown
964c66fa22500ae7375114342d212d7fe15da341 vim/bundle/neoformat
9310f91476a94ee9c2f3a587171893743a343e26 vim/bundle/nerdtree
-c76e0987ec45c84103b408691ec0506e7b99cb30 vim/bundle/paredit
+47f6d8508757559fcfa8f49cd93cd5c86ce847f0 vim/bundle/nextflow-vim
+eb97bf7c88480b9f00b6765c09c3886550ed8d43 vim/bundle/nvim-r
+989d1c046bbe36f7d71878636dbe6af8ea3f4f86 vim/bundle/paredit
1a436f7d875b4ec630da081b041c73264235c7e7 vim/bundle/pgsql
d241974f40e8d206f9970e51fb0069951862ba35 vim/bundle/python-mode
eb8baa5428bde10ecc1cb14eed1d6e16f5f24695 vim/bundle/rainbow-parentheses
@@ -28,12 +31,14 @@
b4398689f7483b01684044ab6b55bf369744c9b3 vim/bundle/sexp
4346419ac57ef341a15aa39c827c0848f17c6faf vim/bundle/shellcheck
0459df8a0bbfc8ef1bfd88db889e881626f65914 vim/bundle/simpylfold
+caaba2594df4f1859a448e6cf13c64d956419acd vim/bundle/singularity-syntax
062b18eebd153c13e6f36577707acb17893cd959 vim/bundle/splice
26fbdd7d1f1aa5600d2ebf39bbdd292c38aac16e vim/bundle/strftimedammit
aa1f120ad3a29c27cc41d581cda3751c59343cce vim/bundle/surround
19c3d966440b6cfe8d74251881a48e961ddb8648 vim/bundle/swig
f6f2d6618a321f5b0065586a7bc934325fec81ab vim/bundle/targets
+b393ba65386d47664421e1f8b246a87a6e8b218c vim/bundle/ultisnips
e9d7ff3eb4a369f0cb2069c8f77ae68796bca308 vim/bundle/vim-go
-5d5c71044880443035e07009497962feacb56b20 vim/bundle/vimtex
-54feb567738398ab65d783e096bc84938e7620a0 vim/bundle/vlime
+9df79e15bf035d1cfb32c11fffed38dd7b6a0501 vim/bundle/vimtex
+eee93632fee3b680fe992008b723b0802bb94a60 vim/bundle/vlime
6876fe38b33732cb124d415ffc4156f16da5e118 vim/bundle/windowswap
--- a/.mbsyncrc Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-# tweaked https://raw.githubusercontent.com/bm4cs/dots/Far/.mbsyncrc
-
-IMAPAccount fastmail
-Host imap.fastmail.com
-Port 993
-User steve@stevelosh.com
-PassCmd "pass show mutt/ouroboros/steve@stevelosh.com | head -n 1"
-SSLType IMAPS
-SSLVersion TLSv1.2
-CertificateFile /etc/ssl/certs/ca-certificates.crt
-
-# Remote
-IMAPStore mail-remote
-Account fastmail
-
-# Local
-MaildirStore mail-local
-Subfolders Verbatim
-Path ~/.mail2/sjl/
-Inbox ~/.mail2/sjl/INBOX
-
-# CONNECTIONS SPECIFY LINKS BETWEEN REMOTE AND LOCAL FOLDERS
-# CONNECTIONS ARE SPECIFIED USING PATTERNS, WHICH MATCH REMOTE MAIl
-# FOLDERS. SOME COMMONLY USED PATTERS INCLUDE:
-#
-# 1 "*" TO MATCH EVERYTHING
-# 2 "!DIR" TO EXCLUDE "DIR"
-# 3 "DIR" TO MATCH DIR
-
-Channel mail-inbox
-Far :mail-remote:
-Near :mail-local:
-Patterns INBOX
-Create Near
-Sync All
-Expunge Both
-SyncState *
-
-Channel mail-sent
-Far :mail-remote:
-Near :mail-local:
-Patterns "Sent"
-Create Both
-Sync All
-Expunge Both
-SyncState *
-
-Channel mail-spam
-Far :mail-remote:
-Near :mail-local:
-Patterns "Uncaught Spam"
-Create Near
-Sync All
-Expunge Both
-SyncState *
-
-Channel mail-drafts
-Far :mail-remote:
-Near :mail-local:
-Patterns "Drafts"
-Create Near
-Sync All
-Expunge Both
-SyncState *
-
-Channel mail-archive
-Far :mail-remote:
-Near :mail-local:
-Patterns "Arc*"
-Create Near
-Sync All
-Expunge Both
-SyncState *
-
-Group mail
-Channel mail-inbox
-Channel mail-spam
-Channel mail-drafts
-Channel mail-sent
-Channel mail-archive
-
--- a/Xmodmap Sun May 18 14:59:11 2025 -0400
+++ b/Xmodmap Wed Aug 27 16:19:24 2025 -0400
@@ -6,7 +6,11 @@
clear mod4
clear mod5
+! Laptop lctrl
keycode 37 = Hyper_L
+! Laptop rctrl
+keycode 105 = Hyper_R
+
keycode 66 = Control_L
keycode 133 = Alt_L
keycode 64 = Super_L
@@ -53,4 +57,4 @@
add mod1 = Alt_L
add mod2 = Num_Lock
add mod4 = Super_L
-add mod5 = Hyper_L
+add mod5 = Hyper_L Hyper_R
--- a/bash_profile Sun May 18 14:59:11 2025 -0400
+++ b/bash_profile Wed Aug 27 16:19:24 2025 -0400
@@ -6,6 +6,7 @@
shopt -s expand_aliases
shopt -s histappend
+shopt -s checkwinsize
# Save multiline commands as a single history entry.
shopt -s cmdhist
@@ -21,12 +22,12 @@
eval "$(dircolors -b ~/.dircolors)"
fi
-D=$'\e[37m'
-RED=$'\e[31m'
-GREEN=$'\e[32m'
-ORANGE=$'\e[33m'
-BLUE=$'\e[34m'
-PINK=$'\e[35m'
+D=$'\x01\e[37m\x02'
+RED=$'\x01\e[31m\x02'
+GREEN=$'\x01\e[32m\x02'
+ORANGE=$'\x01\e[33m\x02'
+BLUE=$'\x01\e[34m\x02'
+PINK=$'\x01\e[35m\x02'
# CYAN=$'\e[36m'
function last_return_value() {
@@ -54,6 +55,10 @@
alias .....="cd ../../../.."
alias ......="cd ../../../../.."
+function mcd {
+ mkdir "$1" && cd "$1"
+}
+
alias js='cd ~/scratch'
alias :q=exit
@@ -114,3 +119,5 @@
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
+
+export PKG_CONFIG_PATH="/usr/lib/x86_64-linux-gnu/pkgconfig"
--- a/bin/abcl Sun May 18 14:59:11 2025 -0400
+++ b/bin/abcl Wed Aug 27 16:19:24 2025 -0400
@@ -1,3 +1,3 @@
#!/usr/bin/env bash
-rlwrap-lisp /usr/local/bin/abcl "$@"
+rlwrap-lisp abcl-raw "$@"
--- a/bin/abcl-raw Sun May 18 14:59:11 2025 -0400
+++ b/bin/abcl-raw Wed Aug 27 16:19:24 2025 -0400
@@ -1,4 +1,5 @@
#!/usr/bin/env bash
set -e
-/usr/local/bin/abcl --noinform "$@"
+
+java -jar /usr/local/bin/abcl.jar --noinform "$@"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/battery Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+function check_battery {
+ # TODO make this less shit
+ BATT="$1"
+ echo "$BATT"
+ cat "$BATT"/type
+
+ if test -e "$BATT"/online; then
+ echo -n "Online: "
+ cat "$BATT"/online
+ fi
+
+ if test -e "$BATT"/capacity; then
+ echo -n "Capacity (%): "
+ cat "$BATT"/capacity
+ fi
+
+ echo
+}
+
+for b in /sys/class/power_supply/*; do
+ check_battery "$b"
+done
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/bht Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+ffplay -v 0 -nodisp -autoexit ~/bin/bht.mp3
--- a/bin/bootstrap.sh Sun May 18 14:59:11 2025 -0400
+++ b/bin/bootstrap.sh Wed Aug 27 16:19:24 2025 -0400
@@ -12,11 +12,14 @@
mkdir -p ~/.local/share
mkdir -p ~/.config/fish
mkdir -p ~/.config/nvim
+mkdir -p ~/.config/htop
+mkdir -p ~/.config/mpd
mkdir -p ~/src/hg
mkdir -p ~/src/virtualenvs
mkdir -p ~/bin
mkdir -p ~/src
mkdir -p ~/.w3m
+mkdir -p ~/.weechat
ensure_link "src/dotfiles/hgrc" ".hgrc"
@@ -44,8 +47,10 @@
ensure_link "src/dotfiles/gitignore" ".gitignore"
ensure_link "src/dotfiles/gnuplot" ".gnuplot"
ensure_link "src/dotfiles/hgignore" ".hgignore"
+ensure_link "src/dotfiles/htoprc" ".config/htop/htoprc"
ensure_link "src/dotfiles/lisprc" ".lisprc"
ensure_link "src/dotfiles/lispwords" ".lispwords"
+ensure_link "src/dotfiles/mpd.conf" ".config/mpd/mpd.conf"
ensure_link "src/dotfiles/mutt" ".mutt"
ensure_link "src/dotfiles/mutt/mailcap" ".mailcap"
ensure_link "src/dotfiles/mutt/muttrc" ".muttrc"
@@ -54,13 +59,13 @@
ensure_link "src/dotfiles/sbclrc" ".sbclrc"
ensure_link "src/dotfiles/shellcheckrc" ".shellcheckrc"
ensure_link "src/dotfiles/sqliterc" ".sqliterc"
-ensure_link "src/dotfiles/stumpwmrc" ".stumpwmrc"
ensure_link "src/dotfiles/stumpwm/local-share-stumpwm" ".local/share/stumpwm"
+ensure_link "src/dotfiles/stumpwm/stumpwmrc" ".stumpwmrc"
ensure_link "src/dotfiles/vim" ".vim"
ensure_link "src/dotfiles/vim/vimrc" ".vimrc"
-ensure_link "src/dotfiles/w3m-keymap" ".w3m/keymap"
+ensure_link "src/dotfiles/vim/vimrc-minimal" ".vimrc-minimal"
ensure_link "src/dotfiles/w3m-config" ".w3m/config"
-ensure_link "src/dotfiles/weechat" ".weechat"
+ensure_link "src/dotfiles/w3m-keymap" ".w3m/keymap"
ensure_link "src/dotfiles/xbindkeysrc" ".xbindkeysrc"
ensure_link "src/dotfiles/xsessionrc" ".xsessionrc"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/boxify Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+
+import sys
+
+lines = sys.stdin.readlines()
+
+def ch(row, col):
+ if row < 0 or row >= len(lines):
+ return None
+ line = lines[row]
+ if col < 0 or col >= len(line):
+ return None
+ return line[col]
+
+for row, line in enumerate(lines):
+ for col, char in enumerate(line):
+ c = char
+ if char == '-':
+ c = '—'
+ elif char == '|':
+ c = '│'
+ elif char == '+':
+ u = ch(row-1, col) == '|'
+ d = ch(row+1, col) == '|'
+ l = ch(row, col-1) == '-'
+ r = ch(row, col+1) == '-'
+ if u and d and l and r: c = '┼'
+ elif u and d and l and not r: c = '┤'
+ elif u and d and not l and r: c = '├'
+ elif u and not d and l and r: c = '┴'
+ elif not u and d and l and r: c = '┬'
+ elif u and r: c = '└'
+ elif u and l: c = '┘'
+ elif d and l: c = '┐'
+ elif d and r: c = '┌'
+ elif u and d: c = '│'
+ elif l and r: c = '─'
+ else:
+ print(u, d, l, r,)
+ assert False
+
+ sys.stdout.write(c)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/cax Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+chmod a+x "$@"
--- a/bin/cmrm Sun May 18 14:59:11 2025 -0400
+++ b/bin/cmrm Wed Aug 27 16:19:24 2025 -0400
@@ -4,4 +4,4 @@
set -x
rm ~/.ssh/controlmaster/*
-ps auxww | grep 'ssh.*[.]sock.*mux' | f 2 | xargs kill
+ps auxww | grep -P 'ssh.*controlmaster.*mux' | f 2 | xargs kill
--- a/bin/code-to-pdf Sun May 18 14:59:11 2025 -0400
+++ b/bin/code-to-pdf Wed Aug 27 16:19:24 2025 -0400
@@ -9,12 +9,11 @@
--toc \
--header '%H - $N | | page $% of $= in file $v' \
--fancy-header=sjl \
- --font "UbuntuMono-Regular@9" \
- --header-font "UbuntuMono-Bold@12" \
+ --font "UbuntuMono-Regular@8" \
+ --header-font "UbuntuMono-Bold@10" \
--title "$TITLE" \
--baselineskip 3 \
--line-numbers \
- --highlight \
--color \
--mark-wrapped-lines=arrow \
--margins=50:50:18:50 \
@@ -25,3 +24,4 @@
# --margins=20:40:30:30 \
# --landscape \
+ # --highlight \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/cqn Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+/home/sjl/bin/lqn.core --script ~/src/lqn/bin/cqn-sh.lisp "$@"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/csv2tsv Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,20 @@
+#!/usr/bin/env python3
+
+import sys
+import csv
+
+def run_file(f):
+ w = csv.writer(sys.stdout, delimiter='\t')
+ for row in csv.reader(f):
+ w.writerow(row)
+
+def run(paths):
+ for path in paths:
+ if path == '-':
+ run_file(sys.stdin)
+ else:
+ with open(path) as f:
+ run_file(f)
+
+if __name__ == '__main__':
+ run(sys.argv[1:] or ['-'])
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/csvconcat Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+head -n1 "$1"
+tail --quiet -n +2 "$@"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/cux Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+chmod u+x "$@"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/cv Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+column -s, -t "$@"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/cvs Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+csvconcat "$@" | cv
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/date-block Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,19 @@
+#!/usr/bin/env python3
+
+import sys, datetime
+
+
+days = 'mtwhfsu'
+start_date = datetime.datetime.strptime(sys.argv[1] + ' 12:00:00 UTC', '%Y-%m-%d %H:%M:%S %Z')
+
+d = start_date
+
+for _ in range(7 * 8):
+ day = days[d.weekday()]
+ date = d.strftime('%Y-%m-%d')
+
+ if day == 'm':
+ print()
+
+ print(f"{day} {date}")
+ d += datetime.timedelta(days=1)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/descendent-pids Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+pids="$1"
+
+while [ "$pids" ]; do
+ echo $pids
+ pids=$(for p in $pids; do cat /proc/$p/task/$p/children; done)
+done | tr " " "\n"
--- a/bin/disks Sun May 18 14:59:11 2025 -0400
+++ b/bin/disks Wed Aug 27 16:19:24 2025 -0400
@@ -12,10 +12,10 @@
OUT=$(df -h)
echo "$OUT" | head -1
echo "$OUT" | head -1 | sed 's/./-/g'
-echo "$OUT" | tail +2 | grep -Pv '^/dev/loop' | grep -Pv tmpfs | sort -k6
+echo "$OUT" | tail +2 | grep -Pv '^/dev/loop' | grep -Pv tmpfs | sort -k6 | grep -P --color '/dev.*\s/$|'
echo
OUT=$(df -i)
echo "$OUT" | head -1
echo "$OUT" | head -1 | sed 's/./-/g'
-echo "$OUT" | tail +2 | grep -Pv '^/dev/loop' | grep -Pv tmpfs | sort -k6
+echo "$OUT" | tail +2 | grep -Pv '^/dev/loop' | grep -Pv tmpfs | sort -k6 | grep -P --color '/dev.*\s/$|'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/f, Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+cut -d, -f "$@"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/fnums Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+# Field Numbers, because I'm sick of manually counting
+
+head -n 1 "$@" | tr -s ',\t' '\n\n' | nl
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/forward-igv Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+slurm_node="$1"
+
+ssh -NAL 60151:localhost:60151 "$slurm_node"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/heic-conv-all Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+# from https://github.com/lokarithm/HandyBashCommands/blob/main/heic-converter/heic-converter.sh
+
+if ! command -v heif-convert &> /dev/null
+then
+ echo "heif-convert COMMAND could not be found."
+ echo "Please install 'libheif-examples' first."
+ echo "To install 'libheif-examples', run the following command:"
+ echo " sudo apt install libheif-examples"
+ exit
+else
+ fileExtension="jpg"
+
+ while getopts :p flag; do
+ case ${flag} in
+ # -p flag: convert heic files to png format instead
+ p) fileExtension="png"
+ ;;
+ esac
+ done
+
+ start_time=$(date +%s.%3N)
+
+ # look for files in current path that contains ".heic" in a case-insensitive manner
+ for file in $( ls | grep -iF ".heic")
+ do
+ echo "Converting file: $file"
+
+ # file extension of current file
+ currFileExtension=`echo $file | grep -iFo "heic"`
+ sedCommand="s/${currFileExtension}/${fileExtension}/g;s/HEIC/${fileExtension}/g"
+
+ #replace original file name by changing the extension from heic to jpg
+ outputFileName=`echo $file | sed -e $sedCommand`
+ heif-convert $file $outputFileName
+ done
+
+ end_time=$(date +%s.%3N)
+
+ elapsed=$(echo "scale=3; $end_time - $start_time" | bc)
+ echo -e "\nElapsed time: \e[32m$elapsed \e[39mmilliseconds."
+
+fi
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/hg-clone-llp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+repo="$1"
+
+h clone hsl://"$repo" "$repo"
+cd "$repo"
+llp
--- a/bin/hl Sun May 18 14:59:11 2025 -0400
+++ b/bin/hl Wed Aug 27 16:19:24 2025 -0400
@@ -1,3 +1,3 @@
#!/usr/bin/env bash
-less -R
+less -S -R "$@"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/install-figlet-fonts Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+# I'm so sick of doing this shit by hand every time I get a new computer.
+#
+# cd ~/src/
+# git clont https://github.com/xero/figlet-fonts
+
+for f in /home/sjl/src/figlet-fonts/*.fl*; do
+ dest="$(echo "$f" | cut -d/ -f 6 | tr '[:upper:] ' '[:lower:]-')"
+ sudo cp "$f" "/usr/share/figlet/$dest"
+done
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/jabref Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+jabref-bin
+
+scp ~/Sync/papers/sjl.bib "cylinder:bib-backup/sjl.bib.$(nicedate).backup"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/jqn Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+/home/sjl/bin/lqn.core --script ~/src/lqn/bin/jqn-sh.lisp "$@"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/lesss Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+less -S "$@"
--- a/bin/longcat Sun May 18 14:59:11 2025 -0400
+++ b/bin/longcat Wed Aug 27 16:19:24 2025 -0400
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
import sys
@@ -7,25 +7,24 @@
else:
n = 20
-print r'''
- /\___/\
- / \
- | o o |
- \ # |
- \ _|_ /
- / \______
- / _______ ___ \
- |_____ \ \__/
+print (r'''
+ /\___/\
+ / \
+ | o o |
+ \ # |
+ \ _|_ /
+ / \______
+ / _______ ___ \
+ |_____ \ \__/
| \__/
- | |
-''',
-print ' | |\n' * n,
-print r''' | |
+ | |'''),
+print(' | |\n' * n, end='')
+print(r''' | |
/ \
/ ____ \
| / \ |
| | | |
/ | | \
\__/ \__/
-'''
+''')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/lqn Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+/home/sjl/bin/lqn.core --script ~/src/lqn/bin/lqn-sh.lisp "$@"
--- a/bin/mark Sun May 18 14:59:11 2025 -0400
+++ b/bin/mark Wed Aug 27 16:19:24 2025 -0400
@@ -4,6 +4,8 @@
EVENT="$1"
shift
+HOST=$(hostname)
+DIR="$HOME/Sync/marks/$HOST/"
-mkdir -p "$HOME/.marks"
-echo "$(date --iso-8601=seconds --utc)" "$@" >> "$HOME/.marks/$EVENT"
+mkdir -p "$DIR"
+echo "$(date --iso-8601=seconds --utc)" "$@" >> "$DIR/$EVENT"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/minimap2-index Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+# minimap2-index \
+# /nfs/turbo/boylelab/slosh/reference-genomes/hg38_no_alt/seq/GCA_000001405.15_GRCh38_no_alt_analysis_set.fna \
+# map-ont/ \
+# -x map-ont
+reference="$1"
+shift
+output_dir="$1"
+shift
+
+# remaining opts are e.g. -x ont
+
+minimap="/nfs/turbo/boylelab/slosh/software/minimap2-2.30_x64-linux/minimap2"
+
+mkdir -p "$output_dir"
+
+{
+ echo Minimap version: "$("$minimap" --version)"
+
+ echo Indexing command: " "
+ echo \
+ "$minimap" -t 8 -d "$output_dir/index.mmi" "$reference" "$@"
+
+ echo
+ echo Output:
+ "$minimap" -t 8 -d "$output_dir/index.mmi" "$reference" "$@"
+} >> "$output_dir"/README.txt 2>&1
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/mklatex Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+NAME="$1"
+
+pdflatex "$NAME".tex
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/mklatex-bib Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+NAME="$1"
+
+pdflatex "$NAME".tex
+biber "$NAME"
+pdflatex "$NAME".tex
+pdflatex "$NAME".tex
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/mkxelatex Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+NAME="$1"
+
+xelatex "$NAME".tex
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/mkxelatex-bib Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+NAME="$1"
+
+xelatex "$NAME".tex
+bibtex "$NAME"
+xelatex "$NAME".tex
+xelatex "$NAME".tex
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/nicedate Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+date --utc +%Y-%m-%dT%H-%M-%SZ
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/pspg Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+/usr/bin/pspg --vertical-cursor --style 5 "$@"
--- a/bin/remap-hggit-mapfile Sun May 18 14:59:11 2025 -0400
+++ b/bin/remap-hggit-mapfile Wed Aug 27 16:19:24 2025 -0400
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
from __future__ import print_function
import collections
@@ -22,11 +22,11 @@
def hg(*args):
l = ["hg", "--repository", hg_dir] + list(args)
- return subprocess.check_output(l).strip()
+ return subprocess.check_output(l).strip().decode('utf-8')
def git(*args):
l = ["git", "-C", git_dir] + list(args)
- return subprocess.check_output(l).strip()
+ return subprocess.check_output(l).strip().decode('utf-8')
def find_hg_root_revision():
rev = hg('log', '-r', 'roots(all())', nodetempl).split()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/rsync-file-pattern Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+# USAGE: rsync-file-pattern PATTERN SOURCE DEST
+# rsync-file-pattern '*.ipynb' ./ vm:path/to/foo/
+
+# Goal: given a source and dest, sync any files inside the source that match
+# a given pattern. Rsync's syntax for this is utterly deranged, so I'm making
+# a script so I don't have to search for this every time.
+
+pattern="$1"
+shift
+
+source="$1"
+shift
+
+dest="$1"
+shift
+
+
+rsync -av \
+ --prune-empty-dirs \
+ --include='*/' \
+ --include="$pattern" \
+ --exclude='*' \
+ "${source}" "${dest}" \
+ "$@"
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/sb Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -e
+
+sbcl --eval '(ql:quickload :losh :silent t)' --eval '(in-package :losh-user)' "$@"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/st-apl Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+st -f 'BQN386 Unicode:style=Regular:size=12' "$@"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/subtree-cpu-usage Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+x="$1"
+pidstat -h -u -p $(descendent-pids "$x" | tr -s '\n' ',' | sed -e 's/,$//') 5 1
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/sv Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+column -s ' ' -t "$@"
--- a/bin/switch-yubikeys Sun May 18 14:59:11 2025 -0400
+++ b/bin/switch-yubikeys Wed Aug 27 16:19:24 2025 -0400
@@ -1,6 +1,4 @@
#!/usr/bin/env bash
-set -euo pipefail
-
grep -rl shadowed-private-key ~/.gnupg/private-keys-v1.d/ | xargs rm
gpg --card-status
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/t. Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+stumpish terminal-in "$(pwd)"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/temps Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+function find_names {
+ grep . /sys/class/hwmon/hwmon*/name
+}
+
+function find_temp_numbers {
+ dir="$1"
+ find "/sys/class/hwmon/$dir/" -name 'temp*_input' \
+ | xargs -n1 basename \
+ | tr -dc '0-9\n' \
+ | sort -n
+}
+
+function display_temp {
+ dir="$1"
+ n="$2"
+
+ set +e
+
+ if test -e "/sys/class/hwmon/$dir/temp${n}_label"; then
+ paste \
+ <(cat "/sys/class/hwmon/$dir/temp${n}_label") \
+ <(cat "/sys/class/hwmon/$dir/temp${n}_input" 2>&1)
+ else
+ paste \
+ <(echo '???') \
+ <(cat "/sys/class/hwmon/$dir/temp${n}_input")
+ fi
+
+ set -e
+}
+
+if test -z "${1:-}"; then
+ find_names
+else
+ dir=$(find_names | grep "$1" | cut -d / -f 5)
+
+ find_temp_numbers "$dir" | while read -r n; do
+ display_temp "$dir" "$n"
+ done
+ # paste \
+ # <(grep . "/sys/class/hwmon/$dir/"temp*_label | sort) \
+ # <(grep . "/sys/class/hwmon/$dir/"temp*_input | sort) \
+ # | tr -s ':' ' ' \
+ # | awk '/Core/ {print $2, $3, $5/1000.0 }' \
+ # | sort -n -k2
+fi
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/tqn Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+/home/sjl/bin/lqn.core --script ~/src/lqn/bin/tqn-sh.lisp "$@"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/trst Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+tr ' ' $'\t'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/tsv2csv Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,20 @@
+#!/usr/bin/env python3
+
+import sys
+import csv
+
+def run_file(f):
+ w = csv.writer(sys.stdout)
+ for row in csv.reader(f, delimiter='\t'):
+ w.writerow(row)
+
+def run(paths):
+ for path in paths:
+ if path == '-':
+ run_file(sys.stdin)
+ else:
+ with open(path) as f:
+ run_file(f)
+
+if __name__ == '__main__':
+ run(sys.argv[1:] or ['-'])
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/tv Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+column -s $'\t' -t "$@"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/tvs Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+csvconcat "$@" | tv
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/um-pass Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+echo Retrieving password, press yubikey... > /dev/tty
+pass show umich.edu/slosh | head -1 | tr -d '\n'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/unboxify Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,20 @@
+#!/usr/bin/env python3
+
+import sys
+
+for line in sys.stdin:
+ for ch in line:
+ sys.stdout.write({
+ '┤': '+',
+ '├': '+',
+ '┴': '+',
+ '┬': '+',
+ '┼': '+',
+ '└': '+',
+ '┘': '+',
+ '┐': '+',
+ '┌': '+',
+ '│': '|',
+ '—': '-',
+ }.get(ch, ch))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/watch-latex Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+NAME="$1"
+shift
+
+echo "$NAME".tex "$@" | peat "mklatex $NAME"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/watch-xelatex Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+NAME="$1"
+shift
+
+echo "$NAME".tex "$@" | peat "mkxelatex $NAME"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/watch-xelatex-bib Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+NAME="$1"
+shift
+
+echo "$NAME".tex "$@" | peat "mkxelatex-bib $NAME"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bootstrap.markdown Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,177 @@
+# Bootstrapping a New Machine
+
+Notes for myself for the next time I set up a new laptop or whatever.
+
+First, back up everything if necessary:
+
+* Faster on wired ethernet.
+* Don't forget password-store!
+* Don't forget hidden dotfiles!
+
+Install Debian. Partition everything, install nothing.
+
+Goal is now to get the dotfiles repo onto the machine and have everything
+required to run `bootstrap.sh`.
+
+Boot into a command line as root (no `sudo` yet):
+
+ apt install sudo
+ usermod -a -G sudo sjl
+ usermod -a -G netdev sjl
+
+Allow non-free packages:
+
+ vim /etc/apt/sources.list
+
+ edit to add non-free to lines, e.g.:
+ deb http://http.us.debian.org/debian bookworm main contrib non-free
+
+ apt update
+
+Install first round of stuff:
+
+ apt install \
+ nmtui sudo make
+ curl wget network-manager
+ fish rlwrap
+ mercurial git
+ xcape
+ pulseaudio alsa-utils
+ libzstd-dev libx11-dev libxft-dev
+ build-essential autoconf pkg-config
+ bzip2 udisks2 lm-sensors htop
+ fonts-ubuntu w3m atool psmisc
+ suckless-tools trash-cli
+ dunst neovim python3-dulwich
+ silversearcher-ag
+ irssi
+
+TODO: `xautolock` is no longer in the Debian repos. Figure out how to get it.
+
+TODO: Need to disable networkd, remove interface from `/etc/interfaces` and
+restart `NetworkManager` to get that in place.
+
+Set up `.ssh` and keys.
+
+Bootstrap lisp:
+
+ sudo apt install sbcl
+ git clone https://github.com/sbcl/sbcl
+ git checkout sbcl-2.3.6
+ SBCL_MAKE_JOBS=-j8 SBCL_MAKE_PARALLEL=8 sh make.sh --with-core-compression
+ sudo sh install.sh
+ sudo apt remove sbcl
+
+ curl -O https://beta.quicklisp.org/quicklisp.lisp
+ sbcl --load quicklisp.lisp
+ (quicklisp-quickstart:install :path "/home/sjl/.quicklisp/")
+
+ TODO CCL and others
+
+Get dotfiles:
+
+ hg clone ssh://hsl/repos/dotfiles
+ cd dotfiles
+ ./bin/bootstrap.sh
+
+ TODO build hg from scratch to get hg-git working maybe
+ TODO bootstrap script might fuck up the fish config because shit's already created, thanks a lot fish
+
+ git clone https://github.com/sjl/z-fish
+
+`chsh` to `/usr/bin/fish`
+
+Clone repos:
+
+ hg clone hsl://adopt adopt
+ cd adopt
+ llp
+ cd ..
+
+ hg-clone-llp for lisp
+
+ others:
+
+ 1am
+ adopt
+ beast
+ bobbin
+ boots
+ cacl
+ chancery
+ cl-d-api
+ cl-digraph
+ cl-losh
+ cl-netpbm
+ cl-pcg
+ conserve
+ d
+ dbvolve
+ docs.stevelosh.com
+ friendly-find
+ jarl
+ peat
+ t
+
+ …etc etc
+
+
+Build st:
+
+ git clone https://github.com/sjl/st
+
+Get a desktop environment up and running:
+
+ sudo apt install xinit xdm
+
+ git clone https://github.com/stumpwm/stumpwm
+
+ (ql:quickload "clx")
+ (ql:quickload "cl-ppcre")
+ (ql:quickload "alexandria")
+ (ql:quickload "xembed")
+
+ ./autogen.sh
+ ./configure
+ make
+ sudo make install
+
+ cd ~/.stumpwm.d/
+ git clone https://github.com/sjl/stumpwm-contrib modules
+ cd modules
+ git co sjl
+
+Set up `pass`:
+
+ git clone sjl@redacted:password-store .password-store
+ sudo apt install pass scdaemon pinentry-gtk2
+ gpg -a --export 5D... > public-key.txt
+ copy over
+ gpg --import < public-key.txt
+
+Pulseaudio crap:
+
+ sudo apt install pulseaudio alsa-utils
+ pulseaudio --start
+ alsamixer
+ (m to unmute, up/down to change vol)
+
+Set up Firefox:
+
+ wget 'https://download.mozilla.org/?product=firefox-latest-ssl&os=linux64&lang=en-US'
+ aunpack fi…
+ mv firefox /opt/
+ cd /opt/
+ chown -R root:root firefox
+ ln -s /opt/firefox/firefox /usr/local/bin/firefox
+ sudo apt install libgtk-3-0 libdbus-glib-1-2
+ finish the rest of this garbage https://wiki.debian.org/Firefox#Hardware_Video_Acceleration
+
+VLC (installs the world, sigh):
+
+ sudo apt install vlc
+ View > Docked Playlist to unbreak the playlist
+
+UM VPN:
+
+ TODO
--- a/dunstrc Sun May 18 14:59:11 2025 -0400
+++ b/dunstrc Wed Aug 27 16:19:24 2025 -0400
@@ -22,6 +22,7 @@
browser = xdg-open
frame_width = 1
frame_color = "#000000"
+ icon_position = off
[urgency_low]
background = "#ffffff"
--- a/fish/config.fish Sun May 18 14:59:11 2025 -0400
+++ b/fish/config.fish Wed Aug 27 16:19:24 2025 -0400
@@ -6,13 +6,14 @@
function eg; nvim ~/.gitconfig; end
function eh; nvim ~/.hgrc; end
function ei; hg -R ~/src/inventory/ pull -u; and nvim ~/src/inventory/inventory.markdown; and hg -R ~/src/inventory/ ci -m 'Update inventory'; and hg -R ~/src/inventory/ push; end
-function el; cd ~/Dropbox/life; nvim .; end
function em; nvim ~/.mutt/muttrc; end
-function es; pushd ~/src/stumpwm; e ~/.stumpwmrc; popd; end
+function es; pushd ~/src/dotfiles/stumpwm; e stumpwmrc; popd; end
function ev; nvim ~/.vimrc; end
+function evm; nvim ~/.vimrc-minimal; end
function eff; nvim ~/.config/fish/functions; end
function efh; nvim ~/.local/share/fish/fish_history; end # I have visited https://github.com/fish-shell/fish-shell/issues/862 nine thousand times and I'm fucking sick of opening a web browser to figure out where the fuck fish keeps its equivalent of ~/.bash_history
function essh; nvim ~/.ssh/config; end
+function est; nvim ~/Sync/school/TODO; end
function js; cd ~/scratch; end
function jd; cd /dump; end
@@ -33,6 +34,10 @@
end
end
+function mcd --argument dir
+ mkdir "$dir"; and cd "$dir"
+end
+
alias sin singularity
alias sm snakemake
@@ -46,6 +51,7 @@
abbr --add ai "sudo apt install"
abbr --add sc "sudo systemctl"
+abbr --add paper --set-cursor=% "cp -t . ~/Sync/papers/files/%"
# }}}
# Completions {{{
@@ -62,6 +68,7 @@
complete -c ew -w which
complete -c h -w hg
complete -c g -w git
+complete -c hl -w less
# }}}
# Bind Keys {{{
@@ -108,6 +115,7 @@
prepend_to_path "$HOME/src/hg"
prepend_to_path "$HOME/bin"
prepend_to_path "$HOME/.go/bin"
+prepend_to_path "$HOME/bin/jdk-21.0.2/bin"
set BROWSER open
@@ -118,6 +126,8 @@
set -g -x PAGER 'less -iX'
# set -g -x _JAVA_OPTIONS "-Djava.awt.headless=true"
+set -g -x JAVA_HOME "$HOME/bin/jdk-21.0.2/"
+
set -g -x GPG_TTY (tty)
# Less Colors for Man Pages
@@ -193,9 +203,23 @@
end
end
+function hg-managed
+ set --local d "$(pwd)"
+ while test "/" != "$d"
+ if test -d "$d/.hg"
+ return 0
+ end
+ set d (path dirname "$d")
+ end
+
+ return 1
+end
+
function hg_prompt
if test -z "$NOVCSPROMPT"
- hg prompt --angle-brackets $hg_promptstring 2>/dev/null
+ if hg-managed
+ hg prompt --angle-brackets $hg_promptstring 2>/dev/null
+ end
end
end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/fish/functions/el.fish Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,8 @@
+function el -d "Edit lab notes"
+ pushd ~/lab
+ ./pull.sh
+ nvim README.markdown
+ hg ci -m 'Update'
+ ./push.sh
+ popd
+end
--- a/fish/functions/eln.fish Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-function ep -d "Edit lab notes"
- pushd ~/lab
- hg pull -u
- nvim README.markdown
- hg ci -m 'Update'
- hg push
- popd
-end
--- a/fish/functions/mutt.fish Sun May 18 14:59:11 2025 -0400
+++ b/fish/functions/mutt.fish Wed Aug 27 16:19:24 2025 -0400
@@ -1,5 +1,5 @@
set -g -x MUTT_BIN (which neomutt)
function mutt
- bash --login -c "cd ~/Downloads; $MUTT_BIN \$@" custom_mutt $argv
+ bash --login -c "cd ~/downloads; $MUTT_BIN \$@" custom_mutt $argv
end
--- a/fish/functions/pj.fish Sun May 18 14:59:11 2025 -0400
+++ b/fish/functions/pj.fish Wed Aug 27 16:19:24 2025 -0400
@@ -1,9 +1,9 @@
function pj -d "Prettify JSON"
if test "$argv[1]" = "-C"
# no color
- python -m json.tool
+ python3 -m json.tool
else
- python -m json.tool | pygmentize -l json
+ python3 -m json.tool | pygmentize -l json
end
end
--- a/gitconfig Sun May 18 14:59:11 2025 -0400
+++ b/gitconfig Wed Aug 27 16:19:24 2025 -0400
@@ -94,6 +94,7 @@
fuom = "!sh -c 'git co $(git mainbranch) && git fo && git uo' -"
fuum = "!sh -c 'git co $(git mainbranch) && git fu && git uu' -"
rom = "!sh -c 'git fo && git rebase origin/$(git mainbranch)' -"
+ jom = "!sh -c 'git join origin/$(git mainbranch)' -"
recon = rebase --continue
addremove = !git add . && git add -u
@@ -138,20 +139,28 @@
path = ~/.gitconfig_local
[credential]
- helper = store
- useHttpPath = true
+ helper = store
+ useHttpPath = false
+
+[credential "https://github.com"]
+ username = sjl
[sendemail]
smtpserver = "/home/sjl/src/dotfiles/bin/msmtp-stevelosh"
[pretty]
bazel = %C(auto)%H %Cgreen%cd %C(auto)%s %Cred%al %C(auto)%d
+
[pull]
ff = only
+
[filter "lfs"]
clean = git-lfs clean -- %f
smudge = git-lfs smudge -- %f
process = git-lfs filter-process
required = true
+
[diff "lfs"]
textconv = cat
+[init]
+ defaultBranch = main
--- a/gitignore Sun May 18 14:59:11 2025 -0400
+++ b/gitignore Wed Aug 27 16:19:24 2025 -0400
@@ -17,5 +17,9 @@
*.dx64fsl
.sjl-rsync-exclude
sjl-jupyter
+sjl-sync-*.sh
+sjl-*-push.sh
+sjl-*-pull.sh
+*.sif
*.waiting
--- a/hgignore Sun May 18 14:59:11 2025 -0400
+++ b/hgignore Wed Aug 27 16:19:24 2025 -0400
@@ -8,3 +8,4 @@
*.dx64fsl
*.lx64fsl
.notmylispwords
+.ipynb_checkpoints
--- a/hgrc Sun May 18 14:59:11 2025 -0400
+++ b/hgrc Wed Aug 27 16:19:24 2025 -0400
@@ -125,9 +125,11 @@
# Some useful little aliases.
st = status -SC
+stl = !"$HG" status -SC --color=always | less -SR
? = summary
fdiff = diff -U 10000000000000
+
# Push to the git mirror at the same time.
pg = !"$HG" push; "$HG" push git
@@ -136,10 +138,9 @@
# Commit message shortcuts.
cm = commit -m
-cus = commit -m 'Update subrepository state'
-cmm = commit -m 'Merge'
-ct = commit -m 'Update TODO' TODO .TODO.done
-cb = commit -m 'Close branch' --close-branch
+cmore = commit -m 'More'
+cmerge = commit -m 'Merge'
+cus = commit -m 'Update subrepository state'
# Merge shortcuts.
mergelocal = !$HG --config ui.merge=internal:local merge $@
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/htoprc Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,63 @@
+# Beware! This file is rewritten by htop when settings are changed in the interface.
+# The parser is also very primitive, and not human-friendly.
+htop_version=3.2.2
+config_reader_min_version=3
+fields=0 48 17 18 39 2 46 47 49 1
+hide_kernel_threads=0
+hide_userland_threads=1
+hide_running_in_container=0
+shadow_other_users=0
+show_thread_names=0
+show_program_path=1
+highlight_base_name=1
+highlight_deleted_exe=1
+shadow_distribution_path_prefix=0
+highlight_megabytes=1
+highlight_threads=1
+highlight_changes=1
+highlight_changes_delay_secs=5
+find_comm_in_cmdline=1
+strip_exe_from_cmdline=1
+show_merged_command=0
+header_margin=1
+screen_tabs=1
+detailed_cpu_time=0
+cpu_count_from_one=0
+show_cpu_usage=1
+show_cpu_frequency=0
+show_cpu_temperature=0
+degree_fahrenheit=0
+update_process_names=0
+account_guest_in_cpu_meter=0
+color_scheme=0
+enable_mouse=1
+delay=10
+hide_function_bar=0
+header_layout=two_50_50
+column_meters_0=LeftCPUs2 Memory Swap
+column_meter_modes_0=1 1 1
+column_meters_1=RightCPUs2 Tasks LoadAverage Uptime
+column_meter_modes_1=1 2 2 2
+tree_view=1
+sort_key=46
+tree_sort_key=0
+sort_direction=-1
+tree_sort_direction=1
+tree_view_always_by_pid=1
+all_branches_collapsed=0
+screen:Main=PID USER PRIORITY NICE M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command
+.sort_key=PERCENT_CPU
+.tree_sort_key=PID
+.tree_view=1
+.tree_view_always_by_pid=1
+.sort_direction=-1
+.tree_sort_direction=1
+.all_branches_collapsed=0
+screen:I/O=PID USER IO_PRIORITY IO_RATE IO_READ_RATE IO_WRITE_RATE PERCENT_SWAP_DELAY PERCENT_IO_DELAY Command
+.sort_key=IO_RATE
+.tree_sort_key=PID
+.tree_view=0
+.tree_view_always_by_pid=0
+.sort_direction=-1
+.tree_sort_direction=1
+.all_branches_collapsed=0
--- a/lisp/batchcolor.lisp Sun May 18 14:59:11 2025 -0400
+++ b/lisp/batchcolor.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -10,6 +10,7 @@
;;;; Configuration ------------------------------------------------------------
(defparameter *start* 0)
(defparameter *dark* t)
+(defparameter *multi* t)
;;;; Errors -------------------------------------------------------------------
@@ -67,10 +68,14 @@
(defun find-color (string)
(gethash string *explicits*
- (let ((colors (if *dark* *dark-colors* *light-colors*)))
- (aref colors
- (mod (+ (djb2 string) *start*)
- (length colors))))))
+ (if *multi*
+ (let ((colors (if *dark* *dark-colors* *light-colors*)))
+ (aref colors
+ (mod (+ (djb2 string) *start*)
+ (length colors))))
+ (if *dark*
+ (rgb-code 5 2 3)
+ (rgb-code 2 0 2)))))
(defun ansi-color-start (color)
(format nil "~C[38;5;~Dm" #\Escape color))
@@ -128,6 +133,13 @@
(return-from parse-explicit (cons string (rgb-code r g b))))
(error 'malformed-explicit :spec spec))
+(adopt:defparameters (*option-multicolor* *option-no-multicolor*)
+ (adopt:make-boolean-options 'multicolor
+ :help "Use different colors for different matches (the default)."
+ :help-no "Use a single color for everything (a useful kludge)."
+ :long "multicolor"
+ :short #\m
+ :initial-value t))
(adopt:defparameters (*option-randomize* *option-no-randomize*)
(adopt:make-boolean-options 'randomize
@@ -187,6 +199,9 @@
"If no FILEs are given, standard input will be used. A file of - stands for ~
standard input as well.~@
~@
+ As a handy kludge, the --no-multicolor option can be used to highlight ~
+ everything in a single color.~@
+ ~@
Overlapping capturing groups are not supported because it's not clear what ~
the result should be. For example: what should ((f)oo|(b)oo) highlight when ~
matched against 'foo'? Should it highlight 'foo' in one color? The 'f' in ~
@@ -224,7 +239,9 @@
*option-no-randomize*
*option-dark*
*option-light*
- *option-explicit*)))))
+ *option-explicit*
+ *option-multicolor*
+ *option-no-multicolor*)))))
(defmacro exit-on-ctrl-c (&body body)
@@ -237,7 +254,8 @@
(setf *start* (if (gethash 'randomize options)
(random 256 (make-random-state t))
0)
- *dark* (gethash 'dark options)))
+ *dark* (gethash 'dark options)
+ *multi* (gethash 'multicolor options)))
(defun toplevel ()
(sb-ext:disable-debugger)
--- a/lisp/clhs.lisp Sun May 18 14:59:11 2025 -0400
+++ b/lisp/clhs.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -77,7 +77,8 @@
:direction :output
:if-exists :supersede
:if-does-not-exist :create)
- (prin1 symbols out))
+ (let ((*print-length* nil))
+ (prin1 symbols out)))
symbols)))
(defun symbol-map ()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/parens.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,58 @@
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (ql:quickload
+ '(:adopt :losh :parenscript)
+ :silent t))
+
+(defpackage :parens
+ (:use :cl :iterate :losh)
+ (:export :toplevel :*ui*))
+
+(defpackage :parenscript-user
+ (:use :cl :parenscript))
+
+(in-package :parens)
+
+;;;; Run ----------------------------------------------------------------------
+(defun compile-with-parenscript (in-stream out-stream)
+ (let ((parenscript:*parenscript-stream* out-stream))
+ (ps:ps-compile-stream in-stream)))
+
+(defun run ()
+ (compile-with-parenscript *standard-input* *standard-output*))
+
+
+;;;; User Interface -----------------------------------------------------------
+(defparameter *option-help*
+ (adopt:make-option 'help
+ :help "Display help and exit."
+ :long "help"
+ :short #\h
+ :reduce (constantly t)))
+
+
+(adopt:define-string *help-text*
+ "parens is a helper program to invoke the parenscript compiler for standalone ~
+ files, so you can just use parenscript without the Common Lisp bits.")
+
+(defparameter *examples*
+ '(("Compile:" . "cat foo.lisp | parens > out.js")))
+
+(defparameter *ui*
+ (adopt:make-interface
+ :name "parens"
+ :usage "[OPTIONS]"
+ :summary "standalone parenscript compiler"
+ :help *help-text*
+ :examples *examples*
+ :contents (list *option-help*)))
+
+
+(defun toplevel ()
+ (multiple-value-bind (arguments options) (adopt:parse-options-or-exit *ui*)
+ (handler-case
+ (if (gethash 'help options)
+ (adopt:print-help-and-exit *ui*)
+ (progn (assert (null arguments))
+ (run)))
+ (error (e) (adopt:print-error-and-exit e)))))
+
--- a/lisp/weather.lisp Sun May 18 14:59:11 2025 -0400
+++ b/lisp/weather.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -1,6 +1,6 @@
(eval-when (:compile-toplevel :load-toplevel :execute)
(ql:quickload
- '(:adopt :with-user-abort :split-sequence :iterate :losh :drakma :jarl :flexi-streams :local-time)
+ '(:adopt :with-user-abort :split-sequence :iterate :losh :drakma :jarl :flexi-streams :local-time :safe-read)
:silent t))
(defpackage :weather
@@ -10,6 +10,10 @@
(in-package :weather)
+(defpackage :conf
+ (:use)
+ (:export :zip-code))
+
;;;; Zip Codes ----------------------------------------------------------------
(defun load-zip-code-coordinates ()
@@ -28,27 +32,27 @@
;;;; Config -------------------------------------------------------------------
(defvar *api-token* nil)
+(defvar *zip-code* nil)
(defun load-config ()
(let* ((token-paths (uiop:xdg-config-pathnames "weather/api-token"))
(config-paths (uiop:xdg-config-pathnames "weather/config"))
(token-path (find-if #'probe-file token-paths))
(config-paths (reverse (remove-if-not #'probe-file config-paths))))
- (declare (ignore config-paths)) ; TODO
(if (null token-path)
(error "Cannot find API token file at any of the following paths:~2%~
~{ ~A~%~}~%~
Visit https://home.openweathermap.org/api_keys to get one."
token-paths)
(setf *api-token* (string-trim '(#\space #\newline)
- (alexandria:read-file-into-string token-path))))))
+ (alexandria:read-file-into-string token-path))))
+ (dolist (config-path config-paths)
+ (with-open-file (f config-path)
+ (let ((config (safe-read:safe-read f (list :conf))))
+ (setf *zip-code* (getf config 'conf:zip-code *zip-code*)))))))
;;;; OpenWeatherMap -----------------------------------------------------------
-;;; TODO Switch to weather.gov some day to get my taxes' worth, e.g.
-;;; https://forecast.weather.gov/MapClick.php?lat=43.1577&lon=-77.6066&FcstType=digitalDWML
-;;; Sadly this is XML.
-
(defclass* response ()
((hourly :json (vector hour)))
(:metaclass jarl:json-class))
@@ -84,8 +88,9 @@
(defun query-weather (latitude longitude)
+ (assert *api-token* () "API token not loaded, call (load-config) first.")
(multiple-value-bind (body status headers uri stream needs-close reason)
- (drakma:http-request (format nil "https://api.openweathermap.org/data/2.5/onecall")
+ (drakma:http-request (format nil "https://api.openweathermap.org/data/3.0/onecall")
:redirect t
:parameters `(("lat" . ,latitude)
("lon" . ,longitude)
@@ -105,13 +110,16 @@
(defun display-hour (hour &optional force-date)
(let* ((ts (timestamp hour))
- (h (local-time:timestamp-hour ts)))
- (format t "~12A ~2,'0D:00 ~4D°F ~4D% ~{~A~^, ~}~%"
+ (h (local-time:timestamp-hour ts))
+ (temperature (temperature hour))
+ (feels-like (feels-like hour)))
+ (format t "~12A ~2,'0D:00 ~4D°F ~4D°f ~4D% ~{~A~^, ~}~%"
(if (or force-date (zerop h))
(ymd ts)
"")
h
- (round (temperature hour))
+ (round temperature)
+ (round feels-like)
(round (* 100 (precipitation hour)))
(map 'list #'description (weather hour)))))
@@ -179,7 +187,7 @@
(adopt:print-help-and-exit *ui*)
(progn
(load-config)
- (run (or (first arguments) "48105")
+ (run (or (first arguments) *zip-code*)
:hours (gethash 'hours options))))
(error (e) (adopt:print-error-and-exit e))))))
--- a/lisprc Sun May 18 14:59:11 2025 -0400
+++ b/lisprc Wed Aug 27 16:19:24 2025 -0400
@@ -22,6 +22,9 @@
(defglobal ,var nil)
(setf ,var ,val)))
+(defun :vlime ()
+ (load "~/src/dotfiles/vim/bundle/vlime/lisp/start-vlime.lisp"))
+
;;;; Scratch Marker -----------------------------------------------------------
(defun sharp-semicolon-reader (stream sub-char numarg)
--- a/lispwords Sun May 18 14:59:11 2025 -0400
+++ b/lispwords Wed Aug 27 16:19:24 2025 -0400
@@ -92,6 +92,7 @@
(1 multiple-value-bind*)
(1 do-repeat do-range do-irange do-ring-buffer do-vector do-file do-hash-set)
(1 timing profile-when)
+(1 recase)
; qtools
(1 qtenumcase)
@@ -117,7 +118,7 @@
; ppcre
(2 register-groups-bind do-register-groups)
-(1 do-scans)
+(1 do-scans do-matches)
; lparallel
(1 pdotimes)
@@ -145,6 +146,19 @@
; boots
(1 event-case)
+(2 canvas)
; parsnip
(1 let!)
+
+; sqlite
+(1 execute-non-query execute-script execute-to-list execute-single execute-non-query/named execute-single/named execute-to-list/named)
+
+; easy-routes
+(3 defroute)
+
+; djula
+(2 render-template*)
+
+; cl-who
+(1 who)
Binary file man/index.db has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mbsyncrc Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,81 @@
+# tweaked https://raw.githubusercontent.com/bm4cs/dots/Far/.mbsyncrc
+
+IMAPAccount fastmail
+Host imap.fastmail.com
+Port 993
+User steve@stevelosh.com
+PassCmd "pass show mutt/ouroboros/steve@stevelosh.com | head -n 1"
+SSLType IMAPS
+SSLVersion TLSv1.2
+CertificateFile /etc/ssl/certs/ca-certificates.crt
+
+# Remote
+IMAPStore mail-remote
+Account fastmail
+
+# Local
+MaildirStore mail-local
+Subfolders Verbatim
+Path ~/.mail2/sjl/
+Inbox ~/.mail2/sjl/INBOX
+
+# CONNECTIONS SPECIFY LINKS BETWEEN REMOTE AND LOCAL FOLDERS
+# CONNECTIONS ARE SPECIFIED USING PATTERNS, WHICH MATCH REMOTE MAIl
+# FOLDERS. SOME COMMONLY USED PATTERS INCLUDE:
+#
+# 1 "*" TO MATCH EVERYTHING
+# 2 "!DIR" TO EXCLUDE "DIR"
+# 3 "DIR" TO MATCH DIR
+
+Channel mail-inbox
+Far :mail-remote:
+Near :mail-local:
+Patterns INBOX
+Create Near
+Sync All
+Expunge Both
+SyncState *
+
+Channel mail-sent
+Far :mail-remote:
+Near :mail-local:
+Patterns "Sent"
+Create Both
+Sync All
+Expunge Both
+SyncState *
+
+Channel mail-spam
+Far :mail-remote:
+Near :mail-local:
+Patterns "Uncaught Spam"
+Create Near
+Sync All
+Expunge Both
+SyncState *
+
+Channel mail-drafts
+Far :mail-remote:
+Near :mail-local:
+Patterns "Drafts"
+Create Near
+Sync All
+Expunge Both
+SyncState *
+
+Channel mail-archive
+Far :mail-remote:
+Near :mail-local:
+Patterns "Arc*"
+Create Near
+Sync All
+Expunge Both
+SyncState *
+
+Group mail
+Channel mail-inbox
+Channel mail-spam
+Channel mail-drafts
+Channel mail-sent
+Channel mail-archive
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/md-to-html/Makefile Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,6 @@
+%.html: %.markdown template.html Makefile
+ pandoc \
+ --template template.html \
+ --table-of-contents \
+ -f markdown -t html \
+ "$<" > "$@"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/md-to-html/foo.html Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,57 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Some Test</title>
+ <style type="text/css">
+ body {
+ margin:40px auto;
+ max-width: 800px;
+ line-height:1.5;
+ font-size:16px;
+ color: #222;
+ padding:0 10px;
+ font-family: serif;
+ }
+ h1,h2,h3 {
+ line-height:1.2
+ }
+ code {
+ font-size: 14px;
+ background-color: #eee;
+ border: 1px solid #ccc;
+ padding: 1px;
+ }
+ pre code {
+ border: none;
+ padding: none;
+ }
+ pre {
+ font-size: 14px;
+ background-color: #eee;
+ border: 1px solid #ccc;
+ padding: 1px;
+ overflow-x: auto;
+ margin: 24px 20px 24px 0px;
+ padding: 4px 10px;
+ }
+ a {
+ color: #e50053;
+ }
+
+ h1 {
+ text-align: center;
+ }
+ </style>
+ </head>
+ <body>
+ <h1>Some Test</h1>
+<ul>
+<li><a href="#example">Example</a></li>
+</ul>
+<h2 id="example">Example</h2>
+<p>This is a test.</p>
+<pre><code>Some code.</code></pre>
+<p>Done testing.</p>
+ </body>
+</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/md-to-html/foo.markdown Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,11 @@
+---
+title: Some Test
+---
+
+## Example
+
+This is a test.
+
+ Some code.
+
+Done testing.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/md-to-html/template.html Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,63 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>$title$</title>
+ <style type="text/css">
+ body {
+ margin:40px auto;
+ max-width: 800px;
+ line-height:1.5;
+ font-size:16px;
+ color: #222;
+ padding:0 10px;
+ font-family: serif;
+ }
+ h1,h2,h3 {
+ line-height:1.2
+ }
+ code {
+ font-size: 14px;
+ background-color: #eee;
+ border: 1px solid #ccc;
+ padding: 1px 4px;
+ }
+ pre code {
+ border: 0;
+ padding: 0;
+ }
+ pre {
+ font-size: 14px;
+ background-color: #eee;
+ border: 1px solid #ccc;
+ padding: 1px;
+ overflow-x: auto;
+ margin: 24px 20px 24px 0px;
+ padding: 4px 10px;
+ }
+ a {
+ color: #e50053;
+ }
+
+ h1 {
+ text-align: center;
+ }
+ #table-of-contents > ul {
+ padding-left: 0;
+ }
+ #table-of-contents li {
+ list-style-type: none;
+ }
+ </style>
+ </head>
+ <body>
+ <h1>$title$</h1>
+ <details>
+ <summary>Table of contents</summary>
+ <div id="table-of-contents">$toc$</div>
+ </details>
+
+$body$
+
+ </body>
+</html>
--- a/remote/bash_profile Sun May 18 14:59:11 2025 -0400
+++ b/remote/bash_profile Wed Aug 27 16:19:24 2025 -0400
@@ -8,6 +8,7 @@
shopt -s expand_aliases
shopt -s histappend
+shopt -s checkwinsize
# Save multiline commands as a single history entry.
shopt -s cmdhist
@@ -23,12 +24,12 @@
eval "$(dircolors -b ~/.dircolors)"
fi
-D=$'\e[37m'
-RED=$'\e[31m'
-GREEN=$'\e[32m'
-ORANGE=$'\e[33m'
-BLUE=$'\e[34m'
-PINK=$'\e[35m'
+D=$'\x01\e[37m\x02'
+RED=$'\x01\e[31m\x02'
+GREEN=$'\x01\e[32m\x02'
+ORANGE=$'\x01\e[33m\x02'
+BLUE=$'\x01\e[34m\x02'
+PINK=$'\x01\e[35m\x02'
# CYAN=$'\e[36m'
function last_return_value() {
@@ -42,13 +43,22 @@
history | grep "$@" | tac | f 2-
}
+function mcd {
+ mkdir "$1" && cd "$1"
+}
+
if [ -n "$SSH_CLIENT" ] || [ -n "$SSH_TTY" ]; then
HOST_COLOR="$BLUE"
else
HOST_COLOR="$ORANGE"
fi
-export PS1='\n${PINK}\u ${D}at ${HOST_COLOR}\h ${D}in ${GREEN}\w${D} $(last_return_value)$ '
+PS1_HOST='\h'
+if test -e ~/.cosmetic_hostname; then
+ PS1_HOST=$(cat ~/.cosmetic_hostname)
+fi
+
+export PS1='\n${PINK}\u ${D}at ${HOST_COLOR}'"$PS1_HOST"' ${D}in ${GREEN}\w${D} $(last_return_value)$ '
alias ..="cd .."
alias ...="cd ../.."
@@ -56,8 +66,6 @@
alias .....="cd ../../../.."
alias ......="cd ../../../../.."
-alias js='cd ~/scratch'
-
alias :q=exit
alias :qa=exit
alias :wqa=exit
@@ -101,3 +109,12 @@
export GPG_TTY
export EDITOR=vim
+
+export _Z_CMD=j
+export _Z_DATA=$HOME/.z-bash
+
+. $HOME/src/dotfiles/z.sh
+
+if [ -f $HOME/.bash_profile_sjl_local ]; then
+ source $HOME/.bash_profile_sjl_local
+fi
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/arblogs Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+sudo journalctl -fu arb_sequencing_worker "$@" \
+ | batchcolor '(ERROR|WARNING|INFO|[0-9]{8}_[0-9]{4}_[-A-Z0-9]+_[A-Z0-9]+_[a-z0-9]{8}|Checking for.* in )' \
+ | cut -d ' ' -f 6-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/bclogs Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+sudo journalctl -fu arb_basecalling_worker "$@" \
+ | batchcolor '(ERROR|WARNING|INFO|[0-9]{8}_[0-9]{4}_[-A-Z0-9]+_[A-Z0-9]+_[a-z0-9]{8}|Checking for.* in )' \
+ | cut -d ' ' -f 6-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/cax Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1 @@
+../../bin/cax
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/csv2tsv Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1 @@
+../../bin/csv2tsv
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/csvconcat Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1 @@
+../../bin/csvconcat
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/cux Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1 @@
+../../bin/cux
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/cv Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1 @@
+../../bin/cv
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/cvs Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1 @@
+../../bin/cvs
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/f, Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1 @@
+../../bin/f,
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/fnums Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1 @@
+../../bin/fnums
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/lesss Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1 @@
+../../bin/lesss
\ No newline at end of file
--- a/remote/bin/lines Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-../../lisp/bin/lines
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/minimap2-index Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1 @@
+../../bin/minimap2-index
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/mksb Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+name="$1"
+
+read -p "Account? "
+acct="$REPLY"
+
+read -p "Hours? "
+hours="$REPLY"
+
+read -p "CPUs? "
+cpus="$REPLY"
+
+read -p "Memory (gb)? "
+mem_gb="$REPLY"
+mem_mb=$(( mem_gb * 1024 ))
+
+cat << EOF > "$name".sbat
+#!/usr/bin/env bash
+
+#SBATCH --job-name=$name
+#SBATCH --mail-type=END
+#SBATCH --nodes=1
+#SBATCH --ntasks-per-node=1
+#SBATCH --cpus-per-task=$cpus
+#SBATCH --mem=$mem_mb
+#SBATCH --time=$hours:00:00
+#SBATCH --account=$acct
+#SBATCH --partition=standard
+#SBATCH --output=slurm-%x-%j.log
+#SBATCH --export=NONE
+
+set -euo pipefail
+
+my_job_header
+echo
+echo Arguments: "\$@"
+echo
+
+EOF
+
+chmod u+x "$name.sbat"
+
+"$EDITOR" "$name.sbat"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/nicedate Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1 @@
+../../bin/nicedate
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/ont-basecalling-model-info Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+bam_file="$1"
+
+module load Bioinformatics samtools >/dev/null 2>&1
+
+# view all the SAM headers
+# only look at @RG
+# pull out the basecall_model=… field
+# coalesce
+
+samtools view -H "$bam_file" \
+ | grep -P '^@RG' \
+ | grep -P -o $'basecall_model=[^ \t]+' \
+ | sort | uniq -c
+
+samtools view -H "$bam_file" \
+ | grep -P '^@RG' \
+ | grep -P -o $'modbase_models=[^ \t]+' \
+ | sort | uniq -c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/potential-to-bed Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+cut -f 1-4,8- | awk -F'\t' -v OFS='\t' '{print $1, $2, $3, $4 " " $5}'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/run-igv Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+module load openjdk
+~/src/igv/build/IGV-dist/igv.sh
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/slurm-shell Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+read -p "Account? "
+acct="$REPLY"
+
+read -p "Hours? "
+hours="$REPLY"
+
+read -p "CPUs? "
+cpus="$REPLY"
+
+read -p "Memory (gb)? "
+mem_gb="$REPLY"
+mem_kb=$(( mem_gb * 1024 * 1024 / cpus ))
+
+read -p "X11? "
+case $REPLY in
+ y | yes | Y | YES)
+ x11="--x11"
+ ;;
+
+ *)
+ x11=""
+ ;;
+esac
+
+if test -z "$x11"; then
+ exec salloc --account="$acct" --nodes=1 --ntasks-per-node=1 --mem-per-cpu="$mem_kb"K --cpus-per-task="$cpus" --time="$hours":00:00
+else
+ exec salloc --account="$acct" --nodes=1 --ntasks-per-node=1 --mem-per-cpu="$mem_kb"K --cpus-per-task="$cpus" --time="$hours":00:00 --x11
+fi
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/sv Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1 @@
+../../bin/sv
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/temps Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1 @@
+../../bin/temps
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/trst Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1 @@
+../../bin/trst
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/tsv2csv Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1 @@
+../../bin/tsv2csv
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/tv Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1 @@
+../../bin/tv
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/bin/tvs Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1 @@
+../../bin/tvs
\ No newline at end of file
--- a/remote/bootstrap.sh Sun May 18 14:59:11 2025 -0400
+++ b/remote/bootstrap.sh Wed Aug 27 16:19:24 2025 -0400
@@ -21,12 +21,21 @@
mkdir -p ~/.config/fish
mkdir -p ~/.config/nvim
+mkdir -p ~/.config/htop
mkdir -p ~/bin
+mkdir -p ~/.vim
+mkdir -p ~/.terminfo/s
+ensure_link "src/dotfiles/bash_profile" ".bash_profile"
+ensure_link "src/dotfiles/config.fish" ".config/fish/config.fish"
+ensure_link "src/dotfiles/dircolors" ".dircolors"
+ensure_link "src/dotfiles/ffignore" ".ffignore"
ensure_link "src/dotfiles/gitconfig" ".gitconfig"
ensure_link "src/dotfiles/gitignore" ".gitignore"
-ensure_link "src/dotfiles/bash_profile" ".bash_profile"
-ensure_link "src/dotfiles/dircolors" ".dircolors"
-ensure_link "src/dotfiles/ffignore" ".ffignore"
-ensure_link "src/dotfiles/config.fish" ".config/fish/config.fish"
+ensure_link "src/dotfiles/htoprc" ".config/htop/htoprc"
ensure_link "src/dotfiles/hushlogin" ".hushlogin"
+ensure_link "src/dotfiles/st" ".terminfo/s/st"
+ensure_link "src/dotfiles/st-256color" ".terminfo/s/st-256color"
+ensure_link "src/dotfiles/vim-colors" ".vim/colors"
+ensure_link "src/dotfiles/vimrc" ".vimrc"
+ensure_link "src/dotfiles/vimrc-remote-local" ".vimrc_remote_local"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/htoprc Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1 @@
+../htoprc
\ No newline at end of file
Binary file remote/st has changed
Binary file remote/st-256color has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/vim-colors Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1 @@
+../vim/bundle/badwolf/colors/
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/vimrc Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1 @@
+../vim/vimrc-minimal
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/vimrc-remote-local Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1 @@
+colorscheme badwolf
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/remote/z.sh Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,267 @@
+# Copyright (c) 2009 rupa deadwyler. Licensed under the WTFPL license, Version 2
+
+# maintains a jump-list of the directories you actually use
+#
+# INSTALL:
+# * put something like this in your .bashrc/.zshrc:
+# . /path/to/z.sh
+# * cd around for a while to build up the db
+# * PROFIT!!
+# * optionally:
+# set $_Z_CMD in .bashrc/.zshrc to change the command (default z).
+# set $_Z_DATA in .bashrc/.zshrc to change the datafile (default ~/.z).
+# set $_Z_MAX_SCORE lower to age entries out faster (default 9000).
+# set $_Z_NO_RESOLVE_SYMLINKS to prevent symlink resolution.
+# set $_Z_NO_PROMPT_COMMAND if you're handling PROMPT_COMMAND yourself.
+# set $_Z_EXCLUDE_DIRS to an array of directories to exclude.
+# set $_Z_OWNER to your username if you want use z while sudo with $HOME kept
+#
+# USE:
+# * z foo # cd to most frecent dir matching foo
+# * z foo bar # cd to most frecent dir matching foo and bar
+# * z -r foo # cd to highest ranked dir matching foo
+# * z -t foo # cd to most recently accessed dir matching foo
+# * z -l foo # list matches instead of cd
+# * z -e foo # echo the best match, don't cd
+# * z -c foo # restrict matches to subdirs of $PWD
+# * z -x # remove the current directory from the datafile
+# * z -h # show a brief help message
+
+[ -d "${_Z_DATA:-$HOME/.z}" ] && {
+ echo "ERROR: z.sh's datafile (${_Z_DATA:-$HOME/.z}) is a directory."
+}
+
+_z() {
+
+ local datafile="${_Z_DATA:-$HOME/.z}"
+
+ # if symlink, dereference
+ [ -h "$datafile" ] && datafile=$(readlink "$datafile")
+
+ # bail if we don't own ~/.z and $_Z_OWNER not set
+ [ -z "$_Z_OWNER" -a -f "$datafile" -a ! -O "$datafile" ] && return
+
+ _z_dirs () {
+ [ -f "$datafile" ] || return
+
+ local line
+ while read line; do
+ # only count directories
+ [ -d "${line%%\|*}" ] && echo "$line"
+ done < "$datafile"
+ return 0
+ }
+
+ # add entries
+ if [ "$1" = "--add" ]; then
+ shift
+
+ # $HOME and / aren't worth matching
+ [ "$*" = "$HOME" -o "$*" = '/' ] && return
+
+ # don't track excluded directory trees
+ if [ ${#_Z_EXCLUDE_DIRS[@]} -gt 0 ]; then
+ local exclude
+ for exclude in "${_Z_EXCLUDE_DIRS[@]}"; do
+ case "$*" in "$exclude"*) return;; esac
+ done
+ fi
+
+ # maintain the data file
+ local tempfile="$datafile.$RANDOM"
+ local score=${_Z_MAX_SCORE:-9000}
+ _z_dirs | awk -v path="$*" -v now="$(date +%s)" -v score=$score -F"|" '
+ BEGIN {
+ rank[path] = 1
+ time[path] = now
+ }
+ $2 >= 1 {
+ # drop ranks below 1
+ if( $1 == path ) {
+ rank[$1] = $2 + 1
+ time[$1] = now
+ } else {
+ rank[$1] = $2
+ time[$1] = $3
+ }
+ count += $2
+ }
+ END {
+ if( count > score ) {
+ # aging
+ for( x in rank ) print x "|" 0.99*rank[x] "|" time[x]
+ } else for( x in rank ) print x "|" rank[x] "|" time[x]
+ }
+ ' 2>/dev/null >| "$tempfile"
+ # do our best to avoid clobbering the datafile in a race condition.
+ if [ $? -ne 0 -a -f "$datafile" ]; then
+ env rm -f "$tempfile"
+ else
+ [ "$_Z_OWNER" ] && chown $_Z_OWNER:"$(id -ng $_Z_OWNER)" "$tempfile"
+ env mv -f "$tempfile" "$datafile" || env rm -f "$tempfile"
+ fi
+
+ # tab completion
+ elif [ "$1" = "--complete" -a -s "$datafile" ]; then
+ _z_dirs | awk -v q="$2" -F"|" '
+ BEGIN {
+ q = substr(q, 3)
+ if( q == tolower(q) ) imatch = 1
+ gsub(/ /, ".*", q)
+ }
+ {
+ if( imatch ) {
+ if( tolower($1) ~ q ) print $1
+ } else if( $1 ~ q ) print $1
+ }
+ ' 2>/dev/null
+
+ else
+ # list/go
+ local echo fnd last list opt typ
+ while [ "$1" ]; do case "$1" in
+ --) while [ "$1" ]; do shift; fnd="$fnd${fnd:+ }$1";done;;
+ -*) opt=${1:1}; while [ "$opt" ]; do case ${opt:0:1} in
+ c) fnd="^$PWD $fnd";;
+ e) echo=1;;
+ h) echo "${_Z_CMD:-z} [-cehlrtx] args" >&2; return;;
+ l) list=1;;
+ r) typ="rank";;
+ t) typ="recent";;
+ x) sed -i -e "\:^${PWD}|.*:d" "$datafile";;
+ esac; opt=${opt:1}; done;;
+ *) fnd="$fnd${fnd:+ }$1";;
+ esac; last=$1; [ "$#" -gt 0 ] && shift; done
+ [ "$fnd" -a "$fnd" != "^$PWD " ] || list=1
+
+ # if we hit enter on a completion just go there
+ case "$last" in
+ # completions will always start with /
+ /*) [ -z "$list" -a -d "$last" ] && builtin cd "$last" && return;;
+ esac
+
+ # no file yet
+ [ -f "$datafile" ] || return
+
+ local cd
+ cd="$( < <( _z_dirs ) awk -v t="$(date +%s)" -v list="$list" -v typ="$typ" -v q="$fnd" -F"|" '
+ function frecent(rank, time) {
+ # relate frequency and time
+ dx = t - time
+ return int(10000 * rank * (3.75/((0.0001 * dx + 1) + 0.25)))
+ }
+ function output(matches, best_match, common) {
+ # list or return the desired directory
+ if( list ) {
+ if( common ) {
+ printf "%-10s %s\n", "common:", common > "/dev/stderr"
+ }
+ cmd = "sort -n >&2"
+ for( x in matches ) {
+ if( matches[x] ) {
+ printf "%-10s %s\n", matches[x], x | cmd
+ }
+ }
+ } else {
+ if( common && !typ ) best_match = common
+ print best_match
+ }
+ }
+ function common(matches) {
+ # find the common root of a list of matches, if it exists
+ for( x in matches ) {
+ if( matches[x] && (!short || length(x) < length(short)) ) {
+ short = x
+ }
+ }
+ if( short == "/" ) return
+ for( x in matches ) if( matches[x] && index(x, short) != 1 ) {
+ return
+ }
+ return short
+ }
+ BEGIN {
+ gsub(" ", ".*", q)
+ hi_rank = ihi_rank = -9999999999
+ }
+ {
+ if( typ == "rank" ) {
+ rank = $2
+ } else if( typ == "recent" ) {
+ rank = $3 - t
+ } else rank = frecent($2, $3)
+ if( $1 ~ q ) {
+ matches[$1] = rank
+ } else if( tolower($1) ~ tolower(q) ) imatches[$1] = rank
+ if( matches[$1] && matches[$1] > hi_rank ) {
+ best_match = $1
+ hi_rank = matches[$1]
+ } else if( imatches[$1] && imatches[$1] > ihi_rank ) {
+ ibest_match = $1
+ ihi_rank = imatches[$1]
+ }
+ }
+ END {
+ # prefer case sensitive
+ if( best_match ) {
+ output(matches, best_match, common(matches))
+ exit
+ } else if( ibest_match ) {
+ output(imatches, ibest_match, common(imatches))
+ exit
+ }
+ exit(1)
+ }
+ ')"
+
+ if [ "$?" -eq 0 ]; then
+ if [ "$cd" ]; then
+ if [ "$echo" ]; then echo "$cd"; else builtin cd "$cd"; fi
+ fi
+ else
+ return $?
+ fi
+ fi
+}
+
+alias ${_Z_CMD:-z}='_z 2>&1'
+
+[ "$_Z_NO_RESOLVE_SYMLINKS" ] || _Z_RESOLVE_SYMLINKS="-P"
+
+if type compctl >/dev/null 2>&1; then
+ # zsh
+ [ "$_Z_NO_PROMPT_COMMAND" ] || {
+ # populate directory list, avoid clobbering any other precmds.
+ if [ "$_Z_NO_RESOLVE_SYMLINKS" ]; then
+ _z_precmd() {
+ (_z --add "${PWD:a}" &)
+ : $RANDOM
+ }
+ else
+ _z_precmd() {
+ (_z --add "${PWD:A}" &)
+ : $RANDOM
+ }
+ fi
+ [[ -n "${precmd_functions[(r)_z_precmd]}" ]] || {
+ precmd_functions[$(($#precmd_functions+1))]=_z_precmd
+ }
+ }
+ _z_zsh_tab_completion() {
+ # tab completion
+ local compl
+ read -l compl
+ reply=(${(f)"$(_z --complete "$compl")"})
+ }
+ compctl -U -K _z_zsh_tab_completion _z
+elif type complete >/dev/null 2>&1; then
+ # bash
+ # tab completion
+ complete -o filenames -C '_z --complete "$COMP_LINE"' ${_Z_CMD:-z}
+ [ "$_Z_NO_PROMPT_COMMAND" ] || {
+ # populate directory list. avoid clobbering other PROMPT_COMMANDs.
+ grep "_z --add" <<< "$PROMPT_COMMAND" >/dev/null || {
+ PROMPT_COMMAND="$PROMPT_COMMAND"$'\n''(_z --add "$(command pwd '$_Z_RESOLVE_SYMLINKS' 2>/dev/null)" 2>/dev/null &);'
+ }
+ }
+fi
--- a/sqliterc Sun May 18 14:59:11 2025 -0400
+++ b/sqliterc Wed Aug 27 16:19:24 2025 -0400
@@ -1,5 +1,7 @@
.timer on
.headers on
-.mode tabs
+.mode columns
+
+.prompt "\nsqlite> " "> "
.nullvalue ∅'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/applications.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,37 @@
+(in-package :stumpwm-user)
+
+(defcommand igv () ()
+ (run-or-raise "igv" '(:class "org-broad-igv-ui-Main")))
+
+(defcommand spotify () ()
+ (run-or-raise "spotify" '(:class "Spotify")))
+
+(defcommand zoom-meeting () ()
+ (run-or-raise "echom 'No meeting running.'" '(:class "zoom" :title "Meeting")))
+
+(defcommand files () ()
+ (run-shell-command "open $HOME"))
+
+(defcommand browser () ()
+ (run-or-raise "firefox" '(:class "firefox")))
+
+(defcommand vlc () ()
+ (run-or-raise "vlc" '(:class "vlc")))
+
+(defcommand terminal () ()
+ (run-shell-command (format nil "st -f 'Ubuntu Mono:size=~D'" *terminal-font-size*)))
+
+(defcommand terminal-in (path) ((:string "Working directory: "))
+ (run-shell-command
+ (format nil "st -f 'Ubuntu Mono:size=~D' env -C '~A' fish" ; todo actually escape this
+ *terminal-font-size* path)))
+
+(defcommand terminal-apl () ()
+ (run-shell-command "st -f 'BQN386 Unicode:style=Regular:size=12'"))
+
+(defcommand gcontrol () ()
+ (run-or-raise "gcontrol" '(:class "Gnome-control-center")))
+
+(defcommand papers () ()
+ (run-or-raise "jabref" '(:class "org.jabref.gui.MainApplication")))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/bioinf.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,74 @@
+(in-package :stumpwm-user)
+
+
+(defun random-base ()
+ (random-elt "GCAT"))
+
+(defun random-dna-string (n)
+ (let ((result (make-string n)))
+ (dotimes (i n)
+ (setf (char result i) (random-base)))
+ result))
+
+(defcommand random-dna (n) ((:integer "Length: "))
+ (pbcopy (random-dna-string n)))
+
+(defun random-fasta-string (entries entry-length)
+ (str:join #\newline
+ (loop :for i :from 0 :below entries
+ :collect (format nil ">seq ~D" i)
+ :collect (random-dna-string entry-length))))
+
+(defcommand random-fasta
+ (entries entry-length)
+ ((:integer "Entries: ")
+ (:integer "Entry Length: "))
+ (check-type entries (integer 1 *))
+ (check-type entry-length (integer 1 *))
+ (pbcopy (random-fasta-string entries entry-length)))
+
+
+(defun kmers-of (k seq)
+ (loop :for start :from 0
+ :for end :from k :to (length seq)
+ :collect (subseq seq start end)))
+
+(defun kmerized-string (k seq)
+ (with-output-to-string (s)
+ (loop :for i :from 0
+ :for kmer :in (kmers-of k seq)
+ :do (format s "~,,V,@A~%" i kmer))))
+
+(defcommand kmerize (k) ((:integer "k: "))
+ (pbcopy (kmerized-string k (pbpaste))))
+
+(defun reverse-complement (seq)
+ (nreverse (map 'string (lambda (base)
+ (case base
+ (#\A #\T)
+ (#\C #\G)
+ (#\G #\C)
+ (#\T #\A)
+ (#\N #\N)
+ (#\a #\t)
+ (#\c #\g)
+ (#\g #\c)
+ (#\t #\a)
+ (#\n #\n)
+ (t #\?)))
+ seq)))
+
+(defcommand revcomp () ()
+ (pbcopy (reverse-complement (pbpaste))))
+
+(defcommand rev () ()
+ (pbcopy (reverse (pbpaste))))
+
+
+#; Scratch --------------------------------------------------------------------
+
+(random-fasta-string 10 100)
+
+(format nil "~,,99,@A" "hello")
+
+(kmers-of 3 "ACTTAC")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/brightness.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,26 @@
+(in-package :stumpwm-user)
+
+(defparameter *brightness-values* #(0 1 5 10 20 30 40 55 70 85 100))
+(defvar *brightness-index* 5)
+
+(defun brightness ()
+ (aref *brightness-values* *brightness-index*))
+
+(defun set-brightness (value)
+ (run-and-echo-shell-command
+ (hostcase
+ ((:gro :juss) (format nil "light -S ~D" value))
+ (t (message "Not sure how to set brightness on this machine.")))))
+
+(defun rotate-brightness (delta)
+ (setf *brightness-index*
+ (mod+ *brightness-index* delta (length *brightness-values*)))
+ (set-brightness (brightness)))
+
+
+(defcommand rotate-brightness-up () ()
+ (rotate-brightness 1))
+
+(defcommand rotate-brightness-down () ()
+ (rotate-brightness -1))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/budget.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,51 @@
+(in-package :stumpwm-user)
+
+(defparameter *tz/eastern*
+ (local-time:find-timezone-by-location-name "US/Eastern"))
+
+(defparameter *budget/start*
+ (local-time:encode-timestamp 0 0 0 0 29 8 2023 :timezone *tz/eastern*))
+
+(defun budget/per-day ()
+ (first (losh:read-all-from-file "/home/sjl/Sync/budget/per-day")))
+
+(defun budget/elapsed ()
+ (local-time:timestamp-difference (local-time:now) *budget/start*))
+
+(defun budget/days-elapsed ()
+ (floor (/ (budget/elapsed) (* 60 60 24))))
+
+(defun budget/in ()
+ (* (budget/days-elapsed) (budget/per-day)))
+
+(defun budget/out ()
+ (loop :for path :in (directory "/home/sjl/Sync/budget/hosts/*/total")
+ :summing (print (first (read-all-from-file (print path))))))
+
+(defun budget/current ()
+ (- (budget/in) (budget/out)))
+
+(defcommand budget-dump () ()
+ (message
+ (sh '("sh" "-c" "tail -n 5 /home/sjl/Sync/budget/hosts/*/records")
+ :result-type 'string)))
+
+(defcommand budget () ()
+ (message "$~D" (budget/current)))
+
+(defmacro with-budget-file ((f file &rest open-args) &body body)
+ `(with-open-file
+ (,f (format nil "/home/sjl/Sync/budget/hosts/~(~A~)/~A" *host* ,file)
+ ,@open-args)
+ ,@body))
+
+(defcommand spend (amount what) ((:integer "Amount: $") (:string "For: "))
+ (let ((current (with-budget-file (total "total")
+ (first (read-all-from-file total))))
+ (timestamp (local-time:to-rfc3339-timestring (local-time:now))))
+ (with-budget-file (total "total" :direction :output :if-exists :supersede)
+ (print (+ current amount) total))
+ (with-budget-file (records "records" :direction :output :if-exists :append :if-does-not-exist :create)
+ (print (list timestamp amount what) records))
+ (message "Spent $~D for ~A at ~A" amount what timestamp)))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/clipboard.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,12 @@
+(in-package :stumpwm-user)
+
+(defcommand generate-random-uuid () ()
+ (pbcopy (string-downcase (princ-to-string (uuid:make-v4-uuid))))
+ (message "Copied random UUID to clipboard."))
+
+(defcommand bee-movie-script () ()
+ (run-shell-command "pbeecopy")
+ (message "Copied the entire Bee Movie script to clipboard."))
+
+#; Scratch --------------------------------------------------------------------
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/config.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,32 @@
+(in-package :stumpwm-user)
+
+(set-prefix-key (kbd "C-space"))
+(local-time:reread-timezone-repository)
+
+(set-focus-color "#aaaaaa")
+(set-win-bg-color "#111111")
+(set-unfocus-color "#444444")
+(setf *normal-border-width* 1
+ *default-bg-color* #x222222
+ *window-border-style* :thin
+ (xlib:window-background (screen-root (current-screen))) *default-bg-color*)
+
+(defvar *redirected* (redirect-all-output (data-dir-file "debug" "log")))
+
+(setf *mouse-focus-policy* :click
+ *message-window-gravity* :center
+ *input-window-gravity* :center
+ *debug-level* 0
+ *resize-increment* 75
+ *new-frame-action* :empty
+ *window-format* "(%n%m%60t)"
+ *window-name-source* :title
+ *maximum-completions* 20
+ *shell-program* "/home/sjl/src/dotfiles/bin/bash-dammit"
+ losh:*pbcopy-command* "/home/sjl/src/dotfiles/bin/pbcopy"
+ losh:*pbpaste-command* "/home/sjl/src/dotfiles/bin/pbpaste")
+
+(defun stumpwm::input-insert-hyphen-or-space (input key)
+ ;; Unbreak typing
+ (declare (ignore key))
+ (input-insert-char input #\space))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/external-screens.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,26 @@
+(in-package :stumpwm-user)
+
+(defcommand screen-laptop () ()
+ (only)
+ (hostcase
+ ((:gro :juss) (loop :with laptop = "eDP"
+ :with extern = (hostcase (:gro "DisplayPort-0")
+ (:juss "HDMI-A-0"))
+ :for (output commands) :in `((,laptop ("--auto"))
+ (,laptop ("--primary"))
+ (,extern ("--off")))
+ :do (progn (uiop:run-program `("xrandr" "--output" ,output ,@commands)))))
+ (t (message "Not configured on this system."))))
+
+(defcommand screen-external () ()
+ (only)
+ (hostcase
+ ((:gro :juss) (loop :with laptop = "eDP"
+ :with extern = (hostcase (:gro "DisplayPort-0")
+ (:juss "HDMI-A-0"))
+ :for (output commands) :in `((,extern ("--auto"))
+ (,extern ("--primary"))
+ (,laptop ("--off")))
+ :do (uiop:run-program `("xrandr" "--output" ,output ,@commands))))
+ (t (message "Not configured on this system."))))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/icelandic.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,50 @@
+(in-package :stumpwm-user)
+
+
+(defun send-keys (keys &key (win (current-window)) (sleep 0))
+ (dolist (k keys)
+ (send-key (kbd k) win)
+ (sleep sleep)))
+
+(defmacro defmultikey (name key compose-keys)
+ ;; Unfortunately we can't reliably autogen the name with something like
+ ;; (symb 'mk- compose-key) here because things like đ (th) and Đ (TH) would
+ ;; case fold to the same name.
+ `(progn
+ (defcommand ,name () ()
+ (send-keys '("Multi_key" ,@(map 'list #'string compose-keys))))
+ (define-key *top-map*
+ (kbd ,key) ,(string name))))
+
+(defmacro defmultikeys (&rest bindings)
+ `(progn ,@(loop for binding :in bindings :collect `(defmultikey ,@binding))))
+
+(defmultikeys
+ (isk-l-á "M-a" "'a")
+ (isk-u-Á "M-A" "'A")
+ (isk-l-é "M-e" "'e")
+ (isk-u-É "M-E" "'E")
+ (isk-l-í "M-i" "'i")
+ (isk-u-Í "M-I" "'I")
+ (isk-l-ó "M-o" "'o")
+ (isk-u-Ó "M-O" "'O")
+ (isk-l-ö "M-m" "\"o")
+ (isk-u-Ö "M-M" "\"O")
+ (isk-l-ú "M-u" "'u")
+ (isk-u-Ú "M-U" "'U")
+ (isk-l-ý "M-y" "'y")
+ (isk-u-Ý "M-Y" "'Y")
+ (isk-l-þ "M-t" "th")
+ (isk-u-Þ "M-T" "TH")
+ (isk-l-đ "M-d" "dh")
+ (isk-u-Đ "M-D" "DH")
+ (isk-l-æ "M-h" "ae")
+ (isk-u-Æ "M-H" "AE"))
+
+
+(defcommand thinkpad-ret () ()
+ (send-key (kbd "RET")))
+
+(defcommand thinkpad-bs () ()
+ (send-key (kbd "BackSpace")))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/igv.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,105 @@
+(defpackage :stumpwm-user/igv
+ (:use :cl :losh)
+ (:import-from :stumpwm :defcommand :echo)
+ (:export
+ :igv/supplementary-on
+ :igv/supplementary-off
+ :igv/group-none
+ :igv/group-selected
+ :igv/clear-read-selections
+ :igv/select-reads
+ :igv/goto
+ :igv/goto-read
+ :igv/zoom-in
+ :igv/zoom-out
+ :igv/init))
+
+(in-package :stumpwm-user/igv)
+
+
+;;;; Implementation -----------------------------------------------------------
+(defun send-igv-command (string &key want-resp)
+ (usocket:with-client-socket (socket stream "127.0.0.1" 60151)
+ ;; Do this here instead of passing :timeout above because that only sets
+ ;; *read* timeout and we don't want to permahang stump when IGV hangs and
+ ;; can't read.
+ (setf (sb-impl::fd-stream-timeout (usocket:socket-stream socket)) 5.0f0)
+ (unwind-protect (progn (write-line string stream)
+ (force-output stream)
+ (when want-resp
+ (read-line stream)))
+ (usocket:socket-close socket))))
+
+(defun send-igv-batch-file (path)
+ (send-igv-command (alexandria:read-file-into-string path)))
+
+(defun alignment-tracks% ()
+ (str:split #\, (send-igv-command "alignmentTrackNames ," :want-resp t)))
+
+(defun group% (option)
+ (send-igv-command (format nil "group ~A" (ecase option
+ ((:selected) "selected")
+ ((nil) "none")))))
+
+(defun select-reads% ()
+ (dolist (track-name (alignment-tracks%))
+ (let ((read-names (ppcre:all-matches-as-strings
+ "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"
+ (pbpaste))))
+ (send-igv-command (format nil "selectByName ~A ~A ," track-name (str:join "," read-names)))
+ (echo (format nil "Selected ~D read~:P." (length read-names))))))
+
+(defun goto% ()
+ (ppcre:register-groups-bind (chr start end)
+ ("(chr[A-Za-z0-9_]+)[\\s:]+([\\d,]+)(?:[\\s:]+)?([\\d,]+)?" (pbpaste))
+ (send-igv-command (format nil "goto ~A:~A~@[-~A~]" chr start end))))
+
+(defun clear-read-selections% ()
+ (dolist (track-name (alignment-tracks%))
+ (send-igv-command (format nil "clearSelections ~A" track-name))))
+
+(defun zoom-in% ()
+ (send-igv-command "zoomin"))
+
+(defun zoom-out% ()
+ (send-igv-command "zoomout"))
+
+
+;;;; Commands -----------------------------------------------------------------
+(defcommand igv/supplementary-on () ()
+ (send-igv-command "preference SAM.FILTER_SUPPLEMENTARY_ALIGNMENTS FALSE")
+ (echo "Supplementary alignments now on."))
+
+(defcommand igv/supplementary-off () ()
+ (send-igv-command "preference SAM.FILTER_SUPPLEMENTARY_ALIGNMENTS TRUE")
+ (echo "Supplementary alignments now off."))
+
+(defcommand igv/group-none () ()
+ (group% nil))
+
+(defcommand igv/group-selected () ()
+ (group% :selected))
+
+(defcommand igv/clear-read-selections () ()
+ (clear-read-selections%))
+
+(defcommand igv/select-reads () ()
+ (select-reads%)
+ (group% :selected))
+
+(defcommand igv/goto () ()
+ (goto%))
+
+(defcommand igv/zoom-in () ()
+ (zoom-in%))
+
+(defcommand igv/zoom-out () ()
+ (zoom-out%))
+
+(defcommand igv/goto-read () ()
+ (clear-read-selections%)
+ (goto%)
+ (select-reads%))
+
+(defcommand igv/init () ()
+ (send-igv-batch-file "/home/sjl/bin/igv.batch"))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/key-mapping.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,279 @@
+(in-package :stumpwm-user)
+
+(defcommand nop () ()
+ nil)
+
+
+;;; Conventions:
+;;;
+;;; * Hyper-dir: move focus
+;;; * Hyper-Shift-dir: move window
+;;; * Hyper-Shift-Control-dir: swap window
+;;; * Hyper-F*: hardware
+;;; * Shift-F*: timers
+;;; * Hyper-Super-*: layout
+;;; * Hyper-*: miscellaneous
+;;; * Super-GAMERMOUSE: duplicate stuff to avoid swapping back
+
+
+(defmacro define-top-keys (&body keyforms)
+ `(progn ,@(loop :for form :in keyforms
+ :collect `(define-key *top-map*
+ (kbd ,(first form))
+ ,(second form)))))
+
+
+(define-top-keys ;; miscellaneous
+ ("H-m" "terminal")
+ ("H-M" "toggle-zoom-mute")
+ ("H-SunPageUp" "st-font-up")
+ ("H-SunPageDown" "st-font-down")
+ ("H-z" "fullscreen")
+ ("H-Z" "end-zoom")
+ ("C-S-F1" "bht")
+ ("H-F4" "switch-yubikeys")
+ ("H-\\" "pass-personal")
+ ("H-|" "generate-password")
+ ("s-1" "pass-um-1")
+ ("s-2" "pass-um-2")
+ ("H-b" "browser")
+ ("H-O" "spotify")
+ ("H-o" "files")
+ ("H-t" "zoom-meeting")
+ ("H-P" "papers")
+ ("F26" "prev")
+ ("S-F26" "next")
+ ("H-q" "exec lock-screen")
+ ("H-y" "screenshot")
+ ("H-Y" "delayed-screenshot")
+ ("H-r" "rain")
+ ("H-V" "vlc")
+ ("H-e" "budget")
+ ("H-E" "spend")
+ ("S-XF86AudioMute" "terminal-font-size-show")
+ ("S-XF86AudioRaiseVolume" "terminal-font-size-down") ; todo unfuck the backwards mapping in qmk
+ ("S-XF86AudioLowerVolume" "terminal-font-size-up")
+ ("C-BackSpace" "clear-notifications"))
+
+
+(define-top-keys ;; clipboard
+ ("H-c" "show-clipboard-history")
+ ("H-C" "clear-clipboard-history")
+ ("H-u" "generate-random-uuid")
+ ("H-B" "bee-movie-script"))
+
+(define-top-keys ;; movement
+ ("H-h" "move-focus* left")
+ ("H-j" "move-focus down")
+ ("H-k" "move-focus up")
+ ("H-l" "move-focus* right")
+
+ ("H-H" "move-window left")
+ ("H-J" "move-window down")
+ ("H-K" "move-window up")
+ ("H-L" "move-window right")
+
+ ("H-1" "gselect 1")
+ ("H-2" "gselect 2")
+ ("H-3" "gselect 3")
+ ("H-4" "gselect 4")
+ ("H-5" "gselect 5")
+ ("H-6" "gselect 6")
+
+ ("KP_Insert" "vgroups")
+ ("KP_End" "gselect 1")
+ ("KP_Down" "gselect 2")
+ ("KP_Page_Down" "gselect 3")
+ ("KP_Left" "gselect 4")
+ ("KP_Begin" "gselect 5")
+ ("KP_Right" "gselect 6")
+ ("KP_Home" "gselect 7")
+ ("KP_Up" "gselect 8")
+ ("KP_Page_Up" "gselect 9")
+
+ ("H-!" "gmove 1")
+ ("H-@" "gmove 2")
+ ("H-#" "gmove 3")
+ ("H-$" "gmove 4")
+ ("H-%" "gmove 5")
+ ("H-^" "gmove 6")
+
+ ("C-H-H" "exchange-direction left")
+ ("C-H-J" "exchange-direction down")
+ ("C-H-K" "exchange-direction up")
+ ("C-H-L" "exchange-direction right")
+
+ ("H-`" "next")
+ ("S-H-`" "prev")
+ ("H-n" "next-in-frame")
+ ("H-p" "prev-in-frame")
+ ("H-N" "pull-hidden-next")
+
+ ("H-," "pull-from-windowlist")
+ ("H-less" "title")
+
+ ("H-." "ggo")
+ ("H-greater" "grename")
+ )
+
+(define-top-keys ;; splitting
+ ("H-s" "sane-vsplit")
+ ("H-v" "sane-hsplit")
+ ("H-=" "balance-frames"))
+
+(define-top-keys ;; killing
+ ("H-w" "delete")
+ ("H-W" "kill")
+ ("H-BackSpace" "remove")
+ ("S-H-BackSpace" "kill-and-remove")
+ ("H-Delete" "gkill"))
+
+(define-top-keys ;; naming
+ ("H-'" "title")
+ ("H-\"" "grename"))
+
+(define-top-keys ;; sound
+ ("H-F1" "mute")
+ ("H-F2" "volume-down")
+ ("H-F3" "volume-up")
+ ("XF86AudioMute" "mute")
+ ("XF86AudioRaiseVolume" "volume-down") ; todo unfuck the backwards mapping in qmk
+ ("XF86AudioLowerVolume" "volume-up"))
+
+(define-top-keys ;; screen
+ ("H-F5" "rotate-brightness-down")
+ ("H-F6" "rotate-brightness-up")
+ ("H-F7" "screen-laptop")
+ ("H-F8" "screen-external"))
+
+(define-top-keys ;; layout
+ ("s-H-o" "only")
+ ("s-H-n" "restore-from-file notes")
+ ("s-H-t" "restore-from-file thirds")
+ ("s-H-m" "restore-from-file dev")
+ ("s-H-s" "restore-from-file streaming")
+ ("s-H-w" "restore-from-file work")
+ ("s-H-z" "restore-from-file zoom"))
+
+(define-top-keys ;; timers
+ ("s-F7" "tea-timer")
+ ("s-F9" "run-pop-timer")
+ ("s-F8" "set-pop-timer")
+ ("s-p" "posture-start")
+ ("s-P" "posture-stop")
+ ("s-y" "posture-answer-yes")
+ ("s-h" "posture-answer-meh")
+ ("s-n" "posture-answer-no")
+ ("s-\\" "posture-toggle-pause")
+ ("s-o" "posture-snooze"))
+
+(define-top-keys ;; stump
+ ("Pause" "terminal") ; jesus christ
+ ("H-F9" "sleep-machine")
+ ("H-F10" "toggle-stumptray")
+ ("H-F11" "toggle-current-mode-line")
+ ("H-F12" "refresh-heads"))
+
+(defvar *keymap/igv* (make-sparse-keymap))
+
+(define-key *keymap/igv* (kbd "s") "igv/supplementary-on")
+(define-key *keymap/igv* (kbd "S") "igv/supplementary-off")
+(define-key *keymap/igv* (kbd "g") "igv/group-selected")
+(define-key *keymap/igv* (kbd "G") "igv/group-none")
+(define-key *keymap/igv* (kbd "r") "igv/select-reads")
+(define-key *keymap/igv* (kbd "R") "igv/clear-read-selections")
+(define-key *keymap/igv* (kbd "l") "igv/goto")
+(define-key *keymap/igv* (kbd "L") "igv/goto-read")
+(define-key *keymap/igv* (kbd "i") "igv/init")
+(define-key *keymap/igv* (kbd "H-i") "igv")
+
+(define-top-keys ;; alternate maps
+ ("H-i" *keymap/igv*))
+
+
+;;; The G A M E R M O U S E:
+;;;
+;;; +----------+----------+----------+ _____
+;;; | | ^ | | / \ +--------+
+;;; | igv | | | igv | | | | |
+;;; | z out | move | z in | group | | group | |
+;;; +----------+----------+----------+ <----- | | -----> | |
+;;; | | | | | | | |
+;;; | move | | move | | | | |
+;;; | <-- | | --> | \_____/ | kill |
+;;; +----------+----------+----------+ | |
+;;; | | move | | | |
+;;; | | | | | +---------+ | |
+;;; | | v | | | | | |
+;;; +----------+----------+----------+ | next | | |
+;;; | split | FULL | split | | | +--------+
+;;; | [-] | SCREEN | [|] | +---------+
+;;; | | | | | |
+;;; +----------+----------+----------+ | prev |
+;;; | |
+;;; +---------+
+(define-top-keys ;; GAMER MOUSE
+ ;; Super layer
+ ("s-!" "igv/zoom-out") ("s-@" "move-window up") ("s-#" "igv/zoom-in")
+ ("s-$" "move-window left") ("s-%" "nop") ("s-^" "move-window right")
+ ("s-&" "nop") ("s-*" "move-window down") ("s-(" "nop")
+ ("s-)" "sane-vsplit") ("s-_" "fullscreen") ("s-+" "sane-hsplit")
+
+ ("M-s-Left" "gprev") ("M-s-Right" "gnext")
+
+ ("s-Home" "next-in-frame")
+ ("s-End" "prev-in-frame")
+
+ ("s-Delete" "remove")
+
+ ;; Hyper-Super layer
+ ("H-s-!" "nop") ("H-s-@" "resize-direction up") ("H-s-#" "nop")
+ ("H-s-$" "resize-direction left") ("H-s-%" "fullscreen") ("H-s-^" "resize-direction right")
+ ("H-s-&" "nop") ("H-s-*" "resize-direction down") ("H-s-(" "nop")
+ ("H-s-)" "nop") ("H-s-_" "nop") ("H-s-+" "nop")
+
+ )
+
+(define-top-keys ;; Single-key keyboard
+ ("S-C-F1" "igv"))
+
+
+(define-remapped-keys
+ '(("st-256color"
+ ("s-c" . "C-C")
+ ("s-v" . "C-V")
+ ("C-=" . "S-C-SunPageUp")
+ ("C--" . "S-C-SunPageDown")
+ ("C-0" . "S-C-Home"))
+ ("(firefox|Google-chrome|Chromium-browser)"
+ ("s-[" . "C-S-Tab")
+ ("s-]" . "C-Tab")
+ ("C-M-Left" . "C-S-Tab")
+ ("C-M-Right" . "C-Tab")
+ ("C-a" . "Home")
+ ("C-e" . "End")
+ ;; I always try to hit ctrl-d to kill a browser window because I'm so used
+ ;; to terminal windows, and it ends up bookmarking the damn page. In the
+ ;; interest of not having a random collection of bookmarks grow over time,
+ ;; I'll just add a mapping to compensate for my stupid brain.
+ ("C-d" . "C-w")
+ ;; todo debug why this breaks a really fast C-a-k roll
+ ;; ("C-a" . "Home")
+ ;; ("C-e" . "End")
+ ("s-a" . "C-a")
+ ("s-d" . "C-d")
+ ("s-l" . "C-l")
+ ("s-t" . "C-t")
+ ("s-w" . "C-w")
+ ("s-r" . "C-r")
+ ("s-f" . "C-f")
+ ("s-z" . "C-z")
+ ("s-x" . "C-x")
+ ("s-c" . "C-c")
+ ("s-v" . "C-v"))
+ (""
+ ("s-z" . "C-z")
+ ("s-x" . "C-x")
+ ("s-c" . "C-c")
+ ("s-v" . "C-v"))))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/local-share-stumpwm/522.dump Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,52 @@
+#S(GDUMP
+ :NUMBER 3
+ :NAME "code"
+ :TREE (((#S(FDUMP
+ :NUMBER 0
+ :X 0
+ :Y 0
+ :WIDTH 1200
+ :HEIGHT 1020
+ :WINDOWS (31457288)
+ :CURRENT 31457288)
+ #S(FDUMP
+ :NUMBER 3
+ :X 0
+ :Y 1020
+ :WIDTH 1200
+ :HEIGHT 420
+ :WINDOWS (29360133)
+ :CURRENT 29360133))
+ ((#S(FDUMP
+ :NUMBER 1
+ :X 1200
+ :Y 0
+ :WIDTH 1735
+ :HEIGHT 1440
+ :WINDOWS (33554437)
+ :CURRENT 33554437)
+ (#S(FDUMP
+ :NUMBER 4
+ :X 2935
+ :Y 0
+ :WIDTH 985
+ :HEIGHT 720
+ :WINDOWS (37748741)
+ :CURRENT 37748741)
+ #S(FDUMP
+ :NUMBER 5
+ :X 2935
+ :Y 720
+ :WIDTH 985
+ :HEIGHT 720
+ :WINDOWS NIL
+ :CURRENT NIL)))
+ #S(FDUMP
+ :NUMBER 2
+ :X 3920
+ :Y 0
+ :WIDTH 1200
+ :HEIGHT 1440
+ :WINDOWS (16977044)
+ :CURRENT 16977044))))
+ :CURRENT 1)
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/local-share-stumpwm/notes.dump Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,44 @@
+#S(GDUMP
+ :NUMBER 2
+ :NAME "notes"
+ :TREE (((#S(FDUMP
+ :NUMBER 0
+ :X 0
+ :Y 0
+ :WIDTH 2560/3
+ :HEIGHT 720
+ :WINDOWS (25165829)
+ :CURRENT 25165829)
+ #S(FDUMP
+ :NUMBER 4
+ :X 0
+ :Y 720
+ :WIDTH 2560/3
+ :HEIGHT 720
+ :WINDOWS (35651589)
+ :CURRENT 35651589))
+ (#S(FDUMP
+ :NUMBER 1
+ :X 2560/3
+ :Y 0
+ :WIDTH 2560/3
+ :HEIGHT 1440
+ :WINDOWS (27262981)
+ :CURRENT 27262981)
+ (#S(FDUMP
+ :NUMBER 2
+ :X 5120/3
+ :Y 0
+ :WIDTH 2560/3
+ :HEIGHT 720
+ :WINDOWS (29360133)
+ :CURRENT 29360133)
+ #S(FDUMP
+ :NUMBER 3
+ :X 5120/3
+ :Y 720
+ :WIDTH 2560/3
+ :HEIGHT 720
+ :WINDOWS (33554437)
+ :CURRENT 33554437)))))
+ :CURRENT 1)
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/local-share-stumpwm/uw-test.dump Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,7 @@
+#S(GDUMP
+ :NUMBER 1
+ :NAME "Default"
+ :TREE ((#S(FDUMP :NUMBER 0 :X 0 :Y 0 :WIDTH 640 :HEIGHT 1440 :WINDOWS NIL :CURRENT NIL)
+ (#S(FDUMP :NUMBER 1 :X 640 :Y 0 :WIDTH 3840 :HEIGHT 1440 :WINDOWS NIL :CURRENT NIL)
+ #S(FDUMP :NUMBER 2 :X 4480 :Y 0 :WIDTH 640 :HEIGHT 1440 :WINDOWS NIL :CURRENT NIL))))
+ :CURRENT 1)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/miscellaneous.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,112 @@
+(in-package :stumpwm-user)
+
+(defcommand sane-hsplit () ()
+ (hsplit)
+ (move-focus :right))
+
+(defcommand sane-vsplit () ()
+ (vsplit)
+ (move-focus :down))
+
+
+(defcommand move-focus* (direction)
+ ((:direction "Enter a direction: "))
+ (labels ((in-float-p ()
+ (typep (current-group) 'stumpwm::float-group))
+ (focus-first-frame ()
+ (unless (in-float-p)
+ ;; After moving to a new group we don't know which frame is
+ ;; focused, and unfortunately Stump doesn't give us a nice way to
+ ;; say "focus the leftmost frame" so we'll just move the focus
+ ;; a bunch of times and hope it's enough. Sigh.
+ (loop :repeat 15
+ :until (eql (current-frame)
+ (progn (move-focus (ecase direction
+ (:left :right)
+ (:right :left)))
+ (current-frame))))))
+ (next-group ()
+ (ecase direction
+ (:right (gnext))
+ (:left (gprev)))
+ (focus-first-frame)))
+ (unless (in-float-p)
+ (banish))
+ (if (in-float-p)
+ (next-group)
+ (let ((frame (current-frame)))
+ (move-focus direction)
+ (when (eql frame (current-frame))
+ (next-group))))))
+
+(defcommand toggle-current-mode-line () ()
+ (toggle-mode-line (current-screen) (current-head)))
+
+(defcommand toggle-stumptray () ()
+ (run-commands "stumptray"))
+
+(defcommand kill-and-remove () ()
+ (run-commands "kill" "remove"))
+
+(defcommand sleep-machine ()
+ ()
+ (hostcase
+ ((:gro :juss)
+ (run-shell-command "exec lock-screen")
+ (run-shell-command "systemctl suspend"))
+ (t (message "Not sleeping this machine for safety."))))
+
+(defcommand copy-clhs-url (s)
+ ((:string "Symbol: "))
+ (run-shell-command (format nil "clhs --url 'http://www.lispworks.com/documentation/HyperSpec/' --quiet --open echon '~A' | pbcopy" s)))
+
+(defcommand describe-window () ()
+ (show-window-properties))
+
+(defcached (weather :seconds 120) ()
+ (losh:sh '("/home/sjl/src/dotfiles/lisp/bin/weather" "48105" "-H" "36") :result-type 'list))
+
+(defcommand rain () ()
+ (_ (weather)
+ (mapcar (lambda (line) (ppcre:regex-replace " 1[0-9]:00 " line "^6\\&^*")) _)
+ (message "~{~A~^~%~}" _)))
+
+(defcommand mark (thing) ((:string "Mark: "))
+ (run-shell-command (format nil "mark ~A" thing)))
+
+(defcommand clear-notifications () ()
+ (run-shell-command "dunstctl close-all"))
+
+(defcommand start-vm () ()
+ (echo "Starting VM.")
+ (run-shell-command "/home/sjl/vms/run"))
+
+
+(defcommand toggle-zoom-mute () ()
+ (when-let-window (win "^Zoom Meeting.*")
+ ;; Zoom stupidly won't accept the shortcut unless it's in focus
+ (unless (eql (window-group win) (current-group))
+ ;; jesus christ stump just export switch-to-group come on
+ (gselect (princ-to-string (group-number (window-group win)))))
+ (focus-window win t)
+ (meta (kbd "M-a"))))
+
+(defcommand end-zoom () ()
+ (when-let-window (win "^Zoom Meeting.*")
+ (kill-window win)
+ (message "Killed meeting")))
+
+
+(defcommand ggo () ()
+ (let* ((current-screen (current-screen))
+ (current-group (stumpwm::screen-current-group current-screen))
+ (all-groups (sort (copy-seq (screen-groups current-screen)) #'< :key #'group-number))
+ (groups (mapcar (lambda (group)
+ (list (format nil "~2D~A~A"
+ (group-number group)
+ (if (eql current-group group) #\* #\space)
+ (group-name group))
+ group))
+ all-groups))
+ (selected (select-from-menu current-screen groups)))
+ (when selected (stumpwm::switch-to-group (second selected)))))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/modeline.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,51 @@
+(in-package :stumpwm-user)
+
+(defun ensure-mode-line ()
+ (when (not (stumpwm::head-mode-line (current-head)))
+ (toggle-mode-line (current-screen) (current-head))))
+
+
+(defun configure-modeline ()
+ (setf
+ *time-modeline-string*
+ "%a %b %e %H:%M"
+
+ cpu::*cpu-usage-modeline-fmt*
+ "^[~A~3D%^]"
+
+ cpu::*cpu-modeline-fmt*
+ "[%c] [%f]"
+
+ mem::*mem-modeline-fmt*
+ "%p"
+
+ *screen-mode-line-format*
+ (append
+ (list "[^B"
+ '(:eval (princ-to-string (group-number (current-group))))
+ ":%n^b@%h] %W^>")
+
+ (list "(V %V) ")
+
+ ;; battery and brightness for laptops
+ (hostcase
+ ((:gro :juss)
+ (list "(B %B)"
+ " (BR "
+ '(:eval (princ-to-string (brightness)))
+ "%) ")))
+
+ ;; temperature
+ (list "(TEMP %S) ")
+
+ ;; cpu, mem, time, tray
+ (list "(CPU %C) (MEM%M) %d %T")
+ ))
+
+ (setf *mode-line-timeout* 10)
+ (setf *mode-line-background-color* "#111111")
+
+ (ensure-mode-line))
+
+(configure-modeline)
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/modules.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,9 @@
+(in-package :stumpwm-user)
+
+(load-module "pass")
+(load-module "battery-portable")
+(load-module "cpu")
+(load-module "hostname")
+(load-module "mem")
+(load-module "stumptray")
+(load-module "clipboard-history")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/package.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,5 @@
+(in-package :stumpwm-user)
+
+(shadow :window)
+
+(use-package :losh)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/passwords.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,23 @@
+(in-package :stumpwm-user)
+
+(defcommand pass-personal () ()
+ (let ((pass:*password-store* "/home/sjl/.password-store/")
+ (pass:*pass-notification-message* t))
+ (pass:pass-copy)))
+
+(defcommand pass-um-1 () ()
+ (echo "Copying UM level 1 password, touch key.")
+ (run-shell-command "pass -c umich.edu/slosh"))
+
+(defcommand pass-um-2 () ()
+ (echo "Copying UM level 2 password, touch key.")
+ (run-shell-command "pass -c umich.edu/l2"))
+
+(defcommand switch-yubikeys () ()
+ (echo (run-shell-command "switch-yubikeys" t)))
+
+(defcommand generate-password () ()
+ (run-shell-command "genpass | pbc")
+ (message "Generated a fresh password and copied it to the clipboard."))
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/posture.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,84 @@
+(in-package :stumpwm-user)
+
+(defparameter *posture-thread* nil)
+(defparameter *posture-should-stop* nil)
+(defparameter *posture-paused* nil)
+(defparameter *posture-snooze* nil)
+(defparameter *posture-current* 30)
+(defparameter *posture-min* 5)
+(defparameter *posture-max* (hours->seconds 2))
+
+(defun posture-paused-p ()
+ ;; this is the dumbest shit ever, but I can't figure out how to call into
+ ;; stumpish from the setguid slock process
+ (or *posture-paused* (probe-file "/tmp/.posture-pause")))
+
+(defun posture-snoozed-p ()
+ (and *posture-snooze*
+ (< (get-universal-time) *posture-snooze*)))
+
+(defcommand posture-pause () ()
+ (message "Pausing posture.")
+ (setf *posture-paused* t))
+
+(defcommand posture-unpause () ()
+ (message "Unpausing posture.")
+ (setf *posture-paused* nil))
+
+(defcommand posture-toggle-pause () ()
+ (if (setf *posture-paused* (not *posture-paused*))
+ (message "Posture is now paused.")
+ (message "Posture is now unpaused.")))
+
+(defcommand posture-snooze (hours)
+ ((:real "Snooze for how many hours? "))
+ (setf *posture-snooze* (+ (hours->seconds hours) (get-universal-time))))
+
+(defun posture-update (delta)
+ (setf *posture-current*
+ (clamp *posture-min* *posture-max* (* *posture-current* delta))))
+
+(defun posture-query ()
+ (speak "Is your posture okay?"))
+
+(defcommand posture-answer-yes () ()
+ (message "Good work.")
+ (run-shell-command "echo $(epochseconds) 1.0 >> ~/.posture.log")
+ (posture-update 11/10))
+
+(defcommand posture-answer-meh () ()
+ (message "Better than nothing.")
+ (run-shell-command "echo $(epochseconds) 0.5 >> ~/.posture.log"))
+
+(defcommand posture-answer-no () ()
+ (message "Try harder.")
+ (run-shell-command "echo $(epochseconds) 0.0 >> ~/.posture.log")
+ (posture-update 8/10))
+
+(defun posture% ()
+ (if *posture-should-stop*
+ nil
+ (progn (unless (or (posture-paused-p) (posture-snoozed-p))
+ (posture-query)
+ (sleep 10))
+ *posture-current*)))
+
+(defun posture-running-p ()
+ (and *posture-thread* (sb-thread:thread-alive-p *posture-thread*)))
+
+(defcommand posture-stop () ()
+ (setf *posture-should-stop* t))
+
+(defcommand posture-start () ()
+ (setf *posture-should-stop* nil)
+ (if (posture-running-p)
+ (message "Posture loop was already running.")
+ (setf *posture-thread*
+ (sb-thread:make-thread
+ (lambda ()
+ (loop :for seconds = (posture%)
+ :while seconds
+ :do (sleep seconds))
+ (message "Posture loop exiting."))
+ :name "Posture thread"))))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/screenshots.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,8 @@
+(in-package :stumpwm-user)
+
+(defcommand delayed-screenshot () ()
+ (run-shell-command "sleep 10 && screenshot"))
+
+(defcommand screenshot () ()
+ (run-shell-command "screenshot"))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/sensors.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,48 @@
+(in-package :stumpwm-user)
+
+(defun ? (obj &rest keys)
+ (if (null keys)
+ obj
+ (apply #'? (etypecase obj
+ (hash-table (gethash (first keys) obj)))
+ (rest keys))))
+
+(defun parse-sensors ()
+ ;; sensors -j is stupid and will output errors before the actual output on
+ ;; standard out, instead of putting them on standard err like a reasonable
+ ;; program, e.g.:
+ ;;
+ ;; ERROR: Can't get value of subfeature temp1_input: Can't read
+ ;; {
+ ;; "iwlwifi_1-virtual-0":{ … },
+ ;; …
+ ;;
+ ;; So we'll have to drop the `ERROR` lines before we can get to the actual
+ ;; goddamn JSON. UNIX programs are so great.
+ (let ((s (losh:sh '("sensors" "-j") :result-type 'stream)))
+ (loop :while (char= #\E (peek-char nil s)) :do (read-line s))
+ (jarl:read t s)))
+
+(defun sensors% (&aux (sensors (parse-sensors)))
+ (hostcase
+ (:ouroboros (format nil "[CPU ~D°C] [GPU ~D°C ~D°C ~D°C]"
+ (round (? sensors "nct6779-isa-0290" "CPUTIN" "temp2_input"))
+ (round (? sensors "amdgpu-pci-4500" "edge" "temp1_input"))
+ (round (? sensors "amdgpu-pci-4500" "junction" "temp2_input"))
+ (round (? sensors "amdgpu-pci-4500" "mem" "temp3_input"))))
+ ((:gro :juss) (format nil "[CPU ~D°C] [GPU ~D°C]"
+ (round (? sensors "thinkpad-isa-0000" "CPU" "temp1_input"))
+ (round (? sensors "amdgpu-pci-0400" "edge" "temp1_input"))))
+ (t "?")))
+
+(defcached (sensors :seconds 10.0)
+ (sensors%))
+
+(defun sensors-modeline (ml)
+ (declare (ignore ml))
+ (sensors))
+
+(add-screen-mode-line-formatter #\S #'sensors-modeline)
+
+#; Scratch --------------------------------------------------------------------
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/sound.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,40 @@
+(in-package :stumpwm-user)
+
+(defun volume% ()
+ (_ (run-shell-command "amixer sget Master" t)
+ (string-grep "Front Left:" _ :first-only t)
+ (string-split "[]" _)
+ second
+ (string-trim "%" _)
+ parse-integer))
+
+(defcached (volume :seconds 30)
+ (volume%))
+
+(defcommand mute () ()
+ (run-shell-command "mute")
+ (volume/uncache)
+ (message "Muted."))
+
+(defcommand volume-up () ()
+ (run-shell-command "amixer -q sset Master 5%+")
+ (volume/uncache)
+ (message "Volume: ~D%" (volume)))
+
+(defcommand volume-down () ()
+ (run-shell-command "amixer -q sset Master 5%-")
+ (volume/uncache)
+ (message "Volume: ~D%" (volume)))
+
+(defun volume-modeline (ml)
+ (declare (ignore ml))
+ (format nil "~D%" (volume)))
+
+(defcommand bht () ()
+ (run-shell-command (format nil "amixer -q sset Master 50%; bht; amixer -q sset Master ~D%" (volume)))
+ (volume/uncache))
+
+(add-screen-mode-line-formatter #\V #'volume-modeline)
+
+#; Scratch --------------------------------------------------------------------
+(volume)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/stumpconfig.asd Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,40 @@
+(asdf:defsystem :stumpconfig
+ :description "My StumpWM configuration."
+ :author "Steve Losh <steve@stevelosh.com>"
+
+ :depends-on (:losh
+ :split-sequence
+ :alexandria
+ :parse-number
+ :str
+ :cl-ppcre
+ :bordeaux-threads
+ :jarl
+ :local-time
+ :usocket
+ :uuid)
+
+ :serial t
+ :components ((:file "package")
+ (:file "modules")
+ (:file "config")
+ (:file "utils")
+ (:file "posture")
+ (:file "budget")
+ (:file "screenshots")
+ (:file "sound")
+ (:file "brightness")
+ (:file "passwords")
+ (:file "terminal-fonts")
+ (:file "clipboard")
+ (:file "applications")
+ (:file "timers")
+ (:file "icelandic")
+ (:file "sensors")
+ (:file "modeline")
+ (:file "vlime")
+ (:file "external-screens")
+ (:file "miscellaneous")
+ (:file "igv")
+ (:file "bioinf")
+ (:file "key-mapping")))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/stumpwmrc Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,20 @@
+(in-package :stumpwm-user)
+
+(ql:quickload :stumpconfig)
+
+(defvar *tray-loaded*
+ (run-commands "stumptray"))
+
+(defvar *dunst*
+ (run-shell-command "/usr/bin/dunst -conf ~/.dunstrc"))
+
+(defvar *clip*
+ (clipboard-history:start-clipboard-manager))
+
+(when (probe-file "/home/sjl/.stumpwmrc.local")
+ (load "/home/sjl/.stumpwmrc.local"))
+
+
+#; Scratch --------------------------------------------------------------------
+
+(princ *window-format*)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/terminal-fonts.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,36 @@
+(in-package :stumpwm-user)
+
+(defun read-terminal-font-size-from-config ()
+ (if (probe-file "/home/sjl/.terminal-font")
+ (with-open-file (f "/home/sjl/.terminal-font")
+ (read f))
+ 11))
+
+(defvar *terminal-font-size* (read-terminal-font-size-from-config))
+
+(defcommand reload-terminal-font-size () ()
+ (setf *terminal-font-size* (read-terminal-font-size-from-config)))
+
+(defcommand terminal-font-size-show () ()
+ (message "~D" *terminal-font-size*))
+
+(defcommand terminal-font-size-up () ()
+ (message "~D" (incf *terminal-font-size*)))
+
+(defcommand terminal-font-size-down () ()
+ (message "~D" (setf *terminal-font-size* (max 6 (1- *terminal-font-size*)))))
+
+(defcommand st-font-up () ()
+ (loop :repeat 7 :do (meta (kbd "C-S-SunPageUp"))))
+
+(defcommand st-font-down () ()
+ (loop :repeat 7 :do (meta (kbd "C-S-SunPageDown"))))
+
+(defcommand st-font-reset () ()
+ (meta (kbd "C-S-Home")))
+
+(defcommand tfont (size)
+ ((:integer "Size: "))
+ (setf *terminal-font-size* size))
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/timers.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,38 @@
+(in-package :stumpwm-user)
+
+(defparameter *pop-timer-minutes* nil)
+(defparameter *pop-timer-seconds* nil)
+
+(defun pop-timer ()
+ (if (or (null *pop-timer-minutes*)
+ (null *pop-timer-seconds*))
+ (message "Pop timer is not configured.")
+ (progn
+ (message "Setting pop timer for ~D:~2,'0D."
+ *pop-timer-minutes* *pop-timer-seconds*)
+ (let* ((warning-time 30)
+ (total-time (+ (* *pop-timer-minutes* 60) *pop-timer-seconds*))
+ (initial-time (- total-time warning-time)))
+ (sb-thread:make-thread
+ (lambda ()
+ (if (plusp initial-time)
+ (progn (sleep initial-time)
+ (speak "Pop soon.")
+ (sleep warning-time))
+ (sleep total-time))
+ (speak "Pop!"))
+ :name "Pop Timer")))))
+
+(defcommand run-pop-timer () ()
+ (pop-timer))
+
+(defcommand set-pop-timer (minutes seconds)
+ ((:integer "Minutes: ")
+ (:integer "Seconds: "))
+ (setf *pop-timer-minutes* minutes
+ *pop-timer-seconds* seconds))
+
+(defcommand tea-timer (seconds)
+ ((:integer "Seconds: "))
+ (run-shell-command (format nil "tea ~D" seconds)))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/utils.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,156 @@
+(in-package :stumpwm-user)
+
+(defun send-key% (key &optional (win (current-window)))
+ "Send key press and key release events for KEY to window WIN."
+ ;; from https://github.com/alezost/stumpwm-config/blob/master/utils.lisp
+ (let ((xwin (window-xwin win)))
+ (multiple-value-bind (code state) (stumpwm::key-to-keycode+state key)
+ (flet ((send (event)
+ (xlib:send-event xwin event (xlib:make-event-mask event)
+ :display *display*
+ :root (screen-root (window-screen win))
+ :x 0 :y 0 :root-x 0 :root-y 0
+ :window xwin :event-window xwin
+ :code code
+ :state state)))
+ (send :key-press)
+ (send :key-release)
+ (xlib:display-finish-output *display*)))))
+
+(defcommand send-key (key &optional (win (current-window))) (:key)
+ "Send key press and key release events for KEY to window WIN."
+ (send-key% key win))
+
+
+(defun string-contains (needle string)
+ (and (search needle string :test #'char=) t))
+
+(defun string-grep (needle text &key first-only)
+ (_ text
+ (split-sequence:split-sequence #\newline _)
+ (if first-only
+ (find needle _ :test #'string-contains)
+ (remove-if-not (alexandria:curry #'string-contains needle) _))))
+
+(defun string-split (delimiters string)
+ (split-sequence:split-sequence delimiters string
+ :test (lambda (bag ch)
+ (find ch bag :test #'char=))))
+
+(defun run-and-echo-shell-command (command &rest args)
+ (message command)
+ (apply #'run-shell-command command args))
+
+
+(defun mod+ (n increment modulo)
+ (mod (+ n increment) modulo))
+
+
+(defmacro defcached ((name &key seconds) &body body)
+ (let ((seconds (coerce seconds 'double-float)))
+ (with-gensyms (ttl next value)
+ `(let ((,ttl ,seconds)
+ (,next nil)
+ (,value nil))
+ (defun ,name ()
+ (when (or (null ,next)
+ (>= (get-internal-real-time) ,next))
+ (setf ,next (+ (get-internal-real-time)
+ (* internal-time-units-per-second ,ttl))
+ ,value (progn ,@body)))
+ ,value)
+ (defun ,(symb name '/uncache) ()
+ (setf ,next nil ,value nil))))))
+
+
+(defun current-frame ()
+ (stumpwm::tile-group-current-frame (current-group)))
+
+
+(defun keywordize (string)
+ (_ string
+ (string-trim (string #\newline) _)
+ string-upcase
+ (intern _ (find-package :keyword))))
+
+(defparameter *host* (keywordize (machine-instance)))
+
+
+(defmacro ehostcase (&body clauses)
+ `(ecase *host* ,@clauses))
+
+(defmacro hostcase (&body clauses)
+ `(case *host* ,@clauses))
+
+
+(defcommand speak (text)
+ ((:string "Text: "))
+ (message text)
+ (run-shell-command (format nil "~~/src/dotfiles/bin/say '~A'" text)))
+
+
+(defun seconds->hours (seconds)
+ (/ seconds 60 60))
+
+(defun hours->seconds (hours)
+ (* hours 60 60))
+
+
+(define-stumpwm-type :integer (input prompt)
+ ;; Annoyingly, StumpWM's built-in :number type isn't actually number, but is
+ ;; actually just integers. Define a better-named type here.
+ (when-let ((n (or (argument-pop input)
+ (read-one-line (current-screen) prompt))))
+ (handler-case
+ (parse-integer n)
+ (parse-error (c)
+ (declare (ignore c))
+ (throw 'error "Integer required.")))))
+
+(define-stumpwm-type :real (input prompt)
+ (when-let ((n (or (argument-pop input)
+ (read-one-line (current-screen) prompt))))
+ (handler-case
+ (let ((result (parse-number:parse-number n)))
+ (assert (typep result 'real))
+ result)
+ (error (c)
+ (declare (ignore c))
+ (throw 'error "Real required.")))))
+
+
+(defun window-match-p (query window)
+ "Return whether `window` matches `query`.
+
+ `query` must be of the form `(query-type query-value)`.
+
+ `query-type` must be one of `:title` or `:class`.
+
+ `query-value` must either be a string (which must be matched exactly) or
+ a PPCRE scanner.
+
+ "
+ (destructuring-bind (query-type query) query
+ (let ((value (ecase query-type
+ (:title (window-title window))
+ (:class (window-class window)))))
+ (etypecase query
+ (string (string= query value))
+ (function (ppcre:scan query value))))))
+
+(defun all-windows ()
+ "Return a fresh list of all windows on all screens. Yes, all of them."
+ (mapcan #'screen-windows *screen-list*))
+
+(defun find-window (query)
+ "Find and return the first window that matches `query` under `window-match-p`."
+ (find-if (lambda (w) (window-match-p query w)) (all-windows)))
+
+(defun find-windows (query)
+ "Find and return a fresh list of all windows that match `query` under `window-match-p`."
+ (remove-if-not (lambda (w) (window-match-p query w)) (all-windows)))
+
+(defmacro when-let-window ((symbol title-query) &body body)
+ `(when-let ((,symbol (find-window `(:title ,(ppcre:create-scanner ,title-query)))))
+ ,@body))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/vlime.lisp Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,15 @@
+(in-package :stumpwm-user)
+
+(defcommand vlime () ()
+ (load "~/src/dotfiles/vim/bundle/vlime/lisp/start-vlime.lisp")
+ (message "Started VLIME"))
+
+(defcommand vlime-port (port) ((:integer "Port: "))
+ "Start VLIME on the given port.
+
+ Good for bootstrapping a VLIME connection when you accidentally started a
+ VLIME instance on another port that you don't want to mess with.
+
+ "
+ (funcall (read-from-string "vlime-loader::run") port)
+ (message "Started VLIME"))
--- a/stumpwmrc Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,951 +0,0 @@
-(in-package :stumpwm-user)
-(shadow :window)
-
-(ql:quickload '(:losh :split-sequence :alexandria :parse-number :str :cl-ppcre :bordeaux-threads :jarl :local-time)
- :silent t)
-
-(use-package :losh)
-
-;;;; Config -------------------------------------------------------------------
-(set-prefix-key (kbd "C-space"))
-(local-time:reread-timezone-repository)
-
-(defvar *redirected* (redirect-all-output (data-dir-file "debug" "log")))
-
-(setf *mouse-focus-policy* :click
- *message-window-gravity* :center
- *input-window-gravity* :center
- *debug-level* 0
- *resize-increment* 75
- *new-frame-action* :empty
- *window-format* "(%n%m%20t)"
- *window-name-source* :title
- *maximum-completions* 20
- *shell-program* "/home/sjl/src/dotfiles/bin/bash-dammit"
- losh:*pbcopy-command* "/home/sjl/src/dotfiles/bin/pbcopy"
- losh:*pbpaste-command* "/home/sjl/src/dotfiles/bin/pbpaste")
-
-
-;;;; Utils --------------------------------------------------------------------
-(defun string-contains (needle string)
- (and (search needle string :test #'char=) t))
-
-(defun string-grep (needle text &key first-only)
- (_ text
- (split-sequence:split-sequence #\newline _)
- (if first-only
- (find needle _ :test #'string-contains)
- (remove-if-not (alexandria:curry #'string-contains needle) _))))
-
-(defun string-split (delimiters string)
- (split-sequence:split-sequence delimiters string
- :test (lambda (bag ch)
- (find ch bag :test #'char=))))
-
-(defun run-and-echo-shell-command (command &rest args)
- (message command)
- (apply #'run-shell-command command args))
-
-
-(defun mod+ (n increment modulo)
- (mod (+ n increment) modulo))
-
-
-(defun volume ()
- (_ (run-shell-command "amixer sget Master" t)
- (string-grep "Front Left:" _ :first-only t)
- (string-split "[]" _)
- second
- (string-trim "%" _)
- parse-integer))
-
-
-(defun current-frame ()
- (stumpwm::tile-group-current-frame (current-group)))
-
-
-(defun keywordize (string)
- (_ string
- (string-trim (string #\newline) _)
- string-upcase
- (intern _ (find-package :keyword))))
-
-(defparameter *host* (keywordize (machine-instance)))
-
-
-(defmacro ehostcase (&body clauses)
- `(ecase *host* ,@clauses))
-
-(defmacro hostcase (&body clauses)
- `(case *host* ,@clauses))
-
-
-(defcommand speak (text)
- ((:string "Text: "))
- (message text)
- (run-shell-command (format nil "~~/src/dotfiles/bin/say '~A'" text)))
-
-
-(defun seconds->hours (seconds)
- (/ seconds 60 60))
-
-(defun hours->seconds (hours)
- (* hours 60 60))
-
-
-(define-stumpwm-type :integer (input prompt)
- ;; Annoyingly, StumpWM's built-in :number type isn't actually number, but is
- ;; actually just integers. Define a better-named type here.
- (when-let ((n (or (argument-pop input)
- (read-one-line (current-screen) prompt))))
- (handler-case
- (parse-integer n)
- (parse-error (c)
- (declare (ignore c))
- (throw 'error "Integer required.")))))
-
-(define-stumpwm-type :real (input prompt)
- (when-let ((n (or (argument-pop input)
- (read-one-line (current-screen) prompt))))
- (handler-case
- (let ((result (parse-number:parse-number n)))
- (assert (typep result 'real))
- result)
- (error (c)
- (declare (ignore c))
- (throw 'error "Real required.")))))
-
-
-(defun window-match-p (query window)
- "Return whether `window` matches `query`.
-
- `query` must be of the form `(query-type query-value)`.
-
- `query-type` must be one of `:title` or `:class`.
-
- `query-value` must either be a string (which must be matched exactly) or
- a PPCRE scanner.
-
- "
- (destructuring-bind (query-type query) query
- (let ((value (ecase query-type
- (:title (window-title window))
- (:class (window-class window)))))
- (etypecase query
- (string (string= query value))
- (function (ppcre:scan query value))))))
-
-(defun all-windows ()
- "Return a fresh list of all windows on all screens. Yes, all of them."
- (mapcan #'screen-windows *screen-list*))
-
-(defun find-window (query)
- "Find and return the first window that matches `query` under `window-match-p`."
- (find-if (lambda (w) (window-match-p query w)) (all-windows)))
-
-(defun find-windows (query)
- "Find and return a fresh list of all windows that match `query` under `window-match-p`."
- (remove-if-not (lambda (w) (window-match-p query w)) (all-windows)))
-
-(defmacro when-let-window ((symbol title-query) &body body)
- `(when-let ((,symbol (find-window `(:title ,(ppcre:create-scanner ,title-query)))))
- ,@body))
-
-
-;;;; Posture ------------------------------------------------------------------
-(defparameter *posture-thread* nil)
-(defparameter *posture-should-stop* nil)
-(defparameter *posture-paused* nil)
-(defparameter *posture-snooze* nil)
-(defparameter *posture-current* 30)
-(defparameter *posture-min* 5)
-(defparameter *posture-max* (hours->seconds 2))
-
-(defun posture-paused-p ()
- ;; this is the dumbest shit ever, but I can't figure out how to call into
- ;; stumpish from the setguid slock process
- (or *posture-paused* (probe-file "/tmp/.posture-pause")))
-
-(defun posture-snoozed-p ()
- (and *posture-snooze*
- (< (get-universal-time) *posture-snooze*)))
-
-(defcommand posture-pause () ()
- (message "Pausing posture.")
- (setf *posture-paused* t))
-
-(defcommand posture-unpause () ()
- (message "Unpausing posture.")
- (setf *posture-paused* nil))
-
-(defcommand posture-toggle-pause () ()
- (if (setf *posture-paused* (not *posture-paused*))
- (message "Posture is now paused.")
- (message "Posture is now unpaused.")))
-
-(defcommand posture-snooze (hours)
- ((:real "Snooze for how many hours? "))
- (setf *posture-snooze* (+ (hours->seconds hours) (get-universal-time))))
-
-(defun posture-update (delta)
- (setf *posture-current*
- (clamp *posture-min* *posture-max* (* *posture-current* delta))))
-
-(defun posture-query ()
- (speak "Is your posture okay?"))
-
-(defcommand posture-answer-yes () ()
- (message "Good work.")
- (run-shell-command "echo $(epochseconds) 1.0 >> ~/.posture.log")
- (posture-update 11/10))
-
-(defcommand posture-answer-meh () ()
- (message "Better than nothing.")
- (run-shell-command "echo $(epochseconds) 0.5 >> ~/.posture.log"))
-
-(defcommand posture-answer-no () ()
- (message "Try harder.")
- (run-shell-command "echo $(epochseconds) 0.0 >> ~/.posture.log")
- (posture-update 8/10))
-
-(defun posture% ()
- (if *posture-should-stop*
- nil
- (progn (unless (or (posture-paused-p) (posture-snoozed-p))
- (posture-query)
- (sleep 10))
- *posture-current*)))
-
-(defun posture-running-p ()
- (and *posture-thread* (sb-thread:thread-alive-p *posture-thread*)))
-
-(defcommand posture-stop () ()
- (setf *posture-should-stop* t))
-
-(defcommand posture-start () ()
- (setf *posture-should-stop* nil)
- (if (posture-running-p)
- (message "Posture loop was already running.")
- (setf *posture-thread*
- (sb-thread:make-thread
- (lambda ()
- (loop :for seconds = (posture%)
- :while seconds
- :do (sleep seconds))
- (message "Posture loop exiting."))
- :name "Posture thread"))))
-
-
-;;;; Budget ------------------------------------------------------------------
-(defparameter *tz/eastern*
- (local-time:find-timezone-by-location-name "US/Eastern"))
-
-(defparameter *budget/start*
- (local-time:encode-timestamp 0 0 0 0 29 8 2023 :timezone *tz/eastern*))
-
-(defun budget/per-day ()
- (first (losh:read-all-from-file "/home/sjl/Sync/budget/per-day")))
-
-(defun budget/elapsed ()
- (local-time:timestamp-difference (local-time:now) *budget/start*))
-
-(defun budget/days-elapsed ()
- (floor (/ (budget/elapsed) (* 60 60 24))))
-
-(defun budget/in ()
- (* (budget/days-elapsed) (budget/per-day)))
-
-(defun budget/out ()
- (loop :for path :in (directory "/home/sjl/Sync/budget/hosts/*/total")
- :summing (print (first (read-all-from-file (print path))))))
-
-(defun budget/current ()
- (- (budget/in) (budget/out)))
-
-(defcommand budget-dump () ()
- (message
- (sh '("sh" "-c" "tail -n 5 /home/sjl/Sync/budget/hosts/*/records")
- :result-type 'string)))
-
-(defcommand budget () ()
- (message "$~D" (budget/current)))
-
-(defmacro with-budget-file ((f file &rest open-args) &body body)
- `(with-open-file
- (,f (format nil "/home/sjl/Sync/budget/hosts/~(~A~)/~A" *host* ,file)
- ,@open-args)
- ,@body))
-
-(defcommand spend (amount what) ((:integer "Amount: $") (:string "For: "))
- (let ((current (with-budget-file (total "total")
- (first (read-all-from-file total))))
- (timestamp (local-time:to-rfc3339-timestring (local-time:now))))
- (with-budget-file (total "total" :direction :output :if-exists :supersede)
- (print (+ current amount) total))
- (with-budget-file (records "records" :direction :output :if-exists :append :if-does-not-exist :create)
- (print (list timestamp amount what) records))
- (message "Spent $~D for ~A at ~A" amount what timestamp)))
-
-
-;;;; Load ---------------------------------------------------------------------
-(load-module "pass")
-
-;;;; Screenshotting -----------------------------------------------------------
-(defcommand screenshot () ()
- (run-shell-command "screenshot"))
-
-(defcommand save-fucked-screenshot () ()
- (run-shell-command "broken-screenshot"))
-
-(defcommand delete-fucked-screenshot () ()
- (run-shell-command "delete-broken-screenshot"))
-
-
-;;;; Brightness ---------------------------------------------------------------
-(defparameter *brightness-values* #(0 1 5 10 20 30 40 55 70 85 100))
-(defvar *brightness-index* 5)
-
-(defun brightness ()
- (aref *brightness-values* *brightness-index*))
-
-(defun set-brightness (value)
- (run-and-echo-shell-command
- (hostcase
- ((:gro) (format nil "xrandr --output ~A --brightness ~D"
- (hostcase (:gro "eDP"))
- (/ value 100.0)))
- (t (message "Not sure how to set brightness on this machine.")))))
-
-(defun rotate-brightness (delta)
- (setf *brightness-index*
- (mod+ *brightness-index* delta (length *brightness-values*)))
- (set-brightness (brightness)))
-
-
-(defcommand rotate-brightness-up () ()
- (rotate-brightness 1))
-
-(defcommand rotate-brightness-down () ()
- (rotate-brightness -1))
-
-
-;;;; Miscellaneous ------------------------------------------------------------
-(defcommand sane-hsplit () ()
- (hsplit)
- (move-focus :right))
-
-(defcommand sane-vsplit () ()
- (vsplit)
- (move-focus :down))
-
-
-(defcommand move-focus* (direction)
- ((:direction "Enter a direction: "))
- (labels ((in-float-p ()
- (typep (current-group) 'stumpwm::float-group))
- (focus-first-frame ()
- (unless (in-float-p)
- ;; After moving to a new group we don't know which frame is
- ;; focused, and unfortunately Stump doesn't give us a nice way to
- ;; say "focus the leftmost frame" so we'll just move the focus
- ;; a bunch of times and hope it's enough. Sigh.
- (loop :repeat 15
- :until (eql (current-frame)
- (progn (move-focus (ecase direction
- (:left :right)
- (:right :left)))
- (current-frame))))))
- (next-group ()
- (ecase direction
- (:right (gnext))
- (:left (gprev)))
- (focus-first-frame)))
- (unless (in-float-p)
- (banish))
- (if (in-float-p)
- (next-group)
- (let ((frame (current-frame)))
- (move-focus direction)
- (when (eql frame (current-frame))
- (next-group))))))
-
-(defcommand screen-laptop () ()
- (only)
- (loop :with laptop = "eDP-1"
- :with extern = "DP-1"
- :for (output commands) :in `((,laptop ("--auto"))
- (,laptop ("--primary"))
- (,extern ("--off")))
- :do (progn (uiop:run-program `("xrandr" "--output" ,output ,@commands)))))
-
-(defcommand screen-external () ()
- (only)
- (loop :with laptop = "eDP-1"
- :with extern = "DP-1"
- :for (output commands) :in `(
- ;; (,laptop ("--off"))
- (,extern ("--auto"))
- (,extern ("--primary"))
- (,laptop ("--auto"))
- (,laptop ("--left-of" ,extern))
- )
- :do (uiop:run-program `("xrandr" "--output" ,output ,@commands))))
-
-(defcommand vlime () ()
- (load "~/src/dotfiles/vim/bundle/vlime/lisp/start-vlime.lisp")
- (message "Started VLIME"))
-
-(defcommand vlime-port (port) ((:integer "Port: "))
- "Start VLIME on the given port.
-
- Good for bootstrapping a VLIME connection when you accidentally started a
- VLIME instance on another port that you don't want to mess with.
-
- "
- (funcall (read-from-string "vlime-loader::run") port)
- (message "Started VLIME"))
-
-(defcommand toggle-current-mode-line () ()
- (toggle-mode-line (current-screen) (current-head)))
-
-(defcommand toggle-stumptray () ()
- (run-commands "stumptray"))
-
-(defcommand pass-personal () ()
- (let ((pass:*password-store* "/home/sjl/.password-store/")
- (pass:*pass-notification-message* t))
- (pass:pass-copy)))
-
-(defcommand generate-password () ()
- (run-shell-command "genpass | pbc")
- (message "Generated a fresh password and copied it to the clipboard."))
-
-(defcommand kill-and-remove () ()
- (run-commands "kill" "remove"))
-
-(defcommand sleep-machine ()
- ()
- (hostcase
- ((:gro)
- (run-shell-command "exec lock-screen")
- (run-shell-command "systemctl suspend"))
- (t (message "Not sleeping this machine for safety."))))
-
-(defcommand copy-clhs-url (s)
- ((:string "Symbol: "))
- (run-shell-command (format nil "clhs --url 'http://www.lispworks.com/documentation/HyperSpec/' --quiet --open echon '~A' | pbcopy" s)))
-
-(defcommand describe-window () ()
- (show-window-properties))
-
-(defcommand rain () ()
- (_ '("/home/sjl/src/dotfiles/lisp/bin/weather" "-H" "36")
- (losh:sh _ :result-type 'list)
- (mapcar (lambda (line) (ppcre:regex-replace " 1[0-9]:00 " line "^6\\&^*")) _)
- (message "~{~A~^~%~}" _)))
-
-(defcommand mark (thing) ((:string "Mark: "))
- (run-shell-command (format nil "mark ~A" thing)))
-
-(defcommand toggle-zoom-mute () ()
- (when-let-window (win "^Zoom Meeting.*")
- ;; Zoom stupidly won't accept the shortcut unless it's in focus
- (unless (eql (window-group win) (current-group))
- ;; jesus christ stump just export switch-to-group come on
- (gselect (princ-to-string (group-number (window-group win)))))
- (focus-window win t)
- (meta (kbd "M-a"))))
-
-(defcommand end-zoom () ()
- (when-let-window (win "^Zoom Meeting.*")
- (kill-window win))
- (sleep 2)
- (when-let-window (win "^Zoom -.*")
- (kill-window win)))
-
-(defcommand clear-notifications () ()
- (run-shell-command "dunstctl close-all"))
-
-
-
-;;;; Terminal Fonts -----------------------------------------------------------
-(defcommand reload-terminal-font-size ()
- ()
- (setf *terminal-font-size*
- (if (probe-file "/home/sjl/.terminal-font")
- (with-open-file (f "/home/sjl/.terminal-font")
- (read f))
- 11)))
-
-(defparameter *terminal-font-size* (if (probe-file "/home/sjl/.terminal-font")
- (with-open-file (f "/home/sjl/.terminal-font")
- (read f))
- 11))
-
-(defcommand st-font-up ()
- ()
- (loop :repeat 7 :do (meta (kbd "C-S-SunPageUp"))))
-
-(defcommand st-font-down ()
- ()
- (loop :repeat 7 :do (meta (kbd "C-S-SunPageDown"))))
-
-(defcommand st-font-reset ()
- ()
- (meta (kbd "C-S-Home")))
-
-
-;;;; Clipboard/Data Generation ------------------------------------------------
-(load-module "clipboard-history")
-(clipboard-history:start-clipboard-manager)
-
-(defcommand generate-random-uuid () ()
- (run-shell-command "uuidgen | tr -d '\\n' | ~/src/dotfiles/bin/pbcopy")
- (message "Copied random UUID to clipboard."))
-
-(defcommand bee-movie-script () ()
- (run-shell-command "pbeecopy")
- (message "Copied the entire Bee Movie script to clipboard."))
-
-(defcommand urlize-jira-issue () ()
- (let ((issue (str:trim (pbpaste))))
- (if (ppcre:scan "^[A-Z0-9]+-\\d+$" issue)
- (let* ((endpoint (str:trim (run-shell-command "grep endpoint .jira.d/config.yml | sed -e 's/.*: //'" t)))
- (url (format nil "~A/browse/~A" endpoint issue)))
- (pbcopy url)
- (message "Copied ~A to the clipboard." url))
- (message "Clipboard does not look like a JIRA issue."))))
-
-
-;;;; Applications -------------------------------------------------------------
-(defcommand spotify () ()
- (run-or-raise "spotify" '(:class "Spotify")))
-
-(defcommand files () ()
- (run-shell-command "open $HOME"))
-
-(defcommand browser () ()
- (run-or-raise "firefox" '(:class "firefox")))
-
-(defcommand vlc () ()
- (run-or-raise "vlc" '(:class "vlc")))
-
-(defcommand terminal () ()
- (run-shell-command (format nil "st -f 'Ubuntu Mono:size=~D'" *terminal-font-size*)))
-
-(defcommand gcontrol () ()
- (run-or-raise "gcontrol" '(:class "Gnome-control-center")))
-
-(defcommand zoom () ()
- (when-let-window (w "^Zoom Meeting.*")
- (focus-window w t)))
-
-
-;;;; Timers -------------------------------------------------------------------
-(defparameter *pop-timer-minutes* nil)
-(defparameter *pop-timer-seconds* nil)
-
-(defun pop-timer ()
- (if (or (null *pop-timer-minutes*)
- (null *pop-timer-seconds*))
- (message "Pop timer is not configured.")
- (progn
- (message "Setting pop timer for ~D:~2,'0D."
- *pop-timer-minutes* *pop-timer-seconds*)
- (let* ((warning-time 30)
- (total-time (+ (* *pop-timer-minutes* 60) *pop-timer-seconds*))
- (initial-time (- total-time warning-time)))
- (sb-thread:make-thread
- (lambda ()
- (if (plusp initial-time)
- (progn (sleep initial-time)
- (speak "Pop soon.")
- (sleep warning-time))
- (sleep total-time))
- (speak "Pop!"))
- :name "Pop Timer")))))
-
-(defcommand run-pop-timer () ()
- (pop-timer))
-
-(defcommand set-pop-timer (minutes seconds)
- ((:integer "Minutes: ")
- (:integer "Seconds: "))
- (setf *pop-timer-minutes* minutes
- *pop-timer-seconds* seconds))
-
-(defcommand tea-timer (seconds)
- ((:integer "Seconds: "))
- (run-shell-command (format nil "tea ~D" seconds)))
-
-
-;;;; Isk ----------------------------------------------------------------------
-(defcommand send-key (key &optional (win (current-window))) (:key)
- "Send key press and key release events for KEY to window WIN."
- ;; from https://github.com/alezost/stumpwm-config/blob/master/utils.lisp
- (let ((xwin (window-xwin win)))
- (multiple-value-bind (code state) (stumpwm::key-to-keycode+state key)
- (flet ((send (event)
- (xlib:send-event xwin event (xlib:make-event-mask event)
- :display *display*
- :root (screen-root (window-screen win))
- :x 0 :y 0 :root-x 0 :root-y 0
- :window xwin :event-window xwin
- :code code
- :state state)))
- (send :key-press)
- (send :key-release)
- (xlib:display-finish-output *display*)))))
-
-(defun send-keys (keys &key (win (current-window)) (sleep 0))
- (dolist (k keys)
- (send-key (kbd k) win)
- (sleep sleep)))
-
-(defmacro defmultikey (name key compose-keys)
- ;; Unfortunately we can't reliably autogen the name with something like
- ;; (symb 'mk- compose-key) here because things like đ (th) and Đ (TH) would
- ;; case fold to the same name.
- `(progn
- (defcommand ,name () ()
- (send-keys '("Multi_key" ,@(map 'list #'string compose-keys))))
- (define-key *top-map*
- (kbd ,key) ,(string name))))
-
-(defmacro defmultikeys (&rest bindings)
- `(progn ,@(loop for binding :in bindings :collect `(defmultikey ,@binding))))
-
-(defmultikeys
- (isk-l-á "M-a" "'a")
- (isk-u-Á "M-A" "'A")
- (isk-l-é "M-e" "'e")
- (isk-u-É "M-E" "'E")
- (isk-l-í "M-i" "'i")
- (isk-u-Í "M-I" "'I")
- (isk-l-ó "M-o" "'o")
- (isk-u-Ó "M-O" "'O")
- (isk-l-ö "M-m" "\"o")
- (isk-u-Ö "M-M" "\"O")
- (isk-l-ú "M-u" "'u")
- (isk-u-Ú "M-U" "'U")
- (isk-l-ý "M-y" "'y")
- (isk-u-Ý "M-Y" "'Y")
- (isk-l-þ "M-t" "th")
- (isk-u-Þ "M-T" "TH")
- (isk-l-đ "M-d" "dh")
- (isk-u-Đ "M-D" "DH")
- (isk-l-æ "M-h" "ae")
- (isk-u-Æ "M-H" "AE"))
-
-
-;;;; Key Mapping --------------------------------------------------------------
-;;; Conventions:
-;;;
-;;; * Hyper-dir: move focus
-;;; * Hyper-Shift-dir: move window
-;;; * Hyper-Shift-Control-dir: swap window
-;;; * Hyper-F*: hardware
-;;; * Shift-F*: timers
-;;; * Hyper-Super-*: layout
-;;; * Hyper-*: miscellaneous
-
-(defmacro define-top-keys (&body keyforms)
- `(progn ,@(loop :for form :in keyforms
- :collect `(define-key *top-map*
- (kbd ,(first form))
- ,(second form)))))
-
-
-(define-top-keys ;; miscellaneous
- ("H-m" "terminal")
- ("H-M" "mark")
- ("H-SunPageUp" "st-font-up")
- ("H-SunPageDown" "st-font-down")
- ("H-Home" "st-font-reset")
- ("H-\\" "pass-personal")
- ("H-|" "generate-password")
- ("H-b" "browser")
- ("H-O" "spotify")
- ("H-o" "files")
- ("H-z" "zoom")
- ("H-Z" "toggle-zoom-mute")
- ("C-H-Z" "end-zoom")
- ("F26" "prev")
- ("S-F26" "next")
- ("H-q" "exec lock-screen")
- ("H-y" "screenshot")
- ("H-g" "gcontrol")
- ("H-f" "save-fucked-screenshot")
- ("H-F" "delete-fucked-screenshot")
- ("H-R" "loadrc")
- ("H-r" "rain")
- ("H-V" "vlc")
- ("H-4" "budget")
- ("H-$" "spend")
- ("C-BackSpace" "clear-notifications")
- )
-
-(define-top-keys ;; clipboard
- ("H-c" "show-clipboard-history")
- ("H-C" "clear-clipboard-history")
- ("H-u" "generate-random-uuid")
- ("H-B" "bee-movie-script")
- ("M-H-u" "urlize-jira-issue"))
-
-(define-top-keys ;; movement
- ("H-h" "move-focus* left")
- ("H-j" "move-focus down")
- ("H-k" "move-focus up")
- ("H-l" "move-focus* right")
-
- ("H-H" "move-window left")
- ("H-J" "move-window down")
- ("H-K" "move-window up")
- ("H-L" "move-window right")
-
- ("H-1" "gselect 1")
- ("H-2" "gselect 2")
- ("H-3" "gselect 3")
-
- ("H-!" "gmove 1")
- ("H-@" "gmove 2")
- ("H-#" "gmove 3")
-
- ("C-H-H" "exchange-direction left")
- ("C-H-J" "exchange-direction down")
- ("C-H-K" "exchange-direction up")
- ("C-H-L" "exchange-direction right")
-
- ("H-`" "next")
- ("H-~" "prev") ;; "
- ("H-n" "next-in-frame")
- ("H-p" "prev-in-frame")
- ("H-N" "pull-hidden-next")
- ("H-P" "prev-in-frame")
-
- ("H-," "pull-from-windowlist"))
-
-(define-top-keys ;; splitting
- ("H-s" "sane-vsplit")
- ("H-v" "sane-hsplit")
- ("H-=" "balance-frames"))
-
-(define-top-keys ;; killing
- ("H-w" "delete")
- ("H-W" "kill")
- ("H-BackSpace" "remove")
- ("S-H-BackSpace" "kill-and-remove"))
-
-(define-top-keys ;; naming
- ("H-'" "title"))
-
-(define-top-keys ;; sound
- ("H-F1" "mute")
- ("H-F2" "exec amixer -q sset Master 5%-")
- ("H-F3" "exec amixer -q sset Master 5%+"))
-
-(define-top-keys ;; screen
- ("H-F5" "rotate-brightness-down")
- ("H-F6" "rotate-brightness-up")
- ("H-F7" "screen-laptop")
- ("H-F8" "screen-external"))
-
-(define-top-keys ;; layout
- ("s-H-t" "restore-from-file thirds")
- ("s-H-m" "restore-from-file dev")
- ("s-H-s" "restore-from-file streaming")
- ("s-H-w" "restore-from-file work")
- ("s-H-z" "restore-from-file zoom"))
-
-(define-top-keys ;; timers
- ("s-F7" "tea-timer")
- ("s-F9" "run-pop-timer")
- ("s-F8" "set-pop-timer")
- ("s-p" "posture-start")
- ("s-P" "posture-stop")
- ("s-y" "posture-answer-yes")
- ("s-h" "posture-answer-meh")
- ("s-n" "posture-answer-no")
- ("s-\\" "posture-toggle-pause")
- ("s-o" "posture-snooze"))
-
-(define-top-keys ;; stump
- ("Pause" "terminal") ; jesus christ
- ("H-F9" "sleep-machine")
- ("H-F10" "toggle-stumptray")
- ("H-F11" "toggle-current-mode-line")
- ("H-F12" "refresh-heads"))
-
-
-;; (stumpwm::unbind-remapped-keys)
-(define-remapped-keys
- '(("st-256color"
- ("s-c" . "C-C")
- ("s-v" . "C-V")
- ("C-=" . "S-C-SunPageUp")
- ("C--" . "S-C-SunPageDown")
- ("C-0" . "S-C-Home"))
- ("(firefox|Google-chrome|Chromium-browser)"
- ("s-1" . "C-S-Tab")
- ("s-2" . "C-Tab")
- ("C-a" . "Home")
- ("C-e" . "End")
- ;; I always try to hit ctrl-d to kill a browser window because I'm so used
- ;; to terminal windows, and it ends up bookmarking the damn page. In the
- ;; interest of not having a random collection of bookmarks grow over time,
- ;; I'll just add a mapping to compensate for my stupid brain.
- ("C-d" . "C-w")
- ;; todo debug why this breaks a really fast C-a-k roll
- ;; ("C-a" . "Home")
- ;; ("C-e" . "End")
- ("s-a" . "C-a")
- ("s-d" . "C-d")
- ("s-l" . "C-l")
- ("s-t" . "C-t")
- ("s-w" . "C-w")
- ("s-r" . "C-r")
- ("s-f" . "C-f")
- ("s-z" . "C-z")
- ("s-x" . "C-x")
- ("s-c" . "C-c")
- ("s-v" . "C-v"))
- (""
- ("s-z" . "C-z")
- ("s-x" . "C-x")
- ("s-c" . "C-c")
- ("s-v" . "C-v"))))
-
-
-;;;; Sensors ------------------------------------------------------------------
-(defun ? (obj &rest keys)
- (if (null keys)
- obj
- (apply #'? (etypecase obj
- (hash-table (gethash (first keys) obj)))
- (rest keys))))
-
-(defun parse-sensors ()
- ;; sensors -j is stupid and will output errors before the actual output on
- ;; standard out, instead of putting them on standard err like a reasonable
- ;; program, e.g.:
- ;;
- ;; ERROR: Can't get value of subfeature temp1_input: Can't read
- ;; {
- ;; "iwlwifi_1-virtual-0":{ … },
- ;; …
- ;;
- ;; So we'll have to drop the `ERROR` lines before we can get to the actual
- ;; goddamn JSON. UNIX programs are so great.
- (let ((s (losh:sh '("sensors" "-j") :result-type 'stream)))
- (loop :while (char= #\E (peek-char nil s)) :do (read-line s))
- (jarl:read t s)))
-
-(defparameter *sensors-refresh-delay* 5.0 "How long between sensor refreshes (in seconds).")
-(defparameter *sensors-next-refresh* nil)
-(defparameter *sensors-cache* nil)
-
-(defun sensors% (&aux (sensors (parse-sensors)))
- (hostcase
- (:ouroboros (format nil "[CPU ~D°C] [GPU ~D°C ~D°C ~D°C]"
- (round (? sensors "nct6779-isa-0290" "CPUTIN" "temp2_input"))
- (round (? sensors "amdgpu-pci-4500" "edge" "temp1_input"))
- (round (? sensors "amdgpu-pci-4500" "junction" "temp2_input"))
- (round (? sensors "amdgpu-pci-4500" "mem" "temp3_input"))))
- (:gro (format nil "[CPU ~D°C] [GPU ~D°C]"
- (round (? sensors "thinkpad-isa-0000" "CPU" "temp1_input"))
- (round (? sensors "amdgpu-pci-0600" "edge" "temp1_input"))))
- (t "?")))
-
-(defun sensors (&aux (now (get-internal-real-time)))
- (if (or (null *sensors-next-refresh*)
- (>= now *sensors-next-refresh*))
- (setf *sensors-next-refresh* (+ now (* internal-time-units-per-second *sensors-refresh-delay*))
- *sensors-cache* (sensors%))
- *sensors-cache*))
-
-(defun sensors-modeline (ml)
- (declare (ignore ml))
- (sensors))
-
-(add-screen-mode-line-formatter #\S #'sensors-modeline)
-
-
-;;;; Modeline -----------------------------------------------------------------
-(load-module "battery-portable")
-(load-module "cpu")
-(load-module "hostname")
-(load-module "mem")
-
-(defun ensure-mode-line ()
- (when (not (stumpwm::head-mode-line (current-head)))
- (toggle-mode-line (current-screen) (current-head))))
-
-
-(defun configure-modeline ()
- (setf
- *time-modeline-string*
- "%a %b %e %H:%M"
-
- cpu::*cpu-usage-modeline-fmt*
- "^[~A~3D%^]"
-
- cpu::*cpu-modeline-fmt*
- "[%c] [%f]"
-
- mem::*mem-modeline-fmt*
- "%b"
-
- *screen-mode-line-format*
- (append
- (list "[^B%n^b@%h] %W^>")
-
- #+todo-some-day (list ;; "(V "
- ;; ;; '(:eval (volume))
- ;; ")"
- " ")
-
- ;; battery and brightness for laptops
- (hostcase
- ((:gro)
- '("(B %B)"
- " (BR "
- (:eval (princ-to-string (brightness)))
- "%)")))
-
- ;; temp, cpu, mem, time, tray
- #+no (list "(TEMP %S) (CPU %C) (MEM %M) %d %T")
- (list "(CPU %C) (MEM %M) %d %T")
- ))
-
- (setf *mode-line-timeout* 10)
- (setf *mode-line-background-color* "#111111")
-
- (ensure-mode-line)
- )
-
-(configure-modeline)
-
-
-;;;; System Tray --------------------------------------------------------------
-(load-module "stumptray")
-(defvar *tray-loaded* (run-commands "stumptray"))
-
-
-;;;; Unbreak Typing -----------------------------------------------------------
-(defun stumpwm::input-insert-hyphen-or-space (input key)
- (declare (ignore key))
- (input-insert-char input #\space))
-
-
-;;;; Startup ------------------------------------------------------------------
-;; (defvar *dropbox*
- ;; (run-shell-command "~/.dropbox-dist/dropboxd"))
-
-#+no(defvar *dunst*
- (run-shell-command "/usr/bin/dunst -conf ~/.dunstrc"))
-
-(when (probe-file "/home/sjl/.stumpwmrc.local")
- (load "/home/sjl/.stumpwmrc.local"))
-
-
-#;;; Scratch ------------------------------------------------------------------
--- a/vim/custom-dictionary.utf-8.add Sun May 18 14:59:11 2025 -0400
+++ b/vim/custom-dictionary.utf-8.add Wed Aug 27 16:19:24 2025 -0400
@@ -336,3 +336,235 @@
skald
dwarven
tradeoff
+Snakefile
+Snakemake
+melanosomes
+melanosome
+threonine
+multiomics
+ANOVA
+μL
+Opentrons
+walkthrough
+nanopore
+intron
+retrotranspose
+retrotransposition
+transposons
+retrotransposon
+transposon
+retrotransposons
+wildcard
+PIBS
+Thinkpad
+HG545
+Lenovo
+methylation
+CpG
+thermocycler
+CAS9
+RNP
+gDNA
+QuickCIP
+dephosphorylation
+labware
+thermocycling
+Torrin
+thermocycle
+Neisseria
+Gonorrhoeae
+Azithromycin
+Ceftriaxone
+Quinolone
+Quinolones
+folates
+sulfanilamide
+prodrug
+diethylene
+Prontosil
+transpeptidase
+lactam
+crosslinking
+crosslink
+Unicycler
+FASTQs
+Jupyter
+topoisomerases
+topoisomerase
+quinolone
+ribosomes
+macrolide
+Macrolides
+Trimmomatic
+microbiome
+BI529
+BS522
+BS521
+NCBI
+conda
+Dhatri
+OOMs
+BI602
+lactamase
+backends
+webapp
+webapps
+B2
+ggplot2
+AMRFinder
+AMRFinderPlus
+BI545
+PIBS800
+isoforms
+gyrase
+handwave
+SNPs
+Prensner
+acetylation
+arginine
+histone
+histones
+octamer
+heterochromatin
+trimethylated
+BIOINF
+astrocyte
+Armis
+Taubman
+H3K27M
+polycomb
+Cristian
+DBvolve
+sqlite
+Bree
+Midgewater
+Chetwood
+noncanonical
+XRT
+Snakemake's
+RiboTIE
+rawdogging
+multimapping
+Riboseq
+X11
+JS
+GRSA
+CCTC
+BLASTs
+mosaicism
+amyloid
+microcentrifuge
+P1000
+P10
+Trypan
+mL
+vortexer
+OOM
+Nanopal
+Slurm
+hardcode
+SDS
+dropwise
+vortexing
+segfaulting
+segfaulted
+IGV
+hardcoding
+nonlocal
+basecalling
+systemd
+basecall
+basecalls
+basecaller
+basecalled
+Cas9
+samtools
+mux
+hostnames
+wifi
+Minimap2
+hedera
+Ansible
+SMaHT
+NOP
+unboxing
+NUCs
+Torrin's
+Margit
+hederas
+auth
+MinKNOW
+MinION
+deduplicate
+snakemake
+repo's
+pericentromeric
+MEI
+inodes
+AluYb
+AluYa
+FASTQ
+BAM
+pseudobulk
+MEIs
+sneakernet
+CADIA
+deduplicates
+unmerged
+DCMB
+EECS
+GM12878
+WGS
+VCFs
+traceback
+ethernet
+reenabled
+deduplicated
+dedupe
+Alu
+Alus
+coreutils
+CRISPR
+overcounting
+speedrun
+FF6
+kimchi
+SAA
+DJ'ed
+TA'ed
+wildcards
+counterintuitive
+ish
+ECC
+Ypsi
+etouffée
+Ansibilize
+fstab
+Datura
+Ansibilized
+indels
+DSBs
+NHEJ
+LINEs
+LRS
+SV
+SRS
+HC
+transposase
+ATAC
+clusterfuck
+Kerrytown
+Friendsgiving
+LVM
+basecaller
+flowcell
+reparse
+venv
+env
+concat
+Beszel
+basecallers
+untar
+unclustered
+helicase
+reauthenticate
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/ftplugin/nextflow/folding.vim Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,16 @@
+setlocal foldmethod=expr
+setlocal foldexpr=GetNextflowFold(v:lnum)
+
+function! GetNextflowFold(lnum)
+ let line = getline(a:lnum)
+
+ if line =~ '^\S.*{$'
+ return '>1'
+ elseif line =~ '^}$'
+ return '<1'
+ elseif line =~ '^\S'
+ return 0
+ else
+ return "="
+ end
+endfunction
--- a/vim/ftplugin/snakemake/folding.vim Sun May 18 14:59:11 2025 -0400
+++ b/vim/ftplugin/snakemake/folding.vim Wed Aug 27 16:19:24 2025 -0400
@@ -9,16 +9,25 @@
let thisline = getline(a:lnum)
- " blank lines end folds
+ " two blank lines end folds
if thisline =~? '\v^\s*$'
+ if ActualPreviousLineNonblank(a:lnum)
+ return "="
+ endif
return '-1'
" start fold on top level rules or python objects
elseif thisline =~? '\v^(rule|def|checkpoint|class)'
return ">1"
elseif thisline =~? '\v^\S'
- if PreviousLineIndented(a:lnum) && NextRuleIndented(a:lnum)
- return ">1"
+ if IsEmpty(a:lnum+1)
+ if IsEmpty(a:lnum+2)
+ return ">1"
+ endif
endif
+ return "0"
+ " if PreviousLineIndented(a:lnum) && NextRuleIndented(a:lnum)
+ " return ">1"
+ " endif
endif
return "="
@@ -59,3 +68,35 @@
return 0
endfunction
+
+function! ActualPreviousLineNonblank(lnum)
+ let current = a:lnum - 1
+
+ if current >= 1
+ let thisline = getline(current)
+ if thisline =~? '\v^\s*\S'
+ return 1
+ else
+ return 0
+ endif
+ endwhile
+
+ return 0
+endfunction
+
+function! IsEmpty(lnum)
+ let numlines = line('$')
+ if a:lnum > numlines
+ return 0
+ elseif a:lnum < 1
+ return 0
+ else
+ let thisline = getline(a:lnum)
+ if thisline =~? '\v^$'
+ return 1
+ else
+ return 0
+ endif
+ endif
+endfunction
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/syntax/htmlten.vim Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,38 @@
+" based on the htmldjango syntax
+
+if exists("b:current_syntax")
+ finish
+endif
+
+if !exists("main_syntax")
+ let main_syntax = 'html'
+endif
+
+runtime! syntax/html.vim
+unlet b:current_syntax
+
+syn region tenArgument contained start=/"/ skip=/\\"/ end=/"/
+
+syn match tenError "%}\|}}\|#}"
+syn match tenTagError contained "#}\|{{\|[^%]}}\|[#]"
+syn match tenVarError contained "#}\|{%\|%}\|[<>!#]"
+
+syn cluster tenBlocks add=tenTagBlock,tenVarBlock,tenComment,tenComBlock
+
+syn region tenTagBlock start="{%" end="%}" contains=tenArgument,tenTagError display containedin=ALLBUT,@djangoBlocks
+syn region tenVarBlock start="{{" end="}}" contains=tenArgument,tenVarError display containedin=ALLBUT,@djangoBlocks
+syn region tenComment start="{%\s*comment\(\s\+.\{-}\)\?%}" end="{%\s*endcomment\s*%}" containedin=ALLBUT,@tenBlocks
+syn region tenComBlock start="{#" end="#}" containedin=ALLBUT,@tenBlocks
+
+
+hi def link tenTagBlock PreProc
+hi def link tenVarBlock PreProc
+hi def link tenComment Comment
+hi def link tenComBlock Comment
+
+hi def link tenArgument Constant
+hi def link tenError Error
+hi def link tenTagError Error
+hi def link tenVarError Error
+
+let b:current_syntax = "htmlten"
--- a/vim/syntax/lisp.vim Sun May 18 14:59:11 2025 -0400
+++ b/vim/syntax/lisp.vim Wed Aug 27 16:19:24 2025 -0400
@@ -63,8 +63,9 @@
" start prefixparameters modifiers directive
syn match lispStringFormatDirective contained /\v[~](([-+]?[0-9]*|'.),?)*(:\@|\@:|:|\@)?./
+syn match lispStringAprilComment contained /\v⍝(.)*/
-syn region lispString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=lispStringFormatDirective,@Spell
+syn region lispString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=lispStringFormatDirective,lispStringAprilComment,@Spell
if exists("g:lisp_instring")
syn region lispInString keepend matchgroup=Delimiter start=+"(+rs=s+1 skip=+|.\{-}|+ matchgroup=Delimiter end=+)"+ contains=@lispBaseListCluster,lispInStringString
@@ -106,6 +107,7 @@
hi def link lispAtomMark lispMark
hi def link lispInStringString lispString
hi def link lispStringFormatDirective SpecialChar
+ hi def link lispStringAprilComment lispComment
hi def link lispAtom Identifier
hi def link lispAtomBarSymbol Special
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/ultisnips-bullshit/README.markdown Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,5 @@
+This directory would be named "snippets" like a reasonable person would
+immediately think to name it, except that UltiSnips hardcodes that one
+particular directory name and magically parses it as some other snippet format,
+which fails with an inscrutable error. So we have to name it something else.
+Great first 30 second experience, UltiSnips.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/ultisnips-bullshit/html.snippets Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,191 @@
+snippet doctype "HTML - 5.0 (doctype)" b
+<!DOCTYPE html>
+endsnippet
+
+snippet html "tag: html" bi
+<html>
+ $1
+</html>
+endsnippet
+
+snippet head "tag: head" b
+<head>
+ $1
+</head>
+endsnippet
+
+snippet body "tag: body" b
+<body>
+ $1
+</body>
+endsnippet
+
+snippet nav "tag: nav" b
+<nav>
+ $1
+</nav>
+endsnippet
+
+snippet title "tag: title" b
+<title>$1</title>$0
+endsnippet
+
+snippet form "tag: form" b
+<form action="${1:/path}" method="${2:get}">
+ $0
+</form>
+endsnippet
+
+snippet fieldset "tag:fieldset" b
+<fieldset>
+ <legend>$1</legend>
+ $2
+</fieldset>
+endsnippet
+
+snippet label "tag: label" b
+<label for="${1:field}">$2</label>
+endsnippet
+
+snippet input "tag: input" b
+<input id="${1:field}" type="${2:text}" name="${3:name}" value="$4" />
+endsnippet
+
+snippet submit "tag: submit" b
+<input type="submit" value="${1:Search}" />
+endsnippet
+
+snippet style "stylesheet" b
+<link href="${1:style.css}" rel="stylesheet" type="text/css" />
+endsnippet
+
+snippet main "tag: main" b
+<main>
+ $0
+</main>
+endsnippet
+
+snippet header "tag: header" b
+<header>
+ $0
+</header>
+endsnippet
+
+snippet footer "tag: footer" b
+<footer>
+ $0
+</footer>
+endsnippet
+
+snippet table "tag: table" b
+<table>
+ $0
+</table>
+endsnippet
+
+snippet thead "tag: thead" b
+<thead>
+ $0
+</thead>
+endsnippet
+
+snippet tbody "tag: tbody" b
+<tbody>
+ $0
+</tbody>
+endsnippet
+
+snippet tfoot "tag: tfoot" b
+<tfoot>
+ $0
+</tfoot>
+endsnippet
+
+
+snippet tr "tag: tr" b
+<tr>
+ $0
+</tr>
+endsnippet
+
+snippet td "tag: td" b
+<td>$1</td>
+endsnippet
+
+snippet th "tag: th" b
+<th>$1</th>
+endsnippet
+
+snippet href "tag: a href" i
+<a href="${1:https://example.com}">${2:Text}</a>
+endsnippet
+
+snippet " ." "class" i
+ class="$1"
+endsnippet
+
+snippet P "tag: p" b
+<p>
+ $1
+</p>
+endsnippet
+
+snippet p "tag: p" b
+<p>$1</p>
+endsnippet
+
+snippet legend "tag: legend" b
+<legend>$1</legend>
+endsnippet
+
+snippet span "tag: span" b
+<span$1>$2</span>
+endsnippet
+
+snippet div "tag: div" b
+<div$1>$2</div>
+endsnippet
+
+snippet Div "tag: div" b
+<div$1>
+ $2
+</div>
+endsnippet
+
+snippet ul "tag: ul" b
+<ul>
+ $1
+</ul>
+endsnippet
+
+snippet li "tag: li" b
+<li>$1</li>
+endsnippet
+
+snippet h1 "tag: h1" b
+<h1>$1</h1>
+endsnippet
+
+snippet h2 "tag: h2" b
+<h2>$1</h2>
+endsnippet
+
+snippet h3 "tag: h3" b
+<h3>$1</h3>
+endsnippet
+
+snippet h4 "tag: h4" b
+<h4>$1</h4>
+endsnippet
+
+snippet h5 "tag: h5" b
+<h5>$1</h5>
+endsnippet
+
+snippet h6 "tag: h6" b
+<h6>$1</h6>
+endsnippet
+
+snippet script "tag: script" b
+<script src="$1"></script>
+endsnippet
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/ultisnips-bullshit/htmldjango.snippets Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,47 @@
+extends html
+
+snippet % "Tag" i
+{% ${1:tag} %}
+ $0
+{% end %}
+endsnippet
+
+snippet { "Var" i
+{{ $1 }}$0
+endsnippet
+
+snippet include "include" b
+{% include "${1:foo.html}" %}
+endsnippet
+
+snippet includewith "include with" b
+{% include "${1:foo.html}" with ${2:var}=${3:val} ${4:only }%}
+endsnippet
+
+snippet with "With" b
+{% with ${1:var}=${2:val} %}
+ $0
+{% end %}
+endsnippet
+
+snippet for "For" b
+{% for ${1:var} in ${2:val} %}
+ $0
+{% endfor %}
+endsnippet
+
+snippet if "If" b
+{% if ${1:cond} %}
+ $0
+{% endif %}
+endsnippet
+
+snippet else "Else" b
+{% else %}
+ $0
+endsnippet
+
+snippet elif "Elif" b
+{% elif ${1:cond} %}
+ $0
+endsnippet
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/ultisnips-bullshit/htmlten.snippets Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,29 @@
+extends html
+
+snippet % "Tag" i
+{% ${1:tag} %}
+ $0
+{% end %}
+endsnippet
+
+snippet { "Var" i
+{{ $1 }}$0
+endsnippet
+
+snippet section "Section (single line)" i
+{% section ${1:name} %}$2{% end %}
+endsnippet
+
+snippet Section "Section (multiline)" i
+{% section ${1:name} %}
+ $2
+{% end %}
+endsnippet
+
+snippet template "Template" b
+{% template ${1:name} (${2::extends ${3:base}}) (&key$4) %}
+
+$5
+
+{% end %}
+endsnippet
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/ultisnips-bullshit/markdown.snippets Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,8 @@
+snippet scrum "Scrum template" b
+Scrum:
+
+* Yesterday:
+ * $1
+* Today:
+ * $2
+endsnippet
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/ultisnips-bullshit/python.snippets Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,27 @@
+snippet #! "#!" b
+#!/usr/bin/env python3
+endsnippet
+
+snippet ifmain "if main" b
+if __name__ == '__main__':
+ ${1:pass}
+endsnippet
+
+snippet p "print()" b
+print($1)
+endsnippet
+
+snippet i "if" b
+if ${1:cond}:
+ ${2:pass}
+endsnippet
+
+snippet ei "elif" b
+elif ${1:cond}:
+ ${2:pass}
+endsnippet
+
+snippet e "else" b
+else:
+ ${1:pass}
+endsnippet
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/ultisnips-bullshit/r.snippets Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,7 @@
+snippet lib "library()" b
+library(${1:name})
+endsnippet
+
+snippet inst "install.packages()" b
+install.packages("${1:name}")
+endsnippet
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/ultisnips-bullshit/sh.snippets Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,15 @@
+snippet sete "set -euo pipefail" b
+set -euo pipefail
+endsnippet
+
+snippet = "var" b
+${1:var}="${2:val}"
+endsnippet
+
+snippet #! "#!" b
+#!/usr/bin/env bash
+endsnippet
+
+snippet == "VAR=${$VAR:-default}" b
+${1:VAR}="\$\{$1:-${2:default}\}"
+endsnippet
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/ultisnips-bullshit/tex.snippets Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,85 @@
+global !p
+def math():
+ return vim.eval('vimtex#syntax#in_mathzone()') == '1'
+endglobal
+
+# Structure -------------------------------------------------------------------
+snippet b "An environment" b
+\begin{$1}
+ $0
+\end{$1}
+endsnippet
+
+snippet sec "\section*" b
+\section*{$1}$0
+endsnippet
+
+snippet ssec "\subsection*" b
+\subsection*{$1}$0
+endsnippet
+
+snippet desc "\begin{description}" b
+\begin{description}
+ $0
+\end{description}
+endsnippet
+
+snippet lst "\begin{lstlisting}" b
+\begin{lstlisting}
+$0
+\end{lstlisting}
+endsnippet
+
+snippet i "\item[]" b
+\item[$1]$0
+endsnippet
+
+snippet eq "Equation environment" b
+\begin{equation*}
+\begin{split}
+ $0
+\end{split}
+\end{equation*}
+endsnippet
+
+snippet jot "Set jot length (should go between equation and split envs)" b
+\setlength{\jot}{${1:10}pt}$0
+endsnippet
+
+# Markup ----------------------------------------------------------------------
+
+snippet href "The hyperref package's \href{}{} command (for url links)"
+\href{${1:url}}{${2:display name}}$0
+endsnippet
+
+snippet url "A linked URL"
+\url{${1:url}}$0
+endsnippet
+
+snippet fn "Footnote"
+\footnote{${1:Note}}$0
+endsnippet
+
+snippet em "\emph{}"
+\emph{${1:${VISUAL:}}}$0
+endsnippet
+
+# Math ------------------------------------------------------------------------
+snippet "(^|[^a-zA-Z])mm" "Inline LaTeX math" rA
+`!p snip.rv = match.group(1)`\$ ${1:${VISUAL:}} \$$0
+endsnippet
+
+context "math()"
+snippet ff "(math) \frac{}{}"
+\frac{$1}{$2}$0
+endsnippet
+
+context "math()"
+snippet rm "(math) \mathrm{}"
+\mathrm{$1}$0
+endsnippet
+
+context "math()"
+snippet __ "(math) _{}" iA
+_{ $1 }$0
+endsnippet
--- a/vim/vimrc Sun May 18 14:59:11 2025 -0400
+++ b/vim/vimrc Wed Aug 27 16:19:24 2025 -0400
@@ -2,8 +2,9 @@
" Author: Steve Losh <steve@stevelosh.com>
" Source: https://hg.stevelosh.com/dotfiles/file/tip/vim/vimrc
+" See also vimrc-minimal for the rest of this.
+
" Preamble ---------------------------------------------------------------- {{{
-"
set shell=/bin/bash\ --login
filetype off
@@ -12,39 +13,12 @@
set nocompatible
" }}}
-" Basic options ----------------------------------------------------------- {{{
-
-set modelines=0
-set autoindent
-set showmode
-set showcmd
-set hidden
-set visualbell
-set ttyfast
-set ruler
-set backspace=indent,eol,start
-set nonumber
-set norelativenumber
-set laststatus=2
-set history=1000
-set undofile
-set undoreload=10000
-set list
-set listchars=tab:▸\ ,eol:¬,extends:❯,precedes:❮
-set lazyredraw
-set matchtime=3
-set showbreak=↪
-set splitbelow
-set splitright
-set autowrite
-set autoread
-set shiftround
-set title
-set linebreak
-set colorcolumn=+1
-set diffopt+=vertical
-
-" Spelling
+" Load Minimal ------------------------------------------------------------ {{{
+
+source ~/.vimrc-minimal
+
+" }}}
+" Spelling ---------------------------------------------------------------- {{{
"
" There are three dictionaries I use for spellchecking:
"
@@ -63,136 +37,15 @@
set spellfile=~/.vim/custom-dictionary.utf-8.add,~/.vim-local-dictionary.utf-8.add
nnoremap zG 2zg
-" Don't try to highlight lines longer than 500 characters.
-set synmaxcol=500
-
-" Time out on key codes but not mappings.
-" Basically this makes terminal Vim work sanely.
-set notimeout
-set ttimeout
-set ttimeoutlen=10
-
-" Make Vim able to edit crontab files again.
-set backupskip=/tmp/*,/private/tmp/*"
-
-" Better Completion
-set complete=.,w,b,u,t
-set completeopt=longest,menuone
-inoremap <c-o> <c-x><c-o>
-
-" Save when losing focus
-au FocusLost * :silent! wall
-
-" Leader
-let mapleader = ","
-let maplocalleader = "\\"
-
-" Cursorline {{{
-" Only show cursorline in the current window and in normal mode.
-
-augroup cline
- au!
- au WinLeave,InsertEnter * set nocursorline
- au WinEnter,InsertLeave * set cursorline
-augroup END
-
-" }}}
-" cpoptions+=J, dammit {{{
-
-" Something occasionally removes this. If I manage to find it I'm going to
-" comment out the line and replace all its characters with 'FUCK'.
-augroup twospace
- au!
- au BufRead * :set cpoptions+=J
-augroup END
-
-" }}}
-" Trailing whitespace {{{
-" Only shown when not in insert mode so I don't go insane.
-
-augroup trailing
- au!
- au InsertEnter * :set listchars-=trail:⌴
- au InsertLeave * :set listchars+=trail:⌴
-augroup END
+" Invoke spellcheck in insert mode with c-s, or c-S to do it and autotake the
+" first suggestion for stuff I intentually use spellcheck to type.
+inoremap <c-s> <c-x><c-s>
+inoremap <c-s-s> <c-x><c-s><c-n><c-y>
" }}}
-" Wildmenu completion {{{
-
-set wildmenu
-set wildmode=list:longest
-
-set wildignore+=.hg,.git,.svn " Version control
-set wildignore+=*.aux,*.out,*.toc " LaTeX intermediate files
-set wildignore+=*.jpg,*.bmp,*.gif,*.png,*.jpeg " binary images
-set wildignore+=*.o,*.obj,*.exe,*.dll,*.manifest " compiled object files
-set wildignore+=*.spl " compiled spelling word lists
-set wildignore+=*.sw? " Vim swap files
-set wildignore+=*.DS_Store " OSX bullshit
-
-set wildignore+=*.luac " Lua byte code
-
-set wildignore+=migrations " Django migrations
-set wildignore+=*.pyc " Python byte code
-
-set wildignore+=*.orig " Merge resolution files
-
-set wildignore+=*.fasl " Lisp FASLs
-set wildignore+=*.dx64fsl " CCL
-set wildignore+=*.lx64fsl " CCL
-
-" }}}
-" Line Return {{{
-
-" Make sure Vim returns to the same line when you reopen a file.
-" Thanks, Amit
-augroup line_return
- au!
- au BufReadPost *
- \ if line("'\"") > 0 && line("'\"") <= line("$") |
- \ execute 'normal! g`"zvzz' |
- \ endif
-augroup END
-
-" }}}
-" Tabs, spaces, wrapping {{{
-
-set tabstop=8
-set shiftwidth=4
-set softtabstop=4
-set expandtab
-set wrap
-set textwidth=80
-set formatoptions=qrn1j
-set colorcolumn=+1
-
-" }}}
-" Backups {{{
-
-set backup " enable backups
-set noswapfile " it's 2013, Vim.
-
-set undodir=~/.vim/tmp/undo// " undo files
-set backupdir=~/.vim/tmp/backup// " backups
-set directory=~/.vim/tmp/swap// " swap files
-
-" Make those folders automatically if they don't already exist.
-if !isdirectory(expand(&undodir))
- call mkdir(expand(&undodir), "p")
-endif
-if !isdirectory(expand(&backupdir))
- call mkdir(expand(&backupdir), "p")
-endif
-if !isdirectory(expand(&directory))
- call mkdir(expand(&directory), "p")
-endif
-
-" }}}
-" Color scheme {{{
-
-syntax on
+" Color scheme ------------------------------------------------------------ {{{
+
set termguicolors
-set background=dark
let g:badwolf_tabline = 2
let g:badwolf_html_link_underline = 0
colorscheme goodwolf
@@ -207,81 +60,11 @@
" Highlight VCS conflict markers
match ErrorMsg '^\(<\|=\|>\)\{7\}\([^=].\+\)\?$'
-" }}}
-
-" }}}
-" Abbreviations & Digraphs ------------------------------------------------ {{{
-
-iabbrev todo TODO
-
-silent! digr -. 8230 "U+2026=… HORIZONTAL ELLIPSIS
-silent! digr !, 8816 "U+2270=≰ NEITHER LESS-THAN NOR EQUAL TO
-silent! digr !. 8817 "U+2271=≱ NEITHER GREATER-THAN NOR EQUAL TO
-silent! digr es 8337 "U+2091=ₑ SUBSCRIPT E
-silent! digr xs 8339 "U+2093=ₓ SUBSCRIPT X
-silent! digr ls 8343 "U+2097=ₗ SUBSCRIPT L
-silent! digr ms 8344 "U+2098=ₗ SUBSCRIPT M
-silent! digr ns 8345 "U+2099=ₙ SUBSCRIPT N
-silent! digr ps 8346 "U+209A=ₚ SUBSCRIPT P
-silent! digr ss 8347 "U+209B=ₛ SUBSCRIPT S
-silent! digr ts 8348 "U+209C=ₜ SUBSCRIPT T
-silent! digr >< 8652 "U+21cc=⇌ EQUILIBRIUM
-silent! digr o+ 8853 "U+2295=⊕ CIRCLED PLUS
-silent! digr -^ 8593 "U+2191=↑ UPWARDS ARROW
-
-silent! digr -- 8212 "U+2014=— EM DASH
-
-silent! digr // 9585 "U+2571=╱ BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT
-silent! digr \\ 9586 "U+2572=╲ BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT
-
-silent! digr \|\| 8214 "U+2016=‖ DOUBLE VERTICAL LINE
-
-silent! digr ~~ 8967 "U+2307=⌇ WAVY LINE
+nnoremap <leader>B :call GoodWolfToggleBoldStrings()<cr>
" }}}
" Convenience mappings ---------------------------------------------------- {{{
-" Fuck you, help key.
-noremap <F1> :checktime<cr>
-inoremap <F1> <esc>:checktime<cr>
-
-" Stop it, hash key.
-inoremap # X<BS>#
-
-" Kill window
-nnoremap K :q<cr>
-
-" Save
-nnoremap s :w<cr>
-
-" Man
-nnoremap M K
-
-" Clean up windows
-nnoremap - :wincmd =<cr>
-
-" Toggle line numbers
-nnoremap <leader>n :setlocal number!<cr>
-
-" Sort lines
-nnoremap <leader>s vip:sort<cr>
-vnoremap <leader>s :sort<cr>
-
-" Tabs
-nnoremap <leader>( :tabprev<cr>
-nnoremap <leader>) :tabnext<cr>
-
-" My garbage brain can't ever remember digraph codes
-inoremap <c-k><c-k> <esc>:help digraph-table<cr>
-
-" Wrap
-" mnemonic: less' -S command/option
-nnoremap <leader>S :set wrap!<cr>
-
-" Inserting blank lines
-" I never use the default behavior of <cr> and this saves me a keystroke...
-nnoremap <cr> o<esc>
-
" Copying/pasting text to the system clipboard.
noremap <leader>p "+p
vnoremap <leader>y "+y
@@ -296,21 +79,6 @@
nnoremap <leader>o "zyiW:call Open(@z)<cr>
vnoremap <leader>o "zy:call Open(@z)<cr>
-" Delete to black hole register
-nnoremap dD "_dd
-vnoremap D "_d
-
-" Yank to end of line
-nnoremap Y y$
-
-" Reselect last-pasted text
-nnoremap gv `[v`]
-
-" I constantly hit "u" in visual mode when I mean to "y". Use "gu" for those rare occasions.
-" From https://github.com/henrik/dotfiles/blob/master/vim/config/mappings.vim
-vnoremap u <nop>
-vnoremap gu u
-
" Rebuild Ctags (mnemonic RC -> CR -> <cr>)
nnoremap <leader><cr> :silent !myctags >/dev/null 2>&1 &<cr>:redraw!<cr>
@@ -319,169 +87,15 @@
\ . synIDattr(synID(line("."),col("."),0),"name") . "> lo<"
\ . synIDattr(synIDtrans(synID(line("."),col("."),1)),"name") . ">"<CR>
-" Clean trailing whitespace
-nnoremap <leader>ww mz:%s/\s\+$//<cr>:let @/=''<cr>`z
-
" Send visual selection to paste.stevelosh.com
vnoremap <leader>P :w !pb && open `pbpaste`<cr>
-" Select entire buffer
-nnoremap vaa ggvGg_
-nnoremap Vaa ggVG
-
-" Fix from spellcheck
-" I can never remember if it's zg or z=, and the wrong one adds the word to
-" the DB (lol), so fuck it, just add an easier mapping.
-nnoremap zz z=
-nnoremap z= :echo "use zz you idiot"<cr>
-
-" "Uppercase word" mapping.
-"
-" This mapping allows you to press <c-u> in insert mode to convert the current
-" word to uppercase. It's handy when you're writing names of constants and
-" don't want to use Capslock.
-"
-" To use it you type the name of the constant in lowercase. While your
-" cursor is at the end of the word, press <c-u> to uppercase it, and then
-" continue happily on your way:
-"
-" cursor
-" v
-" max_connections_allowed|
-" <c-u>
-" MAX_CONNECTIONS_ALLOWED|
-" ^
-" cursor
-"
-" It works by exiting out of insert mode, recording the current cursor location
-" in the z mark, using gUiw to uppercase inside the current word, moving back to
-" the z mark, and entering insert mode again.
-"
-" Note that this will overwrite the contents of the z mark. I never use it, but
-" if you do you'll probably want to use another mark.
-inoremap <C-u> <esc>mzgUiw`za
-
-" Panic Button
-nnoremap <f9> mzggg?G`z
-
-" zt is okay for putting something at the top of the screen, but when I'm
-" writing prose I often want to put something at not-quite-the-top of the
-" screen. zh is "zoom to head level"
-nnoremap zh mzzt10<c-u>`z
-
-" Diffoff
-nnoremap <leader>D :diffoff!<cr>
-
-" Formatting, TextMate-style
-nnoremap Q gqip
-vnoremap Q gq
-
-" Reformat line.
-" I never use l as a macro register anyway.
-nnoremap ql gqq
-
-" Indent/dedent/autoindent what you just pasted.
-nnoremap <lt>> V`]<
-nnoremap ><lt> V`]>
-nnoremap =- V`]=
-
-" Keep the cursor in place while joining lines
-nnoremap J mzJ`z
-
-" Join an entire paragraph.
-"
-" Useful for writing GitHub comments in actual Markdown and then translating it
-" to their bastardized version of Markdown.
-nnoremap <leader>j mzvipJ`z
-
-" Split line (sister to [J]oin lines)
-" The normal use of S is covered by cc, so don't worry about shadowing it.
-nnoremap S i<cr><esc>^mwgk:silent! s/\v +$//<cr>:noh<cr>`w
-
-" Substitute
-nnoremap <c-s> :%s/
-vnoremap <c-s> :s/
-
-" Marks and Quotes
-noremap ' `
-noremap æ '
-noremap ` <C-^>
-
-" Select (charwise) the contents of the current line, excluding indentation.
-" Great for pasting Python lines into REPLs.
-nnoremap vv ^vg_
-
-" Typos
-command! -bang E e<bang>
-command! -bang Q q<bang>
-command! -bang W w<bang>
-command! -bang QA qa<bang>
-command! -bang Qa qa<bang>
-command! -bang Wa wa<bang>
-command! -bang WA wa<bang>
-command! -bang Wq wq<bang>
-command! -bang WQ wq<bang>
-command! -bang Wqa wqa<bang>
-
-" Unfuck my screen
-nnoremap U :syntax sync fromstart<cr>:redraw!<cr>
-
-" Zip Right
-"
-" Moves the character under the cursor to the end of the line. Handy when you
-" have something like:
-"
-" foo
-"
-" And you want to wrap it in a method call, so you type:
-"
-" println()foo
-"
-" Once you hit escape your cursor is on the closing paren, so you can 'zip' it
-" over to the right with this mapping.
-"
-" This should preserve your last yank/delete as well.
-nnoremap zl :let @z=@"<cr>x$p:let @"=@z<cr>
-
-" Indent from insert mode
-" has to be imap because we want to be able to use the "go-indent" mapping
-imap <c-l> <c-o>gi
-
-" Diff Navigation
-nnoremap ]d ]c
-nnoremap [d [c
-
-" Typo navigation
-nnoremap ]z ]S
-nnoremap [z [S
-nnoremap ]Z ]Sz=
-nnoremap [Z [Sz=
-
-" Header Lines
-nnoremap <leader>- o<esc>80a-<esc>kJ079lD
-
-" Insert Mode Completion {{{
-
-inoremap <c-f> <c-x><c-f>
-inoremap <c-]> <c-x><c-]>
-inoremap <c-l> <c-x><c-l>
-
-" }}}
-
-" Window Resizing {{{
-" right/up : bigger
-" left/down : smaller
-nnoremap <m-right> :vertical resize +3<cr>
-nnoremap <m-left> :vertical resize -3<cr>
-nnoremap <m-up> :resize +3<cr>
-nnoremap <m-down> :resize -3<cr>
-" }}}
-
" }}}
" Quick editing ----------------------------------------------------------- {{{
nnoremap <leader>eb :vsplit ~/Dropbox/bitly.txt<cr>
-nnoremap <leader>ed :vsplit ~/.vim/custom-dictionary.utf-8.add<cr>
+nnoremap <leader>ez :vsplit ~/.vim/custom-dictionary.utf-8.add<cr>Gmzzt10<c-u>`z
+nnoremap <leader>eZ :vsplit ~/.vim-local-dictionary.utf-8.add<cr>Gmzzt10<c-u>`z
nnoremap <leader>ef :vsplit ~/.config/fish/config.fish<cr>
nnoremap <leader>eg :vsplit ~/.gitconfig<cr>
nnoremap <leader>es :vsplit ~/.stumpwmrc<cr>
@@ -492,7 +106,8 @@
nnoremap <leader>eln :vsplit ~/lab/README.markdown<cr>
nnoremap <leader>eq :vsplit ~/Dropbox/quotes.txt<cr>Gzz
nnoremap <leader>et :vsplit ~/.tmux.conf<cr>
-nnoremap <leader>ev :vsplit ~/.vimrc<cr>
+nnoremap <leader>evf :vsplit ~/.vimrc<cr>
+nnoremap <leader>evm :vsplit ~/.vimrc-minimal<cr>
" }}}
" Status Line ------------------------------------------------------------- {{{
@@ -604,191 +219,76 @@
" set statusline+=%=
" }}}
-" Searching and movement -------------------------------------------------- {{{
-
-" Use sane regexes.
-nnoremap / /\v
-vnoremap / /\v
-
-set ignorecase
-set smartcase
-set incsearch
-set showmatch
-set hlsearch
-set gdefault
-
-set scrolloff=5
-set sidescroll=1
-set sidescrolloff=10
-
-set virtualedit+=block
-
-noremap <silent> <leader><space> :noh<cr>:call clearmatches()<cr>
-
-runtime macros/matchit.vim
-map <tab> %
-silent! unmap [%
-silent! unmap ]%
-
-" Made D behave
-nnoremap D d$
-
-" Don't move on *
-" I'd use a function for this but Vim clobbers the last search when you're in
-" a function so fuck it, practicality beats purity.
-nnoremap <silent> * :let stay_star_view = winsaveview()<cr>*:call winrestview(stay_star_view)<cr>
-
-" Jumping to tags.
-"
-" Basically, <c-]> jumps to tags (like normal) and <c-\> opens the tag in a new
-" split instead.
-"
-" Both of them will align the destination line to the upper middle part of the
-" screen. Both will pulse the cursor line so you can see where the hell you
-" are. <c-\> will also fold everything in the buffer and then unfold just
-" enough for you to see the destination line.
-"
-function! JumpTo(jumpcommand)
- execute a:jumpcommand
- call FocusLine()
- Pulse
-endfunction
-function! JumpToInSplit(jumpcommand)
- execute "normal! \<c-w>v"
- execute a:jumpcommand
- Pulse
-endfunction
-
-function! JumpToTag()
- call JumpTo("normal! \<c-]>")
-endfunction
-function! JumpToTagInSplit()
- call JumpToInSplit("normal \<c-]>")
-endfunction
-
-nnoremap <c-]> :silent! call JumpToTag()<cr>
-nnoremap <c-\> :silent! call JumpToTagInSplit()<cr>
-
-" Keep search matches in the middle of the window.
-nnoremap n nzzzv
-nnoremap N Nzzzv
-
-" Same when jumping around
-nnoremap g; g;zz
-nnoremap g, g,zz
-nnoremap <c-o> <c-o>zz
-
-" Easier to type, and I never use the default behavior.
-noremap H ^
-noremap L $
-vnoremap L g_
-
-" Heresy
-inoremap <c-a> <esc>I
-inoremap <c-e> <esc>A
-cnoremap <c-a> <home>
-cnoremap <c-e> <end>
-
-" go indent
-nnoremap gi mzVap=`z
-nnoremap gI mzgg=G`z
-
-" Fix linewise visual selection of various text objects
-nnoremap VV V
-nnoremap Vit vitVkoj
-nnoremap Vat vatV
-nnoremap Vab vabV
-nnoremap VaB vaBV
-
-" Directional Keys {{{
-
-" It's 2013.
-noremap j gj
-noremap k gk
-noremap gj j
-noremap gk k
-
-" Easy buffer navigation
-noremap <C-h> <C-w>h
-noremap <C-j> <C-w>j
-noremap <C-k> <C-w>k
-noremap <C-l> <C-w>l
-
-noremap <leader>v <C-w>v
+" Filetype-specific ------------------------------------------------------- {{{
+
+" April {{{
+
+augroup ft_commonlisp_april " {{{
+ au!
+
+ au FileType lisp inoremap <buffer> <c-j>; ⍝
+ au FileType lisp inoremap <buffer> <c-j>i ⍳
+ au FileType lisp inoremap <buffer> <c-j>r ⍴
+ au FileType lisp inoremap <buffer> <c-j>e ∊
+ au FileType lisp inoremap <buffer> <c-j>_i ⍸
+ au FileType lisp inoremap <buffer> <c-j>_, ⍪
+
+ au FileType lisp inoremap <buffer> <c-j>1 ¨
+ au FileType lisp inoremap <buffer> <c-j>F ⍨
+ au FileType lisp inoremap <buffer> <c-j>P ⍣
+ au FileType lisp inoremap <buffer> <c-j>R ⍤
+
+ au FileType lisp inoremap <buffer> <c-j>* ×
+ au FileType lisp inoremap <buffer> <c-j>/ ÷
+ au FileType lisp inoremap <buffer> <c-j>0 ∘
+ au FileType lisp inoremap <buffer> <c-j>- ¯
+ au FileType lisp inoremap <buffer> <c-j>l ⍟
+
+ au FileType lisp inoremap <buffer> <c-j>c ⌈
+ au FileType lisp inoremap <buffer> <c-j>f ⌊
+
+ au FileType lisp inoremap <buffer> <c-j><c-j> ←
+ au FileType lisp inoremap <buffer> <c-j>< ←
+ au FileType lisp inoremap <buffer> <c-j>> →
+ au FileType lisp inoremap <buffer> <c-j>^ ↑
+ au FileType lisp inoremap <buffer> <c-j>v ↓
+
+ au FileType lisp inoremap <buffer> <c-j>d ∆
+ au FileType lisp inoremap <buffer> <c-j>D ⍙
+
+ au FileType lisp inoremap <buffer> <c-j>Gk ⍋
+ au FileType lisp inoremap <buffer> <c-j>Gj ⍒
+
+ au FileType lisp inoremap <buffer> <c-j>oo ○
+ au FileType lisp inoremap <buffer> <c-j>o\| ⌽
+ au FileType lisp inoremap <buffer> <c-j>o\ ⍉
+ au FileType lisp inoremap <buffer> <c-j>o- ⊖
+
+ au FileType lisp inoremap <buffer> <c-j>A ∧
+ au FileType lisp inoremap <buffer> <c-j>O ∨
+
+ au FileType lisp inoremap <buffer> <c-j>=< ≤
+ au FileType lisp inoremap <buffer> <c-j>=< ≥
+ au FileType lisp inoremap <buffer> <c-j>=/ ≠
+ au FileType lisp inoremap <buffer> <c-j>=== ≡
+ au FileType lisp inoremap <buffer> <c-j>==/ ≢
+
+ au FileType lisp inoremap <buffer> <c-j>q ⎕
+ au FileType lisp inoremap <buffer> <c-j>Q ⌷
+ au FileType lisp inoremap <buffer> <c-j>t ⊢
+
+ au FileType lisp inoremap <buffer> <c-j>u ∪
+ au FileType lisp inoremap <buffer> <c-j>U ∩
+ au FileType lisp inoremap <buffer> <c-j>+ ⌿
+ au FileType lisp inoremap <buffer> <c-j>[ ⊂
+ au FileType lisp inoremap <buffer> <c-j>] ⊃
+
+ au FileType lisp inoremap <buffer> <c-j>a ⍺
+ au FileType lisp inoremap <buffer> <c-j>w ⍵
+ au FileType lisp inoremap <buffer> <c-j>z ⍬
+augroup END " }}}
" }}}
-" Visual Mode */# from Scrooloose {{{
-
-function! s:VSetSearch()
- let temp = @@
- norm! gvy
- let @/ = '\V' . substitute(escape(@@, '\'), '\n', '\\n', 'g')
- let @@ = temp
-endfunction
-
-vnoremap * :<C-u>call <SID>VSetSearch()<CR>//<CR><c-o>
-vnoremap # :<C-u>call <SID>VSetSearch()<CR>??<CR><c-o>
-
-" }}}
-" List navigation {{{
-
-nnoremap <left> :cprev<cr>zvzz
-nnoremap <right> :cnext<cr>zvzz
-nnoremap <up> :lprev<cr>zvzz
-nnoremap <down> :lnext<cr>zvzz
-
-" }}}
-
-" }}}
-" Folding ----------------------------------------------------------------- {{{
-
-set foldlevelstart=0
-
-" Space to toggle folds.
-nnoremap <Space> za
-vnoremap <Space> za
-
-" Make zO recursively open whatever fold we're in, even if it's partially open.
-nnoremap zO zczO
-
-" "Focus" the current line. Basically:
-"
-" 1. Close all folds.
-" 2. Open just the folds containing the current line.
-" 3. Move the line to a bit (25 lines) down from the top of the screen.
-" 4. Pulse the line.
-"
-" This mapping wipes out the z mark, which I never use.
-"
-" I use :sus for the rare times I want to actually background Vim.
-function! FocusLine()
- let oldscrolloff = &scrolloff
- set scrolloff=0
- execute "keepjumps normal! mzzMzvzt25\<c-y>`z:Pulse\<cr>"
- let &scrolloff = oldscrolloff
-endfunction
-nnoremap <c-z> :call FocusLine()<cr>
-
-function! MyFoldText() " {{{
- let line = getline(v:foldstart)
-
- let nucolwidth = &fdc + &number * &numberwidth
- let windowwidth = winwidth(0) - nucolwidth - 3
- let foldedlinecount = v:foldend - v:foldstart
-
- " expand tabs into spaces
- let onetab = strpart(' ', 0, &tabstop)
- let line = substitute(line, '\t', onetab, 'g')
-
- let line = strpart(line, 0, windowwidth - 2 -len(foldedlinecount))
- let fillcharcount = windowwidth - len(line) - len(foldedlinecount)
- return line . '…' . repeat(" ",fillcharcount) . foldedlinecount . '…' . ' '
-endfunction " }}}
-set foldtext=MyFoldText()
-
-" }}}
-" Filetype-specific ------------------------------------------------------- {{{
-
" Assembly {{{
augroup ft_asm
@@ -1069,6 +569,7 @@
au BufNewFile,BufRead *.paren set filetype=lisp
au BufNewFile,BufRead .abclrc set filetype=lisp
au BufNewFile,BufRead .lisprc set filetype=lisp
+ au BufNewFile,BufRead stumpwmrc set filetype=lisp
au BufNewFile,BufRead .stumpwmrc set filetype=lisp
au BufNewFile,BufRead .stumpwmrc.local set filetype=lisp
@@ -1125,57 +626,6 @@
" Writing
au FileType lisp noremap <buffer> <localleader>= I; => <esc>
-
- " April
- au FileType lisp inoremap <buffer> <c-j>; ⍝
- au FileType lisp inoremap <buffer> <c-j>i ⍳
- au FileType lisp inoremap <buffer> <c-j>r ⍴
- au FileType lisp inoremap <buffer> <c-j>e ∊
- au FileType lisp inoremap <buffer> <c-j>_i ⍸
- au FileType lisp inoremap <buffer> <c-j>_, ⍪
- au FileType lisp inoremap <buffer> <c-j>:~ ⍨
- au FileType lisp inoremap <buffer> <c-j>:^ ¨
-
- au FileType lisp inoremap <buffer> <c-j>* ×
- au FileType lisp inoremap <buffer> <c-j>/ ÷
- au FileType lisp inoremap <buffer> <c-j>0 ∘
- au FileType lisp inoremap <buffer> <c-j>- ¯
-
- au FileType lisp inoremap <buffer> <c-j>c ⌈
- au FileType lisp inoremap <buffer> <c-j>f ⌊
-
- au FileType lisp inoremap <buffer> <c-j>< ←
- au FileType lisp inoremap <buffer> <c-j>> →
- au FileType lisp inoremap <buffer> <c-j>^ ↑
- au FileType lisp inoremap <buffer> <c-j>v ↓
-
- au FileType lisp inoremap <buffer> <c-j>G^ ⍋
- au FileType lisp inoremap <buffer> <c-j>Gv ⍒
-
- au FileType lisp inoremap <buffer> <c-j>o\| ⌽
- au FileType lisp inoremap <buffer> <c-j>o/ ⍉
- au FileType lisp inoremap <buffer> <c-j>o- ⊖
-
- au FileType lisp inoremap <buffer> <c-j>A ∧
- au FileType lisp inoremap <buffer> <c-j>O ∨
-
- au FileType lisp inoremap <buffer> <c-j>=< ≤
- au FileType lisp inoremap <buffer> <c-j>=< ≥
- au FileType lisp inoremap <buffer> <c-j>=/ ≠
- au FileType lisp inoremap <buffer> <c-j>=== ≡
- au FileType lisp inoremap <buffer> <c-j>==/ ≢
-
- au FileType lisp inoremap <buffer> <c-j>q ⎕
- au FileType lisp inoremap <buffer> <c-j>Q ⌷
- au FileType lisp inoremap <buffer> <c-j>t ⊢
-
- au FileType lisp inoremap <buffer> <c-j>u ∪
- au FileType lisp inoremap <buffer> <c-j>U ∩
- au FileType lisp inoremap <buffer> <c-j>+ ⌿
- au FileType lisp inoremap <buffer> <c-j>[ ⊂
- au FileType lisp inoremap <buffer> <c-j>] ⊃
-
- au FileType lisp inoremap <buffer> <c-j>z ⍬
augroup END " }}}
" }}}
@@ -1270,6 +720,9 @@
au BufNewFile,BufRead dashboard.py normal! zR
au BufNewFile,BufRead local_settings.py normal! zR
+ au BufNewFile,BufRead *.htmldjango setlocal filetype=htmldjango
+ au BufNewFile,BufRead *.htmldjango.hx setlocal filetype=htmldjango
+
au BufNewFile,BufRead admin.py setlocal filetype=python.django
au BufNewFile,BufRead urls.py setlocal filetype=python.django
au BufNewFile,BufRead models.py setlocal filetype=python.django
@@ -1480,11 +933,6 @@
au FileType go iabbrev <buffer> enilrs if err != nil {<cr>return "", err<down>
au FileType go iabbrev <buffer> enilrz if err != nil {<cr>return 0, err<down>
- au FileType go iabbrev <buffer> enilrw if err != nil {<cr>return fmt.Errorf(": %w", err<left><left><left><left><left><left><left><left><left><left><C-R>=Eatchar('\s')<cr>
- au FileType go iabbrev <buffer> enilrwn if err != nil {<cr>return nil, fmt.Errorf(": %w", err<left><left><left><left><left><left><left><left><left><left><C-R>=Eatchar('\s')<cr>
- au FileType go iabbrev <buffer> enilrws if err != nil {<cr>return "", fmt.Errorf(": %w", err<left><left><left><left><left><left><left><left><left><left><C-R>=Eatchar('\s')<cr>
- au FileType go iabbrev <buffer> enilrwz if err != nil {<cr>return 0, fmt.Errorf(": %w", err<left><left><left><left><left><left><left><left><left><left><C-R>=Eatchar('\s')<cr>
-
au FileType gohtmltmpl setlocal shiftwidth=4
augroup END
@@ -1501,30 +949,21 @@
" }}}
" HTML, Django, Jinja, Dram, Go, Kill Me {{{
-let g:html_indent_tags = ['p', 'li']
+let g:html_indent_tags = ['p', 'li', 'div']
+
+let g:html_indent_inctags = "html,body,head,tbody,div"
augroup ft_html
au!
- au BufNewFile,BufRead *.html setlocal filetype=gohtmltmpl
+ au BufNewFile,BufRead *.djula setlocal filetype=htmldjango
au BufNewFile,BufRead *.dram setlocal filetype=htmldjango
au FileType html,jinja,htmldjango,gohtmltmpl setlocal foldmethod=manual
-
- " Use <localleader>f to fold the current tag.
- au FileType html,jinja,htmldjango,gohtmltmpl nnoremap <buffer> <localleader>f Vatzf
-
- " Use <localleader>t to fold the current templatetag.
- au FileType html,jinja,htmldjango nmap <buffer> <localleader>t viikojozf
-
- " Indent tag
- au FileType html,jinja,htmldjango,gohtmltmpl nnoremap <buffer> <localleader>= Vat=
-
- " Django tags
- au FileType jinja,htmldjango inoremap <buffer> <c-t> {%<space><space>%}<left><left><left>
-
- " Django variables
- au FileType jinja,htmldjango inoremap <buffer> <c-b> {{<space><space>}}<left><left><left>
+ au FileType html,jinja,htmldjango,gohtmltmpl setlocal foldmethod=manual
+
+ au FileType html,jinja,htmldjango,gohtmltmp inoremap <silent> <C-c> </<C-X><C-O><C-X><esc>a
+ au FileType html,jinja,htmldjango,gohtmltmp inoremap <c-cr> <cr><esc>O
augroup END
" }}}
@@ -1558,12 +997,24 @@
augroup END
" }}}
+" JSON {{{
+
+augroup ft_json
+ au!
+
+ au FileType json setlocal sw=4
+augroup END
+
+" }}}
" Latex {{{
let g:tex_flavor = 'latex'
augroup ft_latex
au!
+
+ au Filetype tex inoremap <buffer> <c-b> \begin{}<left>
+ au Filetype tex nnoremap <buffer> <localleader>q :%s/[‘’]/'/<cr>
augroup END
" }}}
@@ -1657,8 +1108,6 @@
au Filetype markdown nnoremap <buffer> <localleader>3 mzI###<space><esc>`zllll
au Filetype markdown nnoremap <buffer> <localleader>4 mzI####<space><esc>`zlllll
- au Filetype markdown inoremap <buffer> <c-cr> <cr><esc>mz?^ *\*?e<cr>"zy0:noh<cr>`z"zpA* <esc>a
- au Filetype markdown inoremap <buffer> <s-c-cr> <cr><esc>mz?^ *\*?e<cr>"zy0:noh<cr>`z"zpA * <esc>a
au Filetype markdown inoremap <buffer> <s-tab> <esc>mz0xx`za
au Filetype markdown inoremap <buffer> <c-tab> <esc>mzI <esc>`zlla
augroup END
@@ -1693,6 +1142,15 @@
augroup END
" }}}
+" NextFlow {{{
+
+augroup ft_nextflow
+ au!
+
+ " au BufRead,BufNewFile *.nf set ft=groovy
+augroup END
+
+" }}}
" Nginx {{{
augroup ft_nginx
@@ -1761,17 +1219,51 @@
" }}}
" Python {{{
+" Helper Functions {{{
+let g:current_python_lsp_client = 0
+
+function! PythonLSPConnect() "{{{
+ if g:current_python_lsp_client == 0
+ lua vim.lsp.start_client({cmd={"nc", "127.0.0.1", "9898"}})
+ " TODO lol
+ let g:current_python_lsp_client = 1
+ endif
+endfunction "}}}
+
+function! PythonLSPAttach() "{{{
+ call PythonLSPConnect()
+ lua vim.lsp.buf_attach_client(0, 1)
+ call PythonLSPMappings()
+endfunction "}}}
+
+function! PythonLSPSig() "{{{
+ lua vim.lsp.buf.signature_help()
+ return ""
+endfunction "}}}
+
+function! PythonLSPMappings() "{{{
+ setlocal omnifunc=v:lua.vim.lsp.omnifunc
+ inoremap <buffer> <c-n> <c-x><c-o>
+ nnoremap <buffer> <c-]> :lua vim.lsp.buf.definition()<cr>
+ nnoremap <buffer> M :lua vim.lsp.buf.hover()<cr>
+ inoremap <buffer> <c-m> <c-r>=PythonLSPSig()<cr>
+endfunction "}}}
+
+" }}}
+
augroup ft_python
au!
au FileType python setlocal define=^\s*\\(def\\\\|class\\)
- au FileType python setlocal textwidth=100
+ au FileType python setlocal textwidth=88
" Jesus tapdancing Christ, built-in Python syntax, you couldn't let me
" override this in a normal way, could you?
au FileType python if exists("python_space_error_highlight") | unlet python_space_error_highlight | endif
au FileType python nnoremap <buffer> gi :Neoformat black<cr>zx
+
+ au FileType python nnoremap <buffer> <localleader>cc :call PythonLSPAttach()<cr>
augroup END
" }}}
@@ -1786,6 +1278,59 @@
augroup END
" }}}
+" R {{{
+
+let R_external_term = 'st -t "R REPL" --'
+let R_args = ['--no-save', '--quiet']
+let R_save_win_pos = 0
+let R_arrange_windows = 0
+let R_assign = 0
+let R_clear_line = 1
+let R_nvim_wd = 1
+let R_user_maps_only = 1
+let R_nvimpager = 'vertical'
+let R_help_w = 81
+let r_indent_ess_comments = 0
+let r_indent_ess_compatible = 0
+let r_indent_align_args = 0
+
+function! s:customNvimRMappings()
+ " Normal Mode
+ nmap <buffer> <localleader>Or <Plug>RStart
+ nmap <buffer> <localleader>Cr <Plug>RClose
+ nmap <buffer> <localleader>c <Plug>RClearConsole
+ nmap <buffer> <localleader>h <Plug>RHelp
+ nmap <buffer> M <Plug>RShowArgs
+ nmap <buffer> <localleader>e <Plug>RSendParagraph
+ nmap <buffer> <localleader>w mzviw<Plug>RSendSelection<esc>'z
+ nmap <buffer> <localleader>W mzviW<Plug>RSendSelection<esc>'z
+ nmap <buffer> <localleader>S <Plug>RSendLine
+ nmap <buffer> <localleader>E <Plug>RSendLine
+ nmap <buffer> <localleader>f <Plug>RSendFile
+ nmap <buffer> <localleader>i <Plug>RViewDFv
+ nmap <buffer> <localleader>I <Plug>RViewDFs
+ nmap <buffer> gi mzvip:Rformat<cr>'z
+
+ inoremap <buffer> <c-n> <c-x><c-o>
+
+ " Insert Mode
+ inoremap <buffer> <c-.> <esc>A \|><cr>
+ inoremap <buffer> <c-,> <-<space>
+ inoremap <buffer> <c-'> <esc>A +<cr>
+
+ " Visual Mode
+ vmap <buffer> <localleader>e <Plug>RSendSelection
+ vmap <buffer> gi :Rformat<cr>
+endfunction
+
+augroup ft_r
+ au!
+
+ autocmd filetype r setlocal shiftwidth=2
+ autocmd filetype r call s:customNvimRMappings()
+augroup END
+
+" }}}
" Sh {{{
function! SendShellParagraph() "{{{
@@ -1795,6 +1340,8 @@
augroup ft_sh
au!
+ au BufRead,BufNewFile *.sbat set ft=sh
+
au FileType sh nnoremap <buffer> <localleader>e :call NeoReplSendCurrentLine()<cr>
au FileType sh nnoremap <buffer> <localleader>E :call SendShellParagraph()<cr>
au FileType sh nnoremap <buffer> <localleader>F :call NeoReplSendEntireFile(1)<cr>
@@ -1830,6 +1377,39 @@
augroup END
" }}}
+" TEN templates {{{
+
+function! RecompileTENTemplates() "{{{
+ let systems = split(system('ls -1 *.asd | grep -v test | cut -d. -f1 | uniq')) " its fine
+ if len(systems) == 0
+ echom "Could not find any .asd files..."
+ return
+ elseif len(systems) > 1
+ echom "Found too many any .asd files..."
+ return
+ endif
+
+ call vlime#plugin#SendToREPL("(ql:quickload :" . systems[0] . ")")
+endfunction "}}}
+
+augroup ft_ten
+ au!
+
+ au BufNewFile,BufRead *.ten setlocal filetype=htmlten
+ au BufNewFile,BufRead *.ten nnoremap <buffer> <localleader>q :call RecompileTENTemplates()<cr>
+augroup END
+
+" }}}
+" TODOs {{{
+
+augroup ft_todos
+ au!
+
+ au BufWritePost /home/sjl/Sync/school/todo/TODO :silent !hg -R /home/sjl/Sync/school/todo cmore
+ au BufWritePost /home/sjl/Sync/school/todo/irons.markdown :silent !hg -R /home/sjl/Sync/school/todo cmore
+augroup END
+
+" }}}
" Vagrant {{{
augroup ft_vagrant
@@ -1860,6 +1440,7 @@
au!
au FileType yaml set shiftwidth=2
+ au FileType yaml set foldmethod=marker foldmarker={{{,}}}
augroup END
" }}}
@@ -1899,6 +1480,7 @@
vmap -< ++<
vmap -^ ++^
vmap -V ++v
+vmap -- +-
" }}}
" Clam {{{
@@ -1923,12 +1505,17 @@
au FileType lisp setlocal commentstring=;;\ %s
au FileType makerlisp setlocal commentstring=;;\ %s
au FileType puppet setlocal commentstring=#\ %s
+ au FileType snakemake setlocal commentstring=#\ %s
au FileType nginx setlocal commentstring=#\ %s
au FileType fish setlocal commentstring=#\ %s
au FileType gnuplot setlocal commentstring=#\ %s
+ au FileType singularity setlocal commentstring=#\ %s
au FileType cs setlocal commentstring=//\ %s
+ au FileType c setlocal commentstring=//\ %s
+ au FileType cpp setlocal commentstring=//\ %s
au FileType arduino setlocal commentstring=//\ %s
au FileType pandabt setlocal commentstring=//\ %s
+ au FileType nextflow setlocal commentstring=//\ %s
augroup END
" }}}
@@ -2021,6 +1608,7 @@
let g:gundo_preview_bottom = 1
let g:gundo_tree_statusline = "Gundo"
let g:gundo_preview_statusline = "Gundo Preview"
+let g:gundo_prefer_python3 = 1
" }}}
" HTML5 {{{
@@ -2043,6 +1631,16 @@
nnoremap <f6> :Neoformat<cr>
+let g:neoformat_snakemake_snakefmt = {
+ \ 'exe': '/home/sjl/bin/venvs/tools/bin/snakefmt',
+ \ 'args': [],
+ \ 'replace': 1,
+ \ 'stdin': 0,
+ \ 'valid_exit_codes': [0]
+ \ }
+
+let g:neoformat_enabled_snakemake = ['snakefmt']
+
" }}}
" NeoRepl {{{
@@ -2118,7 +1716,7 @@
let g:paredit_smartjump = 1
let g:paredit_shortmaps = 0
-let g:paredit_electric_return = 0
+let g:paredit_electric_return = 1
let g:paredit_matchlines = 200
let g:paredit_disable_lisp = 1
@@ -2407,6 +2005,15 @@
let g:targets_pairs = '()b {}B []r <>'
" }}}
+" Ultisnips {{{
+
+let g:UltiSnipsExpandTrigger = '<Tab>'
+let g:UltiSnipsJumpForwardTrigger = '<Tab>'
+let g:UltiSnipsJumpBackwardTrigger = '<S-Tab>'
+
+let g:UltiSnipsSnippetDirectories=[$HOME.'/.vim/ultisnips-bullshit']
+
+" }}}
" Vlime {{{
@@ -2513,7 +2120,7 @@
au FileType lisp,vlime_repl,vlime_inspector,vlime_sldb,vlime_notes,vlime_xref,vlime_preview call MapVlimeKeys()
" Fix <cr>
- au FileType lisp inoremap <buffer> <cr> <cr><c-r>=vlime#plugin#VlimeKey("cr")<cr>
+ au FileType lisp inoremap <buffer> <cr> <c-r>=vlime#plugin#VlimeKey("cr")<cr>
au FileType vlime_xref nnoremap <buffer> <cr> :call vlime#ui#xref#OpenCurXref()<cr>
au FileType vlime_notes nnoremap <buffer> <cr> :call vlime#ui#compiler_notes#OpenCurNote()<cr>
au FileType vlime_sldb nnoremap <buffer> <cr> :call vlime#ui#sldb#ChooseCurRestart()<cr>
@@ -2533,69 +2140,7 @@
" Windowswap {{{
let g:windowswap_map_keys = 0 "prevent default bindings
-nnoremap <silent> <leader>ws :call WindowSwap#EasyWindowSwap()<CR>
-
-" }}}
-
-" }}}
-" Text objects ------------------------------------------------------------ {{{
-
-" Folds {{{
-
-onoremap if :<c-u>normal! [zv]z<cr>
-onoremap af :<c-u>normal! [zV]z<cr>
-vnoremap if :<c-u>normal! ]zv[z<cr>
-vnoremap af :<c-u>normal! ]zV[z<cr>
-
-" }}}
-" Shortcut for [] {{{
-
-onoremap ir i[
-onoremap ar a[
-vnoremap ir i[
-vnoremap ar a[
-
-" }}}
-" Numbers {{{
-
-" Motion for numbers. Great for CSS. Lets you do things like this:
-"
-" margin-top: 200px; -> daN -> margin-top: px;
-" ^ ^
-" TODO: Handle floats.
-
-onoremap N :<c-u>call <SID>NumberTextObject(0)<cr>
-xnoremap N :<c-u>call <SID>NumberTextObject(0)<cr>
-onoremap aN :<c-u>call <SID>NumberTextObject(1)<cr>
-xnoremap aN :<c-u>call <SID>NumberTextObject(1)<cr>
-onoremap iN :<c-u>call <SID>NumberTextObject(1)<cr>
-xnoremap iN :<c-u>call <SID>NumberTextObject(1)<cr>
-
-function! s:NumberTextObject(whole)
- let num = '\v[0-9]'
-
- " If the current char isn't a number, walk forward.
- while getline('.')[col('.') - 1] !~# num
- normal! l
- endwhile
-
- " Now that we're on a number, start selecting it.
- normal! v
-
- " If the char after the cursor is a number, select it.
- while getline('.')[col('.')] =~# num
- normal! l
- endwhile
-
- " If we want an entire word, flip the select point and walk.
- if a:whole
- normal! o
-
- while col('.') > 1 && getline('.')[col('.') - 2] =~# num
- normal! h
- endwhile
- endif
-endfunction
+nnoremap <silent> <c-w><c-w> :call WindowSwap#EasyWindowSwap()<CR>
" }}}
@@ -2902,6 +2447,7 @@
nnoremap <silent> <leader>4 :call HiInterestingWord(4)<cr>
nnoremap <silent> <leader>5 :call HiInterestingWord(5)<cr>
nnoremap <silent> <leader>6 :call HiInterestingWord(6)<cr>
+nnoremap <silent> <leader>7 :call HiInterestingWord(7)<cr>
" }}}
" Default Highlights {{{
@@ -2912,6 +2458,7 @@
hi def InterestingWord4 guifg=#000000 ctermfg=16 guibg=#b88853 ctermbg=137
hi def InterestingWord5 guifg=#000000 ctermfg=16 guibg=#ff9eb8 ctermbg=211
hi def InterestingWord6 guifg=#000000 ctermfg=16 guibg=#ff2c4b ctermbg=195
+hi def InterestingWord7 guifg=#000000 ctermfg=16 guibg=#af5fff ctermbg=135
" }}}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/vimrc-minimal Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,672 @@
+" .vimrc-minimal
+" Author: Steve Losh <steve@stevelosh.com>
+" Source: https://hg.stevelosh.com/dotfiles/file/tip/vim/vimrc
+
+" A minimal subset of my vimrc settings, suitable for syncing on its own to
+" a server so I have some basic settings, but don't have to install the whole
+" hog.
+
+" Preamble ---------------------------------------------------------------- {{{
+
+set shell=/bin/bash\ --login
+
+" }}}
+" Basic options ----------------------------------------------------------- {{{
+
+set modelines=0
+set autoindent
+set showmode
+set showcmd
+set hidden
+set visualbell
+set ttyfast
+set ruler
+set backspace=indent,eol,start
+set nonumber
+set norelativenumber
+set laststatus=2
+set history=1000
+set undofile
+set undoreload=10000
+set list
+set listchars=tab:▸\ ,eol:¬,extends:❯,precedes:❮
+set lazyredraw
+set matchtime=3
+set showbreak=↪
+set splitbelow
+set splitright
+set autowrite
+set autoread
+set shiftround
+set title
+set linebreak
+set colorcolumn=+1
+set diffopt+=vertical
+
+" Don't try to highlight lines longer than 500 characters.
+set synmaxcol=500
+
+" Time out on key codes but not mappings.
+" Basically this makes terminal Vim work sanely.
+set notimeout
+set ttimeout
+set ttimeoutlen=10
+
+" Make Vim able to edit crontab files again.
+set backupskip=/tmp/*,/private/tmp/*"
+
+" Better Completion
+set complete=.,w,b,u,t
+set completeopt=longest,menuone
+inoremap <c-o> <c-x><c-o>
+
+" Save when losing focus
+au FocusLost * :silent! wall
+
+" Leader
+let mapleader = ","
+let maplocalleader = "\\"
+
+" Cursorline {{{
+" Only show cursorline in the current window and in normal mode.
+
+augroup cline
+ au!
+ au WinLeave,InsertEnter * set nocursorline
+ au WinEnter,InsertLeave * set cursorline
+augroup END
+
+" }}}
+" cpoptions+=J, dammit {{{
+
+" Something occasionally removes this. If I manage to find it I'm going to
+" comment out the line and replace all its characters with 'FUCK'.
+augroup twospace
+ au!
+ au BufRead * :set cpoptions+=J
+augroup END
+
+" }}}
+" Trailing whitespace {{{
+" Only shown when not in insert mode so I don't go insane.
+
+augroup trailing
+ au!
+ au InsertEnter * :set listchars-=trail:⌴
+ au InsertLeave * :set listchars+=trail:⌴
+augroup END
+
+" }}}
+" Wildmenu completion {{{
+
+set wildmenu
+set wildmode=list:longest
+
+set wildignore+=.hg,.git,.svn " Version control
+set wildignore+=*.aux,*.out,*.toc " LaTeX intermediate files
+set wildignore+=*.jpg,*.bmp,*.gif,*.png,*.jpeg " binary images
+set wildignore+=*.o,*.obj,*.exe,*.dll,*.manifest " compiled object files
+set wildignore+=*.spl " compiled spelling word lists
+set wildignore+=*.sw? " Vim swap files
+set wildignore+=*.DS_Store " OSX bullshit
+
+set wildignore+=*.luac " Lua byte code
+
+set wildignore+=migrations " Django migrations
+set wildignore+=*.pyc " Python byte code
+
+set wildignore+=*.orig " Merge resolution files
+
+set wildignore+=*.fasl " Lisp FASLs
+set wildignore+=*.dx64fsl " CCL
+set wildignore+=*.lx64fsl " CCL
+
+" }}}
+" Line Return {{{
+
+" Make sure Vim returns to the same line when you reopen a file.
+" Thanks, Amit
+augroup line_return
+ au!
+ au BufReadPost *
+ \ if line("'\"") > 0 && line("'\"") <= line("$") |
+ \ execute 'normal! g`"zvzz' |
+ \ endif
+augroup END
+
+" }}}
+" Tabs, spaces, wrapping {{{
+
+set tabstop=8
+set shiftwidth=4
+set softtabstop=4
+set expandtab
+set wrap
+set textwidth=80
+set formatoptions=qrn1j
+set colorcolumn=+1
+
+" }}}
+" Backups {{{
+
+set backup " enable backups
+set noswapfile " it's 2013, Vim.
+
+set undodir=~/.vim/tmp/undo// " undo files
+set backupdir=~/.vim/tmp/backup// " backups
+set directory=~/.vim/tmp/swap// " swap files
+
+" Make those folders automatically if they don't already exist.
+if !isdirectory(expand(&undodir))
+ call mkdir(expand(&undodir), "p")
+endif
+if !isdirectory(expand(&backupdir))
+ call mkdir(expand(&backupdir), "p")
+endif
+if !isdirectory(expand(&directory))
+ call mkdir(expand(&directory), "p")
+endif
+
+" }}}
+" Color scheme {{{
+
+syntax on
+set background=dark
+
+" }}}
+
+" }}}
+" Abbreviations & Digraphs ------------------------------------------------ {{{
+
+iabbrev todo TODO
+
+silent! digr -. 8230 "U+2026=… HORIZONTAL ELLIPSIS
+silent! digr !, 8816 "U+2270=≰ NEITHER LESS-THAN NOR EQUAL TO
+silent! digr !. 8817 "U+2271=≱ NEITHER GREATER-THAN NOR EQUAL TO
+silent! digr es 8337 "U+2091=ₑ SUBSCRIPT E
+silent! digr xs 8339 "U+2093=ₓ SUBSCRIPT X
+silent! digr ls 8343 "U+2097=ₗ SUBSCRIPT L
+silent! digr ms 8344 "U+2098=ₗ SUBSCRIPT M
+silent! digr ns 8345 "U+2099=ₙ SUBSCRIPT N
+silent! digr ps 8346 "U+209A=ₚ SUBSCRIPT P
+silent! digr ss 8347 "U+209B=ₛ SUBSCRIPT S
+silent! digr ts 8348 "U+209C=ₜ SUBSCRIPT T
+silent! digr >< 8652 "U+21cc=⇌ EQUILIBRIUM
+silent! digr o+ 8853 "U+2295=⊕ CIRCLED PLUS
+silent! digr -^ 8593 "U+2191=↑ UPWARDS ARROW
+
+silent! digr -- 8212 "U+2014=— EM DASH
+
+silent! digr // 9585 "U+2571=╱ BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT
+silent! digr \\ 9586 "U+2572=╲ BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT
+
+silent! digr \|\| 8214 "U+2016=‖ DOUBLE VERTICAL LINE
+
+silent! digr ~~ 8967 "U+2307=⌇ WAVY LINE
+
+" }}}
+" Convenience mappings ---------------------------------------------------- {{{
+
+" Fuck you, help key.
+noremap <F1> :checktime<cr>
+inoremap <F1> <esc>:checktime<cr>
+
+" Stop it, hash key.
+inoremap # X<BS>#
+
+" Kill window
+nnoremap K :q<cr>
+
+" Save
+nnoremap s :w<cr>
+
+" Man
+nnoremap M K
+
+" Clean up windows
+nnoremap - :wincmd =<cr>
+
+" Toggle line numbers
+nnoremap <leader>n :setlocal number!<cr>
+
+" Sort lines
+nnoremap <leader>s vip:sort<cr>
+vnoremap <leader>s :sort<cr>
+
+" Tabs
+nnoremap <leader>( :tabprev<cr>
+nnoremap <leader>) :tabnext<cr>
+
+" My garbage brain can't ever remember digraph codes
+inoremap <c-k><c-k> <esc>:help digraph-table<cr>
+
+" Wrap
+" mnemonic: less' -S command/option
+nnoremap <leader>S :set wrap!<cr>
+
+" Inserting blank lines
+" I never use the default behavior of <cr> and this saves me a keystroke...
+nnoremap <cr> o<esc>
+
+" Delete to black hole register
+nnoremap dD "_dd
+vnoremap D "_d
+
+" Yank to end of line
+nnoremap Y y$
+
+" Reselect last-pasted text
+nnoremap gv `[v`]
+
+" I constantly hit "u" in visual mode when I mean to "y". Use "gu" for those rare occasions.
+" From https://github.com/henrik/dotfiles/blob/master/vim/config/mappings.vim
+vnoremap u <nop>
+vnoremap gu u
+
+" Clean trailing whitespace
+nnoremap <leader>ww mz:%s/\s\+$//<cr>:let @/=''<cr>`z
+
+" Select entire buffer
+nnoremap vaa ggvGg_
+nnoremap Vaa ggVG
+
+" Fix from spellcheck
+" I can never remember if it's zg or z=, and the wrong one adds the word to
+" the DB (lol), so fuck it, just add an easier mapping.
+nnoremap zz z=
+nnoremap z= :echo "use zz you idiot"<cr>
+
+" "Uppercase word" mapping.
+"
+" This mapping allows you to press <c-u> in insert mode to convert the current
+" word to uppercase. It's handy when you're writing names of constants and
+" don't want to use Capslock.
+"
+" To use it you type the name of the constant in lowercase. While your
+" cursor is at the end of the word, press <c-u> to uppercase it, and then
+" continue happily on your way:
+"
+" cursor
+" v
+" max_connections_allowed|
+" <c-u>
+" MAX_CONNECTIONS_ALLOWED|
+" ^
+" cursor
+"
+" It works by exiting out of insert mode, recording the current cursor location
+" in the z mark, using gUiw to uppercase inside the current word, moving back to
+" the z mark, and entering insert mode again.
+"
+" Note that this will overwrite the contents of the z mark. I never use it, but
+" if you do you'll probably want to use another mark.
+inoremap <C-u> <esc>mzgUiw`za
+
+" Wrap Toggle
+nnoremap <f9> :set nowrap!<cr>
+
+" zt is okay for putting something at the top of the screen, but when I'm
+" writing prose I often want to put something at not-quite-the-top of the
+" screen. zh is "zoom to head level"
+nnoremap zh mzzt10<c-u>`z
+
+" Diffoff
+nnoremap <leader>D :diffoff!<cr>
+
+" Formatting, TextMate-style
+nnoremap Q gqip
+vnoremap Q gq
+
+" Reformat line.
+" I never use l as a macro register anyway.
+nnoremap ql gqq
+
+" Indent/dedent/autoindent what you just pasted.
+nnoremap <lt>> V`]<
+nnoremap ><lt> V`]>
+nnoremap =- V`]=
+
+" Keep the cursor in place while joining lines
+nnoremap J mzJ`z
+
+" Join an entire paragraph.
+"
+" Useful for writing GitHub comments in actual Markdown and then translating it
+" to their bastardized version of Markdown.
+nnoremap <leader>j mzvipJ`z
+
+" Split line (sister to [J]oin lines)
+" The normal use of S is covered by cc, so don't worry about shadowing it.
+nnoremap S i<cr><esc>^mwgk:silent! s/\v +$//<cr>:noh<cr>`w
+
+" Substitute
+nnoremap <c-s> :%s/
+vnoremap <c-s> :s/
+
+" Marks and Quotes
+noremap ' `
+noremap æ '
+noremap ` <C-^>
+
+" Select (charwise) the contents of the current line, excluding indentation.
+" Great for pasting Python lines into REPLs.
+nnoremap vv ^vg_
+
+" Typos
+command! -bang E e<bang>
+command! -bang Q q<bang>
+command! -bang W w<bang>
+command! -bang QA qa<bang>
+command! -bang Qa qa<bang>
+command! -bang Wa wa<bang>
+command! -bang WA wa<bang>
+command! -bang Wq wq<bang>
+command! -bang WQ wq<bang>
+command! -bang Wqa wqa<bang>
+
+" Unfuck my screen
+nnoremap U :syntax sync fromstart<cr>:redraw!<cr>
+
+" Zip Right
+"
+" Moves the character under the cursor to the end of the line. Handy when you
+" have something like:
+"
+" foo
+"
+" And you want to wrap it in a method call, so you type:
+"
+" println()foo
+"
+" Once you hit escape your cursor is on the closing paren, so you can 'zip' it
+" over to the right with this mapping.
+"
+" This should preserve your last yank/delete as well.
+nnoremap zl :let @z=@"<cr>x$p:let @"=@z<cr>
+
+" Diff Navigation
+nnoremap ]d ]c
+nnoremap [d [c
+
+" Typo navigation
+nnoremap ]z ]S
+nnoremap [z [S
+nnoremap ]Z ]Sz=
+nnoremap [Z [Sz=
+
+" Header Lines
+nnoremap <leader>- o<esc>80a-<esc>kJ079lD
+
+" Insert Mode Completion
+inoremap <c-f> <c-x><c-f>
+inoremap <c-]> <c-x><c-]>
+inoremap <c-l> <c-x><c-l>
+
+" Window Resizing
+" right/up : bigger
+" left/down : smaller
+nnoremap <m-right> :vertical resize +3<cr>
+nnoremap <m-left> :vertical resize -3<cr>
+nnoremap <m-up> :resize +3<cr>
+nnoremap <m-down> :resize -3<cr>
+
+" Newline without having to go to the end of the line
+inoremap <c-cr> <esc>o
+
+" Spelling correct without having to exit insert mode
+inoremap <c-z> <esc>z=
+
+" }}}
+" Searching and movement -------------------------------------------------- {{{
+
+" Use sane regexes.
+nnoremap / /\v
+vnoremap / /\v
+
+set ignorecase
+set smartcase
+set incsearch
+set showmatch
+set hlsearch
+set gdefault
+
+set scrolloff=5
+set sidescroll=1
+set sidescrolloff=10
+
+set virtualedit+=block
+
+noremap <silent> <leader><space> :noh<cr>:call clearmatches()<cr>
+
+runtime macros/matchit.vim
+map <tab> %
+silent! unmap [%
+silent! unmap ]%
+
+" Made D behave
+nnoremap D d$
+
+" Don't move on *
+" I'd use a function for this but Vim clobbers the last search when you're in
+" a function so fuck it, practicality beats purity.
+nnoremap <silent> * :let stay_star_view = winsaveview()<cr>*:call winrestview(stay_star_view)<cr>
+
+" Jumping to tags.
+"
+" Basically, <c-]> jumps to tags (like normal) and <c-\> opens the tag in a new
+" split instead.
+"
+" Both of them will align the destination line to the upper middle part of the
+" screen. Both will pulse the cursor line so you can see where the hell you
+" are. <c-\> will also fold everything in the buffer and then unfold just
+" enough for you to see the destination line.
+"
+function! JumpTo(jumpcommand)
+ execute a:jumpcommand
+ call FocusLine()
+ Pulse
+endfunction
+function! JumpToInSplit(jumpcommand)
+ execute "normal! \<c-w>v"
+ execute a:jumpcommand
+ Pulse
+endfunction
+
+function! JumpToTag()
+ call JumpTo("normal! \<c-]>")
+endfunction
+function! JumpToTagInSplit()
+ call JumpToInSplit("normal \<c-]>")
+endfunction
+
+nnoremap <c-]> :silent! call JumpToTag()<cr>
+nnoremap <c-\> :silent! call JumpToTagInSplit()<cr>
+
+" Keep search matches in the middle of the window.
+nnoremap n nzzzv
+nnoremap N Nzzzv
+
+" Same when jumping around
+nnoremap g; g;zz
+nnoremap g, g,zz
+nnoremap <c-o> <c-o>zz
+
+" Easier to type, and I never use the default behavior.
+noremap H ^
+noremap L $
+vnoremap L g_
+
+" Heresy
+inoremap <c-a> <esc>I
+inoremap <c-e> <esc>A
+cnoremap <c-a> <home>
+cnoremap <c-e> <end>
+
+" go indent
+nnoremap gi mzVap=`z
+nnoremap gI mzgg=G`z
+
+" Fix linewise visual selection of various text objects
+nnoremap VV V
+nnoremap Vit vitVkoj
+nnoremap Vat vatV
+nnoremap Vab vabV
+nnoremap VaB vaBV
+
+
+" Directional Keys {{{
+
+" It's 2013.
+noremap j gj
+noremap k gk
+noremap gj j
+noremap gk k
+
+" Easy buffer navigation
+noremap <C-h> <C-w>h
+noremap <C-j> <C-w>j
+noremap <C-k> <C-w>k
+noremap <C-l> <C-w>l
+
+noremap <leader>v <C-w>v
+
+" }}}
+" Visual Mode */# from Scrooloose {{{
+
+function! s:VSetSearch()
+ let temp = @@
+ norm! gvy
+ let @/ = '\V' . substitute(escape(@@, '\'), '\n', '\\n', 'g')
+ let @@ = temp
+endfunction
+
+vnoremap * :<C-u>call <SID>VSetSearch()<CR>//<CR><c-o>
+vnoremap # :<C-u>call <SID>VSetSearch()<CR>??<CR><c-o>
+
+" }}}
+" List navigation {{{
+
+nnoremap <left> :cprev<cr>zvzz
+nnoremap <right> :cnext<cr>zvzz
+nnoremap <up> :lprev<cr>zvzz
+nnoremap <down> :lnext<cr>zvzz
+
+" }}}
+
+" }}}
+" Folding ----------------------------------------------------------------- {{{
+
+set foldlevelstart=0
+
+" Space to toggle folds.
+nnoremap <Space> za
+vnoremap <Space> za
+
+" Make zO recursively open whatever fold we're in, even if it's partially open.
+nnoremap zO zczO
+
+" "Focus" the current line. Basically:
+"
+" 1. Close all folds.
+" 2. Open just the folds containing the current line.
+" 3. Move the line to a bit (25 lines) down from the top of the screen.
+" 4. Pulse the line.
+"
+" This mapping wipes out the z mark, which I never use.
+"
+" I use :sus for the rare times I want to actually background Vim.
+function! FocusLine()
+ let oldscrolloff = &scrolloff
+ set scrolloff=0
+ execute "keepjumps normal! mzzMzvzt25\<c-y>`z:Pulse\<cr>"
+ let &scrolloff = oldscrolloff
+endfunction
+nnoremap <c-z> :call FocusLine()<cr>
+
+function! MyFoldText() " {{{
+ let line = getline(v:foldstart)
+
+ let nucolwidth = &fdc + &number * &numberwidth
+ let windowwidth = winwidth(0) - nucolwidth - 3
+ let foldedlinecount = v:foldend - v:foldstart
+
+ " expand tabs into spaces
+ let onetab = strpart(' ', 0, &tabstop)
+ let line = substitute(line, '\t', onetab, 'g')
+
+ let line = strpart(line, 0, windowwidth - 2 -len(foldedlinecount))
+ let fillcharcount = windowwidth - len(line) - len(foldedlinecount)
+ return line . '…' . repeat(" ",fillcharcount) . foldedlinecount . '…' . ' '
+endfunction " }}}
+set foldtext=MyFoldText()
+
+" }}}
+" Text objects ------------------------------------------------------------ {{{
+
+" Folds {{{
+
+onoremap if :<c-u>normal! [zv]z<cr>
+onoremap af :<c-u>normal! [zV]z<cr>
+vnoremap if :<c-u>normal! ]zv[z<cr>
+vnoremap af :<c-u>normal! ]zV[z<cr>
+
+" }}}
+" Shortcut for [] {{{
+
+onoremap ir i[
+onoremap ar a[
+vnoremap ir i[
+vnoremap ar a[
+
+" }}}
+" Numbers {{{
+
+" Motion for numbers. Great for CSS. Lets you do things like this:
+"
+" margin-top: 200px; -> daN -> margin-top: px;
+" ^ ^
+" TODO: Handle floats.
+
+onoremap N :<c-u>call <SID>NumberTextObject(0)<cr>
+xnoremap N :<c-u>call <SID>NumberTextObject(0)<cr>
+onoremap aN :<c-u>call <SID>NumberTextObject(1)<cr>
+xnoremap aN :<c-u>call <SID>NumberTextObject(1)<cr>
+onoremap iN :<c-u>call <SID>NumberTextObject(1)<cr>
+xnoremap iN :<c-u>call <SID>NumberTextObject(1)<cr>
+
+function! s:NumberTextObject(whole)
+ let num = '\v[0-9]'
+
+ " If the current char isn't a number, walk forward.
+ while getline('.')[col('.') - 1] !~# num
+ normal! l
+ endwhile
+
+ " Now that we're on a number, start selecting it.
+ normal! v
+
+ " If the char after the cursor is a number, select it.
+ while getline('.')[col('.')] =~# num
+ normal! l
+ endwhile
+
+ " If we want an entire word, flip the select point and walk.
+ if a:whole
+ normal! o
+
+ while col('.') > 1 && getline('.')[col('.') - 2] =~# num
+ normal! h
+ endwhile
+ endif
+endfunction
+
+" }}}
+
+" }}}
+" Optional Remote-Local Vimrc --------------------------------------------- {{{
+
+if filereadable(expand('~/.vimrc_remote_local'))
+ source ~/.vimrc_remote_local
+endif
+
+" }}}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/.agignore Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,2 @@
+logs/
+urls.log
Binary file weechat-old/GandiStandardSSLCA.crt has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/alias.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,45 @@
+#
+# weechat -- alias.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use commands like /set or /fset to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/quickstart/
+#
+
+[cmd]
+AAWAY = "allserv /away"
+AME = "allchan /me"
+AMSG = "allchan /msg *"
+ANICK = "allserv /nick"
+b = "/buffer"
+BYE = "quit"
+C = "buffer clear"
+CHAT = "dcc chat"
+CL = "buffer clear"
+CLOSE = "buffer close"
+EXIT = "quit"
+IG = "ignore"
+J = "join"
+K = "kick"
+KB = "kickban"
+LEAVE = "part"
+M = "msg"
+MUB = "unban *"
+N = "names"
+Q = "query"
+REDRAW = "window refresh"
+SAY = "msg *"
+SIGNOFF = "quit"
+T = "topic"
+UB = "unban"
+V = "command core version"
+W = "who"
+WC = "window merge"
+WI = "whois"
+WII = "whois $1 $1"
+WW = "whowas"
+
+[completion]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/aspell.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,33 @@
+#
+# weechat -- aspell.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use /set or similar command to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/quickstart
+#
+
+[color]
+misspelled = lightred
+suggestion = default
+suggestion_delimiter_dict = cyan
+suggestion_delimiter_word = cyan
+
+[check]
+commands = "ame,amsg,away,command,cycle,kick,kickban,me,msg,notice,part,query,quit,topic"
+default_dict = "en"
+during_search = off
+enabled = off
+real_time = off
+suggestions = -1
+word_min_length = 2
+
+[dict]
+
+[look]
+suggestion_delimiter_dict = " / "
+suggestion_delimiter_word = ","
+
+[option]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/autosort.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,24 @@
+#
+# weechat -- autosort.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use commands like /set or /fset to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/quickstart/
+#
+
+[sorting]
+case_sensitive = off
+debug_log = off
+replacements = "[]"
+rules = "[["core", 0], ["irc", 2], ["*", 1], ["irc.irc_raw", 0], ["irc.server", 1]]"
+signal_delay = 5
+signals = "buffer_opened buffer_merged buffer_unmerged buffer_renamed"
+sort_limit = 100
+sort_on_config_change = on
+
+[v3]
+helpers = "{"core_first": "${if:${buffer.full_name}!=core.weechat}", "irc_raw_first": "${if:${buffer.full_name}!=irc.irc_raw}", "irc_raw_last": "${if:${buffer.full_name}==irc.irc_raw}", "hashless_name": "${info:autosort_replace,#,,${info:autosort_escape,${buffer.name}}}", "script_or_plugin": "${if:${script_name}?${script_name}:${plugin}}"}"
+rules = "["${core_first}", "${info:autosort_order,${info:autosort_escape,${script_or_plugin}},core,*,irc,bitlbee,matrix,slack}", "${script_or_plugin}", "${irc_raw_first}", "${server}", "${info:autosort_order,${type},server,*,channel,private}", "${hashless_name}", "${buffer.full_name}"]"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/buffers.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,77 @@
+#
+# weechat -- buffers.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use /set or similar command to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/quickstart
+#
+
+[color]
+current_bg = green
+current_fg = black
+default_bg = default
+default_fg = default
+hotlist_highlight_bg = default
+hotlist_highlight_fg = *magenta
+hotlist_low_bg = default
+hotlist_low_fg = white
+hotlist_message_bg = default
+hotlist_message_fg = green
+hotlist_private_bg = default
+hotlist_private_fg = *magenta
+none_channel_bg = default
+none_channel_fg = 240
+number = green
+number_char = green
+prefix_bufname = default
+queries_default_bg = default
+queries_default_fg = default
+queries_highlight_bg = default
+queries_highlight_fg = default
+queries_message_bg = default
+queries_message_fg = default
+suffix_bufname = default
+whitelist_default_bg = default
+whitelist_default_fg = default
+whitelist_highlight_bg = default
+whitelist_highlight_fg = default
+whitelist_low_bg = default
+whitelist_low_fg = default
+whitelist_message_bg = default
+whitelist_message_fg = default
+whitelist_private_bg = default
+whitelist_private_fg = default
+
+[look]
+core_to_front = off
+detach = 0
+detach_buffer_immediately = ""
+detach_display_window_number = off
+detach_displayed_buffers = on
+detach_free_content = off
+detach_query = off
+hide_merged_buffers = none
+hotlist_counter = off
+immune_detach_buffers = ""
+indenting = on
+indenting_number = on
+jump_prev_next_visited_buffer = off
+mark_inactive = off
+mouse_move_buffer = on
+name_crop_suffix = "+"
+name_size_max = 0
+number_char = " "
+prefix = off
+prefix_bufname = ""
+prefix_empty = on
+prefix_for_query = ""
+short_names = on
+show_lag = off
+show_number = on
+sort = number
+suffix_bufname = ""
+toogle_bar = on
+whitelist_buffers = ""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/buflist.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,41 @@
+#
+# weechat -- buflist.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use commands like /set or /fset to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/quickstart/
+#
+
+[look]
+add_newline = on
+auto_scroll = 50
+display_conditions = "${buffer.hidden}==0"
+enabled = on
+mouse_jump_visited_buffer = off
+mouse_move_buffer = on
+mouse_wheel = on
+nick_prefix = off
+nick_prefix_empty = on
+signals_refresh = ""
+sort = "number,-active"
+use_items = 1
+
+[format]
+buffer = "${format_number}${indent}${format_nick_prefix}${color_hotlist}${format_name}"
+buffer_current = "${color:,blue}${format_buffer}"
+hotlist = " ${color:green}(${hotlist}${color:green})"
+hotlist_highlight = "${color:magenta}"
+hotlist_low = "${color:white}"
+hotlist_message = "${color:green}"
+hotlist_none = "${color:default}"
+hotlist_private = "${color:magenta}"
+hotlist_separator = "${color:default},"
+indent = " "
+lag = " ${color:green}[${color:brown}${lag}${color:green}]"
+name = "${name}"
+nick_prefix = "${color_nick_prefix}${nick_prefix}"
+number = "${color:green}${number}${if:${number_displayed}?.: }"
+tls_version = " ${color:default}(${if:${tls_version}==TLS1.3?${color:green}:${if:${tls_version}==TLS1.2?${color:yellow}:${color:red}}}${translate:${tls_version}}${color:default})"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/charset.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,18 @@
+#
+# weechat -- charset.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use commands like /set or /fset to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/quickstart/
+#
+
+[default]
+decode = "iso-8859-1"
+encode = ""
+
+[decode]
+
+[encode]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/exec.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,19 @@
+#
+# weechat -- exec.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use commands like /set or /fset to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/quickstart/
+#
+
+[command]
+default_options = ""
+purge_delay = 0
+shell = "${env:SHELL}"
+
+[color]
+flag_finished = lightred
+flag_running = lightgreen
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/fifo.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,14 @@
+#
+# weechat -- fifo.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use commands like /set or /fset to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/quickstart/
+#
+
+[file]
+enabled = on
+path = "%h/weechat_fifo"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/fset.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,96 @@
+#
+# weechat -- fset.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use commands like /set or /fset to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/quickstart/
+#
+
+[look]
+auto_refresh = "*"
+auto_unmark = off
+condition_catch_set = "${count} >= 1"
+export_help_default = on
+format_number = 1
+marked_string = "*"
+scroll_horizontal = 10
+show_plugins_desc = off
+sort = "~name"
+unmarked_string = " "
+use_color_value = off
+use_keys = on
+use_mute = off
+
+[format]
+export_help = "# ${description2}"
+export_option = "/set ${name} ${quoted_value}"
+export_option_null = "/unset ${name}"
+option1 = ""
+option2 = "${marked} ${name} ${type} ${value2}${newline} ${empty_name} ${_default_value}${color:darkgray} -- ${min}..${max}${newline} ${empty_name} ${description}"
+
+[color]
+default_value = default
+default_value_selected = white
+description = default
+description_selected = white
+file = default
+file_changed = brown
+file_changed_selected = yellow
+file_selected = white
+help_default_value = white
+help_description = default
+help_name = white
+help_quotes = darkgray
+help_values = default
+index = cyan
+index_selected = lightcyan
+line_marked_bg1 = default
+line_marked_bg2 = default
+line_selected_bg1 = blue
+line_selected_bg2 = red
+marked = brown
+marked_selected = yellow
+max = default
+max_selected = white
+min = default
+min_selected = white
+name = default
+name_changed = brown
+name_changed_selected = yellow
+name_selected = white
+option = default
+option_changed = brown
+option_changed_selected = yellow
+option_selected = white
+parent_name = default
+parent_name_selected = white
+parent_value = cyan
+parent_value_selected = lightcyan
+quotes = darkgray
+quotes_changed = default
+quotes_changed_selected = white
+quotes_selected = default
+section = default
+section_changed = brown
+section_changed_selected = yellow
+section_selected = white
+string_values = default
+string_values_selected = white
+title_count_options = cyan
+title_current_option = lightcyan
+title_filter = yellow
+title_marked_options = lightgreen
+title_sort = white
+type = green
+type_selected = lightgreen
+unmarked = default
+unmarked_selected = white
+value = cyan
+value_changed = brown
+value_changed_selected = yellow
+value_selected = lightcyan
+value_undef = magenta
+value_undef_selected = lightmagenta
Binary file weechat-old/icon.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/logger.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,36 @@
+#
+# weechat -- logger.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use commands like /set or /fset to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/quickstart/
+#
+
+[look]
+backlog = 20
+backlog_conditions = ""
+
+[color]
+backlog_end = darkgray
+backlog_line = darkgray
+
+[file]
+auto_log = on
+color_lines = off
+flush_delay = 120
+fsync = off
+info_lines = off
+mask = "$plugin.$name.weechatlog"
+name_lower_case = on
+nick_prefix = " <"
+nick_suffix = "> "
+path = "%h/logs/"
+replacement_char = "_"
+time_format = "%Y-%m-%d %H:%M:%S"
+
+[level]
+
+[mask]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/lua.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,14 @@
+#
+# weechat -- lua.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use /set or similar command to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/quickstart
+#
+
+[look]
+check_license = off
+eval_keep_context = on
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/perl.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,14 @@
+#
+# weechat -- perl.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use commands like /set or /fset to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/quickstart
+#
+
+[look]
+check_license = off
+eval_keep_context = on
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/perl/autoload/colorize_lines.pl Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,251 @@
+#
+# Copyright (c) 2010-2013 by Nils Görs <weechatter@arcor.de>
+# Copyleft (ɔ) 2013 by oakkitten
+#
+# colors the channel text with nick color and also highlight the whole line
+# colorize_nicks.py script will be supported
+#
+# 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/>.
+#
+# with version 3.0 some options were renamed or have new possible values:
+# old: new:
+# avail_buffer buffer
+# blacklist_channels blacklist_buffers
+# highlight new values
+
+# obsolete options:
+# buffer_autoset
+# hotlist_max_level_nicks_add
+# highlight_regex
+# highlight_words
+# shuffle
+# chat see option highlight
+
+# history:
+# 3.0: large part of script rewritten
+# fix: works nicely with irc colors
+# improved: highlight_regex and highlight_words work in a natural way
+# removed: command /colorize_lines
+# removed: option shuffle
+# 2.2: fix: regex with [tab] in message (patch by sqrrl)
+# 2.1: fix: changing highlight color did not apply messages already displayed (reported by rafi_)
+# 2.0: fix: debugging weechat::print() removed (thanks demure)
+# 1.9: fix: display bug with nick_mode
+# 1.8 add: option "use_irc_colors" (requested by Zertap)
+# fix: empty char for nick_mode was used, even when "irc.look.nick_mode_empty" was OFF (reported by FlashCode)
+# 1.7: fix: broken lines in dcc chat (reported by equatorping)
+# 1.6: improved: wildcard "*" can be used for server and/or nick. (requested by ldvx)
+# : add: new value, "only", for option "own_lines" (read help!)
+# 1.5: sync: option weechat.look.nickmode changed in 0.3.9 to "irc.look.nick_mode"
+# 1.4: fix: whole ctcp message was display in prefix (reported by : Mkaysi)
+# 1.3: fix: now using weechat::buffer_get_string() instead of regex to prevent problems with dots inside server-/channelnames (reported by surfhai)
+# 1.2: add: hook_modifier("colorize_lines") to use colorize_lines with another script.
+# : fix: regex was too greedy and also hit tag "prefix_nick_ccc"
+# 1.1: fix: problems with temporary server (reported by nand`)
+# : improved: using weechat_string_has_highlight()
+# 1.0: fix: irc.look.nick_prefix wasn't supported
+# 0.9: added: option "own_nick" (idea by travkin)
+# : new value (always) for option highlight
+# : clean up code
+# 0.8.1: fix: regex()
+# 0.8: added: option "avail_buffer" and "nicks" (please read help-page) (suggested by ldvx)
+# : fix: blacklist_buffers wasn't load at start
+# : fix: nick_modes wasn't displayed since v0.7
+# : rewrote init() routine
+# : thanks to stfn for hint with unescaped variables in regex.
+# 0.7: fix: bug when irc.look.nick_suffix was set (reported and beta-testing by: hw2) (>= weechat 0.3.4)
+# blacklist_buffers option supports servername
+# clean up code
+# 0.6: code optimazations.
+# rename of script (rainbow_text.pl -> colorize_lines.pl) (suggested by xt and flashcode)
+# 0.5: support of hotlist_max_level_nicks_add and weechat.color.chat_nick_colors (>= weechat 0.3.4)
+# 0.4: support of weechat.look.highlight_regex option (>= weechat 0.3.4)
+# : support of weechat.look.highlight option
+# : highlighted line did not work with "." inside servername
+# ; internal "autoset" function fixed
+# 0.3: support of colorize_nicks.py implemented.
+# : /me text displayed wrong nick colour (colour from suffix was used)
+# : highlight messages will be checked case insensitiv
+# 0.2: supports highlight_words_add from buffer_autoset.py script (suggested: Emralegna)
+# : correct look_nickmode colour will be used (bug reported by: Emralegna)
+# : /me text will be coloured, too
+# 0.1: initial release
+#
+# Development is currently hosted at
+# https://github.com/weechatter/weechat-scripts
+
+# use Data::Dumper
+# $Data::Dumper::Useqq=1;
+
+use strict;
+my $prgname = "colorize_lines";
+my $version = "3.0";
+my $description = "colors text in chat area with according nick color, including highlights";
+
+my %config = ("buffers" => "all", # all, channel, query
+ "blacklist_buffers" => "", # "a,b,c"
+ "lines" => "on",
+ "highlight" => "on", # on, off, nicks
+ "nicks" => "", # "d,e,f", "/file"
+ "own_lines" => "on", # on, off, only
+);
+
+my %help_desc = ("buffers" => "buffer type affected by the script (all/channel/query, default: all)",
+ "blacklist_buffers" => "comma-separated list of channels to be ignored (e.g. freenode.#weechat,*.#python)",
+ "lines" => "apply nickname color to the non-highlighted lines (off/on/nicks). the latter will limit highlighting to nicknames in option 'nicks'",
+ "highlight" => "apply highlight color to the highlighted lines (off/on/nicks). the latter will limit highlighting to nicknames in option 'nicks'",
+ "nicks" => "comma-separater list of nicks (e.g. freenode.cat,*.dog) OR file name starting with '/' (e.g. /file.txt). in the latter case, nicknames will get loaded from that file inside weechat folder (e.g. from ~/.weechat/file.txt). nicknames in file are newline-separated (e.g. freenode.dog\\n*.cat)",
+ "own_lines" => "apply nickname color to own lines (off/on/only). the latter turns off all other kinds of coloring altogether",
+);
+
+#################################################################################################### config
+
+# program starts here
+sub colorize_cb {
+ my ( $data, $modifier, $modifier_data, $string ) = @_;
+
+ # quit if it's not a privmsg or ctcp
+ # or we are not supposed to
+ if ((index($modifier_data,"irc_privmsg") == -1) ||
+ (index($modifier_data,"irc_ctcp") >= 0)) {
+ return $string;
+ }
+
+ # find buffer pointer
+ $modifier_data =~ m/([^;]*);([^;]*);/;
+ my $buffer = weechat::buffer_search($1, $2);
+ return $string if ($buffer eq "");
+
+ # find buffer name, server name
+ # return if buffer is in a blacklist
+ my $buffername = weechat::buffer_get_string($buffer, "name");
+ return $string if weechat::string_has_highlight($buffername, $config{blacklist_buffers});
+ my $servername = weechat::buffer_get_string($buffer, "localvar_server");
+
+ # find stuff between \t
+ $string =~ m/^([^\t]*)\t(.*)/;
+ my $left = $1;
+ my $right = $2;
+
+ # find nick of the sender
+ # find out if we are doing an action
+ my $nick = ($modifier_data =~ m/(^|,)nick_([^,]*)/) ? $2 : weechat::string_remove_color($left, "");
+ my $action = ($modifier_data =~ m/\birc_action\b/) ? 1 : 0;
+
+ ######################################## get color
+
+ my $color = "";
+ my $my_nick = weechat::buffer_get_string($buffer, "localvar_nick");
+ if ($my_nick eq $nick) {
+ # it's our own line
+ # process only if own_lines is "on" or "only" (i.e. not "off")
+ return $string if ($config{own_lines} eq "off");
+ $color = weechat::color("chat_nick_self");
+ } else {
+ # it's someone else's line
+ # don't process is own_lines are "only"
+ # in order to get correct matching, remove colors from the string
+ return $string if ($config{own_lines} eq "only");
+ my $right_nocolor = weechat::string_remove_color($right, "");
+ if (weechat::string_has_highlight($right_nocolor, weechat::buffer_string_replace_local_var($buffer, weechat::buffer_get_string($buffer, "highlight_words"))) ||
+ weechat::string_has_highlight($right_nocolor, weechat::config_string(weechat::config_get("weechat.look.highlight"))) ||
+ weechat::string_has_highlight_regex($right_nocolor, weechat::config_string(weechat::config_get("weechat.look.highlight_regex"))) ||
+ weechat::string_has_highlight_regex($right_nocolor, weechat::buffer_get_string($buffer, "highlight_regex"))
+ ) {
+ # we have a hilight! get a hilight color
+ # and replace the first occurance of coloring, that'd be nick color
+ # process only if highlight is "on" OR "nicks" & nick's in nicks
+ return $string if ($config{highlight} eq "off" ||
+ ($config{highlight} eq "nicks" && !weechat::string_has_highlight("$servername.$nick", $config{nicks})));
+ $color = weechat::color('chat_highlight');
+ $right =~ s/\31[^\31 ]+?\Q$nick/$color$nick/ if ($action);
+ } else {
+ # that's not a highlight
+ # process only if lines is "on" OR "nicks" & nick's in nicks
+ return $string if ($config{lines} eq "off" ||
+ ($config{lines} eq "nicks" && !weechat::string_has_highlight("$servername.$nick", $config{nicks})));
+ $color = weechat::info_get('irc_nick_color', $nick);
+ }
+ }
+
+ ######################################## inject colors and go!
+
+ my $out = "";
+ if ($action) {
+ # remove the first color reset - after * nick
+ # make other resets reset to our color
+ $right =~ s/\34//;
+ $right =~ s/\34/\34$color/g;
+ $out = $left . "\t" . $right . "\34"
+ } else {
+ # make other resets reset to our color
+ $right =~ s/\34/\34$color/g;
+ $out = $left . "\t" . $color . $right . "\34"
+ }
+ #weechat::print("", ""); weechat::print("", "\$str " . Dumper($string)); weechat::print("", "\$out " . Dumper($out));
+ return $out;
+}
+
+#################################################################################################### config
+
+# read nicknames if $conf{nisks} starts with /
+# after this, $conf{nisks} is of form a,b,c,d
+# if it doesnt start with /, assume it's already a,b,c,d
+sub nicklist_read {
+ return if (substr($config{nicks}, 0, 1) ne "/");
+ my $file = weechat::info_get("weechat_dir", "") . $config{nicks};
+ return unless -e $file;
+ my $nili = "";
+ open (WL, "<", $file) || DEBUG("$file: $!");
+ while (<WL>) {
+ chomp; # kill LF
+ $nili .= $_ . ",";
+ }
+ close WL;
+ chop $nili; # remove last ","
+ $config{nicks} = $nili;
+}
+
+# called when a config option ha been changed
+# $name = plugins.var.perl.$prgname.nicks etc
+sub toggle_config_by_set {
+ my ($pointer, $name, $value) = @_;
+ $name = substr($name,length("plugins.var.perl.$prgname."),length($name));
+ $config{$name} = lc($value);
+ nicklist_read() if ($name eq "nicks");
+}
+
+# read configuration from weechat OR
+# set default options and
+# set dectription if weechat >= 0.3.5
+# after done, read nicklist from file if needed
+sub init_config {
+ my $weechat_version = weechat::info_get('version_number', '') || 0;
+ foreach my $option (keys %config){
+ if (!weechat::config_is_set_plugin($option)) {
+ weechat::config_set_plugin($option, $config{$option});
+ weechat::config_set_desc_plugin($option, $help_desc{$option}) if ($weechat_version >= 0x00030500); # v0.3.5
+ } else {
+ $config{$option} = lc(weechat::config_get_plugin($option));
+ }
+ }
+ nicklist_read();
+}
+
+#################################################################################################### start
+
+weechat::register($prgname, "Nils Görs <weechatter\@arcor.de>", $version, "GPL3", $description, "", "");
+weechat::hook_modifier("500|weechat_print","colorize_cb", "");
+init_config();
+weechat::hook_config("plugins.var.perl.$prgname.*", "toggle_config_by_set", "");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/python.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,14 @@
+#
+# weechat -- python.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use commands like /set or /fset to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/quickstart/
+#
+
+[look]
+check_license = off
+eval_keep_context = on
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/python/autoload/autosort.py Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1 @@
+../autosort.py
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/python/autoload/brows.py Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,77 @@
+import subprocess
+import os
+
+SCRIPT_NAME = 'brows'
+SCRIPT_AUTHOR = 'Steve Losh <steve@stevelosh.com>'
+SCRIPT_VERSION = '1.0'
+SCRIPT_LICENSE = 'MIT/X11'
+SCRIPT_DESC = 'Launch brows to view URLs'
+SCRIPT_COMMAND = 'brows'
+
+import_ok = True
+
+BROWS = os.environ.get('BROWS', 'brows')
+
+try:
+ import weechat
+except ImportError:
+ print('This is a weechat script, what are you doing, run it in weechat, jesus')
+ import_ok = False
+
+weechat_version = 0
+
+def hd(fn, name, obj, attr):
+ return fn(weechat.hdata_get(name), obj, attr)
+
+def hdp(name, obj, attr):
+ return hd(weechat.hdata_pointer, name, obj, attr)
+
+def hds(name, obj, attr):
+ return hd(weechat.hdata_string, name, obj, attr)
+
+def get_lines(buffer, numlines):
+ lines = hdp("buffer", buffer, "own_lines")
+ if not lines:
+ # null pointer wat do
+ return None
+
+ last_lines = []
+
+ line = hdp("lines", lines, "last_line")
+ for _ in range(numlines):
+ if not line:
+ # shit we're at the top of the buffer
+ break
+
+ data = hdp("line", line, "data")
+ msg = hds("line_data", data, "message")
+ msg = weechat.string_remove_color(msg, "")
+
+ last_lines.append(msg.strip())
+
+ line = hdp("line", line, "prev_line")
+
+ return last_lines
+
+def brows(data, buffer, args, numlines=100):
+ lines = get_lines(buffer, numlines)
+
+ proc = subprocess.Popen([BROWS], stdin=subprocess.PIPE)
+ proc.communicate(input='\n'.join(lines))
+ weechat.command("", "/window refresh")
+
+ return weechat.WEECHAT_RC_OK
+
+
+if __name__ == '__main__' and import_ok:
+ if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION,
+ SCRIPT_LICENSE, SCRIPT_DESC, '', ''):
+ weechat_version = weechat.info_get('version_number', '') or 0
+ weechat.hook_command(
+ SCRIPT_COMMAND,
+ 'Launch brows to view URLs',
+ '',
+ '',
+ '',
+ 'brows',
+ '')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/python/autoload/editor.py Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,65 @@
+import subprocess
+import os
+import tempfile
+
+SCRIPT_NAME = 'editor'
+SCRIPT_AUTHOR = 'Steve Losh <steve@stevelosh.com>'
+SCRIPT_VERSION = '1.0'
+SCRIPT_LICENSE = 'MIT/X11'
+SCRIPT_DESC = 'Launch an external editor to compose a message'
+SCRIPT_COMMAND = 'editor'
+
+import_ok = True
+
+EDITOR = os.environ.get('EDITOR','vim')
+
+try:
+ import weechat
+except ImportError:
+ print('This is a weechat script, what are you doing, run it in weechat, jesus')
+ import_ok = False
+
+weechat_version = 0
+
+
+def get_data(suffix, initial_data):
+ with tempfile.NamedTemporaryFile(suffix=".%s" % suffix, mode="w+") as tf:
+ tf.write(initial_data)
+ tf.flush()
+
+ if subprocess.call([EDITOR, tf.name]) != 0:
+ return None
+
+ # Reopen, because most editors do atomic write-tmp+rename saves which
+ # fucks with Python here.
+ tf.file.close()
+ with open(tf.name) as tf2:
+ return tf2.read()
+
+def editor(data, buffer, args):
+ suffix = args or "tmp"
+
+ line = weechat.buffer_get_string(buffer, "input")
+
+ data = get_data(suffix, line)
+ if data:
+ weechat.command(buffer, "/input delete_line")
+ weechat.command(buffer, data.strip())
+
+ weechat.command("", "/window refresh")
+
+ return weechat.WEECHAT_RC_OK
+
+
+if __name__ == '__main__' and import_ok:
+ if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION,
+ SCRIPT_LICENSE, SCRIPT_DESC, '', ''):
+ weechat_version = weechat.info_get('version_number', '') or 0
+ weechat.hook_command(
+ SCRIPT_COMMAND,
+ 'Open $EDITOR to compose a message',
+ '[file-extension]',
+ 'If an argument is given, it will be used as the extension for the temporary file.',
+ '',
+ 'editor',
+ '')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/python/autoload/notify.py Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,29 @@
+import weechat, subprocess
+
+SCRIPT_NAME = 'notify'
+SCRIPT_AUTHOR = 'Steve Losh <steve@stevelosh.com>'
+SCRIPT_VERSION = '0.0.1'
+SCRIPT_LICENSE = 'MIT'
+SCRIPT_DESC = 'notify-send for weechat'
+
+weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, '', '')
+
+weechat.hook_print('', 'irc_privmsg', '', 1, 'notify', '')
+
+def _notify(text):
+ subprocess.call(['notify-send', text])
+
+def notify(data, buffer, date, tags, displayed, highlight, prefix, message):
+ # ignore if it's yourself
+ own_nick = weechat.buffer_get_string(buffer, 'localvar_nick')
+
+ if prefix == own_nick or prefix == ('@%s' % own_nick):
+ return weechat.WEECHAT_RC_OK
+
+ if int(highlight):
+ channel = weechat.buffer_get_string(buffer, 'localvar_channel')
+ _notify('%s %s\n%s' % (prefix, channel, message))
+ elif 'notify_private' in tags:
+ _notify('%s [PM]\n%s' % (prefix, message))
+
+ return weechat.WEECHAT_RC_OK
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/python/autoload/quotes.py Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,124 @@
+import subprocess, os
+
+SCRIPT_NAME = 'quotes'
+SCRIPT_AUTHOR = 'Steve Losh <steve@stevelosh.com>'
+SCRIPT_VERSION = '1.0'
+SCRIPT_LICENSE = 'MIT/X11'
+SCRIPT_DESC = 'Grab quotes and shove them into a text file.'
+SCRIPT_COMMAND = 'quo'
+SCRIPT_COMMAND_LONG = 'quoo'
+SCRIPT_COMMAND_COPY = 'cop'
+
+HOME = os.getenv("HOME")
+QUOTE_FILE = '%s/Dropbox/quotes.txt' % HOME
+COPY_FILE = '%s/.ircopy.irc' % HOME
+
+import_ok = True
+
+try:
+ import weechat
+except ImportError:
+ print('This is a weechat script, what are you doing, run it in weechat, jesus')
+ import_ok = False
+
+weechat_version = 0
+
+def hd(fn, name, obj, attr):
+ return fn(weechat.hdata_get(name), obj, attr)
+
+def hdp(name, obj, attr):
+ return hd(weechat.hdata_pointer, name, obj, attr)
+
+def hds(name, obj, attr):
+ return hd(weechat.hdata_string, name, obj, attr)
+
+def get_lines(buffer, numlines):
+ lines = hdp("buffer", buffer, "own_lines")
+ if not lines:
+ # null pointer wat do
+ return None
+
+ last_lines = []
+
+ line = hdp("lines", lines, "last_line")
+ for _ in range(numlines):
+ if not line:
+ # shit we're at the top of the buffer
+ break
+
+ data = hdp("line", line, "data")
+ msg = hds("line_data", data, "message")
+ pre = hds("line_data", data, "prefix")
+
+ msg = weechat.string_remove_color(msg, "")
+ pre = weechat.string_remove_color(pre, "")
+
+ last_lines.append("<%s> %s" % (pre.strip(), msg.strip()))
+
+ line = hdp("line", line, "prev_line")
+
+ last_lines.reverse()
+ return last_lines
+
+def quote_grab(data, buffer, args, numlines=15):
+ lines = get_lines(buffer, numlines)
+
+ with open(QUOTE_FILE, 'a') as f:
+ f.write("\n---\n")
+ f.write('\n'.join(lines))
+
+ subprocess.call(["nvim", QUOTE_FILE,
+ # start at the bottom of the file
+ "+",
+ # move up N lines, where N is how many we appended
+ "-c", "normal! %dk" % len(lines)])
+ weechat.command("", "/window refresh")
+
+ return weechat.WEECHAT_RC_OK
+
+def quote_grab_long(data, buffer, args):
+ return quote_grab(data, buffer, args, 75)
+
+def quote_grab_copy(data, buffer, args):
+ lines = get_lines(buffer, 1000)
+
+ with open(COPY_FILE, 'w') as f:
+ f.write('\n'.join(lines))
+
+ subprocess.call(["nvim", COPY_FILE,
+ # start at the bottom of the file
+ "+"])
+ weechat.command("", "/window refresh")
+
+ return weechat.WEECHAT_RC_OK
+
+if __name__ == '__main__' and import_ok:
+ if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION,
+ SCRIPT_LICENSE, SCRIPT_DESC, '', ''):
+ weechat_version = weechat.info_get('version_number', '') or 0
+ weechat.hook_command(
+ SCRIPT_COMMAND,
+ 'Appends the last 15 lines of the current buffer to your quotes '
+ 'file and opens it in Vim so you can trim it.',
+ '',
+ '',
+ '',
+ 'quote_grab',
+ '')
+ weechat.hook_command(
+ SCRIPT_COMMAND_LONG,
+ 'Appends the last 75 lines of the current buffer to your quotes '
+ 'file and opens it in Vim so you can trim it.',
+ '',
+ '',
+ '',
+ 'quote_grab_long',
+ '')
+ weechat.hook_command(
+ SCRIPT_COMMAND_COPY,
+ 'Open the last 1000 lines of the file in Vim so you can copy.',
+ '',
+ '',
+ '',
+ 'quote_grab_copy',
+ '')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/python/autoload/urlgrab.py Wed Aug 27 16:19:24 2025 -0400
@@ -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):
+ # weechat.prnt("", out)
+ # weechat.prnt("", 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")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/python/autoload/wee_slack.py Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1 @@
+/home/sjl/src/wee-slack/wee_slack.py
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/python/autosort.py Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,1075 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2013-2017 Maarten de Vries <maarten@de-vri.es>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+#
+# Autosort automatically keeps your buffers sorted and grouped by server.
+# You can define your own sorting rules. See /help autosort for more details.
+#
+# https://github.com/de-vri-es/weechat-autosort
+#
+
+#
+# Changelog:
+# 3.9:
+# * Remove `buffers.pl` from recommended settings.
+# 3,8:
+# * Fix relative sorting on script name in default rules.
+# * Document a useful property of stable sort algorithms.
+# 3.7:
+# * Make default rules work with bitlbee, matrix and slack.
+# 3.6:
+# * Add more documentation on provided info hooks.
+# 3.5:
+# * Add ${info:autosort_escape,...} to escape arguments for other info hooks.
+# 3.4:
+# * Fix rate-limit of sorting to prevent high CPU load and lock-ups.
+# * Fix bug in parsing empty arguments for info hooks.
+# * Add debug_log option to aid with debugging.
+# * Correct a few typos.
+# 3.3:
+# * Fix the /autosort debug command for unicode.
+# * Update the default rules to work better with Slack.
+# 3.2:
+# * Fix python3 compatiblity.
+# 3.1:
+# * Use colors to format the help text.
+# 3.0:
+# * Switch to evaluated expressions for sorting.
+# * Add `/autosort debug` command.
+# * Add ${info:autosort_replace,from,to,text} to replace substrings in sort rules.
+# * Add ${info:autosort_order,value,first,second,third} to ease writing sort rules.
+# * Make tab completion context aware.
+# 2.8:
+# * Fix compatibility with python 3 regarding unicode handling.
+# 2.7:
+# * Fix sorting of buffers with spaces in their name.
+# 2.6:
+# * Ignore case in rules when doing case insensitive sorting.
+# 2.5:
+# * Fix handling unicode buffer names.
+# * Add hint to set irc.look.server_buffer to independent and buffers.look.indenting to on.
+# 2.4:
+# * Make script python3 compatible.
+# 2.3:
+# * Fix sorting items without score last (regressed in 2.2).
+# 2.2:
+# * Add configuration option for signals that trigger a sort.
+# * Add command to manually trigger a sort (/autosort sort).
+# * Add replacement patterns to apply before sorting.
+# 2.1:
+# * Fix some minor style issues.
+# 2.0:
+# * Allow for custom sort rules.
+#
+
+
+import json
+import math
+import re
+import sys
+import time
+import weechat
+
+SCRIPT_NAME = 'autosort'
+SCRIPT_AUTHOR = 'Maarten de Vries <maarten@de-vri.es>'
+SCRIPT_VERSION = '3.9'
+SCRIPT_LICENSE = 'GPL3'
+SCRIPT_DESC = 'Flexible automatic (or manual) buffer sorting based on eval expressions.'
+
+
+config = None
+hooks = []
+signal_delay_timer = None
+sort_limit_timer = None
+sort_queued = False
+
+
+# Make sure that unicode, bytes and str are always available in python2 and 3.
+# For python 2, str == bytes
+# For python 3, str == unicode
+if sys.version_info[0] >= 3:
+ unicode = str
+
+def ensure_str(input):
+ '''
+ Make sure the given type if the correct string type for the current python version.
+ That means bytes for python2 and unicode for python3.
+ '''
+ if not isinstance(input, str):
+ if isinstance(input, bytes):
+ return input.encode('utf-8')
+ if isinstance(input, unicode):
+ return input.decode('utf-8')
+ return input
+
+
+if hasattr(time, 'perf_counter'):
+ perf_counter = time.perf_counter
+else:
+ perf_counter = time.clock
+
+def casefold(string):
+ if hasattr(string, 'casefold'): return string.casefold()
+ # Fall back to lowercasing for python2.
+ return string.lower()
+
+def list_swap(values, a, b):
+ values[a], values[b] = values[b], values[a]
+
+def list_move(values, old_index, new_index):
+ values.insert(new_index, values.pop(old_index))
+
+def list_find(collection, value):
+ for i, elem in enumerate(collection):
+ if elem == value: return i
+ return None
+
+class HumanReadableError(Exception):
+ pass
+
+def parse_int(arg, arg_name = 'argument'):
+ ''' Parse an integer and provide a more human readable error. '''
+ arg = arg.strip()
+ try:
+ return int(arg)
+ except ValueError:
+ raise HumanReadableError('Invalid {0}: expected integer, got "{1}".'.format(arg_name, arg))
+
+def decode_rules(blob):
+ parsed = json.loads(blob)
+ if not isinstance(parsed, list):
+ log('Malformed rules, expected a JSON encoded list of strings, but got a {0}. No rules have been loaded. Please fix the setting manually.'.format(type(parsed)))
+ return []
+
+ for i, entry in enumerate(parsed):
+ if not isinstance(entry, (str, unicode)):
+ log('Rule #{0} is not a string but a {1}. No rules have been loaded. Please fix the setting manually.'.format(i, type(entry)))
+ return []
+
+ return parsed
+
+def decode_helpers(blob):
+ parsed = json.loads(blob)
+ if not isinstance(parsed, dict):
+ log('Malformed helpers, expected a JSON encoded dictionary but got a {0}. No helpers have been loaded. Please fix the setting manually.'.format(type(parsed)))
+ return {}
+
+ for key, value in parsed.items():
+ if not isinstance(value, (str, unicode)):
+ log('Helper "{0}" is not a string but a {1}. No helpers have been loaded. Please fix setting manually.'.format(key, type(value)))
+ return {}
+ return parsed
+
+class Config:
+ ''' The autosort configuration. '''
+
+ default_rules = json.dumps([
+ '${core_first}',
+ '${info:autosort_order,${info:autosort_escape,${script_or_plugin}},core,*,irc,bitlbee,matrix,slack}',
+ '${script_or_plugin}',
+ '${irc_raw_first}',
+ '${server}',
+ '${info:autosort_order,${type},server,*,channel,private}',
+ '${hashless_name}',
+ '${buffer.full_name}',
+ ])
+
+ default_helpers = json.dumps({
+ 'core_first': '${if:${buffer.full_name}!=core.weechat}',
+ 'irc_raw_first': '${if:${buffer.full_name}!=irc.irc_raw}',
+ 'irc_raw_last': '${if:${buffer.full_name}==irc.irc_raw}',
+ 'hashless_name': '${info:autosort_replace,#,,${info:autosort_escape,${buffer.name}}}',
+ 'script_or_plugin': '${if:${script_name}?${script_name}:${plugin}}',
+ })
+
+ default_signal_delay = 5
+ default_sort_limit = 100
+
+ default_signals = 'buffer_opened buffer_merged buffer_unmerged buffer_renamed'
+
+ def __init__(self, filename):
+ ''' Initialize the configuration. '''
+
+ self.filename = filename
+ self.config_file = weechat.config_new(self.filename, '', '')
+ self.sorting_section = None
+ self.v3_section = None
+
+ self.case_sensitive = False
+ self.rules = []
+ self.helpers = {}
+ self.signals = []
+ self.signal_delay = Config.default_signal_delay,
+ self.sort_limit = Config.default_sort_limit,
+ self.sort_on_config = True
+ self.debug_log = False
+
+ self.__case_sensitive = None
+ self.__rules = None
+ self.__helpers = None
+ self.__signals = None
+ self.__signal_delay = None
+ self.__sort_limit = None
+ self.__sort_on_config = None
+ self.__debug_log = None
+
+ if not self.config_file:
+ log('Failed to initialize configuration file "{0}".'.format(self.filename))
+ return
+
+ self.sorting_section = weechat.config_new_section(self.config_file, 'sorting', False, False, '', '', '', '', '', '', '', '', '', '')
+ self.v3_section = weechat.config_new_section(self.config_file, 'v3', False, False, '', '', '', '', '', '', '', '', '', '')
+
+ if not self.sorting_section:
+ log('Failed to initialize section "sorting" of configuration file.')
+ weechat.config_free(self.config_file)
+ return
+
+ self.__case_sensitive = weechat.config_new_option(
+ self.config_file, self.sorting_section,
+ 'case_sensitive', 'boolean',
+ 'If this option is on, sorting is case sensitive.',
+ '', 0, 0, 'off', 'off', 0,
+ '', '', '', '', '', ''
+ )
+
+ weechat.config_new_option(
+ self.config_file, self.sorting_section,
+ 'rules', 'string',
+ 'Sort rules used by autosort v2.x and below. Not used by autosort anymore.',
+ '', 0, 0, '', '', 0,
+ '', '', '', '', '', ''
+ )
+
+ weechat.config_new_option(
+ self.config_file, self.sorting_section,
+ 'replacements', 'string',
+ 'Replacement patterns used by autosort v2.x and below. Not used by autosort anymore.',
+ '', 0, 0, '', '', 0,
+ '', '', '', '', '', ''
+ )
+
+ self.__rules = weechat.config_new_option(
+ self.config_file, self.v3_section,
+ 'rules', 'string',
+ 'An ordered list of sorting rules encoded as JSON. See /help autosort for commands to manipulate these rules.',
+ '', 0, 0, Config.default_rules, Config.default_rules, 0,
+ '', '', '', '', '', ''
+ )
+
+ self.__helpers = weechat.config_new_option(
+ self.config_file, self.v3_section,
+ 'helpers', 'string',
+ 'A dictionary helper variables to use in the sorting rules, encoded as JSON. See /help autosort for commands to manipulate these helpers.',
+ '', 0, 0, Config.default_helpers, Config.default_helpers, 0,
+ '', '', '', '', '', ''
+ )
+
+ self.__signals = weechat.config_new_option(
+ self.config_file, self.sorting_section,
+ 'signals', 'string',
+ 'A space separated list of signals that will cause autosort to resort your buffer list.',
+ '', 0, 0, Config.default_signals, Config.default_signals, 0,
+ '', '', '', '', '', ''
+ )
+
+ self.__signal_delay = weechat.config_new_option(
+ self.config_file, self.sorting_section,
+ 'signal_delay', 'integer',
+ 'Delay in milliseconds to wait after a signal before sorting the buffer list. This prevents triggering many times if multiple signals arrive in a short time. It can also be needed to wait for buffer localvars to be available.',
+ '', 0, 1000, str(Config.default_signal_delay), str(Config.default_signal_delay), 0,
+ '', '', '', '', '', ''
+ )
+
+ self.__sort_limit = weechat.config_new_option(
+ self.config_file, self.sorting_section,
+ 'sort_limit', 'integer',
+ 'Minimum delay in milliseconds to wait after sorting before signals can trigger a sort again. This is effectively a rate limit on sorting. Keeping signal_delay low while setting this higher can reduce excessive sorting without a long initial delay.',
+ '', 0, 1000, str(Config.default_sort_limit), str(Config.default_sort_limit), 0,
+ '', '', '', '', '', ''
+ )
+
+ self.__sort_on_config = weechat.config_new_option(
+ self.config_file, self.sorting_section,
+ 'sort_on_config_change', 'boolean',
+ 'Decides if the buffer list should be sorted when autosort configuration changes.',
+ '', 0, 0, 'on', 'on', 0,
+ '', '', '', '', '', ''
+ )
+
+ self.__debug_log = weechat.config_new_option(
+ self.config_file, self.sorting_section,
+ 'debug_log', 'boolean',
+ 'If enabled, print more debug messages. Not recommended for normal usage.',
+ '', 0, 0, 'off', 'off', 0,
+ '', '', '', '', '', ''
+ )
+
+ if weechat.config_read(self.config_file) != weechat.WEECHAT_RC_OK:
+ log('Failed to load configuration file.')
+
+ if weechat.config_write(self.config_file) != weechat.WEECHAT_RC_OK:
+ log('Failed to write configuration file.')
+
+ self.reload()
+
+ def reload(self):
+ ''' Load configuration variables. '''
+
+ self.case_sensitive = weechat.config_boolean(self.__case_sensitive)
+
+ rules_blob = weechat.config_string(self.__rules)
+ helpers_blob = weechat.config_string(self.__helpers)
+ signals_blob = weechat.config_string(self.__signals)
+
+ self.rules = decode_rules(rules_blob)
+ self.helpers = decode_helpers(helpers_blob)
+ self.signals = signals_blob.split()
+ self.signal_delay = weechat.config_integer(self.__signal_delay)
+ self.sort_limit = weechat.config_integer(self.__sort_limit)
+ self.sort_on_config = weechat.config_boolean(self.__sort_on_config)
+ self.debug_log = weechat.config_boolean(self.__debug_log)
+
+ def save_rules(self, run_callback = True):
+ ''' Save the current rules to the configuration. '''
+ weechat.config_option_set(self.__rules, json.dumps(self.rules), run_callback)
+
+ def save_helpers(self, run_callback = True):
+ ''' Save the current helpers to the configuration. '''
+ weechat.config_option_set(self.__helpers, json.dumps(self.helpers), run_callback)
+
+
+def pad(sequence, length, padding = None):
+ ''' Pad a list until is has a certain length. '''
+ return sequence + [padding] * max(0, (length - len(sequence)))
+
+def log(message, buffer = 'NULL'):
+ weechat.prnt(buffer, 'autosort: {0}'.format(message))
+
+def debug(message, buffer = 'NULL'):
+ if config.debug_log:
+ weechat.prnt(buffer, 'autosort: debug: {0}'.format(message))
+
+def get_buffers():
+ ''' Get a list of all the buffers in weechat. '''
+ hdata = weechat.hdata_get('buffer')
+ buffer = weechat.hdata_get_list(hdata, "gui_buffers");
+
+ result = []
+ while buffer:
+ number = weechat.hdata_integer(hdata, buffer, 'number')
+ result.append((number, buffer))
+ buffer = weechat.hdata_pointer(hdata, buffer, 'next_buffer')
+ return hdata, result
+
+class MergedBuffers(list):
+ """ A list of merged buffers, possibly of size 1. """
+ def __init__(self, number):
+ super(MergedBuffers, self).__init__()
+ self.number = number
+
+def merge_buffer_list(buffers):
+ '''
+ Group merged buffers together.
+ The output is a list of MergedBuffers.
+ '''
+ if not buffers: return []
+ result = {}
+ for number, buffer in buffers:
+ if number not in result: result[number] = MergedBuffers(number)
+ result[number].append(buffer)
+ return result.values()
+
+def sort_buffers(hdata, buffers, rules, helpers, case_sensitive):
+ for merged in buffers:
+ for buffer in merged:
+ name = weechat.hdata_string(hdata, buffer, 'name')
+
+ return sorted(buffers, key=merged_sort_key(rules, helpers, case_sensitive))
+
+def buffer_sort_key(rules, helpers, case_sensitive):
+ ''' Create a sort key function for a list of lists of merged buffers. '''
+ def key(buffer):
+ extra_vars = {}
+ for helper_name, helper in sorted(helpers.items()):
+ expanded = weechat.string_eval_expression(helper, {"buffer": buffer}, {}, {})
+ extra_vars[helper_name] = expanded if case_sensitive else casefold(expanded)
+ result = []
+ for rule in rules:
+ expanded = weechat.string_eval_expression(rule, {"buffer": buffer}, extra_vars, {})
+ result.append(expanded if case_sensitive else casefold(expanded))
+ return result
+
+ return key
+
+def merged_sort_key(rules, helpers, case_sensitive):
+ buffer_key = buffer_sort_key(rules, helpers, case_sensitive)
+ def key(merged):
+ best = None
+ for buffer in merged:
+ this = buffer_key(buffer)
+ if best is None or this < best: best = this
+ return best
+ return key
+
+def apply_buffer_order(buffers):
+ ''' Sort the buffers in weechat according to the given order. '''
+ for i, buffer in enumerate(buffers):
+ weechat.buffer_set(buffer[0], "number", str(i + 1))
+
+def split_args(args, expected, optional = 0):
+ ''' Split an argument string in the desired number of arguments. '''
+ split = args.split(' ', expected - 1)
+ if (len(split) < expected):
+ raise HumanReadableError('Expected at least {0} arguments, got {1}.'.format(expected, len(split)))
+ return split[:-1] + pad(split[-1].split(' ', optional), optional + 1, '')
+
+def do_sort(verbose = False):
+ start = perf_counter()
+
+ hdata, buffers = get_buffers()
+ buffers = merge_buffer_list(buffers)
+ buffers = sort_buffers(hdata, buffers, config.rules, config.helpers, config.case_sensitive)
+ apply_buffer_order(buffers)
+
+ elapsed = perf_counter() - start
+ if verbose:
+ log("Finished sorting buffers in {0:.4f} seconds.".format(elapsed))
+ else:
+ debug("Finished sorting buffers in {0:.4f} seconds.".format(elapsed))
+
+def command_sort(buffer, command, args):
+ ''' Sort the buffers and print a confirmation. '''
+ do_sort(True)
+ return weechat.WEECHAT_RC_OK
+
+def command_debug(buffer, command, args):
+ hdata, buffers = get_buffers()
+ buffers = merge_buffer_list(buffers)
+
+ # Show evaluation results.
+ log('Individual evaluation results:')
+ start = perf_counter()
+ key = buffer_sort_key(config.rules, config.helpers, config.case_sensitive)
+ results = []
+ for merged in buffers:
+ for buffer in merged:
+ fullname = weechat.hdata_string(hdata, buffer, 'full_name')
+ results.append((fullname, key(buffer)))
+ elapsed = perf_counter() - start
+
+ for fullname, result in results:
+ fullname = ensure_str(fullname)
+ result = [ensure_str(x) for x in result]
+ log('{0}: {1}'.format(fullname, result))
+ log('Computing evaluation results took {0:.4f} seconds.'.format(elapsed))
+
+ return weechat.WEECHAT_RC_OK
+
+def command_rule_list(buffer, command, args):
+ ''' Show the list of sorting rules. '''
+ output = 'Sorting rules:\n'
+ for i, rule in enumerate(config.rules):
+ output += ' {0}: {1}\n'.format(i, rule)
+ if not len(config.rules):
+ output += ' No sorting rules configured.\n'
+ log(output )
+
+ return weechat.WEECHAT_RC_OK
+
+
+def command_rule_add(buffer, command, args):
+ ''' Add a rule to the rule list. '''
+ config.rules.append(args)
+ config.save_rules()
+ command_rule_list(buffer, command, '')
+
+ return weechat.WEECHAT_RC_OK
+
+
+def command_rule_insert(buffer, command, args):
+ ''' Insert a rule at the desired position in the rule list. '''
+ index, rule = split_args(args, 2)
+ index = parse_int(index, 'index')
+
+ config.rules.insert(index, rule)
+ config.save_rules()
+ command_rule_list(buffer, command, '')
+ return weechat.WEECHAT_RC_OK
+
+
+def command_rule_update(buffer, command, args):
+ ''' Update a rule in the rule list. '''
+ index, rule = split_args(args, 2)
+ index = parse_int(index, 'index')
+
+ config.rules[index] = rule
+ config.save_rules()
+ command_rule_list(buffer, command, '')
+ return weechat.WEECHAT_RC_OK
+
+
+def command_rule_delete(buffer, command, args):
+ ''' Delete a rule from the rule list. '''
+ index = args.strip()
+ index = parse_int(index, 'index')
+
+ config.rules.pop(index)
+ config.save_rules()
+ command_rule_list(buffer, command, '')
+ return weechat.WEECHAT_RC_OK
+
+
+def command_rule_move(buffer, command, args):
+ ''' Move a rule to a new position. '''
+ index_a, index_b = split_args(args, 2)
+ index_a = parse_int(index_a, 'index')
+ index_b = parse_int(index_b, 'index')
+
+ list_move(config.rules, index_a, index_b)
+ config.save_rules()
+ command_rule_list(buffer, command, '')
+ return weechat.WEECHAT_RC_OK
+
+
+def command_rule_swap(buffer, command, args):
+ ''' Swap two rules. '''
+ index_a, index_b = split_args(args, 2)
+ index_a = parse_int(index_a, 'index')
+ index_b = parse_int(index_b, 'index')
+
+ list_swap(config.rules, index_a, index_b)
+ config.save_rules()
+ command_rule_list(buffer, command, '')
+ return weechat.WEECHAT_RC_OK
+
+
+def command_helper_list(buffer, command, args):
+ ''' Show the list of helpers. '''
+ output = 'Helper variables:\n'
+
+ width = max(map(lambda x: len(x) if len(x) <= 30 else 0, config.helpers.keys()))
+
+ for name, expression in sorted(config.helpers.items()):
+ output += ' {0:>{width}}: {1}\n'.format(name, expression, width=width)
+ if not len(config.helpers):
+ output += ' No helper variables configured.'
+ log(output)
+
+ return weechat.WEECHAT_RC_OK
+
+
+def command_helper_set(buffer, command, args):
+ ''' Add/update a helper to the helper list. '''
+ name, expression = split_args(args, 2)
+
+ config.helpers[name] = expression
+ config.save_helpers()
+ command_helper_list(buffer, command, '')
+
+ return weechat.WEECHAT_RC_OK
+
+def command_helper_delete(buffer, command, args):
+ ''' Delete a helper from the helper list. '''
+ name = args.strip()
+
+ del config.helpers[name]
+ config.save_helpers()
+ command_helper_list(buffer, command, '')
+ return weechat.WEECHAT_RC_OK
+
+
+def command_helper_rename(buffer, command, args):
+ ''' Rename a helper to a new position. '''
+ old_name, new_name = split_args(args, 2)
+
+ try:
+ config.helpers[new_name] = config.helpers[old_name]
+ del config.helpers[old_name]
+ except KeyError:
+ raise HumanReadableError('No such helper: {0}'.format(old_name))
+ config.save_helpers()
+ command_helper_list(buffer, command, '')
+ return weechat.WEECHAT_RC_OK
+
+
+def command_helper_swap(buffer, command, args):
+ ''' Swap two helpers. '''
+ a, b = split_args(args, 2)
+ try:
+ config.helpers[b], config.helpers[a] = config.helpers[a], config.helpers[b]
+ except KeyError as e:
+ raise HumanReadableError('No such helper: {0}'.format(e.args[0]))
+
+ config.helpers.swap(index_a, index_b)
+ config.save_helpers()
+ command_helper_list(buffer, command, '')
+ return weechat.WEECHAT_RC_OK
+
+def call_command(buffer, command, args, subcommands):
+ ''' Call a subcommand from a dictionary. '''
+ subcommand, tail = pad(args.split(' ', 1), 2, '')
+ subcommand = subcommand.strip()
+ if (subcommand == ''):
+ child = subcommands.get(' ')
+ else:
+ command = command + [subcommand]
+ child = subcommands.get(subcommand)
+
+ if isinstance(child, dict):
+ return call_command(buffer, command, tail, child)
+ elif callable(child):
+ return child(buffer, command, tail)
+
+ log('{0}: command not found'.format(' '.join(command)))
+ return weechat.WEECHAT_RC_ERROR
+
+def on_signal(data, signal, signal_data):
+ global signal_delay_timer
+ global sort_queued
+
+ # If the sort limit timeout is started, we're in the hold-off time after sorting, just queue a sort.
+ if sort_limit_timer is not None:
+ if sort_queued:
+ debug('Signal {0} ignored, sort limit timeout is active and sort is already queued.'.format(signal))
+ else:
+ debug('Signal {0} received but sort limit timeout is active, sort is now queued.'.format(signal))
+ sort_queued = True
+ return weechat.WEECHAT_RC_OK
+
+ # If the signal delay timeout is started, a signal was recently received, so ignore this signal.
+ if signal_delay_timer is not None:
+ debug('Signal {0} ignored, signal delay timeout active.'.format(signal))
+ return weechat.WEECHAT_RC_OK
+
+ # Otherwise, start the signal delay timeout.
+ debug('Signal {0} received, starting signal delay timeout of {1} ms.'.format(signal, config.signal_delay))
+ weechat.hook_timer(config.signal_delay, 0, 1, "on_signal_delay_timeout", "")
+ return weechat.WEECHAT_RC_OK
+
+def on_signal_delay_timeout(pointer, remaining_calls):
+ """ Called when the signal_delay_timer triggers. """
+ global signal_delay_timer
+ global sort_limit_timer
+ global sort_queued
+
+ signal_delay_timer = None
+
+ # If the sort limit timeout was started, we're still in the no-sort period, so just queue a sort.
+ if sort_limit_timer is not None:
+ debug('Signal delay timeout expired, but sort limit timeout is active, sort is now queued.')
+ sort_queued = True
+ return weechat.WEECHAT_RC_OK
+
+ # Time to sort!
+ debug('Signal delay timeout expired, starting sort.')
+ do_sort()
+
+ # Start the sort limit timeout if not disabled.
+ if config.sort_limit > 0:
+ debug('Starting sort limit timeout of {0} ms.'.format(config.sort_limit))
+ sort_limit_timer = weechat.hook_timer(config.sort_limit, 0, 1, "on_sort_limit_timeout", "")
+
+ return weechat.WEECHAT_RC_OK
+
+def on_sort_limit_timeout(pointer, remainin_calls):
+ """ Called when de sort_limit_timer triggers. """
+ global sort_limit_timer
+ global sort_queued
+
+ # If no signal was received during the timeout, we're done.
+ if not sort_queued:
+ debug('Sort limit timeout expired without receiving a signal.')
+ sort_limit_timer = None
+ return weechat.WEECHAT_RC_OK
+
+ # Otherwise it's time to sort.
+ debug('Signal received during sort limit timeout, starting queued sort.')
+ do_sort()
+ sort_queued = False
+
+ # Start the sort limit timeout again if not disabled.
+ if config.sort_limit > 0:
+ debug('Starting sort limit timeout of {0} ms.'.format(config.sort_limit))
+ sort_limit_timer = weechat.hook_timer(config.sort_limit, 0, 1, "on_sort_limit_timeout", "")
+
+ return weechat.WEECHAT_RC_OK
+
+
+def apply_config():
+ # Unhook all signals and hook the new ones.
+ for hook in hooks:
+ weechat.unhook(hook)
+ for signal in config.signals:
+ hooks.append(weechat.hook_signal(signal, 'on_signal', ''))
+
+ if config.sort_on_config:
+ debug('Sorting because configuration changed.')
+ do_sort()
+
+def on_config_changed(*args, **kwargs):
+ ''' Called whenever the configuration changes. '''
+ config.reload()
+ apply_config()
+
+ return weechat.WEECHAT_RC_OK
+
+def parse_arg(args):
+ if not args: return '', None
+
+ result = ''
+ escaped = False
+ for i, c in enumerate(args):
+ if not escaped:
+ if c == '\\':
+ escaped = True
+ continue
+ elif c == ',':
+ return result, args[i+1:]
+ result += c
+ escaped = False
+ return result, None
+
+def parse_args(args, max = None):
+ result = []
+ i = 0
+ while max is None or i < max:
+ i += 1
+ arg, args = parse_arg(args)
+ if arg is None: break
+ result.append(arg)
+ if args is None: break
+ return result, args
+
+def on_info_escape(pointer, name, arguments):
+ result = ''
+ for c in arguments:
+ if c == '\\':
+ result += '\\\\'
+ elif c == ',':
+ result += '\\,'
+ else:
+ result +=c
+ return result
+
+def on_info_replace(pointer, name, arguments):
+ arguments, rest = parse_args(arguments, 3)
+ if rest or len(arguments) < 3:
+ log('usage: ${{info:{0},old,new,text}}'.format(name))
+ return ''
+ old, new, text = arguments
+
+ return text.replace(old, new)
+
+def on_info_order(pointer, name, arguments):
+ arguments, rest = parse_args(arguments)
+ if len(arguments) < 1:
+ log('usage: ${{info:{0},value,first,second,third,...}}'.format(name))
+ return ''
+
+ value = arguments[0]
+ keys = arguments[1:]
+ if not keys: return '0'
+
+ # Find the value in the keys (or '*' if we can't find it)
+ result = list_find(keys, value)
+ if result is None: result = list_find(keys, '*')
+ if result is None: result = len(keys)
+
+ # Pad result with leading zero to make sure string sorting works.
+ width = int(math.log10(len(keys))) + 1
+ return '{0:0{1}}'.format(result, width)
+
+
+def on_autosort_command(data, buffer, args):
+ ''' Called when the autosort command is invoked. '''
+ try:
+ return call_command(buffer, ['/autosort'], args, {
+ ' ': command_sort,
+ 'sort': command_sort,
+ 'debug': command_debug,
+
+ 'rules': {
+ ' ': command_rule_list,
+ 'list': command_rule_list,
+ 'add': command_rule_add,
+ 'insert': command_rule_insert,
+ 'update': command_rule_update,
+ 'delete': command_rule_delete,
+ 'move': command_rule_move,
+ 'swap': command_rule_swap,
+ },
+ 'helpers': {
+ ' ': command_helper_list,
+ 'list': command_helper_list,
+ 'set': command_helper_set,
+ 'delete': command_helper_delete,
+ 'rename': command_helper_rename,
+ 'swap': command_helper_swap,
+ },
+ })
+ except HumanReadableError as e:
+ log(e)
+ return weechat.WEECHAT_RC_ERROR
+
+def add_completions(completion, words):
+ for word in words:
+ weechat.hook_completion_list_add(completion, word, 0, weechat.WEECHAT_LIST_POS_END)
+
+def autosort_complete_rules(words, completion):
+ if len(words) == 0:
+ add_completions(completion, ['add', 'delete', 'insert', 'list', 'move', 'swap', 'update'])
+ if len(words) == 1 and words[0] in ('delete', 'insert', 'move', 'swap', 'update'):
+ add_completions(completion, map(str, range(len(config.rules))))
+ if len(words) == 2 and words[0] in ('move', 'swap'):
+ add_completions(completion, map(str, range(len(config.rules))))
+ if len(words) == 2 and words[0] in ('update'):
+ try:
+ add_completions(completion, [config.rules[int(words[1])]])
+ except KeyError: pass
+ except ValueError: pass
+ else:
+ add_completions(completion, [''])
+ return weechat.WEECHAT_RC_OK
+
+def autosort_complete_helpers(words, completion):
+ if len(words) == 0:
+ add_completions(completion, ['delete', 'list', 'rename', 'set', 'swap'])
+ elif len(words) == 1 and words[0] in ('delete', 'rename', 'set', 'swap'):
+ add_completions(completion, sorted(config.helpers.keys()))
+ elif len(words) == 2 and words[0] == 'swap':
+ add_completions(completion, sorted(config.helpers.keys()))
+ elif len(words) == 2 and words[0] == 'rename':
+ add_completions(completion, sorted(config.helpers.keys()))
+ elif len(words) == 2 and words[0] == 'set':
+ try:
+ add_completions(completion, [config.helpers[words[1]]])
+ except KeyError: pass
+ return weechat.WEECHAT_RC_OK
+
+def on_autosort_complete(data, name, buffer, completion):
+ cmdline = weechat.buffer_get_string(buffer, "input")
+ cursor = weechat.buffer_get_integer(buffer, "input_pos")
+ prefix = cmdline[:cursor]
+ words = prefix.split()[1:]
+
+ # If the current word isn't finished yet,
+ # ignore it for coming up with completion suggestions.
+ if prefix[-1] != ' ': words = words[:-1]
+
+ if len(words) == 0:
+ add_completions(completion, ['debug', 'helpers', 'rules', 'sort'])
+ elif words[0] == 'rules':
+ return autosort_complete_rules(words[1:], completion)
+ elif words[0] == 'helpers':
+ return autosort_complete_helpers(words[1:], completion)
+ return weechat.WEECHAT_RC_OK
+
+command_description = r'''{*white}# General commands{reset}
+
+{*white}/autosort {brown}sort{reset}
+Manually trigger the buffer sorting.
+
+{*white}/autosort {brown}debug{reset}
+Show the evaluation results of the sort rules for each buffer.
+
+
+{*white}# Sorting rule commands{reset}
+
+{*white}/autosort{brown} rules list{reset}
+Print the list of sort rules.
+
+{*white}/autosort {brown}rules add {cyan}<expression>{reset}
+Add a new rule at the end of the list.
+
+{*white}/autosort {brown}rules insert {cyan}<index> <expression>{reset}
+Insert a new rule at the given index in the list.
+
+{*white}/autosort {brown}rules update {cyan}<index> <expression>{reset}
+Update a rule in the list with a new expression.
+
+{*white}/autosort {brown}rules delete {cyan}<index>
+Delete a rule from the list.
+
+{*white}/autosort {brown}rules move {cyan}<index_from> <index_to>{reset}
+Move a rule from one position in the list to another.
+
+{*white}/autosort {brown}rules swap {cyan}<index_a> <index_b>{reset}
+Swap two rules in the list
+
+
+{*white}# Helper variable commands{reset}
+
+{*white}/autosort {brown}helpers list
+Print the list of helper variables.
+
+{*white}/autosort {brown}helpers set {cyan}<name> <expression>
+Add or update a helper variable with the given name.
+
+{*white}/autosort {brown}helpers delete {cyan}<name>
+Delete a helper variable.
+
+{*white}/autosort {brown}helpers rename {cyan}<old_name> <new_name>
+Rename a helper variable.
+
+{*white}/autosort {brown}helpers swap {cyan}<name_a> <name_b>
+Swap the expressions of two helper variables in the list.
+
+
+{*white}# Info hooks{reset}
+Autosort comes with a number of info hooks to add some extra functionality to regular weechat eval strings.
+Info hooks can be used in eval strings in the form of {cyan}${{info:some_hook,arguments}}{reset}.
+
+Commas and backslashes in arguments to autosort info hooks (except for {cyan}${{info:autosort_escape}}{reset}) must be escaped with a backslash.
+
+{*white}${{info:{brown}autosort_replace{white},{cyan}pattern{white},{cyan}replacement{white},{cyan}source{white}}}{reset}
+Replace all occurrences of {cyan}pattern{reset} with {cyan}replacement{reset} in the string {cyan}source{reset}.
+Can be used to ignore certain strings when sorting by replacing them with an empty string.
+
+For example: {cyan}${{info:autosort_replace,cat,dog,the dog is meowing}}{reset} expands to "the cat is meowing".
+
+{*white}${{info:{brown}autosort_order{white},{cyan}value{white},{cyan}option0{white},{cyan}option1{white},{cyan}option2{white},{cyan}...{white}}}
+Generate a zero-padded number that corresponds to the index of {cyan}value{reset} in the list of options.
+If one of the options is the special value {brown}*{reset}, then any value not explicitly mentioned will be sorted at that position.
+Otherwise, any value that does not match an option is assigned the highest number available.
+Can be used to easily sort buffers based on a manual sequence.
+
+For example: {cyan}${{info:autosort_order,${{server}},freenode,oftc,efnet}}{reset} will sort freenode before oftc, followed by efnet and then any remaining servers.
+Alternatively, {cyan}${{info:autosort_order,${{server}},freenode,oftc,*,efnet}}{reset} will sort any unlisted servers after freenode and oftc, but before efnet.
+
+{*white}${{info:{brown}autosort_escape{white},{cyan}text{white}}}{reset}
+Escape commas and backslashes in {cyan}text{reset} by prepending them with a backslash.
+This is mainly useful to pass arbitrary eval strings as arguments to other autosort info hooks.
+Otherwise, an eval string that expands to something with a comma would be interpreted as multiple arguments.
+
+For example, it can be used to safely pass buffer names to {cyan}${{info:autosort_replace}}{reset} like so:
+{cyan}${{info:autosort_replace,##,#,${{info:autosort_escape,${{buffer.name}}}}}}{reset}.
+
+
+{*white}# Description
+Autosort is a weechat script to automatically keep your buffers sorted. The sort
+order can be customized by defining your own sort rules, but the default should
+be sane enough for most people. It can also group IRC channel/private buffers
+under their server buffer if you like.
+
+Autosort uses a stable sorting algorithm, meaning that you can manually move buffers
+to change their relative order, if they sort equal with your rule set.
+
+{*white}# Sort rules{reset}
+Autosort evaluates a list of eval expressions (see {*default}/help eval{reset}) and sorts the
+buffers based on evaluated result. Earlier rules will be considered first. Only
+if earlier rules produced identical results is the result of the next rule
+considered for sorting purposes.
+
+You can debug your sort rules with the `{*default}/autosort debug{reset}` command, which will
+print the evaluation results of each rule for each buffer.
+
+{*brown}NOTE:{reset} The sort rules for version 3 are not compatible with version 2 or vice
+versa. You will have to manually port your old rules to version 3 if you have any.
+
+{*white}# Helper variables{reset}
+You may define helper variables for the main sort rules to keep your rules
+readable. They can be used in the main sort rules as variables. For example,
+a helper variable named `{cyan}foo{reset}` can be accessed in a main rule with the
+string `{cyan}${{foo}}{reset}`.
+
+{*white}# Automatic or manual sorting{reset}
+By default, autosort will automatically sort your buffer list whenever a buffer
+is opened, merged, unmerged or renamed. This should keep your buffers sorted in
+almost all situations. However, you may wish to change the list of signals that
+cause your buffer list to be sorted. Simply edit the `{cyan}autosort.sorting.signals{reset}`
+option to add or remove any signal you like.
+
+If you remove all signals you can still sort your buffers manually with the
+`{*default}/autosort sort{reset}` command. To prevent all automatic sorting, the option
+`{cyan}autosort.sorting.sort_on_config_change{reset}` should also be disabled.
+
+{*white}# Recommended settings
+For the best visual effect, consider setting the following options:
+ {*white}/set {cyan}irc.look.server_buffer{reset} {brown}independent{reset}
+
+This setting allows server buffers to be sorted independently, which is
+needed to create a hierarchical tree view of the server and channel buffers.
+
+If you are using the {*default}buflist{reset} plugin you can (ab)use Unicode to draw a tree
+structure with the following setting (modify to suit your need):
+ {*white}/set {cyan}buflist.format.indent {brown}"${{color:237}}${{if:${{buffer.next_buffer.local_variables.type}}=~^(channel|private)$?├─:└─}}"{reset}
+'''
+
+command_completion = '%(plugin_autosort) %(plugin_autosort) %(plugin_autosort) %(plugin_autosort) %(plugin_autosort)'
+
+info_replace_description = (
+ 'Replace all occurrences of `pattern` with `replacement` in the string `source`. '
+ 'Can be used to ignore certain strings when sorting by replacing them with an empty string. '
+ 'See /help autosort for examples.'
+)
+info_replace_arguments = 'pattern,replacement,source'
+
+info_order_description = (
+ 'Generate a zero-padded number that corresponds to the index of `value` in the list of options. '
+ 'If one of the options is the special value `*`, then any value not explicitly mentioned will be sorted at that position. '
+ 'Otherwise, any value that does not match an option is assigned the highest number available. '
+ 'Can be used to easily sort buffers based on a manual sequence. '
+ 'See /help autosort for examples.'
+)
+info_order_arguments = 'value,first,second,third,...'
+
+info_escape_description = (
+ 'Escape commas and backslashes in `text` by prepending them with a backslash. '
+ 'This is mainly useful to pass arbitrary eval strings as arguments to other autosort info hooks. '
+ 'Otherwise, an eval string that expands to something with a comma would be interpreted as multiple arguments.'
+ 'See /help autosort for examples.'
+)
+info_escape_arguments = 'text'
+
+
+if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, "", ""):
+ config = Config('autosort')
+
+ colors = {
+ 'default': weechat.color('default'),
+ 'reset': weechat.color('reset'),
+ 'black': weechat.color('black'),
+ 'red': weechat.color('red'),
+ 'green': weechat.color('green'),
+ 'brown': weechat.color('brown'),
+ 'yellow': weechat.color('yellow'),
+ 'blue': weechat.color('blue'),
+ 'magenta': weechat.color('magenta'),
+ 'cyan': weechat.color('cyan'),
+ 'white': weechat.color('white'),
+ '*default': weechat.color('*default'),
+ '*black': weechat.color('*black'),
+ '*red': weechat.color('*red'),
+ '*green': weechat.color('*green'),
+ '*brown': weechat.color('*brown'),
+ '*yellow': weechat.color('*yellow'),
+ '*blue': weechat.color('*blue'),
+ '*magenta': weechat.color('*magenta'),
+ '*cyan': weechat.color('*cyan'),
+ '*white': weechat.color('*white'),
+ }
+
+ weechat.hook_config('autosort.*', 'on_config_changed', '')
+ weechat.hook_completion('plugin_autosort', '', 'on_autosort_complete', '')
+ weechat.hook_command('autosort', command_description.format(**colors), '', '', command_completion, 'on_autosort_command', '')
+ weechat.hook_info('autosort_escape', info_escape_description, info_escape_arguments, 'on_info_escape', '')
+ weechat.hook_info('autosort_replace', info_replace_description, info_replace_arguments, 'on_info_replace', '')
+ weechat.hook_info('autosort_order', info_order_description, info_order_arguments, 'on_info_order', '')
+
+ apply_config()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/python/colon_complete.py Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,69 @@
+SCRIPT_NAME='coloncomplete'
+SCRIPT_AUTHOR='Steve Losh <steve@stevelosh.com>'
+SCRIPT_VERSION='1.0'
+SCRIPT_LICENSE='MIT/X11'
+SCRIPT_DESC='Add a colon after nick completion when all the previous words in the input are also nicks.'
+
+EXTRA_NICKS = ['all', 'backend', 'clojerks', 'ops', 'support']
+
+import_ok=True
+
+try:
+ import weechat
+except ImportError:
+ print 'This script must be run under WeeChat'
+ print 'You can obtain a copy of WeeChat, for free, at http://www.weechat.org'
+ import_ok=False
+
+weechat_version=0
+
+def get_nicks(buffer, prefix=''):
+ channel = weechat.buffer_get_string(buffer, 'localvar_channel')
+ server = weechat.buffer_get_string(buffer, 'localvar_server')
+ prefix = prefix.lower()
+
+ matches = []
+
+ infolist = weechat.infolist_get('irc_nick', '', '%s,%s' % (server, channel))
+ while weechat.infolist_next(infolist):
+ nick = weechat.infolist_string(infolist, 'name')
+ if nick != 'localhost' and nick.lower().startswith(prefix):
+ matches.append(nick)
+ weechat.infolist_free(infolist)
+
+ for nick in EXTRA_NICKS:
+ if nick.lower().startswith(prefix):
+ matches.append(nick)
+
+ return matches
+
+def completer(data, buffer, command):
+ cb = weechat.current_buffer()
+ if command == "/input complete_next":
+ line = weechat.buffer_get_string(cb, "input")
+ words = line.split(' ')
+ prefix = words[-1]
+ if prefix and words and all([s.endswith(':') for s in words[:-1] if s]):
+ nicks = get_nicks(cb, prefix)
+ if len(nicks) == 1:
+ for _ in range(len(prefix)):
+ weechat.command(buffer, "/input delete_previous_char")
+ weechat.command(buffer, "/input insert " + nicks[-1] + ":\\x20")
+ elif len(nicks) > 1:
+ l = min(len(nick) for nick in nicks)
+ for i in range(len(prefix), l):
+ if len(set(nick[i] for nick in nicks)) > 1:
+ break
+ else:
+ weechat.command(buffer, "/input insert " + nicks[0][i])
+
+ for nick in nicks:
+ weechat.prnt(cb, "==> " + nick)
+ return weechat.WEECHAT_RC_OK_EAT
+
+ return weechat.WEECHAT_RC_OK
+
+if __name__ == "__main__" and import_ok:
+ if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, "", ""):
+ weechat_version = weechat.info_get("version_number", "") or 0
+ weechat.hook_command_run('/input complete*', 'completer', '')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/python/listbuffer.py Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,467 @@
+# -*- coding: utf-8 -*-
+#
+# ListBuffer, version 0.8.1 for WeeChat version 0.3
+# Latest development version: https://github.com/FiXato/listbuffer
+#
+# Show /list results in a common buffer and interact with them.
+#
+# This script allows you to easily join channels from the /list output.
+# It will open a common buffer for the /list result, through which you
+# browse with your cursor keys, and join with the meta-enter keys.
+# Adjust sorting with meta->, meta-< and meta-/ keybindings.
+#
+## History:
+### 2011-09-08: FiXato:
+#
+# * version 0.1: initial release.
+# * added a common buffer for /list results
+# * added highlighting for currently selected line
+# * added /join support via enter key
+# * added scroll_top and scroll_bottom support
+#
+# * version 0.2: /list format bugfix
+# * added support for /list results without modes
+# * some servers don't send 321 (/list start). Taken into account.
+#
+# * version 0.3: Sorting support
+# * Added some basic sorting support. Scroll through sort options
+# with meta-> and meta-< (users, channel, topic, modes)
+#
+### 2011-09-19: FiXato
+#
+# * version 0.4:
+# * Case-insensitive buffer lookup fix.
+# * Removed default enter keybind
+#
+### 2011-12-28: troydm:
+#
+# * version 0.5: It's an upside-down-world
+# * Added inverted sorting support provided by Dmitry "troydm" Geurkov
+# Use meta-/ to switch between inverted and regular sorting.
+#
+### 2012-02-10: FiXato:
+#
+# * version 0.6: Stop shoving that buffer in my face!
+# * The listbuffer should no longer pop up by itself when you load the script.
+# It should only pop up now when you actually do a /list query.
+#
+# * version 0.7: .. but please pop it up in my current window when I ask for it
+# * Added setting plugins.var.python.listbuffer.autofocus
+# This will autofocus the listbuffer in the current window if another window isn't
+# already showing it, and of course only when the user issues /list
+#
+### 2012-07-10: FiXato:
+#
+# * version 0.7.1: Forgetful bugfix
+# * Made sure lb_curline global variable is defined
+#
+### 2013-03-19: FiXato:
+#
+# * version 0.8: Sorted out the sorting
+# * Added automatically updating options for sorting:
+# * plugins.var.python.listbuffer.sort_inverted
+# * plugins.var.python.listbuffer.sort_order
+# * version 0.8.1: Pad it baby!
+# * Channel modes are equally padded even when there are no channel modes.
+# * Added padding options:
+# * plugins.var.python.listbuffer.modes_min_width
+# * plugins.var.python.listbuffer.channel_min_width
+# * plugins.var.python.listbuffer.users_min_width
+#
+## Acknowledgements:
+# * Dmitry "troydm" Geurkov, for providing the inverse-sorting patch to the project.
+# * Sebastien "Flashcode" Helleu, for developing the kick-ass IRC client WeeChat
+# and the iset.pl script which inspired me to this script.
+# * Nils "nils_2" Görs, for his contributions to iset.pl which served as
+# example code.
+# * David "drubin" Rubin, for his urlgrab.py script, which also served
+# as example code.
+# * ArZa, whose listsort.pl script helped me getting started with
+# grabbing the /list results. Parts of his code have been shamelessly
+# copied and ported to Python.
+# * Khaled Mardam-Bey, for making me yearn for similar /list support in
+# WeeChat as mIRC already offered. :P
+# * mave_, for pointing out that sort orders weren't remembered.
+#
+## TODO:
+# - Auto-scroll selected line upon window scroll.
+# - Add option to hide already joined channels.
+# - Improve sorting methods
+# - Add auto-join support
+# - Detect if channel is already in auto-join
+# - Allow automatically switching to the listbuffer
+# - Add support for ALIS (/squery alis LIST * -mix 100 (IRCNet)
+# - Make colours configurable
+# - Limit number of channels to parse
+# - Add filter support a la iset
+# - Allow selecting multiple channels
+# - Add optional command redirection.
+#
+## Copyright (c) 2011,2012,2013 Filip H.F. "FiXato" Slagter,
+# <FiXato [at] Gmail [dot] com>
+# http://profile.fixato.org
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+SCRIPT_NAME = "listbuffer"
+SCRIPT_AUTHOR = "Filip H.F. 'FiXato' Slagter <fixato [at] gmail [dot] com>"
+SCRIPT_VERSION = "0.8.1"
+SCRIPT_LICENSE = "MIT"
+SCRIPT_DESC = "A common buffer for /list output."
+SCRIPT_COMMAND = "listbuffer"
+
+import_ok = True
+
+try:
+ import weechat
+except ImportError:
+ print "This script must be run under WeeChat."
+ import_ok = False
+
+import re
+
+lb_settings = (
+ ("autofocus", "on", "Focus the listbuffer in the current window if it isn't already displayed by a window."),
+ ("sort_order", "users", "Last used sort order for the channel list."),
+ ("sort_inverted", "on", "Invert the sort order for the channel list."),
+ ("modes_min_width", "8", "The minimum width used for modes in the channel list. If a channel has less modes than this amount, the column will be padded with spaces."),
+ ("channel_min_width", "25", "The minimum width used for the channel name in the channel list. If a channelname is shorter than this amount, the column will be padded with spaces."),
+ ("users_min_width", "8", "The minimum width used for the usercount in the channel list. If the usercount has less digits than this amount, the column will be padded with spaces."),
+)
+lb_buffer = None
+lb_curline = 0
+lb_channels = []
+lb_network = None
+lb_list_started = False
+lb_current_sort = None
+lb_sort_inverted = False
+lb_sort_options = (
+ 'channel',
+ 'users',
+ 'modes',
+ 'topic',
+)
+
+# server numeric Nick Chan Users Modes Topic
+lb_channel_list_expression = '(:\S+) (\d{3}) (\S+) (\S+) (\d+) :(\[(.*?)\] )?(.*)'
+
+# Create listbuffer.
+def lb_create_buffer():
+ global lb_buffer, lb_curline
+
+ if not lb_buffer:
+ lb_buffer = weechat.buffer_new("listbuffer", "lb_input_cb", \
+ "", "lb_close_cb", "")
+ lb_set_buffer_title()
+ # Sets notify to 0 as this buffer does not need to be in hotlist.
+ weechat.buffer_set(lb_buffer, "notify", "0")
+ weechat.buffer_set(lb_buffer, "nicklist", "0")
+ weechat.buffer_set(lb_buffer, "type", "free")
+ weechat.buffer_set(lb_buffer, "key_bind_ctrl-L", "/listbuffer **refresh")
+ weechat.buffer_set(lb_buffer, "key_bind_meta2-A", "/listbuffer **up")
+ weechat.buffer_set(lb_buffer, "key_bind_meta2-B", "/listbuffer **down")
+ weechat.buffer_set(lb_buffer, "key_bind_meta2-1~", "/listbuffer **scroll_top")
+ weechat.buffer_set(lb_buffer, "key_bind_meta2-4~", "/listbuffer **scroll_bottom")
+ weechat.buffer_set(lb_buffer, "key_bind_meta-ctrl-J", "/listbuffer **enter")
+ weechat.buffer_set(lb_buffer, "key_bind_meta-ctrl-M", "/listbuffer **enter")
+ weechat.buffer_set(lb_buffer, "key_bind_meta->", "/listbuffer **sort_next")
+ weechat.buffer_set(lb_buffer, "key_bind_meta-<", "/listbuffer **sort_previous")
+ weechat.buffer_set(lb_buffer, "key_bind_meta-/", "/listbuffer **sort_invert")
+ lb_curline = 0
+ if weechat.config_get_plugin("autofocus") == "on":
+ if not weechat.window_search_with_buffer(lb_buffer):
+ weechat.command("", "/buffer " + weechat.buffer_get_string(lb_buffer,"name"))
+
+def lb_set_buffer_title():
+ global lb_buffer, lb_current_sort
+ ascdesc = '(v)' if lb_sort_inverted else '(^)'
+ weechat.buffer_set(lb_buffer, "title", lb_line_format({
+ 'channel': 'Channel name%s' % (ascdesc if lb_current_sort == 'channel' else ''),
+ 'users': 'Users%s' % (ascdesc if lb_current_sort == 'users' else ''),
+ 'modes': 'Modes%s' % (ascdesc if lb_current_sort == 'modes' else ''),
+ 'topic': 'Topic%s' % (ascdesc if lb_current_sort == 'topic' else ''),
+ 'nomodes': None,
+ }))
+
+def lb_list_start(data, signal, message):
+ lb_initialise_list
+
+ return weechat.WEECHAT_RC_OK
+
+def lb_initialise_list(signal):
+ global lb_channels, lb_network, lb_list_started
+
+ lb_create_buffer()
+ lb_channels = []
+ lb_network = signal.split(',')[0]
+ lb_list_started = True
+ return
+
+
+def lb_list_chan(data, signal, message):
+ global lb_channels, lb_buffer, lb_list_started
+
+ # Work-around for IRCds which don't send 321 Numeric (/List start)
+ if not lb_list_started:
+ lb_initialise_list(signal)
+
+ for chan_data in re.findall(lb_channel_list_expression,message):
+ lb_channels.append({
+ 'server': chan_data[0][1:-1],
+ 'numeric': chan_data[1],
+ 'nick': chan_data[2],
+ 'channel': chan_data[3],
+ 'users': chan_data[4],
+ 'nomodes': chan_data[5] == '',
+ 'modes': chan_data[6],
+ 'topic': weechat.hook_modifier_exec("irc_color_decode", "1", chan_data[7])
+ })
+ return weechat.WEECHAT_RC_OK
+
+def lb_list_end(data, signal, message):
+ global lb_list_started
+
+ # Work-around for IRCds which don't send 321 Numeric (/List start)
+ if not lb_list_started:
+ lb_initialise_list(signal)
+
+ lb_list_started = False
+ if lb_current_sort:
+ lb_sort()
+ lb_refresh()
+ return weechat.WEECHAT_RC_OK
+
+def keyEvent (data, buffer, args):
+ global lb_options
+ lb_options[args]()
+
+def lb_input_cb(data, buffer, input_data):
+ global lb_options, lb_curline
+ lb_options[input_data]()
+ return weechat.WEECHAT_RC_OK
+
+def lb_refresh():
+ global lb_channels, lb_buffer
+ weechat.buffer_clear(lb_buffer)
+
+ y = 0
+ for list_data in lb_channels:
+ lb_refresh_line(y)
+ y += 1
+ return
+
+def lb_refresh_line(y):
+ global lb_buffer, lb_curline, lb_channels
+ if y >= 0 and y < len(lb_channels):
+ formatted_line = lb_line_format(lb_channels[y], y == lb_curline)
+ weechat.prnt_y(lb_buffer, y, formatted_line)
+
+def lb_refresh_curline():
+ global lb_curline
+ lb_refresh_line(lb_curline-1)
+ lb_refresh_line(lb_curline)
+ lb_refresh_line(lb_curline+1)
+ return
+
+def lb_line_format(list_data,curr=False):
+ str = ""
+ if (curr):
+ str += weechat.color("yellow,red")
+ channel_text = list_data['channel'].ljust(int(weechat.config_get_plugin('channel_min_width')))
+ users_text = "(%s)" % list_data['users']
+ padded_users_text = users_text.rjust(int(weechat.config_get_plugin('users_min_width')) + 2)
+ str += "%s%s %s " % (weechat.color("bold"), channel_text, padded_users_text)
+ if not list_data['nomodes']:
+ modes = "[%s]" % list_data['modes']
+ else:
+ modes = "[]"
+ str += "%s: " % modes.rjust(int(weechat.config_get_plugin('modes_min_width')) + 2)
+ str += "%s" % list_data['topic']
+ return str
+
+def lb_line_up():
+ global lb_curline
+ if lb_curline <= 0:
+ return
+ lb_curline -= 1
+ lb_refresh_curline()
+ lb_check_outside_window()
+ return
+
+def lb_line_down():
+ global lb_curline, lb_channels
+ if lb_curline+1 >= len(lb_channels):
+ return
+ lb_curline += 1
+ lb_refresh_curline()
+ lb_check_outside_window()
+ return
+
+def lb_line_run():
+ global lb_channels, lb_curline, lb_network
+ buff = weechat.info_get("irc_buffer", lb_network)
+ channel = lb_channels[lb_curline]['channel']
+ command = "/join %s" % channel
+ weechat.command(buff, command)
+ return
+
+def lb_line_select():
+ return
+
+def lb_scroll_top():
+ global lb_curline
+ old_y = lb_curline
+ lb_curline = 0
+ lb_refresh_curline()
+ lb_refresh_line(old_y)
+ weechat.command(lb_buffer, "/window scroll_top")
+ return
+
+def lb_scroll_bottom():
+ global lb_curline, lb_channels
+ old_y = lb_curline
+ lb_curline = len(lb_channels)-1
+ lb_refresh_curline()
+ lb_refresh_line(old_y)
+ weechat.command(lb_buffer, "/window scroll_bottom")
+ return
+
+def lb_check_outside_window():
+ global lb_buffer, lb_curline
+ if (lb_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 > lb_curline):
+ weechat.command(lb_buffer, "/window scroll -%i" %(start_line_y - lb_curline))
+ elif(start_line_y <= lb_curline - chat_height):
+ weechat.command(lb_buffer, "/window scroll +%i"%(lb_curline - start_line_y - chat_height + 1))
+ weechat.infolist_free(infolist)
+
+def lb_sort_next():
+ global lb_current_sort, lb_sort_options
+ if lb_current_sort:
+ new_index = lb_sort_options.index(lb_current_sort) + 1
+ else:
+ new_index = 0
+
+ if len(lb_sort_options) <= new_index:
+ new_index = 0
+
+ lb_set_current_sort_order(lb_sort_options[new_index])
+ lb_sort()
+
+def lb_set_current_sort_order(value):
+ global lb_current_sort
+ lb_current_sort = value
+ weechat.config_set_plugin('sort_order', lb_current_sort)
+
+def lb_set_invert_sort_order(value):
+ global lb_sort_inverted
+ lb_sort_inverted = value
+ weechat.config_set_plugin('sort_inverted', ('on' if lb_sort_inverted else 'off'))
+
+def lb_sort_previous():
+ global lb_current_sort, lb_sort_options
+ if lb_current_sort:
+ new_index = lb_sort_options.index(lb_current_sort) - 1
+ else:
+ new_index = 0
+
+ if new_index < 0:
+ new_index = len(lb_sort_options) - 1
+
+ lb_set_current_sort_order(lb_sort_options[new_index])
+ lb_sort()
+
+def lb_sort(sort_key=None):
+ global lb_channels, lb_current_sort, lb_sort_inverted
+ if sort_key:
+ lb_set_current_sort_order(sort_key)
+ if lb_current_sort == 'users':
+ lb_channels = sorted(lb_channels, key=lambda chan_data: int(chan_data[lb_current_sort]))
+ else:
+ lb_channels = sorted(lb_channels, key=lambda chan_data: chan_data[lb_current_sort])
+ if lb_sort_inverted:
+ lb_channels.reverse()
+ lb_set_buffer_title()
+ lb_refresh()
+
+def lb_sort_invert():
+ global lb_current_sort, lb_sort_inverted
+ if lb_current_sort:
+ lb_set_invert_sort_order(not lb_sort_inverted)
+ lb_sort()
+
+def lb_close_cb(*kwargs):
+ """ A callback for buffer closing. """
+ global lb_buffer
+
+ lb_buffer = None
+ return weechat.WEECHAT_RC_OK
+
+lb_options = {
+ 'refresh' : lb_refresh,
+ 'up' : lb_line_up,
+ 'down' : lb_line_down,
+ 'enter' : lb_line_run,
+ 'space' : lb_line_select,
+ 'scroll_top' : lb_scroll_top,
+ 'scroll_bottom': lb_scroll_bottom,
+ 'sort_next' : lb_sort_next,
+ 'sort_previous': lb_sort_previous,
+ 'sort_invert': lb_sort_invert
+}
+
+def lb_command_main(data, buffer, args):
+ if args[0:2] == "**":
+ keyEvent(data, buffer, args[2:])
+ return weechat.WEECHAT_RC_OK
+
+def lb_set_default_settings():
+ global lb_settings
+ # Set default settings
+ for option, default_value, description in lb_settings:
+ if not weechat.config_is_set_plugin(option):
+ weechat.config_set_plugin(option, default_value)
+ version = weechat.info_get("version_number", "") or 0
+ if int(version) >= 0x00030500:
+ weechat.config_set_desc_plugin(option, description)
+
+def lb_reset_stored_sort_order():
+ global lb_current_sort, lb_sort_inverted
+ lb_current_sort = weechat.config_get_plugin('sort_order')
+ lb_sort_inverted = (True if weechat.config_get_plugin('sort_inverted') == 'on' else False)
+
+if __name__ == "__main__" and import_ok:
+ if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION,
+ SCRIPT_LICENSE, SCRIPT_DESC, "lb_close_cb", ""):
+ lb_set_default_settings()
+ lb_reset_stored_sort_order()
+ lb_buffer = weechat.buffer_search("python", "listbuffer")
+
+ weechat.hook_signal("*,irc_in_321", "lb_list_start", "")
+ weechat.hook_signal("*,irc_in_322", "lb_list_chan", "")
+ weechat.hook_signal("*,irc_in_323", "lb_list_end", "")
+ weechat.hook_command(SCRIPT_COMMAND,
+ "List Buffer",
+ "", "", "",
+ "lb_command_main", "")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/python/sanitize_jira.py Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,33 @@
+import re, weechat, subprocess
+
+SCRIPT_NAME = 'sanitize_jira'
+SCRIPT_AUTHOR = 'Steve Losh <steve@stevelosh.com>'
+SCRIPT_VERSION = '0.0.1'
+SCRIPT_LICENSE = 'MIT'
+SCRIPT_DESC = 'clean up the garbage jirabot sends to channels into something readable'
+
+weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, '', '')
+
+weechat.hook_line('*', '', 'nick_Jira_Cloud', 'sanitize_jira', '')
+
+first_line_re = re.compile(
+ r'(?P<link>https://[^/]+/browse/[^?]+)[?]atlOrigin=[^ ]+ [(](?P<title>.+)[)]'
+)
+
+detail_line_re = re.compile(
+ r'''Status: \x1a\x01[*](?P<status>[^*]+)[*]\x1b\x01.*Type: \x1a\x01[*](?P<type>[^*]+)[*]\x1b\x01.*Assignee: \x1a\x01[*](?P<assignee>[^*]+)[*]\x1b\x01.*Priority: \x1a\x01[*](?P<priority>[^*]+)[*]\x1b\x01'''
+)
+
+def sanitize_jira(data, line):
+ if 'sign up for an Atlassian account to view this link' in line['message']:
+ return {'message': ' '}
+
+ m = first_line_re.search(line['message'])
+ if m:
+ return {'message': '%s | %s' % (m.group('title'), m.group('link'))}
+
+ m = detail_line_re.search(line['message'])
+ if m:
+ return {'message': '%s / %s / %s' % (m.group('type'), m.group('status'), m.group('assignee'))}
+
+ return {}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/relay.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,59 @@
+#
+# weechat -- relay.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use commands like /set or /fset to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/quickstart/
+#
+
+[look]
+auto_open_buffer = on
+raw_messages = 256
+
+[color]
+client = cyan
+status_active = lightblue
+status_auth_failed = lightred
+status_connecting = yellow
+status_disconnected = lightred
+status_waiting_auth = brown
+text = default
+text_bg = default
+text_selected = white
+
+[network]
+allow_empty_password = off
+allowed_ips = ""
+auth_timeout = 60
+bind_address = ""
+clients_purge_delay = 0
+compression = 20
+ipv6 = on
+max_clients = 5
+nonce_size = 16
+password = ""
+password_hash_algo = "*"
+password_hash_iterations = 100000
+ssl_cert_key = "%h/ssl/relay.pem"
+ssl_priorities = "NORMAL:-VERS-SSL3.0"
+totp_secret = ""
+totp_window = 0
+websocket_allowed_origins = ""
+
+[irc]
+backlog_max_minutes = 1440
+backlog_max_number = 256
+backlog_since_last_disconnect = on
+backlog_since_last_message = off
+backlog_tags = "irc_privmsg"
+backlog_time_format = "[%H:%M] "
+
+[weechat]
+commands = ""
+
+[port]
+
+[path]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/rmodifier.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,11 @@
+#
+# rmodifier.conf -- weechat v0.4.3
+#
+
+[look]
+hide_char = "*"
+
+[modifier]
+nickserv = "history_add,input_text_display;^(/(msg|quote) +nickserv +(identify|ghost \S+) +)(.*);1,4*"
+oper = "history_add,input_text_display;^(/oper +\S+ +)(.*);1,2*"
+set_pass = "history_add;^(/set +\S*password\S* +)(.*);1,2*"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/ruby.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,14 @@
+#
+# weechat -- ruby.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use commands like /set or /fset to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/quickstart
+#
+
+[look]
+check_license = off
+eval_keep_context = on
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/script.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,57 @@
+#
+# weechat -- script.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use commands like /set or /fset to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/quickstart/
+#
+
+[look]
+columns = "%s %n %V %v %u | %d | %t"
+diff_color = on
+diff_command = "auto"
+display_source = on
+quiet_actions = on
+sort = "p,n"
+translate_description = on
+use_keys = on
+
+[color]
+status_autoloaded = cyan
+status_held = white
+status_installed = lightcyan
+status_obsolete = lightmagenta
+status_popular = yellow
+status_running = lightgreen
+status_unknown = lightred
+text = default
+text_bg = default
+text_bg_selected = red
+text_date = default
+text_date_selected = white
+text_delimiters = darkgray
+text_description = default
+text_description_selected = white
+text_extension = default
+text_extension_selected = white
+text_name = cyan
+text_name_selected = lightcyan
+text_selected = white
+text_tags = brown
+text_tags_selected = yellow
+text_version = magenta
+text_version_loaded = default
+text_version_loaded_selected = white
+text_version_selected = lightmagenta
+
+[scripts]
+autoload = on
+cache_expire = 60
+download_enabled = on
+download_timeout = 30
+hold = ""
+path = "%h/script"
+url = "http://www.weechat.org/files/plugins.xml.gz"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/spell.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,33 @@
+#
+# weechat -- spell.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use commands like /set or /fset to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/quickstart
+#
+
+[color]
+misspelled = lightred
+suggestion = default
+suggestion_delimiter_dict = cyan
+suggestion_delimiter_word = cyan
+
+[check]
+commands = "away,command,cycle,kick,kickban,me,msg,notice,part,query,quit,topic"
+default_dict = ""
+during_search = off
+enabled = off
+real_time = off
+suggestions = -1
+word_min_length = 2
+
+[dict]
+
+[look]
+suggestion_delimiter_dict = " / "
+suggestion_delimiter_word = ","
+
+[option]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/tcl.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,14 @@
+#
+# weechat -- tcl.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use /set or similar command to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/quickstart
+#
+
+[look]
+check_license = off
+eval_keep_context = on
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/trigger.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,83 @@
+#
+# weechat -- trigger.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use commands like /set or /fset to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/quickstart/
+#
+
+[look]
+enabled = on
+monitor_strip_colors = off
+
+[color]
+flag_command = lightgreen
+flag_conditions = yellow
+flag_post_action = lightblue
+flag_regex = lightcyan
+flag_return_code = lightmagenta
+regex = white
+replace = cyan
+trigger = green
+trigger_disabled = red
+
+[trigger]
+beep.arguments = ""
+beep.command = "/print -beep"
+beep.conditions = "${tg_highlight} || ${tg_msg_pv}"
+beep.enabled = on
+beep.hook = print
+beep.post_action = none
+beep.regex = ""
+beep.return_code = ok
+cmd_pass.arguments = "5000|input_text_display;5000|history_add;5000|irc_command_auth"
+cmd_pass.command = ""
+cmd_pass.conditions = ""
+cmd_pass.enabled = on
+cmd_pass.hook = modifier
+cmd_pass.post_action = none
+cmd_pass.regex = "==^((/(msg|quote) +nickserv +(id|identify|register|ghost +[^ ]+|release +[^ ]+|regain +[^ ]+) +)|/oper +[^ ]+ +|/quote +pass +|/set +[^ ]*password[^ ]* +|/secure +(passphrase|decrypt|set +[^ ]+) +)(.*)==$1$.*+"
+cmd_pass.return_code = ok
+dumbass_buffer.arguments = "4000|input_text_for_buffer;4000|history_add"
+dumbass_buffer.command = ""
+dumbass_buffer.conditions = ""
+dumbass_buffer.enabled = on
+dumbass_buffer.hook = modifier
+dumbass_buffer.post_action = none
+dumbass_buffer.regex = "==^ +/?b (.+)==/b ${re:1}"
+dumbass_buffer.return_code = ok
+idiot_buffer.arguments = "4000|input_text_for_buffer;4000|history_add"
+idiot_buffer.command = ""
+idiot_buffer.conditions = ""
+idiot_buffer.enabled = on
+idiot_buffer.hook = modifier
+idiot_buffer.post_action = none
+idiot_buffer.regex = "==^b (.+)==/b ${re:1}"
+idiot_buffer.return_code = ok
+msg_auth.arguments = "5000|irc_message_auth"
+msg_auth.command = ""
+msg_auth.conditions = ""
+msg_auth.enabled = on
+msg_auth.hook = modifier
+msg_auth.post_action = none
+msg_auth.regex = "==^(.*(id|identify|register|ghost +[^ ]+|release +[^ ]+) +)(.*)==$1$.*+"
+msg_auth.return_code = ok
+server_pass.arguments = "5000|input_text_display;5000|history_add"
+server_pass.command = ""
+server_pass.conditions = ""
+server_pass.enabled = on
+server_pass.hook = modifier
+server_pass.post_action = none
+server_pass.regex = "==^(/(server|connect) .*-(sasl_)?password=)([^ ]+)(.*)==$1$.*4$5"
+server_pass.return_code = ok
+uncc.arguments = "weechat_print"
+uncc.command = ""
+uncc.conditions = "${tg_tag_nick}"
+uncc.enabled = on
+uncc.hook = modifier
+uncc.post_action = none
+uncc.regex = "== \[cc: [^ ]+\]===="
+uncc.return_code = ok
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/typing.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,19 @@
+#
+# weechat -- typing.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use commands like /set or /fset to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/quickstart/
+#
+
+[look]
+delay_purge_paused = 30
+delay_purge_typing = 6
+delay_set_paused = 10
+enabled_nicks = off
+enabled_self = off
+input_min_chars = 4
+item_max_length = 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/urlgrab.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,29 @@
+#
+# weechat -- urlgrab.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use commands like /set or /fset to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/quickstart/
+#
+
+[color]
+color_bg_selected = green
+color_buffer = red
+color_buffer_selected = red
+color_time = cyan
+color_time_selected = cyan
+color_url = blue
+color_url_selected = blue
+
+[default]
+copycmd = "xsel -i"
+historysize = 20
+localcmd = "xdg-open %s"
+method = "local"
+output_main_buffer = off
+remotecmd = "ssh -x localhost -i ~/.ssh/id_rsa -C "export DISPLAY=":0.0" && firefox %s""
+time_format = "%H:%M:%S"
+url_log = "~/.weechat/urls.log"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/weechat.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,693 @@
+#
+# WeeChat -- weechat.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use commands like /set or /fset to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/quickstart
+#
+
+[debug]
+
+[startup]
+command_after_plugins = ""
+command_before_plugins = ""
+display_logo = on
+display_version = on
+sys_rlimit = ""
+
+[look]
+align_end_of_lines = message
+align_multiline_words = on
+bar_more_down = "++"
+bar_more_left = "<<"
+bar_more_right = ">>"
+bar_more_up = "--"
+bare_display_exit_on_input = on
+bare_display_time_format = "%H:%M"
+buffer_auto_renumber = on
+buffer_notify_default = all
+buffer_position = end
+buffer_search_case_sensitive = off
+buffer_search_force_default = off
+buffer_search_regex = off
+buffer_search_where = prefix_message
+buffer_time_format = "%H:%M"
+buffer_time_same = ""
+color_basic_force_bold = off
+color_inactive_buffer = off
+color_inactive_message = on
+color_inactive_prefix = on
+color_inactive_prefix_buffer = on
+color_inactive_time = off
+color_inactive_window = off
+color_nick_offline = off
+color_pairs_auto_reset = 5
+color_real_white = off
+command_chars = ""
+command_incomplete = off
+confirm_quit = off
+confirm_upgrade = off
+day_change = on
+day_change_message_1date = "-- %a, %d %b %Y --"
+day_change_message_2dates = "-- %%a, %%d %%b %%Y (%a, %d %b %Y) --"
+eat_newline_glitch = off
+emphasized_attributes = ""
+highlight = "sjl,slosh,slj,horrifying,steve.losh,@steve.losh,stevelosh"
+highlight_regex = "(steve losh|rob ford|(jesus )?fucking christ|(horse|mouse|clown)fuckers?|((mother)?fuck([ie]ng?|er)?|(god?)?damn(ed)?|dammit|(bull|horse)?shite?){3,})"
+highlight_tags = ""
+hotlist_add_conditions = "${away} || ${buffer.num_displayed} == 0"
+hotlist_buffer_separator = ", "
+hotlist_count_max = 2
+hotlist_count_min_msg = 2
+hotlist_names_count = 3
+hotlist_names_length = 0
+hotlist_names_level = 12
+hotlist_names_merged_buffers = off
+hotlist_prefix = "H: "
+hotlist_remove = merged
+hotlist_short_names = on
+hotlist_sort = group_time_asc
+hotlist_suffix = ""
+hotlist_unique_numbers = on
+hotlist_update_on_buffer_switch = on
+input_cursor_scroll = 20
+input_share = none
+input_share_overwrite = off
+input_undo_max = 32
+item_away_message = on
+item_buffer_filter = "*"
+item_buffer_zoom = "!"
+item_mouse_status = "M"
+item_time_format = "%H:%M"
+jump_current_to_previous_buffer = on
+jump_previous_buffer_when_closing = on
+jump_smart_back_to_buffer = on
+key_bind_safe = on
+key_grab_delay = 800
+mouse = off
+mouse_timer_delay = 100
+nick_color_force = ""
+nick_color_hash = djb2
+nick_color_hash_salt = ""
+nick_color_stop_chars = "_|["
+nick_prefix = ""
+nick_suffix = ""
+paste_auto_add_newline = on
+paste_bracketed = off
+paste_bracketed_timer_delay = 10
+paste_max_lines = 3
+prefix_action = " *"
+prefix_align = right
+prefix_align_max = 15
+prefix_align_min = 0
+prefix_align_more = "+"
+prefix_align_more_after = on
+prefix_buffer_align = right
+prefix_buffer_align_max = 0
+prefix_buffer_align_more = "+"
+prefix_buffer_align_more_after = on
+prefix_error = "=!="
+prefix_join = "✔"
+prefix_network = "--"
+prefix_quit = "✘"
+prefix_same_nick = ""
+prefix_same_nick_middle = ""
+prefix_suffix = "|"
+quote_nick_prefix = "<"
+quote_nick_suffix = ">"
+quote_time_format = "%H:%M:%S"
+read_marker = line
+read_marker_always_show = on
+read_marker_string = "─"
+read_marker_update_on_buffer_switch = on
+save_config_on_exit = off
+save_config_with_fsync = off
+save_layout_on_exit = all
+scroll_amount = 3
+scroll_bottom_after_switch = off
+scroll_page_percent = 100
+search_text_not_found_alert = on
+separator_horizontal = "-"
+separator_vertical = ""
+tab_width = 1
+time_format = "%a, %d %b %Y %T"
+window_auto_zoom = off
+window_separator_horizontal = on
+window_separator_vertical = on
+window_title = ""
+word_chars_highlight = "!\u00A0,-,_,|,@,.,alnum"
+word_chars_input = "!\u00A0,-,_,|,alnum"
+
+[palette]
+
+[color]
+bar_more = magenta
+chat = default
+chat_bg = default
+chat_buffer = white
+chat_channel = white
+chat_day_change = cyan
+chat_delimiters = green
+chat_highlight = 207
+chat_highlight_bg = default
+chat_host = cyan
+chat_inactive_buffer = darkgray
+chat_inactive_window = darkgray
+chat_nick = lightcyan
+chat_nick_colors = "027,048,068,028,081,082,099,112,129,136,169,178,208,226,113,196,161,23,59,222"
+chat_nick_offline = darkgray
+chat_nick_offline_highlight = default
+chat_nick_offline_highlight_bg = darkgray
+chat_nick_other = cyan
+chat_nick_prefix = green
+chat_nick_self = white
+chat_nick_suffix = green
+chat_prefix_action = white
+chat_prefix_buffer = brown
+chat_prefix_buffer_inactive_buffer = darkgray
+chat_prefix_error = yellow
+chat_prefix_join = lightgreen
+chat_prefix_more = lightmagenta
+chat_prefix_network = magenta
+chat_prefix_quit = lightred
+chat_prefix_suffix = green
+chat_read_marker = green
+chat_read_marker_bg = default
+chat_server = brown
+chat_tags = red
+chat_text_found = yellow
+chat_text_found_bg = lightmagenta
+chat_time = 238
+chat_time_delimiters = 236
+chat_value = cyan
+chat_value_null = blue
+emphasized = yellow
+emphasized_bg = magenta
+input_actions = lightgreen
+input_text_not_found = red
+item_away = yellow
+nicklist_away = cyan
+nicklist_group = green
+separator = green
+status_count_highlight = magenta
+status_count_msg = brown
+status_count_other = 16
+status_count_private = green
+status_data_highlight = lightmagenta
+status_data_msg = yellow
+status_data_other = 16
+status_data_private = lightgreen
+status_filter = green
+status_more = 16
+status_mouse = green
+status_name = *16
+status_name_ssl = *16
+status_nicklist_count = default
+status_number = 16
+status_time = *16
+
+[completion]
+base_word_until_cursor = on
+command_inline = on
+default_template = "%(nicks)|%(irc_channels)"
+nick_add_space = on
+nick_case_sensitive = off
+nick_completer = ":"
+nick_first_only = off
+nick_ignore_chars = "[]`_-^"
+partial_completion_alert = on
+partial_completion_command = off
+partial_completion_command_arg = off
+partial_completion_count = on
+partial_completion_other = off
+partial_completion_templates = "config_options"
+
+[history]
+display_default = 5
+max_buffer_lines_minutes = 0
+max_buffer_lines_number = 4096
+max_commands = 100
+max_visited_buffers = 50
+
+[proxy]
+
+[network]
+connection_timeout = 60
+gnutls_ca_system = on
+gnutls_ca_user = ""
+gnutls_handshake_timeout = 30
+proxy_curl = ""
+
+[plugin]
+autoload = "*"
+debug = off
+extension = ".so"
+path = "%h/plugins"
+save_config_on_unload = on
+
+[signal]
+sighup = "${if:${info:weechat_headless}?/reload:/quit -yes}"
+sigquit = "/quit -yes"
+sigterm = "/quit -yes"
+sigusr1 = ""
+sigusr2 = ""
+
+[bar]
+buffers.color_bg = default
+buffers.color_bg_inactive = default
+buffers.color_delim = default
+buffers.color_fg = default
+buffers.conditions = ""
+buffers.filling_left_right = vertical
+buffers.filling_top_bottom = horizontal
+buffers.hidden = on
+buffers.items = "buffers"
+buffers.position = left
+buffers.priority = 0
+buffers.separator = on
+buffers.size = 0
+buffers.size_max = 0
+buffers.type = root
+buflist.color_bg = default
+buflist.color_bg_inactive = default
+buflist.color_delim = default
+buflist.color_fg = default
+buflist.conditions = ""
+buflist.filling_left_right = vertical
+buflist.filling_top_bottom = columns_vertical
+buflist.hidden = off
+buflist.items = "buflist"
+buflist.position = left
+buflist.priority = 0
+buflist.separator = on
+buflist.size = 0
+buflist.size_max = 25
+buflist.type = root
+fset.color_bg = default
+fset.color_bg_inactive = default
+fset.color_delim = cyan
+fset.color_fg = default
+fset.conditions = "${buffer.full_name} == fset.fset"
+fset.filling_left_right = vertical
+fset.filling_top_bottom = horizontal
+fset.hidden = off
+fset.items = "fset"
+fset.position = top
+fset.priority = 0
+fset.separator = on
+fset.size = 3
+fset.size_max = 3
+fset.type = window
+input.color_bg = default
+input.color_bg_inactive = default
+input.color_delim = green
+input.color_fg = default
+input.conditions = ""
+input.filling_left_right = vertical
+input.filling_top_bottom = horizontal
+input.hidden = off
+input.items = "[input_prompt]+(away),[input_search],[input_paste],input_text"
+input.position = bottom
+input.priority = 1000
+input.separator = off
+input.size = 1
+input.size_max = 0
+input.type = window
+nicklist.color_bg = default
+nicklist.color_bg_inactive = default
+nicklist.color_delim = cyan
+nicklist.color_fg = default
+nicklist.conditions = "nicklist"
+nicklist.filling_left_right = vertical
+nicklist.filling_top_bottom = columns_vertical
+nicklist.hidden = on
+nicklist.items = "buffer_nicklist"
+nicklist.position = right
+nicklist.priority = 200
+nicklist.separator = on
+nicklist.size = 0
+nicklist.size_max = 0
+nicklist.type = window
+status.color_bg = green
+status.color_bg_inactive = default
+status.color_delim = 0
+status.color_fg = 0
+status.conditions = ""
+status.filling_left_right = vertical
+status.filling_top_bottom = horizontal
+status.hidden = off
+status.items = "[time],buffer_number+:+buffer_name,buffer_title"
+status.position = bottom
+status.priority = 500
+status.separator = off
+status.size = 1
+status.size_max = 0
+status.type = window
+title.color_bg = green
+title.color_bg_inactive = default
+title.color_delim = cyan
+title.color_fg = 16
+title.conditions = ""
+title.filling_left_right = vertical
+title.filling_top_bottom = horizontal
+title.hidden = on
+title.items = "buffer_title"
+title.position = top
+title.priority = 500
+title.separator = off
+title.size = 1
+title.size_max = 0
+title.type = window
+
+[layout]
+
+[notify]
+python.slack.10xgenomics.&cloud-alerts-pagerduty = highlight
+python.slack.10xgenomics.&cloud-sumo-prod-support-alerts = highlight
+python.slack.10xgenomics.&lacework-10xdev = highlight
+python.slack.10xgenomics.&lacework-10xprod = highlight
+python.slack.10xgenomics.&testing1234 = highlight
+
+[filter]
+irc_smart = on;*;irc_smart_filter;*
+nicks = on;*;irc_366;*
+
+[key]
+ctrl-? = "/input delete_previous_char"
+ctrl-A = "/input move_beginning_of_line"
+ctrl-B = "/brows"
+ctrl-Cb = "/input insert \x02"
+ctrl-Cc = "/input insert \x03"
+ctrl-Ci = "/input insert \x1D"
+ctrl-Co = "/input insert \x0F"
+ctrl-Cr = "/input insert \x12"
+ctrl-Cu = "/input insert \x15"
+ctrl-D = "/buffer close"
+ctrl-E = "/input move_end_of_line"
+ctrl-F = "/input move_next_char"
+ctrl-H = "/input delete_previous_char"
+ctrl-I = "/input complete_next"
+ctrl-J = "/input jump_smart"
+ctrl-K = "/input delete_end_of_line"
+ctrl-L = "/window refresh"
+ctrl-M = "/input return"
+ctrl-N = "/buffer +1"
+ctrl-O = "/editor"
+ctrl-P = "/buffer -1"
+ctrl-R = "/input search_text"
+ctrl-Sctrl-U = "/input set_unread"
+ctrl-T = "/input transpose_chars"
+ctrl-U = "/url 1"
+ctrl-W = "/input delete_previous_word"
+ctrl-X = "/input switch_active_buffer"
+ctrl-Y = "/input clipboard_paste"
+meta-meta-OP = "/bar scroll buflist * b"
+meta-meta-OQ = "/bar scroll buflist * e"
+meta-meta2-11~ = "/bar scroll buflist * b"
+meta-meta2-12~ = "/bar scroll buflist * e"
+meta-meta2-1~ = "/window scroll_top"
+meta-meta2-23~ = "/bar scroll nicklist * yb"
+meta-meta2-24~ = "/bar scroll nicklist * ye"
+meta-meta2-4~ = "/window scroll_bottom"
+meta-meta2-5~ = "/window scroll_up"
+meta-meta2-6~ = "/window scroll_down"
+meta-meta2-7~ = "/window scroll_top"
+meta-meta2-8~ = "/window scroll_bottom"
+meta-meta2-A = "/buffer move -1"
+meta-meta2-B = "/buffer move +1"
+meta-meta2-C = "/buffer +1"
+meta-meta2-D = "/buffer -1"
+meta-0 = "/buffer *10"
+meta-1 = "/buffer *1"
+meta-2 = "/buffer *2"
+meta-3 = "/buffer *3"
+meta-4 = "/buffer *4"
+meta-5 = "/buffer *5"
+meta-6 = "/buffer *6"
+meta-7 = "/buffer *7"
+meta-8 = "/buffer *8"
+meta-9 = "/buffer *9"
+meta-< = "/input jump_previously_visited_buffer"
+meta-= = "/filter toggle"
+meta-> = "/input jump_next_visited_buffer"
+meta-B = "/buflist toggle"
+meta-OA = "/input history_global_previous"
+meta-OB = "/input history_global_next"
+meta-OC = "/input move_next_word"
+meta-OD = "/input move_previous_word"
+meta-OF = "/input move_end_of_line"
+meta-OH = "/input move_beginning_of_line"
+meta-OP = "/bar scroll buflist * -100%"
+meta-OQ = "/bar scroll buflist * +100%"
+meta-Oa = "/input history_global_previous"
+meta-Ob = "/input history_global_next"
+meta-Oc = "/input move_next_word"
+meta-Od = "/input move_previous_word"
+meta2-11^ = "/bar scroll buflist * -100%"
+meta2-11~ = "/bar scroll buflist * -100%"
+meta2-12^ = "/bar scroll buflist * +100%"
+meta2-12~ = "/bar scroll buflist * +100%"
+meta2-15~ = "/bar scroll nicklist * y-100%"
+meta2-17~ = "/bar scroll nicklist * y+100%"
+meta2-18~ = "/window -1"
+meta2-19~ = "/window +1"
+meta2-1;3A = "/buffer -1"
+meta2-1;3B = "/buffer +1"
+meta2-1;3C = "/buffer +1"
+meta2-1;3D = "/buffer -1"
+meta2-1;3P = "/bar scroll buflist * b"
+meta2-1;3Q = "/bar scroll buflist * e"
+meta2-1;5A = "/input history_global_previous"
+meta2-1;5B = "/input history_global_next"
+meta2-1;5P = "/bar scroll buflist * -100%"
+meta2-1;5Q = "/bar scroll buflist * +100%"
+meta2-1;9A = "/buffer move -1"
+meta2-1;9B = "/buffer move +1"
+meta2-1~ = "/input move_beginning_of_line"
+meta2-20~ = "/bar scroll title * x-50%"
+meta2-21~ = "/bar scroll title * x+50%"
+meta2-23~ = "/bar scroll nicklist * y-100%"
+meta2-24~ = "/bar scroll nicklist * y+100%"
+meta2-3~ = "/input delete_next_char"
+meta2-4~ = "/input move_end_of_line"
+meta2-5;3~ = "/window scroll_up"
+meta2-5~ = "/window page_up"
+meta2-6;3~ = "/window scroll_down"
+meta2-6~ = "/window page_down"
+meta2-7~ = "/input move_beginning_of_line"
+meta2-8~ = "/input move_end_of_line"
+meta2-A = "/input history_previous"
+meta2-B = "/input history_next"
+meta2-C = "/input move_next_char"
+meta2-D = "/input move_previous_char"
+meta2-F = "/input move_end_of_line"
+meta2-G = "/window page_down"
+meta2-H = "/input move_beginning_of_line"
+meta2-I = "/window page_up"
+meta2-Z = "/input complete_previous"
+meta-_ = "/input redo"
+meta-a = "/input jump_smart"
+meta-b = "/input move_previous_word"
+meta-d = "/input delete_next_word"
+meta-f = "/input move_next_word"
+meta-h = "/input hotlist_clear"
+meta-jmeta-l = "/input jump_last_buffer"
+meta-jmeta-r = "/server raw"
+meta-jmeta-s = "/server jump"
+meta-j01 = "/buffer 1"
+meta-j02 = "/buffer 2"
+meta-j03 = "/buffer 3"
+meta-j04 = "/buffer 4"
+meta-j05 = "/buffer 5"
+meta-j06 = "/buffer 6"
+meta-j07 = "/buffer 7"
+meta-j08 = "/buffer 8"
+meta-j09 = "/buffer 9"
+meta-j10 = "/buffer 10"
+meta-j11 = "/buffer 11"
+meta-j12 = "/buffer 12"
+meta-j13 = "/buffer 13"
+meta-j14 = "/buffer 14"
+meta-j15 = "/buffer 15"
+meta-j16 = "/buffer 16"
+meta-j17 = "/buffer 17"
+meta-j18 = "/buffer 18"
+meta-j19 = "/buffer 19"
+meta-j20 = "/buffer 20"
+meta-j21 = "/buffer 21"
+meta-j22 = "/buffer 22"
+meta-j23 = "/buffer 23"
+meta-j24 = "/buffer 24"
+meta-j25 = "/buffer 25"
+meta-j26 = "/buffer 26"
+meta-j27 = "/buffer 27"
+meta-j28 = "/buffer 28"
+meta-j29 = "/buffer 29"
+meta-j30 = "/buffer 30"
+meta-j31 = "/buffer 31"
+meta-j32 = "/buffer 32"
+meta-j33 = "/buffer 33"
+meta-j34 = "/buffer 34"
+meta-j35 = "/buffer 35"
+meta-j36 = "/buffer 36"
+meta-j37 = "/buffer 37"
+meta-j38 = "/buffer 38"
+meta-j39 = "/buffer 39"
+meta-j40 = "/buffer 40"
+meta-j41 = "/buffer 41"
+meta-j42 = "/buffer 42"
+meta-j43 = "/buffer 43"
+meta-j44 = "/buffer 44"
+meta-j45 = "/buffer 45"
+meta-j46 = "/buffer 46"
+meta-j47 = "/buffer 47"
+meta-j48 = "/buffer 48"
+meta-j49 = "/buffer 49"
+meta-j50 = "/buffer 50"
+meta-j51 = "/buffer 51"
+meta-j52 = "/buffer 52"
+meta-j53 = "/buffer 53"
+meta-j54 = "/buffer 54"
+meta-j55 = "/buffer 55"
+meta-j56 = "/buffer 56"
+meta-j57 = "/buffer 57"
+meta-j58 = "/buffer 58"
+meta-j59 = "/buffer 59"
+meta-j60 = "/buffer 60"
+meta-j61 = "/buffer 61"
+meta-j62 = "/buffer 62"
+meta-j63 = "/buffer 63"
+meta-j64 = "/buffer 64"
+meta-j65 = "/buffer 65"
+meta-j66 = "/buffer 66"
+meta-j67 = "/buffer 67"
+meta-j68 = "/buffer 68"
+meta-j69 = "/buffer 69"
+meta-j70 = "/buffer 70"
+meta-j71 = "/buffer 71"
+meta-j72 = "/buffer 72"
+meta-j73 = "/buffer 73"
+meta-j74 = "/buffer 74"
+meta-j75 = "/buffer 75"
+meta-j76 = "/buffer 76"
+meta-j77 = "/buffer 77"
+meta-j78 = "/buffer 78"
+meta-j79 = "/buffer 79"
+meta-j80 = "/buffer 80"
+meta-j81 = "/buffer 81"
+meta-j82 = "/buffer 82"
+meta-j83 = "/buffer 83"
+meta-j84 = "/buffer 84"
+meta-j85 = "/buffer 85"
+meta-j86 = "/buffer 86"
+meta-j87 = "/buffer 87"
+meta-j88 = "/buffer 88"
+meta-j89 = "/buffer 89"
+meta-j90 = "/buffer 90"
+meta-j91 = "/buffer 91"
+meta-j92 = "/buffer 92"
+meta-j93 = "/buffer 93"
+meta-j94 = "/buffer 94"
+meta-j95 = "/buffer 95"
+meta-j96 = "/buffer 96"
+meta-j97 = "/buffer 97"
+meta-j98 = "/buffer 98"
+meta-j99 = "/buffer 99"
+meta-k = "/input grab_key_command"
+meta-n = "/window scroll_next_highlight"
+meta-p = "/window scroll_previous_highlight"
+meta-r = "/input delete_line"
+meta-u = "/input scroll_unread"
+meta-wmeta-meta2-A = "/window up"
+meta-wmeta-meta2-B = "/window down"
+meta-wmeta-meta2-C = "/window right"
+meta-wmeta-meta2-D = "/window left"
+meta-wmeta2-1;3A = "/window up"
+meta-wmeta2-1;3B = "/window down"
+meta-wmeta2-1;3C = "/window right"
+meta-wmeta2-1;3D = "/window left"
+meta-wmeta-b = "/window balance"
+meta-wmeta-s = "/window swap"
+meta-x = "/bar toggle nicklist"
+meta-z = "/window zoom"
+ctrl-_ = "/input undo"
+
+[key_search]
+ctrl-J = "/input search_stop"
+ctrl-M = "/input search_stop"
+ctrl-R = "/input search_switch_case"
+meta2-A = "/input search_previous"
+meta2-B = "/input search_next"
+
+[key_cursor]
+ctrl-J = "/cursor stop"
+ctrl-M = "/cursor stop"
+meta-meta2-A = "/cursor move area_up"
+meta-meta2-B = "/cursor move area_down"
+meta-meta2-C = "/cursor move area_right"
+meta-meta2-D = "/cursor move area_left"
+meta2-1;3A = "/cursor move area_up"
+meta2-1;3B = "/cursor move area_down"
+meta2-1;3C = "/cursor move area_right"
+meta2-1;3D = "/cursor move area_left"
+meta2-A = "/cursor move up"
+meta2-B = "/cursor move down"
+meta2-C = "/cursor move right"
+meta2-D = "/cursor move left"
+@chat(python.*):D = "hsignal:slack_cursor_delete"
+@chat(python.*):L = "hsignal:slack_cursor_linkarchive"
+@chat(python.*):M = "hsignal:slack_cursor_message"
+@chat(python.*):R = "hsignal:slack_cursor_reply"
+@chat(python.*):T = "hsignal:slack_cursor_thread"
+@item(buffer_nicklist):K = "/window ${_window_number};/kickban ${nick}"
+@item(buffer_nicklist):b = "/window ${_window_number};/ban ${nick}"
+@item(buffer_nicklist):k = "/window ${_window_number};/kick ${nick}"
+@item(buffer_nicklist):q = "/window ${_window_number};/query ${nick};/cursor stop"
+@item(buffer_nicklist):w = "/window ${_window_number};/whois ${nick}"
+@chat:Q = "hsignal:chat_quote_time_prefix_message;/cursor stop"
+@chat:m = "hsignal:chat_quote_message;/cursor stop"
+@chat:q = "hsignal:chat_quote_prefix_message;/cursor stop"
+
+[key_mouse]
+@bar(buflist):ctrl-wheeldown = "hsignal:buflist_mouse"
+@bar(buflist):ctrl-wheelup = "hsignal:buflist_mouse"
+@bar(input):button2 = "/input grab_mouse_area"
+@bar(nicklist):button1-gesture-down = "/bar scroll nicklist ${_window_number} +100%"
+@bar(nicklist):button1-gesture-down-long = "/bar scroll nicklist ${_window_number} e"
+@bar(nicklist):button1-gesture-up = "/bar scroll nicklist ${_window_number} -100%"
+@bar(nicklist):button1-gesture-up-long = "/bar scroll nicklist ${_window_number} b"
+@chat(fset.fset):button1 = "/window ${_window_number};/fset -go ${_chat_line_y}"
+@chat(fset.fset):button2* = "hsignal:fset_mouse"
+@chat(fset.fset):wheeldown = "/fset -down 5"
+@chat(fset.fset):wheelup = "/fset -up 5"
+@chat(python.*):button2 = "hsignal:slack_mouse"
+@chat(script.scripts):button1 = "/window ${_window_number};/script go ${_chat_line_y}"
+@chat(script.scripts):button2 = "/window ${_window_number};/script go ${_chat_line_y};/script installremove -q ${script_name_with_extension}"
+@chat(script.scripts):wheeldown = "/script down 5"
+@chat(script.scripts):wheelup = "/script up 5"
+@item(buffer_nicklist):button1 = "/window ${_window_number};/query ${nick}"
+@item(buffer_nicklist):button1-gesture-left = "/window ${_window_number};/kick ${nick}"
+@item(buffer_nicklist):button1-gesture-left-long = "/window ${_window_number};/kickban ${nick}"
+@item(buffer_nicklist):button2 = "/window ${_window_number};/whois ${nick}"
+@item(buffer_nicklist):button2-gesture-left = "/window ${_window_number};/ban ${nick}"
+@item(buffers):button1* = "hsignal:buffers_mouse"
+@item(buffers):button2* = "hsignal:buffers_mouse"
+@item(buflist):button1* = "hsignal:buflist_mouse"
+@item(buflist):button2* = "hsignal:buflist_mouse"
+@item(buflist2):button1* = "hsignal:buflist_mouse"
+@item(buflist2):button2* = "hsignal:buflist_mouse"
+@item(buflist3):button1* = "hsignal:buflist_mouse"
+@item(buflist3):button2* = "hsignal:buflist_mouse"
+@bar:wheeldown = "/bar scroll ${_bar_name} ${_window_number} +20%"
+@bar:wheelup = "/bar scroll ${_bar_name} ${_window_number} -20%"
+@chat:button1 = "/window ${_window_number}"
+@chat:button1-gesture-left = "/window ${_window_number};/buffer -1"
+@chat:button1-gesture-left-long = "/window ${_window_number};/buffer 1"
+@chat:button1-gesture-right = "/window ${_window_number};/buffer +1"
+@chat:button1-gesture-right-long = "/window ${_window_number};/input jump_last_buffer"
+@chat:wheeldown = "/window scroll_down -window ${_window_number}"
+@chat:wheelup = "/window scroll_up -window ${_window_number}"
+@*:button3 = "/cursor go ${_x},${_y}"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/weechat-old/xfer.conf Wed Aug 27 16:19:24 2025 -0400
@@ -0,0 +1,49 @@
+#
+# weechat -- xfer.conf
+#
+# WARNING: It is NOT recommended to edit this file by hand,
+# especially if WeeChat is running.
+#
+# Use commands like /set or /fset to change settings in WeeChat.
+#
+# For more info, see: https://weechat.org/doc/quickstart/
+#
+
+[look]
+auto_open_buffer = on
+progress_bar_size = 20
+pv_tags = "notify_private"
+
+[color]
+status_aborted = lightred
+status_active = lightblue
+status_connecting = yellow
+status_done = lightgreen
+status_failed = lightred
+status_waiting = lightcyan
+text = default
+text_bg = default
+text_selected = white
+
+[network]
+blocksize = 65536
+fast_send = on
+own_ip = ""
+port_range = ""
+send_ack = on
+speed_limit_recv = 0
+speed_limit_send = 0
+timeout = 300
+
+[file]
+auto_accept_chats = off
+auto_accept_files = off
+auto_accept_nicks = ""
+auto_check_crc32 = off
+auto_rename = on
+auto_resume = on
+convert_spaces = on
+download_path = "%h/xfer"
+download_temporary_suffix = ".part"
+upload_path = "~"
+use_nick_in_filename = on
--- a/weechat/.agignore Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-logs/
-urls.log
Binary file weechat/GandiStandardSSLCA.crt has changed
--- a/weechat/alias.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-#
-# weechat -- alias.conf
-#
-# WARNING: It is NOT recommended to edit this file by hand,
-# especially if WeeChat is running.
-#
-# Use commands like /set or /fset to change settings in WeeChat.
-#
-# For more info, see: https://weechat.org/doc/quickstart/
-#
-
-[cmd]
-AAWAY = "allserv /away"
-AME = "allchan /me"
-AMSG = "allchan /msg *"
-ANICK = "allserv /nick"
-b = "/buffer"
-BYE = "quit"
-C = "buffer clear"
-CHAT = "dcc chat"
-CL = "buffer clear"
-CLOSE = "buffer close"
-EXIT = "quit"
-IG = "ignore"
-J = "join"
-K = "kick"
-KB = "kickban"
-LEAVE = "part"
-M = "msg"
-MUB = "unban *"
-N = "names"
-Q = "query"
-REDRAW = "window refresh"
-SAY = "msg *"
-SIGNOFF = "quit"
-T = "topic"
-UB = "unban"
-V = "command core version"
-W = "who"
-WC = "window merge"
-WI = "whois"
-WII = "whois $1 $1"
-WW = "whowas"
-
-[completion]
--- a/weechat/aspell.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-#
-# weechat -- aspell.conf
-#
-# WARNING: It is NOT recommended to edit this file by hand,
-# especially if WeeChat is running.
-#
-# Use /set or similar command to change settings in WeeChat.
-#
-# For more info, see: https://weechat.org/doc/quickstart
-#
-
-[color]
-misspelled = lightred
-suggestion = default
-suggestion_delimiter_dict = cyan
-suggestion_delimiter_word = cyan
-
-[check]
-commands = "ame,amsg,away,command,cycle,kick,kickban,me,msg,notice,part,query,quit,topic"
-default_dict = "en"
-during_search = off
-enabled = off
-real_time = off
-suggestions = -1
-word_min_length = 2
-
-[dict]
-
-[look]
-suggestion_delimiter_dict = " / "
-suggestion_delimiter_word = ","
-
-[option]
--- a/weechat/autosort.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-#
-# weechat -- autosort.conf
-#
-# WARNING: It is NOT recommended to edit this file by hand,
-# especially if WeeChat is running.
-#
-# Use commands like /set or /fset to change settings in WeeChat.
-#
-# For more info, see: https://weechat.org/doc/quickstart/
-#
-
-[sorting]
-case_sensitive = off
-debug_log = off
-replacements = "[]"
-rules = "[["core", 0], ["irc", 2], ["*", 1], ["irc.irc_raw", 0], ["irc.server", 1]]"
-signal_delay = 5
-signals = "buffer_opened buffer_merged buffer_unmerged buffer_renamed"
-sort_limit = 100
-sort_on_config_change = on
-
-[v3]
-helpers = "{"core_first": "${if:${buffer.full_name}!=core.weechat}", "irc_raw_first": "${if:${buffer.full_name}!=irc.irc_raw}", "irc_raw_last": "${if:${buffer.full_name}==irc.irc_raw}", "hashless_name": "${info:autosort_replace,#,,${info:autosort_escape,${buffer.name}}}", "script_or_plugin": "${if:${script_name}?${script_name}:${plugin}}"}"
-rules = "["${core_first}", "${info:autosort_order,${info:autosort_escape,${script_or_plugin}},core,*,irc,bitlbee,matrix,slack}", "${script_or_plugin}", "${irc_raw_first}", "${server}", "${info:autosort_order,${type},server,*,channel,private}", "${hashless_name}", "${buffer.full_name}"]"
--- a/weechat/buffers.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-#
-# weechat -- buffers.conf
-#
-# WARNING: It is NOT recommended to edit this file by hand,
-# especially if WeeChat is running.
-#
-# Use /set or similar command to change settings in WeeChat.
-#
-# For more info, see: https://weechat.org/doc/quickstart
-#
-
-[color]
-current_bg = green
-current_fg = black
-default_bg = default
-default_fg = default
-hotlist_highlight_bg = default
-hotlist_highlight_fg = *magenta
-hotlist_low_bg = default
-hotlist_low_fg = white
-hotlist_message_bg = default
-hotlist_message_fg = green
-hotlist_private_bg = default
-hotlist_private_fg = *magenta
-none_channel_bg = default
-none_channel_fg = 240
-number = green
-number_char = green
-prefix_bufname = default
-queries_default_bg = default
-queries_default_fg = default
-queries_highlight_bg = default
-queries_highlight_fg = default
-queries_message_bg = default
-queries_message_fg = default
-suffix_bufname = default
-whitelist_default_bg = default
-whitelist_default_fg = default
-whitelist_highlight_bg = default
-whitelist_highlight_fg = default
-whitelist_low_bg = default
-whitelist_low_fg = default
-whitelist_message_bg = default
-whitelist_message_fg = default
-whitelist_private_bg = default
-whitelist_private_fg = default
-
-[look]
-core_to_front = off
-detach = 0
-detach_buffer_immediately = ""
-detach_display_window_number = off
-detach_displayed_buffers = on
-detach_free_content = off
-detach_query = off
-hide_merged_buffers = none
-hotlist_counter = off
-immune_detach_buffers = ""
-indenting = on
-indenting_number = on
-jump_prev_next_visited_buffer = off
-mark_inactive = off
-mouse_move_buffer = on
-name_crop_suffix = "+"
-name_size_max = 0
-number_char = " "
-prefix = off
-prefix_bufname = ""
-prefix_empty = on
-prefix_for_query = ""
-short_names = on
-show_lag = off
-show_number = on
-sort = number
-suffix_bufname = ""
-toogle_bar = on
-whitelist_buffers = ""
--- a/weechat/buflist.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-#
-# weechat -- buflist.conf
-#
-# WARNING: It is NOT recommended to edit this file by hand,
-# especially if WeeChat is running.
-#
-# Use commands like /set or /fset to change settings in WeeChat.
-#
-# For more info, see: https://weechat.org/doc/quickstart/
-#
-
-[look]
-add_newline = on
-auto_scroll = 50
-display_conditions = "${buffer.hidden}==0"
-enabled = on
-mouse_jump_visited_buffer = off
-mouse_move_buffer = on
-mouse_wheel = on
-nick_prefix = off
-nick_prefix_empty = on
-signals_refresh = ""
-sort = "number,-active"
-use_items = 1
-
-[format]
-buffer = "${format_number}${indent}${format_nick_prefix}${color_hotlist}${format_name}"
-buffer_current = "${color:,blue}${format_buffer}"
-hotlist = " ${color:green}(${hotlist}${color:green})"
-hotlist_highlight = "${color:magenta}"
-hotlist_low = "${color:white}"
-hotlist_message = "${color:green}"
-hotlist_none = "${color:default}"
-hotlist_private = "${color:magenta}"
-hotlist_separator = "${color:default},"
-indent = " "
-lag = " ${color:green}[${color:brown}${lag}${color:green}]"
-name = "${name}"
-nick_prefix = "${color_nick_prefix}${nick_prefix}"
-number = "${color:green}${number}${if:${number_displayed}?.: }"
-tls_version = " ${color:default}(${if:${tls_version}==TLS1.3?${color:green}:${if:${tls_version}==TLS1.2?${color:yellow}:${color:red}}}${translate:${tls_version}}${color:default})"
--- a/weechat/charset.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-#
-# weechat -- charset.conf
-#
-# WARNING: It is NOT recommended to edit this file by hand,
-# especially if WeeChat is running.
-#
-# Use commands like /set or /fset to change settings in WeeChat.
-#
-# For more info, see: https://weechat.org/doc/quickstart/
-#
-
-[default]
-decode = "iso-8859-1"
-encode = ""
-
-[decode]
-
-[encode]
--- a/weechat/exec.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-#
-# weechat -- exec.conf
-#
-# WARNING: It is NOT recommended to edit this file by hand,
-# especially if WeeChat is running.
-#
-# Use commands like /set or /fset to change settings in WeeChat.
-#
-# For more info, see: https://weechat.org/doc/quickstart/
-#
-
-[command]
-default_options = ""
-purge_delay = 0
-shell = "${env:SHELL}"
-
-[color]
-flag_finished = lightred
-flag_running = lightgreen
--- a/weechat/fifo.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-#
-# weechat -- fifo.conf
-#
-# WARNING: It is NOT recommended to edit this file by hand,
-# especially if WeeChat is running.
-#
-# Use commands like /set or /fset to change settings in WeeChat.
-#
-# For more info, see: https://weechat.org/doc/quickstart/
-#
-
-[file]
-enabled = on
-path = "%h/weechat_fifo"
--- a/weechat/fset.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +0,0 @@
-#
-# weechat -- fset.conf
-#
-# WARNING: It is NOT recommended to edit this file by hand,
-# especially if WeeChat is running.
-#
-# Use commands like /set or /fset to change settings in WeeChat.
-#
-# For more info, see: https://weechat.org/doc/quickstart/
-#
-
-[look]
-auto_refresh = "*"
-auto_unmark = off
-condition_catch_set = "${count} >= 1"
-export_help_default = on
-format_number = 1
-marked_string = "*"
-scroll_horizontal = 10
-show_plugins_desc = off
-sort = "~name"
-unmarked_string = " "
-use_color_value = off
-use_keys = on
-use_mute = off
-
-[format]
-export_help = "# ${description2}"
-export_option = "/set ${name} ${quoted_value}"
-export_option_null = "/unset ${name}"
-option1 = ""
-option2 = "${marked} ${name} ${type} ${value2}${newline} ${empty_name} ${_default_value}${color:darkgray} -- ${min}..${max}${newline} ${empty_name} ${description}"
-
-[color]
-default_value = default
-default_value_selected = white
-description = default
-description_selected = white
-file = default
-file_changed = brown
-file_changed_selected = yellow
-file_selected = white
-help_default_value = white
-help_description = default
-help_name = white
-help_quotes = darkgray
-help_values = default
-index = cyan
-index_selected = lightcyan
-line_marked_bg1 = default
-line_marked_bg2 = default
-line_selected_bg1 = blue
-line_selected_bg2 = red
-marked = brown
-marked_selected = yellow
-max = default
-max_selected = white
-min = default
-min_selected = white
-name = default
-name_changed = brown
-name_changed_selected = yellow
-name_selected = white
-option = default
-option_changed = brown
-option_changed_selected = yellow
-option_selected = white
-parent_name = default
-parent_name_selected = white
-parent_value = cyan
-parent_value_selected = lightcyan
-quotes = darkgray
-quotes_changed = default
-quotes_changed_selected = white
-quotes_selected = default
-section = default
-section_changed = brown
-section_changed_selected = yellow
-section_selected = white
-string_values = default
-string_values_selected = white
-title_count_options = cyan
-title_current_option = lightcyan
-title_filter = yellow
-title_marked_options = lightgreen
-title_sort = white
-type = green
-type_selected = lightgreen
-unmarked = default
-unmarked_selected = white
-value = cyan
-value_changed = brown
-value_changed_selected = yellow
-value_selected = lightcyan
-value_undef = magenta
-value_undef_selected = lightmagenta
Binary file weechat/icon.png has changed
--- a/weechat/logger.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-#
-# weechat -- logger.conf
-#
-# WARNING: It is NOT recommended to edit this file by hand,
-# especially if WeeChat is running.
-#
-# Use commands like /set or /fset to change settings in WeeChat.
-#
-# For more info, see: https://weechat.org/doc/quickstart/
-#
-
-[look]
-backlog = 20
-backlog_conditions = ""
-
-[color]
-backlog_end = darkgray
-backlog_line = darkgray
-
-[file]
-auto_log = on
-color_lines = off
-flush_delay = 120
-fsync = off
-info_lines = off
-mask = "$plugin.$name.weechatlog"
-name_lower_case = on
-nick_prefix = " <"
-nick_suffix = "> "
-path = "%h/logs/"
-replacement_char = "_"
-time_format = "%Y-%m-%d %H:%M:%S"
-
-[level]
-
-[mask]
--- a/weechat/lua.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-#
-# weechat -- lua.conf
-#
-# WARNING: It is NOT recommended to edit this file by hand,
-# especially if WeeChat is running.
-#
-# Use /set or similar command to change settings in WeeChat.
-#
-# For more info, see: https://weechat.org/doc/quickstart
-#
-
-[look]
-check_license = off
-eval_keep_context = on
--- a/weechat/perl.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-#
-# weechat -- perl.conf
-#
-# WARNING: It is NOT recommended to edit this file by hand,
-# especially if WeeChat is running.
-#
-# Use commands like /set or /fset to change settings in WeeChat.
-#
-# For more info, see: https://weechat.org/doc/quickstart
-#
-
-[look]
-check_license = off
-eval_keep_context = on
--- a/weechat/perl/autoload/colorize_lines.pl Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,251 +0,0 @@
-#
-# Copyright (c) 2010-2013 by Nils Görs <weechatter@arcor.de>
-# Copyleft (ɔ) 2013 by oakkitten
-#
-# colors the channel text with nick color and also highlight the whole line
-# colorize_nicks.py script will be supported
-#
-# 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/>.
-#
-# with version 3.0 some options were renamed or have new possible values:
-# old: new:
-# avail_buffer buffer
-# blacklist_channels blacklist_buffers
-# highlight new values
-
-# obsolete options:
-# buffer_autoset
-# hotlist_max_level_nicks_add
-# highlight_regex
-# highlight_words
-# shuffle
-# chat see option highlight
-
-# history:
-# 3.0: large part of script rewritten
-# fix: works nicely with irc colors
-# improved: highlight_regex and highlight_words work in a natural way
-# removed: command /colorize_lines
-# removed: option shuffle
-# 2.2: fix: regex with [tab] in message (patch by sqrrl)
-# 2.1: fix: changing highlight color did not apply messages already displayed (reported by rafi_)
-# 2.0: fix: debugging weechat::print() removed (thanks demure)
-# 1.9: fix: display bug with nick_mode
-# 1.8 add: option "use_irc_colors" (requested by Zertap)
-# fix: empty char for nick_mode was used, even when "irc.look.nick_mode_empty" was OFF (reported by FlashCode)
-# 1.7: fix: broken lines in dcc chat (reported by equatorping)
-# 1.6: improved: wildcard "*" can be used for server and/or nick. (requested by ldvx)
-# : add: new value, "only", for option "own_lines" (read help!)
-# 1.5: sync: option weechat.look.nickmode changed in 0.3.9 to "irc.look.nick_mode"
-# 1.4: fix: whole ctcp message was display in prefix (reported by : Mkaysi)
-# 1.3: fix: now using weechat::buffer_get_string() instead of regex to prevent problems with dots inside server-/channelnames (reported by surfhai)
-# 1.2: add: hook_modifier("colorize_lines") to use colorize_lines with another script.
-# : fix: regex was too greedy and also hit tag "prefix_nick_ccc"
-# 1.1: fix: problems with temporary server (reported by nand`)
-# : improved: using weechat_string_has_highlight()
-# 1.0: fix: irc.look.nick_prefix wasn't supported
-# 0.9: added: option "own_nick" (idea by travkin)
-# : new value (always) for option highlight
-# : clean up code
-# 0.8.1: fix: regex()
-# 0.8: added: option "avail_buffer" and "nicks" (please read help-page) (suggested by ldvx)
-# : fix: blacklist_buffers wasn't load at start
-# : fix: nick_modes wasn't displayed since v0.7
-# : rewrote init() routine
-# : thanks to stfn for hint with unescaped variables in regex.
-# 0.7: fix: bug when irc.look.nick_suffix was set (reported and beta-testing by: hw2) (>= weechat 0.3.4)
-# blacklist_buffers option supports servername
-# clean up code
-# 0.6: code optimazations.
-# rename of script (rainbow_text.pl -> colorize_lines.pl) (suggested by xt and flashcode)
-# 0.5: support of hotlist_max_level_nicks_add and weechat.color.chat_nick_colors (>= weechat 0.3.4)
-# 0.4: support of weechat.look.highlight_regex option (>= weechat 0.3.4)
-# : support of weechat.look.highlight option
-# : highlighted line did not work with "." inside servername
-# ; internal "autoset" function fixed
-# 0.3: support of colorize_nicks.py implemented.
-# : /me text displayed wrong nick colour (colour from suffix was used)
-# : highlight messages will be checked case insensitiv
-# 0.2: supports highlight_words_add from buffer_autoset.py script (suggested: Emralegna)
-# : correct look_nickmode colour will be used (bug reported by: Emralegna)
-# : /me text will be coloured, too
-# 0.1: initial release
-#
-# Development is currently hosted at
-# https://github.com/weechatter/weechat-scripts
-
-# use Data::Dumper
-# $Data::Dumper::Useqq=1;
-
-use strict;
-my $prgname = "colorize_lines";
-my $version = "3.0";
-my $description = "colors text in chat area with according nick color, including highlights";
-
-my %config = ("buffers" => "all", # all, channel, query
- "blacklist_buffers" => "", # "a,b,c"
- "lines" => "on",
- "highlight" => "on", # on, off, nicks
- "nicks" => "", # "d,e,f", "/file"
- "own_lines" => "on", # on, off, only
-);
-
-my %help_desc = ("buffers" => "buffer type affected by the script (all/channel/query, default: all)",
- "blacklist_buffers" => "comma-separated list of channels to be ignored (e.g. freenode.#weechat,*.#python)",
- "lines" => "apply nickname color to the non-highlighted lines (off/on/nicks). the latter will limit highlighting to nicknames in option 'nicks'",
- "highlight" => "apply highlight color to the highlighted lines (off/on/nicks). the latter will limit highlighting to nicknames in option 'nicks'",
- "nicks" => "comma-separater list of nicks (e.g. freenode.cat,*.dog) OR file name starting with '/' (e.g. /file.txt). in the latter case, nicknames will get loaded from that file inside weechat folder (e.g. from ~/.weechat/file.txt). nicknames in file are newline-separated (e.g. freenode.dog\\n*.cat)",
- "own_lines" => "apply nickname color to own lines (off/on/only). the latter turns off all other kinds of coloring altogether",
-);
-
-#################################################################################################### config
-
-# program starts here
-sub colorize_cb {
- my ( $data, $modifier, $modifier_data, $string ) = @_;
-
- # quit if it's not a privmsg or ctcp
- # or we are not supposed to
- if ((index($modifier_data,"irc_privmsg") == -1) ||
- (index($modifier_data,"irc_ctcp") >= 0)) {
- return $string;
- }
-
- # find buffer pointer
- $modifier_data =~ m/([^;]*);([^;]*);/;
- my $buffer = weechat::buffer_search($1, $2);
- return $string if ($buffer eq "");
-
- # find buffer name, server name
- # return if buffer is in a blacklist
- my $buffername = weechat::buffer_get_string($buffer, "name");
- return $string if weechat::string_has_highlight($buffername, $config{blacklist_buffers});
- my $servername = weechat::buffer_get_string($buffer, "localvar_server");
-
- # find stuff between \t
- $string =~ m/^([^\t]*)\t(.*)/;
- my $left = $1;
- my $right = $2;
-
- # find nick of the sender
- # find out if we are doing an action
- my $nick = ($modifier_data =~ m/(^|,)nick_([^,]*)/) ? $2 : weechat::string_remove_color($left, "");
- my $action = ($modifier_data =~ m/\birc_action\b/) ? 1 : 0;
-
- ######################################## get color
-
- my $color = "";
- my $my_nick = weechat::buffer_get_string($buffer, "localvar_nick");
- if ($my_nick eq $nick) {
- # it's our own line
- # process only if own_lines is "on" or "only" (i.e. not "off")
- return $string if ($config{own_lines} eq "off");
- $color = weechat::color("chat_nick_self");
- } else {
- # it's someone else's line
- # don't process is own_lines are "only"
- # in order to get correct matching, remove colors from the string
- return $string if ($config{own_lines} eq "only");
- my $right_nocolor = weechat::string_remove_color($right, "");
- if (weechat::string_has_highlight($right_nocolor, weechat::buffer_string_replace_local_var($buffer, weechat::buffer_get_string($buffer, "highlight_words"))) ||
- weechat::string_has_highlight($right_nocolor, weechat::config_string(weechat::config_get("weechat.look.highlight"))) ||
- weechat::string_has_highlight_regex($right_nocolor, weechat::config_string(weechat::config_get("weechat.look.highlight_regex"))) ||
- weechat::string_has_highlight_regex($right_nocolor, weechat::buffer_get_string($buffer, "highlight_regex"))
- ) {
- # we have a hilight! get a hilight color
- # and replace the first occurance of coloring, that'd be nick color
- # process only if highlight is "on" OR "nicks" & nick's in nicks
- return $string if ($config{highlight} eq "off" ||
- ($config{highlight} eq "nicks" && !weechat::string_has_highlight("$servername.$nick", $config{nicks})));
- $color = weechat::color('chat_highlight');
- $right =~ s/\31[^\31 ]+?\Q$nick/$color$nick/ if ($action);
- } else {
- # that's not a highlight
- # process only if lines is "on" OR "nicks" & nick's in nicks
- return $string if ($config{lines} eq "off" ||
- ($config{lines} eq "nicks" && !weechat::string_has_highlight("$servername.$nick", $config{nicks})));
- $color = weechat::info_get('irc_nick_color', $nick);
- }
- }
-
- ######################################## inject colors and go!
-
- my $out = "";
- if ($action) {
- # remove the first color reset - after * nick
- # make other resets reset to our color
- $right =~ s/\34//;
- $right =~ s/\34/\34$color/g;
- $out = $left . "\t" . $right . "\34"
- } else {
- # make other resets reset to our color
- $right =~ s/\34/\34$color/g;
- $out = $left . "\t" . $color . $right . "\34"
- }
- #weechat::print("", ""); weechat::print("", "\$str " . Dumper($string)); weechat::print("", "\$out " . Dumper($out));
- return $out;
-}
-
-#################################################################################################### config
-
-# read nicknames if $conf{nisks} starts with /
-# after this, $conf{nisks} is of form a,b,c,d
-# if it doesnt start with /, assume it's already a,b,c,d
-sub nicklist_read {
- return if (substr($config{nicks}, 0, 1) ne "/");
- my $file = weechat::info_get("weechat_dir", "") . $config{nicks};
- return unless -e $file;
- my $nili = "";
- open (WL, "<", $file) || DEBUG("$file: $!");
- while (<WL>) {
- chomp; # kill LF
- $nili .= $_ . ",";
- }
- close WL;
- chop $nili; # remove last ","
- $config{nicks} = $nili;
-}
-
-# called when a config option ha been changed
-# $name = plugins.var.perl.$prgname.nicks etc
-sub toggle_config_by_set {
- my ($pointer, $name, $value) = @_;
- $name = substr($name,length("plugins.var.perl.$prgname."),length($name));
- $config{$name} = lc($value);
- nicklist_read() if ($name eq "nicks");
-}
-
-# read configuration from weechat OR
-# set default options and
-# set dectription if weechat >= 0.3.5
-# after done, read nicklist from file if needed
-sub init_config {
- my $weechat_version = weechat::info_get('version_number', '') || 0;
- foreach my $option (keys %config){
- if (!weechat::config_is_set_plugin($option)) {
- weechat::config_set_plugin($option, $config{$option});
- weechat::config_set_desc_plugin($option, $help_desc{$option}) if ($weechat_version >= 0x00030500); # v0.3.5
- } else {
- $config{$option} = lc(weechat::config_get_plugin($option));
- }
- }
- nicklist_read();
-}
-
-#################################################################################################### start
-
-weechat::register($prgname, "Nils Görs <weechatter\@arcor.de>", $version, "GPL3", $description, "", "");
-weechat::hook_modifier("500|weechat_print","colorize_cb", "");
-init_config();
-weechat::hook_config("plugins.var.perl.$prgname.*", "toggle_config_by_set", "");
--- a/weechat/python.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-#
-# weechat -- python.conf
-#
-# WARNING: It is NOT recommended to edit this file by hand,
-# especially if WeeChat is running.
-#
-# Use commands like /set or /fset to change settings in WeeChat.
-#
-# For more info, see: https://weechat.org/doc/quickstart/
-#
-
-[look]
-check_license = off
-eval_keep_context = on
--- a/weechat/python/autoload/autosort.py Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-../autosort.py
\ No newline at end of file
--- a/weechat/python/autoload/brows.py Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-import subprocess
-import os
-
-SCRIPT_NAME = 'brows'
-SCRIPT_AUTHOR = 'Steve Losh <steve@stevelosh.com>'
-SCRIPT_VERSION = '1.0'
-SCRIPT_LICENSE = 'MIT/X11'
-SCRIPT_DESC = 'Launch brows to view URLs'
-SCRIPT_COMMAND = 'brows'
-
-import_ok = True
-
-BROWS = os.environ.get('BROWS', 'brows')
-
-try:
- import weechat
-except ImportError:
- print('This is a weechat script, what are you doing, run it in weechat, jesus')
- import_ok = False
-
-weechat_version = 0
-
-def hd(fn, name, obj, attr):
- return fn(weechat.hdata_get(name), obj, attr)
-
-def hdp(name, obj, attr):
- return hd(weechat.hdata_pointer, name, obj, attr)
-
-def hds(name, obj, attr):
- return hd(weechat.hdata_string, name, obj, attr)
-
-def get_lines(buffer, numlines):
- lines = hdp("buffer", buffer, "own_lines")
- if not lines:
- # null pointer wat do
- return None
-
- last_lines = []
-
- line = hdp("lines", lines, "last_line")
- for _ in range(numlines):
- if not line:
- # shit we're at the top of the buffer
- break
-
- data = hdp("line", line, "data")
- msg = hds("line_data", data, "message")
- msg = weechat.string_remove_color(msg, "")
-
- last_lines.append(msg.strip())
-
- line = hdp("line", line, "prev_line")
-
- return last_lines
-
-def brows(data, buffer, args, numlines=100):
- lines = get_lines(buffer, numlines)
-
- proc = subprocess.Popen([BROWS], stdin=subprocess.PIPE)
- proc.communicate(input='\n'.join(lines))
- weechat.command("", "/window refresh")
-
- return weechat.WEECHAT_RC_OK
-
-
-if __name__ == '__main__' and import_ok:
- if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION,
- SCRIPT_LICENSE, SCRIPT_DESC, '', ''):
- weechat_version = weechat.info_get('version_number', '') or 0
- weechat.hook_command(
- SCRIPT_COMMAND,
- 'Launch brows to view URLs',
- '',
- '',
- '',
- 'brows',
- '')
--- a/weechat/python/autoload/editor.py Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-import subprocess
-import os
-import tempfile
-
-SCRIPT_NAME = 'editor'
-SCRIPT_AUTHOR = 'Steve Losh <steve@stevelosh.com>'
-SCRIPT_VERSION = '1.0'
-SCRIPT_LICENSE = 'MIT/X11'
-SCRIPT_DESC = 'Launch an external editor to compose a message'
-SCRIPT_COMMAND = 'editor'
-
-import_ok = True
-
-EDITOR = os.environ.get('EDITOR','vim')
-
-try:
- import weechat
-except ImportError:
- print('This is a weechat script, what are you doing, run it in weechat, jesus')
- import_ok = False
-
-weechat_version = 0
-
-
-def get_data(suffix, initial_data):
- with tempfile.NamedTemporaryFile(suffix=".%s" % suffix, mode="w+") as tf:
- tf.write(initial_data)
- tf.flush()
-
- if subprocess.call([EDITOR, tf.name]) != 0:
- return None
-
- # Reopen, because most editors do atomic write-tmp+rename saves which
- # fucks with Python here.
- tf.file.close()
- with open(tf.name) as tf2:
- return tf2.read()
-
-def editor(data, buffer, args):
- suffix = args or "tmp"
-
- line = weechat.buffer_get_string(buffer, "input")
-
- data = get_data(suffix, line)
- if data:
- weechat.command(buffer, "/input delete_line")
- weechat.command(buffer, data.strip())
-
- weechat.command("", "/window refresh")
-
- return weechat.WEECHAT_RC_OK
-
-
-if __name__ == '__main__' and import_ok:
- if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION,
- SCRIPT_LICENSE, SCRIPT_DESC, '', ''):
- weechat_version = weechat.info_get('version_number', '') or 0
- weechat.hook_command(
- SCRIPT_COMMAND,
- 'Open $EDITOR to compose a message',
- '[file-extension]',
- 'If an argument is given, it will be used as the extension for the temporary file.',
- '',
- 'editor',
- '')
--- a/weechat/python/autoload/notify.py Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-import weechat, subprocess
-
-SCRIPT_NAME = 'notify'
-SCRIPT_AUTHOR = 'Steve Losh <steve@stevelosh.com>'
-SCRIPT_VERSION = '0.0.1'
-SCRIPT_LICENSE = 'MIT'
-SCRIPT_DESC = 'notify-send for weechat'
-
-weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, '', '')
-
-weechat.hook_print('', 'irc_privmsg', '', 1, 'notify', '')
-
-def _notify(text):
- subprocess.call(['notify-send', text])
-
-def notify(data, buffer, date, tags, displayed, highlight, prefix, message):
- # ignore if it's yourself
- own_nick = weechat.buffer_get_string(buffer, 'localvar_nick')
-
- if prefix == own_nick or prefix == ('@%s' % own_nick):
- return weechat.WEECHAT_RC_OK
-
- if int(highlight):
- channel = weechat.buffer_get_string(buffer, 'localvar_channel')
- _notify('%s %s\n%s' % (prefix, channel, message))
- elif 'notify_private' in tags:
- _notify('%s [PM]\n%s' % (prefix, message))
-
- return weechat.WEECHAT_RC_OK
--- a/weechat/python/autoload/quotes.py Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,124 +0,0 @@
-import subprocess, os
-
-SCRIPT_NAME = 'quotes'
-SCRIPT_AUTHOR = 'Steve Losh <steve@stevelosh.com>'
-SCRIPT_VERSION = '1.0'
-SCRIPT_LICENSE = 'MIT/X11'
-SCRIPT_DESC = 'Grab quotes and shove them into a text file.'
-SCRIPT_COMMAND = 'quo'
-SCRIPT_COMMAND_LONG = 'quoo'
-SCRIPT_COMMAND_COPY = 'cop'
-
-HOME = os.getenv("HOME")
-QUOTE_FILE = '%s/Dropbox/quotes.txt' % HOME
-COPY_FILE = '%s/.ircopy.irc' % HOME
-
-import_ok = True
-
-try:
- import weechat
-except ImportError:
- print('This is a weechat script, what are you doing, run it in weechat, jesus')
- import_ok = False
-
-weechat_version = 0
-
-def hd(fn, name, obj, attr):
- return fn(weechat.hdata_get(name), obj, attr)
-
-def hdp(name, obj, attr):
- return hd(weechat.hdata_pointer, name, obj, attr)
-
-def hds(name, obj, attr):
- return hd(weechat.hdata_string, name, obj, attr)
-
-def get_lines(buffer, numlines):
- lines = hdp("buffer", buffer, "own_lines")
- if not lines:
- # null pointer wat do
- return None
-
- last_lines = []
-
- line = hdp("lines", lines, "last_line")
- for _ in range(numlines):
- if not line:
- # shit we're at the top of the buffer
- break
-
- data = hdp("line", line, "data")
- msg = hds("line_data", data, "message")
- pre = hds("line_data", data, "prefix")
-
- msg = weechat.string_remove_color(msg, "")
- pre = weechat.string_remove_color(pre, "")
-
- last_lines.append("<%s> %s" % (pre.strip(), msg.strip()))
-
- line = hdp("line", line, "prev_line")
-
- last_lines.reverse()
- return last_lines
-
-def quote_grab(data, buffer, args, numlines=15):
- lines = get_lines(buffer, numlines)
-
- with open(QUOTE_FILE, 'a') as f:
- f.write("\n---\n")
- f.write('\n'.join(lines))
-
- subprocess.call(["nvim", QUOTE_FILE,
- # start at the bottom of the file
- "+",
- # move up N lines, where N is how many we appended
- "-c", "normal! %dk" % len(lines)])
- weechat.command("", "/window refresh")
-
- return weechat.WEECHAT_RC_OK
-
-def quote_grab_long(data, buffer, args):
- return quote_grab(data, buffer, args, 75)
-
-def quote_grab_copy(data, buffer, args):
- lines = get_lines(buffer, 1000)
-
- with open(COPY_FILE, 'w') as f:
- f.write('\n'.join(lines))
-
- subprocess.call(["nvim", COPY_FILE,
- # start at the bottom of the file
- "+"])
- weechat.command("", "/window refresh")
-
- return weechat.WEECHAT_RC_OK
-
-if __name__ == '__main__' and import_ok:
- if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION,
- SCRIPT_LICENSE, SCRIPT_DESC, '', ''):
- weechat_version = weechat.info_get('version_number', '') or 0
- weechat.hook_command(
- SCRIPT_COMMAND,
- 'Appends the last 15 lines of the current buffer to your quotes '
- 'file and opens it in Vim so you can trim it.',
- '',
- '',
- '',
- 'quote_grab',
- '')
- weechat.hook_command(
- SCRIPT_COMMAND_LONG,
- 'Appends the last 75 lines of the current buffer to your quotes '
- 'file and opens it in Vim so you can trim it.',
- '',
- '',
- '',
- 'quote_grab_long',
- '')
- weechat.hook_command(
- SCRIPT_COMMAND_COPY,
- 'Open the last 1000 lines of the file in Vim so you can copy.',
- '',
- '',
- '',
- 'quote_grab_copy',
- '')
--- a/weechat/python/autoload/urlgrab.py Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,699 +0,0 @@
-# -*- 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):
- # weechat.prnt("", out)
- # weechat.prnt("", 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")
--- a/weechat/python/autoload/wee_slack.py Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-/home/sjl/src/wee-slack/wee_slack.py
\ No newline at end of file
--- a/weechat/python/autosort.py Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1075 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013-2017 Maarten de Vries <maarten@de-vri.es>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-
-#
-# Autosort automatically keeps your buffers sorted and grouped by server.
-# You can define your own sorting rules. See /help autosort for more details.
-#
-# https://github.com/de-vri-es/weechat-autosort
-#
-
-#
-# Changelog:
-# 3.9:
-# * Remove `buffers.pl` from recommended settings.
-# 3,8:
-# * Fix relative sorting on script name in default rules.
-# * Document a useful property of stable sort algorithms.
-# 3.7:
-# * Make default rules work with bitlbee, matrix and slack.
-# 3.6:
-# * Add more documentation on provided info hooks.
-# 3.5:
-# * Add ${info:autosort_escape,...} to escape arguments for other info hooks.
-# 3.4:
-# * Fix rate-limit of sorting to prevent high CPU load and lock-ups.
-# * Fix bug in parsing empty arguments for info hooks.
-# * Add debug_log option to aid with debugging.
-# * Correct a few typos.
-# 3.3:
-# * Fix the /autosort debug command for unicode.
-# * Update the default rules to work better with Slack.
-# 3.2:
-# * Fix python3 compatiblity.
-# 3.1:
-# * Use colors to format the help text.
-# 3.0:
-# * Switch to evaluated expressions for sorting.
-# * Add `/autosort debug` command.
-# * Add ${info:autosort_replace,from,to,text} to replace substrings in sort rules.
-# * Add ${info:autosort_order,value,first,second,third} to ease writing sort rules.
-# * Make tab completion context aware.
-# 2.8:
-# * Fix compatibility with python 3 regarding unicode handling.
-# 2.7:
-# * Fix sorting of buffers with spaces in their name.
-# 2.6:
-# * Ignore case in rules when doing case insensitive sorting.
-# 2.5:
-# * Fix handling unicode buffer names.
-# * Add hint to set irc.look.server_buffer to independent and buffers.look.indenting to on.
-# 2.4:
-# * Make script python3 compatible.
-# 2.3:
-# * Fix sorting items without score last (regressed in 2.2).
-# 2.2:
-# * Add configuration option for signals that trigger a sort.
-# * Add command to manually trigger a sort (/autosort sort).
-# * Add replacement patterns to apply before sorting.
-# 2.1:
-# * Fix some minor style issues.
-# 2.0:
-# * Allow for custom sort rules.
-#
-
-
-import json
-import math
-import re
-import sys
-import time
-import weechat
-
-SCRIPT_NAME = 'autosort'
-SCRIPT_AUTHOR = 'Maarten de Vries <maarten@de-vri.es>'
-SCRIPT_VERSION = '3.9'
-SCRIPT_LICENSE = 'GPL3'
-SCRIPT_DESC = 'Flexible automatic (or manual) buffer sorting based on eval expressions.'
-
-
-config = None
-hooks = []
-signal_delay_timer = None
-sort_limit_timer = None
-sort_queued = False
-
-
-# Make sure that unicode, bytes and str are always available in python2 and 3.
-# For python 2, str == bytes
-# For python 3, str == unicode
-if sys.version_info[0] >= 3:
- unicode = str
-
-def ensure_str(input):
- '''
- Make sure the given type if the correct string type for the current python version.
- That means bytes for python2 and unicode for python3.
- '''
- if not isinstance(input, str):
- if isinstance(input, bytes):
- return input.encode('utf-8')
- if isinstance(input, unicode):
- return input.decode('utf-8')
- return input
-
-
-if hasattr(time, 'perf_counter'):
- perf_counter = time.perf_counter
-else:
- perf_counter = time.clock
-
-def casefold(string):
- if hasattr(string, 'casefold'): return string.casefold()
- # Fall back to lowercasing for python2.
- return string.lower()
-
-def list_swap(values, a, b):
- values[a], values[b] = values[b], values[a]
-
-def list_move(values, old_index, new_index):
- values.insert(new_index, values.pop(old_index))
-
-def list_find(collection, value):
- for i, elem in enumerate(collection):
- if elem == value: return i
- return None
-
-class HumanReadableError(Exception):
- pass
-
-def parse_int(arg, arg_name = 'argument'):
- ''' Parse an integer and provide a more human readable error. '''
- arg = arg.strip()
- try:
- return int(arg)
- except ValueError:
- raise HumanReadableError('Invalid {0}: expected integer, got "{1}".'.format(arg_name, arg))
-
-def decode_rules(blob):
- parsed = json.loads(blob)
- if not isinstance(parsed, list):
- log('Malformed rules, expected a JSON encoded list of strings, but got a {0}. No rules have been loaded. Please fix the setting manually.'.format(type(parsed)))
- return []
-
- for i, entry in enumerate(parsed):
- if not isinstance(entry, (str, unicode)):
- log('Rule #{0} is not a string but a {1}. No rules have been loaded. Please fix the setting manually.'.format(i, type(entry)))
- return []
-
- return parsed
-
-def decode_helpers(blob):
- parsed = json.loads(blob)
- if not isinstance(parsed, dict):
- log('Malformed helpers, expected a JSON encoded dictionary but got a {0}. No helpers have been loaded. Please fix the setting manually.'.format(type(parsed)))
- return {}
-
- for key, value in parsed.items():
- if not isinstance(value, (str, unicode)):
- log('Helper "{0}" is not a string but a {1}. No helpers have been loaded. Please fix setting manually.'.format(key, type(value)))
- return {}
- return parsed
-
-class Config:
- ''' The autosort configuration. '''
-
- default_rules = json.dumps([
- '${core_first}',
- '${info:autosort_order,${info:autosort_escape,${script_or_plugin}},core,*,irc,bitlbee,matrix,slack}',
- '${script_or_plugin}',
- '${irc_raw_first}',
- '${server}',
- '${info:autosort_order,${type},server,*,channel,private}',
- '${hashless_name}',
- '${buffer.full_name}',
- ])
-
- default_helpers = json.dumps({
- 'core_first': '${if:${buffer.full_name}!=core.weechat}',
- 'irc_raw_first': '${if:${buffer.full_name}!=irc.irc_raw}',
- 'irc_raw_last': '${if:${buffer.full_name}==irc.irc_raw}',
- 'hashless_name': '${info:autosort_replace,#,,${info:autosort_escape,${buffer.name}}}',
- 'script_or_plugin': '${if:${script_name}?${script_name}:${plugin}}',
- })
-
- default_signal_delay = 5
- default_sort_limit = 100
-
- default_signals = 'buffer_opened buffer_merged buffer_unmerged buffer_renamed'
-
- def __init__(self, filename):
- ''' Initialize the configuration. '''
-
- self.filename = filename
- self.config_file = weechat.config_new(self.filename, '', '')
- self.sorting_section = None
- self.v3_section = None
-
- self.case_sensitive = False
- self.rules = []
- self.helpers = {}
- self.signals = []
- self.signal_delay = Config.default_signal_delay,
- self.sort_limit = Config.default_sort_limit,
- self.sort_on_config = True
- self.debug_log = False
-
- self.__case_sensitive = None
- self.__rules = None
- self.__helpers = None
- self.__signals = None
- self.__signal_delay = None
- self.__sort_limit = None
- self.__sort_on_config = None
- self.__debug_log = None
-
- if not self.config_file:
- log('Failed to initialize configuration file "{0}".'.format(self.filename))
- return
-
- self.sorting_section = weechat.config_new_section(self.config_file, 'sorting', False, False, '', '', '', '', '', '', '', '', '', '')
- self.v3_section = weechat.config_new_section(self.config_file, 'v3', False, False, '', '', '', '', '', '', '', '', '', '')
-
- if not self.sorting_section:
- log('Failed to initialize section "sorting" of configuration file.')
- weechat.config_free(self.config_file)
- return
-
- self.__case_sensitive = weechat.config_new_option(
- self.config_file, self.sorting_section,
- 'case_sensitive', 'boolean',
- 'If this option is on, sorting is case sensitive.',
- '', 0, 0, 'off', 'off', 0,
- '', '', '', '', '', ''
- )
-
- weechat.config_new_option(
- self.config_file, self.sorting_section,
- 'rules', 'string',
- 'Sort rules used by autosort v2.x and below. Not used by autosort anymore.',
- '', 0, 0, '', '', 0,
- '', '', '', '', '', ''
- )
-
- weechat.config_new_option(
- self.config_file, self.sorting_section,
- 'replacements', 'string',
- 'Replacement patterns used by autosort v2.x and below. Not used by autosort anymore.',
- '', 0, 0, '', '', 0,
- '', '', '', '', '', ''
- )
-
- self.__rules = weechat.config_new_option(
- self.config_file, self.v3_section,
- 'rules', 'string',
- 'An ordered list of sorting rules encoded as JSON. See /help autosort for commands to manipulate these rules.',
- '', 0, 0, Config.default_rules, Config.default_rules, 0,
- '', '', '', '', '', ''
- )
-
- self.__helpers = weechat.config_new_option(
- self.config_file, self.v3_section,
- 'helpers', 'string',
- 'A dictionary helper variables to use in the sorting rules, encoded as JSON. See /help autosort for commands to manipulate these helpers.',
- '', 0, 0, Config.default_helpers, Config.default_helpers, 0,
- '', '', '', '', '', ''
- )
-
- self.__signals = weechat.config_new_option(
- self.config_file, self.sorting_section,
- 'signals', 'string',
- 'A space separated list of signals that will cause autosort to resort your buffer list.',
- '', 0, 0, Config.default_signals, Config.default_signals, 0,
- '', '', '', '', '', ''
- )
-
- self.__signal_delay = weechat.config_new_option(
- self.config_file, self.sorting_section,
- 'signal_delay', 'integer',
- 'Delay in milliseconds to wait after a signal before sorting the buffer list. This prevents triggering many times if multiple signals arrive in a short time. It can also be needed to wait for buffer localvars to be available.',
- '', 0, 1000, str(Config.default_signal_delay), str(Config.default_signal_delay), 0,
- '', '', '', '', '', ''
- )
-
- self.__sort_limit = weechat.config_new_option(
- self.config_file, self.sorting_section,
- 'sort_limit', 'integer',
- 'Minimum delay in milliseconds to wait after sorting before signals can trigger a sort again. This is effectively a rate limit on sorting. Keeping signal_delay low while setting this higher can reduce excessive sorting without a long initial delay.',
- '', 0, 1000, str(Config.default_sort_limit), str(Config.default_sort_limit), 0,
- '', '', '', '', '', ''
- )
-
- self.__sort_on_config = weechat.config_new_option(
- self.config_file, self.sorting_section,
- 'sort_on_config_change', 'boolean',
- 'Decides if the buffer list should be sorted when autosort configuration changes.',
- '', 0, 0, 'on', 'on', 0,
- '', '', '', '', '', ''
- )
-
- self.__debug_log = weechat.config_new_option(
- self.config_file, self.sorting_section,
- 'debug_log', 'boolean',
- 'If enabled, print more debug messages. Not recommended for normal usage.',
- '', 0, 0, 'off', 'off', 0,
- '', '', '', '', '', ''
- )
-
- if weechat.config_read(self.config_file) != weechat.WEECHAT_RC_OK:
- log('Failed to load configuration file.')
-
- if weechat.config_write(self.config_file) != weechat.WEECHAT_RC_OK:
- log('Failed to write configuration file.')
-
- self.reload()
-
- def reload(self):
- ''' Load configuration variables. '''
-
- self.case_sensitive = weechat.config_boolean(self.__case_sensitive)
-
- rules_blob = weechat.config_string(self.__rules)
- helpers_blob = weechat.config_string(self.__helpers)
- signals_blob = weechat.config_string(self.__signals)
-
- self.rules = decode_rules(rules_blob)
- self.helpers = decode_helpers(helpers_blob)
- self.signals = signals_blob.split()
- self.signal_delay = weechat.config_integer(self.__signal_delay)
- self.sort_limit = weechat.config_integer(self.__sort_limit)
- self.sort_on_config = weechat.config_boolean(self.__sort_on_config)
- self.debug_log = weechat.config_boolean(self.__debug_log)
-
- def save_rules(self, run_callback = True):
- ''' Save the current rules to the configuration. '''
- weechat.config_option_set(self.__rules, json.dumps(self.rules), run_callback)
-
- def save_helpers(self, run_callback = True):
- ''' Save the current helpers to the configuration. '''
- weechat.config_option_set(self.__helpers, json.dumps(self.helpers), run_callback)
-
-
-def pad(sequence, length, padding = None):
- ''' Pad a list until is has a certain length. '''
- return sequence + [padding] * max(0, (length - len(sequence)))
-
-def log(message, buffer = 'NULL'):
- weechat.prnt(buffer, 'autosort: {0}'.format(message))
-
-def debug(message, buffer = 'NULL'):
- if config.debug_log:
- weechat.prnt(buffer, 'autosort: debug: {0}'.format(message))
-
-def get_buffers():
- ''' Get a list of all the buffers in weechat. '''
- hdata = weechat.hdata_get('buffer')
- buffer = weechat.hdata_get_list(hdata, "gui_buffers");
-
- result = []
- while buffer:
- number = weechat.hdata_integer(hdata, buffer, 'number')
- result.append((number, buffer))
- buffer = weechat.hdata_pointer(hdata, buffer, 'next_buffer')
- return hdata, result
-
-class MergedBuffers(list):
- """ A list of merged buffers, possibly of size 1. """
- def __init__(self, number):
- super(MergedBuffers, self).__init__()
- self.number = number
-
-def merge_buffer_list(buffers):
- '''
- Group merged buffers together.
- The output is a list of MergedBuffers.
- '''
- if not buffers: return []
- result = {}
- for number, buffer in buffers:
- if number not in result: result[number] = MergedBuffers(number)
- result[number].append(buffer)
- return result.values()
-
-def sort_buffers(hdata, buffers, rules, helpers, case_sensitive):
- for merged in buffers:
- for buffer in merged:
- name = weechat.hdata_string(hdata, buffer, 'name')
-
- return sorted(buffers, key=merged_sort_key(rules, helpers, case_sensitive))
-
-def buffer_sort_key(rules, helpers, case_sensitive):
- ''' Create a sort key function for a list of lists of merged buffers. '''
- def key(buffer):
- extra_vars = {}
- for helper_name, helper in sorted(helpers.items()):
- expanded = weechat.string_eval_expression(helper, {"buffer": buffer}, {}, {})
- extra_vars[helper_name] = expanded if case_sensitive else casefold(expanded)
- result = []
- for rule in rules:
- expanded = weechat.string_eval_expression(rule, {"buffer": buffer}, extra_vars, {})
- result.append(expanded if case_sensitive else casefold(expanded))
- return result
-
- return key
-
-def merged_sort_key(rules, helpers, case_sensitive):
- buffer_key = buffer_sort_key(rules, helpers, case_sensitive)
- def key(merged):
- best = None
- for buffer in merged:
- this = buffer_key(buffer)
- if best is None or this < best: best = this
- return best
- return key
-
-def apply_buffer_order(buffers):
- ''' Sort the buffers in weechat according to the given order. '''
- for i, buffer in enumerate(buffers):
- weechat.buffer_set(buffer[0], "number", str(i + 1))
-
-def split_args(args, expected, optional = 0):
- ''' Split an argument string in the desired number of arguments. '''
- split = args.split(' ', expected - 1)
- if (len(split) < expected):
- raise HumanReadableError('Expected at least {0} arguments, got {1}.'.format(expected, len(split)))
- return split[:-1] + pad(split[-1].split(' ', optional), optional + 1, '')
-
-def do_sort(verbose = False):
- start = perf_counter()
-
- hdata, buffers = get_buffers()
- buffers = merge_buffer_list(buffers)
- buffers = sort_buffers(hdata, buffers, config.rules, config.helpers, config.case_sensitive)
- apply_buffer_order(buffers)
-
- elapsed = perf_counter() - start
- if verbose:
- log("Finished sorting buffers in {0:.4f} seconds.".format(elapsed))
- else:
- debug("Finished sorting buffers in {0:.4f} seconds.".format(elapsed))
-
-def command_sort(buffer, command, args):
- ''' Sort the buffers and print a confirmation. '''
- do_sort(True)
- return weechat.WEECHAT_RC_OK
-
-def command_debug(buffer, command, args):
- hdata, buffers = get_buffers()
- buffers = merge_buffer_list(buffers)
-
- # Show evaluation results.
- log('Individual evaluation results:')
- start = perf_counter()
- key = buffer_sort_key(config.rules, config.helpers, config.case_sensitive)
- results = []
- for merged in buffers:
- for buffer in merged:
- fullname = weechat.hdata_string(hdata, buffer, 'full_name')
- results.append((fullname, key(buffer)))
- elapsed = perf_counter() - start
-
- for fullname, result in results:
- fullname = ensure_str(fullname)
- result = [ensure_str(x) for x in result]
- log('{0}: {1}'.format(fullname, result))
- log('Computing evaluation results took {0:.4f} seconds.'.format(elapsed))
-
- return weechat.WEECHAT_RC_OK
-
-def command_rule_list(buffer, command, args):
- ''' Show the list of sorting rules. '''
- output = 'Sorting rules:\n'
- for i, rule in enumerate(config.rules):
- output += ' {0}: {1}\n'.format(i, rule)
- if not len(config.rules):
- output += ' No sorting rules configured.\n'
- log(output )
-
- return weechat.WEECHAT_RC_OK
-
-
-def command_rule_add(buffer, command, args):
- ''' Add a rule to the rule list. '''
- config.rules.append(args)
- config.save_rules()
- command_rule_list(buffer, command, '')
-
- return weechat.WEECHAT_RC_OK
-
-
-def command_rule_insert(buffer, command, args):
- ''' Insert a rule at the desired position in the rule list. '''
- index, rule = split_args(args, 2)
- index = parse_int(index, 'index')
-
- config.rules.insert(index, rule)
- config.save_rules()
- command_rule_list(buffer, command, '')
- return weechat.WEECHAT_RC_OK
-
-
-def command_rule_update(buffer, command, args):
- ''' Update a rule in the rule list. '''
- index, rule = split_args(args, 2)
- index = parse_int(index, 'index')
-
- config.rules[index] = rule
- config.save_rules()
- command_rule_list(buffer, command, '')
- return weechat.WEECHAT_RC_OK
-
-
-def command_rule_delete(buffer, command, args):
- ''' Delete a rule from the rule list. '''
- index = args.strip()
- index = parse_int(index, 'index')
-
- config.rules.pop(index)
- config.save_rules()
- command_rule_list(buffer, command, '')
- return weechat.WEECHAT_RC_OK
-
-
-def command_rule_move(buffer, command, args):
- ''' Move a rule to a new position. '''
- index_a, index_b = split_args(args, 2)
- index_a = parse_int(index_a, 'index')
- index_b = parse_int(index_b, 'index')
-
- list_move(config.rules, index_a, index_b)
- config.save_rules()
- command_rule_list(buffer, command, '')
- return weechat.WEECHAT_RC_OK
-
-
-def command_rule_swap(buffer, command, args):
- ''' Swap two rules. '''
- index_a, index_b = split_args(args, 2)
- index_a = parse_int(index_a, 'index')
- index_b = parse_int(index_b, 'index')
-
- list_swap(config.rules, index_a, index_b)
- config.save_rules()
- command_rule_list(buffer, command, '')
- return weechat.WEECHAT_RC_OK
-
-
-def command_helper_list(buffer, command, args):
- ''' Show the list of helpers. '''
- output = 'Helper variables:\n'
-
- width = max(map(lambda x: len(x) if len(x) <= 30 else 0, config.helpers.keys()))
-
- for name, expression in sorted(config.helpers.items()):
- output += ' {0:>{width}}: {1}\n'.format(name, expression, width=width)
- if not len(config.helpers):
- output += ' No helper variables configured.'
- log(output)
-
- return weechat.WEECHAT_RC_OK
-
-
-def command_helper_set(buffer, command, args):
- ''' Add/update a helper to the helper list. '''
- name, expression = split_args(args, 2)
-
- config.helpers[name] = expression
- config.save_helpers()
- command_helper_list(buffer, command, '')
-
- return weechat.WEECHAT_RC_OK
-
-def command_helper_delete(buffer, command, args):
- ''' Delete a helper from the helper list. '''
- name = args.strip()
-
- del config.helpers[name]
- config.save_helpers()
- command_helper_list(buffer, command, '')
- return weechat.WEECHAT_RC_OK
-
-
-def command_helper_rename(buffer, command, args):
- ''' Rename a helper to a new position. '''
- old_name, new_name = split_args(args, 2)
-
- try:
- config.helpers[new_name] = config.helpers[old_name]
- del config.helpers[old_name]
- except KeyError:
- raise HumanReadableError('No such helper: {0}'.format(old_name))
- config.save_helpers()
- command_helper_list(buffer, command, '')
- return weechat.WEECHAT_RC_OK
-
-
-def command_helper_swap(buffer, command, args):
- ''' Swap two helpers. '''
- a, b = split_args(args, 2)
- try:
- config.helpers[b], config.helpers[a] = config.helpers[a], config.helpers[b]
- except KeyError as e:
- raise HumanReadableError('No such helper: {0}'.format(e.args[0]))
-
- config.helpers.swap(index_a, index_b)
- config.save_helpers()
- command_helper_list(buffer, command, '')
- return weechat.WEECHAT_RC_OK
-
-def call_command(buffer, command, args, subcommands):
- ''' Call a subcommand from a dictionary. '''
- subcommand, tail = pad(args.split(' ', 1), 2, '')
- subcommand = subcommand.strip()
- if (subcommand == ''):
- child = subcommands.get(' ')
- else:
- command = command + [subcommand]
- child = subcommands.get(subcommand)
-
- if isinstance(child, dict):
- return call_command(buffer, command, tail, child)
- elif callable(child):
- return child(buffer, command, tail)
-
- log('{0}: command not found'.format(' '.join(command)))
- return weechat.WEECHAT_RC_ERROR
-
-def on_signal(data, signal, signal_data):
- global signal_delay_timer
- global sort_queued
-
- # If the sort limit timeout is started, we're in the hold-off time after sorting, just queue a sort.
- if sort_limit_timer is not None:
- if sort_queued:
- debug('Signal {0} ignored, sort limit timeout is active and sort is already queued.'.format(signal))
- else:
- debug('Signal {0} received but sort limit timeout is active, sort is now queued.'.format(signal))
- sort_queued = True
- return weechat.WEECHAT_RC_OK
-
- # If the signal delay timeout is started, a signal was recently received, so ignore this signal.
- if signal_delay_timer is not None:
- debug('Signal {0} ignored, signal delay timeout active.'.format(signal))
- return weechat.WEECHAT_RC_OK
-
- # Otherwise, start the signal delay timeout.
- debug('Signal {0} received, starting signal delay timeout of {1} ms.'.format(signal, config.signal_delay))
- weechat.hook_timer(config.signal_delay, 0, 1, "on_signal_delay_timeout", "")
- return weechat.WEECHAT_RC_OK
-
-def on_signal_delay_timeout(pointer, remaining_calls):
- """ Called when the signal_delay_timer triggers. """
- global signal_delay_timer
- global sort_limit_timer
- global sort_queued
-
- signal_delay_timer = None
-
- # If the sort limit timeout was started, we're still in the no-sort period, so just queue a sort.
- if sort_limit_timer is not None:
- debug('Signal delay timeout expired, but sort limit timeout is active, sort is now queued.')
- sort_queued = True
- return weechat.WEECHAT_RC_OK
-
- # Time to sort!
- debug('Signal delay timeout expired, starting sort.')
- do_sort()
-
- # Start the sort limit timeout if not disabled.
- if config.sort_limit > 0:
- debug('Starting sort limit timeout of {0} ms.'.format(config.sort_limit))
- sort_limit_timer = weechat.hook_timer(config.sort_limit, 0, 1, "on_sort_limit_timeout", "")
-
- return weechat.WEECHAT_RC_OK
-
-def on_sort_limit_timeout(pointer, remainin_calls):
- """ Called when de sort_limit_timer triggers. """
- global sort_limit_timer
- global sort_queued
-
- # If no signal was received during the timeout, we're done.
- if not sort_queued:
- debug('Sort limit timeout expired without receiving a signal.')
- sort_limit_timer = None
- return weechat.WEECHAT_RC_OK
-
- # Otherwise it's time to sort.
- debug('Signal received during sort limit timeout, starting queued sort.')
- do_sort()
- sort_queued = False
-
- # Start the sort limit timeout again if not disabled.
- if config.sort_limit > 0:
- debug('Starting sort limit timeout of {0} ms.'.format(config.sort_limit))
- sort_limit_timer = weechat.hook_timer(config.sort_limit, 0, 1, "on_sort_limit_timeout", "")
-
- return weechat.WEECHAT_RC_OK
-
-
-def apply_config():
- # Unhook all signals and hook the new ones.
- for hook in hooks:
- weechat.unhook(hook)
- for signal in config.signals:
- hooks.append(weechat.hook_signal(signal, 'on_signal', ''))
-
- if config.sort_on_config:
- debug('Sorting because configuration changed.')
- do_sort()
-
-def on_config_changed(*args, **kwargs):
- ''' Called whenever the configuration changes. '''
- config.reload()
- apply_config()
-
- return weechat.WEECHAT_RC_OK
-
-def parse_arg(args):
- if not args: return '', None
-
- result = ''
- escaped = False
- for i, c in enumerate(args):
- if not escaped:
- if c == '\\':
- escaped = True
- continue
- elif c == ',':
- return result, args[i+1:]
- result += c
- escaped = False
- return result, None
-
-def parse_args(args, max = None):
- result = []
- i = 0
- while max is None or i < max:
- i += 1
- arg, args = parse_arg(args)
- if arg is None: break
- result.append(arg)
- if args is None: break
- return result, args
-
-def on_info_escape(pointer, name, arguments):
- result = ''
- for c in arguments:
- if c == '\\':
- result += '\\\\'
- elif c == ',':
- result += '\\,'
- else:
- result +=c
- return result
-
-def on_info_replace(pointer, name, arguments):
- arguments, rest = parse_args(arguments, 3)
- if rest or len(arguments) < 3:
- log('usage: ${{info:{0},old,new,text}}'.format(name))
- return ''
- old, new, text = arguments
-
- return text.replace(old, new)
-
-def on_info_order(pointer, name, arguments):
- arguments, rest = parse_args(arguments)
- if len(arguments) < 1:
- log('usage: ${{info:{0},value,first,second,third,...}}'.format(name))
- return ''
-
- value = arguments[0]
- keys = arguments[1:]
- if not keys: return '0'
-
- # Find the value in the keys (or '*' if we can't find it)
- result = list_find(keys, value)
- if result is None: result = list_find(keys, '*')
- if result is None: result = len(keys)
-
- # Pad result with leading zero to make sure string sorting works.
- width = int(math.log10(len(keys))) + 1
- return '{0:0{1}}'.format(result, width)
-
-
-def on_autosort_command(data, buffer, args):
- ''' Called when the autosort command is invoked. '''
- try:
- return call_command(buffer, ['/autosort'], args, {
- ' ': command_sort,
- 'sort': command_sort,
- 'debug': command_debug,
-
- 'rules': {
- ' ': command_rule_list,
- 'list': command_rule_list,
- 'add': command_rule_add,
- 'insert': command_rule_insert,
- 'update': command_rule_update,
- 'delete': command_rule_delete,
- 'move': command_rule_move,
- 'swap': command_rule_swap,
- },
- 'helpers': {
- ' ': command_helper_list,
- 'list': command_helper_list,
- 'set': command_helper_set,
- 'delete': command_helper_delete,
- 'rename': command_helper_rename,
- 'swap': command_helper_swap,
- },
- })
- except HumanReadableError as e:
- log(e)
- return weechat.WEECHAT_RC_ERROR
-
-def add_completions(completion, words):
- for word in words:
- weechat.hook_completion_list_add(completion, word, 0, weechat.WEECHAT_LIST_POS_END)
-
-def autosort_complete_rules(words, completion):
- if len(words) == 0:
- add_completions(completion, ['add', 'delete', 'insert', 'list', 'move', 'swap', 'update'])
- if len(words) == 1 and words[0] in ('delete', 'insert', 'move', 'swap', 'update'):
- add_completions(completion, map(str, range(len(config.rules))))
- if len(words) == 2 and words[0] in ('move', 'swap'):
- add_completions(completion, map(str, range(len(config.rules))))
- if len(words) == 2 and words[0] in ('update'):
- try:
- add_completions(completion, [config.rules[int(words[1])]])
- except KeyError: pass
- except ValueError: pass
- else:
- add_completions(completion, [''])
- return weechat.WEECHAT_RC_OK
-
-def autosort_complete_helpers(words, completion):
- if len(words) == 0:
- add_completions(completion, ['delete', 'list', 'rename', 'set', 'swap'])
- elif len(words) == 1 and words[0] in ('delete', 'rename', 'set', 'swap'):
- add_completions(completion, sorted(config.helpers.keys()))
- elif len(words) == 2 and words[0] == 'swap':
- add_completions(completion, sorted(config.helpers.keys()))
- elif len(words) == 2 and words[0] == 'rename':
- add_completions(completion, sorted(config.helpers.keys()))
- elif len(words) == 2 and words[0] == 'set':
- try:
- add_completions(completion, [config.helpers[words[1]]])
- except KeyError: pass
- return weechat.WEECHAT_RC_OK
-
-def on_autosort_complete(data, name, buffer, completion):
- cmdline = weechat.buffer_get_string(buffer, "input")
- cursor = weechat.buffer_get_integer(buffer, "input_pos")
- prefix = cmdline[:cursor]
- words = prefix.split()[1:]
-
- # If the current word isn't finished yet,
- # ignore it for coming up with completion suggestions.
- if prefix[-1] != ' ': words = words[:-1]
-
- if len(words) == 0:
- add_completions(completion, ['debug', 'helpers', 'rules', 'sort'])
- elif words[0] == 'rules':
- return autosort_complete_rules(words[1:], completion)
- elif words[0] == 'helpers':
- return autosort_complete_helpers(words[1:], completion)
- return weechat.WEECHAT_RC_OK
-
-command_description = r'''{*white}# General commands{reset}
-
-{*white}/autosort {brown}sort{reset}
-Manually trigger the buffer sorting.
-
-{*white}/autosort {brown}debug{reset}
-Show the evaluation results of the sort rules for each buffer.
-
-
-{*white}# Sorting rule commands{reset}
-
-{*white}/autosort{brown} rules list{reset}
-Print the list of sort rules.
-
-{*white}/autosort {brown}rules add {cyan}<expression>{reset}
-Add a new rule at the end of the list.
-
-{*white}/autosort {brown}rules insert {cyan}<index> <expression>{reset}
-Insert a new rule at the given index in the list.
-
-{*white}/autosort {brown}rules update {cyan}<index> <expression>{reset}
-Update a rule in the list with a new expression.
-
-{*white}/autosort {brown}rules delete {cyan}<index>
-Delete a rule from the list.
-
-{*white}/autosort {brown}rules move {cyan}<index_from> <index_to>{reset}
-Move a rule from one position in the list to another.
-
-{*white}/autosort {brown}rules swap {cyan}<index_a> <index_b>{reset}
-Swap two rules in the list
-
-
-{*white}# Helper variable commands{reset}
-
-{*white}/autosort {brown}helpers list
-Print the list of helper variables.
-
-{*white}/autosort {brown}helpers set {cyan}<name> <expression>
-Add or update a helper variable with the given name.
-
-{*white}/autosort {brown}helpers delete {cyan}<name>
-Delete a helper variable.
-
-{*white}/autosort {brown}helpers rename {cyan}<old_name> <new_name>
-Rename a helper variable.
-
-{*white}/autosort {brown}helpers swap {cyan}<name_a> <name_b>
-Swap the expressions of two helper variables in the list.
-
-
-{*white}# Info hooks{reset}
-Autosort comes with a number of info hooks to add some extra functionality to regular weechat eval strings.
-Info hooks can be used in eval strings in the form of {cyan}${{info:some_hook,arguments}}{reset}.
-
-Commas and backslashes in arguments to autosort info hooks (except for {cyan}${{info:autosort_escape}}{reset}) must be escaped with a backslash.
-
-{*white}${{info:{brown}autosort_replace{white},{cyan}pattern{white},{cyan}replacement{white},{cyan}source{white}}}{reset}
-Replace all occurrences of {cyan}pattern{reset} with {cyan}replacement{reset} in the string {cyan}source{reset}.
-Can be used to ignore certain strings when sorting by replacing them with an empty string.
-
-For example: {cyan}${{info:autosort_replace,cat,dog,the dog is meowing}}{reset} expands to "the cat is meowing".
-
-{*white}${{info:{brown}autosort_order{white},{cyan}value{white},{cyan}option0{white},{cyan}option1{white},{cyan}option2{white},{cyan}...{white}}}
-Generate a zero-padded number that corresponds to the index of {cyan}value{reset} in the list of options.
-If one of the options is the special value {brown}*{reset}, then any value not explicitly mentioned will be sorted at that position.
-Otherwise, any value that does not match an option is assigned the highest number available.
-Can be used to easily sort buffers based on a manual sequence.
-
-For example: {cyan}${{info:autosort_order,${{server}},freenode,oftc,efnet}}{reset} will sort freenode before oftc, followed by efnet and then any remaining servers.
-Alternatively, {cyan}${{info:autosort_order,${{server}},freenode,oftc,*,efnet}}{reset} will sort any unlisted servers after freenode and oftc, but before efnet.
-
-{*white}${{info:{brown}autosort_escape{white},{cyan}text{white}}}{reset}
-Escape commas and backslashes in {cyan}text{reset} by prepending them with a backslash.
-This is mainly useful to pass arbitrary eval strings as arguments to other autosort info hooks.
-Otherwise, an eval string that expands to something with a comma would be interpreted as multiple arguments.
-
-For example, it can be used to safely pass buffer names to {cyan}${{info:autosort_replace}}{reset} like so:
-{cyan}${{info:autosort_replace,##,#,${{info:autosort_escape,${{buffer.name}}}}}}{reset}.
-
-
-{*white}# Description
-Autosort is a weechat script to automatically keep your buffers sorted. The sort
-order can be customized by defining your own sort rules, but the default should
-be sane enough for most people. It can also group IRC channel/private buffers
-under their server buffer if you like.
-
-Autosort uses a stable sorting algorithm, meaning that you can manually move buffers
-to change their relative order, if they sort equal with your rule set.
-
-{*white}# Sort rules{reset}
-Autosort evaluates a list of eval expressions (see {*default}/help eval{reset}) and sorts the
-buffers based on evaluated result. Earlier rules will be considered first. Only
-if earlier rules produced identical results is the result of the next rule
-considered for sorting purposes.
-
-You can debug your sort rules with the `{*default}/autosort debug{reset}` command, which will
-print the evaluation results of each rule for each buffer.
-
-{*brown}NOTE:{reset} The sort rules for version 3 are not compatible with version 2 or vice
-versa. You will have to manually port your old rules to version 3 if you have any.
-
-{*white}# Helper variables{reset}
-You may define helper variables for the main sort rules to keep your rules
-readable. They can be used in the main sort rules as variables. For example,
-a helper variable named `{cyan}foo{reset}` can be accessed in a main rule with the
-string `{cyan}${{foo}}{reset}`.
-
-{*white}# Automatic or manual sorting{reset}
-By default, autosort will automatically sort your buffer list whenever a buffer
-is opened, merged, unmerged or renamed. This should keep your buffers sorted in
-almost all situations. However, you may wish to change the list of signals that
-cause your buffer list to be sorted. Simply edit the `{cyan}autosort.sorting.signals{reset}`
-option to add or remove any signal you like.
-
-If you remove all signals you can still sort your buffers manually with the
-`{*default}/autosort sort{reset}` command. To prevent all automatic sorting, the option
-`{cyan}autosort.sorting.sort_on_config_change{reset}` should also be disabled.
-
-{*white}# Recommended settings
-For the best visual effect, consider setting the following options:
- {*white}/set {cyan}irc.look.server_buffer{reset} {brown}independent{reset}
-
-This setting allows server buffers to be sorted independently, which is
-needed to create a hierarchical tree view of the server and channel buffers.
-
-If you are using the {*default}buflist{reset} plugin you can (ab)use Unicode to draw a tree
-structure with the following setting (modify to suit your need):
- {*white}/set {cyan}buflist.format.indent {brown}"${{color:237}}${{if:${{buffer.next_buffer.local_variables.type}}=~^(channel|private)$?├─:└─}}"{reset}
-'''
-
-command_completion = '%(plugin_autosort) %(plugin_autosort) %(plugin_autosort) %(plugin_autosort) %(plugin_autosort)'
-
-info_replace_description = (
- 'Replace all occurrences of `pattern` with `replacement` in the string `source`. '
- 'Can be used to ignore certain strings when sorting by replacing them with an empty string. '
- 'See /help autosort for examples.'
-)
-info_replace_arguments = 'pattern,replacement,source'
-
-info_order_description = (
- 'Generate a zero-padded number that corresponds to the index of `value` in the list of options. '
- 'If one of the options is the special value `*`, then any value not explicitly mentioned will be sorted at that position. '
- 'Otherwise, any value that does not match an option is assigned the highest number available. '
- 'Can be used to easily sort buffers based on a manual sequence. '
- 'See /help autosort for examples.'
-)
-info_order_arguments = 'value,first,second,third,...'
-
-info_escape_description = (
- 'Escape commas and backslashes in `text` by prepending them with a backslash. '
- 'This is mainly useful to pass arbitrary eval strings as arguments to other autosort info hooks. '
- 'Otherwise, an eval string that expands to something with a comma would be interpreted as multiple arguments.'
- 'See /help autosort for examples.'
-)
-info_escape_arguments = 'text'
-
-
-if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, "", ""):
- config = Config('autosort')
-
- colors = {
- 'default': weechat.color('default'),
- 'reset': weechat.color('reset'),
- 'black': weechat.color('black'),
- 'red': weechat.color('red'),
- 'green': weechat.color('green'),
- 'brown': weechat.color('brown'),
- 'yellow': weechat.color('yellow'),
- 'blue': weechat.color('blue'),
- 'magenta': weechat.color('magenta'),
- 'cyan': weechat.color('cyan'),
- 'white': weechat.color('white'),
- '*default': weechat.color('*default'),
- '*black': weechat.color('*black'),
- '*red': weechat.color('*red'),
- '*green': weechat.color('*green'),
- '*brown': weechat.color('*brown'),
- '*yellow': weechat.color('*yellow'),
- '*blue': weechat.color('*blue'),
- '*magenta': weechat.color('*magenta'),
- '*cyan': weechat.color('*cyan'),
- '*white': weechat.color('*white'),
- }
-
- weechat.hook_config('autosort.*', 'on_config_changed', '')
- weechat.hook_completion('plugin_autosort', '', 'on_autosort_complete', '')
- weechat.hook_command('autosort', command_description.format(**colors), '', '', command_completion, 'on_autosort_command', '')
- weechat.hook_info('autosort_escape', info_escape_description, info_escape_arguments, 'on_info_escape', '')
- weechat.hook_info('autosort_replace', info_replace_description, info_replace_arguments, 'on_info_replace', '')
- weechat.hook_info('autosort_order', info_order_description, info_order_arguments, 'on_info_order', '')
-
- apply_config()
--- a/weechat/python/colon_complete.py Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-SCRIPT_NAME='coloncomplete'
-SCRIPT_AUTHOR='Steve Losh <steve@stevelosh.com>'
-SCRIPT_VERSION='1.0'
-SCRIPT_LICENSE='MIT/X11'
-SCRIPT_DESC='Add a colon after nick completion when all the previous words in the input are also nicks.'
-
-EXTRA_NICKS = ['all', 'backend', 'clojerks', 'ops', 'support']
-
-import_ok=True
-
-try:
- import weechat
-except ImportError:
- print 'This script must be run under WeeChat'
- print 'You can obtain a copy of WeeChat, for free, at http://www.weechat.org'
- import_ok=False
-
-weechat_version=0
-
-def get_nicks(buffer, prefix=''):
- channel = weechat.buffer_get_string(buffer, 'localvar_channel')
- server = weechat.buffer_get_string(buffer, 'localvar_server')
- prefix = prefix.lower()
-
- matches = []
-
- infolist = weechat.infolist_get('irc_nick', '', '%s,%s' % (server, channel))
- while weechat.infolist_next(infolist):
- nick = weechat.infolist_string(infolist, 'name')
- if nick != 'localhost' and nick.lower().startswith(prefix):
- matches.append(nick)
- weechat.infolist_free(infolist)
-
- for nick in EXTRA_NICKS:
- if nick.lower().startswith(prefix):
- matches.append(nick)
-
- return matches
-
-def completer(data, buffer, command):
- cb = weechat.current_buffer()
- if command == "/input complete_next":
- line = weechat.buffer_get_string(cb, "input")
- words = line.split(' ')
- prefix = words[-1]
- if prefix and words and all([s.endswith(':') for s in words[:-1] if s]):
- nicks = get_nicks(cb, prefix)
- if len(nicks) == 1:
- for _ in range(len(prefix)):
- weechat.command(buffer, "/input delete_previous_char")
- weechat.command(buffer, "/input insert " + nicks[-1] + ":\\x20")
- elif len(nicks) > 1:
- l = min(len(nick) for nick in nicks)
- for i in range(len(prefix), l):
- if len(set(nick[i] for nick in nicks)) > 1:
- break
- else:
- weechat.command(buffer, "/input insert " + nicks[0][i])
-
- for nick in nicks:
- weechat.prnt(cb, "==> " + nick)
- return weechat.WEECHAT_RC_OK_EAT
-
- return weechat.WEECHAT_RC_OK
-
-if __name__ == "__main__" and import_ok:
- if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, "", ""):
- weechat_version = weechat.info_get("version_number", "") or 0
- weechat.hook_command_run('/input complete*', 'completer', '')
--- a/weechat/python/listbuffer.py Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,467 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# ListBuffer, version 0.8.1 for WeeChat version 0.3
-# Latest development version: https://github.com/FiXato/listbuffer
-#
-# Show /list results in a common buffer and interact with them.
-#
-# This script allows you to easily join channels from the /list output.
-# It will open a common buffer for the /list result, through which you
-# browse with your cursor keys, and join with the meta-enter keys.
-# Adjust sorting with meta->, meta-< and meta-/ keybindings.
-#
-## History:
-### 2011-09-08: FiXato:
-#
-# * version 0.1: initial release.
-# * added a common buffer for /list results
-# * added highlighting for currently selected line
-# * added /join support via enter key
-# * added scroll_top and scroll_bottom support
-#
-# * version 0.2: /list format bugfix
-# * added support for /list results without modes
-# * some servers don't send 321 (/list start). Taken into account.
-#
-# * version 0.3: Sorting support
-# * Added some basic sorting support. Scroll through sort options
-# with meta-> and meta-< (users, channel, topic, modes)
-#
-### 2011-09-19: FiXato
-#
-# * version 0.4:
-# * Case-insensitive buffer lookup fix.
-# * Removed default enter keybind
-#
-### 2011-12-28: troydm:
-#
-# * version 0.5: It's an upside-down-world
-# * Added inverted sorting support provided by Dmitry "troydm" Geurkov
-# Use meta-/ to switch between inverted and regular sorting.
-#
-### 2012-02-10: FiXato:
-#
-# * version 0.6: Stop shoving that buffer in my face!
-# * The listbuffer should no longer pop up by itself when you load the script.
-# It should only pop up now when you actually do a /list query.
-#
-# * version 0.7: .. but please pop it up in my current window when I ask for it
-# * Added setting plugins.var.python.listbuffer.autofocus
-# This will autofocus the listbuffer in the current window if another window isn't
-# already showing it, and of course only when the user issues /list
-#
-### 2012-07-10: FiXato:
-#
-# * version 0.7.1: Forgetful bugfix
-# * Made sure lb_curline global variable is defined
-#
-### 2013-03-19: FiXato:
-#
-# * version 0.8: Sorted out the sorting
-# * Added automatically updating options for sorting:
-# * plugins.var.python.listbuffer.sort_inverted
-# * plugins.var.python.listbuffer.sort_order
-# * version 0.8.1: Pad it baby!
-# * Channel modes are equally padded even when there are no channel modes.
-# * Added padding options:
-# * plugins.var.python.listbuffer.modes_min_width
-# * plugins.var.python.listbuffer.channel_min_width
-# * plugins.var.python.listbuffer.users_min_width
-#
-## Acknowledgements:
-# * Dmitry "troydm" Geurkov, for providing the inverse-sorting patch to the project.
-# * Sebastien "Flashcode" Helleu, for developing the kick-ass IRC client WeeChat
-# and the iset.pl script which inspired me to this script.
-# * Nils "nils_2" Görs, for his contributions to iset.pl which served as
-# example code.
-# * David "drubin" Rubin, for his urlgrab.py script, which also served
-# as example code.
-# * ArZa, whose listsort.pl script helped me getting started with
-# grabbing the /list results. Parts of his code have been shamelessly
-# copied and ported to Python.
-# * Khaled Mardam-Bey, for making me yearn for similar /list support in
-# WeeChat as mIRC already offered. :P
-# * mave_, for pointing out that sort orders weren't remembered.
-#
-## TODO:
-# - Auto-scroll selected line upon window scroll.
-# - Add option to hide already joined channels.
-# - Improve sorting methods
-# - Add auto-join support
-# - Detect if channel is already in auto-join
-# - Allow automatically switching to the listbuffer
-# - Add support for ALIS (/squery alis LIST * -mix 100 (IRCNet)
-# - Make colours configurable
-# - Limit number of channels to parse
-# - Add filter support a la iset
-# - Allow selecting multiple channels
-# - Add optional command redirection.
-#
-## Copyright (c) 2011,2012,2013 Filip H.F. "FiXato" Slagter,
-# <FiXato [at] Gmail [dot] com>
-# http://profile.fixato.org
-#
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to
-# permit persons to whom the Software is furnished to do so, subject to
-# the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-# NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-SCRIPT_NAME = "listbuffer"
-SCRIPT_AUTHOR = "Filip H.F. 'FiXato' Slagter <fixato [at] gmail [dot] com>"
-SCRIPT_VERSION = "0.8.1"
-SCRIPT_LICENSE = "MIT"
-SCRIPT_DESC = "A common buffer for /list output."
-SCRIPT_COMMAND = "listbuffer"
-
-import_ok = True
-
-try:
- import weechat
-except ImportError:
- print "This script must be run under WeeChat."
- import_ok = False
-
-import re
-
-lb_settings = (
- ("autofocus", "on", "Focus the listbuffer in the current window if it isn't already displayed by a window."),
- ("sort_order", "users", "Last used sort order for the channel list."),
- ("sort_inverted", "on", "Invert the sort order for the channel list."),
- ("modes_min_width", "8", "The minimum width used for modes in the channel list. If a channel has less modes than this amount, the column will be padded with spaces."),
- ("channel_min_width", "25", "The minimum width used for the channel name in the channel list. If a channelname is shorter than this amount, the column will be padded with spaces."),
- ("users_min_width", "8", "The minimum width used for the usercount in the channel list. If the usercount has less digits than this amount, the column will be padded with spaces."),
-)
-lb_buffer = None
-lb_curline = 0
-lb_channels = []
-lb_network = None
-lb_list_started = False
-lb_current_sort = None
-lb_sort_inverted = False
-lb_sort_options = (
- 'channel',
- 'users',
- 'modes',
- 'topic',
-)
-
-# server numeric Nick Chan Users Modes Topic
-lb_channel_list_expression = '(:\S+) (\d{3}) (\S+) (\S+) (\d+) :(\[(.*?)\] )?(.*)'
-
-# Create listbuffer.
-def lb_create_buffer():
- global lb_buffer, lb_curline
-
- if not lb_buffer:
- lb_buffer = weechat.buffer_new("listbuffer", "lb_input_cb", \
- "", "lb_close_cb", "")
- lb_set_buffer_title()
- # Sets notify to 0 as this buffer does not need to be in hotlist.
- weechat.buffer_set(lb_buffer, "notify", "0")
- weechat.buffer_set(lb_buffer, "nicklist", "0")
- weechat.buffer_set(lb_buffer, "type", "free")
- weechat.buffer_set(lb_buffer, "key_bind_ctrl-L", "/listbuffer **refresh")
- weechat.buffer_set(lb_buffer, "key_bind_meta2-A", "/listbuffer **up")
- weechat.buffer_set(lb_buffer, "key_bind_meta2-B", "/listbuffer **down")
- weechat.buffer_set(lb_buffer, "key_bind_meta2-1~", "/listbuffer **scroll_top")
- weechat.buffer_set(lb_buffer, "key_bind_meta2-4~", "/listbuffer **scroll_bottom")
- weechat.buffer_set(lb_buffer, "key_bind_meta-ctrl-J", "/listbuffer **enter")
- weechat.buffer_set(lb_buffer, "key_bind_meta-ctrl-M", "/listbuffer **enter")
- weechat.buffer_set(lb_buffer, "key_bind_meta->", "/listbuffer **sort_next")
- weechat.buffer_set(lb_buffer, "key_bind_meta-<", "/listbuffer **sort_previous")
- weechat.buffer_set(lb_buffer, "key_bind_meta-/", "/listbuffer **sort_invert")
- lb_curline = 0
- if weechat.config_get_plugin("autofocus") == "on":
- if not weechat.window_search_with_buffer(lb_buffer):
- weechat.command("", "/buffer " + weechat.buffer_get_string(lb_buffer,"name"))
-
-def lb_set_buffer_title():
- global lb_buffer, lb_current_sort
- ascdesc = '(v)' if lb_sort_inverted else '(^)'
- weechat.buffer_set(lb_buffer, "title", lb_line_format({
- 'channel': 'Channel name%s' % (ascdesc if lb_current_sort == 'channel' else ''),
- 'users': 'Users%s' % (ascdesc if lb_current_sort == 'users' else ''),
- 'modes': 'Modes%s' % (ascdesc if lb_current_sort == 'modes' else ''),
- 'topic': 'Topic%s' % (ascdesc if lb_current_sort == 'topic' else ''),
- 'nomodes': None,
- }))
-
-def lb_list_start(data, signal, message):
- lb_initialise_list
-
- return weechat.WEECHAT_RC_OK
-
-def lb_initialise_list(signal):
- global lb_channels, lb_network, lb_list_started
-
- lb_create_buffer()
- lb_channels = []
- lb_network = signal.split(',')[0]
- lb_list_started = True
- return
-
-
-def lb_list_chan(data, signal, message):
- global lb_channels, lb_buffer, lb_list_started
-
- # Work-around for IRCds which don't send 321 Numeric (/List start)
- if not lb_list_started:
- lb_initialise_list(signal)
-
- for chan_data in re.findall(lb_channel_list_expression,message):
- lb_channels.append({
- 'server': chan_data[0][1:-1],
- 'numeric': chan_data[1],
- 'nick': chan_data[2],
- 'channel': chan_data[3],
- 'users': chan_data[4],
- 'nomodes': chan_data[5] == '',
- 'modes': chan_data[6],
- 'topic': weechat.hook_modifier_exec("irc_color_decode", "1", chan_data[7])
- })
- return weechat.WEECHAT_RC_OK
-
-def lb_list_end(data, signal, message):
- global lb_list_started
-
- # Work-around for IRCds which don't send 321 Numeric (/List start)
- if not lb_list_started:
- lb_initialise_list(signal)
-
- lb_list_started = False
- if lb_current_sort:
- lb_sort()
- lb_refresh()
- return weechat.WEECHAT_RC_OK
-
-def keyEvent (data, buffer, args):
- global lb_options
- lb_options[args]()
-
-def lb_input_cb(data, buffer, input_data):
- global lb_options, lb_curline
- lb_options[input_data]()
- return weechat.WEECHAT_RC_OK
-
-def lb_refresh():
- global lb_channels, lb_buffer
- weechat.buffer_clear(lb_buffer)
-
- y = 0
- for list_data in lb_channels:
- lb_refresh_line(y)
- y += 1
- return
-
-def lb_refresh_line(y):
- global lb_buffer, lb_curline, lb_channels
- if y >= 0 and y < len(lb_channels):
- formatted_line = lb_line_format(lb_channels[y], y == lb_curline)
- weechat.prnt_y(lb_buffer, y, formatted_line)
-
-def lb_refresh_curline():
- global lb_curline
- lb_refresh_line(lb_curline-1)
- lb_refresh_line(lb_curline)
- lb_refresh_line(lb_curline+1)
- return
-
-def lb_line_format(list_data,curr=False):
- str = ""
- if (curr):
- str += weechat.color("yellow,red")
- channel_text = list_data['channel'].ljust(int(weechat.config_get_plugin('channel_min_width')))
- users_text = "(%s)" % list_data['users']
- padded_users_text = users_text.rjust(int(weechat.config_get_plugin('users_min_width')) + 2)
- str += "%s%s %s " % (weechat.color("bold"), channel_text, padded_users_text)
- if not list_data['nomodes']:
- modes = "[%s]" % list_data['modes']
- else:
- modes = "[]"
- str += "%s: " % modes.rjust(int(weechat.config_get_plugin('modes_min_width')) + 2)
- str += "%s" % list_data['topic']
- return str
-
-def lb_line_up():
- global lb_curline
- if lb_curline <= 0:
- return
- lb_curline -= 1
- lb_refresh_curline()
- lb_check_outside_window()
- return
-
-def lb_line_down():
- global lb_curline, lb_channels
- if lb_curline+1 >= len(lb_channels):
- return
- lb_curline += 1
- lb_refresh_curline()
- lb_check_outside_window()
- return
-
-def lb_line_run():
- global lb_channels, lb_curline, lb_network
- buff = weechat.info_get("irc_buffer", lb_network)
- channel = lb_channels[lb_curline]['channel']
- command = "/join %s" % channel
- weechat.command(buff, command)
- return
-
-def lb_line_select():
- return
-
-def lb_scroll_top():
- global lb_curline
- old_y = lb_curline
- lb_curline = 0
- lb_refresh_curline()
- lb_refresh_line(old_y)
- weechat.command(lb_buffer, "/window scroll_top")
- return
-
-def lb_scroll_bottom():
- global lb_curline, lb_channels
- old_y = lb_curline
- lb_curline = len(lb_channels)-1
- lb_refresh_curline()
- lb_refresh_line(old_y)
- weechat.command(lb_buffer, "/window scroll_bottom")
- return
-
-def lb_check_outside_window():
- global lb_buffer, lb_curline
- if (lb_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 > lb_curline):
- weechat.command(lb_buffer, "/window scroll -%i" %(start_line_y - lb_curline))
- elif(start_line_y <= lb_curline - chat_height):
- weechat.command(lb_buffer, "/window scroll +%i"%(lb_curline - start_line_y - chat_height + 1))
- weechat.infolist_free(infolist)
-
-def lb_sort_next():
- global lb_current_sort, lb_sort_options
- if lb_current_sort:
- new_index = lb_sort_options.index(lb_current_sort) + 1
- else:
- new_index = 0
-
- if len(lb_sort_options) <= new_index:
- new_index = 0
-
- lb_set_current_sort_order(lb_sort_options[new_index])
- lb_sort()
-
-def lb_set_current_sort_order(value):
- global lb_current_sort
- lb_current_sort = value
- weechat.config_set_plugin('sort_order', lb_current_sort)
-
-def lb_set_invert_sort_order(value):
- global lb_sort_inverted
- lb_sort_inverted = value
- weechat.config_set_plugin('sort_inverted', ('on' if lb_sort_inverted else 'off'))
-
-def lb_sort_previous():
- global lb_current_sort, lb_sort_options
- if lb_current_sort:
- new_index = lb_sort_options.index(lb_current_sort) - 1
- else:
- new_index = 0
-
- if new_index < 0:
- new_index = len(lb_sort_options) - 1
-
- lb_set_current_sort_order(lb_sort_options[new_index])
- lb_sort()
-
-def lb_sort(sort_key=None):
- global lb_channels, lb_current_sort, lb_sort_inverted
- if sort_key:
- lb_set_current_sort_order(sort_key)
- if lb_current_sort == 'users':
- lb_channels = sorted(lb_channels, key=lambda chan_data: int(chan_data[lb_current_sort]))
- else:
- lb_channels = sorted(lb_channels, key=lambda chan_data: chan_data[lb_current_sort])
- if lb_sort_inverted:
- lb_channels.reverse()
- lb_set_buffer_title()
- lb_refresh()
-
-def lb_sort_invert():
- global lb_current_sort, lb_sort_inverted
- if lb_current_sort:
- lb_set_invert_sort_order(not lb_sort_inverted)
- lb_sort()
-
-def lb_close_cb(*kwargs):
- """ A callback for buffer closing. """
- global lb_buffer
-
- lb_buffer = None
- return weechat.WEECHAT_RC_OK
-
-lb_options = {
- 'refresh' : lb_refresh,
- 'up' : lb_line_up,
- 'down' : lb_line_down,
- 'enter' : lb_line_run,
- 'space' : lb_line_select,
- 'scroll_top' : lb_scroll_top,
- 'scroll_bottom': lb_scroll_bottom,
- 'sort_next' : lb_sort_next,
- 'sort_previous': lb_sort_previous,
- 'sort_invert': lb_sort_invert
-}
-
-def lb_command_main(data, buffer, args):
- if args[0:2] == "**":
- keyEvent(data, buffer, args[2:])
- return weechat.WEECHAT_RC_OK
-
-def lb_set_default_settings():
- global lb_settings
- # Set default settings
- for option, default_value, description in lb_settings:
- if not weechat.config_is_set_plugin(option):
- weechat.config_set_plugin(option, default_value)
- version = weechat.info_get("version_number", "") or 0
- if int(version) >= 0x00030500:
- weechat.config_set_desc_plugin(option, description)
-
-def lb_reset_stored_sort_order():
- global lb_current_sort, lb_sort_inverted
- lb_current_sort = weechat.config_get_plugin('sort_order')
- lb_sort_inverted = (True if weechat.config_get_plugin('sort_inverted') == 'on' else False)
-
-if __name__ == "__main__" and import_ok:
- if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION,
- SCRIPT_LICENSE, SCRIPT_DESC, "lb_close_cb", ""):
- lb_set_default_settings()
- lb_reset_stored_sort_order()
- lb_buffer = weechat.buffer_search("python", "listbuffer")
-
- weechat.hook_signal("*,irc_in_321", "lb_list_start", "")
- weechat.hook_signal("*,irc_in_322", "lb_list_chan", "")
- weechat.hook_signal("*,irc_in_323", "lb_list_end", "")
- weechat.hook_command(SCRIPT_COMMAND,
- "List Buffer",
- "", "", "",
- "lb_command_main", "")
--- a/weechat/python/sanitize_jira.py Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-import re, weechat, subprocess
-
-SCRIPT_NAME = 'sanitize_jira'
-SCRIPT_AUTHOR = 'Steve Losh <steve@stevelosh.com>'
-SCRIPT_VERSION = '0.0.1'
-SCRIPT_LICENSE = 'MIT'
-SCRIPT_DESC = 'clean up the garbage jirabot sends to channels into something readable'
-
-weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, '', '')
-
-weechat.hook_line('*', '', 'nick_Jira_Cloud', 'sanitize_jira', '')
-
-first_line_re = re.compile(
- r'(?P<link>https://[^/]+/browse/[^?]+)[?]atlOrigin=[^ ]+ [(](?P<title>.+)[)]'
-)
-
-detail_line_re = re.compile(
- r'''Status: \x1a\x01[*](?P<status>[^*]+)[*]\x1b\x01.*Type: \x1a\x01[*](?P<type>[^*]+)[*]\x1b\x01.*Assignee: \x1a\x01[*](?P<assignee>[^*]+)[*]\x1b\x01.*Priority: \x1a\x01[*](?P<priority>[^*]+)[*]\x1b\x01'''
-)
-
-def sanitize_jira(data, line):
- if 'sign up for an Atlassian account to view this link' in line['message']:
- return {'message': ' '}
-
- m = first_line_re.search(line['message'])
- if m:
- return {'message': '%s | %s' % (m.group('title'), m.group('link'))}
-
- m = detail_line_re.search(line['message'])
- if m:
- return {'message': '%s / %s / %s' % (m.group('type'), m.group('status'), m.group('assignee'))}
-
- return {}
--- a/weechat/relay.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-#
-# weechat -- relay.conf
-#
-# WARNING: It is NOT recommended to edit this file by hand,
-# especially if WeeChat is running.
-#
-# Use commands like /set or /fset to change settings in WeeChat.
-#
-# For more info, see: https://weechat.org/doc/quickstart/
-#
-
-[look]
-auto_open_buffer = on
-raw_messages = 256
-
-[color]
-client = cyan
-status_active = lightblue
-status_auth_failed = lightred
-status_connecting = yellow
-status_disconnected = lightred
-status_waiting_auth = brown
-text = default
-text_bg = default
-text_selected = white
-
-[network]
-allow_empty_password = off
-allowed_ips = ""
-auth_timeout = 60
-bind_address = ""
-clients_purge_delay = 0
-compression = 20
-ipv6 = on
-max_clients = 5
-nonce_size = 16
-password = ""
-password_hash_algo = "*"
-password_hash_iterations = 100000
-ssl_cert_key = "%h/ssl/relay.pem"
-ssl_priorities = "NORMAL:-VERS-SSL3.0"
-totp_secret = ""
-totp_window = 0
-websocket_allowed_origins = ""
-
-[irc]
-backlog_max_minutes = 1440
-backlog_max_number = 256
-backlog_since_last_disconnect = on
-backlog_since_last_message = off
-backlog_tags = "irc_privmsg"
-backlog_time_format = "[%H:%M] "
-
-[weechat]
-commands = ""
-
-[port]
-
-[path]
--- a/weechat/rmodifier.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-#
-# rmodifier.conf -- weechat v0.4.3
-#
-
-[look]
-hide_char = "*"
-
-[modifier]
-nickserv = "history_add,input_text_display;^(/(msg|quote) +nickserv +(identify|ghost \S+) +)(.*);1,4*"
-oper = "history_add,input_text_display;^(/oper +\S+ +)(.*);1,2*"
-set_pass = "history_add;^(/set +\S*password\S* +)(.*);1,2*"
--- a/weechat/ruby.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-#
-# weechat -- ruby.conf
-#
-# WARNING: It is NOT recommended to edit this file by hand,
-# especially if WeeChat is running.
-#
-# Use commands like /set or /fset to change settings in WeeChat.
-#
-# For more info, see: https://weechat.org/doc/quickstart
-#
-
-[look]
-check_license = off
-eval_keep_context = on
--- a/weechat/ruby/autoload/challengeauth.rb Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-# Copyright (c) 2013 Dominik Honnef <dominikh@fork-bomb.org>
-
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to
-# permit persons to whom the Software is furnished to do so, subject to
-# the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-# History:
-# 2013-04-20, Dominik Honnef
-# version 0.0.1: initial version
-
-require "openssl"
-
-QBot = "Q@CServe.quakenet.org"
-QBotHost = "Q!TheQBot@CServe.quakenet.org"
-Request = Struct.new(:username, :hash)
-
-def weechat_init
- @requests = {}
-
- Weechat.register("challengeauth",
- "Dominik Honnef",
- "0.0.1",
- "MIT",
- "Securely authenticate with QuakeNet by using CHALLENGEAUTH",
- "",
- "")
-
- Weechat.hook_command("challengeauth",
- "Authenticate with Q using CHALLENGEAUTH",
- "[username] [password]",
- "",
- "",
- "challengeauth",
- "")
-
- Weechat.hook_modifier("irc_in_notice", "challenge_notice", "")
-
- return Weechat::WEECHAT_RC_OK
-end
-
-def calculate_q_hash(username, hash, challenge)
- username = username.tr("A-Z[]\\\\^", "a-z{}|~")
-
- key = OpenSSL::Digest::SHA256.hexdigest("#{username}:#{hash}")
- return OpenSSL::HMAC.hexdigest("SHA256", key, challenge)
-end
-
-def get_server_buffer(server)
- Weechat.buffer_search("irc", "server." + server)
-end
-
-def challengeauth(data, buffer, args)
- plugin = Weechat.buffer_get_string(buffer, "localvar_plugin")
- if plugin != "irc"
- Weechat.print(buffer, "/challengeauth only works for IRC buffers.")
- return Weechat.WEECHAT_RC_ERROR
- end
-
- server = Weechat.buffer_get_string(buffer, "localvar_server")
- args = args.split(" ", 2)
- username = args[0]
- password = args[1]
- hash = OpenSSL::Digest::SHA256.hexdigest(password[0, 10])
-
- @requests[server] = Request.new(username, hash)
- server_buffer = get_server_buffer(server)
- Weechat.print(server_buffer, "Authenticating as #{username}...")
- Weechat.command(server_buffer, "/quote PRIVMSG #{QBot} :CHALLENGE")
-
- return Weechat::WEECHAT_RC_OK
-end
-
-def challenge_notice(modifier, data, server, line)
- return line unless @requests.has_key?(server)
-
- parts = line.split(" ")
- return line unless parts.size > 5
-
- host = parts[0][1..-1]
- command = parts[3][1..-1]
- challenge = parts[4]
-
- return line if host != QBotHost || command != "CHALLENGE"
-
- request = @requests[server]
- response = calculate_q_hash(request.username, request.hash, challenge)
- server_buffer = get_server_buffer(server)
-
- Weechat.print(server_buffer, "Sending challengeauth for #{request.username}...")
- Weechat.command(server_buffer,
- "/quote PRIVMSG %s :CHALLENGEAUTH %s %s HMAC-SHA-256" % [QBot, request.username, response])
-
- @requests.delete(server)
-
- return line
-end
--- a/weechat/script.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-#
-# weechat -- script.conf
-#
-# WARNING: It is NOT recommended to edit this file by hand,
-# especially if WeeChat is running.
-#
-# Use commands like /set or /fset to change settings in WeeChat.
-#
-# For more info, see: https://weechat.org/doc/quickstart/
-#
-
-[look]
-columns = "%s %n %V %v %u | %d | %t"
-diff_color = on
-diff_command = "auto"
-display_source = on
-quiet_actions = on
-sort = "p,n"
-translate_description = on
-use_keys = on
-
-[color]
-status_autoloaded = cyan
-status_held = white
-status_installed = lightcyan
-status_obsolete = lightmagenta
-status_popular = yellow
-status_running = lightgreen
-status_unknown = lightred
-text = default
-text_bg = default
-text_bg_selected = red
-text_date = default
-text_date_selected = white
-text_delimiters = darkgray
-text_description = default
-text_description_selected = white
-text_extension = default
-text_extension_selected = white
-text_name = cyan
-text_name_selected = lightcyan
-text_selected = white
-text_tags = brown
-text_tags_selected = yellow
-text_version = magenta
-text_version_loaded = default
-text_version_loaded_selected = white
-text_version_selected = lightmagenta
-
-[scripts]
-autoload = on
-cache_expire = 60
-download_enabled = on
-download_timeout = 30
-hold = ""
-path = "%h/script"
-url = "http://www.weechat.org/files/plugins.xml.gz"
--- a/weechat/spell.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-#
-# weechat -- spell.conf
-#
-# WARNING: It is NOT recommended to edit this file by hand,
-# especially if WeeChat is running.
-#
-# Use commands like /set or /fset to change settings in WeeChat.
-#
-# For more info, see: https://weechat.org/doc/quickstart
-#
-
-[color]
-misspelled = lightred
-suggestion = default
-suggestion_delimiter_dict = cyan
-suggestion_delimiter_word = cyan
-
-[check]
-commands = "away,command,cycle,kick,kickban,me,msg,notice,part,query,quit,topic"
-default_dict = ""
-during_search = off
-enabled = off
-real_time = off
-suggestions = -1
-word_min_length = 2
-
-[dict]
-
-[look]
-suggestion_delimiter_dict = " / "
-suggestion_delimiter_word = ","
-
-[option]
--- a/weechat/tcl.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-#
-# weechat -- tcl.conf
-#
-# WARNING: It is NOT recommended to edit this file by hand,
-# especially if WeeChat is running.
-#
-# Use /set or similar command to change settings in WeeChat.
-#
-# For more info, see: https://weechat.org/doc/quickstart
-#
-
-[look]
-check_license = off
-eval_keep_context = on
--- a/weechat/trigger.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-#
-# weechat -- trigger.conf
-#
-# WARNING: It is NOT recommended to edit this file by hand,
-# especially if WeeChat is running.
-#
-# Use commands like /set or /fset to change settings in WeeChat.
-#
-# For more info, see: https://weechat.org/doc/quickstart/
-#
-
-[look]
-enabled = on
-monitor_strip_colors = off
-
-[color]
-flag_command = lightgreen
-flag_conditions = yellow
-flag_post_action = lightblue
-flag_regex = lightcyan
-flag_return_code = lightmagenta
-regex = white
-replace = cyan
-trigger = green
-trigger_disabled = red
-
-[trigger]
-beep.arguments = ""
-beep.command = "/print -beep"
-beep.conditions = "${tg_highlight} || ${tg_msg_pv}"
-beep.enabled = on
-beep.hook = print
-beep.post_action = none
-beep.regex = ""
-beep.return_code = ok
-cmd_pass.arguments = "5000|input_text_display;5000|history_add;5000|irc_command_auth"
-cmd_pass.command = ""
-cmd_pass.conditions = ""
-cmd_pass.enabled = on
-cmd_pass.hook = modifier
-cmd_pass.post_action = none
-cmd_pass.regex = "==^((/(msg|quote) +nickserv +(id|identify|register|ghost +[^ ]+|release +[^ ]+|regain +[^ ]+) +)|/oper +[^ ]+ +|/quote +pass +|/set +[^ ]*password[^ ]* +|/secure +(passphrase|decrypt|set +[^ ]+) +)(.*)==$1$.*+"
-cmd_pass.return_code = ok
-dumbass_buffer.arguments = "4000|input_text_for_buffer;4000|history_add"
-dumbass_buffer.command = ""
-dumbass_buffer.conditions = ""
-dumbass_buffer.enabled = on
-dumbass_buffer.hook = modifier
-dumbass_buffer.post_action = none
-dumbass_buffer.regex = "==^ +/?b (.+)==/b ${re:1}"
-dumbass_buffer.return_code = ok
-idiot_buffer.arguments = "4000|input_text_for_buffer;4000|history_add"
-idiot_buffer.command = ""
-idiot_buffer.conditions = ""
-idiot_buffer.enabled = on
-idiot_buffer.hook = modifier
-idiot_buffer.post_action = none
-idiot_buffer.regex = "==^b (.+)==/b ${re:1}"
-idiot_buffer.return_code = ok
-msg_auth.arguments = "5000|irc_message_auth"
-msg_auth.command = ""
-msg_auth.conditions = ""
-msg_auth.enabled = on
-msg_auth.hook = modifier
-msg_auth.post_action = none
-msg_auth.regex = "==^(.*(id|identify|register|ghost +[^ ]+|release +[^ ]+) +)(.*)==$1$.*+"
-msg_auth.return_code = ok
-server_pass.arguments = "5000|input_text_display;5000|history_add"
-server_pass.command = ""
-server_pass.conditions = ""
-server_pass.enabled = on
-server_pass.hook = modifier
-server_pass.post_action = none
-server_pass.regex = "==^(/(server|connect) .*-(sasl_)?password=)([^ ]+)(.*)==$1$.*4$5"
-server_pass.return_code = ok
-uncc.arguments = "weechat_print"
-uncc.command = ""
-uncc.conditions = "${tg_tag_nick}"
-uncc.enabled = on
-uncc.hook = modifier
-uncc.post_action = none
-uncc.regex = "== \[cc: [^ ]+\]===="
-uncc.return_code = ok
--- a/weechat/typing.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-#
-# weechat -- typing.conf
-#
-# WARNING: It is NOT recommended to edit this file by hand,
-# especially if WeeChat is running.
-#
-# Use commands like /set or /fset to change settings in WeeChat.
-#
-# For more info, see: https://weechat.org/doc/quickstart/
-#
-
-[look]
-delay_purge_paused = 30
-delay_purge_typing = 6
-delay_set_paused = 10
-enabled_nicks = off
-enabled_self = off
-input_min_chars = 4
-item_max_length = 0
--- a/weechat/urlgrab.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-#
-# weechat -- urlgrab.conf
-#
-# WARNING: It is NOT recommended to edit this file by hand,
-# especially if WeeChat is running.
-#
-# Use commands like /set or /fset to change settings in WeeChat.
-#
-# For more info, see: https://weechat.org/doc/quickstart/
-#
-
-[color]
-color_bg_selected = green
-color_buffer = red
-color_buffer_selected = red
-color_time = cyan
-color_time_selected = cyan
-color_url = blue
-color_url_selected = blue
-
-[default]
-copycmd = "xsel -i"
-historysize = 20
-localcmd = "xdg-open %s"
-method = "local"
-output_main_buffer = off
-remotecmd = "ssh -x localhost -i ~/.ssh/id_rsa -C "export DISPLAY=":0.0" && firefox %s""
-time_format = "%H:%M:%S"
-url_log = "~/.weechat/urls.log"
--- a/weechat/weechat.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,693 +0,0 @@
-#
-# WeeChat -- weechat.conf
-#
-# WARNING: It is NOT recommended to edit this file by hand,
-# especially if WeeChat is running.
-#
-# Use commands like /set or /fset to change settings in WeeChat.
-#
-# For more info, see: https://weechat.org/doc/quickstart
-#
-
-[debug]
-
-[startup]
-command_after_plugins = ""
-command_before_plugins = ""
-display_logo = on
-display_version = on
-sys_rlimit = ""
-
-[look]
-align_end_of_lines = message
-align_multiline_words = on
-bar_more_down = "++"
-bar_more_left = "<<"
-bar_more_right = ">>"
-bar_more_up = "--"
-bare_display_exit_on_input = on
-bare_display_time_format = "%H:%M"
-buffer_auto_renumber = on
-buffer_notify_default = all
-buffer_position = end
-buffer_search_case_sensitive = off
-buffer_search_force_default = off
-buffer_search_regex = off
-buffer_search_where = prefix_message
-buffer_time_format = "%H:%M"
-buffer_time_same = ""
-color_basic_force_bold = off
-color_inactive_buffer = off
-color_inactive_message = on
-color_inactive_prefix = on
-color_inactive_prefix_buffer = on
-color_inactive_time = off
-color_inactive_window = off
-color_nick_offline = off
-color_pairs_auto_reset = 5
-color_real_white = off
-command_chars = ""
-command_incomplete = off
-confirm_quit = off
-confirm_upgrade = off
-day_change = on
-day_change_message_1date = "-- %a, %d %b %Y --"
-day_change_message_2dates = "-- %%a, %%d %%b %%Y (%a, %d %b %Y) --"
-eat_newline_glitch = off
-emphasized_attributes = ""
-highlight = "sjl,slosh,slj,horrifying,steve.losh,@steve.losh,stevelosh"
-highlight_regex = "(steve losh|rob ford|(jesus )?fucking christ|(horse|mouse|clown)fuckers?|((mother)?fuck([ie]ng?|er)?|(god?)?damn(ed)?|dammit|(bull|horse)?shite?){3,})"
-highlight_tags = ""
-hotlist_add_conditions = "${away} || ${buffer.num_displayed} == 0"
-hotlist_buffer_separator = ", "
-hotlist_count_max = 2
-hotlist_count_min_msg = 2
-hotlist_names_count = 3
-hotlist_names_length = 0
-hotlist_names_level = 12
-hotlist_names_merged_buffers = off
-hotlist_prefix = "H: "
-hotlist_remove = merged
-hotlist_short_names = on
-hotlist_sort = group_time_asc
-hotlist_suffix = ""
-hotlist_unique_numbers = on
-hotlist_update_on_buffer_switch = on
-input_cursor_scroll = 20
-input_share = none
-input_share_overwrite = off
-input_undo_max = 32
-item_away_message = on
-item_buffer_filter = "*"
-item_buffer_zoom = "!"
-item_mouse_status = "M"
-item_time_format = "%H:%M"
-jump_current_to_previous_buffer = on
-jump_previous_buffer_when_closing = on
-jump_smart_back_to_buffer = on
-key_bind_safe = on
-key_grab_delay = 800
-mouse = off
-mouse_timer_delay = 100
-nick_color_force = ""
-nick_color_hash = djb2
-nick_color_hash_salt = ""
-nick_color_stop_chars = "_|["
-nick_prefix = ""
-nick_suffix = ""
-paste_auto_add_newline = on
-paste_bracketed = off
-paste_bracketed_timer_delay = 10
-paste_max_lines = 3
-prefix_action = " *"
-prefix_align = right
-prefix_align_max = 15
-prefix_align_min = 0
-prefix_align_more = "+"
-prefix_align_more_after = on
-prefix_buffer_align = right
-prefix_buffer_align_max = 0
-prefix_buffer_align_more = "+"
-prefix_buffer_align_more_after = on
-prefix_error = "=!="
-prefix_join = "✔"
-prefix_network = "--"
-prefix_quit = "✘"
-prefix_same_nick = ""
-prefix_same_nick_middle = ""
-prefix_suffix = "|"
-quote_nick_prefix = "<"
-quote_nick_suffix = ">"
-quote_time_format = "%H:%M:%S"
-read_marker = line
-read_marker_always_show = on
-read_marker_string = "─"
-read_marker_update_on_buffer_switch = on
-save_config_on_exit = off
-save_config_with_fsync = off
-save_layout_on_exit = all
-scroll_amount = 3
-scroll_bottom_after_switch = off
-scroll_page_percent = 100
-search_text_not_found_alert = on
-separator_horizontal = "-"
-separator_vertical = ""
-tab_width = 1
-time_format = "%a, %d %b %Y %T"
-window_auto_zoom = off
-window_separator_horizontal = on
-window_separator_vertical = on
-window_title = ""
-word_chars_highlight = "!\u00A0,-,_,|,@,.,alnum"
-word_chars_input = "!\u00A0,-,_,|,alnum"
-
-[palette]
-
-[color]
-bar_more = magenta
-chat = default
-chat_bg = default
-chat_buffer = white
-chat_channel = white
-chat_day_change = cyan
-chat_delimiters = green
-chat_highlight = 207
-chat_highlight_bg = default
-chat_host = cyan
-chat_inactive_buffer = darkgray
-chat_inactive_window = darkgray
-chat_nick = lightcyan
-chat_nick_colors = "027,048,068,028,081,082,099,112,129,136,169,178,208,226,113,196,161,23,59,222"
-chat_nick_offline = darkgray
-chat_nick_offline_highlight = default
-chat_nick_offline_highlight_bg = darkgray
-chat_nick_other = cyan
-chat_nick_prefix = green
-chat_nick_self = white
-chat_nick_suffix = green
-chat_prefix_action = white
-chat_prefix_buffer = brown
-chat_prefix_buffer_inactive_buffer = darkgray
-chat_prefix_error = yellow
-chat_prefix_join = lightgreen
-chat_prefix_more = lightmagenta
-chat_prefix_network = magenta
-chat_prefix_quit = lightred
-chat_prefix_suffix = green
-chat_read_marker = green
-chat_read_marker_bg = default
-chat_server = brown
-chat_tags = red
-chat_text_found = yellow
-chat_text_found_bg = lightmagenta
-chat_time = 238
-chat_time_delimiters = 236
-chat_value = cyan
-chat_value_null = blue
-emphasized = yellow
-emphasized_bg = magenta
-input_actions = lightgreen
-input_text_not_found = red
-item_away = yellow
-nicklist_away = cyan
-nicklist_group = green
-separator = green
-status_count_highlight = magenta
-status_count_msg = brown
-status_count_other = 16
-status_count_private = green
-status_data_highlight = lightmagenta
-status_data_msg = yellow
-status_data_other = 16
-status_data_private = lightgreen
-status_filter = green
-status_more = 16
-status_mouse = green
-status_name = *16
-status_name_ssl = *16
-status_nicklist_count = default
-status_number = 16
-status_time = *16
-
-[completion]
-base_word_until_cursor = on
-command_inline = on
-default_template = "%(nicks)|%(irc_channels)"
-nick_add_space = on
-nick_case_sensitive = off
-nick_completer = ":"
-nick_first_only = off
-nick_ignore_chars = "[]`_-^"
-partial_completion_alert = on
-partial_completion_command = off
-partial_completion_command_arg = off
-partial_completion_count = on
-partial_completion_other = off
-partial_completion_templates = "config_options"
-
-[history]
-display_default = 5
-max_buffer_lines_minutes = 0
-max_buffer_lines_number = 4096
-max_commands = 100
-max_visited_buffers = 50
-
-[proxy]
-
-[network]
-connection_timeout = 60
-gnutls_ca_system = on
-gnutls_ca_user = ""
-gnutls_handshake_timeout = 30
-proxy_curl = ""
-
-[plugin]
-autoload = "*"
-debug = off
-extension = ".so"
-path = "%h/plugins"
-save_config_on_unload = on
-
-[signal]
-sighup = "${if:${info:weechat_headless}?/reload:/quit -yes}"
-sigquit = "/quit -yes"
-sigterm = "/quit -yes"
-sigusr1 = ""
-sigusr2 = ""
-
-[bar]
-buffers.color_bg = default
-buffers.color_bg_inactive = default
-buffers.color_delim = default
-buffers.color_fg = default
-buffers.conditions = ""
-buffers.filling_left_right = vertical
-buffers.filling_top_bottom = horizontal
-buffers.hidden = on
-buffers.items = "buffers"
-buffers.position = left
-buffers.priority = 0
-buffers.separator = on
-buffers.size = 0
-buffers.size_max = 0
-buffers.type = root
-buflist.color_bg = default
-buflist.color_bg_inactive = default
-buflist.color_delim = default
-buflist.color_fg = default
-buflist.conditions = ""
-buflist.filling_left_right = vertical
-buflist.filling_top_bottom = columns_vertical
-buflist.hidden = off
-buflist.items = "buflist"
-buflist.position = left
-buflist.priority = 0
-buflist.separator = on
-buflist.size = 0
-buflist.size_max = 25
-buflist.type = root
-fset.color_bg = default
-fset.color_bg_inactive = default
-fset.color_delim = cyan
-fset.color_fg = default
-fset.conditions = "${buffer.full_name} == fset.fset"
-fset.filling_left_right = vertical
-fset.filling_top_bottom = horizontal
-fset.hidden = off
-fset.items = "fset"
-fset.position = top
-fset.priority = 0
-fset.separator = on
-fset.size = 3
-fset.size_max = 3
-fset.type = window
-input.color_bg = default
-input.color_bg_inactive = default
-input.color_delim = green
-input.color_fg = default
-input.conditions = ""
-input.filling_left_right = vertical
-input.filling_top_bottom = horizontal
-input.hidden = off
-input.items = "[input_prompt]+(away),[input_search],[input_paste],input_text"
-input.position = bottom
-input.priority = 1000
-input.separator = off
-input.size = 1
-input.size_max = 0
-input.type = window
-nicklist.color_bg = default
-nicklist.color_bg_inactive = default
-nicklist.color_delim = cyan
-nicklist.color_fg = default
-nicklist.conditions = "nicklist"
-nicklist.filling_left_right = vertical
-nicklist.filling_top_bottom = columns_vertical
-nicklist.hidden = on
-nicklist.items = "buffer_nicklist"
-nicklist.position = right
-nicklist.priority = 200
-nicklist.separator = on
-nicklist.size = 0
-nicklist.size_max = 0
-nicklist.type = window
-status.color_bg = green
-status.color_bg_inactive = default
-status.color_delim = 0
-status.color_fg = 0
-status.conditions = ""
-status.filling_left_right = vertical
-status.filling_top_bottom = horizontal
-status.hidden = off
-status.items = "[time],buffer_number+:+buffer_name,buffer_title"
-status.position = bottom
-status.priority = 500
-status.separator = off
-status.size = 1
-status.size_max = 0
-status.type = window
-title.color_bg = green
-title.color_bg_inactive = default
-title.color_delim = cyan
-title.color_fg = 16
-title.conditions = ""
-title.filling_left_right = vertical
-title.filling_top_bottom = horizontal
-title.hidden = on
-title.items = "buffer_title"
-title.position = top
-title.priority = 500
-title.separator = off
-title.size = 1
-title.size_max = 0
-title.type = window
-
-[layout]
-
-[notify]
-python.slack.10xgenomics.&cloud-alerts-pagerduty = highlight
-python.slack.10xgenomics.&cloud-sumo-prod-support-alerts = highlight
-python.slack.10xgenomics.&lacework-10xdev = highlight
-python.slack.10xgenomics.&lacework-10xprod = highlight
-python.slack.10xgenomics.&testing1234 = highlight
-
-[filter]
-irc_smart = on;*;irc_smart_filter;*
-nicks = on;*;irc_366;*
-
-[key]
-ctrl-? = "/input delete_previous_char"
-ctrl-A = "/input move_beginning_of_line"
-ctrl-B = "/brows"
-ctrl-Cb = "/input insert \x02"
-ctrl-Cc = "/input insert \x03"
-ctrl-Ci = "/input insert \x1D"
-ctrl-Co = "/input insert \x0F"
-ctrl-Cr = "/input insert \x12"
-ctrl-Cu = "/input insert \x15"
-ctrl-D = "/buffer close"
-ctrl-E = "/input move_end_of_line"
-ctrl-F = "/input move_next_char"
-ctrl-H = "/input delete_previous_char"
-ctrl-I = "/input complete_next"
-ctrl-J = "/input jump_smart"
-ctrl-K = "/input delete_end_of_line"
-ctrl-L = "/window refresh"
-ctrl-M = "/input return"
-ctrl-N = "/buffer +1"
-ctrl-O = "/editor"
-ctrl-P = "/buffer -1"
-ctrl-R = "/input search_text"
-ctrl-Sctrl-U = "/input set_unread"
-ctrl-T = "/input transpose_chars"
-ctrl-U = "/url 1"
-ctrl-W = "/input delete_previous_word"
-ctrl-X = "/input switch_active_buffer"
-ctrl-Y = "/input clipboard_paste"
-meta-meta-OP = "/bar scroll buflist * b"
-meta-meta-OQ = "/bar scroll buflist * e"
-meta-meta2-11~ = "/bar scroll buflist * b"
-meta-meta2-12~ = "/bar scroll buflist * e"
-meta-meta2-1~ = "/window scroll_top"
-meta-meta2-23~ = "/bar scroll nicklist * yb"
-meta-meta2-24~ = "/bar scroll nicklist * ye"
-meta-meta2-4~ = "/window scroll_bottom"
-meta-meta2-5~ = "/window scroll_up"
-meta-meta2-6~ = "/window scroll_down"
-meta-meta2-7~ = "/window scroll_top"
-meta-meta2-8~ = "/window scroll_bottom"
-meta-meta2-A = "/buffer move -1"
-meta-meta2-B = "/buffer move +1"
-meta-meta2-C = "/buffer +1"
-meta-meta2-D = "/buffer -1"
-meta-0 = "/buffer *10"
-meta-1 = "/buffer *1"
-meta-2 = "/buffer *2"
-meta-3 = "/buffer *3"
-meta-4 = "/buffer *4"
-meta-5 = "/buffer *5"
-meta-6 = "/buffer *6"
-meta-7 = "/buffer *7"
-meta-8 = "/buffer *8"
-meta-9 = "/buffer *9"
-meta-< = "/input jump_previously_visited_buffer"
-meta-= = "/filter toggle"
-meta-> = "/input jump_next_visited_buffer"
-meta-B = "/buflist toggle"
-meta-OA = "/input history_global_previous"
-meta-OB = "/input history_global_next"
-meta-OC = "/input move_next_word"
-meta-OD = "/input move_previous_word"
-meta-OF = "/input move_end_of_line"
-meta-OH = "/input move_beginning_of_line"
-meta-OP = "/bar scroll buflist * -100%"
-meta-OQ = "/bar scroll buflist * +100%"
-meta-Oa = "/input history_global_previous"
-meta-Ob = "/input history_global_next"
-meta-Oc = "/input move_next_word"
-meta-Od = "/input move_previous_word"
-meta2-11^ = "/bar scroll buflist * -100%"
-meta2-11~ = "/bar scroll buflist * -100%"
-meta2-12^ = "/bar scroll buflist * +100%"
-meta2-12~ = "/bar scroll buflist * +100%"
-meta2-15~ = "/bar scroll nicklist * y-100%"
-meta2-17~ = "/bar scroll nicklist * y+100%"
-meta2-18~ = "/window -1"
-meta2-19~ = "/window +1"
-meta2-1;3A = "/buffer -1"
-meta2-1;3B = "/buffer +1"
-meta2-1;3C = "/buffer +1"
-meta2-1;3D = "/buffer -1"
-meta2-1;3P = "/bar scroll buflist * b"
-meta2-1;3Q = "/bar scroll buflist * e"
-meta2-1;5A = "/input history_global_previous"
-meta2-1;5B = "/input history_global_next"
-meta2-1;5P = "/bar scroll buflist * -100%"
-meta2-1;5Q = "/bar scroll buflist * +100%"
-meta2-1;9A = "/buffer move -1"
-meta2-1;9B = "/buffer move +1"
-meta2-1~ = "/input move_beginning_of_line"
-meta2-20~ = "/bar scroll title * x-50%"
-meta2-21~ = "/bar scroll title * x+50%"
-meta2-23~ = "/bar scroll nicklist * y-100%"
-meta2-24~ = "/bar scroll nicklist * y+100%"
-meta2-3~ = "/input delete_next_char"
-meta2-4~ = "/input move_end_of_line"
-meta2-5;3~ = "/window scroll_up"
-meta2-5~ = "/window page_up"
-meta2-6;3~ = "/window scroll_down"
-meta2-6~ = "/window page_down"
-meta2-7~ = "/input move_beginning_of_line"
-meta2-8~ = "/input move_end_of_line"
-meta2-A = "/input history_previous"
-meta2-B = "/input history_next"
-meta2-C = "/input move_next_char"
-meta2-D = "/input move_previous_char"
-meta2-F = "/input move_end_of_line"
-meta2-G = "/window page_down"
-meta2-H = "/input move_beginning_of_line"
-meta2-I = "/window page_up"
-meta2-Z = "/input complete_previous"
-meta-_ = "/input redo"
-meta-a = "/input jump_smart"
-meta-b = "/input move_previous_word"
-meta-d = "/input delete_next_word"
-meta-f = "/input move_next_word"
-meta-h = "/input hotlist_clear"
-meta-jmeta-l = "/input jump_last_buffer"
-meta-jmeta-r = "/server raw"
-meta-jmeta-s = "/server jump"
-meta-j01 = "/buffer 1"
-meta-j02 = "/buffer 2"
-meta-j03 = "/buffer 3"
-meta-j04 = "/buffer 4"
-meta-j05 = "/buffer 5"
-meta-j06 = "/buffer 6"
-meta-j07 = "/buffer 7"
-meta-j08 = "/buffer 8"
-meta-j09 = "/buffer 9"
-meta-j10 = "/buffer 10"
-meta-j11 = "/buffer 11"
-meta-j12 = "/buffer 12"
-meta-j13 = "/buffer 13"
-meta-j14 = "/buffer 14"
-meta-j15 = "/buffer 15"
-meta-j16 = "/buffer 16"
-meta-j17 = "/buffer 17"
-meta-j18 = "/buffer 18"
-meta-j19 = "/buffer 19"
-meta-j20 = "/buffer 20"
-meta-j21 = "/buffer 21"
-meta-j22 = "/buffer 22"
-meta-j23 = "/buffer 23"
-meta-j24 = "/buffer 24"
-meta-j25 = "/buffer 25"
-meta-j26 = "/buffer 26"
-meta-j27 = "/buffer 27"
-meta-j28 = "/buffer 28"
-meta-j29 = "/buffer 29"
-meta-j30 = "/buffer 30"
-meta-j31 = "/buffer 31"
-meta-j32 = "/buffer 32"
-meta-j33 = "/buffer 33"
-meta-j34 = "/buffer 34"
-meta-j35 = "/buffer 35"
-meta-j36 = "/buffer 36"
-meta-j37 = "/buffer 37"
-meta-j38 = "/buffer 38"
-meta-j39 = "/buffer 39"
-meta-j40 = "/buffer 40"
-meta-j41 = "/buffer 41"
-meta-j42 = "/buffer 42"
-meta-j43 = "/buffer 43"
-meta-j44 = "/buffer 44"
-meta-j45 = "/buffer 45"
-meta-j46 = "/buffer 46"
-meta-j47 = "/buffer 47"
-meta-j48 = "/buffer 48"
-meta-j49 = "/buffer 49"
-meta-j50 = "/buffer 50"
-meta-j51 = "/buffer 51"
-meta-j52 = "/buffer 52"
-meta-j53 = "/buffer 53"
-meta-j54 = "/buffer 54"
-meta-j55 = "/buffer 55"
-meta-j56 = "/buffer 56"
-meta-j57 = "/buffer 57"
-meta-j58 = "/buffer 58"
-meta-j59 = "/buffer 59"
-meta-j60 = "/buffer 60"
-meta-j61 = "/buffer 61"
-meta-j62 = "/buffer 62"
-meta-j63 = "/buffer 63"
-meta-j64 = "/buffer 64"
-meta-j65 = "/buffer 65"
-meta-j66 = "/buffer 66"
-meta-j67 = "/buffer 67"
-meta-j68 = "/buffer 68"
-meta-j69 = "/buffer 69"
-meta-j70 = "/buffer 70"
-meta-j71 = "/buffer 71"
-meta-j72 = "/buffer 72"
-meta-j73 = "/buffer 73"
-meta-j74 = "/buffer 74"
-meta-j75 = "/buffer 75"
-meta-j76 = "/buffer 76"
-meta-j77 = "/buffer 77"
-meta-j78 = "/buffer 78"
-meta-j79 = "/buffer 79"
-meta-j80 = "/buffer 80"
-meta-j81 = "/buffer 81"
-meta-j82 = "/buffer 82"
-meta-j83 = "/buffer 83"
-meta-j84 = "/buffer 84"
-meta-j85 = "/buffer 85"
-meta-j86 = "/buffer 86"
-meta-j87 = "/buffer 87"
-meta-j88 = "/buffer 88"
-meta-j89 = "/buffer 89"
-meta-j90 = "/buffer 90"
-meta-j91 = "/buffer 91"
-meta-j92 = "/buffer 92"
-meta-j93 = "/buffer 93"
-meta-j94 = "/buffer 94"
-meta-j95 = "/buffer 95"
-meta-j96 = "/buffer 96"
-meta-j97 = "/buffer 97"
-meta-j98 = "/buffer 98"
-meta-j99 = "/buffer 99"
-meta-k = "/input grab_key_command"
-meta-n = "/window scroll_next_highlight"
-meta-p = "/window scroll_previous_highlight"
-meta-r = "/input delete_line"
-meta-u = "/input scroll_unread"
-meta-wmeta-meta2-A = "/window up"
-meta-wmeta-meta2-B = "/window down"
-meta-wmeta-meta2-C = "/window right"
-meta-wmeta-meta2-D = "/window left"
-meta-wmeta2-1;3A = "/window up"
-meta-wmeta2-1;3B = "/window down"
-meta-wmeta2-1;3C = "/window right"
-meta-wmeta2-1;3D = "/window left"
-meta-wmeta-b = "/window balance"
-meta-wmeta-s = "/window swap"
-meta-x = "/bar toggle nicklist"
-meta-z = "/window zoom"
-ctrl-_ = "/input undo"
-
-[key_search]
-ctrl-J = "/input search_stop"
-ctrl-M = "/input search_stop"
-ctrl-R = "/input search_switch_case"
-meta2-A = "/input search_previous"
-meta2-B = "/input search_next"
-
-[key_cursor]
-ctrl-J = "/cursor stop"
-ctrl-M = "/cursor stop"
-meta-meta2-A = "/cursor move area_up"
-meta-meta2-B = "/cursor move area_down"
-meta-meta2-C = "/cursor move area_right"
-meta-meta2-D = "/cursor move area_left"
-meta2-1;3A = "/cursor move area_up"
-meta2-1;3B = "/cursor move area_down"
-meta2-1;3C = "/cursor move area_right"
-meta2-1;3D = "/cursor move area_left"
-meta2-A = "/cursor move up"
-meta2-B = "/cursor move down"
-meta2-C = "/cursor move right"
-meta2-D = "/cursor move left"
-@chat(python.*):D = "hsignal:slack_cursor_delete"
-@chat(python.*):L = "hsignal:slack_cursor_linkarchive"
-@chat(python.*):M = "hsignal:slack_cursor_message"
-@chat(python.*):R = "hsignal:slack_cursor_reply"
-@chat(python.*):T = "hsignal:slack_cursor_thread"
-@item(buffer_nicklist):K = "/window ${_window_number};/kickban ${nick}"
-@item(buffer_nicklist):b = "/window ${_window_number};/ban ${nick}"
-@item(buffer_nicklist):k = "/window ${_window_number};/kick ${nick}"
-@item(buffer_nicklist):q = "/window ${_window_number};/query ${nick};/cursor stop"
-@item(buffer_nicklist):w = "/window ${_window_number};/whois ${nick}"
-@chat:Q = "hsignal:chat_quote_time_prefix_message;/cursor stop"
-@chat:m = "hsignal:chat_quote_message;/cursor stop"
-@chat:q = "hsignal:chat_quote_prefix_message;/cursor stop"
-
-[key_mouse]
-@bar(buflist):ctrl-wheeldown = "hsignal:buflist_mouse"
-@bar(buflist):ctrl-wheelup = "hsignal:buflist_mouse"
-@bar(input):button2 = "/input grab_mouse_area"
-@bar(nicklist):button1-gesture-down = "/bar scroll nicklist ${_window_number} +100%"
-@bar(nicklist):button1-gesture-down-long = "/bar scroll nicklist ${_window_number} e"
-@bar(nicklist):button1-gesture-up = "/bar scroll nicklist ${_window_number} -100%"
-@bar(nicklist):button1-gesture-up-long = "/bar scroll nicklist ${_window_number} b"
-@chat(fset.fset):button1 = "/window ${_window_number};/fset -go ${_chat_line_y}"
-@chat(fset.fset):button2* = "hsignal:fset_mouse"
-@chat(fset.fset):wheeldown = "/fset -down 5"
-@chat(fset.fset):wheelup = "/fset -up 5"
-@chat(python.*):button2 = "hsignal:slack_mouse"
-@chat(script.scripts):button1 = "/window ${_window_number};/script go ${_chat_line_y}"
-@chat(script.scripts):button2 = "/window ${_window_number};/script go ${_chat_line_y};/script installremove -q ${script_name_with_extension}"
-@chat(script.scripts):wheeldown = "/script down 5"
-@chat(script.scripts):wheelup = "/script up 5"
-@item(buffer_nicklist):button1 = "/window ${_window_number};/query ${nick}"
-@item(buffer_nicklist):button1-gesture-left = "/window ${_window_number};/kick ${nick}"
-@item(buffer_nicklist):button1-gesture-left-long = "/window ${_window_number};/kickban ${nick}"
-@item(buffer_nicklist):button2 = "/window ${_window_number};/whois ${nick}"
-@item(buffer_nicklist):button2-gesture-left = "/window ${_window_number};/ban ${nick}"
-@item(buffers):button1* = "hsignal:buffers_mouse"
-@item(buffers):button2* = "hsignal:buffers_mouse"
-@item(buflist):button1* = "hsignal:buflist_mouse"
-@item(buflist):button2* = "hsignal:buflist_mouse"
-@item(buflist2):button1* = "hsignal:buflist_mouse"
-@item(buflist2):button2* = "hsignal:buflist_mouse"
-@item(buflist3):button1* = "hsignal:buflist_mouse"
-@item(buflist3):button2* = "hsignal:buflist_mouse"
-@bar:wheeldown = "/bar scroll ${_bar_name} ${_window_number} +20%"
-@bar:wheelup = "/bar scroll ${_bar_name} ${_window_number} -20%"
-@chat:button1 = "/window ${_window_number}"
-@chat:button1-gesture-left = "/window ${_window_number};/buffer -1"
-@chat:button1-gesture-left-long = "/window ${_window_number};/buffer 1"
-@chat:button1-gesture-right = "/window ${_window_number};/buffer +1"
-@chat:button1-gesture-right-long = "/window ${_window_number};/input jump_last_buffer"
-@chat:wheeldown = "/window scroll_down -window ${_window_number}"
-@chat:wheelup = "/window scroll_up -window ${_window_number}"
-@*:button3 = "/cursor go ${_x},${_y}"
--- a/weechat/xfer.conf Sun May 18 14:59:11 2025 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-#
-# weechat -- xfer.conf
-#
-# WARNING: It is NOT recommended to edit this file by hand,
-# especially if WeeChat is running.
-#
-# Use commands like /set or /fset to change settings in WeeChat.
-#
-# For more info, see: https://weechat.org/doc/quickstart/
-#
-
-[look]
-auto_open_buffer = on
-progress_bar_size = 20
-pv_tags = "notify_private"
-
-[color]
-status_aborted = lightred
-status_active = lightblue
-status_connecting = yellow
-status_done = lightgreen
-status_failed = lightred
-status_waiting = lightcyan
-text = default
-text_bg = default
-text_selected = white
-
-[network]
-blocksize = 65536
-fast_send = on
-own_ip = ""
-port_range = ""
-send_ack = on
-speed_limit_recv = 0
-speed_limit_send = 0
-timeout = 300
-
-[file]
-auto_accept_chats = off
-auto_accept_files = off
-auto_accept_nicks = ""
-auto_check_crc32 = off
-auto_rename = on
-auto_resume = on
-convert_spaces = on
-download_path = "%h/xfer"
-download_temporary_suffix = ".part"
-upload_path = "~"
-use_nick_in_filename = on