summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-x.bin/.requirements.txt7
-rwxr-xr-x.bin/ag-audio55
-rwxr-xr-x.bin/ag-autorandr14
-rwxr-xr-x.bin/ag-light14
-rwxr-xr-x.bin/ag-lock21
-rwxr-xr-x.bin/ag-status56
-rwxr-xr-x.bin/ag-term9
-rwxr-xr-x.bin/cheat3
-rwxr-xr-x.bin/dot-bootstrap17
-rwxr-xr-x.bin/dot-clean7
-rwxr-xr-x.bin/dot-pkgs76
-rwxr-xr-x.bin/dot-sync25
-rwxr-xr-x.bin/draw-logo23
-rwxr-xr-x.bin/dwm-start17
-rwxr-xr-x.bin/get-mailbox-imap77
-rwxr-xr-x.bin/get-pass11
-rwxr-xr-x.bin/music70
-rwxr-xr-x.bin/nmutt19
-rwxr-xr-x.bin/nwsflux192
-rwxr-xr-x.bin/oak180
-rwxr-xr-x.bin/password-gen28
-rwxr-xr-x.bin/pinentry-common3
-rwxr-xr-x.bin/pipx-sync12
-rwxr-xr-x.bin/qb130
-rwxr-xr-x.bin/resume-to-pdf17
-rwxr-xr-x.bin/screen-copy22
-rwxr-xr-x.bin/show-pass22
-rwxr-xr-x.bin/start-org25
-rwxr-xr-x.bin/sync-public-dotfiles52
-rwxr-xr-x.bin/synchronize-ereader21
-rwxr-xr-x.bin/tenv87
-rwxr-xr-x.bin/term-color36
-rwxr-xr-x.bin/unlock-key5
-rwxr-xr-x.bin/wchat4
-rwxr-xr-x.bin/wttr3
-rwxr-xr-x.bin/x11-config39
-rwxr-xr-x.bin/x11-screen27
-rwxr-xr-x.bin/yubikey-reset20
-rwxr-xr-x.config/alacritty/alacritty.yml45
-rwxr-xr-x.config/alacritty/hidpi.yml16
-rwxr-xr-x.config/calcurse/conf32
-rwxr-xr-x.config/calcurse/keys55
-rwxr-xr-x.config/cmus/classic.theme48
-rwxr-xr-x.config/cmus/rc170
-rwxr-xr-x.config/dot/term-color-dark1
-rwxr-xr-x.config/dot/term-color-light1
-rwxr-xr-x.config/gopass/config.yml10
-rwxr-xr-x.config/i3/config177
-rwxr-xr-x.config/mimeapps.list12
-rwxr-xr-x.config/neomutt/neomuttrc128
-rwxr-xr-x.config/newsboat/config38
-rwxr-xr-x.config/nvim/.netrwhist12
-rwxr-xr-x.config/nvim/.nvim/autoload/plug.vim2526
-rwxr-xr-x.config/nvim/after/ftplugin/dhall.lua1
-rwxr-xr-x.config/nvim/after/ftplugin/dockerfile.lua4
-rwxr-xr-x.config/nvim/after/ftplugin/gitcommit.lua1
-rwxr-xr-x.config/nvim/after/ftplugin/javascript.lua3
-rwxr-xr-x.config/nvim/after/ftplugin/lua.lua6
-rwxr-xr-x.config/nvim/after/ftplugin/mail.lua4
-rwxr-xr-x.config/nvim/after/ftplugin/markdown.lua8
-rwxr-xr-x.config/nvim/after/ftplugin/nix.lua6
-rwxr-xr-x.config/nvim/after/ftplugin/python.lua1
-rwxr-xr-x.config/nvim/after/ftplugin/sh.lua1
-rwxr-xr-x.config/nvim/after/ftplugin/shell.lua0
-rwxr-xr-x.config/nvim/after/ftplugin/terraform.lua1
-rwxr-xr-x.config/nvim/after/ftplugin/typescript.lua2
-rwxr-xr-x.config/nvim/after/ftplugin/typescriptreact.lua2
-rwxr-xr-x.config/nvim/after/ftplugin/yaml.lua6
-rwxr-xr-x.config/nvim/after/indent/sass.vim4
-rwxr-xr-x.config/nvim/colors/colorscheme.vim208
-rwxr-xr-x.config/nvim/init.lua10
-rwxr-xr-x.config/nvim/init.old.vim218
-rwxr-xr-x.config/nvim/lua/ftplugin/common-markup.lua3
-rwxr-xr-x.config/nvim/lua/ftplugin/common-space.lua5
-rwxr-xr-x.config/nvim/lua/ftplugin/init.lua2
-rwxr-xr-x.config/nvim/lua/keymaps.lua24
-rwxr-xr-x.config/nvim/lua/lsp.lua17
-rwxr-xr-x.config/nvim/lua/plugins.lua145
-rwxr-xr-x.config/nvim/lua/settings.lua30
-rwxr-xr-x.config/nvim/lua/statusline.lua54
-rwxr-xr-x.config/nvim/lua/utils.lua46
-rwxr-xr-x.config/qutebrowser/config.py111
-rwxr-xr-x.config/qutebrowser/greasemonkey/boursorama-css.js18
-rwxr-xr-x.config/qutebrowser/greasemonkey/duckduckgo.js12
-rwxr-xr-x.config/qutebrowser/greasemonkey/github-css.js12
-rwxr-xr-x.config/qutebrowser/greasemonkey/imgur-to-imgin.js8
-rwxr-xr-x.config/qutebrowser/greasemonkey/instagram-to-bibliogram.js8
-rwxr-xr-x.config/qutebrowser/greasemonkey/medium-to-scribe.js8
-rwxr-xr-x.config/qutebrowser/greasemonkey/miniflux.js83
-rwxr-xr-x.config/qutebrowser/greasemonkey/minimal-css.js16
-rwxr-xr-x.config/qutebrowser/greasemonkey/no-sticky-headers.js15
-rwxr-xr-x.config/qutebrowser/greasemonkey/reddit-to-teddit.js9
-rwxr-xr-x.config/qutebrowser/greasemonkey/scaleway-css.js11
-rwxr-xr-x.config/qutebrowser/greasemonkey/scribe-css.js16
-rwxr-xr-x.config/qutebrowser/greasemonkey/stackoverflow-css.js15
-rwxr-xr-x.config/qutebrowser/greasemonkey/twitter-to-nitter.js9
-rwxr-xr-x.config/qutebrowser/greasemonkey/youtube-to-yewtube.js9
-rwxr-xr-x.config/sway/config166
-rwxr-xr-x.config/tmux/tmux.conf14
-rwxr-xr-x.config/user-dirs.dirs15
-rwxr-xr-x.config/user-dirs.locale1
-rwxr-xr-x.gnupg/gpg-agent.conf3
-rwxr-xr-x.kshrc13
-rwxr-xr-x.public-keys/0xF85CD02DB419D68C109
-rwxr-xr-x.weechat/buflist.conf41
-rwxr-xr-x.weechat/fset.conf96
-rwxr-xr-x.weechat/logger.conf36
-rwxr-xr-x.weechat/weechat.conf704
-rwxr-xr-x.xinitrc.dwm2
-rwxr-xr-x.zshrc62
110 files changed, 7151 insertions, 0 deletions
diff --git a/.bin/.requirements.txt b/.bin/.requirements.txt
new file mode 100755
index 0000000..3e69fba
--- /dev/null
+++ b/.bin/.requirements.txt
@@ -0,0 +1,7 @@
+
+# ~/.bin/.requirements.txt
+# dotfiles requirements for python packages
+
+requests
+miniflux
+ansible_runner
diff --git a/.bin/ag-audio b/.bin/ag-audio
new file mode 100755
index 0000000..f39522f
--- /dev/null
+++ b/.bin/ag-audio
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+__increase() {
+ pamixer -i "${1:-10}"
+}
+
+__decrease() {
+ pamixer -d "${1:-10}"
+}
+
+__mic_toggle() {
+ pamixer --default-source -t
+}
+
+__toggle() {
+ pamixer -t
+}
+
+__next() {
+ cmus-remote -n
+}
+
+__prev() {
+ cmus-remote -r
+}
+
+__play() {
+ cmus-remote -u
+}
+
+case "${1}" in
+ i*)
+ __increase "${2}"
+ ;;
+ d*)
+ __decrease "${2}"
+ ;;
+ mic-t*)
+ __mic_toggle
+ ;;
+ t*)
+ __toggle
+ ;;
+ n*)
+ __next
+ ;;
+ pr*)
+ __prev
+ ;;
+ pl*)
+ __play
+ ;;
+esac
+
+ag-status &>/dev/null
diff --git a/.bin/ag-autorandr b/.bin/ag-autorandr
new file mode 100755
index 0000000..0a75819
--- /dev/null
+++ b/.bin/ag-autorandr
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+screens=$(xrandr --listmonitors | tail -n +2 | rev | cut -d " " -f 1 | rev)
+screen_master=$(echo "${screens}" | cut -d " " -f 1)
+
+logger -s reset xrandr size
+xrandr -s 0
+xrandr --output "${screen_master}" --auto
+
+for screen in $(echo ${screens} | cut -d " " -f 2); do
+ xrandr --output "${screen}" --auto --right-of "${screen_master}"
+ echo --output "${screen}" --auto --right-of "${screen_master}"
+ screen_master=${screen}
+done
diff --git a/.bin/ag-light b/.bin/ag-light
new file mode 100755
index 0000000..0d15722
--- /dev/null
+++ b/.bin/ag-light
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+light -N 1
+
+case "${1}" in
+ i*)
+ light -A 10
+ ;;
+ d*)
+ light -U 10
+ ;;
+esac
+
+ag-status &>/dev/null
diff --git a/.bin/ag-lock b/.bin/ag-lock
new file mode 100755
index 0000000..e340245
--- /dev/null
+++ b/.bin/ag-lock
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+set -xe
+
+if pgrep xidle >/dev/null; then
+ pkill -USR1 xidle &
+elif command -v xsecurelock >/dev/null; then
+ xsecurelock &
+fi
+
+if [ ! "${1}" = "-s" ]; then
+ exit 0
+fi
+
+sleep 1
+
+if command -v systemctl; then
+ systemctl suspend
+elif command -v zzz; then
+ ! doas -n zzz && zzz
+fi
diff --git a/.bin/ag-status b/.bin/ag-status
new file mode 100755
index 0000000..150b9db
--- /dev/null
+++ b/.bin/ag-status
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+__cleanup_value() {
+ cat /dev/stdin | sed 's/%//g'
+}
+
+uname=$(uname)
+
+battery=""
+battery_status=""
+time=""
+volume=""
+
+while true; do
+ case "${uname}" in
+ OpenBSD)
+ battery=$(apm -l)
+ battery_status=$(apm |
+ grep "A/C" |
+ cut -d ":" -f 2 |
+ tr -d " ")
+ volume=$(sndioctl -n output.level |
+ cut -c 3-4)
+ ;;
+ Linux)
+ battery=$(acpi -b |
+ cut -d " " -f 4 |
+ __cleanup_value)
+ battery_status=$(acpi -a |
+ tr -s " " |
+ cut -d " " -f 3)
+ volume=$(pamixer --get-volume |
+ __cleanup_value)
+
+ if $(pamixer --get-mute); then
+ volume_status="__mute__"
+ fi
+ ;;
+ esac
+
+ time=$(date +%Y-%m-%dT%H:%M:%S)
+ status="VOL: ${volume}%"
+
+ if [ -n "${battery}" ]; then
+ status="${status} | BATTERY ${battery}%"
+ fi
+
+ status="${status} | DATE: ${time}"
+
+ echo "${status}"
+ xsetroot -name " ${status}"
+
+ [ "${1}" != "-l" ] && exit 0
+
+ sleep 5
+done
diff --git a/.bin/ag-term b/.bin/ag-term
new file mode 100755
index 0000000..2d0c6b1
--- /dev/null
+++ b/.bin/ag-term
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+set -xe
+
+terms="alacritty st xterm"
+
+for term in ${terms}; do
+ command -v "${term}" && exec "${term}"
+done
diff --git a/.bin/cheat b/.bin/cheat
new file mode 100755
index 0000000..cb6f4aa
--- /dev/null
+++ b/.bin/cheat
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+curl -s "cheat.sh/${1}"
diff --git a/.bin/dot-bootstrap b/.bin/dot-bootstrap
new file mode 100755
index 0000000..56840dc
--- /dev/null
+++ b/.bin/dot-bootstrap
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+set -x -e
+
+directories="git \
+ downloads \
+ .cache/dot \
+ .cache/neomutt \
+ .local/bin \
+ .local/share/dot"
+
+remote=$(yadm remote | head -n 1)
+yadm branch --set-upstream-to="${remote}/trunk" trunk
+
+for directory in ${directories}; do
+ [ ! -d "${HOME}/${directory}" ] && mkdir -p "${HOME}/${directory}"
+done
diff --git a/.bin/dot-clean b/.bin/dot-clean
new file mode 100755
index 0000000..5b326ea
--- /dev/null
+++ b/.bin/dot-clean
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+clean_files="*.core"
+
+for file in ${clean_files}; do
+ find "${HOME}" -maxdepth 2 -iname "*.core" -exec rm {} \;
+done
diff --git a/.bin/dot-pkgs b/.bin/dot-pkgs
new file mode 100755
index 0000000..f5c80ee
--- /dev/null
+++ b/.bin/dot-pkgs
@@ -0,0 +1,76 @@
+#!/usr/bin/env python3
+
+import subprocess
+import os
+
+ENV = {
+ 'path': f'{os.environ["HOME"]}/.cache/dot-pkgs',
+ 'bin_path': f'{os.environ["HOME"]}/.local/bin'
+}
+
+PKGS = [
+ {
+ 'src': 'git://git.suckless.org/dwm',
+ 'path': f'{ENV["path"]}/dwm',
+ 'bin': 'dwm',
+ 'type': 'git'
+ },
+ {
+ 'src': 'git://git.suckless.org/st',
+ 'path': f'{ENV["path"]}/st',
+ 'bin': 'st',
+ 'type': 'git'
+ },
+ {
+ 'src': 'git@st0dev1:_suckless/dwm',
+ 'path': f'{os.environ["HOME"]}/git.rgoncalves.se/_suckless/dwm',
+ 'bin': 'st',
+ 'type': 'git'
+ },
+ {
+ 'src': '',
+ 'type': ''
+ }
+]
+
+
+def copy_executable_file(src, dst):
+ pass
+
+
+def merge_mappings(pkgs, env):
+ """
+ Merge two dict in the first one.
+ """
+ for index, pkg in enumerate(pkgs):
+ pkgs[index] = {**env, **pkg}
+
+
+def handle_git(pkg):
+ """
+ Clone and update git repo.
+ """
+ commands = [
+ ['git', 'clone', pkg['src'], pkg['path']],
+ ['git', 'pull']
+ ]
+
+ for command in commands:
+ subprocess.run(command, cwd=pkg['path'])
+
+
+def handle_make(pkg):
+ subprocess.run(['make'], cwd=pkg['path'])
+
+
+def main():
+ merge_mappings(PKGS, ENV)
+
+ for pkg in [pkg for pkg in PKGS if pkg['type'] == 'git']:
+ os.makedirs(pkg['path'], exist_ok=True)
+ handle_git(pkg)
+ handle_make(pkg)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/.bin/dot-sync b/.bin/dot-sync
new file mode 100755
index 0000000..7f4d490
--- /dev/null
+++ b/.bin/dot-sync
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+set -x -e
+
+export GIT_SSH_COMMAND="ssh -o ConnectTimeout=1 -o ConnectionAttempts=1"
+
+yadm pull
+
+yadm add -u
+yadm add \
+ $HOME/.bin \
+ $HOME/.config/i3 \
+ $HOME/.config/neomutt \
+ $HOME/.config/newsboat \
+ $HOME/.config/nvim \
+ $HOME/.config/qutebrowser/{*.py,greasemonkey} \
+ $HOME/.config/sway \
+ $HOME/.config/yadm \
+ $HOME/.config/waybar \
+ $HOME/.public-keys
+
+yadm push
+
+yadm commit -m "$(date +%Y-%m-%dT%H:%M:%S)"
+yadm push
diff --git a/.bin/draw-logo b/.bin/draw-logo
new file mode 100755
index 0000000..d65bf8f
--- /dev/null
+++ b/.bin/draw-logo
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+set -xe
+
+circle_coordinates="256,256,256"
+background_color="white"
+foreground_color="black"
+
+convert -size 512x512 \
+ "xc:${backgrond_color}" \
+ -fill "${foreground_color}" \
+ -draw "circle ${circle_coordinates},32" \
+ -fill "${background_color}" \
+ -draw "circle ${circle_coordinates},96" \
+ "${1}.png"
+
+convert "${1}.png" -resize "50%" "${1}-medium.png"
+convert "${1}.png" -resize "25%" "${1}-small.png"
+
+convert "${1}-small.png" \
+ -level -10%,12% \
+ -ordered-dither o8x8 \
+ "${1}-dithered.png"
diff --git a/.bin/dwm-start b/.bin/dwm-start
new file mode 100755
index 0000000..e411db5
--- /dev/null
+++ b/.bin/dwm-start
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+kill -9 xidle
+xidle &
+
+while true; do
+ # status
+ pkill -9 -f "ag-status"
+ ag-status -l >/dev/null &
+
+ # x11 configuration
+ x11-config
+ . ~/.bin/x11-screen
+
+ # dwm
+ dwm 2> ~/.dwm.log
+done
diff --git a/.bin/get-mailbox-imap b/.bin/get-mailbox-imap
new file mode 100755
index 0000000..fad7e21
--- /dev/null
+++ b/.bin/get-mailbox-imap
@@ -0,0 +1,77 @@
+#!/usr/bin/env python3
+
+import imaplib
+import sys
+
+
+def get_boxes(list):
+ """
+ Retrieve and decode all mailboxes.
+ """
+ return [box.decode('utf-8').rsplit(' ')[-1] for box in list]
+
+
+def flatten_output(list):
+ """
+ Print all boxes with a flattened output,
+ primarily for neomutt usage.
+ """
+ print(''.join([f"+'{box}' " for box in list]), end=' ')
+
+
+def sort_boxes(bxs):
+ """
+ Sort boxes list,
+ according to a predefined order
+ """
+
+ order = [
+ "INBOX",
+ "Unread",
+ "Drafts",
+ "Sent",
+ "Spam",
+ "Trash",
+ "Junk",
+ "Archive"
+ ]
+
+ bxs_orig = sorted(bxs)
+
+ # sort based on predefined order
+ bxs = []
+ for exp in order:
+ matching = [s for s in bxs_orig if exp in s]
+ bxs.extend(matching)
+
+ # ensure all retrieved boxes are present
+ for bx in bxs_orig:
+ if bx not in bxs:
+ bxs.append(bx)
+
+ return bxs
+
+
+def main():
+ """
+ Retrieve, sort, and pretty print for neomutt
+ """
+
+ # user information
+ remote = sys.argv[1]
+ username = sys.argv[2]
+ password = sys.argv[3]
+
+ # connection
+ mail = imaplib.IMAP4_SSL(remote)
+ mail.login(username, password)
+
+ # parse folders output
+ bxs = sort_boxes(get_boxes(mail.list()[1]))
+
+ # oneline pretty-print for neomutt
+ flatten_output(bxs)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/.bin/get-pass b/.bin/get-pass
new file mode 100755
index 0000000..4cdeafd
--- /dev/null
+++ b/.bin/get-pass
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+set -e
+
+[ ${#} -ne 0 ]
+
+password_file=$(gopass ls --flat | grep "${@}" | head -n 1)
+[ "${password_file}" ]
+
+echo "+ password: ${password_file}" >&2
+exec gopass show -o "${password_file}"
diff --git a/.bin/music b/.bin/music
new file mode 100755
index 0000000..5498dc0
--- /dev/null
+++ b/.bin/music
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+
+MUSIC_DIR="${HOME}/music"
+MUSIC_LIST=
+MUSIC_FILE=
+MUSIC_YT_OPTIONS=
+
+log() {
+ echo "[${0} ] ${@}"
+}
+
+main() {
+ # arguments
+ while getopts "c:" arg; do
+ case "${arg}" in
+ c)
+ MUSIC_FILE="${OPTARG}"
+ ;;
+ h)
+ exit 0
+ ;;
+ esac
+ done
+
+ # ensure parameters are correct
+ [ ! -f "${MUSIC_FILE}" ] && exit 1
+
+
+ while read -r line; do
+
+ # skip comments
+ line=$(echo ${line} | grep -v -e "^$" -e "^#")
+ [ -z "${line}" ] && continue
+
+ # retrieve playlist params
+ url=$(echo "${line}" | cut -d " " -f 1)
+ dir=$(echo "${line}" | cut -d " " -f 2)
+
+ dir="${MUSIC_DIR}/${dir}"
+
+ [ -d "${dir}" ] &&
+ log "${dir}: directory already exists" &&
+ continue
+
+ mkdir "${dir}"
+ log "${dir} ${url}: download"
+
+ youtube-dl --rm-cache-dir >/dev/null
+ youtube-dl \
+ --extract-audio \
+ --audio-format mp3 \
+ --prefer-ffmpeg \
+ --audio-quality 0 \
+ --embed-thumbnail \
+ --metadata-from-title "%(artist)s - %(title)s" \
+ --no-warnings \
+ --ignore-errors \
+ --no-overwrites \
+ --continue \
+ --add-metadata \
+ --user-agent "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" \
+ --output "${dir}/'%(title)s.%(ext)s'" \
+ "${url}"
+
+
+ done < "${MUSIC_FILE}"
+}
+
+main ${@}
diff --git a/.bin/nmutt b/.bin/nmutt
new file mode 100755
index 0000000..9a2bb28
--- /dev/null
+++ b/.bin/nmutt
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+NMUTT_CONFIGURATION_FILE="${HOME}/.config/neomutt/personal"
+
+get_param() {
+ grep "${2}" "${1}" |
+ head -n 1 |
+ rev |
+ cut -d " " -f 1 |
+ rev |
+ tr -d '"'
+}
+
+# export server/username for get-mailbox-imap script
+export MAIL_SERVER=$(get_param "${NMUTT_CONFIGURATION_FILE}" "my_server")
+export MAIL_USERNAME=$(get_param "${NMUTT_CONFIGURATION_FILE}" "my_user")
+
+export MAIL_PASSWORD=$(get-pass "mailbox")
+exec neomutt ${@}
diff --git a/.bin/nwsflux b/.bin/nwsflux
new file mode 100755
index 0000000..3e725b0
--- /dev/null
+++ b/.bin/nwsflux
@@ -0,0 +1,192 @@
+#!/usr/bin/env python3
+#
+# nwsflux
+
+import miniflux
+import urllib3
+import logging
+import os
+import re
+import shlex
+import argparse
+
+
+def get_local_feed(line: str) -> dict:
+ """
+ Parse a line and return a constructed dict like miniflux's API.
+ """
+ feed_url = shlex.split(line)[0]
+ title = shlex.split(line)[-1]
+ tag = shlex.split(line)[1]
+ if tag == title:
+ tag = 'all'
+
+ return {
+ 'feed_url': feed_url,
+ 'title': title,
+ 'category': {
+ 'title': tag
+ }
+ }
+
+
+def get_local_feeds(filename: str) -> list:
+ """
+ Read feeds from a newsboat url file.
+ """
+
+ with open(filename, 'r') as f:
+ content = f.readlines()
+
+ r = re.compile('^http.*://')
+ content = list(filter(r.match, content))
+
+ return [get_local_feed(line) for line in content]
+
+
+def delete_categories(client: miniflux.Client, local_cats: dict, remote_cats: dict):
+ """
+ Remove remote categories that are absent in local file.
+
+ . Useless for now, miniflux's API return non-empty categories only!
+ """
+ for remote in remote_cats:
+ if any(local['title'] == remote['title'] for local in local_cats):
+ continue
+ try:
+ logging.info(f'remove category: {remote["title"]}')
+ client.delete_category(remote['id'])
+ # ignores categories that are empty on remote
+ except miniflux.ClientError as e:
+ logging.error('can not remove non-empty category:'
+ f'{remote["title"]} {e}')
+
+
+def create_categories(client: miniflux.Client, local_cats: dict, remote_cats: dict):
+ """
+ Create categories present in local file and absent on remote.
+ """
+ for local in local_cats:
+ if any(remote['title'] == local['title'] for remote in remote_cats):
+ continue
+ try:
+ logging.info(f'create category: {local["title"]}')
+ client.create_category(local['title'])
+ # ignores categories that are empty on remote
+ except miniflux.ClientError as e:
+ logging.error(f'remote category empty: {local["title"]} {e}')
+
+
+def sync_categories(client: miniflux.Client, local_feeds: dict, remote_feeds: dict) -> dict:
+ local_cats = get_uniq_categories(local_feeds)
+ remote_cats = get_uniq_categories(remote_feeds)
+
+ delete_categories(client, local_cats, remote_cats)
+ create_categories(client, local_cats, remote_cats)
+ return client.get_categories()
+
+
+def get_uniq_categories(buffer: list) -> list:
+ return list(map(dict, frozenset(
+ frozenset(x['category'].items()) for x in buffer
+ )))
+
+
+def delete_feeds(client: miniflux.Client, local_feeds: dict, remote_feeds: dict):
+ """
+ Remove remote feeds that are absent in local file.
+ """
+ for remote in remote_feeds:
+ if any(local['feed_url'] == remote['feed_url'] for local in local_feeds):
+ continue
+ logging.info(f'remove feed: {remote["feed_url"]}')
+ client.delete_feed(remote['id'])
+
+
+def create_feeds(client: miniflux.Client, local_feeds: dict, remote_feeds: dict):
+ """
+ Create feeds that are present in local file and absent on remote.
+ """
+ categories = client.get_categories()
+
+ for local in local_feeds:
+ if any(remote['feed_url'] == local['feed_url'] for remote in remote_feeds):
+ continue
+ logging.info(f'create feed: {local}')
+ category_id = next((x['id'] for x in categories if x['title'] == local['category']['title']), None)
+ try:
+ client.create_feed(local['feed_url'], category_id)
+ except miniflux.ClientError as e:
+ logging.error(e)
+
+
+def sync_feeds(client: miniflux.Client, local_feeds: dict, remote_feeds: dict) -> dict:
+ """
+ Synchronize all given feeds.
+ """
+ delete_feeds(client, local_feeds, remote_feeds)
+ create_feeds(client, local_feeds, remote_feeds)
+ return client.get_feeds()
+
+
+def parse():
+ """
+ Parse command-line arguments.
+ """
+ parser = argparse.ArgumentParser(description='Synchronize RSS feeds from'
+ 'newsboat to an miniflux instance.')
+
+ parser.add_argument('-c', dest='config', type=str, required=True,
+ help='Newsboat url file')
+ parser.add_argument('-k', dest='key', type=str, required=True,
+ help='Miniflux API key')
+ parser.add_argument('-u', dest='url', type=str, required=True,
+ help='Miniflux url')
+ parser.add_argument('-d', dest='debug', action='store_true',
+ help='Enable debugging output')
+ parser.add_argument('-e', dest='export', action='store_true',
+ help='Export Miniflux OPML config to stdout')
+
+ return parser.parse_args()
+
+
+def main():
+
+ """
+ SSL verify.
+ """
+ os.environ["CURL_CA_BUNDLE"] = ""
+ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
+
+ """
+ Arguments.
+ """
+ args = parse()
+
+ """
+ Debugging.
+ """
+ logging.basicConfig(level=logging.INFO)
+ if not args.debug:
+ logging.level = logging.NOTSET
+
+ """
+ Synchronization.
+ """
+ client = miniflux.Client(args.url, api_key=args.key)
+
+ local_feeds = get_local_feeds(args.config)
+ remote_feeds = client.get_feeds()
+
+ sync_categories(client, local_feeds, remote_feeds)
+ sync_feeds(client, local_feeds, remote_feeds)
+
+ """
+ Export Miniflux to OPML.
+ """
+ if args.export:
+ print(client.export_feeds())
+
+
+if __name__ == '__main__':
+ main()
diff --git a/.bin/oak b/.bin/oak
new file mode 100755
index 0000000..4ea560e
--- /dev/null
+++ b/.bin/oak
@@ -0,0 +1,180 @@
+#!/bin/sh
+# _
+# | |
+# __ __, | |
+# / \_/ | |/_)
+# \__/ \_/|_/| \_/
+#
+# ~/.bin/oak
+#
+# manage multiple dotfiles repo in same work tree
+#
+# Example:
+# dotfiles.d/
+# |_ config
+# |_ secret
+#
+# ~ rgoncalves.se
+
+usage() {
+ cat <<- EOF
+ USAGE ${0} : git_dirname git_action [arguments...]
+ -b: enable bootstraping for absent repository
+ -d: oak directory to work on
+ works in batch mode if no directory is provided
+ -f: force all operations
+ use with caution
+ -m: commit message when using -s option
+ commits_msg_have_to_be_written_like_this
+ -r: git remote with full path to parent directory
+ -h: show usage of ${0}
+ EOF
+}
+
+log() {
+ echo "[OAK ] ${@}"
+}
+
+git_chroot() {
+ log ">> ${1}"
+ git --git-dir="${OAK_ROOT}/${1}" --work-tree="${HOME}" ${2}
+}
+
+git_bootstrap() {
+ # directory url remote
+ git_chroot "${1}" "clone --bare git@${2}/${1} ${OAK_ROOT}/${1}"
+ [ "${?}" -ne 0 ] && log "Failed to bootstrap ${1}" && exit 1
+ git_chroot "${1}" "checkout trunk" >/dev/null 2>&1
+ git_chroot "${1}" "branch --set-upstream-to trunk origin/trunk" >/dev/null 2>&1
+ git_chroot "${1}" "push -u origin trunk" >/dev/null 2>&1
+}
+
+git_sync() {
+ # actions
+ git_sane_all
+ pull_out=$(git_chroot "${1}" "pull")
+ if [ "${?}" -ne 0 ]; then
+ log "error while doing git pull"
+ exit 1
+ fi
+ echo "${pull_out}" 2>&1 | grep " ."
+ git_chroot "${1}" "add -u" >/dev/null
+ git_chroot "${1}" "commit -m ${OAK_MSG}"
+ git_chroot "${1}" "push --set-upstream origin trunk"
+ # cleanup
+ unset pull_out push_out
+}
+
+git_list() {
+ log "Available repositories : "
+ for dir in ${OAK_DIRS}; do
+ log " - ${dir}"
+ done
+}
+
+git_sane() {
+ if [ "${1}" = "${2}" ]; then
+ return 1
+ fi
+ git_chroot "${1}" "ls-files" > ~/.cache/oak_a
+ git_chroot "${2}" "ls-files" > ~/.cache/oak_b
+ out=$(grep -F -x -f ~/.cache/oak_a ~/.cache/oak_b)
+ if [ -n "${out}" ]; then
+ log "detected similar files for directories: ${repo_a}, ${repo_b}"
+ echo "${out}"
+ return 1
+ fi
+ return 0
+}
+
+git_sane_all() {
+ for repo_a in ${DOT_DIRS}; do
+ for repo_b in ${DOT_DIRS}; do
+ if [ "${repo_a}" = "${repo_b}" ]; then
+ continue
+ fi
+ git_sane "${DOT_ROOT}/${repo_a}" "${DOT_ROOT}/${repo_b}"
+ if [ "${?}" -ne 0 ]; then
+ log "aborting"
+ exit 1
+ fi
+ done
+ done
+}
+
+main() {
+
+ OAK_ROOT="${HOME}/.dotfiles.d"
+ OAK_DIRS=$(ls "${OAK_ROOT}")
+ OAK_DIR=""
+ OAK_REMOTE=""
+ OAK_CMD=""
+ OAK_MSG=""
+
+ # Retrieve oak params
+ while getopts "bd:hlm:r:s" arg; do
+ case "${arg}" in
+ b)
+ OAK_BOOTSTRAP=1
+ ;;
+ d)
+ OAK_DIR="${OPTARG}"
+ ;;
+ f)
+ OAK_FORCE=1
+ ;;
+ h)
+ usage && exit 0
+ ;;
+ m)
+ OAK_MSG="$(echo ${OPTARG} | tr -s '_' ' ')"
+ ;;
+ l)
+ git_list
+ exit 0
+ ;;
+ r)
+ OAK_REMOTE="${OPTARG}"
+ ;;
+ s)
+ OAK_SYNC=1
+ ;;
+ esac
+ done
+
+ [ -z "${OAK_MSG}" ] && OAK_MSG=$(date +%j_%H_%M_%S)
+
+ # Bootstrap non-present repositories
+ if [ -n "${OAK_DIR}" ] && [ ! -d "${OAK_ROOT}/${OAK_DIR}" ]; then
+ git_bootstrap "${OAK_DIR}" "${OAK_REMOTE}"
+ if [ "${?}" -ne 0 ]; then
+ log "Git repository does not exist. Error"
+ exit 1
+ fi
+ log "Successfully bootstraped git repository"
+ fi
+
+ # Retrieve oak command for git
+ shift $(expr "${OPTIND}" - 1)
+ for cmd in "${@}"; do
+ OAK_CMD="${OAK_CMD}${cmd} "
+ done
+
+ # Git synchronization in each repository
+ if [ -n "${OAK_SYNC}" ]; then
+ for dir in ${OAK_DIR:-$OAK_DIRS}; do
+ log "synchronize ${dir}"
+ git_sync "${dir}"
+ done
+ fi
+
+ # Raw command in each git repository
+ if [ -n "${OAK_CMD}" ]; then
+ for dir in ${OAK_DIR:-$OAK_DIRS}; do
+ git_chroot "${dir}" "${OAK_CMD}"
+ done
+ fi
+
+}
+
+main $@
diff --git a/.bin/password-gen b/.bin/password-gen
new file mode 100755
index 0000000..e29dcb5
--- /dev/null
+++ b/.bin/password-gen
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+pass_length="24"
+
+usage() {
+ cat <<-EOF
+ usage: pagen [-l pass_length]
+ EOF
+}
+
+main() {
+ while getopts "l:" arg; do
+ case "${arg}" in
+ l)
+ pass_length="${OPTARG-$pass_length}"
+ ;;
+ *)
+ usage
+ exit 1
+ ;;
+ esac
+ done
+
+ # generate and display password
+ openssl rand -base64 "${pass_length}"
+}
+
+main $@
diff --git a/.bin/pinentry-common b/.bin/pinentry-common
new file mode 100755
index 0000000..c97af72
--- /dev/null
+++ b/.bin/pinentry-common
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+exec pinentry-curses
diff --git a/.bin/pipx-sync b/.bin/pipx-sync
new file mode 100755
index 0000000..181768e
--- /dev/null
+++ b/.bin/pipx-sync
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+set -x -e
+
+packages="pywal
+ ipython"
+
+pipx upgrade-all
+
+for package in ${packages}; do
+ pipx install "${package}" || true
+done
diff --git a/.bin/qb b/.bin/qb
new file mode 100755
index 0000000..3caf57e
--- /dev/null
+++ b/.bin/qb
@@ -0,0 +1,130 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+import yaml
+
+
+def bypass_file_exists(func, *args):
+ """
+ Exceptions for existing files are ignored.
+ """
+ try:
+ func(*args)
+ except FileExistsError as e:
+ print(e)
+
+
+def start_qutebrowser(args):
+ """
+ Replace the current process with qutebrowser.
+ """
+ os.execvp('qutebrowser', args)
+
+
+def generate_tab(url):
+ full_url = f'https://{url}'
+
+ return {
+ 'active': True,
+ 'history': [{
+ 'active': True,
+ 'pinned': True,
+ 'last_visited': '2021-09-16T12:22:57',
+ 'scroll-pos': {'x': 0, 'y': 0},
+ 'zoom': 1.0,
+ 'title': url,
+ 'original-url': full_url,
+ 'url': full_url
+ }]
+ }
+
+
+def restore_default_tabs(profile, path=None):
+ """
+ Declarative way to enforce opened/favorite tabs.
+ """
+ mapping = {
+ 'default': [
+ 'mailbox.org',
+ 'news.ycombinator.com',
+ 'miniflux.rgoncalves.se',
+ 'status.rgoncalves.se'
+ ],
+ 'work': [
+ 'mail.zoho.eu',
+ 'cliq.zoho.eu',
+ 'gitlab.viperdev.io',
+ 'projects.zoho.eu',
+ 'kb.viperdev.io',
+ ]
+ }
+
+ assert mapping[profile]
+
+ # Open file an cleanup
+ with open(path, 'r') as file:
+ data = yaml.safe_load(file)
+
+ # Scrap existing tabs,
+ # then filter pinned tabs
+ original_tabs = data['windows'][0]['tabs']
+ tabs = [tab['history'][-1] for tab in original_tabs
+ if len(tab['history']) != 0]
+ tabs = [tab for tab in tabs if tab['pinned']]
+
+ missing_urls = [url for url in mapping[profile]
+ if not any(url in tab['url'] for tab in tabs)]
+
+ for url in missing_urls:
+ original_tabs.append(generate_tab(url))
+
+ original_tabs.sort(reverse=True, key=lambda k: k['history'][-1]['pinned'])
+
+ # Persist tabs in yaml file
+ with open(path, 'w') as file:
+ yaml.dump(data, file)
+
+
+def main():
+ try:
+ QB_PROFILE = f'{sys.argv[1]}'
+ except IndexError:
+ QB_PROFILE = 'default'
+
+ """
+ restore_default_tabs(
+ QB_PROFILE,
+ path=f'{os.environ["HOME"]}/' +
+ '.local/share/qutebrowser/sessions/default.yml'
+ )
+ """
+ start_qutebrowser([' '])
+ sys.exit(0)
+
+ QB_DIR = f'{os.environ["HOME"]}/.config/qutebrowser'
+ QB_PROFILES_DIR = f'{QB_DIR}/_profiles'
+ QB_PROFILE_DIR = f'{QB_PROFILES_DIR}/{QB_PROFILE}'
+ QB_PROFILE_DIR_CONFIG = f'{QB_PROFILE_DIR}/config'
+ QB_PROFILE_TAG = QB_PROFILE[0].lower()
+
+ # Ensure directory existence
+ for dir in [QB_PROFILES_DIR, QB_PROFILE_DIR, QB_PROFILE_DIR_CONFIG]:
+ bypass_file_exists(os.mkdir, dir)
+
+ # Ensure common files are shared with other profiles
+ for file in ['config.py', 'bookmarks', 'greasemonkey']:
+ bypass_file_exists(os.symlink,
+ f'{QB_DIR}/{file}',
+ f'{QB_PROFILE_DIR_CONFIG}/{file}')
+
+ start_qutebrowser([
+ ' ', '--basedir', QB_PROFILE_DIR,
+ '--set', 'window.title_format', f'[{QB_PROFILE_TAG}]',
+ '--set', 'tabs.title.format',
+ f'[{QB_PROFILE_TAG}] {{audio}}{{index}}: {{current_title}}'
+ ])
+
+
+if __name__ == '__main__':
+ main()
diff --git a/.bin/resume-to-pdf b/.bin/resume-to-pdf
new file mode 100755
index 0000000..04dfba8
--- /dev/null
+++ b/.bin/resume-to-pdf
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+src="https://rgoncalves.se/a/resume.html"
+out="resume.pdf"
+margin="2cm"
+
+wkhtmltopdf --page-size A4 \
+ --orientation portrait \
+ --dpi 300 \
+ --image-dpi 300 \
+ --image-quality 100 \
+ --margin-top "${margin}" \
+ --margin-right "${margin}" \
+ --margin-bottom "${margin}" \
+ --margin-left "${margin}" \
+ "${src}" \
+ "${out}"
diff --git a/.bin/screen-copy b/.bin/screen-copy
new file mode 100755
index 0000000..9078dfb
--- /dev/null
+++ b/.bin/screen-copy
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# Copy screen area to clipboard
+
+. ~/.bin/tenv
+
+main() {
+ filename="/tmp/screenshot-$(date +%F_%T).png"
+
+ case "${_DISPLAY_SERVER}" in
+ xorg)
+ scrot -s "${filename}"
+ xclip -selection clip -t image/png "${filename}"
+ ;;
+ *)
+ echo " display server not supported" >&2
+ ;;
+ esac
+
+}
+
+main $@
diff --git a/.bin/show-pass b/.bin/show-pass
new file mode 100755
index 0000000..568570e
--- /dev/null
+++ b/.bin/show-pass
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+usage () {
+ cat >&2 <<-EOF
+ usage: ${0} password_regexp
+ EOF
+}
+
+log () {
+ echo ["${0}"] $@ >&2
+}
+
+main() {
+ # verify arguments
+ [ "${#}" -ne 1 ] && usage && exit 1
+
+ # retrieve password
+ log "retrieving password for : ${1}"
+ gopass show --password $(gopass ls --flat | grep "${1}" | head -n 1)
+}
+
+main $@
diff --git a/.bin/start-org b/.bin/start-org
new file mode 100755
index 0000000..c32b99e
--- /dev/null
+++ b/.bin/start-org
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+set -xe
+
+session_name="org"
+windows="wchat:weechat
+ nmutt:neomutt
+ calcurse:calcurse"
+
+if [ "${1}" = "-f" ]; then
+ tmux kill-session -t "${session_name}"
+fi
+
+tmux new-session -s "${session_name}" -d
+
+for window in ${windows}; do
+ window_cmd=$(echo "${window}" | cut -d ":" -f 1)
+ window_name=$(echo "${window}" | cut -d ":" -f 2)
+
+ tmux new-window -n "${window_name}"
+ tmux send-keys -t "${session_name}:${window_name}" \
+ "${window_cmd} "
+done
+
+tmux a -t "${session_name}"
diff --git a/.bin/sync-public-dotfiles b/.bin/sync-public-dotfiles
new file mode 100755
index 0000000..7bff64f
--- /dev/null
+++ b/.bin/sync-public-dotfiles
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+set -e
+
+command -v yadm git
+
+allowed_patterns="-e ^.bin
+ -e ^.config/alacritty/
+ -e ^.config/calcurse/
+ -e ^.config/cmus/classic.theme
+ -e ^.config/cmus/rc
+ -e ^.config/dot/term-color-*
+ -e ^.config/gopass/config.yml
+ -e ^.config/i3*/config
+ -e ^.config/tmux/
+ -e ^.config/mimeapps.list
+ -e ^.config/neomutt/neomuttrc
+ -e ^.config/newsboat/config
+ -e ^.config/nvim/
+ -e ^.config/qutebrowser/config.py
+ -e ^.config/qutebrowser/greasemonkey/
+ -e ^.config/sway/config
+ -e ^.config/systemd/*.service
+ -e ^.config/user-dirs.dir
+ -e ^.config/user-dirs.locale
+ -e ^.gnupg/gpg-agent.conf
+ -e ^.public-keys/
+ -e ^.kshrc
+ -e ^.weechat/buflist.conf
+ -e ^.weechat/fset.conf
+ -e ^.weechat/logger.conf
+ -e ^.weechat/weechat.conf
+ -e ^.xinitrc
+ -e ^.zshrc
+ "
+
+[ "$(yadm rev-parse --show-toplevel)" != "$(git rev-parse --show-toplevel)" ]
+
+# retrieve existing files
+upstream_files=$(cd "${HOME}" && yadm ls-files)
+local_files=$(find . -not -path "./.git*" -not -path ".")
+
+echo "${local_files}" | xargs rm -rf
+
+allowed_files=$(echo "${upstream_files}" | grep ${allowed_patterns})
+for allowed_file in ${allowed_files}; do
+ echo "${allowed_file}"
+ install -D "${HOME}/${allowed_file}" "${allowed_file}"
+done
+
+git add .
+git commit -m "$(date)"
diff --git a/.bin/synchronize-ereader b/.bin/synchronize-ereader
new file mode 100755
index 0000000..482a846
--- /dev/null
+++ b/.bin/synchronize-ereader
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+set -xe
+
+src_dir=$(readlink -f "${1}")
+out_dir=$(readlink -f "${2}")
+
+[ -d "${src_dir}" ]
+[ -d "${out_dir}" ]
+
+rsync -hvrPt \
+ --delete-after \
+ --fuzzy \
+ --prune-empty-dirs \
+ --include "*.pdf" \
+ --include "*.epub" \
+ --include "*.png" \
+ --include "*.jpg" \
+ --include "*/" \
+ --exclude "*" \
+ "${src_dir}/" "${out_dir}/"
diff --git a/.bin/tenv b/.bin/tenv
new file mode 100755
index 0000000..7718d89
--- /dev/null
+++ b/.bin/tenv
@@ -0,0 +1,87 @@
+#!/bin/sh
+#
+# o
+# _|_ _ _ _ _ _
+# | | / |/ | | | |/ / |/ | | |_
+# |_/|_/ | |_/ \_/|/ |__/ | |_/ \/
+# /|
+# \|
+#
+# tiny env
+# scraps system and user's configuration,
+# providing unified env. variables accross exotic setup.
+#
+# ~ rgoncalves.se
+
+log() {
+ echo [] "${@}"
+}
+
+usage() {
+ cat <<-EOF
+ usage: tinyenv [-d]
+ EOF
+}
+
+get_os_distribution() {
+ # tmp
+ os=$(uname | tr "[:upper:]" "[:lower:]")
+ distribution=$(uname -r)
+ # logic
+ case "${os}" in
+ linux)
+ distribution=$(uname -r)
+ ;;
+ esac
+ # return
+ export _OS_DISTRIBUTION="${os}_${distribution}"
+ unset -v os distribution
+}
+
+get_display() {
+ # tmp
+ list="wayland Xorg"
+ # logic
+ for display in ${list}; do
+ # search display and skip export if not found
+ pgrep "${display}" >/dev/null
+ [ "${?}" -ne 0 ] && display="none" && continue
+ # found display server
+ break
+ done
+ # return
+ export _DISPLAY_SERVER=$(echo "${display}" | tr "[:upper:]" "[:lower:]")
+ unset -v list display
+}
+
+get_screens() {
+ # tmp
+ screens="1"
+ # logic
+ [ "${_DISPLAY_SERVER}" = "xorg" ] && \
+ screens=$(xrandr | grep " connected" | wc -l | tr -d " ")
+ # return
+ export _SCREENS="${screens}"
+ unset screens
+}
+
+show_env() {
+ list=$(env | grep "^_.*" | sort -n)
+ for el in ${list}; do
+ log "${el}"
+ done
+ unset -v list el
+}
+
+main() {
+
+ # must be first
+ get_os_distribution
+ # alpha/numeric ordered
+ get_display
+ get_screens
+
+ [ -n "${DEBUG}" ] && show_env
+}
+
+main $@
diff --git a/.bin/term-color b/.bin/term-color
new file mode 100755
index 0000000..45a2dcc
--- /dev/null
+++ b/.bin/term-color
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+set -xe
+
+sequences="${HOME}/.config/dot/term-color"
+cache_file="${HOME}/.cache/dot/sequences"
+
+if [ "${1}" = "-l" ]; then
+ sequences="${sequences}-light"
+else
+ sequences="${sequences}-dark"
+fi
+
+[ -f "${sequences}" ]
+
+case $(uname) in
+ OpenBSD)
+ ttys=$(ps |
+ tail -n +2 |
+ tr -s " " |
+ sed 's/^ //g' |
+ cut -d " " -f 2 |
+ sed 's/-$//g' |
+ uniq |
+ sed 's/^/\/dev\/tty/g')
+ ;;
+ Linux)
+ ttys=$(find /dev/pts -iname "[0-9]*")
+ ;;
+esac
+
+for tty in $ttys; do
+ [ -c "${tty}" ] && cat "${sequences}" > $tty
+done
+
+cp "${sequences}" "${cache_file}"
diff --git a/.bin/unlock-key b/.bin/unlock-key
new file mode 100755
index 0000000..d0bebbf
--- /dev/null
+++ b/.bin/unlock-key
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+set -xe
+
+gopass show -c .buffer 2>/dev/null
diff --git a/.bin/wchat b/.bin/wchat
new file mode 100755
index 0000000..83f13a3
--- /dev/null
+++ b/.bin/wchat
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+export WEECHAT_PASSPHRASE="$(get-pass weechat)"
+exec weechat $@
diff --git a/.bin/wttr b/.bin/wttr
new file mode 100755
index 0000000..c06c825
--- /dev/null
+++ b/.bin/wttr
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+curl -s "wttr.in/${1}?m"
diff --git a/.bin/x11-config b/.bin/x11-config
new file mode 100755
index 0000000..5230c13
--- /dev/null
+++ b/.bin/x11-config
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+set -xe
+
+wallpaper_file="${HOME}/.local/share/dot/wallpaper"
+synclient_options="TapButton1=1 \
+ TapButton2=3 \
+ TapButton3=2 \
+ PalmDetect=1 \
+ TouchpadOff=0"
+
+# synaptic
+if command -v syndaemon; then
+ pkill syndaemon && syndaemon -RKd -i 0.2
+ synclient ${synclient_options}
+fi
+
+# keyboard
+xset r rate 250 75
+setxkbmap -option compose:ralt
+
+# screen saving
+xset s off
+xset s noblank
+xset -dpms
+
+if [ $(uname -s) = "OpenBSD" ]; then
+ xinput set-prop "/dev/wsmouse" "WS Pointer Wheel Emulation" 1
+ xinput set-prop "/dev/wsmouse" "WS Pointer Wheel Emulation Button" 2
+ xinput set-prop "/dev/wsmouse" "WS Pointer Wheel Emulation Axes" 6 7 4 5
+fi
+
+# background
+xsetroot -mod 2 2 -fg white -bg black
+xsetroot -grey
+
+if [ -f "${wallpaper_file}" ]; then
+ feh --bg-scale "${wallpaper_file}"
+fi
diff --git a/.bin/x11-screen b/.bin/x11-screen
new file mode 100755
index 0000000..ccccda7
--- /dev/null
+++ b/.bin/x11-screen
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+__hidpi() {
+ xrdb -merge ~/.Xresources.hidpi
+
+ export GDK_SCALE=2
+ export GDK_DPI_SCALE=0.5
+ export GDK_SCALE=2
+ export QT_SCREEN_SCALE_FACTORS=2
+ export QT_AUTO_SCREEN_SCALE_FACTOR=1
+ export QT_QPA_PLATFORMTHEME=qt5ct
+}
+
+hostname=$(uname -n)
+
+xrandr -s 0
+xrdb ~/.Xresources
+
+if [ "${hostname}" = "ws-bare01" ]; then
+ xrandr --output HDMI-0 --left-of DVI-D-0
+fi
+
+if [ "${hostname}" = "ws-xps01" ]; then
+ xrandr --output eDP1 --mode 3200x1800
+ echo a
+ __hidpi
+fi
diff --git a/.bin/yubikey-reset b/.bin/yubikey-reset
new file mode 100755
index 0000000..4824bc8
--- /dev/null
+++ b/.bin/yubikey-reset
@@ -0,0 +1,20 @@
+echo "DESTRUCTIVE !!!"
+
+sleep 60
+
+gpg-connect-agent <<EOF
+/hex
+scd serialno
+scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40
+scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40
+scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40
+scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40
+scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40
+scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40
+scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40
+scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40
+scd apdu 00 e6 00 00
+scd apdu 00 44 00 00
+/echo Yubikey has been successfully reset.
+/echo The factory default PINs are 123456 (user) and 12345678 (admin).
+EOF
diff --git a/.config/alacritty/alacritty.yml b/.config/alacritty/alacritty.yml
new file mode 100755
index 0000000..1bbacae
--- /dev/null
+++ b/.config/alacritty/alacritty.yml
@@ -0,0 +1,45 @@
+
+# alacritty ~~ ~/.config/alacritty/alacritty.yml
+#
+
+---
+
+font:
+ normal:
+ family: "Terminus"
+ style: Medium
+ size: 9
+
+window:
+ dimensions:
+ columns: 90
+ lines: 25
+
+colors:
+ primary:
+ background: "#000000"
+ foreground: "#ffffff"
+
+ cursor:
+ text: "#ffffff"
+ cursor: "#ffffff"
+
+ normal:
+ black: "#000000"
+ red: "#cd0000"
+ green: "#00cd00"
+ yellow: "#cdcd00"
+ blue: "#0000ee"
+ magenta: "#cd00cd"
+ cyan: "#00cdcd"
+ white: "#e5e5e5"
+
+ bright:
+ black: "#7f7f7f"
+ red: "#ff0000"
+ green: "#00ff00"
+ yellow: "#ffff00"
+ blue: "#5c5cff"
+ magenta: "#ff00ff"
+ cyan: "#00ffff"
+ white: "#ffffff"
diff --git a/.config/alacritty/hidpi.yml b/.config/alacritty/hidpi.yml
new file mode 100755
index 0000000..3aca533
--- /dev/null
+++ b/.config/alacritty/hidpi.yml
@@ -0,0 +1,16 @@
+
+# alacritty hidpi ~~ ~/.config/alacritty/hidpi.yml
+# workaround for window size on hidpi display with wyaland
+
+---
+
+import :
+ - .config/alacritty/alacritty.yml
+
+window:
+ dimensions:
+ columns: 40
+ lines: 12
+
+font:
+ size: 13
diff --git a/.config/calcurse/conf b/.config/calcurse/conf
new file mode 100755
index 0000000..549b5bd
--- /dev/null
+++ b/.config/calcurse/conf
@@ -0,0 +1,32 @@
+appearance.calendarview=monthly
+appearance.compactpanels=no
+appearance.defaultpanel=calendar
+appearance.layout=1
+appearance.headerline=yes
+appearance.eventseparator=yes
+appearance.dayseparator=yes
+appearance.emptyline=yes
+appearance.emptyday=--
+appearance.notifybar=yes
+appearance.sidebarwidth=0
+appearance.theme=green on default
+appearance.todoview=hide-completed
+appearance.headingpos=right-justified
+daemon.enable=no
+daemon.log=no
+format.inputdate=1
+format.notifydate=%a %F
+format.notifytime=%T
+format.outputdate=%D
+format.dayheading=%B %e, %Y
+general.autogc=no
+general.autosave=yes
+general.confirmdelete=yes
+general.confirmquit=no
+general.firstdayofweek=monday
+general.multipledays=yes
+general.periodicsave=0
+general.systemevents=yes
+notification.command=printf '\a'
+notification.notifyall=flagged-only
+notification.warning=300
diff --git a/.config/calcurse/keys b/.config/calcurse/keys
new file mode 100755
index 0000000..0f5ee8d
--- /dev/null
+++ b/.config/calcurse/keys
@@ -0,0 +1,55 @@
+#
+# Calcurse keys configuration file
+#
+# In this file the keybindings used by Calcurse are defined.
+# It is generated automatically by Calcurse and is maintained
+# via the key configuration menu of the interactive user
+# interface. It should not be edited directly.
+
+generic-cancel ESC
+generic-select SPC
+generic-credits @
+generic-help ?
+generic-quit q Q
+generic-save s S ^S
+generic-reload R
+generic-copy c
+generic-paste p ^V
+generic-change-view TAB
+generic-import i I
+generic-export x X
+generic-goto g G
+generic-other-cmd o O
+generic-config-menu C
+generic-redraw ^R
+generic-add-appt ^A
+generic-add-todo ^T
+generic-prev-day T ^H
+generic-next-day t ^L
+generic-prev-week W ^K
+generic-next-week w
+generic-prev-month M
+generic-next-month m
+generic-prev-year Y
+generic-next-year y
+generic-scroll-down ^N
+generic-scroll-up ^P
+generic-goto-today ^G
+generic-command :
+move-right l L RGT
+move-left h H LFT
+move-down j J DWN
+move-up k K UP
+start-of-week 0
+end-of-week $
+add-item a A
+del-item d D
+edit-item e E
+view-item v V RET
+pipe-item |
+flag-item !
+repeat r
+edit-note n N
+view-note >
+raise-priority +
+lower-priority -
diff --git a/.config/cmus/classic.theme b/.config/cmus/classic.theme
new file mode 100755
index 0000000..9976fcb
--- /dev/null
+++ b/.config/cmus/classic.theme
@@ -0,0 +1,48 @@
+
+# ~ rgoncalves.se
+# cmus colorscheme
+
+# common
+set color_win_fg=15
+set color_win_bg=0
+
+# command line
+set color_cmdline_bg=default
+set color_cmdline_fg=default
+set color_error=1
+set color_info=2
+set color_separator=15
+
+# bottom status line
+set color_statusline_bg=4
+set color_statusline_fg=15
+
+# bottom title line
+set color_titleline_bg=15
+set color_titleline_fg=4
+
+# top title line
+set color_win_title_bg=4
+set color_win_title_fg=15
+
+# playing: unselected
+set color_win_cur=4
+
+# playing: active selection
+set color_win_cur_sel_bg=7
+set color_win_cur_sel_fg=4
+
+# playing: inactive selection
+set color_win_inactive_cur_sel_bg=7
+set color_win_inactive_cur_sel_fg=0
+
+# collection: active selection
+set color_win_sel_bg=7
+set color_win_sel_fg=0
+
+# collection: inactive selection
+set color_win_inactive_sel_bg=0
+set color_win_inactive_sel_fg=7
+
+# directories
+set color_win_dir=7
diff --git a/.config/cmus/rc b/.config/cmus/rc
new file mode 100755
index 0000000..5654b7a
--- /dev/null
+++ b/.config/cmus/rc
@@ -0,0 +1,170 @@
+set aaa_mode=all
+set altformat_current= %F
+set altformat_playlist= %f%= %d
+set altformat_title=%f
+set altformat_trackwin= %f%= %d
+
+set auto_expand_albums_follow=true
+set auto_expand_albums_search=true
+set auto_expand_albums_selcur=true
+set auto_reshuffle=true
+set buffer_seconds=10
+
+set confirm_run=true
+set continue=true
+set continue_album=true
+set display_artist_sort_name=false
+set follow=false
+
+# format
+set format_current= %a - %l -%3n. %t%= %y
+set format_playlist= %-21%a %3n. %t%= %y %d %{?X!=0?%3X ? }
+set format_playlist_va= %-21%A %3n. %t (%a)%= %y %d %{?X!=0?%3X ? }
+set format_statusline= %{status} %{?show_playback_position?%{position} %{?duration?/ %{duration} }?%{?duration?%{duration} }}- %{total} %{?bpm>0?at %{bpm} BPM }%{?volume>=0?vol: %{?lvolume!=rvolume?%{lvolume},%{rvolume} ?%{volume} }}%{?stream?buf: %{buffer} }%{?show_current_bitrate & bitrate>=0? %{bitrate} kbps }%=%{?repeat_current?repeat current?%{?play_library?%{playlist_mode} from %{?play_sorted?sorted }library?playlist}} | %1{continue}%1{follow}%1{repeat}%1{shuffle}
+set format_title=%a - %l - %t (%y)
+set format_trackwin=%3n. %t%= %y %d
+set format_trackwin_album= %l %= %{albumduration}
+set format_trackwin_va=%3n. %t (%a)%= %y %d
+set format_treewin= %l
+set format_treewin_artist=%a
+
+# charset
+set icecast_default_charset=ISO-8859-1
+set id3_default_charset=ISO-8859-1
+
+# input
+set input.cdio.priority=50
+set input.cue.priority=50
+set input.flac.priority=50
+set input.mad.priority=55
+set input.wav.priority=50
+
+set lib_add_filter=
+set lib_sort=albumartist date album discnumber tracknumber title filename play_count
+
+set mouse=true
+set mpris=true
+
+set pl_sort=
+set play_library=true
+set play_sorted=false
+set repeat=true
+set repeat_current=false
+set replaygain=disabled
+set replaygain_limit=true
+set replaygain_preamp=0.000000
+set resume=false
+set rewind_offset=5
+set scroll_offset=2
+set set_term_title=true
+set show_all_tracks=true
+set show_current_bitrate=false
+set show_hidden=false
+set show_playback_position=true
+set show_remaining_time=false
+set shuffle=true
+set skip_track_info=false
+set smart_artist_sort=true
+set softvol=true
+#set softvol_state=0 0
+set start_view=tree
+set status_display_program=
+set stop_after_queue=false
+set time_show_leading_zero=true
+set tree_width_max=0
+set tree_width_percent=33
+set wrap_search=true
+
+# bind
+bind -f browser backspace browser-up
+bind -f browser i toggle show_hidden
+bind -f browser space win-activate
+bind -f browser u win-update
+bind -f common ! push shell
+bind -f common + vol +10%
+bind -f common , seek -1m
+bind -f common - vol -10%
+bind -f common . seek +1m
+bind -f common / search-start
+bind -f common 1 view tree
+bind -f common 2 view sorted
+bind -f common 3 view playlist
+bind -f common 4 view queue
+bind -f common 5 view browser
+bind -f common 6 view filters
+bind -f common 7 view settings
+bind -f common = vol +10%
+bind -f common ? search-b-start
+bind -f common C toggle continue
+bind -f common D win-remove
+bind -f common E win-add-Q
+bind -f common F push filter
+bind -f common G win-bottom
+bind -f common I echo {}
+bind -f common L push live-filter
+bind -f common M toggle play_library
+bind -f common N search-prev
+bind -f common P win-mv-before
+bind -f common U win-update-cache
+bind -f common [ vol +1% +0
+bind -f common ] vol +0 +1%
+bind -f common ^B win-page-up
+bind -f common ^C echo Type :quit<enter> to exit cmus.
+bind -f common ^D win-half-page-down
+bind -f common ^E win-scroll-down
+bind -f common ^F win-page-down
+bind -f common ^L refresh
+bind -f common ^R toggle repeat_current
+bind -f common ^U win-half-page-up
+bind -f common ^Y win-scroll-up
+bind -f common a win-add-l
+bind -f common b player-next
+bind -f common c player-pause
+bind -f common delete win-remove
+bind -f common down win-down
+bind -f common e win-add-q
+bind -f common end win-bottom
+bind -f common enter win-activate
+bind -f common f toggle follow
+bind -f common g win-top
+bind -f common h seek -5
+bind -f common home win-top
+bind -f common i win-sel-cur
+bind -f common j win-down
+bind -f common k win-up
+bind -f common l seek +5
+bind -f common left seek -5
+bind -f common m toggle aaa_mode
+bind -f common mlb_click_selected win-activate
+bind -f common mouse_scroll_down win-down
+bind -f common mouse_scroll_up win-up
+bind -f common n search-next
+bind -f common o toggle play_sorted
+bind -f common p win-mv-after
+bind -f common page_down win-page-down
+bind -f common page_up win-page-up
+bind -f common q quit -i
+bind -f common r toggle repeat
+bind -f common right seek +5
+bind -f common s toggle shuffle
+bind -f common space win-toggle
+bind -f common t toggle show_remaining_time
+bind -f common tab win-next
+bind -f common u update-cache
+bind -f common up win-up
+bind -f common v player-stop
+bind -f common x player-play
+bind -f common y win-add-p
+bind -f common z player-prev
+bind -f common { vol -1% -0
+bind -f common } vol -0 -1%
+
+# fset
+fset 90s=date>=1990&date<2000
+fset classical=genre="Classical"
+fset missing-tag=!stream&(artist=""|album=""|title=""|tracknumber=-1|date=-1)
+fset mp3=filename="*.mp3"
+fset ogg=filename="*.ogg"
+fset ogg-or-mp3=ogg|mp3
+fset unheard=play_count=0
+factivate
diff --git a/.config/dot/term-color-dark b/.config/dot/term-color-dark
new file mode 100755
index 0000000..3f1a1d2
--- /dev/null
+++ b/.config/dot/term-color-dark
@@ -0,0 +1 @@
+]4;0;#000000\]4;1;#af0000\]4;2;#00af00\]4;3;#afd700\]4;4;#005fff\]4;5;#af00af\]4;6;#00d7af\]4;7;#c0c0c0\]4;8;#808080\]4;9;#ff0000\]4;10;#00ff00\]4;11;#ffff00\]4;12;#0000ff\]4;13;#ff00ff\]4;14;#00ffff\]4;15;#ffffff\]10;#ffffff\]11;#000000\]12;#ffffff\]13;#ffffff\]17;#ffffff\]19;#000000\]4;232;#000000\]4;256;#ffffff\]708;#000000\ \ No newline at end of file
diff --git a/.config/dot/term-color-light b/.config/dot/term-color-light
new file mode 100755
index 0000000..96ad2e1
--- /dev/null
+++ b/.config/dot/term-color-light
@@ -0,0 +1 @@
+]4;0;#eeeeee\]4;1;#ff0000\]4;2;#00dd00\]4;3;#dddd00\]4;4;#0000ff\]4;5;#dd00dd\]4;6;#00dddd\]4;7;#808080\]4;8;#c0c0c0\]4;9;#af0000\]4;10;#00af00\]4;11;#afd700\]4;12;#005fff\]4;13;#af00af\]4;14;#00d7af\]4;15;#000000\]10;#000000\]11;#eeeeee\]12;#eeeeee\]13;#000000\]17;#000000\]19;#eeeeee\]4;232;#eeeeee\]4;256;#000000\]708;#eeeeee\
diff --git a/.config/gopass/config.yml b/.config/gopass/config.yml
new file mode 100755
index 0000000..dea9ee4
--- /dev/null
+++ b/.config/gopass/config.yml
@@ -0,0 +1,10 @@
+autoclip: true
+autoimport: true
+cliptimeout: 15
+exportkeys: true
+nopager: false
+notifications: false
+parsing: true
+path: /home/qwd/.password-store
+safecontent: false
+mounts: {}
diff --git a/.config/i3/config b/.config/i3/config
new file mode 100755
index 0000000..dbdb03f
--- /dev/null
+++ b/.config/i3/config
@@ -0,0 +1,177 @@
+
+# ~/.config/i3/config
+
+set $mod Mod1
+set $refresh_i3status killall -SIGUSR1 i3status
+set $launcher ~/.config/common.d/bemenu.sh
+font pango:TerminessTTF Nerd Font 8
+
+floating_modifier $mod
+focus_follows_mouse no
+
+# workspaces
+set $ws1 "1: one "
+set $ws2 "2: two "
+set $ws3 "3: UwU "
+set $ws4 "4: four "
+set $ws5 "5: five "
+set $wss1 "11: one "
+set $wss2 "12: two "
+set $wss3 "13: three "
+set $wss4 "14: four "
+set $wss5 "15: five "
+
+# keybinds
+bindsym $mod+Shift+q kill
+bindsym $mod+Return exec alacritty
+bindsym $mod+d exec "$launcher run"
+bindsym $mod+Shift+d exec "$launcher pass"
+bindsym $mod+Shift+s exec "$launcher colorscheme"
+
+bindsym $mod+h focus left
+bindsym $mod+k focus up
+bindsym $mod+j focus down
+bindsym $mod+l focus right
+bindsym $mod+Left focus left
+bindsym $mod+Down focus down
+bindsym $mod+Up focus up
+bindsym $mod+Right focus right
+bindsym $mod+Shift+h move left
+bindsym $mod+Shift+j move down
+bindsym $mod+Shift+k move up
+bindsym $mod+Shift+l move right
+bindsym $mod+Shift+Left move left
+bindsym $mod+Shift+Down move down
+bindsym $mod+Shift+Up move up
+bindsym $mod+Shift+Right move right
+#bindsym $mod+h split h
+bindsym $mod+v split v
+bindsym $mod+f fullscreen toggle
+bindsym $mod+s layout stacking
+bindsym $mod+w layout tabbed
+bindsym $mod+e layout toggle split
+bindsym $mod+Shift+space floating toggle
+bindsym $mod+space focus mode_toggle
+bindsym $mod+a focus parent
+bindsym $mod+Tab workspace back_and_forth
+bindsym $mod+1 workspace number $ws1
+bindsym $mod+2 workspace number $ws2
+bindsym $mod+3 workspace number $ws3
+bindsym $mod+4 workspace number $ws4
+bindsym $mod+5 workspace number $ws5
+bindsym $mod+F1 workspace number $wss1
+bindsym $mod+F2 workspace number $wss2
+bindsym $mod+F3 workspace number $wss3
+bindsym $mod+F4 workspace number $wss4
+bindsym $mod+F5 workspace number $wss5
+bindsym $mod+Shift+1 move container to workspace number $ws1
+bindsym $mod+Shift+2 move container to workspace number $ws2
+bindsym $mod+Shift+3 move container to workspace number $ws3
+bindsym $mod+Shift+4 move container to workspace number $ws4
+bindsym $mod+Shift+5 move container to workspace number $ws5
+bindsym $mod+Shift+F1 move container to workspace number $wss1
+bindsym $mod+Shift+F2 move container to workspace number $wss2
+bindsym $mod+Shift+F3 move container to workspace number $wss3
+bindsym $mod+Shift+F4 move container to workspace number $wss4
+bindsym $mod+Shift+F5 move container to workspace number $wss5
+bindsym $mod+Shift+c reload
+bindsym $mod+Shift+r restart
+bindsym $mod+Shift+e exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -B 'Yes, exit i3' 'i3-msg exit'"
+bindsym $mod+r mode "resize"
+mode "resize" {
+ bindsym j resize shrink width 10 px or 10 ppt
+ bindsym k resize grow height 10 px or 10 ppt
+ bindsym l resize shrink height 10 px or 10 ppt
+ bindsym semicolon resize grow width 10 px or 10 ppt
+ bindsym Left resize shrink width 10 px or 10 ppt
+ bindsym Down resize grow height 10 px or 10 ppt
+ bindsym Up resize shrink height 10 px or 10 ppt
+ bindsym Right resize grow width 10 px or 10 ppt
+ bindsym Return mode "default"
+ bindsym Escape mode "default"
+ bindsym $mod+r mode "default"
+}
+bindsym XF86AudioRaiseVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ +10% && $refresh_i3status
+bindsym XF86AudioLowerVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ -10% && $refresh_i3status
+bindsym XF86AudioMute exec --no-startup-id pactl set-sink-mute @DEFAULT_SINK@ toggle && $refresh_i3status
+bindsym XF86AudioMicMute exec --no-startup-id pactl set-source-mute @DEFAULT_SOURCE@ toggle && $refresh_i3status
+bindsym XF86MonBrightnessUp exec --no-startup-id "light -A 5"
+bindsym XF86MonBrightnessDown exec --no-startup-id "light -U 5"
+bindsym XF86AudioPlay exec --no-startup-id "cmus-remote -u"
+bindsym XF86AudioNext exec --no-startup-id "cmus-remote -n"
+bindsym XF86AudioPrev exec --no-startup-id "cmus-remote -r"
+
+# i3bar
+bar {
+ status_command ~/.config/common.d/i3status_ext.sh
+ position top
+ strip_workspace_numbers yes
+ font TerminessTTF Nerd Font
+ colors {
+ statusline $wh
+ separator $wh
+ background $bg
+ # colorclass <border> <background><text>
+ focused_workspace $bg $fg #FFFFFF
+ active_workspace $bg $fg #FFFFFF
+ inactive_workspace $bg $bg $fg
+ }
+}
+
+# dual-monitor
+exec_always ~/.config/i3/screen_config.sh
+
+workspace $ws1 output DVI-D-0
+workspace $ws2 output DVI-D-0
+workspace $ws3 output DVI-D-0
+workspace $ws4 output DVI-D-0
+workspace $ws5 output DVI-D-0
+workspace $wss1 output HDMI-0
+workspace $wss2 output HDMI-0
+workspace $wss3 output HDMI-0
+workspace $wss4 output HDMI-0
+workspace $wss5 output HDMI-0
+
+# default workspaces
+assign [class="qutebrowser"] $wss1
+assign [class="discord"] $wss2
+assign [title="discord"] $wss2
+assign [class="Steam"] $wss3
+assign [title="Steam"] $wss3
+assign [class="p4v"] $wss4
+assign [class="p4v"] $wss4
+
+# default window settings
+for_window [class="Steam"] floating enable
+for_window [title="Steam"] floating disable
+for_window [title="win0"] floating enable
+for_window [class="Tor Browser"] floating enable
+for_window [class=".*"] title_format "<big><b>%title</b></big>"
+
+# colorscheme
+set_from_resource $bg color0 #000000
+set_from_resource $rd color1 #FF0000
+set_from_resource $fg color4 #FF0000
+set_from_resource $gr color8 #8F8F8F
+set_from_resource $gl color7 #8F8F8F
+set_from_resource $wh color15 #FFFFFF
+
+# class border backgr. text indicator child_border
+
+client.focused $fg $fg $wh $fg $fg
+client.focused_inactive $gl $gr $wh $gr $gl
+client.unfocused $gl $gr $wh $gr $gl
+client.urgent $rd $rd $wh $rd $rd
+client.placeholder $bg $bg $fg $bg $bg
+
+# lockscreen
+bindsym mod1+F12 exec ~/.config/common.d/lock_screen.sh
+bindsym mod1+Shift+F12 exec ~/.config/common.d/lock_screen.sh -s
+
+# autoexec
+exec_always xset s off
+exec_always xset -dpms
+exec_always xset s noblank
+exec_always xset r rate 250 45
+exec_always setxkbmap -option compose:ralt
+
diff --git a/.config/mimeapps.list b/.config/mimeapps.list
new file mode 100755
index 0000000..fd7ac3e
--- /dev/null
+++ b/.config/mimeapps.list
@@ -0,0 +1,12 @@
+[Default Applications]
+x-scheme-handler/jetbrains=jetbrains-toolbox.desktop
+text/html=org.qutebrowser.qutebrowser.desktop
+x-scheme-handler/http=org.qutebrowser.qutebrowser.desktop
+x-scheme-handler/https=org.qutebrowser.qutebrowser.desktop
+x-scheme-handler/about=org.qutebrowser.qutebrowser.desktop
+x-scheme-handler/unknown=org.qutebrowser.qutebrowser.desktop
+x-scheme-handler/postman=Postman.desktop
+x-scheme-handler/discord-396515353498484736=discord-396515353498484736.desktop
+
+[Added Associations]
+application/vnd.oasis.opendocument.text=libreoffice-writer.desktop;
diff --git a/.config/neomutt/neomuttrc b/.config/neomutt/neomuttrc
new file mode 100755
index 0000000..baea9d8
--- /dev/null
+++ b/.config/neomutt/neomuttrc
@@ -0,0 +1,128 @@
+
+# rgoncalves.se
+
+# personal informations
+set my_server = ""
+set my_user = ""
+set my_password = ""
+source "~/.config/neomutt/personal"
+
+# IMAP
+set imap_user = "$my_user@$my_server"
+set imap_pass = "$my_password"
+set hostname = "imap.$my_server"
+set folder = "imaps://imap.$my_server:993"
+
+# SMTP
+set smtp_url = "smtps://$my_user@$my_server:$my_password@smtp.$my_server"
+set smtp_pass = "$my_password"
+
+# force tls
+set ssl_force_tls = yes
+set ssl_verify_host = yes
+
+# mailboxes
+set spoolfile = "+INBOX"
+set mbox = "+INBOX"
+set record = "+Sent"
+set trash = "+Trash"
+set postponed = "+Drafts"
+set mail_check_stats = yes
+set imap_delim_chars = "aa"
+
+# environment variables required!
+#set imap_check_subscribed
+mailboxes `python3 ~/.bin/get-mailbox-imap \
+imap.$MAIL_SERVER \
+$MAIL_USERNAME@$MAIL_SERVER \
+$MAIL_PASSWORD`
+
+# dl
+set mail_check = 90
+set sleep_time = 0
+set timeout = 15
+set beep = no
+set postpone = no
+set recall = no
+set signature = "~/.signature"
+set charset = "utf-8"
+set date_format = "%d.%m.%Y-%H:%M"
+set sort_re = yes"
+set reverse_name = yes
+set index_format = "%4C %Z %{$date_format} %-15.15L (%?l?%4l&%4c?) %s"
+set send_charset = "utf-8:iso-8859-1:us-ascii"
+set reply_regexp = "^(([Rr][Ee]?(\[[0-9]+\])?: *)?(\[[^]]+\] *)?)*"
+set quote_regexp = "^( {0,4}[>|:#%]| {0,4}[a-z0-9]+[>|]+)+"
+set reflow_space_quotes = yes
+auto_view text/html
+alternative_order text/plain text/html
+
+set reply_to = yes
+set fast_reply = yes
+set sig_dashes = no
+set include = yes
+set forward_quote = yes
+set edit_headers = yes
+set reverse_name = yes
+set pipe_decode = yes
+
+set header_cache = "~/.cache/neomutt"
+set message_cachedir = "$header_cache"
+
+# headers
+ignore *
+unignore from date subject to cc
+unignore organization organisation x-mailer: x-newsreader: x-mailing-list:
+unignore posted-to: list-id:
+
+# threads
+set sort ="threads"
+set sort_aux = "reverse-date"
+#set sort_aux = "last-date-received"
+set strict_threads ="yes"
+set collapse_unread = no
+
+# sidebar
+set sidebar_visible = yes
+set sidebar_short_path = yes
+set sidebar_sort_method = "unsorted"
+set sidebar_delim_chars = "/"
+set sidebar_format = "%D %?F?[%F]?%* %4N|%4S"
+set sidebar_folder_indent = yes
+set sidebar_indent_string = " "
+
+# keybinds
+bind editor <space> noop
+bind editor "> " quote-char
+bind pager c imap-fetch-mail
+bind index G last-entry
+bind index \CR imap-fetch-mail
+bind index g noop
+bind index gg first-entry
+bind index - collapse-thread
+bind index _ collapse-all
+bind pager,attach h exit
+bind attach <return> view-mailcap
+bind attach l view-mailcap
+bind pager j next-line
+bind pager k previous-line
+bind pager l view-attachments
+bind index D delete-message
+bind index U undelete-message
+bind index h noop
+bind index l display-message
+bind browser h goto-parent
+bind browser l select-entry
+bind index \CL limit
+bind pager,browser gg top-page
+bind pager,browser G bottom-page
+bind index,pager R group-reply
+bind index,pager K sidebar-prev
+bind index,pager J sidebar-next
+bind index,pager O sidebar-open
+bind index,pager,browser d half-down
+bind index,pager,browser u half-up
+
+# colors
+# color quoted color08 default
+# color hdrdefault color07 default
diff --git a/.config/newsboat/config b/.config/newsboat/config
new file mode 100755
index 0000000..0e8c120
--- /dev/null
+++ b/.config/newsboat/config
@@ -0,0 +1,38 @@
+
+# rgoncalves.se ~~ ~/.newsboat/config
+# newsboat configuration
+
+
+max-items 100
+reload-threads 100
+auto-reload yes
+reload-time 30
+text-width 80
+ssl-verifyhost no
+ssl-verifypeer no
+
+# vim
+unbind-key j
+unbind-key k
+unbind-key J
+unbind-key K
+bind-key j down
+bind-key k up
+bind-key l open
+bind-key h quit
+
+# colors
+color background color15 color0
+color listnormal color8 color0
+color listfocus color0 color15
+color listnormal_unread color15 color0
+color listfocus_unread color0 color15
+color info color0 color15
+color article color15 color0
+
+# multimedia
+browser "qutebrowser %u"
+macro y set browser "mpv %u" ; open-in-browser ; set browser "qutebrowser %u"
+
+# format
+feedlist-format "%4i %n %8u %24T | %t"
diff --git a/.config/nvim/.netrwhist b/.config/nvim/.netrwhist
new file mode 100755
index 0000000..038dc78
--- /dev/null
+++ b/.config/nvim/.netrwhist
@@ -0,0 +1,12 @@
+let g:netrw_dirhistmax =10
+let g:netrw_dirhistcnt =5
+let g:netrw_dirhist_5='/home/qwd/.config/nvim/after'
+let g:netrw_dirhist_4='/home/qwd/.config/nvim/colors'
+let g:netrw_dirhist_3='/home/qwd/.config/nvim'
+let g:netrw_dirhist_2='/home/qwd/gitlab.viperdev.io/mcanism/backend'
+let g:netrw_dirhist_1='/home/qwd/gitlab.viperdev.io/mcanism/backend/mcanism_finance/management/commands'
+let g:netrw_dirhist_0='/home/qwd/gitlab.viperdev.io/mcanism/backend/mcanism_finance/management'
+let g:netrw_dirhist_9='/home/qwd/gitlab.viperdev.io/mcanism/backend/mcanism_finance'
+let g:netrw_dirhist_8='/home/qwd/gitlab.viperdev.io/mcanism/backend'
+let g:netrw_dirhist_7='/home/qwd/gitlab.viperdev.io/mcanism/backend/mcanism_finance'
+let g:netrw_dirhist_6='/home/qwd/.config/nvim/after/indent'
diff --git a/.config/nvim/.nvim/autoload/plug.vim b/.config/nvim/.nvim/autoload/plug.vim
new file mode 100755
index 0000000..bfafbfb
--- /dev/null
+++ b/.config/nvim/.nvim/autoload/plug.vim
@@ -0,0 +1,2526 @@
+" vim-plug: Vim plugin manager
+" ============================
+"
+" Download plug.vim and put it in ~/.vim/autoload
+"
+" curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
+" https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
+"
+" Edit your .vimrc
+"
+" call plug#begin('~/.vim/plugged')
+"
+" " Make sure you use single quotes
+"
+" " Shorthand notation; fetches https://github.com/junegunn/vim-easy-align
+" Plug 'junegunn/vim-easy-align'
+"
+" " Any valid git URL is allowed
+" Plug 'https://github.com/junegunn/vim-github-dashboard.git'
+"
+" " Multiple Plug commands can be written in a single line using | separators
+" Plug 'SirVer/ultisnips' | Plug 'honza/vim-snippets'
+"
+" " On-demand loading
+" Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' }
+" Plug 'tpope/vim-fireplace', { 'for': 'clojure' }
+"
+" " Using a non-master branch
+" Plug 'rdnetto/YCM-Generator', { 'branch': 'stable' }
+"
+" " Using a tagged release; wildcard allowed (requires git 1.9.2 or above)
+" Plug 'fatih/vim-go', { 'tag': '*' }
+"
+" " Plugin options
+" Plug 'nsf/gocode', { 'tag': 'v.20150303', 'rtp': 'vim' }
+"
+" " Plugin outside ~/.vim/plugged with post-update hook
+" Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' }
+"
+" " Unmanaged plugin (manually installed and updated)
+" Plug '~/my-prototype-plugin'
+"
+" " Initialize plugin system
+" call plug#end()
+"
+" Then reload .vimrc and :PlugInstall to install plugins.
+"
+" Plug options:
+"
+"| Option | Description |
+"| ----------------------- | ------------------------------------------------ |
+"| `branch`/`tag`/`commit` | Branch/tag/commit of the repository to use |
+"| `rtp` | Subdirectory that contains Vim plugin |
+"| `dir` | Custom directory for the plugin |
+"| `as` | Use different name for the plugin |
+"| `do` | Post-update hook (string or funcref) |
+"| `on` | On-demand loading: Commands or `<Plug>`-mappings |
+"| `for` | On-demand loading: File types |
+"| `frozen` | Do not update unless explicitly specified |
+"
+" More information: https://github.com/junegunn/vim-plug
+"
+"
+" Copyright (c) 2017 Junegunn Choi
+"
+" MIT License
+"
+" 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.
+
+if exists('g:loaded_plug')
+ finish
+endif
+let g:loaded_plug = 1
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+let s:plug_src = 'https://github.com/junegunn/vim-plug.git'
+let s:plug_tab = get(s:, 'plug_tab', -1)
+let s:plug_buf = get(s:, 'plug_buf', -1)
+let s:mac_gui = has('gui_macvim') && has('gui_running')
+let s:is_win = has('win32')
+let s:nvim = has('nvim-0.2') || (has('nvim') && exists('*jobwait') && !s:is_win)
+let s:vim8 = has('patch-8.0.0039') && exists('*job_start')
+let s:me = resolve(expand('<sfile>:p'))
+let s:base_spec = { 'branch': 'master', 'frozen': 0 }
+let s:TYPE = {
+\ 'string': type(''),
+\ 'list': type([]),
+\ 'dict': type({}),
+\ 'funcref': type(function('call'))
+\ }
+let s:loaded = get(s:, 'loaded', {})
+let s:triggers = get(s:, 'triggers', {})
+
+function! plug#begin(...)
+ if a:0 > 0
+ let s:plug_home_org = a:1
+ let home = s:path(fnamemodify(expand(a:1), ':p'))
+ elseif exists('g:plug_home')
+ let home = s:path(g:plug_home)
+ elseif !empty(&rtp)
+ let home = s:path(split(&rtp, ',')[0]) . '/plugged'
+ else
+ return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.')
+ endif
+ if fnamemodify(home, ':t') ==# 'plugin' && fnamemodify(home, ':h') ==# s:first_rtp
+ return s:err('Invalid plug home. '.home.' is a standard Vim runtime path and is not allowed.')
+ endif
+
+ let g:plug_home = home
+ let g:plugs = {}
+ let g:plugs_order = []
+ let s:triggers = {}
+
+ call s:define_commands()
+ return 1
+endfunction
+
+function! s:define_commands()
+ command! -nargs=+ -bar Plug call plug#(<args>)
+ if !executable('git')
+ return s:err('`git` executable not found. Most commands will not be available. To suppress this message, prepend `silent!` to `call plug#begin(...)`.')
+ endif
+ command! -nargs=* -bar -bang -complete=customlist,s:names PlugInstall call s:install(<bang>0, [<f-args>])
+ command! -nargs=* -bar -bang -complete=customlist,s:names PlugUpdate call s:update(<bang>0, [<f-args>])
+ command! -nargs=0 -bar -bang PlugClean call s:clean(<bang>0)
+ command! -nargs=0 -bar PlugUpgrade if s:upgrade() | execute 'source' s:esc(s:me) | endif
+ command! -nargs=0 -bar PlugStatus call s:status()
+ command! -nargs=0 -bar PlugDiff call s:diff()
+ command! -nargs=? -bar -bang -complete=file PlugSnapshot call s:snapshot(<bang>0, <f-args>)
+endfunction
+
+function! s:to_a(v)
+ return type(a:v) == s:TYPE.list ? a:v : [a:v]
+endfunction
+
+function! s:to_s(v)
+ return type(a:v) == s:TYPE.string ? a:v : join(a:v, "\n") . "\n"
+endfunction
+
+function! s:glob(from, pattern)
+ return s:lines(globpath(a:from, a:pattern))
+endfunction
+
+function! s:source(from, ...)
+ let found = 0
+ for pattern in a:000
+ for vim in s:glob(a:from, pattern)
+ execute 'source' s:esc(vim)
+ let found = 1
+ endfor
+ endfor
+ return found
+endfunction
+
+function! s:assoc(dict, key, val)
+ let a:dict[a:key] = add(get(a:dict, a:key, []), a:val)
+endfunction
+
+function! s:ask(message, ...)
+ call inputsave()
+ echohl WarningMsg
+ let answer = input(a:message.(a:0 ? ' (y/N/a) ' : ' (y/N) '))
+ echohl None
+ call inputrestore()
+ echo "\r"
+ return (a:0 && answer =~? '^a') ? 2 : (answer =~? '^y') ? 1 : 0
+endfunction
+
+function! s:ask_no_interrupt(...)
+ try
+ return call('s:ask', a:000)
+ catch
+ return 0
+ endtry
+endfunction
+
+function! s:lazy(plug, opt)
+ return has_key(a:plug, a:opt) &&
+ \ (empty(s:to_a(a:plug[a:opt])) ||
+ \ !isdirectory(a:plug.dir) ||
+ \ len(s:glob(s:rtp(a:plug), 'plugin')) ||
+ \ len(s:glob(s:rtp(a:plug), 'after/plugin')))
+endfunction
+
+function! plug#end()
+ if !exists('g:plugs')
+ return s:err('Call plug#begin() first')
+ endif
+
+ if exists('#PlugLOD')
+ augroup PlugLOD
+ autocmd!
+ augroup END
+ augroup! PlugLOD
+ endif
+ let lod = { 'ft': {}, 'map': {}, 'cmd': {} }
+
+ if exists('g:did_load_filetypes')
+ filetype off
+ endif
+ for name in g:plugs_order
+ if !has_key(g:plugs, name)
+ continue
+ endif
+ let plug = g:plugs[name]
+ if get(s:loaded, name, 0) || !s:lazy(plug, 'on') && !s:lazy(plug, 'for')
+ let s:loaded[name] = 1
+ continue
+ endif
+
+ if has_key(plug, 'on')
+ let s:triggers[name] = { 'map': [], 'cmd': [] }
+ for cmd in s:to_a(plug.on)
+ if cmd =~? '^<Plug>.\+'
+ if empty(mapcheck(cmd)) && empty(mapcheck(cmd, 'i'))
+ call s:assoc(lod.map, cmd, name)
+ endif
+ call add(s:triggers[name].map, cmd)
+ elseif cmd =~# '^[A-Z]'
+ let cmd = substitute(cmd, '!*$', '', '')
+ if exists(':'.cmd) != 2
+ call s:assoc(lod.cmd, cmd, name)
+ endif
+ call add(s:triggers[name].cmd, cmd)
+ else
+ call s:err('Invalid `on` option: '.cmd.
+ \ '. Should start with an uppercase letter or `<Plug>`.')
+ endif
+ endfor
+ endif
+
+ if has_key(plug, 'for')
+ let types = s:to_a(plug.for)
+ if !empty(types)
+ augroup filetypedetect
+ call s:source(s:rtp(plug), 'ftdetect/**/*.vim', 'after/ftdetect/**/*.vim')
+ augroup END
+ endif
+ for type in types
+ call s:assoc(lod.ft, type, name)
+ endfor
+ endif
+ endfor
+
+ for [cmd, names] in items(lod.cmd)
+ execute printf(
+ \ 'command! -nargs=* -range -bang -complete=file %s call s:lod_cmd(%s, "<bang>", <line1>, <line2>, <q-args>, %s)',
+ \ cmd, string(cmd), string(names))
+ endfor
+
+ for [map, names] in items(lod.map)
+ for [mode, map_prefix, key_prefix] in
+ \ [['i', '<C-O>', ''], ['n', '', ''], ['v', '', 'gv'], ['o', '', '']]
+ execute printf(
+ \ '%snoremap <silent> %s %s:<C-U>call <SID>lod_map(%s, %s, %s, "%s")<CR>',
+ \ mode, map, map_prefix, string(map), string(names), mode != 'i', key_prefix)
+ endfor
+ endfor
+
+ for [ft, names] in items(lod.ft)
+ augroup PlugLOD
+ execute printf('autocmd FileType %s call <SID>lod_ft(%s, %s)',
+ \ ft, string(ft), string(names))
+ augroup END
+ endfor
+
+ call s:reorg_rtp()
+ filetype plugin indent on
+ if has('vim_starting')
+ if has('syntax') && !exists('g:syntax_on')
+ syntax enable
+ end
+ else
+ call s:reload_plugins()
+ endif
+endfunction
+
+function! s:loaded_names()
+ return filter(copy(g:plugs_order), 'get(s:loaded, v:val, 0)')
+endfunction
+
+function! s:load_plugin(spec)
+ call s:source(s:rtp(a:spec), 'plugin/**/*.vim', 'after/plugin/**/*.vim')
+endfunction
+
+function! s:reload_plugins()
+ for name in s:loaded_names()
+ call s:load_plugin(g:plugs[name])
+ endfor
+endfunction
+
+function! s:trim(str)
+ return substitute(a:str, '[\/]\+$', '', '')
+endfunction
+
+function! s:version_requirement(val, min)
+ for idx in range(0, len(a:min) - 1)
+ let v = get(a:val, idx, 0)
+ if v < a:min[idx] | return 0
+ elseif v > a:min[idx] | return 1
+ endif
+ endfor
+ return 1
+endfunction
+
+function! s:git_version_requirement(...)
+ if !exists('s:git_version')
+ let s:git_version = map(split(split(s:system('git --version'))[2], '\.'), 'str2nr(v:val)')
+ endif
+ return s:version_requirement(s:git_version, a:000)
+endfunction
+
+function! s:progress_opt(base)
+ return a:base && !s:is_win &&
+ \ s:git_version_requirement(1, 7, 1) ? '--progress' : ''
+endfunction
+
+if s:is_win
+ function! s:rtp(spec)
+ return s:path(a:spec.dir . get(a:spec, 'rtp', ''))
+ endfunction
+
+ function! s:path(path)
+ return s:trim(substitute(a:path, '/', '\', 'g'))
+ endfunction
+
+ function! s:dirpath(path)
+ return s:path(a:path) . '\'
+ endfunction
+
+ function! s:is_local_plug(repo)
+ return a:repo =~? '^[a-z]:\|^[%~]'
+ endfunction
+else
+ function! s:rtp(spec)
+ return s:dirpath(a:spec.dir . get(a:spec, 'rtp', ''))
+ endfunction
+
+ function! s:path(path)
+ return s:trim(a:path)
+ endfunction
+
+ function! s:dirpath(path)
+ return substitute(a:path, '[/\\]*$', '/', '')
+ endfunction
+
+ function! s:is_local_plug(repo)
+ return a:repo[0] =~ '[/$~]'
+ endfunction
+endif
+
+function! s:err(msg)
+ echohl ErrorMsg
+ echom '[vim-plug] '.a:msg
+ echohl None
+endfunction
+
+function! s:warn(cmd, msg)
+ echohl WarningMsg
+ execute a:cmd 'a:msg'
+ echohl None
+endfunction
+
+function! s:esc(path)
+ return escape(a:path, ' ')
+endfunction
+
+function! s:escrtp(path)
+ return escape(a:path, ' ,')
+endfunction
+
+function! s:remove_rtp()
+ for name in s:loaded_names()
+ let rtp = s:rtp(g:plugs[name])
+ execute 'set rtp-='.s:escrtp(rtp)
+ let after = globpath(rtp, 'after')
+ if isdirectory(after)
+ execute 'set rtp-='.s:escrtp(after)
+ endif
+ endfor
+endfunction
+
+function! s:reorg_rtp()
+ if !empty(s:first_rtp)
+ execute 'set rtp-='.s:first_rtp
+ execute 'set rtp-='.s:last_rtp
+ endif
+
+ " &rtp is modified from outside
+ if exists('s:prtp') && s:prtp !=# &rtp
+ call s:remove_rtp()
+ unlet! s:middle
+ endif
+
+ let s:middle = get(s:, 'middle', &rtp)
+ let rtps = map(s:loaded_names(), 's:rtp(g:plugs[v:val])')
+ let afters = filter(map(copy(rtps), 'globpath(v:val, "after")'), '!empty(v:val)')
+ let rtp = join(map(rtps, 'escape(v:val, ",")'), ',')
+ \ . ','.s:middle.','
+ \ . join(map(afters, 'escape(v:val, ",")'), ',')
+ let &rtp = substitute(substitute(rtp, ',,*', ',', 'g'), '^,\|,$', '', 'g')
+ let s:prtp = &rtp
+
+ if !empty(s:first_rtp)
+ execute 'set rtp^='.s:first_rtp
+ execute 'set rtp+='.s:last_rtp
+ endif
+endfunction
+
+function! s:doautocmd(...)
+ if exists('#'.join(a:000, '#'))
+ execute 'doautocmd' ((v:version > 703 || has('patch442')) ? '<nomodeline>' : '') join(a:000)
+ endif
+endfunction
+
+function! s:dobufread(names)
+ for name in a:names
+ let path = s:rtp(g:plugs[name])
+ for dir in ['ftdetect', 'ftplugin', 'after/ftdetect', 'after/ftplugin']
+ if len(finddir(dir, path))
+ if exists('#BufRead')
+ doautocmd BufRead
+ endif
+ return
+ endif
+ endfor
+ endfor
+endfunction
+
+function! plug#load(...)
+ if a:0 == 0
+ return s:err('Argument missing: plugin name(s) required')
+ endif
+ if !exists('g:plugs')
+ return s:err('plug#begin was not called')
+ endif
+ let names = a:0 == 1 && type(a:1) == s:TYPE.list ? a:1 : a:000
+ let unknowns = filter(copy(names), '!has_key(g:plugs, v:val)')
+ if !empty(unknowns)
+ let s = len(unknowns) > 1 ? 's' : ''
+ return s:err(printf('Unknown plugin%s: %s', s, join(unknowns, ', ')))
+ end
+ let unloaded = filter(copy(names), '!get(s:loaded, v:val, 0)')
+ if !empty(unloaded)
+ for name in unloaded
+ call s:lod([name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
+ endfor
+ call s:dobufread(unloaded)
+ return 1
+ end
+ return 0
+endfunction
+
+function! s:remove_triggers(name)
+ if !has_key(s:triggers, a:name)
+ return
+ endif
+ for cmd in s:triggers[a:name].cmd
+ execute 'silent! delc' cmd
+ endfor
+ for map in s:triggers[a:name].map
+ execute 'silent! unmap' map
+ execute 'silent! iunmap' map
+ endfor
+ call remove(s:triggers, a:name)
+endfunction
+
+function! s:lod(names, types, ...)
+ for name in a:names
+ call s:remove_triggers(name)
+ let s:loaded[name] = 1
+ endfor
+ call s:reorg_rtp()
+
+ for name in a:names
+ let rtp = s:rtp(g:plugs[name])
+ for dir in a:types
+ call s:source(rtp, dir.'/**/*.vim')
+ endfor
+ if a:0
+ if !s:source(rtp, a:1) && !empty(s:glob(rtp, a:2))
+ execute 'runtime' a:1
+ endif
+ call s:source(rtp, a:2)
+ endif
+ call s:doautocmd('User', name)
+ endfor
+endfunction
+
+function! s:lod_ft(pat, names)
+ let syn = 'syntax/'.a:pat.'.vim'
+ call s:lod(a:names, ['plugin', 'after/plugin'], syn, 'after/'.syn)
+ execute 'autocmd! PlugLOD FileType' a:pat
+ call s:doautocmd('filetypeplugin', 'FileType')
+ call s:doautocmd('filetypeindent', 'FileType')
+endfunction
+
+function! s:lod_cmd(cmd, bang, l1, l2, args, names)
+ call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
+ call s:dobufread(a:names)
+ execute printf('%s%s%s %s', (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args)
+endfunction
+
+function! s:lod_map(map, names, with_prefix, prefix)
+ call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
+ call s:dobufread(a:names)
+ let extra = ''
+ while 1
+ let c = getchar(0)
+ if c == 0
+ break
+ endif
+ let extra .= nr2char(c)
+ endwhile
+
+ if a:with_prefix
+ let prefix = v:count ? v:count : ''
+ let prefix .= '"'.v:register.a:prefix
+ if mode(1) == 'no'
+ if v:operator == 'c'
+ let prefix = "\<esc>" . prefix
+ endif
+ let prefix .= v:operator
+ endif
+ call feedkeys(prefix, 'n')
+ endif
+ call feedkeys(substitute(a:map, '^<Plug>', "\<Plug>", '') . extra)
+endfunction
+
+function! plug#(repo, ...)
+ if a:0 > 1
+ return s:err('Invalid number of arguments (1..2)')
+ endif
+
+ try
+ let repo = s:trim(a:repo)
+ let opts = a:0 == 1 ? s:parse_options(a:1) : s:base_spec
+ let name = get(opts, 'as', fnamemodify(repo, ':t:s?\.git$??'))
+ let spec = extend(s:infer_properties(name, repo), opts)
+ if !has_key(g:plugs, name)
+ call add(g:plugs_order, name)
+ endif
+ let g:plugs[name] = spec
+ let s:loaded[name] = get(s:loaded, name, 0)
+ catch
+ return s:err(v:exception)
+ endtry
+endfunction
+
+function! s:parse_options(arg)
+ let opts = copy(s:base_spec)
+ let type = type(a:arg)
+ if type == s:TYPE.string
+ let opts.tag = a:arg
+ elseif type == s:TYPE.dict
+ call extend(opts, a:arg)
+ if has_key(opts, 'dir')
+ let opts.dir = s:dirpath(expand(opts.dir))
+ endif
+ else
+ throw 'Invalid argument type (expected: string or dictionary)'
+ endif
+ return opts
+endfunction
+
+function! s:infer_properties(name, repo)
+ let repo = a:repo
+ if s:is_local_plug(repo)
+ return { 'dir': s:dirpath(expand(repo)) }
+ else
+ if repo =~ ':'
+ let uri = repo
+ else
+ if repo !~ '/'
+ throw printf('Invalid argument: %s (implicit `vim-scripts'' expansion is deprecated)', repo)
+ endif
+ let fmt = get(g:, 'plug_url_format', 'https://git::@github.com/%s.git')
+ let uri = printf(fmt, repo)
+ endif
+ return { 'dir': s:dirpath(g:plug_home.'/'.a:name), 'uri': uri }
+ endif
+endfunction
+
+function! s:install(force, names)
+ call s:update_impl(0, a:force, a:names)
+endfunction
+
+function! s:update(force, names)
+ call s:update_impl(1, a:force, a:names)
+endfunction
+
+function! plug#helptags()
+ if !exists('g:plugs')
+ return s:err('plug#begin was not called')
+ endif
+ for spec in values(g:plugs)
+ let docd = join([s:rtp(spec), 'doc'], '/')
+ if isdirectory(docd)
+ silent! execute 'helptags' s:esc(docd)
+ endif
+ endfor
+ return 1
+endfunction
+
+function! s:syntax()
+ syntax clear
+ syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber
+ syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX
+ syn match plugNumber /[0-9]\+[0-9.]*/ contained
+ syn match plugBracket /[[\]]/ contained
+ syn match plugX /x/ contained
+ syn match plugDash /^-/
+ syn match plugPlus /^+/
+ syn match plugStar /^*/
+ syn match plugMessage /\(^- \)\@<=.*/
+ syn match plugName /\(^- \)\@<=[^ ]*:/
+ syn match plugSha /\%(: \)\@<=[0-9a-f]\{4,}$/
+ syn match plugTag /(tag: [^)]\+)/
+ syn match plugInstall /\(^+ \)\@<=[^:]*/
+ syn match plugUpdate /\(^* \)\@<=[^:]*/
+ syn match plugCommit /^ \X*[0-9a-f]\{7,9} .*/ contains=plugRelDate,plugEdge,plugTag
+ syn match plugEdge /^ \X\+$/
+ syn match plugEdge /^ \X*/ contained nextgroup=plugSha
+ syn match plugSha /[0-9a-f]\{7,9}/ contained
+ syn match plugRelDate /([^)]*)$/ contained
+ syn match plugNotLoaded /(not loaded)$/
+ syn match plugError /^x.*/
+ syn region plugDeleted start=/^\~ .*/ end=/^\ze\S/
+ syn match plugH2 /^.*:\n-\+$/
+ syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean
+ hi def link plug1 Title
+ hi def link plug2 Repeat
+ hi def link plugH2 Type
+ hi def link plugX Exception
+ hi def link plugBracket Structure
+ hi def link plugNumber Number
+
+ hi def link plugDash Special
+ hi def link plugPlus Constant
+ hi def link plugStar Boolean
+
+ hi def link plugMessage Function
+ hi def link plugName Label
+ hi def link plugInstall Function
+ hi def link plugUpdate Type
+
+ hi def link plugError Error
+ hi def link plugDeleted Ignore
+ hi def link plugRelDate Comment
+ hi def link plugEdge PreProc
+ hi def link plugSha Identifier
+ hi def link plugTag Constant
+
+ hi def link plugNotLoaded Comment
+endfunction
+
+function! s:lpad(str, len)
+ return a:str . repeat(' ', a:len - len(a:str))
+endfunction
+
+function! s:lines(msg)
+ return split(a:msg, "[\r\n]")
+endfunction
+
+function! s:lastline(msg)
+ return get(s:lines(a:msg), -1, '')
+endfunction
+
+function! s:new_window()
+ execute get(g:, 'plug_window', 'vertical topleft new')
+endfunction
+
+function! s:plug_window_exists()
+ let buflist = tabpagebuflist(s:plug_tab)
+ return !empty(buflist) && index(buflist, s:plug_buf) >= 0
+endfunction
+
+function! s:switch_in()
+ if !s:plug_window_exists()
+ return 0
+ endif
+
+ if winbufnr(0) != s:plug_buf
+ let s:pos = [tabpagenr(), winnr(), winsaveview()]
+ execute 'normal!' s:plug_tab.'gt'
+ let winnr = bufwinnr(s:plug_buf)
+ execute winnr.'wincmd w'
+ call add(s:pos, winsaveview())
+ else
+ let s:pos = [winsaveview()]
+ endif
+
+ setlocal modifiable
+ return 1
+endfunction
+
+function! s:switch_out(...)
+ call winrestview(s:pos[-1])
+ setlocal nomodifiable
+ if a:0 > 0
+ execute a:1
+ endif
+
+ if len(s:pos) > 1
+ execute 'normal!' s:pos[0].'gt'
+ execute s:pos[1] 'wincmd w'
+ call winrestview(s:pos[2])
+ endif
+endfunction
+
+function! s:finish_bindings()
+ nnoremap <silent> <buffer> R :call <SID>retry()<cr>
+ nnoremap <silent> <buffer> D :PlugDiff<cr>
+ nnoremap <silent> <buffer> S :PlugStatus<cr>
+ nnoremap <silent> <buffer> U :call <SID>status_update()<cr>
+ xnoremap <silent> <buffer> U :call <SID>status_update()<cr>
+ nnoremap <silent> <buffer> ]] :silent! call <SID>section('')<cr>
+ nnoremap <silent> <buffer> [[ :silent! call <SID>section('b')<cr>
+endfunction
+
+function! s:prepare(...)
+ if empty(getcwd())
+ throw 'Invalid current working directory. Cannot proceed.'
+ endif
+
+ for evar in ['$GIT_DIR', '$GIT_WORK_TREE']
+ if exists(evar)
+ throw evar.' detected. Cannot proceed.'
+ endif
+ endfor
+
+ call s:job_abort()
+ if s:switch_in()
+ if b:plug_preview == 1
+ pc
+ endif
+ enew
+ else
+ call s:new_window()
+ endif
+
+ nnoremap <silent> <buffer> q :if b:plug_preview==1<bar>pc<bar>endif<bar>bd<cr>
+ if a:0 == 0
+ call s:finish_bindings()
+ endif
+ let b:plug_preview = -1
+ let s:plug_tab = tabpagenr()
+ let s:plug_buf = winbufnr(0)
+ call s:assign_name()
+
+ for k in ['<cr>', 'L', 'o', 'X', 'd', 'dd']
+ execute 'silent! unmap <buffer>' k
+ endfor
+ setlocal buftype=nofile bufhidden=wipe nobuflisted nolist noswapfile nowrap cursorline modifiable nospell
+ if exists('+colorcolumn')
+ setlocal colorcolumn=
+ endif
+ setf vim-plug
+ if exists('g:syntax_on')
+ call s:syntax()
+ endif
+endfunction
+
+function! s:assign_name()
+ " Assign buffer name
+ let prefix = '[Plugins]'
+ let name = prefix
+ let idx = 2
+ while bufexists(name)
+ let name = printf('%s (%s)', prefix, idx)
+ let idx = idx + 1
+ endwhile
+ silent! execute 'f' fnameescape(name)
+endfunction
+
+function! s:chsh(swap)
+ let prev = [&shell, &shellcmdflag, &shellredir]
+ if s:is_win
+ set shell=cmd.exe shellcmdflag=/c shellredir=>%s\ 2>&1
+ elseif a:swap
+ set shell=sh shellredir=>%s\ 2>&1
+ endif
+ return prev
+endfunction
+
+function! s:bang(cmd, ...)
+ try
+ let [sh, shellcmdflag, shrd] = s:chsh(a:0)
+ " FIXME: Escaping is incomplete. We could use shellescape with eval,
+ " but it won't work on Windows.
+ let cmd = a:0 ? s:with_cd(a:cmd, a:1) : a:cmd
+ if s:is_win
+ let batchfile = tempname().'.bat'
+ call writefile(["@echo off\r", cmd . "\r"], batchfile)
+ let cmd = s:shellesc(batchfile)
+ endif
+ let g:_plug_bang = (s:is_win && has('gui_running') ? 'silent ' : '').'!'.escape(cmd, '#!%')
+ execute "normal! :execute g:_plug_bang\<cr>\<cr>"
+ finally
+ unlet g:_plug_bang
+ let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd]
+ if s:is_win
+ call delete(batchfile)
+ endif
+ endtry
+ return v:shell_error ? 'Exit status: ' . v:shell_error : ''
+endfunction
+
+function! s:regress_bar()
+ let bar = substitute(getline(2)[1:-2], '.*\zs=', 'x', '')
+ call s:progress_bar(2, bar, len(bar))
+endfunction
+
+function! s:is_updated(dir)
+ return !empty(s:system_chomp('git log --pretty=format:"%h" "HEAD...HEAD@{1}"', a:dir))
+endfunction
+
+function! s:do(pull, force, todo)
+ for [name, spec] in items(a:todo)
+ if !isdirectory(spec.dir)
+ continue
+ endif
+ let installed = has_key(s:update.new, name)
+ let updated = installed ? 0 :
+ \ (a:pull && index(s:update.errors, name) < 0 && s:is_updated(spec.dir))
+ if a:force || installed || updated
+ execute 'cd' s:esc(spec.dir)
+ call append(3, '- Post-update hook for '. name .' ... ')
+ let error = ''
+ let type = type(spec.do)
+ if type == s:TYPE.string
+ if spec.do[0] == ':'
+ if !get(s:loaded, name, 0)
+ let s:loaded[name] = 1
+ call s:reorg_rtp()
+ endif
+ call s:load_plugin(spec)
+ try
+ execute spec.do[1:]
+ catch
+ let error = v:exception
+ endtry
+ if !s:plug_window_exists()
+ cd -
+ throw 'Warning: vim-plug was terminated by the post-update hook of '.name
+ endif
+ else
+ let error = s:bang(spec.do)
+ endif
+ elseif type == s:TYPE.funcref
+ try
+ let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged')
+ call spec.do({ 'name': name, 'status': status, 'force': a:force })
+ catch
+ let error = v:exception
+ endtry
+ else
+ let error = 'Invalid hook type'
+ endif
+ call s:switch_in()
+ call setline(4, empty(error) ? (getline(4) . 'OK')
+ \ : ('x' . getline(4)[1:] . error))
+ if !empty(error)
+ call add(s:update.errors, name)
+ call s:regress_bar()
+ endif
+ cd -
+ endif
+ endfor
+endfunction
+
+function! s:hash_match(a, b)
+ return stridx(a:a, a:b) == 0 || stridx(a:b, a:a) == 0
+endfunction
+
+function! s:checkout(spec)
+ let sha = a:spec.commit
+ let output = s:system('git rev-parse HEAD', a:spec.dir)
+ if !v:shell_error && !s:hash_match(sha, s:lines(output)[0])
+ let output = s:system(
+ \ 'git fetch --depth 999999 && git checkout '.s:esc(sha).' --', a:spec.dir)
+ endif
+ return output
+endfunction
+
+function! s:finish(pull)
+ let new_frozen = len(filter(keys(s:update.new), 'g:plugs[v:val].frozen'))
+ if new_frozen
+ let s = new_frozen > 1 ? 's' : ''
+ call append(3, printf('- Installed %d frozen plugin%s', new_frozen, s))
+ endif
+ call append(3, '- Finishing ... ') | 4
+ redraw
+ call plug#helptags()
+ call plug#end()
+ call setline(4, getline(4) . 'Done!')
+ redraw
+ let msgs = []
+ if !empty(s:update.errors)
+ call add(msgs, "Press 'R' to retry.")
+ endif
+ if a:pull && len(s:update.new) < len(filter(getline(5, '$'),
+ \ "v:val =~ '^- ' && v:val !~# 'Already up.to.date'"))
+ call add(msgs, "Press 'D' to see the updated changes.")
+ endif
+ echo join(msgs, ' ')
+ call s:finish_bindings()
+endfunction
+
+function! s:retry()
+ if empty(s:update.errors)
+ return
+ endif
+ echo
+ call s:update_impl(s:update.pull, s:update.force,
+ \ extend(copy(s:update.errors), [s:update.threads]))
+endfunction
+
+function! s:is_managed(name)
+ return has_key(g:plugs[a:name], 'uri')
+endfunction
+
+function! s:names(...)
+ return sort(filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)'))
+endfunction
+
+function! s:check_ruby()
+ silent! ruby require 'thread'; VIM::command("let g:plug_ruby = '#{RUBY_VERSION}'")
+ if !exists('g:plug_ruby')
+ redraw!
+ return s:warn('echom', 'Warning: Ruby interface is broken')
+ endif
+ let ruby_version = split(g:plug_ruby, '\.')
+ unlet g:plug_ruby
+ return s:version_requirement(ruby_version, [1, 8, 7])
+endfunction
+
+function! s:update_impl(pull, force, args) abort
+ let sync = index(a:args, '--sync') >= 0 || has('vim_starting')
+ let args = filter(copy(a:args), 'v:val != "--sync"')
+ let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ?
+ \ remove(args, -1) : get(g:, 'plug_threads', 16)
+
+ let managed = filter(copy(g:plugs), 's:is_managed(v:key)')
+ let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') :
+ \ filter(managed, 'index(args, v:key) >= 0')
+
+ if empty(todo)
+ return s:warn('echo', 'No plugin to '. (a:pull ? 'update' : 'install'))
+ endif
+
+ if !s:is_win && s:git_version_requirement(2, 3)
+ let s:git_terminal_prompt = exists('$GIT_TERMINAL_PROMPT') ? $GIT_TERMINAL_PROMPT : ''
+ let $GIT_TERMINAL_PROMPT = 0
+ for plug in values(todo)
+ let plug.uri = substitute(plug.uri,
+ \ '^https://git::@github\.com', 'https://github.com', '')
+ endfor
+ endif
+
+ if !isdirectory(g:plug_home)
+ try
+ call mkdir(g:plug_home, 'p')
+ catch
+ return s:err(printf('Invalid plug directory: %s. '.
+ \ 'Try to call plug#begin with a valid directory', g:plug_home))
+ endtry
+ endif
+
+ if has('nvim') && !exists('*jobwait') && threads > 1
+ call s:warn('echom', '[vim-plug] Update Neovim for parallel installer')
+ endif
+
+ let use_job = s:nvim || s:vim8
+ let python = (has('python') || has('python3')) && !use_job
+ let ruby = has('ruby') && !use_job && (v:version >= 703 || v:version == 702 && has('patch374')) && !(s:is_win && has('gui_running')) && threads > 1 && s:check_ruby()
+
+ let s:update = {
+ \ 'start': reltime(),
+ \ 'all': todo,
+ \ 'todo': copy(todo),
+ \ 'errors': [],
+ \ 'pull': a:pull,
+ \ 'force': a:force,
+ \ 'new': {},
+ \ 'threads': (python || ruby || use_job) ? min([len(todo), threads]) : 1,
+ \ 'bar': '',
+ \ 'fin': 0
+ \ }
+
+ call s:prepare(1)
+ call append(0, ['', ''])
+ normal! 2G
+ silent! redraw
+
+ let s:clone_opt = get(g:, 'plug_shallow', 1) ?
+ \ '--depth 1' . (s:git_version_requirement(1, 7, 10) ? ' --no-single-branch' : '') : ''
+
+ if has('win32unix')
+ let s:clone_opt .= ' -c core.eol=lf -c core.autocrlf=input'
+ endif
+
+ let s:submodule_opt = s:git_version_requirement(2, 8) ? ' --jobs='.threads : ''
+
+ " Python version requirement (>= 2.7)
+ if python && !has('python3') && !ruby && !use_job && s:update.threads > 1
+ redir => pyv
+ silent python import platform; print platform.python_version()
+ redir END
+ let python = s:version_requirement(
+ \ map(split(split(pyv)[0], '\.'), 'str2nr(v:val)'), [2, 6])
+ endif
+
+ if (python || ruby) && s:update.threads > 1
+ try
+ let imd = &imd
+ if s:mac_gui
+ set noimd
+ endif
+ if ruby
+ call s:update_ruby()
+ else
+ call s:update_python()
+ endif
+ catch
+ let lines = getline(4, '$')
+ let printed = {}
+ silent! 4,$d _
+ for line in lines
+ let name = s:extract_name(line, '.', '')
+ if empty(name) || !has_key(printed, name)
+ call append('$', line)
+ if !empty(name)
+ let printed[name] = 1
+ if line[0] == 'x' && index(s:update.errors, name) < 0
+ call add(s:update.errors, name)
+ end
+ endif
+ endif
+ endfor
+ finally
+ let &imd = imd
+ call s:update_finish()
+ endtry
+ else
+ call s:update_vim()
+ while use_job && sync
+ sleep 100m
+ if s:update.fin
+ break
+ endif
+ endwhile
+ endif
+endfunction
+
+function! s:log4(name, msg)
+ call setline(4, printf('- %s (%s)', a:msg, a:name))
+ redraw
+endfunction
+
+function! s:update_finish()
+ if exists('s:git_terminal_prompt')
+ let $GIT_TERMINAL_PROMPT = s:git_terminal_prompt
+ endif
+ if s:switch_in()
+ call append(3, '- Updating ...') | 4
+ for [name, spec] in items(filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && (s:update.force || s:update.pull || has_key(s:update.new, v:key))'))
+ let [pos, _] = s:logpos(name)
+ if !pos
+ continue
+ endif
+ if has_key(spec, 'commit')
+ call s:log4(name, 'Checking out '.spec.commit)
+ let out = s:checkout(spec)
+ elseif has_key(spec, 'tag')
+ let tag = spec.tag
+ if tag =~ '\*'
+ let tags = s:lines(s:system('git tag --list '.s:shellesc(tag).' --sort -version:refname 2>&1', spec.dir))
+ if !v:shell_error && !empty(tags)
+ let tag = tags[0]
+ call s:log4(name, printf('Latest tag for %s -> %s', spec.tag, tag))
+ call append(3, '')
+ endif
+ endif
+ call s:log4(name, 'Checking out '.tag)
+ let out = s:system('git checkout -q '.s:esc(tag).' -- 2>&1', spec.dir)
+ else
+ let branch = s:esc(get(spec, 'branch', 'master'))
+ call s:log4(name, 'Merging origin/'.branch)
+ let out = s:system('git checkout -q '.branch.' -- 2>&1'
+ \. (has_key(s:update.new, name) ? '' : ('&& git merge --ff-only origin/'.branch.' 2>&1')), spec.dir)
+ endif
+ if !v:shell_error && filereadable(spec.dir.'/.gitmodules') &&
+ \ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir))
+ call s:log4(name, 'Updating submodules. This may take a while.')
+ let out .= s:bang('git submodule update --init --recursive'.s:submodule_opt.' 2>&1', spec.dir)
+ endif
+ let msg = s:format_message(v:shell_error ? 'x': '-', name, out)
+ if v:shell_error
+ call add(s:update.errors, name)
+ call s:regress_bar()
+ silent execute pos 'd _'
+ call append(4, msg) | 4
+ elseif !empty(out)
+ call setline(pos, msg[0])
+ endif
+ redraw
+ endfor
+ silent 4 d _
+ try
+ call s:do(s:update.pull, s:update.force, filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && has_key(v:val, "do")'))
+ catch
+ call s:warn('echom', v:exception)
+ call s:warn('echo', '')
+ return
+ endtry
+ call s:finish(s:update.pull)
+ call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(s:update.start)))[0] . ' sec.')
+ call s:switch_out('normal! gg')
+ endif
+endfunction
+
+function! s:job_abort()
+ if (!s:nvim && !s:vim8) || !exists('s:jobs')
+ return
+ endif
+
+ for [name, j] in items(s:jobs)
+ if s:nvim
+ silent! call jobstop(j.jobid)
+ elseif s:vim8
+ silent! call job_stop(j.jobid)
+ endif
+ if j.new
+ call s:system('rm -rf ' . s:shellesc(g:plugs[name].dir))
+ endif
+ endfor
+ let s:jobs = {}
+endfunction
+
+function! s:last_non_empty_line(lines)
+ let len = len(a:lines)
+ for idx in range(len)
+ let line = a:lines[len-idx-1]
+ if !empty(line)
+ return line
+ endif
+ endfor
+ return ''
+endfunction
+
+function! s:job_out_cb(self, data) abort
+ let self = a:self
+ let data = remove(self.lines, -1) . a:data
+ let lines = map(split(data, "\n", 1), 'split(v:val, "\r", 1)[-1]')
+ call extend(self.lines, lines)
+ " To reduce the number of buffer updates
+ let self.tick = get(self, 'tick', -1) + 1
+ if !self.running || self.tick % len(s:jobs) == 0
+ let bullet = self.running ? (self.new ? '+' : '*') : (self.error ? 'x' : '-')
+ let result = self.error ? join(self.lines, "\n") : s:last_non_empty_line(self.lines)
+ call s:log(bullet, self.name, result)
+ endif
+endfunction
+
+function! s:job_exit_cb(self, data) abort
+ let a:self.running = 0
+ let a:self.error = a:data != 0
+ call s:reap(a:self.name)
+ call s:tick()
+endfunction
+
+function! s:job_cb(fn, job, ch, data)
+ if !s:plug_window_exists() " plug window closed
+ return s:job_abort()
+ endif
+ call call(a:fn, [a:job, a:data])
+endfunction
+
+function! s:nvim_cb(job_id, data, event) dict abort
+ return a:event == 'stdout' ?
+ \ s:job_cb('s:job_out_cb', self, 0, join(a:data, "\n")) :
+ \ s:job_cb('s:job_exit_cb', self, 0, a:data)
+endfunction
+
+function! s:spawn(name, cmd, opts)
+ let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [''],
+ \ 'batchfile': (s:is_win && (s:nvim || s:vim8)) ? tempname().'.bat' : '',
+ \ 'new': get(a:opts, 'new', 0) }
+ let s:jobs[a:name] = job
+ let cmd = has_key(a:opts, 'dir') ? s:with_cd(a:cmd, a:opts.dir) : a:cmd
+ if !empty(job.batchfile)
+ call writefile(["@echo off\r", cmd . "\r"], job.batchfile)
+ let cmd = s:shellesc(job.batchfile)
+ endif
+ let argv = add(s:is_win ? ['cmd', '/c'] : ['sh', '-c'], cmd)
+
+ if s:nvim
+ call extend(job, {
+ \ 'on_stdout': function('s:nvim_cb'),
+ \ 'on_exit': function('s:nvim_cb'),
+ \ })
+ let jid = jobstart(argv, job)
+ if jid > 0
+ let job.jobid = jid
+ else
+ let job.running = 0
+ let job.error = 1
+ let job.lines = [jid < 0 ? argv[0].' is not executable' :
+ \ 'Invalid arguments (or job table is full)']
+ endif
+ elseif s:vim8
+ let jid = job_start(s:is_win ? join(argv, ' ') : argv, {
+ \ 'out_cb': function('s:job_cb', ['s:job_out_cb', job]),
+ \ 'exit_cb': function('s:job_cb', ['s:job_exit_cb', job]),
+ \ 'out_mode': 'raw'
+ \})
+ if job_status(jid) == 'run'
+ let job.jobid = jid
+ else
+ let job.running = 0
+ let job.error = 1
+ let job.lines = ['Failed to start job']
+ endif
+ else
+ let job.lines = s:lines(call('s:system', [cmd]))
+ let job.error = v:shell_error != 0
+ let job.running = 0
+ endif
+endfunction
+
+function! s:reap(name)
+ let job = s:jobs[a:name]
+ if job.error
+ call add(s:update.errors, a:name)
+ elseif get(job, 'new', 0)
+ let s:update.new[a:name] = 1
+ endif
+ let s:update.bar .= job.error ? 'x' : '='
+
+ let bullet = job.error ? 'x' : '-'
+ let result = job.error ? join(job.lines, "\n") : s:last_non_empty_line(job.lines)
+ call s:log(bullet, a:name, empty(result) ? 'OK' : result)
+ call s:bar()
+
+ if has_key(job, 'batchfile') && !empty(job.batchfile)
+ call delete(job.batchfile)
+ endif
+ call remove(s:jobs, a:name)
+endfunction
+
+function! s:bar()
+ if s:switch_in()
+ let total = len(s:update.all)
+ call setline(1, (s:update.pull ? 'Updating' : 'Installing').
+ \ ' plugins ('.len(s:update.bar).'/'.total.')')
+ call s:progress_bar(2, s:update.bar, total)
+ call s:switch_out()
+ endif
+endfunction
+
+function! s:logpos(name)
+ for i in range(4, line('$'))
+ if getline(i) =~# '^[-+x*] '.a:name.':'
+ for j in range(i + 1, line('$'))
+ if getline(j) !~ '^ '
+ return [i, j - 1]
+ endif
+ endfor
+ return [i, i]
+ endif
+ endfor
+ return [0, 0]
+endfunction
+
+function! s:log(bullet, name, lines)
+ if s:switch_in()
+ let [b, e] = s:logpos(a:name)
+ if b > 0
+ silent execute printf('%d,%d d _', b, e)
+ if b > winheight('.')
+ let b = 4
+ endif
+ else
+ let b = 4
+ endif
+ " FIXME For some reason, nomodifiable is set after :d in vim8
+ setlocal modifiable
+ call append(b - 1, s:format_message(a:bullet, a:name, a:lines))
+ call s:switch_out()
+ endif
+endfunction
+
+function! s:update_vim()
+ let s:jobs = {}
+
+ call s:bar()
+ call s:tick()
+endfunction
+
+function! s:tick()
+ let pull = s:update.pull
+ let prog = s:progress_opt(s:nvim || s:vim8)
+while 1 " Without TCO, Vim stack is bound to explode
+ if empty(s:update.todo)
+ if empty(s:jobs) && !s:update.fin
+ call s:update_finish()
+ let s:update.fin = 1
+ endif
+ return
+ endif
+
+ let name = keys(s:update.todo)[0]
+ let spec = remove(s:update.todo, name)
+ let new = empty(globpath(spec.dir, '.git', 1))
+
+ call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...')
+ redraw
+
+ let has_tag = has_key(spec, 'tag')
+ if !new
+ let [error, _] = s:git_validate(spec, 0)
+ if empty(error)
+ if pull
+ let fetch_opt = (has_tag && !empty(globpath(spec.dir, '.git/shallow'))) ? '--depth 99999999' : ''
+ call s:spawn(name, printf('git fetch %s %s 2>&1', fetch_opt, prog), { 'dir': spec.dir })
+ else
+ let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 }
+ endif
+ else
+ let s:jobs[name] = { 'running': 0, 'lines': s:lines(error), 'error': 1 }
+ endif
+ else
+ call s:spawn(name,
+ \ printf('git clone %s %s %s %s 2>&1',
+ \ has_tag ? '' : s:clone_opt,
+ \ prog,
+ \ s:shellesc(spec.uri),
+ \ s:shellesc(s:trim(spec.dir))), { 'new': 1 })
+ endif
+
+ if !s:jobs[name].running
+ call s:reap(name)
+ endif
+ if len(s:jobs) >= s:update.threads
+ break
+ endif
+endwhile
+endfunction
+
+function! s:update_python()
+let py_exe = has('python') ? 'python' : 'python3'
+execute py_exe "<< EOF"
+import datetime
+import functools
+import os
+try:
+ import queue
+except ImportError:
+ import Queue as queue
+import random
+import re
+import shutil
+import signal
+import subprocess
+import tempfile
+import threading as thr
+import time
+import traceback
+import vim
+
+G_NVIM = vim.eval("has('nvim')") == '1'
+G_PULL = vim.eval('s:update.pull') == '1'
+G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1
+G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)'))
+G_CLONE_OPT = vim.eval('s:clone_opt')
+G_PROGRESS = vim.eval('s:progress_opt(1)')
+G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads'))
+G_STOP = thr.Event()
+G_IS_WIN = vim.eval('s:is_win') == '1'
+
+class PlugError(Exception):
+ def __init__(self, msg):
+ self.msg = msg
+class CmdTimedOut(PlugError):
+ pass
+class CmdFailed(PlugError):
+ pass
+class InvalidURI(PlugError):
+ pass
+class Action(object):
+ INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-']
+
+class Buffer(object):
+ def __init__(self, lock, num_plugs, is_pull):
+ self.bar = ''
+ self.event = 'Updating' if is_pull else 'Installing'
+ self.lock = lock
+ self.maxy = int(vim.eval('winheight(".")'))
+ self.num_plugs = num_plugs
+
+ def __where(self, name):
+ """ Find first line with name in current buffer. Return line num. """
+ found, lnum = False, 0
+ matcher = re.compile('^[-+x*] {0}:'.format(name))
+ for line in vim.current.buffer:
+ if matcher.search(line) is not None:
+ found = True
+ break
+ lnum += 1
+
+ if not found:
+ lnum = -1
+ return lnum
+
+ def header(self):
+ curbuf = vim.current.buffer
+ curbuf[0] = self.event + ' plugins ({0}/{1})'.format(len(self.bar), self.num_plugs)
+
+ num_spaces = self.num_plugs - len(self.bar)
+ curbuf[1] = '[{0}{1}]'.format(self.bar, num_spaces * ' ')
+
+ with self.lock:
+ vim.command('normal! 2G')
+ vim.command('redraw')
+
+ def write(self, action, name, lines):
+ first, rest = lines[0], lines[1:]
+ msg = ['{0} {1}{2}{3}'.format(action, name, ': ' if first else '', first)]
+ msg.extend([' ' + line for line in rest])
+
+ try:
+ if action == Action.ERROR:
+ self.bar += 'x'
+ vim.command("call add(s:update.errors, '{0}')".format(name))
+ elif action == Action.DONE:
+ self.bar += '='
+
+ curbuf = vim.current.buffer
+ lnum = self.__where(name)
+ if lnum != -1: # Found matching line num
+ del curbuf[lnum]
+ if lnum > self.maxy and action in set([Action.INSTALL, Action.UPDATE]):
+ lnum = 3
+ else:
+ lnum = 3
+ curbuf.append(msg, lnum)
+
+ self.header()
+ except vim.error:
+ pass
+
+class Command(object):
+ CD = 'cd /d' if G_IS_WIN else 'cd'
+
+ def __init__(self, cmd, cmd_dir=None, timeout=60, cb=None, clean=None):
+ self.cmd = cmd
+ if cmd_dir:
+ self.cmd = '{0} {1} && {2}'.format(Command.CD, cmd_dir, self.cmd)
+ self.timeout = timeout
+ self.callback = cb if cb else (lambda msg: None)
+ self.clean = clean if clean else (lambda: None)
+ self.proc = None
+
+ @property
+ def alive(self):
+ """ Returns true only if command still running. """
+ return self.proc and self.proc.poll() is None
+
+ def execute(self, ntries=3):
+ """ Execute the command with ntries if CmdTimedOut.
+ Returns the output of the command if no Exception.
+ """
+ attempt, finished, limit = 0, False, self.timeout
+
+ while not finished:
+ try:
+ attempt += 1
+ result = self.try_command()
+ finished = True
+ return result
+ except CmdTimedOut:
+ if attempt != ntries:
+ self.notify_retry()
+ self.timeout += limit
+ else:
+ raise
+
+ def notify_retry(self):
+ """ Retry required for command, notify user. """
+ for count in range(3, 0, -1):
+ if G_STOP.is_set():
+ raise KeyboardInterrupt
+ msg = 'Timeout. Will retry in {0} second{1} ...'.format(
+ count, 's' if count != 1 else '')
+ self.callback([msg])
+ time.sleep(1)
+ self.callback(['Retrying ...'])
+
+ def try_command(self):
+ """ Execute a cmd & poll for callback. Returns list of output.
+ Raises CmdFailed -> return code for Popen isn't 0
+ Raises CmdTimedOut -> command exceeded timeout without new output
+ """
+ first_line = True
+
+ try:
+ tfile = tempfile.NamedTemporaryFile(mode='w+b')
+ preexec_fn = not G_IS_WIN and os.setsid or None
+ self.proc = subprocess.Popen(self.cmd, stdout=tfile,
+ stderr=subprocess.STDOUT,
+ stdin=subprocess.PIPE, shell=True,
+ preexec_fn=preexec_fn)
+ thrd = thr.Thread(target=(lambda proc: proc.wait()), args=(self.proc,))
+ thrd.start()
+
+ thread_not_started = True
+ while thread_not_started:
+ try:
+ thrd.join(0.1)
+ thread_not_started = False
+ except RuntimeError:
+ pass
+
+ while self.alive:
+ if G_STOP.is_set():
+ raise KeyboardInterrupt
+
+ if first_line or random.random() < G_LOG_PROB:
+ first_line = False
+ line = '' if G_IS_WIN else nonblock_read(tfile.name)
+ if line:
+ self.callback([line])
+
+ time_diff = time.time() - os.path.getmtime(tfile.name)
+ if time_diff > self.timeout:
+ raise CmdTimedOut(['Timeout!'])
+
+ thrd.join(0.5)
+
+ tfile.seek(0)
+ result = [line.decode('utf-8', 'replace').rstrip() for line in tfile]
+
+ if self.proc.returncode != 0:
+ raise CmdFailed([''] + result)
+
+ return result
+ except:
+ self.terminate()
+ raise
+
+ def terminate(self):
+ """ Terminate process and cleanup. """
+ if self.alive:
+ if G_IS_WIN:
+ os.kill(self.proc.pid, signal.SIGINT)
+ else:
+ os.killpg(self.proc.pid, signal.SIGTERM)
+ self.clean()
+
+class Plugin(object):
+ def __init__(self, name, args, buf_q, lock):
+ self.name = name
+ self.args = args
+ self.buf_q = buf_q
+ self.lock = lock
+ self.tag = args.get('tag', 0)
+
+ def manage(self):
+ try:
+ if os.path.exists(self.args['dir']):
+ self.update()
+ else:
+ self.install()
+ with self.lock:
+ thread_vim_command("let s:update.new['{0}'] = 1".format(self.name))
+ except PlugError as exc:
+ self.write(Action.ERROR, self.name, exc.msg)
+ except KeyboardInterrupt:
+ G_STOP.set()
+ self.write(Action.ERROR, self.name, ['Interrupted!'])
+ except:
+ # Any exception except those above print stack trace
+ msg = 'Trace:\n{0}'.format(traceback.format_exc().rstrip())
+ self.write(Action.ERROR, self.name, msg.split('\n'))
+ raise
+
+ def install(self):
+ target = self.args['dir']
+ if target[-1] == '\\':
+ target = target[0:-1]
+
+ def clean(target):
+ def _clean():
+ try:
+ shutil.rmtree(target)
+ except OSError:
+ pass
+ return _clean
+
+ self.write(Action.INSTALL, self.name, ['Installing ...'])
+ callback = functools.partial(self.write, Action.INSTALL, self.name)
+ cmd = 'git clone {0} {1} {2} {3} 2>&1'.format(
+ '' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'],
+ esc(target))
+ com = Command(cmd, None, G_TIMEOUT, callback, clean(target))
+ result = com.execute(G_RETRIES)
+ self.write(Action.DONE, self.name, result[-1:])
+
+ def repo_uri(self):
+ cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url'
+ command = Command(cmd, self.args['dir'], G_TIMEOUT,)
+ result = command.execute(G_RETRIES)
+ return result[-1]
+
+ def update(self):
+ actual_uri = self.repo_uri()
+ expect_uri = self.args['uri']
+ regex = re.compile(r'^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$')
+ ma = regex.match(actual_uri)
+ mb = regex.match(expect_uri)
+ if ma is None or mb is None or ma.groups() != mb.groups():
+ msg = ['',
+ 'Invalid URI: {0}'.format(actual_uri),
+ 'Expected {0}'.format(expect_uri),
+ 'PlugClean required.']
+ raise InvalidURI(msg)
+
+ if G_PULL:
+ self.write(Action.UPDATE, self.name, ['Updating ...'])
+ callback = functools.partial(self.write, Action.UPDATE, self.name)
+ fetch_opt = '--depth 99999999' if self.tag and os.path.isfile(os.path.join(self.args['dir'], '.git/shallow')) else ''
+ cmd = 'git fetch {0} {1} 2>&1'.format(fetch_opt, G_PROGRESS)
+ com = Command(cmd, self.args['dir'], G_TIMEOUT, callback)
+ result = com.execute(G_RETRIES)
+ self.write(Action.DONE, self.name, result[-1:])
+ else:
+ self.write(Action.DONE, self.name, ['Already installed'])
+
+ def write(self, action, name, msg):
+ self.buf_q.put((action, name, msg))
+
+class PlugThread(thr.Thread):
+ def __init__(self, tname, args):
+ super(PlugThread, self).__init__()
+ self.tname = tname
+ self.args = args
+
+ def run(self):
+ thr.current_thread().name = self.tname
+ buf_q, work_q, lock = self.args
+
+ try:
+ while not G_STOP.is_set():
+ name, args = work_q.get_nowait()
+ plug = Plugin(name, args, buf_q, lock)
+ plug.manage()
+ work_q.task_done()
+ except queue.Empty:
+ pass
+
+class RefreshThread(thr.Thread):
+ def __init__(self, lock):
+ super(RefreshThread, self).__init__()
+ self.lock = lock
+ self.running = True
+
+ def run(self):
+ while self.running:
+ with self.lock:
+ thread_vim_command('noautocmd normal! a')
+ time.sleep(0.33)
+
+ def stop(self):
+ self.running = False
+
+if G_NVIM:
+ def thread_vim_command(cmd):
+ vim.session.threadsafe_call(lambda: vim.command(cmd))
+else:
+ def thread_vim_command(cmd):
+ vim.command(cmd)
+
+def esc(name):
+ return '"' + name.replace('"', '\"') + '"'
+
+def nonblock_read(fname):
+ """ Read a file with nonblock flag. Return the last line. """
+ fread = os.open(fname, os.O_RDONLY | os.O_NONBLOCK)
+ buf = os.read(fread, 100000).decode('utf-8', 'replace')
+ os.close(fread)
+
+ line = buf.rstrip('\r\n')
+ left = max(line.rfind('\r'), line.rfind('\n'))
+ if left != -1:
+ left += 1
+ line = line[left:]
+
+ return line
+
+def main():
+ thr.current_thread().name = 'main'
+ nthreads = int(vim.eval('s:update.threads'))
+ plugs = vim.eval('s:update.todo')
+ mac_gui = vim.eval('s:mac_gui') == '1'
+
+ lock = thr.Lock()
+ buf = Buffer(lock, len(plugs), G_PULL)
+ buf_q, work_q = queue.Queue(), queue.Queue()
+ for work in plugs.items():
+ work_q.put(work)
+
+ start_cnt = thr.active_count()
+ for num in range(nthreads):
+ tname = 'PlugT-{0:02}'.format(num)
+ thread = PlugThread(tname, (buf_q, work_q, lock))
+ thread.start()
+ if mac_gui:
+ rthread = RefreshThread(lock)
+ rthread.start()
+
+ while not buf_q.empty() or thr.active_count() != start_cnt:
+ try:
+ action, name, msg = buf_q.get(True, 0.25)
+ buf.write(action, name, ['OK'] if not msg else msg)
+ buf_q.task_done()
+ except queue.Empty:
+ pass
+ except KeyboardInterrupt:
+ G_STOP.set()
+
+ if mac_gui:
+ rthread.stop()
+ rthread.join()
+
+main()
+EOF
+endfunction
+
+function! s:update_ruby()
+ ruby << EOF
+ module PlugStream
+ SEP = ["\r", "\n", nil]
+ def get_line
+ buffer = ''
+ loop do
+ char = readchar rescue return
+ if SEP.include? char.chr
+ buffer << $/
+ break
+ else
+ buffer << char
+ end
+ end
+ buffer
+ end
+ end unless defined?(PlugStream)
+
+ def esc arg
+ %["#{arg.gsub('"', '\"')}"]
+ end
+
+ def killall pid
+ pids = [pid]
+ if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM
+ pids.each { |pid| Process.kill 'INT', pid.to_i rescue nil }
+ else
+ unless `which pgrep 2> /dev/null`.empty?
+ children = pids
+ until children.empty?
+ children = children.map { |pid|
+ `pgrep -P #{pid}`.lines.map { |l| l.chomp }
+ }.flatten
+ pids += children
+ end
+ end
+ pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil }
+ end
+ end
+
+ def compare_git_uri a, b
+ regex = %r{^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$}
+ regex.match(a).to_a.drop(1) == regex.match(b).to_a.drop(1)
+ end
+
+ require 'thread'
+ require 'fileutils'
+ require 'timeout'
+ running = true
+ iswin = VIM::evaluate('s:is_win').to_i == 1
+ pull = VIM::evaluate('s:update.pull').to_i == 1
+ base = VIM::evaluate('g:plug_home')
+ all = VIM::evaluate('s:update.todo')
+ limit = VIM::evaluate('get(g:, "plug_timeout", 60)')
+ tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1
+ nthr = VIM::evaluate('s:update.threads').to_i
+ maxy = VIM::evaluate('winheight(".")').to_i
+ vim7 = VIM::evaluate('v:version').to_i <= 703 && RUBY_PLATFORM =~ /darwin/
+ cd = iswin ? 'cd /d' : 'cd'
+ tot = VIM::evaluate('len(s:update.todo)') || 0
+ bar = ''
+ skip = 'Already installed'
+ mtx = Mutex.new
+ take1 = proc { mtx.synchronize { running && all.shift } }
+ logh = proc {
+ cnt = bar.length
+ $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})"
+ $curbuf[2] = '[' + bar.ljust(tot) + ']'
+ VIM::command('normal! 2G')
+ VIM::command('redraw')
+ }
+ where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } }
+ log = proc { |name, result, type|
+ mtx.synchronize do
+ ing = ![true, false].include?(type)
+ bar += type ? '=' : 'x' unless ing
+ b = case type
+ when :install then '+' when :update then '*'
+ when true, nil then '-' else
+ VIM::command("call add(s:update.errors, '#{name}')")
+ 'x'
+ end
+ result =
+ if type || type.nil?
+ ["#{b} #{name}: #{result.lines.to_a.last || 'OK'}"]
+ elsif result =~ /^Interrupted|^Timeout/
+ ["#{b} #{name}: #{result}"]
+ else
+ ["#{b} #{name}"] + result.lines.map { |l| " " << l }
+ end
+ if lnum = where.call(name)
+ $curbuf.delete lnum
+ lnum = 4 if ing && lnum > maxy
+ end
+ result.each_with_index do |line, offset|
+ $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp)
+ end
+ logh.call
+ end
+ }
+ bt = proc { |cmd, name, type, cleanup|
+ tried = timeout = 0
+ begin
+ tried += 1
+ timeout += limit
+ fd = nil
+ data = ''
+ if iswin
+ Timeout::timeout(timeout) do
+ tmp = VIM::evaluate('tempname()')
+ system("(#{cmd}) > #{tmp}")
+ data = File.read(tmp).chomp
+ File.unlink tmp rescue nil
+ end
+ else
+ fd = IO.popen(cmd).extend(PlugStream)
+ first_line = true
+ log_prob = 1.0 / nthr
+ while line = Timeout::timeout(timeout) { fd.get_line }
+ data << line
+ log.call name, line.chomp, type if name && (first_line || rand < log_prob)
+ first_line = false
+ end
+ fd.close
+ end
+ [$? == 0, data.chomp]
+ rescue Timeout::Error, Interrupt => e
+ if fd && !fd.closed?
+ killall fd.pid
+ fd.close
+ end
+ cleanup.call if cleanup
+ if e.is_a?(Timeout::Error) && tried < tries
+ 3.downto(1) do |countdown|
+ s = countdown > 1 ? 's' : ''
+ log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type
+ sleep 1
+ end
+ log.call name, 'Retrying ...', type
+ retry
+ end
+ [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"]
+ end
+ }
+ main = Thread.current
+ threads = []
+ watcher = Thread.new {
+ if vim7
+ while VIM::evaluate('getchar(1)')
+ sleep 0.1
+ end
+ else
+ require 'io/console' # >= Ruby 1.9
+ nil until IO.console.getch == 3.chr
+ end
+ mtx.synchronize do
+ running = false
+ threads.each { |t| t.raise Interrupt } unless vim7
+ end
+ threads.each { |t| t.join rescue nil }
+ main.kill
+ }
+ refresh = Thread.new {
+ while true
+ mtx.synchronize do
+ break unless running
+ VIM::command('noautocmd normal! a')
+ end
+ sleep 0.2
+ end
+ } if VIM::evaluate('s:mac_gui') == 1
+
+ clone_opt = VIM::evaluate('s:clone_opt')
+ progress = VIM::evaluate('s:progress_opt(1)')
+ nthr.times do
+ mtx.synchronize do
+ threads << Thread.new {
+ while pair = take1.call
+ name = pair.first
+ dir, uri, tag = pair.last.values_at *%w[dir uri tag]
+ exists = File.directory? dir
+ ok, result =
+ if exists
+ chdir = "#{cd} #{iswin ? dir : esc(dir)}"
+ ret, data = bt.call "#{chdir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url", nil, nil, nil
+ current_uri = data.lines.to_a.last
+ if !ret
+ if data =~ /^Interrupted|^Timeout/
+ [false, data]
+ else
+ [false, [data.chomp, "PlugClean required."].join($/)]
+ end
+ elsif !compare_git_uri(current_uri, uri)
+ [false, ["Invalid URI: #{current_uri}",
+ "Expected: #{uri}",
+ "PlugClean required."].join($/)]
+ else
+ if pull
+ log.call name, 'Updating ...', :update
+ fetch_opt = (tag && File.exist?(File.join(dir, '.git/shallow'))) ? '--depth 99999999' : ''
+ bt.call "#{chdir} && git fetch #{fetch_opt} #{progress} 2>&1", name, :update, nil
+ else
+ [true, skip]
+ end
+ end
+ else
+ d = esc dir.sub(%r{[\\/]+$}, '')
+ log.call name, 'Installing ...', :install
+ bt.call "git clone #{clone_opt unless tag} #{progress} #{uri} #{d} 2>&1", name, :install, proc {
+ FileUtils.rm_rf dir
+ }
+ end
+ mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok
+ log.call name, result, ok
+ end
+ } if running
+ end
+ end
+ threads.each { |t| t.join rescue nil }
+ logh.call
+ refresh.kill if refresh
+ watcher.kill
+EOF
+endfunction
+
+function! s:shellesc_cmd(arg)
+ let escaped = substitute(a:arg, '[&|<>()@^]', '^&', 'g')
+ let escaped = substitute(escaped, '%', '%%', 'g')
+ let escaped = substitute(escaped, '"', '\\^&', 'g')
+ let escaped = substitute(escaped, '\(\\\+\)\(\\^\)', '\1\1\2', 'g')
+ return '^"'.substitute(escaped, '\(\\\+\)$', '\1\1', '').'^"'
+endfunction
+
+function! s:shellesc(arg)
+ if &shell =~# 'cmd.exe$'
+ return s:shellesc_cmd(a:arg)
+ endif
+ return shellescape(a:arg)
+endfunction
+
+function! s:glob_dir(path)
+ return map(filter(s:glob(a:path, '**'), 'isdirectory(v:val)'), 's:dirpath(v:val)')
+endfunction
+
+function! s:progress_bar(line, bar, total)
+ call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']')
+endfunction
+
+function! s:compare_git_uri(a, b)
+ " See `git help clone'
+ " https:// [user@] github.com[:port] / junegunn/vim-plug [.git]
+ " [git@] github.com[:port] : junegunn/vim-plug [.git]
+ " file:// / junegunn/vim-plug [/]
+ " / junegunn/vim-plug [/]
+ let pat = '^\%(\w\+://\)\='.'\%([^@/]*@\)\='.'\([^:/]*\%(:[0-9]*\)\=\)'.'[:/]'.'\(.\{-}\)'.'\%(\.git\)\=/\?$'
+ let ma = matchlist(a:a, pat)
+ let mb = matchlist(a:b, pat)
+ return ma[1:2] ==# mb[1:2]
+endfunction
+
+function! s:format_message(bullet, name, message)
+ if a:bullet != 'x'
+ return [printf('%s %s: %s', a:bullet, a:name, s:lastline(a:message))]
+ else
+ let lines = map(s:lines(a:message), '" ".v:val')
+ return extend([printf('x %s:', a:name)], lines)
+ endif
+endfunction
+
+function! s:with_cd(cmd, dir)
+ return printf('cd%s %s && %s', s:is_win ? ' /d' : '', s:shellesc(a:dir), a:cmd)
+endfunction
+
+function! s:system(cmd, ...)
+ try
+ let [sh, shellcmdflag, shrd] = s:chsh(1)
+ let cmd = a:0 > 0 ? s:with_cd(a:cmd, a:1) : a:cmd
+ if s:is_win
+ let batchfile = tempname().'.bat'
+ call writefile(["@echo off\r", cmd . "\r"], batchfile)
+ let cmd = s:shellesc(batchfile)
+ endif
+ return system(cmd)
+ finally
+ let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd]
+ if s:is_win
+ call delete(batchfile)
+ endif
+ endtry
+endfunction
+
+function! s:system_chomp(...)
+ let ret = call('s:system', a:000)
+ return v:shell_error ? '' : substitute(ret, '\n$', '', '')
+endfunction
+
+function! s:git_validate(spec, check_branch)
+ let err = ''
+ if isdirectory(a:spec.dir)
+ let result = s:lines(s:system('git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url', a:spec.dir))
+ let remote = result[-1]
+ if v:shell_error
+ let err = join([remote, 'PlugClean required.'], "\n")
+ elseif !s:compare_git_uri(remote, a:spec.uri)
+ let err = join(['Invalid URI: '.remote,
+ \ 'Expected: '.a:spec.uri,
+ \ 'PlugClean required.'], "\n")
+ elseif a:check_branch && has_key(a:spec, 'commit')
+ let result = s:lines(s:system('git rev-parse HEAD 2>&1', a:spec.dir))
+ let sha = result[-1]
+ if v:shell_error
+ let err = join(add(result, 'PlugClean required.'), "\n")
+ elseif !s:hash_match(sha, a:spec.commit)
+ let err = join([printf('Invalid HEAD (expected: %s, actual: %s)',
+ \ a:spec.commit[:6], sha[:6]),
+ \ 'PlugUpdate required.'], "\n")
+ endif
+ elseif a:check_branch
+ let branch = result[0]
+ " Check tag
+ if has_key(a:spec, 'tag')
+ let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir)
+ if a:spec.tag !=# tag && a:spec.tag !~ '\*'
+ let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.',
+ \ (empty(tag) ? 'N/A' : tag), a:spec.tag)
+ endif
+ " Check branch
+ elseif a:spec.branch !=# branch
+ let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.',
+ \ branch, a:spec.branch)
+ endif
+ if empty(err)
+ let [ahead, behind] = split(s:lastline(s:system(printf(
+ \ 'git rev-list --count --left-right HEAD...origin/%s',
+ \ a:spec.branch), a:spec.dir)), '\t')
+ if !v:shell_error && ahead
+ if behind
+ " Only mention PlugClean if diverged, otherwise it's likely to be
+ " pushable (and probably not that messed up).
+ let err = printf(
+ \ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n"
+ \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', a:spec.branch, ahead, behind)
+ else
+ let err = printf("Ahead of origin/%s by %d commit(s).\n"
+ \ .'Cannot update until local changes are pushed.',
+ \ a:spec.branch, ahead)
+ endif
+ endif
+ endif
+ endif
+ else
+ let err = 'Not found'
+ endif
+ return [err, err =~# 'PlugClean']
+endfunction
+
+function! s:rm_rf(dir)
+ if isdirectory(a:dir)
+ call s:system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . s:shellesc(a:dir))
+ endif
+endfunction
+
+function! s:clean(force)
+ call s:prepare()
+ call append(0, 'Searching for invalid plugins in '.g:plug_home)
+ call append(1, '')
+
+ " List of valid directories
+ let dirs = []
+ let errs = {}
+ let [cnt, total] = [0, len(g:plugs)]
+ for [name, spec] in items(g:plugs)
+ if !s:is_managed(name)
+ call add(dirs, spec.dir)
+ else
+ let [err, clean] = s:git_validate(spec, 1)
+ if clean
+ let errs[spec.dir] = s:lines(err)[0]
+ else
+ call add(dirs, spec.dir)
+ endif
+ endif
+ let cnt += 1
+ call s:progress_bar(2, repeat('=', cnt), total)
+ normal! 2G
+ redraw
+ endfor
+
+ let allowed = {}
+ for dir in dirs
+ let allowed[s:dirpath(fnamemodify(dir, ':h:h'))] = 1
+ let allowed[dir] = 1
+ for child in s:glob_dir(dir)
+ let allowed[child] = 1
+ endfor
+ endfor
+
+ let todo = []
+ let found = sort(s:glob_dir(g:plug_home))
+ while !empty(found)
+ let f = remove(found, 0)
+ if !has_key(allowed, f) && isdirectory(f)
+ call add(todo, f)
+ call append(line('$'), '- ' . f)
+ if has_key(errs, f)
+ call append(line('$'), ' ' . errs[f])
+ endif
+ let found = filter(found, 'stridx(v:val, f) != 0')
+ end
+ endwhile
+
+ 4
+ redraw
+ if empty(todo)
+ call append(line('$'), 'Already clean.')
+ else
+ let s:clean_count = 0
+ call append(3, ['Directories to delete:', ''])
+ redraw!
+ if a:force || s:ask_no_interrupt('Delete all directories?')
+ call s:delete([6, line('$')], 1)
+ else
+ call setline(4, 'Cancelled.')
+ nnoremap <silent> <buffer> d :set opfunc=<sid>delete_op<cr>g@
+ nmap <silent> <buffer> dd d_
+ xnoremap <silent> <buffer> d :<c-u>call <sid>delete_op(visualmode(), 1)<cr>
+ echo 'Delete the lines (d{motion}) to delete the corresponding directories'
+ endif
+ endif
+ 4
+ setlocal nomodifiable
+endfunction
+
+function! s:delete_op(type, ...)
+ call s:delete(a:0 ? [line("'<"), line("'>")] : [line("'["), line("']")], 0)
+endfunction
+
+function! s:delete(range, force)
+ let [l1, l2] = a:range
+ let force = a:force
+ while l1 <= l2
+ let line = getline(l1)
+ if line =~ '^- ' && isdirectory(line[2:])
+ execute l1
+ redraw!
+ let answer = force ? 1 : s:ask('Delete '.line[2:].'?', 1)
+ let force = force || answer > 1
+ if answer
+ call s:rm_rf(line[2:])
+ setlocal modifiable
+ call setline(l1, '~'.line[1:])
+ let s:clean_count += 1
+ call setline(4, printf('Removed %d directories.', s:clean_count))
+ setlocal nomodifiable
+ endif
+ endif
+ let l1 += 1
+ endwhile
+endfunction
+
+function! s:upgrade()
+ echo 'Downloading the latest version of vim-plug'
+ redraw
+ let tmp = tempname()
+ let new = tmp . '/plug.vim'
+
+ try
+ let out = s:system(printf('git clone --depth 1 %s %s', s:shellesc(s:plug_src), s:shellesc(tmp)))
+ if v:shell_error
+ return s:err('Error upgrading vim-plug: '. out)
+ endif
+
+ if readfile(s:me) ==# readfile(new)
+ echo 'vim-plug is already up-to-date'
+ return 0
+ else
+ call rename(s:me, s:me . '.old')
+ call rename(new, s:me)
+ unlet g:loaded_plug
+ echo 'vim-plug has been upgraded'
+ return 1
+ endif
+ finally
+ silent! call s:rm_rf(tmp)
+ endtry
+endfunction
+
+function! s:upgrade_specs()
+ for spec in values(g:plugs)
+ let spec.frozen = get(spec, 'frozen', 0)
+ endfor
+endfunction
+
+function! s:status()
+ call s:prepare()
+ call append(0, 'Checking plugins')
+ call append(1, '')
+
+ let ecnt = 0
+ let unloaded = 0
+ let [cnt, total] = [0, len(g:plugs)]
+ for [name, spec] in items(g:plugs)
+ let is_dir = isdirectory(spec.dir)
+ if has_key(spec, 'uri')
+ if is_dir
+ let [err, _] = s:git_validate(spec, 1)
+ let [valid, msg] = [empty(err), empty(err) ? 'OK' : err]
+ else
+ let [valid, msg] = [0, 'Not found. Try PlugInstall.']
+ endif
+ else
+ if is_dir
+ let [valid, msg] = [1, 'OK']
+ else
+ let [valid, msg] = [0, 'Not found.']
+ endif
+ endif
+ let cnt += 1
+ let ecnt += !valid
+ " `s:loaded` entry can be missing if PlugUpgraded
+ if is_dir && get(s:loaded, name, -1) == 0
+ let unloaded = 1
+ let msg .= ' (not loaded)'
+ endif
+ call s:progress_bar(2, repeat('=', cnt), total)
+ call append(3, s:format_message(valid ? '-' : 'x', name, msg))
+ normal! 2G
+ redraw
+ endfor
+ call setline(1, 'Finished. '.ecnt.' error(s).')
+ normal! gg
+ setlocal nomodifiable
+ if unloaded
+ echo "Press 'L' on each line to load plugin, or 'U' to update"
+ nnoremap <silent> <buffer> L :call <SID>status_load(line('.'))<cr>
+ xnoremap <silent> <buffer> L :call <SID>status_load(line('.'))<cr>
+ end
+endfunction
+
+function! s:extract_name(str, prefix, suffix)
+ return matchstr(a:str, '^'.a:prefix.' \zs[^:]\+\ze:.*'.a:suffix.'$')
+endfunction
+
+function! s:status_load(lnum)
+ let line = getline(a:lnum)
+ let name = s:extract_name(line, '-', '(not loaded)')
+ if !empty(name)
+ call plug#load(name)
+ setlocal modifiable
+ call setline(a:lnum, substitute(line, ' (not loaded)$', '', ''))
+ setlocal nomodifiable
+ endif
+endfunction
+
+function! s:status_update() range
+ let lines = getline(a:firstline, a:lastline)
+ let names = filter(map(lines, 's:extract_name(v:val, "[x-]", "")'), '!empty(v:val)')
+ if !empty(names)
+ echo
+ execute 'PlugUpdate' join(names)
+ endif
+endfunction
+
+function! s:is_preview_window_open()
+ silent! wincmd P
+ if &previewwindow
+ wincmd p
+ return 1
+ endif
+endfunction
+
+function! s:find_name(lnum)
+ for lnum in reverse(range(1, a:lnum))
+ let line = getline(lnum)
+ if empty(line)
+ return ''
+ endif
+ let name = s:extract_name(line, '-', '')
+ if !empty(name)
+ return name
+ endif
+ endfor
+ return ''
+endfunction
+
+function! s:preview_commit()
+ if b:plug_preview < 0
+ let b:plug_preview = !s:is_preview_window_open()
+ endif
+
+ let sha = matchstr(getline('.'), '^ \X*\zs[0-9a-f]\{7,9}')
+ if empty(sha)
+ return
+ endif
+
+ let name = s:find_name(line('.'))
+ if empty(name) || !has_key(g:plugs, name) || !isdirectory(g:plugs[name].dir)
+ return
+ endif
+
+ if exists('g:plug_pwindow') && !s:is_preview_window_open()
+ execute g:plug_pwindow
+ execute 'e' sha
+ else
+ execute 'pedit' sha
+ wincmd P
+ endif
+ setlocal previewwindow filetype=git buftype=nofile nobuflisted modifiable
+ try
+ let [sh, shellcmdflag, shrd] = s:chsh(1)
+ let cmd = 'cd '.s:shellesc(g:plugs[name].dir).' && git show --no-color --pretty=medium '.sha
+ if s:is_win
+ let batchfile = tempname().'.bat'
+ call writefile(["@echo off\r", cmd . "\r"], batchfile)
+ let cmd = batchfile
+ endif
+ execute 'silent %!' cmd
+ finally
+ let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd]
+ if s:is_win
+ call delete(batchfile)
+ endif
+ endtry
+ setlocal nomodifiable
+ nnoremap <silent> <buffer> q :q<cr>
+ wincmd p
+endfunction
+
+function! s:section(flags)
+ call search('\(^[x-] \)\@<=[^:]\+:', a:flags)
+endfunction
+
+function! s:format_git_log(line)
+ let indent = ' '
+ let tokens = split(a:line, nr2char(1))
+ if len(tokens) != 5
+ return indent.substitute(a:line, '\s*$', '', '')
+ endif
+ let [graph, sha, refs, subject, date] = tokens
+ let tag = matchstr(refs, 'tag: [^,)]\+')
+ let tag = empty(tag) ? ' ' : ' ('.tag.') '
+ return printf('%s%s%s%s%s (%s)', indent, graph, sha, tag, subject, date)
+endfunction
+
+function! s:append_ul(lnum, text)
+ call append(a:lnum, ['', a:text, repeat('-', len(a:text))])
+endfunction
+
+function! s:diff()
+ call s:prepare()
+ call append(0, ['Collecting changes ...', ''])
+ let cnts = [0, 0]
+ let bar = ''
+ let total = filter(copy(g:plugs), 's:is_managed(v:key) && isdirectory(v:val.dir)')
+ call s:progress_bar(2, bar, len(total))
+ for origin in [1, 0]
+ let plugs = reverse(sort(items(filter(copy(total), (origin ? '' : '!').'(has_key(v:val, "commit") || has_key(v:val, "tag"))'))))
+ if empty(plugs)
+ continue
+ endif
+ call s:append_ul(2, origin ? 'Pending updates:' : 'Last update:')
+ for [k, v] in plugs
+ let range = origin ? '..origin/'.v.branch : 'HEAD@{1}..'
+ let cmd = 'git log --graph --color=never '.join(map(['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range], 's:shellesc(v:val)'))
+ if has_key(v, 'rtp')
+ let cmd .= ' -- '.s:shellesc(v.rtp)
+ endif
+ let diff = s:system_chomp(cmd, v.dir)
+ if !empty(diff)
+ let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : ''
+ call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)')))
+ let cnts[origin] += 1
+ endif
+ let bar .= '='
+ call s:progress_bar(2, bar, len(total))
+ normal! 2G
+ redraw
+ endfor
+ if !cnts[origin]
+ call append(5, ['', 'N/A'])
+ endif
+ endfor
+ call setline(1, printf('%d plugin(s) updated.', cnts[0])
+ \ . (cnts[1] ? printf(' %d plugin(s) have pending updates.', cnts[1]) : ''))
+
+ if cnts[0] || cnts[1]
+ nnoremap <silent> <buffer> <plug>(plug-preview) :silent! call <SID>preview_commit()<cr>
+ if empty(maparg("\<cr>", 'n'))
+ nmap <buffer> <cr> <plug>(plug-preview)
+ endif
+ if empty(maparg('o', 'n'))
+ nmap <buffer> o <plug>(plug-preview)
+ endif
+ endif
+ if cnts[0]
+ nnoremap <silent> <buffer> X :call <SID>revert()<cr>
+ echo "Press 'X' on each block to revert the update"
+ endif
+ normal! gg
+ setlocal nomodifiable
+endfunction
+
+function! s:revert()
+ if search('^Pending updates', 'bnW')
+ return
+ endif
+
+ let name = s:find_name(line('.'))
+ if empty(name) || !has_key(g:plugs, name) ||
+ \ input(printf('Revert the update of %s? (y/N) ', name)) !~? '^y'
+ return
+ endif
+
+ call s:system('git reset --hard HEAD@{1} && git checkout '.s:esc(g:plugs[name].branch).' --', g:plugs[name].dir)
+ setlocal modifiable
+ normal! "_dap
+ setlocal nomodifiable
+ echo 'Reverted'
+endfunction
+
+function! s:snapshot(force, ...) abort
+ call s:prepare()
+ setf vim
+ call append(0, ['" Generated by vim-plug',
+ \ '" '.strftime("%c"),
+ \ '" :source this file in vim to restore the snapshot',
+ \ '" or execute: vim -S snapshot.vim',
+ \ '', '', 'PlugUpdate!'])
+ 1
+ let anchor = line('$') - 3
+ let names = sort(keys(filter(copy(g:plugs),
+ \'has_key(v:val, "uri") && !has_key(v:val, "commit") && isdirectory(v:val.dir)')))
+ for name in reverse(names)
+ let sha = s:system_chomp('git rev-parse --short HEAD', g:plugs[name].dir)
+ if !empty(sha)
+ call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha))
+ redraw
+ endif
+ endfor
+
+ if a:0 > 0
+ let fn = expand(a:1)
+ if filereadable(fn) && !(a:force || s:ask(a:1.' already exists. Overwrite?'))
+ return
+ endif
+ call writefile(getline(1, '$'), fn)
+ echo 'Saved as '.a:1
+ silent execute 'e' s:esc(fn)
+ setf vim
+ endif
+endfunction
+
+function! s:split_rtp()
+ return split(&rtp, '\\\@<!,')
+endfunction
+
+let s:first_rtp = s:escrtp(get(s:split_rtp(), 0, ''))
+let s:last_rtp = s:escrtp(get(s:split_rtp(), -1, ''))
+
+if exists('g:plugs')
+ let g:plugs_order = get(g:, 'plugs_order', keys(g:plugs))
+ call s:upgrade_specs()
+ call s:define_commands()
+endif
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
diff --git a/.config/nvim/after/ftplugin/dhall.lua b/.config/nvim/after/ftplugin/dhall.lua
new file mode 100755
index 0000000..8590c7f
--- /dev/null
+++ b/.config/nvim/after/ftplugin/dhall.lua
@@ -0,0 +1 @@
+require('ftplugin.common-space')
diff --git a/.config/nvim/after/ftplugin/dockerfile.lua b/.config/nvim/after/ftplugin/dockerfile.lua
new file mode 100755
index 0000000..7ec2e69
--- /dev/null
+++ b/.config/nvim/after/ftplugin/dockerfile.lua
@@ -0,0 +1,4 @@
+require('ftplugin.common-space')
+
+bo.smartindent = true
+bo.autoindent = true
diff --git a/.config/nvim/after/ftplugin/gitcommit.lua b/.config/nvim/after/ftplugin/gitcommit.lua
new file mode 100755
index 0000000..1747d21
--- /dev/null
+++ b/.config/nvim/after/ftplugin/gitcommit.lua
@@ -0,0 +1 @@
+-- au! VimEnter COMMIT_EDITMSG exec 'norm gg' | startinsert!
diff --git a/.config/nvim/after/ftplugin/javascript.lua b/.config/nvim/after/ftplugin/javascript.lua
new file mode 100755
index 0000000..b300057
--- /dev/null
+++ b/.config/nvim/after/ftplugin/javascript.lua
@@ -0,0 +1,3 @@
+bo.tabstop = 2
+bo.shiftwidth = 2
+bo.expandtab = true
diff --git a/.config/nvim/after/ftplugin/lua.lua b/.config/nvim/after/ftplugin/lua.lua
new file mode 100755
index 0000000..b89af72
--- /dev/null
+++ b/.config/nvim/after/ftplugin/lua.lua
@@ -0,0 +1,6 @@
+require('ftplugin.common-space')
+
+o.colorcolumn = '80'
+bo.tabstop = 2
+bo.softtabstop = 2
+bo.shiftwidth = 2
diff --git a/.config/nvim/after/ftplugin/mail.lua b/.config/nvim/after/ftplugin/mail.lua
new file mode 100755
index 0000000..7946291
--- /dev/null
+++ b/.config/nvim/after/ftplugin/mail.lua
@@ -0,0 +1,4 @@
+o.colorcolumn = '72'
+bo.textwidth = 72
+bo.formatoptions = 'watqc'
+bo.smartindent = false
diff --git a/.config/nvim/after/ftplugin/markdown.lua b/.config/nvim/after/ftplugin/markdown.lua
new file mode 100755
index 0000000..bfcca96
--- /dev/null
+++ b/.config/nvim/after/ftplugin/markdown.lua
@@ -0,0 +1,8 @@
+require('ftplugin.common-space')
+
+bo.tabstop = 2
+bo.shiftwidth = 2
+-- bo.formatoptions = 'watqc'
+bo.formatoptions = 'qc'
+bo.textwidth = 80
+o.colorcolumn = '80'
diff --git a/.config/nvim/after/ftplugin/nix.lua b/.config/nvim/after/ftplugin/nix.lua
new file mode 100755
index 0000000..4384a54
--- /dev/null
+++ b/.config/nvim/after/ftplugin/nix.lua
@@ -0,0 +1,6 @@
+require('ftplugin.common-space')
+
+bo.colorcolumn = '80'
+bo.tabstop = 2
+bo.softtabstop = 2
+bo.shiftwidth = 2
diff --git a/.config/nvim/after/ftplugin/python.lua b/.config/nvim/after/ftplugin/python.lua
new file mode 100755
index 0000000..b6c54ff
--- /dev/null
+++ b/.config/nvim/after/ftplugin/python.lua
@@ -0,0 +1 @@
+o.colorcolumn = '80'
diff --git a/.config/nvim/after/ftplugin/sh.lua b/.config/nvim/after/ftplugin/sh.lua
new file mode 100755
index 0000000..b6c54ff
--- /dev/null
+++ b/.config/nvim/after/ftplugin/sh.lua
@@ -0,0 +1 @@
+o.colorcolumn = '80'
diff --git a/.config/nvim/after/ftplugin/shell.lua b/.config/nvim/after/ftplugin/shell.lua
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/.config/nvim/after/ftplugin/shell.lua
diff --git a/.config/nvim/after/ftplugin/terraform.lua b/.config/nvim/after/ftplugin/terraform.lua
new file mode 100755
index 0000000..154ab2c
--- /dev/null
+++ b/.config/nvim/after/ftplugin/terraform.lua
@@ -0,0 +1 @@
+require('ftplugin.common-markup')
diff --git a/.config/nvim/after/ftplugin/typescript.lua b/.config/nvim/after/ftplugin/typescript.lua
new file mode 100755
index 0000000..97d0503
--- /dev/null
+++ b/.config/nvim/after/ftplugin/typescript.lua
@@ -0,0 +1,2 @@
+require('ftplugin')
+require('javascript')
diff --git a/.config/nvim/after/ftplugin/typescriptreact.lua b/.config/nvim/after/ftplugin/typescriptreact.lua
new file mode 100755
index 0000000..97d0503
--- /dev/null
+++ b/.config/nvim/after/ftplugin/typescriptreact.lua
@@ -0,0 +1,2 @@
+require('ftplugin')
+require('javascript')
diff --git a/.config/nvim/after/ftplugin/yaml.lua b/.config/nvim/after/ftplugin/yaml.lua
new file mode 100755
index 0000000..b89af72
--- /dev/null
+++ b/.config/nvim/after/ftplugin/yaml.lua
@@ -0,0 +1,6 @@
+require('ftplugin.common-space')
+
+o.colorcolumn = '80'
+bo.tabstop = 2
+bo.softtabstop = 2
+bo.shiftwidth = 2
diff --git a/.config/nvim/after/indent/sass.vim b/.config/nvim/after/indent/sass.vim
new file mode 100755
index 0000000..de3dd44
--- /dev/null
+++ b/.config/nvim/after/indent/sass.vim
@@ -0,0 +1,4 @@
+
+set tabstop=8
+set shiftwidth=8
+set noexpandtab
diff --git a/.config/nvim/colors/colorscheme.vim b/.config/nvim/colors/colorscheme.vim
new file mode 100755
index 0000000..9822ea4
--- /dev/null
+++ b/.config/nvim/colors/colorscheme.vim
@@ -0,0 +1,208 @@
+hi ColorColumn cterm=NONE ctermbg=7 ctermfg=0
+hi Comment cterm=NONE ctermbg=0 ctermfg=7
+hi LineNr cterm=NONE ctermbg=0 ctermfg=15
+hi Menu cterm=NONE ctermbg=4 ctermfg=0
+hi Normal cterm=NONE ctermbg=0 ctermfg=15
+hi Scrollbar cterm=NONE ctermbg=0 ctermfg=4
+hi SpecialKey cterm=NONE ctermbg=0 ctermfg=7
+hi StatusLine cterm=NONE ctermbg=2 ctermfg=0
+
+hi Pmenu cterm=NONE ctermbg=2 ctermfg=0
+hi PmenuSel cterm=NONE ctermbg=11 ctermfg=0
+hi PmenuSbar cterm=NONE ctermbg=15 ctermfg=15
+hi PmenuThumb cterm=NONE ctermbg=7 ctermfg=7
+
+hi link IncSearch Visual
+hi link String Constant
+hi link Character Constant
+hi link Number Constant
+hi link Boolean Constant
+hi link Float Number
+hi link Function Identifier
+hi link Conditional Statement
+hi link Repeat Statement
+hi link Label Statement
+hi link Operator Statement
+hi link Keyword Statement
+hi link Exception Statement
+hi link Include PreProc
+hi link Define PreProc
+hi link Macro PreProc
+hi link PreCondit PreProc
+hi link StorageClass Type
+hi link Structure Type
+hi link Typedef Type
+hi link Tag Special
+hi link SpecialChar Special
+hi link Delimiter Special
+hi link SpecialComment Special
+hi link Debug Special
+
+" indent
+hi IndentGuidesOdd ctermbg=8
+hi IndentGuidesEven ctermbg=8
+
+" vim
+hi vimUserCommand cterm=bold ctermbg=NONE ctermfg=3
+hi vimNotation ctermbg=NONE ctermfg=4
+hi vimMapModKey ctermbg=NONE ctermfg=4
+hi vimBracket ctermbg=NONE ctermfg=7
+hi vimCommentString ctermbg=NONE ctermfg=7
+hi link vimMap vimUserCommand
+hi link vimLet vimUserCommand
+hi link vimCommand vimUserCommand
+hi link vimFTCmd vimUserCommand
+hi link vimAutoCmd vimUserCommand
+hi link vimNotFunc vimUserCommand
+
+" html
+hi htmlLink cterm=bold,underline ctermbg=NONE ctermfg=1
+hi htmlBold cterm=bold ctermbg=NONE ctermfg=5
+hi htmlItalic cterm=REVERSE ctermbg=6 ctermfg=0
+hi htmlEndTag cterm=NONE ctermbg=NONE ctermfg=3
+hi htmlTag cterm=NONE ctermbg=NONE ctermfg=3
+hi htmlTagName cterm=bold ctermbg=NONE ctermfg=3
+hi htmlH1 cterm=bold ctermbg=NONE ctermfg=3
+hi link htmlH2 htmlH1
+hi link htmlH3 htmlH1
+hi link htmlH4 htmlH1
+hi link htmlH5 htmlH1
+hi link htmlH6 htmlH1
+
+" css
+hi cssMultiColumnAttr ctermbg=NONE ctermfg=2
+hi cssBraces ctermbg=NONE ctermfg=2
+hi cssValueLength ctermbg=NONE ctermfg=7
+hi cssUnitDecorators ctermbg=NONE ctermfg=7
+hi cssValueNumber ctermbg=NONE ctermfg=7
+hi cssNoise ctermbg=NONE ctermfg=4
+hi cssTagName ctermbg=NONE ctermfg=1
+hi cssFunctionName ctermbg=NONE ctermfg=4
+hi link cssFontAttr cssMultiColumnAttr
+hi link cssFlexibleBoxAttr cssMultiColumnAttr
+hi link cssAttrComma cssBraces
+hi link cssValueLength cssValueNumber
+
+" scss/sass
+hi scssSelectorChar ctermbg=NONE ctermfg=7
+hi scssAttribute ctermbg=NONE ctermfg=7
+hi sassidChar ctermbg=NONE ctermfg=1
+hi sassClassChar ctermbg=NONE ctermfg=5
+hi sassInclude ctermbg=NONE ctermfg=5
+hi sassMixing ctermbg=NONE ctermfg=5
+hi sassMixinName ctermbg=NONE ctermfg=4
+hi link scssDefinition cssNoise
+
+" javascript
+hi javaScript ctermbg=NONE ctermfg=7
+hi javaScriptBraces ctermbg=NONE ctermfg=7
+hi javaScriptNumber ctermbg=NONE ctermfg=5
+
+hi markdownH1 cterm=bold ctermbg=NONE ctermfg=5
+hi markdownAutomaticLink cterm=underline ctermbg=1 ctermfg=15
+hi markdownError cterm=NONE ctermbg=4 ctermfg=15
+hi markdownCode cterm=NONE ctermbg=NONE ctermfg=3
+" hi markdownCodeBlock cterm=NONE ctermbg=15 ctermfg=3
+hi markdownCodeDelimiter cterm=NONE ctermbg=NONE ctermfg=5
+hi markdownItalic cterm=ITALIC
+hi markdownBold cterm=bold
+hi link markdownH2 markdownH1
+hi link markdownH3 markdownH1
+hi link markdownH4 markdownH1
+hi link markdownH5 markdownH1
+hi link markdownH6 markdownH1
+hi link markdownUrl markdownAutomaticLink
+
+hi xdefaultsValue ctermbg=NONE ctermfg=7
+hi rubyInclude ctermbg=NONE ctermfg=4
+hi rubyDefine ctermbg=NONE ctermfg=5
+hi rubyFunction ctermbg=NONE ctermfg=4
+hi rubyStringDelimiter ctermbg=NONE ctermfg=2
+hi rubyInteger ctermbg=NONE ctermfg=3
+hi rubyAttribute ctermbg=NONE ctermfg=4
+hi rubyConstant ctermbg=NONE ctermfg=3
+hi rubyInterpolation ctermbg=NONE ctermfg=2
+hi rubyInterpolationDelimiter ctermbg=NONE ctermfg=3
+hi rubyRegexp ctermbg=NONE ctermfg=6
+hi rubySymbol ctermbg=NONE ctermfg=2
+hi rubyTodo ctermbg=NONE ctermfg=8
+hi rubyRegexpAnchor ctermbg=NONE ctermfg=7
+hi link rubyRegexpQuantifier rubyRegexpAnchor
+
+hi pythonOperator cterm=bold ctermbg=NONE ctermfg=5
+hi pythonFunction cterm=bold ctermbg=NONE ctermfg=3
+hi pythonRepeat cterm=bold ctermbg=NONE ctermfg=5
+hi pythonStatement cterm=bold ctermbg=NONE ctermfg=1
+hi pythonBuiltIn cterm=bold ctermbg=NONE ctermfg=4
+
+hi phpMemberSelector ctermbg=NONE ctermfg=7
+hi phpComparison ctermbg=NONE ctermfg=7
+hi phpParent ctermbg=NONE ctermfg=7
+
+hi cOperator ctermbg=NONE ctermfg=6
+hi cPreCondit ctermbg=NONE ctermfg=5
+hi SignifySignAdd ctermbg=NONE ctermfg=2
+hi SignifySignChange ctermbg=NONE ctermfg=4
+hi SignifySignDelete ctermbg=NONE ctermfg=1
+
+hi NERDTreeDirSlash cterm=NONE ctermbg=NONE ctermfg=4
+hi NERDTreeExecFile cterm=NONE ctermbg=NONE ctermfg=7
+hi ALEWarning cterm=NONE ctermbg=3 ctermfg=15
+hi ALEWarningSign cterm=NONE ctermbg=3 ctermfg=15
+hi ALEError cterm=NONE ctermbg=4 ctermfg=15
+hi ALEErrorSign cterm=NONE ctermbg=4 ctermfg=15
+hi YcmWarningLine cterm=NONE ctermbg=NONE ctermfg=NONE
+hi YcmWarningSign cterm=bold ctermbg=3 ctermfg=1
+hi YcmWarningSection cterm=bold ctermbg=3 ctermfg=1
+hi YcmErrorLine cterm=NONE ctermbg=NONE ctermfg=NONE
+hi YcmErrorSign cterm=bold ctermbg=4 ctermfg=15
+hi YcmErrorSection cterm=bold ctermbg=4 ctermfg=15
+
+hi SignColumn cterm=NONE ctermbg=0
+hi GitGutterAdd cterm=bold ctermbg=0 ctermfg=2
+hi GitGutterAddLine cterm=NONE ctermbg=2
+hi GitGutterChange cterm=bold ctermbg=0 ctermfg=4
+hi GitGutterChangeLine cterm=NONE ctermbg=4
+hi GitGutterDelete cterm=bold ctermbg=0 ctermfg=1
+hi GitGutterDeleteLine cterm=NONE ctermbg=1
+hi link GitGutterChangeLineNr GitGutterChange
+hi link GitGutterChangeDelete GitGutterDelete
+hi link GitGutterAddLineNr GitGutterAdd
+hi link GitGutterDeleteLineNr GitGutterDelete
+hi link GitGutterChangeDeleteLineNr GitGutterDelete
+
+hi diffAdded cterm=NONE ctermbg=NONE ctermfg=2
+hi diffRemoved cterm=NONE ctermbg=NONE ctermfg=1
+hi diffChanged cterm=NONE ctermbg=NONE ctermfg=4
+
+hi diffChange cterm=NONE ctermbg=NONE ctermfg=4
+hi diffDelete cterm=NONE ctermbg=NONE ctermfg=1
+hi diffAdd cterm=NONE ctermbg=NONE ctermfg=2
+
+hi OverLength cterm=NONE ctermbg=9 ctermfg=15
+
+hi LspDiagnosticsVirtualTextError cterm=bold ctermfg=15 ctermbg=1
+hi LspDiagnosticsVirtualTextWarning cterm=bold ctermfg=15 ctermbg=3
+hi LspDiagnosticsVirtualTextInformation cterm=bold ctermfg=15 ctermbg=4
+hi LspDiagnosticsVirtualTextHint cterm=bold ctermfg=15 ctermbg=2
+hi link LspDiagnosticsSignError LspDiagnosticsVirtualTextError
+hi link LspDiagnosticsSignWarning LspDiagnosticsVirtualTextWarning
+hi link LspDiagnosticsSignInformation LspDiagnosticsVirtualTextInformation
+hi link LspDiagnosticsSignHint LspDiagnosticsVirtualTextHint
+
+"
+hi semshiLocal cterm=NONE ctermbg=0 ctermfg=209
+hi semshiGlobal cterm=NONE ctermbg=0 ctermfg=214
+hi semshiImported cterm=bold ctermbg=0 ctermfg=214
+hi semshiParameter cterm=NONE ctermbg=0 ctermfg=75
+hi semshiParameterUnused cterm=underline ctermbg=0 ctermfg=117
+hi semshiFree cterm=NONE ctermbg=0 ctermfg=218
+hi semshiBuiltin cterm=NONE ctermbg=0 ctermfg=207
+hi semshiAttribute cterm=NONE ctermbg=0 ctermfg=49
+hi semshiSelf cterm=NONE ctermbg=0 ctermfg=249
+hi semshiUnresolved cterm=underline ctermbg=0 ctermfg=226
+hi semshiSelected cterm=NONE ctermbg=161 ctermfg=231
+
+hi semshiErrorSign cterm=NONE ctermbg=160 ctermfg=231
+hi semshiErrorChar cterm=NONE ctermbg=160 ctermfg=231
+sign define semshiError text=E> texthl=semshiErrorSign
diff --git a/.config/nvim/init.lua b/.config/nvim/init.lua
new file mode 100755
index 0000000..eda0497
--- /dev/null
+++ b/.config/nvim/init.lua
@@ -0,0 +1,10 @@
+o = vim.o
+bo = vim.bo
+wo = vim.wo
+cmd = vim.cmd
+
+require('settings')
+require('plugins')
+require('lsp')
+require('statusline')
+require('keymaps')
diff --git a/.config/nvim/init.old.vim b/.config/nvim/init.old.vim
new file mode 100755
index 0000000..35630e8
--- /dev/null
+++ b/.config/nvim/init.old.vim
@@ -0,0 +1,218 @@
+" nvim configuration ~~ rgoncalves.se
+
+" vim-plug autoinstall
+if empty(glob('~/.local/share/nvim/site/autoload/plug.vim'))
+ silent !curl -fLo ~/.local/share/nvim/site/autoload/plug.vim --create-dirs
+ \ https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
+ autocmd VimEnter * PlugInstall --sync | source $MYVIMRC
+endif
+
+" system agnostic detection
+let uname = system('uname')[:-2]
+
+" global configuration
+set encoding=utf-8
+set laststatus=2
+set scrolloff=3
+set viminfo=
+set mouse=a
+set title
+set number
+set noswapfile
+set shell=/bin/sh
+set updatetime=2500
+set synmaxcol=300
+set lazyredraw
+set ttyfast
+set wildoptions=
+set autochdir
+let sh_minlines=100
+let sh_maxlines=600
+
+" filetypes and colorschemes
+syntax on
+filetype on
+filetype plugin on
+filetype indent on
+colorscheme base
+
+" keybindings
+noremap =j :%!python -m json.tool<CR>
+noremap =k :%s/-\|-/-+-/g<CR>
+noremap =l :%s/-+-/-\|-/g<CR>
+noremap <F2> :NERDTreeToggle <CR>
+noremap <F3> :GitGutterCustomToggle <CR>
+inoremap <C-h> <Left>
+inoremap <C-j> <Down>
+inoremap <C-k> <Up>
+inoremap <C-l> <Right>
+" keybindings - omnifunc
+inoremap <expr> <CR> pumvisible() ? "\<C-Y>" : "\<CR>"
+inoremap <expr> <C-l> pumvisible() ? "\<CR>" : "\<CR>"
+inoremap <expr> <C-n> pumvisible() ? '<C-n>' :
+ \ '<C-n><C-r>=pumvisible() ? "\<lt>Down>" : ""<CR>'
+inoremap <expr> <M-,> pumvisible() ? '<C-n>' :
+ \ '<C-x><C-o><C-n><C-p><C-r>=pumvisible() ? "\<lt>Down>" : ""<CR>'
+inoremap <expr> <C-Space> (pumvisible() ? (col('.') > 1 ? '<Esc>i<Right>' : '<Esc>i') : '') .
+ \ '<C-x><C-o><C-r>=pumvisible() ? "\<lt>C-n>\<lt>C-p>\<lt>Down>" : ""<CR>'
+inoremap <expr> <S-Space> (pumvisible() ? (col('.') > 1 ? '<Esc>i<Right>' : '<Esc>i') : '') .
+ \ '<C-x><C-u><C-r>=pumvisible() ? "\<lt>C-n>\<lt>C-p>\<lt>Down>" : ""<CR>'
+
+let g:polyglot_disabled = ['autoindent', 'sensible']
+
+" PLUGINS list
+if !empty(glob('~/.local/share/nvim/site/autoload/plug.vim'))
+ call plug#begin('~/.nvim/plugged/')
+ " core
+ Plug 'scrooloose/nerdtree'
+ " completion
+ Plug 'w0rp/ale'
+ " Plug 'davidhalter/jedi-vim'
+ Plug 'maralla/completor.vim'
+ " fzf
+ Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
+ Plug 'junegunn/fzf.vim'
+ " indent
+ Plug 'nathanaelkane/vim-indent-guides'
+ Plug 'editorconfig/editorconfig-vim'
+ Plug 'pangloss/vim-javascript'
+ Plug 'pearofducks/ansible-vim'
+ Plug 'sheerun/vim-polyglot'
+ Plug 'dbeniamine/vim-mail'
+ Plug 'ap/vim-css-color'
+ " notes
+ Plug 'dhruvasagar/vim-table-mode'
+ Plug 'jceb/vim-orgmode'
+ " headers and source
+ Plug 'vim-scripts/a.vim'
+ Plug 'majutsushi/tagbar'
+ " git
+ Plug 'airblade/vim-gitgutter'
+ Plug 'tpope/vim-fugitive'
+ Plug 'jreybert/vimagit'
+ Plug 'junegunn/gv.vim'
+ " python
+ Plug 'petobens/poet-v'
+ call plug#end()
+endif
+
+" indentation
+set smarttab
+set autoindent
+set nocompatible
+set tabstop=8
+set shiftwidth=8
+set softtabstop=8
+set noexpandtab
+set list lcs=tab:\|\ ".
+set fillchars+=vert:\ ".
+set ve+=onemore
+set list
+set shortmess=atToOF
+
+" plugins settings
+let g:ale_sign_error = ''
+let g:ale_sign_warning = ''
+let g:ale_c_clang_options='-std=gnu99 -Wall'
+let g:ale_c_gcc_options='-std=gnu99 -Wall'
+let g:ale_sh_shellcheck_options = '-e SC1090 SC2181 -e SC2155'
+let g:ale_python_flake8_options = '--ignore=E501'
+let g:ale_linters = { 'python': ['bandit', 'flake8'], }
+
+let g:gitgutter_max_signs = 500
+let g:gitgutter_sign_added = '+'
+let g:gitgutter_sign_modified = '~'
+let g:gitgutter_sign_removed = '-'
+let g:gitgutter_sign_modified_removed = '-'
+
+let g:indent_guides_enable_on_vim_startup = 0
+let g:indent_guides_auto_colors = 0
+let g:indent_cuides_exclude_filetypes = [
+ \'help',
+ \'terminal'
+ \]
+let g:poetv_executables = ['poetry']
+let g:poetv_auto_activate = 1
+
+let g:python_host_prog = system('which python2')[:-2]
+let g:python3_host_prog = system('which python3')[:-2]
+let g:jedi#auto_initialization = 1
+let g:jedi#popup_on_dot = 0
+
+let g:NERDTreeAutoDeleteBuffer = 1
+let g:NERDTreeMinimalUI = 1
+let g:NERDTreeDirArrowExpandable = '+'
+let g:NERDTreeDirArrowCollapsible = '-'
+
+let g:EditorConfig_exclude_patterns = ['fugitive://.*', 'scp://.*']
+let g:fzf_layout = { 'down': '40%'}
+let g:magit_default_fold_level = 2
+let g:sql_type_default = 'pgsql'
+let g:table_mode_always_active = 0
+
+if uname == 'OpenBSD'
+ let g:ncm2_pyclang#library_path = '/usr/local/lib/libclang.so.8.1'
+ let g:completor_python_binary = '/usr/bin/python3'
+endif
+
+" netrw
+let g:netrw_banner = 0
+let g:netrw_browse_split = 4
+let g:netrw_liststyle = 3
+let g:netrw_altv = 1
+let g:netrw_winsize = 25
+let g:netrw_list_hide = '\(^\|\s\s\)\zs\.\S\+'
+
+autocmd BufNewFile,BufReadPost *.{yaml,yml} set filetype=yaml
+autocmd BufNewFile,BufReadPost */*ssh/config.d/* set syntax=sshconfig
+autocmd BufNewFile,BufReadPost */*ansible*/**/*.yml set filetype=yaml.ansible
+autocmd BufNewFile,BufRead ~/.config/i3/config set filetype=i3config
+autocmd BufNewFile,BufRead ~/.config/sway/config set filetype=i3config
+autocmd BufNewFile,BufRead ~/.config/polybar/config set filetype=dosini
+autocmd FileType conf let b:EditorConfig_disable = 1
+autocmd FileType gitcommit let b:EditorConfig_disable = 1
+autocmd FileType java setlocal omnifunc=javacomplete#Complete
+autocmd FileType yaml let b:EditorConfig_disable = 1
+
+" statusline
+set statusline=
+set statusline+=\ %f
+set statusline+=%m
+set statusline+=%=
+set statusline+=\ %y
+set statusline+=\ %{&fileencoding?&fileencoding:&encoding}
+set statusline+=\ [%{&fileformat}\]
+set statusline+=\ %p%%
+set statusline+=\ %l:%c
+set statusline+=\
+
+" completion
+set omnifunc=syntaxcomplete#Complete
+set completeopt=longest,menuone
+
+" commands
+command! PlugSync PlugClean |
+ \ PlugInstall |
+ \ UpdateRemotePlugins
+command! GitGutterCustomToggle GitGutterLineHighlightsToggle |
+ \ GitGutterLineNrHighlightsToggle
+command! Gblame Git blame
+
+" cnoremap <silent> <cr> <cr>:call <SID>CommandCallback()<cr>
+
+function! s:SyncColorcolumnColorscheme()
+ let l:length = '/\%' . string(&colorcolumn + 1) . 'v.\+/'
+ execute 'match' 'OverLength' l:length
+endfunction
+
+function! s:CommandCallback()
+ let l:last_command = @:
+
+ if l:last_command =~ 'set colorcolumn'
+ call s:SyncColorcolumnColorscheme()
+ endif
+endfunction
+
+function! s:Init()
+ call s:SyncColorcolumnColorscheme()
+endfunction
diff --git a/.config/nvim/lua/ftplugin/common-markup.lua b/.config/nvim/lua/ftplugin/common-markup.lua
new file mode 100755
index 0000000..956a163
--- /dev/null
+++ b/.config/nvim/lua/ftplugin/common-markup.lua
@@ -0,0 +1,3 @@
+bo.expandtab = true
+bo.shiftwidth = 2
+bo.tabstop = 2
diff --git a/.config/nvim/lua/ftplugin/common-space.lua b/.config/nvim/lua/ftplugin/common-space.lua
new file mode 100755
index 0000000..ccefb94
--- /dev/null
+++ b/.config/nvim/lua/ftplugin/common-space.lua
@@ -0,0 +1,5 @@
+bo.tabstop=4
+bo.shiftwidth=4
+bo.textwidth=0
+bo.wrapmargin=0
+bo.expandtab = true
diff --git a/.config/nvim/lua/ftplugin/init.lua b/.config/nvim/lua/ftplugin/init.lua
new file mode 100755
index 0000000..906b7a1
--- /dev/null
+++ b/.config/nvim/lua/ftplugin/init.lua
@@ -0,0 +1,2 @@
+package.path = package.path .. ';' .. vim.fn.stdpath('config') .. '/ftplugin/?.lua'
+package.path = package.path .. ';' .. vim.fn.stdpath('config') .. '/after/ftplugin/?.lua'
diff --git a/.config/nvim/lua/keymaps.lua b/.config/nvim/lua/keymaps.lua
new file mode 100755
index 0000000..070ddc9
--- /dev/null
+++ b/.config/nvim/lua/keymaps.lua
@@ -0,0 +1,24 @@
+--[[
+-- Keymaps
+--]]
+
+local map = vim.api.nvim_set_keymap
+local cmd = vim.cmd
+
+options = {noremap=true}
+
+map('i', '<C-h>', '<Left>', options)
+map('i', '<C-j>', '<Down>', options)
+map('i', '<C-k>', '<Up>', options)
+map('i', '<C-l>', '<Right>', options)
+map('i', '<C-l>', '<Right>', options)
+
+map('', '<C-n>', ':NvimTreeToggle<CR>', options)
+
+--[[
+map('n', '<silent><expr>', '<C-Space>', 'compe#complete()', options)
+map('n', '<silent><expr>', '<CR>', 'compe#confirm(\'<CR>\'', options)
+map('n', '<silent><expr>', '<C-e>', 'compe#close(\'<C-e>\'', options)
+map('n', '<silent><expr>', '<C-f>', 'compe#scroll({\'delta\': +4}', options)
+map('n', '<silent><expr>', '<C-d>', 'compe#scroll({\'delta\': -4}', options)
+--]]
diff --git a/.config/nvim/lua/lsp.lua b/.config/nvim/lua/lsp.lua
new file mode 100755
index 0000000..95d9409
--- /dev/null
+++ b/.config/nvim/lua/lsp.lua
@@ -0,0 +1,17 @@
+--[[
+-- lsp
+--]]
+
+local lsp = vim.lsp
+
+lsp.handlers["textDocument/publishDiagnostics"] = lsp.with(
+ lsp.diagnostic.on_publish_diagnostics,
+ {
+ underline = false,
+ virtual_text = {
+ prefix = "▒ ",
+ spacing = 8,
+ },
+ signs = true,
+ }
+)
diff --git a/.config/nvim/lua/plugins.lua b/.config/nvim/lua/plugins.lua
new file mode 100755
index 0000000..24736eb
--- /dev/null
+++ b/.config/nvim/lua/plugins.lua
@@ -0,0 +1,145 @@
+--[[
+-- Plugins
+--]]
+
+-- Bootstrap for Paq
+local install_path = vim.fn.stdpath('data')..'/site/pack/paqs/start/paq-nvim'
+if vim.fn.empty(vim.fn.glob(install_path)) > 0 then
+ vim.fn.system({
+ 'git', 'clone', '--depth=1',
+ 'https://github.com/savq/paq-nvim.git',
+ install_path
+ })
+ vim.cmd 'packadd paq-nvim'
+end
+
+-- Enable Paq
+vim.cmd 'packadd paq-nvim'
+
+-- Plugin list
+require('paq') {
+ {'savq/paq-nvim'};
+
+ -- lsp
+ {
+ 'nvim-treesitter/nvim-treesitter',
+ run=':TSUpdate'
+ };
+ {
+ 'neovim/nvim-lspconfig',
+ run='python3 -m pipx install python-lsp-server[all]'
+ };
+ {
+ 'numirias/semshi',
+ run=':UpdateRemotePlugins'
+ };
+
+ -- utils
+ {'folke/trouble.nvim'};
+ {'hrsh7th/nvim-compe'};
+
+ -- org
+ {'TimUntersberger/neogit'};
+ {'kyazdani42/nvim-tree.lua'};
+
+ -- indent
+ {'lewis6991/gitsigns.nvim'};
+ {'glepnir/indent-guides.nvim'};
+ {'darazaki/indent-o-matic'};
+
+ -- dep
+ {'nvim-lua/plenary.nvim'};
+}
+
+-- Treesitter
+
+local parser_configs = require('nvim-treesitter.parsers').get_parser_configs()
+
+require('nvim-treesitter.configs').setup {
+ ensure_installed = 'maintained',
+ ignore_install = {
+ 'verilog',
+ 'kotlin'
+ },
+ highlight = {
+ enable = true,
+ }
+}
+
+-- completion
+
+require('compe').setup {
+ enabled = true,
+ source = {
+ path = true;
+ buffer = true;
+ calc = true;
+ nvim_lsp = true;
+ nvim_lua = true;
+ vsnip = true;
+ ultisnips = true;
+ luasnip = true;
+ neorg = true;
+ };
+}
+
+-- LSP configuration
+
+local lsputil = require('lspconfig/util')
+local python_venv = require('utils').get_python_venv()
+require('lspconfig').pylsp.setup{
+ cmd = {'pylsp', '-v'},
+ cmd_env = {
+ VIRTUAL_ENV = python_venv,
+ PATH = lsputil.path.join(python_venv, 'bin') .. ':' .. vim.env.PATH
+ },
+}
+
+require('lspconfig').clangd.setup{}
+require('lspconfig').eslint.setup{}
+require('lspconfig').terraformls.setup{}
+
+-- org
+
+require('trouble').setup{}
+require('neogit').setup{}
+
+-- syntactic sugar
+
+require('indent_guides').setup{
+ indent_enable=true;
+ exclude_filetypes={
+ 'help',
+ 'calendar',
+ 'NvimTree'
+ };
+}
+
+require('indent-o-matic').setup {
+ max_lines = 0,
+ standard_widths = { 2, 4, 8 },
+
+ filetype_typescript = {
+ max_lines = 4096,
+ },
+
+ filetype_javascript = {
+ max_lines = 4096,
+ },
+
+ filetype_ = {
+ standard_widths = { 2, 4 },
+ },
+}
+
+vim.g['semshi#update_delay_factor'] = 0.0001
+
+require('gitsigns').setup{
+ signs = {
+ add = {hl = 'GitSignsAdd' , text = '▍', numhl='GitSignsAddNr' , linehl='GitSignsAddLn'},
+ change = {hl = 'GitSignsChange', text = '▍', numhl='GitSignsChangeNr', linehl='GitSignsChangeLn'},
+ delete = {hl = 'GitSignsDelete', text = '▍', numhl='GitSignsDeleteNr', linehl='GitSignsDeleteLn'},
+ topdelete = {hl = 'GitSignsDelete', text = '▍', numhl='GitSignsDeleteNr', linehl='GitSignsDeleteLn'},
+ changedelete = {hl = 'GitSignsChange', text = '▍', numhl='GitSignsChangeNr', linehl='GitSignsChangeLn'},
+ }
+}
diff --git a/.config/nvim/lua/settings.lua b/.config/nvim/lua/settings.lua
new file mode 100755
index 0000000..2741c99
--- /dev/null
+++ b/.config/nvim/lua/settings.lua
@@ -0,0 +1,30 @@
+bo.expandtab = false
+bo.shiftwidth = 8
+bo.softtabstop = 8
+
+o.completeopt = 'longest,menuone'
+o.shell = '/bin/sh'
+o.encoding = 'utf-8'
+o.scrolloff = 3
+o.lazyredraw = true
+o.autochdir = true
+o.smarttab = true
+o.number = true
+o.list = true
+
+o.swapfile = false
+
+-- o.foldmethod = 'expr'
+-- o.foldexpr = 'nvim_treesitter#foldexpr()'
+
+cmd 'syntax enable'
+cmd 'filetype on'
+cmd 'filetype plugin on'
+cmd 'filetype indent on'
+cmd 'colorscheme colorscheme'
+
+vim.cmd([[
+au BufNewFile,BufRead *.Dockerfile,Dockerfile.* setlocal filetype=dockerfile
+au BufNewFile,BufRead *.j2*,*.jinja* setlocal filetype=django
+au BufNewFile,BufRead *.env* setlocal filetype=sh
+]])
diff --git a/.config/nvim/lua/statusline.lua b/.config/nvim/lua/statusline.lua
new file mode 100755
index 0000000..099d930
--- /dev/null
+++ b/.config/nvim/lua/statusline.lua
@@ -0,0 +1,54 @@
+--[[
+-- Statusline
+--]]
+
+local api = vim.api
+local cmd = vim.cmd
+local utils = require('utils')
+
+function _(str)
+ return '[' .. str .. ']'
+end
+
+function set_split()
+ return '%m%='
+end
+
+function get_separator()
+ return ' '
+end
+
+function get_file()
+ return '%f'
+end
+
+function get_fileformat()
+ return '%{&fileformat}'
+end
+
+function get_fileencoding()
+ return '%{&fileencoding?&fileencoding:&encoding}'
+end
+
+function get_scrollposition()
+ return '%p%%'
+end
+
+function get_cursorposition()
+ return '%l:%c'
+end
+
+function get_statusline()
+ return table.concat({
+ get_file(),
+ set_split(),
+ _(get_fileencoding()),
+ _(get_fileformat()),
+ _(get_scrollposition()),
+ _(get_cursorposition()),
+ })
+end
+
+-- Enable statusline
+utils.opt('o', 'laststatus', 2)
+utils.opt('o', 'statusline', get_statusline())
diff --git a/.config/nvim/lua/utils.lua b/.config/nvim/lua/utils.lua
new file mode 100755
index 0000000..ae164e8
--- /dev/null
+++ b/.config/nvim/lua/utils.lua
@@ -0,0 +1,46 @@
+local utils = {}
+
+local scopes = {o = vim.o, b = vim.bo, w = vim.wo}
+
+--- Apply a setting with a specified scope
+function utils.opt(scope, key, value)
+ scopes[scope][key] = value
+ if scope ~= 'o' then scopes['o'][key] = value end
+end
+
+--- Apply an array of settings
+function utils.opts(params)
+ for i, item in ipairs(params) do
+ utils.opt(item[1], item[2], item[3])
+ end
+end
+
+--- Key mapping
+function utils.map(mode, lhs, rhs, opts)
+ local options = {noremap = true}
+ if opts then options = vim.tbl_extend('force', options, opts) end
+ vim.api.nvim_set_keymap(mode, lhs, rhs, options)
+end
+
+function utils.get_python_venv()
+ local lsputil = require('lspconfig/util')
+ local match
+
+ if vim.env.VIRTUAL_ENV then
+ return vim.env.VIRTUAL_ENV
+ end
+
+ match = vim.fn.glob(lsputil.path.join(vim.fn.getcwd(), 'Pipfile'))
+ if match ~= '' then
+ return vim.fn.trim(vim.fn.system('PIPENV_PIPFILE=' .. match .. ' pipenv --venv'))
+ end
+
+ match = vim.fn.system('test -f poetry.lock && poetry env info -p')
+ if vim.v.shell_error then
+ return vim.fn.trim(match)
+ end
+
+ return ''
+end
+
+return utils
diff --git a/.config/qutebrowser/config.py b/.config/qutebrowser/config.py
new file mode 100755
index 0000000..13dca3f
--- /dev/null
+++ b/.config/qutebrowser/config.py
@@ -0,0 +1,111 @@
+
+# ~/.config/qutebrowser/config.py
+# rgoncalves.se
+
+import logging
+import os
+import re
+import subprocess
+import platform
+
+logging.basicConfig(level=logging.DEBUG)
+
+"""
+Bypass flake8 warnings.
+"""
+try:
+ config
+except NameError:
+ config = None
+
+
+def select_rendering_method():
+ """
+ Select best rendering method depending on hardware and system.
+ """
+ method = 'none'
+
+ if platform.uname().system in 'Linux':
+ lspci_bin = subprocess.run(['lspci'], stdout=subprocess.PIPE)
+ if re.search('NVIDIA', lspci_bin.stdout.decode('utf-8')) is None:
+ method = 'qt-quick'
+
+ return method
+
+
+def select_qt_arguments():
+ """
+ Select best arguments for launching qutebrowser.
+ Empty by default, it picks lag free options for musl systems.
+ """
+ args = []
+ ls_bin = os.popen("ldd /bin/ls").read()
+
+ if re.search('musl', ls_bin) is not None:
+ args.append('disable-seccomp-filter-sandbox')
+
+ return args
+
+
+config.load_autoconfig(False)
+config.set('auto_save.session', True)
+config.set('scrolling.smooth', False)
+config.set('qt.highdpi', False)
+config.set('qt.force_software_rendering', select_rendering_method())
+config.set('qt.args', select_qt_arguments())
+
+config.bind(',m', 'spawn mpv {url}')
+
+config.set('content.images', True, 'chrome-devtools://*')
+config.set('content.images', True, 'devtools://*')
+config.set('content.javascript.enabled', True, 'chrome-devtools://*')
+config.set('content.javascript.enabled', True, 'devtools://*')
+config.set('content.javascript.enabled', True, 'chrome://*/*')
+config.set('content.javascript.enabled', True, 'qute://*/*')
+
+config.set('content.notifications.enabled', False)
+config.set('content.notifications.enabled', True, '*://*.zoho.eu/*')
+config.set('content.notifications.enabled', True, '*://*.viperdev.io/*')
+config.set('content.notifications.enabled', True, '*://*.rgoncalves.se/*')
+
+config.set('content.register_protocol_handler', False)
+config.set('content.geolocation', False)
+config.set('content.media.audio_video_capture', True, '*://*.zoho.eu/*')
+config.set('content.media.audio_capture', True, '*://*.zoho.eu/*')
+config.set('content.media.video_capture', True, '*://*.zoho.eu/*')
+
+config.set('completion.web_history.max_items', 0)
+config.set('tabs.background', True)
+config.set('tabs.indicator.width', 3)
+config.set('tabs.indicator.padding',
+ {'bottom': 0, 'left': 1, 'right': 4, 'top': 0})
+
+config.set('fonts.default_family', 'Terminus')
+config.set('fonts.default_size', '12pt')
+
+config.set('colors.tabs.bar.bg', '#000000')
+config.set('colors.tabs.even.bg', '#000000')
+config.set('colors.tabs.even.fg', '#ffffff')
+config.set('colors.tabs.odd.bg', '#000000')
+config.set('colors.tabs.odd.fg', '#ffffff')
+config.set('colors.tabs.indicator.system', 'none')
+config.set('colors.tabs.selected.even.bg', '#ffffff')
+config.set('colors.tabs.selected.even.fg', '#000000')
+config.set('colors.tabs.selected.odd.bg', '#ffffff')
+config.set('colors.tabs.selected.odd.fg', '#000000')
+config.set('colors.tabs.pinned.selected.even.bg', '#ffffff')
+config.set('colors.tabs.pinned.selected.odd.bg', '#ffffff')
+config.set('colors.tabs.pinned.selected.even.fg', '#000000')
+config.set('colors.tabs.pinned.selected.odd.fg', '#000000')
+config.set('colors.tabs.indicator.error', '#ff0000')
+
+config.set('content.prefers_reduced_motion', True)
+config.set('content.headers.referer', 'same-domain')
+config.set('url.default_page', 'https://lite.duckduckgo.com/lite/')
+config.set('url.start_pages', 'https://lite.duckduckgo.com/lite/')
+config.set('url.searchengines',
+ {'DEFAULT': 'https://lite.duckduckgo.com/lite/?q={}'})
+
+config.set('downloads.location.directory', f'{os.environ["HOME"]}/downloads')
+config.set('content.autoplay', False)
+config.set('content.cookies.accept', 'no-3rdparty')
diff --git a/.config/qutebrowser/greasemonkey/boursorama-css.js b/.config/qutebrowser/greasemonkey/boursorama-css.js
new file mode 100755
index 0000000..4d1c29e
--- /dev/null
+++ b/.config/qutebrowser/greasemonkey/boursorama-css.js
@@ -0,0 +1,18 @@
+// ==UserScript==
+// @name boursorama css
+// @match *://*.boursorama.com/*
+// @grant none
+// ==/UserScript==
+
+GM_addStyle(`
+ .chatbot,
+ .c-promotional-cards-wrapper,
+ .c-articles-wrapper,
+ .c-offers__entry-card {
+ width: 0px !important;
+ height: 0px !important;
+ visibility: hidden;
+ margin: 0px;
+ padding: 0px;
+ }
+`)
diff --git a/.config/qutebrowser/greasemonkey/duckduckgo.js b/.config/qutebrowser/greasemonkey/duckduckgo.js
new file mode 100755
index 0000000..d4d971f
--- /dev/null
+++ b/.config/qutebrowser/greasemonkey/duckduckgo.js
@@ -0,0 +1,12 @@
+// ==UserScript==
+// @name custom duckduckgo
+// @match https://duckduckgo.com/*
+// @grant none
+// ==/UserScript==
+
+GM_addStyle(`
+ .result--ads {
+ visibility: hidden !important;
+ height: 0px !important;
+ }
+`)
diff --git a/.config/qutebrowser/greasemonkey/github-css.js b/.config/qutebrowser/greasemonkey/github-css.js
new file mode 100755
index 0000000..7b32636
--- /dev/null
+++ b/.config/qutebrowser/greasemonkey/github-css.js
@@ -0,0 +1,12 @@
+// ==UserScript==
+// @name github css
+// @match *://github.com/*
+// @match *://gist.github.com/*
+// @grant none
+// ==/UserScript==
+
+GM_addStyle(`
+ *, .BtnGroup-item {
+ border-radius: 0px !important;
+ }
+`)
diff --git a/.config/qutebrowser/greasemonkey/imgur-to-imgin.js b/.config/qutebrowser/greasemonkey/imgur-to-imgin.js
new file mode 100755
index 0000000..d4ac434
--- /dev/null
+++ b/.config/qutebrowser/greasemonkey/imgur-to-imgin.js
@@ -0,0 +1,8 @@
+// ==UserScript==
+// @name imgur to imgin
+// @match *://imgur.com/*
+// @grant none
+// @run-at document-start
+// ==/UserScript==
+
+top.location.hostname = "imgin.voidnet.tech";
diff --git a/.config/qutebrowser/greasemonkey/instagram-to-bibliogram.js b/.config/qutebrowser/greasemonkey/instagram-to-bibliogram.js
new file mode 100755
index 0000000..a608059
--- /dev/null
+++ b/.config/qutebrowser/greasemonkey/instagram-to-bibliogram.js
@@ -0,0 +1,8 @@
+// ==UserScript==
+// @name instagram to biliogram
+// @match *://*.instagram.com/*
+// @grant none
+// @run-at document-start
+// ==/UserScript==
+
+top.location.hostname = "bibliogram.art";
diff --git a/.config/qutebrowser/greasemonkey/medium-to-scribe.js b/.config/qutebrowser/greasemonkey/medium-to-scribe.js
new file mode 100755
index 0000000..5a94ab0
--- /dev/null
+++ b/.config/qutebrowser/greasemonkey/medium-to-scribe.js
@@ -0,0 +1,8 @@
+// ==UserScript==
+// @name medium to scribe
+// @match *://*.medium.com/*
+// @grant none
+// @run-at document-start
+// ==/UserScript==
+
+top.location.hostname = "scribe.rip";
diff --git a/.config/qutebrowser/greasemonkey/miniflux.js b/.config/qutebrowser/greasemonkey/miniflux.js
new file mode 100755
index 0000000..4b17b17
--- /dev/null
+++ b/.config/qutebrowser/greasemonkey/miniflux.js
@@ -0,0 +1,83 @@
+// ==UserScript==
+// @name custom miniflux
+// @match *://miniflux.rgoncalves.se/*NONE
+// @grant none
+// ==/UserScript==
+
+function get_img_content(content) {
+ var object = document.createElement("div");
+ object.innerHTML = content;
+
+ try {
+ url = object.getElementsByTagName("img")[0].src;
+ } catch (error) {
+ url = undefined;
+ }
+
+ return url;
+}
+
+function add_entry_images(article) {
+ var article_meta = article.getElementsByClassName("item-meta")[0];
+ var article_frame = document.createElement("div");
+ var article_id = article.getElementsByClassName("item-title")[0]
+ .getElementsByTagName("a")[0]
+ .href
+ .split("/")
+ .splice(-1, 1);
+
+ article_meta.appendChild(article_frame);
+
+ fetch(`/v1/entries/${article_id}`)
+ .then(res => res.json())
+ .then((res) => {
+ var url;
+
+ try {
+ url = res["enclosures"][0]["url"];
+ } catch (error) {
+ url = get_img_content(res["content"]);
+ }
+
+ if (typeof url === 'undefined') {
+ return;
+ }
+
+ console.log(url);
+
+ img = document.createElement("img");
+ img.src = url;
+ img.style.width = "100%";
+ img.style.minHeight = "100px";
+ img.loading = "lazy";
+
+ article_frame.appendChild(img);
+ })
+}
+
+function has_displayable_image(article) {
+ category = article.getElementsByClassName("category")[0]
+ .getElementsByTagName("a")[0]
+ .innerText;
+
+ return category === "blog"
+ || category === "hn"
+ || category === "social"
+ || category === "yt";
+}
+
+details = document.getElementsByTagName("details");
+
+for (let detail of details) {
+ detail.open = true;
+}
+
+articles = document.getElementsByTagName("article");
+
+for (let article of articles) {
+ if (! has_displayable_image(article)) {
+ continue;
+ }
+
+ add_entry_images(article);
+}
diff --git a/.config/qutebrowser/greasemonkey/minimal-css.js b/.config/qutebrowser/greasemonkey/minimal-css.js
new file mode 100755
index 0000000..1fe1414
--- /dev/null
+++ b/.config/qutebrowser/greasemonkey/minimal-css.js
@@ -0,0 +1,16 @@
+// ==UserScript==
+// @name minimal css
+// @match *://www.google.com
+// @match *://www.google.com/search*
+// @match *://cliq.zoho.eu/chats/*
+// @match *://www.pinterest.com/*
+// @grant none
+// ==/UserScript==
+
+GM_addStyle(`
+ * {
+ border-radius: 0px !important;
+ transition: none !important;
+ animation: none !important;
+ }
+`)
diff --git a/.config/qutebrowser/greasemonkey/no-sticky-headers.js b/.config/qutebrowser/greasemonkey/no-sticky-headers.js
new file mode 100755
index 0000000..e5eefa1
--- /dev/null
+++ b/.config/qutebrowser/greasemonkey/no-sticky-headers.js
@@ -0,0 +1,15 @@
+// ==UserScript==
+// @name no sticky header
+// @match *://*.substack.com/*
+// @grant none
+// ==/UserScript==
+
+(function () {
+ var i, elements = document.querySelectorAll('body *');
+
+ for (i = 0; i < elements.length; i++) {
+ if (getComputedStyle(elements[i]).position === 'fixed') {
+ elements[i].parentNode.removeChild(elements[i]);
+ }
+ }
+})();
diff --git a/.config/qutebrowser/greasemonkey/reddit-to-teddit.js b/.config/qutebrowser/greasemonkey/reddit-to-teddit.js
new file mode 100755
index 0000000..8c1ee6b
--- /dev/null
+++ b/.config/qutebrowser/greasemonkey/reddit-to-teddit.js
@@ -0,0 +1,9 @@
+// ==UserScript==
+// @name reddit to teddit
+// @match *://*.reddit.com/*
+// @match *://reddit.com/*
+// @grant none
+// @run-at document-start
+// ==/UserScript==
+
+top.location.hostname = "teddit.net";
diff --git a/.config/qutebrowser/greasemonkey/scaleway-css.js b/.config/qutebrowser/greasemonkey/scaleway-css.js
new file mode 100755
index 0000000..99955ca
--- /dev/null
+++ b/.config/qutebrowser/greasemonkey/scaleway-css.js
@@ -0,0 +1,11 @@
+// ==UserScript==
+// @name scaleway css
+// @match *://*.scaleway.com/*
+// @grant none
+// ==/UserScript==
+
+GM_addStyle(`
+ #outdated {
+ visibility: hidden;
+ }
+`)
diff --git a/.config/qutebrowser/greasemonkey/scribe-css.js b/.config/qutebrowser/greasemonkey/scribe-css.js
new file mode 100755
index 0000000..60fc0e2
--- /dev/null
+++ b/.config/qutebrowser/greasemonkey/scribe-css.js
@@ -0,0 +1,16 @@
+// ==UserScript==
+// @name scribe css
+// @match *://scribe.rip/*
+// @grant none
+// ==/UserScript==
+
+GM_addStyle(`
+ body {
+ background-color: #ffffff;
+ font-family: monospace, monospace;
+ }
+ img {
+ border: 1px solid #000000;
+ }
+`)
+
diff --git a/.config/qutebrowser/greasemonkey/stackoverflow-css.js b/.config/qutebrowser/greasemonkey/stackoverflow-css.js
new file mode 100755
index 0000000..6c98466
--- /dev/null
+++ b/.config/qutebrowser/greasemonkey/stackoverflow-css.js
@@ -0,0 +1,15 @@
+// ==UserScript==
+// @name stackoverflow css
+// @match *://*.stackoverflow.com/*
+// @match *://*.stackexchange.com/*
+// @match *://*.superuser.com/*
+// @match *://*.serverfault.com/*
+// @grant none
+// ==/UserScript==
+
+GM_addStyle(`
+ .js-consent-banner,
+ .js-freemium-cta {
+ visibility: hidden;
+ }
+`)
diff --git a/.config/qutebrowser/greasemonkey/twitter-to-nitter.js b/.config/qutebrowser/greasemonkey/twitter-to-nitter.js
new file mode 100755
index 0000000..f4c3518
--- /dev/null
+++ b/.config/qutebrowser/greasemonkey/twitter-to-nitter.js
@@ -0,0 +1,9 @@
+// ==UserScript==
+// @name twitter to nitter
+// @match *://*.twitter.com/*
+// @match *://twitter.com/*
+// @grant none
+// @run-at document-start
+// ==/UserScript==
+
+top.location.hostname = "nitter.net";
diff --git a/.config/qutebrowser/greasemonkey/youtube-to-yewtube.js b/.config/qutebrowser/greasemonkey/youtube-to-yewtube.js
new file mode 100755
index 0000000..c8f73f1
--- /dev/null
+++ b/.config/qutebrowser/greasemonkey/youtube-to-yewtube.js
@@ -0,0 +1,9 @@
+// ==UserScript==
+// @name youtube to yewtube
+// @match *://*.youtube.com/*
+// @match *://youtube.com/*
+// @grant none
+// @run-at document-start
+// ==/UserScript==
+
+top.location.hostname = "yewtu.be";
diff --git a/.config/sway/config b/.config/sway/config
new file mode 100755
index 0000000..1dd249e
--- /dev/null
+++ b/.config/sway/config
@@ -0,0 +1,166 @@
+
+# ~/.config/sway/config
+
+# variables
+set $mod Mod1
+set $left h
+set $down j
+set $up k
+set $right l
+set $term alacritty --config-file ~/.config/alacritty/hidpi.yml
+set $i3status ~/.config/common.d/i3status_ext.sh
+set $status ~/.bin/dwmstatus -l
+set $launcher ~/.config/common.d/bemenu.sh
+
+# input
+input * {
+ xkb_layout us
+ #xkb_variant altgr-intl
+ xkb_variant ,nodeadkeys
+ xkb_options compose:ralt
+ repeat_delay 300
+ repeat_rate 50
+}
+input "1739:30383:DELL07E6:00_06CB:76AF_Touchpad" {
+ tap enabled
+}
+
+# output
+output eDP-1 scale 2
+output * bg #000000 solid_color
+#output * bg ~/pictures/wallpaper/wallpaper_sway fill
+
+# workspaces
+set $ws1 "1"
+set $ws2 "2"
+set $ws3 "3"
+set $ws4 "4"
+set $ws5 "5"
+set $ws6 "6"
+
+# keybindings
+floating_modifier $mod normal
+bindsym $mod+Return exec $term
+bindsym $mod+Shift+q kill
+bindsym $mod+d exec "$launcher run"
+bindsym $mod+Shift+d exec "$launcher pass"
+bindsym $mod+Shift+s exec "$launcher colorscheme"
+bindsym $mod+Shift+r reload
+bindsym $mod+Shift+e exec swaynag -t warning -m "" -b "EXIT SWAY" "swaymsg exit"
+bindsym $mod+$left focus left
+bindsym $mod+$down focus down
+bindsym $mod+$up focus up
+bindsym $mod+$right focus right
+bindsym $mod+Shift+$left move left
+bindsym $mod+Shift+$down move down
+bindsym $mod+Shift+$up move up
+bindsym $mod+Shift+$right move right
+bindsym $mod+1 workspace $ws1
+bindsym $mod+2 workspace $ws2
+bindsym $mod+3 workspace $ws3
+bindsym $mod+4 workspace $ws4
+bindsym $mod+5 workspace $ws5
+bindsym $mod+6 workspace $ws6
+bindsym $mod+Shift+1 move container to workspace $ws1
+bindsym $mod+Shift+2 move container to workspace $ws2
+bindsym $mod+Shift+3 move container to workspace $ws3
+bindsym $mod+Shift+4 move container to workspace $ws4
+bindsym $mod+Shift+5 move container to workspace $ws5
+bindsym $mod+Shift+6 move container to workspace $ws6
+bindsym $mod+Tab workspace back_and_forth
+bindsym $mod+b splith
+bindsym $mod+v splitv
+bindsym $mod+s layout stacking
+bindsym $mod+w layout tabbed
+bindsym $mod+e layout toggle split
+bindsym $mod+f fullscreen
+bindsym $mod+Shift+space floating toggle
+bindsym $mod+space focus mode_toggle
+bindsym $mod+a focus parent
+bindsym $mod+Shift+minus move scratchpad
+bindsym $mod+minus scratchpad show
+bindsym $mod+r mode "resize"
+# keybidings mutlimeda keys
+bindsym XF86AudioMute exec "pamixer --toggle-mute"
+bindsym XF86AudioRaiseVolume exec "pamixer -i 5 -u"
+bindsym XF86AudioLowerVolume exec "pamixer -d 5 -u"
+bindsym XF86MonBrightnessUp exec "light -A 10"
+bindsym XF86MonBrightnessDown exec "light -U 10"
+bindsym XF86AudioPlay exec "~/bin/audio toggle"
+bindsym XF86AudioNext exec "~/.bin/audio next"
+bindsym XF86AudioPrev exec "~/.bin/audio prev"
+mode "resize" {
+ bindsym $left resize shrink width 10px
+ bindsym $down resize grow height 10px
+ bindsym $up resize shrink height 10px
+ bindsym $right resize grow width 10px
+ bindsym Return mode "default"
+ bindsym Escape mode "default"
+}
+
+# default workspaces
+assign [class="qutebrowser"] $ws2
+
+# default floating
+for_window [class="jetbrains-(.*)" title="Go to Line/Column"] floating enable
+for_window [class="jetbrains-(.*)" title="Rename"] floating enable
+for_window [class="jetbrains-(.*)" title="Open Project"] floating enable
+for_window [class="jetbrains-(.*)" title="win0"] floating enable
+for_window [title="win0"] floating enable
+for_window [app_id="waybar" floating] {
+ move position cursor
+ move down 50px # adjust if some menus still don't fit
+}
+
+# style
+gaps inner 0
+gaps outer 0
+font TerminessTTF Nerd Font 10
+default_border pixel 1
+titlebar_padding 1
+for_window [title="win0"] floating enable
+
+# colorscheme
+set $fg #22ff22
+set $bg #000000
+set $gr #222222
+set $wh #ffffff
+# class border backgr. text indicator child_border
+client.focused $wh $wh $bg $wh $wh
+client.focused_inactive $gr $gr $wh $gr $gr
+client.unfocused $gr $gr $wh $gr $gr
+client.urgent $bg $bg $fg $bg $bg
+client.placeholder $bg $bg $fg $bg $bg
+
+# i3bar
+bar {
+ status_command $status
+ position top
+ strip_workspace_numbers yes
+ font Terminus 10
+ colors {
+ statusline $wh
+ separator $wh
+ background $bg
+ # colorclass border background text
+ focused_workspace $wh $wh $bg
+ active_workspace $bg $bg $wh
+ inactive_workspace $bg $bg $wh
+ }
+}
+
+# auto-exec
+exec_always xrdb ~/.Xdefaults
+exec redshift
+exec libinput-gestures-setup start
+exec light -S 10
+exec swayidle -w before-sleep "$HOME/.bin/lock -s"
+
+# gtk-settings for wayland
+set $gnome-schema org.gnome.desktop.interface
+exec_always {
+ gsettings set $gnome-schema gtk-theme "Raleigh"
+ gsettings set $gnome-schema icon-theme "Adwaita"
+ gsettings set $gnome-schema cursor-theme "Adwaita"
+}
+
diff --git a/.config/tmux/tmux.conf b/.config/tmux/tmux.conf
new file mode 100755
index 0000000..81e5d4f
--- /dev/null
+++ b/.config/tmux/tmux.conf
@@ -0,0 +1,14 @@
+# mod key
+set-option -g prefix `
+set-option -g mouse off
+set-option -g mode-keys vi
+set-option -g escape-time 0
+# set-option -g default-terminal "screen-256color"
+set-option -g history-limit 5000
+set -s escape-time 0
+
+# keybindings
+bind-key ` last-window
+bind-key j next-window
+bind-key k previous-window
+bind-key e send-prefix
diff --git a/.config/user-dirs.dirs b/.config/user-dirs.dirs
new file mode 100755
index 0000000..0e54d8c
--- /dev/null
+++ b/.config/user-dirs.dirs
@@ -0,0 +1,15 @@
+# This file is written by xdg-user-dirs-update
+# If you want to change or add directories, just edit the line you're
+# interested in. All local changes will be retained on the next run.
+# Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped
+# homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an
+# absolute path. No other format is supported.
+#
+XDG_DOWNLOAD_DIR="$HOME/downloads"
+XDG_DOCUMENTS_DIR="$HOME/"
+XDG_MUSIC_DIR="$HOME/"
+XDG_PICTURES_DIR="$HOME/pictures"
+XDG_DESKTOP_DIR="$HOME/"
+XDG_TEMPLATES_DIR="$HOME/"
+XDG_PUBLICSHARE_DIR="$HOME/"
+XDG_VIDEOS_DIR="$HOME/"
diff --git a/.config/user-dirs.locale b/.config/user-dirs.locale
new file mode 100755
index 0000000..3e0b419
--- /dev/null
+++ b/.config/user-dirs.locale
@@ -0,0 +1 @@
+en_US \ No newline at end of file
diff --git a/.gnupg/gpg-agent.conf b/.gnupg/gpg-agent.conf
new file mode 100755
index 0000000..9d344b4
--- /dev/null
+++ b/.gnupg/gpg-agent.conf
@@ -0,0 +1,3 @@
+use-standard-socket
+enable-ssh-support
+pinentry-program /home/qwd/.bin/pinentry-common
diff --git a/.kshrc b/.kshrc
new file mode 100755
index 0000000..a4e5a8c
--- /dev/null
+++ b/.kshrc
@@ -0,0 +1,13 @@
+# ~/.kshrc
+# echo export ENV=$HOME/.kshrc >> ~/.profile
+
+[ -f ~/.cprofile ] && . ~/.cprofile
+
+HOST=$(hostname)
+PS1='${USER}@${HOST%%.*} $(pwd | sed "s,^$HOME,~,") $ '
+
+export PS1
+
+set -o vi
+set -o vi-tabcomplete
+
diff --git a/.public-keys/0xF85CD02DB419D68C b/.public-keys/0xF85CD02DB419D68C
new file mode 100755
index 0000000..eca02c8
--- /dev/null
+++ b/.public-keys/0xF85CD02DB419D68C
@@ -0,0 +1,109 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQINBF5zMOcBEADL3CTqwOUm6S9LlrBo72oOHn3RCpZH7VpogE/ynNID2lsajPG8
+Izsfuy2WuUOyS5rJaUwUa/eitR5fEAjJWeDaDXDuYZAE2J1ZwkFavXQAI01H7Efb
+jkPUGiIXtnVWt70uifPCugYyIW4LzTLai5bxE8mrjTXVNq/R36trg+BKVim5QXLZ
+mKytNjuUmQANZcgQpeNjW/nXrCsYRd0qpqdqVynEalPX2EZaFZGQYZ3rL+pXpR4e
+PH5SmuC4Q5+HoD/qzLjtG+k78PzZj3x+hjC3hm+FRH+CrovYZVQOLshX7FkAfCS0
+u8n1mRoYODfzrZbrT462aAEaZEKd+5JMa3NwHdxTNgWP7oeQqAKztMeKXE+iJ3B6
+q3J5pmJfywxGCyGk7MJUaIF+kFo0qDVdT/Vt/rcR7flfKPug0UdXhAt0Ttpl450/
+fe3+hs1N8Ajix8RKIU5Gcvx4RFH8BdkY8AXa9N3pgqSqUoKAeYnWmY0cOWeNqeV4
+xsisaq2WBgLoo4FGqWzM9B6b3dFbXof4HHR5yimmgKd9B48JqWAOBP5+SccRqOLq
+utV08rC8xEQ31IIHM1iIThqmVX1I5gwurcGiaE+wHqRc03zqKFIyIwi8lrfxF8If
+JRpeuxT3gS57cBz5Y4FIzZYjEn6Suj9AemnvEPPZ474tnDD5KpKqjE5FNQARAQAB
+tCFyb21haW4gKEhlbGxvKSA8bWVAcmdvbmNhbHZlcy5zZT6JAkwEEwEKADYWIQSM
+fW2vFPgUJkDOg+r4XNAttBnWjAUCXnMw5wIbAQQLCQgHBBUKCQgFFgIDAQACHgEC
+F4AACgkQ+FzQLbQZ1owjvA/9E7z9HoTQMOlBS5Tg1axYouwH4+XZ7QDr/H3/ccTY
+8HBaLpF5KAGcaV7C+WzbZf5zPoP4avcNFlir7ncm+8MJUDXp8XGSlFRWuQ/MLsVU
+or81jOHCl0CXSdS1iUe0tNt9Mge9QfB1W7+au3byOCXj3KK7yfOBCYNbubWwoZq4
+wyPOg1zhsi3dSbKgnclS+sDyyJdZv+dzG5ZLuBbiA5qWchVTdKfGIYdft5C/i9c5
+V3FkFlCEyeQ6gYCoN22IHvhoIBXmORbYzzQwzXWOf/0ViXRqrrbViQ1WgIP6blWP
+pboko3ZaExCCVLCZ0W58wK1xQafK5uURmbh4NhL6guaOq285r9IOIRTb5tadkkR0
+qlDz36F4Pr1tSiQ+RvQoRMm11Zo84pr6wCmXn+jRLc4gS1JopeYhdFeE/TFFl/yM
+J+S5T6KivUedWGB3jN52TC9F4acs3rVwcJHhxJtaE7zvWNnZh2ujEbbX41wk/g9/
+ZFJaC+2XLyw3Vgn78h7uzK1+3LrWi1e12qqeyITSviedDA3aFDhzbjlzI7wdjZhI
+2XWHcGX1gxQEqx5XzOCaP76lj1E6tGsAB0140nggRhoqGbdYR2Ufh34AVkoSvQEA
+j2A5aDWIc94neWfRyMHfuW2PFb2hFNtJSSajlxtEsg1/zKP8lRxw2IaHfeMpuNG1
+yyG5Ag0EXnMyQQEQAOJJ016xc91HLpJxd4WsK8BmCTLMmNZf4xfJOSaCCEC7Uh7r
+7fybIsFWJUf/fMf1seBZ0IB2mznaKp6fE78gU1s/wKjQl6yYxt2dmXvoOpCTCb1B
+Zqu2lN54h2+OvRyq/UPXxOCKVZfMqrc6C/eoyl4VorUfBJrhg9VVbciqIOnO11uJ
+UMQZQsYr7uvdFFJYyePI4FyjCytUPnnH38IIHlyL8YYf8mo3TJJGkohuAw/a87GY
+AIFpxk39obIuLbHpQAKSoQKlPCjEjGD/c9dSLmkl/16XnWyMuyIcv6Vw2ek2f0TL
+Bj5Jo26YXwU7+JJL4N3RgEAW6XLEa5Ylirpskt6qjHjlyNfxtnefrVMOL4k/hANQ
+MlBdnE+5oONUlXcuVgSsCGtuMsV2P8dI5Hw3Cr9YkFPzkpJhLLdwPcCLvZz5DM7C
+xrv6+TJEPqtVN3V9ZupRUprS+8E61XfdTZX1CYNytYTHGpb7qJ2wsvPST/GvZbui
+Aa6CrwJb7RkCkoaD0kf5iIDpCmtMZtESivqrbC7J1KONCXL/uK6G5aW/Vr8Gqnvl
+B5vZLb7GxJpYISkmGtegHAGOCd3ecGUJf8S3DLHyghskt2JKsUszaWqHmTsYYLQ4
+QB0ihz3ikoRMs9nd3k+CZwI8BMNKQE0l0rQCxlXhjCGwoibxNFA+vFq4kXcHABEB
+AAGJBGwEGAEKACAWIQSMfW2vFPgUJkDOg+r4XNAttBnWjAUCXnMyQQIbAgJACRD4
+XNAttBnWjMF0IAQZAQoAHRYhBNb92WprxEfuG/J00SQUnphu2qDFBQJeczJBAAoJ
+ECQUnphu2qDFARAQAK0Qs5KGWE1Ark8d0efTr8zQExebx8QJjsIXhdDxy/xJSt1l
+dMqabIV6gO0HQPYoxexHecpJPFZOiAm35Oh+6N2yPRfgoktNllw1Q066BTOqJ9/D
+XFrqCMV0nhuQSl7LSTQpc7qGxH7c92p8a1/wu5LbT3zELRUQm14WGyFX8X055+tm
+wd3YexWTI8ueIFjj4h7hMGZ0sgPffON+GBrqmY0doT1ZIu+2tgLFuU0OlcoAO8g5
+9wS2BDcBa/4Kyq5rnYE7ONnhVZgfxmoWQg4rugVZ6sXs88ew4lD6Gtxh6SizIrgS
+wfR2GbbbpIE7dcZOeFpPyogAKiBANWiUOYCRwe3ABL7lgMt6CETnF8r2sG1oYgng
+E5NebaBlXRkgMmoSYp1Lnhc2uQ6gVUYJh902c/ueGUPDaOEX4s48iyVk/nu7Bbco
+x432QPsZu/7Q5VtUvetnkbNu/aj+JM+snWkE/KfN704xIMk0V5IlwVty5uUDT0Na
+/8BphfJd8RcWOz4uPxsTixgoyovAHUqJ3t1Zy+M0Ow2iLKmw5W/f3sFtlU9n8T2F
+79RmrzYK2dTgXjxZZV/8jBXpqEXJQRmybmeOFKlOIRnBBmvFIWjCdFrbH8U+NB2o
+7Nrazoc0JRYi5ewaxE+3RQk2XwLHUvxbBqqqnPzfOkNKHonM7p4kxeB0/btyWzUP
+/2hM8Rn4tWljUOosMvlm2UCoUj8EJCENMXDFTUnzJob038/QfNNrXYDxwr3V4Fb1
+1TC1eh/2B1hgftNCzo0N8yXTCzyJkBLIp52G4bTHMuQcjpZeLWpf31IqLbCmeQ0P
+t1XuIXgO1DWmDAkd053OdydU5Nq7KyYtfuYIcctu/2zd5t+N236Xf5MO+8VrnMzy
+82pNgAMR07xqq/xfJeR/IJzDRPHOIrBO3FaJP0PkdmTt9NmMgMo7otI3h8LaWnON
+3IAWvXLWXq3F2r2mZNJ9/2kSzBu2f2iwwgqFZ7mNt4/ez+8joHlpXyVAH2nh4jKO
+Hf3RTW8ixLm+n6hFE6a8rbpO/2TPE5+ORIb8M3ZRfchXy62QZ8Gt4UJ7w1plpkZP
+9dwiXyOKz1NMfXH8JywnAE+nmibVsm7hdQ4OCob5nQ9jgmmDWihH1T0at4ajSpY+
+B25mzzGID4sCloh2g7vEnM15VaDWwQ6LOuLhk1868qrvLvhaua4a0NvmdcUbclM8
+Mo1r135urbWOGyDkAZbWaqT3/unbEIezZyN9NiTXEZvHwtKeYX82xfJC/cS0ctRh
+pAf4DJlIho1aei1RRZwmkIrPbLnRDgvmefTYWmVqTU9F+ITdy1JfaVBpE21JVVsQ
+++aZhTLUXcohH4rSAyk9Tl1m0XSL5TFDrxMkEFAM1IPNuQINBF5zMm4BEADTaaZq
++SmImeGBOoUSjO7uu+M/RgqTE52ic7IhanWLOIakMAIAaLY/xeSS1dX8V8tQW+f3
+I5/XFpihaSbBVdN+jm7oQwnYrx6YoxQYIALF63pyg578enG/GK6lqeJRJvJ+5P6F
+uIgI6pHkT/1mY1LYC+s30HlSMhka+dTtFhgMWx3EQjGQHoOuCxw9NV7IhNGVDn09
+7/43pKFtx5VVZ6zDRAzPcvUj3YEk4yfBG1xVsNKVeByz7cS6J6n6S9yVHfPvnlod
+IjCSDFzyF6clp9Z4hhZAM2eD4Rv3CWl6CqFU30ShmjuNwR9Eso0GrZV8dqD060in
+LPPlmyZ3v2PgkHxSJRerVt5T+yhC+oVntvhCs9rG3JpUqmFoxvTkHNohuEnSwGDg
+mrfiSmsVCKUwALamDthEfA10fEZXR49l4ASOtVHiwLiZvQYtngljBIr83i7rYKOx
+r4OKZ1DMmBRWQOeEV2nN7QDLNSazIPnGebLdwBdMUyw0fnrAj5ljm8iIO/azEsTG
+qnhbOzLgNXAj2OT1Gc9XnOXGFeQcT4R5XKLu5N1Kvlpm4gmajMK2m8Nq3ERfhhal
+J9utYm6O8HaVdvWOr1BqRe4leME82XrIs0DAQa3NWnY1sw0CRTdpbpdSdcVzkKgx
+4QMU0UOvLnc3G6EwXfBWtukAKObz70PWGq+t4wARAQABiQI2BBgBCgAgFiEEjH1t
+rxT4FCZAzoPq+FzQLbQZ1owFAl5zMm4CGwwACgkQ+FzQLbQZ1ozrRg//c2Sy6ZMw
+LEpOFv2th39dXNeMvnX1ntGIh0Fm99RSE5vDRK+Frzanx6RNz0OLv084MVe9byPY
+O4QTTTIBgVE2AQzc8Hf1uHh+1cVNMIodei09FLr3237tp4ju3ZbSOPLwH0lZS0bu
+asLrOeAMjFPwRl5oHzfQvWqzB/TofvDDCKAs4A8VXO5I3sOK63c65dWsHKC6B9UQ
+U/9YXHXVNa4kLFqk0xIuWB2UtX4ZghiQcyXXNRMKH5b3YoNbe4eKSsNeNbhn3EJd
+TAFHuyCfGQ29os92pWjFfer7qZrfKYYDK8UXYPPJTGR0NDnXm6U32CZgW6/Cs91z
+W1NZLdsauGcSoLqVKtx+aglkqmQmLxcXWToLIeOA+5KZy40sf0jEK1/sl/W7TTl2
+/BvIGwcVCWUlva/8hwmK12Vqdkkg1e4VvuMODSpnuMcWkY+YuVpX/aUboHPjmmQy
+9x0lMVpMyVMLM9NM3S5Zlj7n1WoMAQJSM4h7xAQP5yCFqE5ZX4R0dx6a9muZG8K5
+wAqRHAbhsgV8QncLdckc+ELDo4mLJMQAMMEl4WLH7vW3iGDhB0zwoDpQoDZxBM87
+dbqaKgfNUBziSQ0OH+mfZ+Rup15c0PMjJQVuJhj0di2dNpnXWeSt2W1/mDTQX2Q9
+ufFZ4BGp5KYAzX4V2fS5JVG/U00winAhaTW5Ag0EXnMyowEQALu3xBuds7ntcpxa
+gtONCWt/f6/t7z2H5UQBzG/+IdXQ8Pr40ID2UTz5MQlwFAMQWMKrOj0KQFrRYhQv
+eqTeJswasdVUfq0bdZ+fW8qmUFnMJtWjgvql3yj53cfIsrDYgOwgqBVdWCVsfrVv
+gM96tEq3PFhYG5hnvkVWnFZ5QqWMGBeHLNej54+o4LdEgszH/rg9YmHJT50nQLx8
+HsBUk74/ekeMl6VbgmQIDHa0YNu5o+Mi3KcLI0cXnaw1PKYviCcKHuD6wUxD6oQB
+JYbJLHThYGlEkTRe5CGUc/HkJ3eq8DJ+7sN4r5Z8IpJt+YhLF/dwoYiWqFZ79pYv
+woEmptUS+ri8JrLsMb+cX8MlNhWyInbqoE0JwREkHvGMREr/BoPTTO5NXLTcFSia
+FjCBMpVvPspxjW8zUof/lA5pJVVh+4N15V1f0hcAhmqbP/OpEyY5CHjqFcVka3Cu
+ARAVR9enENBU3aTrzgBSgLTkwiG2wuPMPKMj9ghPEQMKV/1KR96NVNpGV2X5otHp
+n2z7Zo2Tdpq7lWHLARIPf2iynANs48VlnIyj9ZI9wyfQ0/5FUYxaVbw5ptSRerYh
+JKHnkM0mybAwATzgswkMiTvkgh9mYuC427VXJiWtIC3Qvt0DSBREKE6hdm6UVNJp
+1kbcNYD8MRdwPPju5l/Qbja0w485ABEBAAGJAjYEGAEKACAWIQSMfW2vFPgUJkDO
+g+r4XNAttBnWjAUCXnMyowIbIAAKCRD4XNAttBnWjFtvEACgOyjGPtqfTH+lgttP
+2+fxdrBy7+RcHZvHYzfc48Td/KBP7Vdx38OHyou3PEFAkDVTIrJTs8bIW7C0jCSx
+Jvp2iroBYvyw7reMKJswqLsG0ne2wj0106gJ9Bo/L3fBVlhHM32tm5/cAv0I8M0d
+A2QvqyQeV54rhqD2Y+X35beaOCUYzORuVWzFMfmK3nlWBJJ6LRL1120+XPTDsvuO
+sIfd1YK33Ag2c77W5ybbVddBSw3E7v0SLuAHIv11qypUlkUjADkt3WNUNpEAO1jE
+DiTNxXk9qPLsr7k5D1rMJbKiXaR+Y7z73ykHmlhxJaL0JQJ0AF9CK1GuFAF7siYQ
+CwfYOoIggmGN6sjJyCT4XChjX1pByYNoBeAEF1RL4q0fgVJlFZFZRa/1G7JJxmgU
+tNd+nGxynftzYpdGtHfKcOpWP3kqaQqbMjfFQZpmeglNxVjDeNXQxevhcPCtoeEB
+FeodQ5W0Rpl3jucdMP3ld/T+Gxivx2Tl3ECvaw3EnFuCaepTcCcl5jKmTjywelo8
+UOjRJlSJ/mA1OwNJLz9vulCOHK5Now37FdeDW5K6KoVDi13RAO49zVPobEbmGaiF
+ZaSBZW7ax7Y0/VfAdR1sjWC3fnJOstlUNeb35FoQHsi4166LI4+U+pGcv5orV2iE
+jMkprlyrNuAcrXbn0AsZnS79DA==
+=2hJ5
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/.weechat/buflist.conf b/.weechat/buflist.conf
new file mode 100755
index 0000000..99bd851
--- /dev/null
+++ b/.weechat/buflist.conf
@@ -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}${name}${buffer.nick}"
+buffer_current = "${color:,blue}${format_buffer}"
+hotlist = " ${color:green}(${hotlist}${color:green})"
+hotlist_highlight = "${color:magenta}"
+hotlist_low = "${color:white}"
+hotlist_message = "${color:brown}"
+hotlist_none = "${color:default}"
+hotlist_private = "${color:green}"
+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})"
diff --git a/.weechat/fset.conf b/.weechat/fset.conf
new file mode 100755
index 0000000..3d7a814
--- /dev/null
+++ b/.weechat/fset.conf
@@ -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
diff --git a/.weechat/logger.conf b/.weechat/logger.conf
new file mode 100755
index 0000000..37c02b4
--- /dev/null
+++ b/.weechat/logger.conf
@@ -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 = 0
+backlog_conditions = ""
+
+[color]
+backlog_end = default
+backlog_line = default
+
+[file]
+auto_log = off
+color_lines = off
+flush_delay = 120
+fsync = off
+info_lines = off
+mask = "$plugin.$name.weechatlog"
+name_lower_case = on
+nick_prefix = ""
+nick_suffix = ""
+path = "${weechat_data_dir}/logs"
+replacement_char = "_"
+time_format = "%Y-%m-%d %H:%M:%S"
+
+[level]
+
+[mask]
diff --git a/.weechat/weechat.conf b/.weechat/weechat.conf
new file mode 100755
index 0000000..53e2776
--- /dev/null
+++ b/.weechat/weechat.conf
@@ -0,0 +1,704 @@
+#
+# 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:%S"
+buffer_time_same = ""
+color_basic_force_bold = off
+color_inactive_buffer = on
+color_inactive_message = on
+color_inactive_prefix = on
+color_inactive_prefix_buffer = on
+color_inactive_time = off
+color_inactive_window = on
+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 = ""
+highlight_regex = ""
+highlight_tags = ""
+hotlist_add_conditions = "${away} || ${buffer.num_displayed} == 0 || ${info:relay_client_count,weechat,connected} > 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 = on
+paste_bracketed_timer_delay = 10
+paste_max_lines = 1
+prefix_action = " *"
+prefix_align = right
+prefix_align_max = 0
+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 = off
+read_marker_string = "- "
+read_marker_update_on_buffer_switch = on
+save_config_on_exit = on
+save_config_with_fsync = off
+save_layout_on_exit = none
+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 = lightmagenta
+chat = default
+chat_bg = default
+chat_buffer = white
+chat_channel = white
+chat_day_change = cyan
+chat_delimiters = green
+chat_highlight = yellow
+chat_highlight_bg = magenta
+chat_host = cyan
+chat_inactive_buffer = default
+chat_inactive_window = default
+chat_nick = lightcyan
+chat_nick_colors = "cyan,magenta,green,brown,lightblue,default,lightcyan,lightmagenta,lightgreen,blue"
+chat_nick_offline = default
+chat_nick_offline_highlight = default
+chat_nick_offline_highlight_bg = blue
+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 = default
+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 = magenta
+chat_read_marker_bg = default
+chat_server = brown
+chat_tags = red
+chat_text_found = yellow
+chat_text_found_bg = lightmagenta
+chat_time = default
+chat_time_delimiters = brown
+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 = blue
+status_count_highlight = magenta
+status_count_msg = brown
+status_count_other = default
+status_count_private = green
+status_data_highlight = lightmagenta
+status_data_msg = yellow
+status_data_other = default
+status_data_private = lightgreen
+status_filter = green
+status_more = yellow
+status_mouse = green
+status_name = white
+status_name_ssl = lightgreen
+status_nicklist_count = default
+status_number = yellow
+status_time = default
+
+[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,.dll"
+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]
+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 = 0
+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 = cyan
+input.color_fg = default
+input.conditions = ""
+input.filling_left_right = vertical
+input.filling_top_bottom = horizontal
+input.hidden = off
+input.items = "[mode_indicator+][input_prompt]+(away),[input_search],[input_paste],input_text,[vi_buffer]"
+input.position = bottom
+input.priority = 1000
+input.separator = off
+input.size = 0
+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 = off
+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 = blue
+status.color_bg_inactive = darkgray
+status.color_delim = cyan
+status.color_fg = default
+status.conditions = ""
+status.filling_left_right = vertical
+status.filling_top_bottom = horizontal
+status.hidden = off
+status.items = "[time],[buffer_last_number],[buffer_plugin],buffer_number+:+buffer_name+(buffer_modes)+{buffer_nicklist_count}+buffer_zoom+buffer_filter,scroll,[lag],[hotlist],[matrix_typing_notice],completion"
+status.position = bottom
+status.priority = 500
+status.separator = off
+status.size = 1
+status.size_max = 0
+status.type = window
+title.color_bg = blue
+title.color_bg_inactive = darkgray
+title.color_delim = cyan
+title.color_fg = default
+title.conditions = ""
+title.filling_left_right = vertical
+title.filling_top_bottom = horizontal
+title.hidden = off
+title.items = "buffer_title"
+title.position = top
+title.priority = 500
+title.separator = off
+title.size = 1
+title.size_max = 0
+title.type = window
+vi_line_numbers.color_bg = default
+vi_line_numbers.color_bg_inactive = default
+vi_line_numbers.color_delim = default
+vi_line_numbers.color_fg = default
+vi_line_numbers.conditions = ""
+vi_line_numbers.filling_left_right = vertical
+vi_line_numbers.filling_top_bottom = vertical
+vi_line_numbers.hidden = on
+vi_line_numbers.items = "line_numbers"
+vi_line_numbers.position = left
+vi_line_numbers.priority = 0
+vi_line_numbers.separator = off
+vi_line_numbers.size = 0
+vi_line_numbers.size_max = 0
+vi_line_numbers.type = window
+
+[layout]
+
+[notify]
+
+[filter]
+topic = on;*;irc_topic;*
+irc_smart = on;*,!irc.undernet.*;irc_smart_filter;*
+joinquit = on;*;irc_join,irc_part,irc_quit;*
+irc_join_topic_date_names_creation = on;*;irc_332,irc_333,irc_366,irc_329,irc_372;*
+backlog_in = on;*;<--;*
+backlogtidy = on;*;logger_backlog;^\S+ \(\S+\) has (joined|left|quit) |^\S+ is now known as \S+$
+
+[key]
+ctrl-? = "/input delete_previous_char"
+ctrl-A = "/input move_beginning_of_line"
+ctrl-B = "/input move_previous_char"
+ctrl-C_ = "/input insert \x1F"
+ctrl-Cb = "/input insert \x02"
+ctrl-Cc = "/input insert \x03"
+ctrl-Ci = "/input insert \x1D"
+ctrl-Co = "/input insert \x0F"
+ctrl-Cv = "/input insert \x16"
+ctrl-D = "/input delete_next_char"
+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 return"
+ctrl-K = "/input delete_end_of_line"
+ctrl-L = "/window refresh"
+ctrl-M = "/input return"
+ctrl-N = "/buffer +1"
+ctrl-P = "/buffer -1"
+ctrl-R = "/input search_text_here"
+ctrl-Sctrl-U = "/input set_unread"
+ctrl-T = "/input transpose_chars"
+ctrl-U = "/input delete_beginning_of_line"
+ctrl-W = "/input delete_previous_word"
+ctrl-W= = "/window balance"
+ctrl-Wh = "/window left"
+ctrl-Wj = "/window down"
+ctrl-Wk = "/window up"
+ctrl-Wl = "/window right"
+ctrl-Wq = "/window merge"
+ctrl-Ws = "/window splith"
+ctrl-Wv = "/window splitv"
+ctrl-Wx = "/window swap"
+ctrl-X = "/input switch_active_buffer"
+ctrl-Y = "/input clipboard_paste"
+meta-ctrl-M = "/input insert \n"
+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 * b"
+meta-meta2-24~ = "/bar scroll nicklist * e"
+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 -1"
+meta-meta2-B = "/buffer +1"
+meta-meta2-C = "/buffer +1"
+meta-meta2-D = "/buffer -1"
+meta-- = "/filter toggle @"
+meta-/ = "/input jump_last_buffer_displayed"
+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-N = "/bar toggle nicklist"
+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~ = "/buffer -1"
+meta2-17~ = "/buffer +1"
+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;3F = "/window scroll_bottom"
+meta2-1;3H = "/window scroll_top"
+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;5C = "/input move_next_word"
+meta2-1;5D = "/input move_previous_word"
+meta2-1;5P = "/bar scroll buflist * -100%"
+meta2-1;5Q = "/bar scroll buflist * +100%"
+meta2-1~ = "/input move_beginning_of_line"
+meta2-200~ = "/input paste_start"
+meta2-201~ = "/input paste_stop"
+meta2-20~ = "/bar scroll title * -30%"
+meta2-21~ = "/bar scroll title * +30%"
+meta2-23;3~ = "/bar scroll nicklist * b"
+meta2-23;5~ = "/bar scroll nicklist * -100%"
+meta2-23^ = "/bar scroll nicklist * -100%"
+meta2-23~ = "/bar scroll nicklist * -100%"
+meta2-24;3~ = "/bar scroll nicklist * e"
+meta2-24;5~ = "/bar scroll nicklist * +100%"
+meta2-24^ = "/bar scroll nicklist * +100%"
+meta2-24~ = "/bar scroll nicklist * +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"
+meta2-[E = "/buffer -1"
+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-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-l = "/window bare"
+meta-m = "/mute mouse toggle"
+meta-n = "/window scroll_next_highlight"
+meta-p = "/window scroll_previous_highlight"
+meta-r = "/input delete_line"
+meta-s = "/mute spell toggle"
+meta-u = "/window scroll_unread"
+meta-x = "/input zoom_merged_buffer"
+meta-z = "/window zoom"
+ctrl-^ = "/input jump_last_buffer_displayed"
+ctrl-_ = "/input undo"
+
+[key_search]
+ctrl-I = "/input search_switch_where"
+ctrl-J = "/input search_stop_here"
+ctrl-M = "/input search_stop_here"
+ctrl-Q = "/input search_stop"
+ctrl-R = "/input search_switch_regex"
+meta2-A = "/input search_previous"
+meta2-B = "/input search_next"
+meta-c = "/input search_switch_case"
+
+[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.matrix.*):r = "hsignal:matrix_cursor_reply"
+@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(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(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:ctrl-wheeldown = "/window scroll_horiz -window ${_window_number} +10%"
+@chat:ctrl-wheelup = "/window scroll_horiz -window ${_window_number} -10%"
+@chat:wheeldown = "/window scroll_down -window ${_window_number}"
+@chat:wheelup = "/window scroll_up -window ${_window_number}"
+@*:button3 = "/cursor go ${_x},${_y}"
diff --git a/.xinitrc.dwm b/.xinitrc.dwm
new file mode 100755
index 0000000..04ea96f
--- /dev/null
+++ b/.xinitrc.dwm
@@ -0,0 +1,2 @@
+xrdb -merge ~/.Xresources
+exec dbus-run-session -- ~/.bin/dwm-start
diff --git a/.zshrc b/.zshrc
new file mode 100755
index 0000000..f30f562
--- /dev/null
+++ b/.zshrc
@@ -0,0 +1,62 @@
+# ~/.zshrc configuration ~~ rgoncalves.se
+
+. ~/.cprofile
+
+set -o vi
+
+precmd() {
+ vcs_info
+}
+
+# keybinds
+autoload -U history-search-end
+zle -N history-beginning-search-backward-end history-search-end
+zle -N history-beginning-search-forward-end history-search-end
+bindkey "^[[A" history-beginning-search-backward-end
+bindkey "^[[B" history-beginning-search-forward-end
+bindkey "^[[1;5C" forward-word
+bindkey "^[[1;5D" backward-word
+bindkey "\[[H" beginning-of-line
+bindkey "\[[F" end-of-line
+bindkey "\e[3~" delete-char
+bindkey '^[[Z' reverse-menu-complete
+zstyle ':completion:*' completer _complete
+zstyle ':completion:*' matcher-list '' 'm:{[:lower:][:upper:]}={[:upper:][:lower:]}' '+l:|=* r:|=*'
+autoload -Uz compinit
+compinit -u
+
+# history
+HISTSIZE=1000
+HISTFILE=~/.zsh_history
+SAVEHIST=1000
+HISTDUP=erase
+setopt INC_APPEND_HISTORY
+setopt HIST_IGNORE_ALL_DUPS
+
+# prompt
+setopt PROMPT_SUBST
+_hostname="@${HOST}"
+_user="${USER}"
+_separator=" "
+_prompt="%%"
+_dir="%~"
+_color=002
+
+autoload -Uz vcs_info
+zstyle ':vcs_info:*' enable git svn
+zstyle ':vcs_info:*' enable git
+zstyle ':vcs_info:*' get-revision true
+zstyle ':vcs_info:*' check-for-changes true
+zstyle ':vcs_info:*' stagedstr "●"
+zstyle ':vcs_info:*' unstagedstr "+"
+zstyle ":completion:*" menu select
+zstyle ':vcs_info:git*' formats "%F{$_color} ▒ %b %u%c"
+vcs_info
+
+PROMPT="${_user}%F{$_color}${_hostname}:"
+PROMPT="${PROMPT}%F{015}${_dir}"
+PROMPT="${PROMPT}${_separator}${_prompt}${_separator}%f"
+RPROMPT='${vcs_info_msg_0_}'
+
+(cat ~/.cache/dot/sequences 2>/dev/null &)
+# clear
remember that computers suck.