aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--files/dmenu/note.txt1
-rw-r--r--files/dunst/dunstifyIDs9
-rw-r--r--files/dunst/path.txt1
-rw-r--r--files/i3/path.txt1
-rw-r--r--files/i3blocks/path.txt1
-rw-r--r--files/mpv/path.txt1
-rw-r--r--files/pqiv/.pqivrc20
-rw-r--r--files/pqiv/path.txt1
-rw-r--r--files/qutebrowser/path.txt1
-rw-r--r--files/ranger/path.txt1
-rw-r--r--files/rcs/.xinitrcgnome3
-rw-r--r--files/sxiv/path.txt3
-rw-r--r--files/vim/ftplugin/python/python.vim16
-rw-r--r--files/vim/ftplugin/sent/sent.vim9
-rw-r--r--files/zathura/path.txt1
-rw-r--r--headers/dmenu/config.h (renamed from files/dmenu/config.h)0
-rw-r--r--headers/sxiv/config.h (renamed from files/sxiv/config.h)0
-rwxr-xr-xsetup.sh81
-rw-r--r--stow/XserverDotfiles/dot-xinitrcbspwm1
-rw-r--r--stow/XserverDotfiles/dot-xinitrcdwm2
-rw-r--r--stow/XserverDotfiles/dot-xinitrcgnome3
-rw-r--r--stow/XserverDotfiles/dot-xinitrci3 (renamed from files/rcs/.xinitrci3)0
-rw-r--r--stow/XserverDotfiles/dot-xinitrcpostintel0
-rw-r--r--stow/XserverDotfiles/dot-xinitrcpostnvidia2
-rw-r--r--stow/XserverDotfiles/dot-xinitrcprevintel0
-rw-r--r--stow/XserverDotfiles/dot-xinitrcprevnvidia (renamed from files/rcs/.xinitrcxfce4)2
-rw-r--r--stow/XserverDotfiles/dot-xinitrcxfce41
-rwxr-xr-xstow/bspwm/dot-config/bspwm/bspwmrc28
-rw-r--r--stow/cmus/dot-config/cmus/autosave217
-rw-r--r--stow/colorschemes/dot-config/colorschemes/customBlue29
-rw-r--r--stow/colorschemes/dot-config/colorschemes/gruvbox31
-rw-r--r--stow/colorschemes/dot-config/colorschemes/nord47
-rw-r--r--stow/dunst/dot-config/dunst/dunstifyIDs12
-rw-r--r--stow/dunst/dot-config/dunst/dunstrc (renamed from files/dunst/dunstrc)7
-rw-r--r--stow/emoji/dot-config/emoji (renamed from files/emoji)0
-rw-r--r--stow/i3/dot-config/i3/config (renamed from files/i3/config)6
-rw-r--r--stow/i3blocks/dot-config/i3blocks/config (renamed from files/i3blocks/config)0
-rw-r--r--stow/mpv/dot-config/mpv/input.conf (renamed from files/mpv/input.conf)0
-rw-r--r--stow/mpv/dot-config/mpv/mpv.conf (renamed from files/mpv/mpv.conf)0
-rw-r--r--stow/mpv/dot-config/mpv/scripts-opts/mpv_thumbnail_script.conf70
-rw-r--r--stow/mpv/dot-config/mpv/scripts-opts/youtube-quality.conf41
-rw-r--r--stow/mpv/dot-config/mpv/scripts/mpv-splice.lua328
-rw-r--r--stow/mpv/dot-config/mpv/scripts/mpv_thumbnail_script_client_osc.lua3886
-rw-r--r--stow/mpv/dot-config/mpv/scripts/mpv_thumbnail_script_server.lua736
-rw-r--r--stow/mpv/dot-config/mpv/scripts/youtube-quality.lua275
-rw-r--r--stow/neofetch/dot-config/neofetch/config.conf764
-rw-r--r--stow/neofetch/dot-config/neofetch/neofetchLogin.conf (renamed from files/neofetch/neofetchLogin.conf)0
-rw-r--r--stow/newsboat/dot-config/newsboat/config11
-rw-r--r--stow/nvim/dot-config/nvim/init.vim3
-rw-r--r--stow/pandoc/dot-config/pandoc/luascripts/diagram-generator.lua299
-rw-r--r--stow/picom/dot-config/picom/picom.conf502
-rw-r--r--stow/picom/dot-config/picom/picom.jonaburg.sample.conf512
-rw-r--r--stow/picom/dot-config/picom/picom.upstream.conf (renamed from files/picom/picom.conf)0
-rw-r--r--stow/qutebrowser/dot-config/qutebrowser/config.py (renamed from files/qutebrowser/config.py)68
-rw-r--r--stow/ranger/dot-config/ranger/rc.conf (renamed from files/ranger/rc.conf)0
-rw-r--r--stow/ranger/dot-config/ranger/rifle.conf (renamed from files/ranger/rifle.conf)0
-rwxr-xr-xstow/ranger/dot-config/ranger/scope.sh (renamed from files/ranger/scope.sh)0
-rw-r--r--stow/rcs/dot-Xresources (renamed from files/rcs/.Xresources)37
-rw-r--r--stow/rcs/dot-bash_aliases (renamed from files/rcs/.bash_aliases)10
-rw-r--r--stow/rcs/dot-bash_vars (renamed from files/rcs/.bash_vars)12
-rw-r--r--stow/rcs/dot-bashrc (renamed from files/rcs/.bashrc)1
-rw-r--r--stow/rcs/dot-config/ttymaps.kmap (renamed from files/rcs/ttymaps.kmap)0
-rw-r--r--stow/rcs/dot-inputrc (renamed from files/rcs/.inputrc)1
-rw-r--r--stow/rcs/dot-profile (renamed from files/rcs/.profile)32
-rw-r--r--stow/sxhkd/dot-config/sxhkd/sxhkdrc (renamed from files/sxhkd/sxhkdrc)33
-rw-r--r--stow/sxhkd/dot-config/sxhkd/sxhkdrcbspc92
-rwxr-xr-xstow/sxiv/dot-config/sxiv/exec/image-info (renamed from files/sxiv/image-info)0
-rwxr-xr-xstow/sxiv/dot-config/sxiv/exec/key-handler (renamed from files/sxiv/key-handler)13
-rw-r--r--stow/tmux/dot-tmux.conf (renamed from files/rcs/.tmux.conf)1
-rw-r--r--stow/vim/dot-vim/autoload/plug.vim2788
-rw-r--r--stow/vim/dot-vim/autoload/plug.vim.old2788
-rw-r--r--stow/vim/dot-vim/colors/burnttoast256.vim116
-rw-r--r--stow/vim/dot-vim/colors/nord.vim764
-rw-r--r--stow/vim/dot-vim/ftplugin/antlr/antlr3.vim (renamed from files/vim/ftplugin/antlr/antlr3.vim)0
-rw-r--r--stow/vim/dot-vim/ftplugin/antlr/antlr4.vim (renamed from files/vim/ftplugin/antlr/antlr4.vim)0
-rw-r--r--stow/vim/dot-vim/ftplugin/asciidoc/asciidoc.vim (renamed from files/vim/ftplugin/asciidoc/asciidoc.vim)0
-rw-r--r--stow/vim/dot-vim/ftplugin/cpp/cpp.vim (renamed from files/vim/ftplugin/cpp/cpp.vim)0
-rw-r--r--stow/vim/dot-vim/ftplugin/cpp/snips/template.txt (renamed from files/vim/ftplugin/cpp/snips/template.txt)0
-rw-r--r--stow/vim/dot-vim/ftplugin/css/css.vim (renamed from files/vim/ftplugin/css/css.vim)0
-rw-r--r--stow/vim/dot-vim/ftplugin/html/html.vim (renamed from files/vim/ftplugin/html/html.vim)0
-rw-r--r--stow/vim/dot-vim/ftplugin/html/snips/template.txt (renamed from files/vim/ftplugin/html/snips/template.txt)0
-rw-r--r--stow/vim/dot-vim/ftplugin/java/java.vim (renamed from files/vim/ftplugin/java/java.vim)0
-rw-r--r--stow/vim/dot-vim/ftplugin/javascript/javascript.vim (renamed from files/vim/ftplugin/javascript/javascript.vim)0
-rw-r--r--stow/vim/dot-vim/ftplugin/plantuml/plantuml.vim (renamed from files/vim/ftplugin/plantuml/plantuml.vim)1
-rw-r--r--stow/vim/dot-vim/ftplugin/plantuml/snips/template.txt (renamed from files/vim/ftplugin/plantuml/snips/template.txt)0
-rw-r--r--stow/vim/dot-vim/ftplugin/python/python.vim34
-rw-r--r--stow/vim/dot-vim/ftplugin/python/snips/class3
-rw-r--r--stow/vim/dot-vim/ftplugin/python/snips/template5
-rw-r--r--stow/vim/dot-vim/ftplugin/sent/sent.vim19
-rw-r--r--stow/vim/dot-vim/ftplugin/sh/sh.vim (renamed from files/vim/ftplugin/sh/sh.vim)0
-rw-r--r--stow/vim/dot-vim/ftplugin/sh/snippets/if.txt (renamed from files/vim/ftplugin/sh/snippets/if.txt)0
-rw-r--r--stow/vim/dot-vim/ftplugin/tex/snips/template.txt (renamed from files/vim/ftplugin/tex/snips/template.txt)0
-rw-r--r--stow/vim/dot-vim/ftplugin/tex/tex.vim (renamed from files/vim/ftplugin/tex/tex.vim)15
-rw-r--r--stow/vim/dot-vim/ftplugin/text/text.vim (renamed from files/vim/ftplugin/text/text.vim)2
-rw-r--r--stow/vim/dot-vim/ftplugin/typescript/typescript.vim (renamed from files/vim/ftplugin/typescript/typescript.vim)0
-rw-r--r--stow/vim/dot-vim/ftplugin/vimwiki/snips/plantUml.txt (renamed from files/vim/ftplugin/vimwiki/snips/plantUml.txt)0
-rw-r--r--stow/vim/dot-vim/ftplugin/vimwiki/snips/template.txt (renamed from files/vim/ftplugin/vimwiki/snips/template.txt)0
-rw-r--r--stow/vim/dot-vim/ftplugin/vimwiki/vimwiki.vim (renamed from files/vim/ftplugin/vimwiki/vimwiki.vim)8
-rw-r--r--stow/vim/dot-vim/plugin/SWTC.vim (renamed from files/vim/plugin/SWTC.vim)0
-rw-r--r--stow/vim/dot-vim/plugin/dragvisuals.vim (renamed from files/vim/plugin/dragvisuals.vim)0
-rw-r--r--stow/vim/dot-vim/plugin/rng.vim (renamed from files/vim/plugin/rng.vim)0
-rw-r--r--stow/vim/dot-vimrc (renamed from files/vim/.vimrc)51
-rw-r--r--stow/vis/dot-config/vis/colors/basic_colors15
-rw-r--r--stow/vis/dot-config/vis/colors/rainbow30
-rw-r--r--stow/vis/dot-config/vis/config91
-rw-r--r--stow/zathura/dot-config/zathura/zathurarc (renamed from files/zathura/zathurarc)9
106 files changed, 14738 insertions, 266 deletions
diff --git a/files/dmenu/note.txt b/files/dmenu/note.txt
deleted file mode 100644
index 536830f..0000000
--- a/files/dmenu/note.txt
+++ /dev/null
@@ -1 +0,0 @@
-Remove config.h before compiling so it gets generated based on config.def.h
diff --git a/files/dunst/dunstifyIDs b/files/dunst/dunstifyIDs
deleted file mode 100644
index 03850b4..0000000
--- a/files/dunst/dunstifyIDs
+++ /dev/null
@@ -1,9 +0,0 @@
-vol 1001
-musicMode 1002
-brightness 1003
-cpu 1004
-memory 1005
-disk 1006
-batteryLow 1007
-moon 1008
-calcurse 1009
diff --git a/files/dunst/path.txt b/files/dunst/path.txt
deleted file mode 100644
index 7f7316a..0000000
--- a/files/dunst/path.txt
+++ /dev/null
@@ -1 +0,0 @@
-~/.config/dunst/dunstrc
diff --git a/files/i3/path.txt b/files/i3/path.txt
deleted file mode 100644
index 74c30ea..0000000
--- a/files/i3/path.txt
+++ /dev/null
@@ -1 +0,0 @@
-~/.config/i3/config
diff --git a/files/i3blocks/path.txt b/files/i3blocks/path.txt
deleted file mode 100644
index 7457366..0000000
--- a/files/i3blocks/path.txt
+++ /dev/null
@@ -1 +0,0 @@
-~/.config/i3blocks/config
diff --git a/files/mpv/path.txt b/files/mpv/path.txt
deleted file mode 100644
index 98bbc7e..0000000
--- a/files/mpv/path.txt
+++ /dev/null
@@ -1 +0,0 @@
-~/.config/mpv/input.conf
diff --git a/files/pqiv/.pqivrc b/files/pqiv/.pqivrc
deleted file mode 100644
index 0d6981e..0000000
--- a/files/pqiv/.pqivrc
+++ /dev/null
@@ -1,20 +0,0 @@
-[options]
-command-1=|convert - -blur 80 -
-
-[keybindings]
-j { shift_y(-5); }
-k { shift_y(5); }
-h { shift_x(5); }
-l { shift_x(-5); }
-J { shift_y(-40); }
-K { shift_y(40); }
-H { shift_x(40); }
-L { shift_x(-40); }
-<control>j { shift_y(-200); }
-<control>k { shift_y(200); }
-<control>h { shift_x(200); }
-<control>l { shift_x(-200); }
-w { rotate_left(); }
-e { rotate_right(); }
-g { jump_dialog(); }
-<equal> { reset_scale_level() }
diff --git a/files/pqiv/path.txt b/files/pqiv/path.txt
deleted file mode 100644
index 3dd864f..0000000
--- a/files/pqiv/path.txt
+++ /dev/null
@@ -1 +0,0 @@
-~/.pqivrc
diff --git a/files/qutebrowser/path.txt b/files/qutebrowser/path.txt
deleted file mode 100644
index 3b67556..0000000
--- a/files/qutebrowser/path.txt
+++ /dev/null
@@ -1 +0,0 @@
-~/.config/qutebrowser/config.py
diff --git a/files/ranger/path.txt b/files/ranger/path.txt
deleted file mode 100644
index 6ea46fa..0000000
--- a/files/ranger/path.txt
+++ /dev/null
@@ -1 +0,0 @@
-~/.config/ranger/
diff --git a/files/rcs/.xinitrcgnome b/files/rcs/.xinitrcgnome
deleted file mode 100644
index 727aa2b..0000000
--- a/files/rcs/.xinitrcgnome
+++ /dev/null
@@ -1,3 +0,0 @@
-export XDG_CURRENT_DESKTOP=GNOME-Classic:GNOME
-export GNOME_SHELL_SESSION_MODE=classic
-exec gnome-session --session=gnome-classic
diff --git a/files/sxiv/path.txt b/files/sxiv/path.txt
deleted file mode 100644
index 6b97c7f..0000000
--- a/files/sxiv/path.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-image-info path: ~/.config/sxiv/exec/image-info
-key-handler path: ~/.config/sxiv/exec/key-handler
-config.h path: sxiv source code folder
diff --git a/files/vim/ftplugin/python/python.vim b/files/vim/ftplugin/python/python.vim
deleted file mode 100644
index 7246eed..0000000
--- a/files/vim/ftplugin/python/python.vim
+++ /dev/null
@@ -1,16 +0,0 @@
-" ~/.vim/ftplugin/python/python.vim
-" Python-specific vim configuration
-
-" Column marker: 80 characters
-call matchadd('ColorColumn', '\%81v', 100)
-"let b:syntastic_mode="passive"
-
-nnoremap <buffer> <leader>C :sp ~/.vim/ftplugin/python/python.vim<CR>
-nnoremap <buffer> <leader>E :Errors<CR>
-
-nnoremap <buffer> <leader>gt :YcmCompleter GoTo<CR>
-nnoremap <buffer> <leader>gr :YcmCompleter GoToReferences<CR>
-nnoremap <buffer> <leader>gd :YcmCompleter GetDoc<CR>
-nnoremap <buffer> <leader>gT :YcmCompleter GetType<CR>
-nnoremap <buffer> <leader>fi :YcmCompleter FixIt<CR>
-nnoremap <buffer> <leader>e :YcmDiags<CR>
diff --git a/files/vim/ftplugin/sent/sent.vim b/files/vim/ftplugin/sent/sent.vim
deleted file mode 100644
index c5813d5..0000000
--- a/files/vim/ftplugin/sent/sent.vim
+++ /dev/null
@@ -1,9 +0,0 @@
-" ~/.vim/ftplugin/sent/sent.vim
-" Vim configuration for writing sent presentations
-
-call matchadd('ColorColumn', '\%21v')
-setlocal textwidth=20
-
-setlocal spell
-
-nnoremap ,r :w<CR>:!sent % & <CR><CR>
diff --git a/files/zathura/path.txt b/files/zathura/path.txt
deleted file mode 100644
index aad3d2c..0000000
--- a/files/zathura/path.txt
+++ /dev/null
@@ -1 +0,0 @@
-~/.config/zathura/zathurarc
diff --git a/files/dmenu/config.h b/headers/dmenu/config.h
index c7c17f2..c7c17f2 100644
--- a/files/dmenu/config.h
+++ b/headers/dmenu/config.h
diff --git a/files/sxiv/config.h b/headers/sxiv/config.h
index 10049ea..10049ea 100644
--- a/files/sxiv/config.h
+++ b/headers/sxiv/config.h
diff --git a/setup.sh b/setup.sh
deleted file mode 100755
index 6605f72..0000000
--- a/setup.sh
+++ /dev/null
@@ -1,81 +0,0 @@
-#!/bin/sh
-
-# Setup all config files in their place
-
-mkdir -p "$HOME/.config"
-
-# keymap
-cp "$HOME/repos/configs/files/rcs/ttymaps.kmap" "$HOME/.config/ttymaps.kmap"
-
-# sxhkd
-mkdir -p "$HOME/.config/sxhkd"
-cp "$HOME/repos/configs/files/sxhkd/sxhkdrc" "$HOME/.config/sxhkd/sxhkdrc"
-
-# i3
-mkdir -p "$HOME/.config/i3"
-cp "$HOME/repos/configs/files/i3/config" "$HOME/.config/i3/config"
-
-# i3blocks
-mkdir -p "$HOME/.config/i3blocks"
-cp "$HOME/repos/configs/files/i3blocks/config" "$HOME/.config/i3blocks/config"
-
-# picom
-mkdir -p "$HOME/.config/picom"
-cp "$HOME/repos/configs/files/picom/picom.conf" "$HOME/.config/picom/picom.conf"
-
-# dmenu
-mkdir -p "$HOME/repos/dmenu"
-cp "$HOME/repos/configs/files/dmenu/config.h" "$HOME/repos/dmenu/config.h"
-
-# dunst
-mkdir -p "$HOME/.config/dunst"
-cp "$HOME/repos/configs/files/dunst/dunstrc" "$HOME/.config/dunst/dunstrc"
-cp "$HOME/repos/configs/files/dunst/dunstifyIDs" "$HOME/.config/dunst/dunstifyIDs"
-
-# mpv
-mkdir -p "$HOME/.config/mpv"
-cp "$HOME/repos/configs/files/mpv/input.conf" "$HOME/.config/mpv/input.conf"
-cp "$HOME/repos/configs/files/mpv/mpv.conf" "$HOME/.config/mpv/mpv.conf"
-
-# neofetch
-mkdir -p "$HOME/.config/neofetch"
-cp "$HOME/repos/configs/files/neofetch/neofetchLogin.conf" "$HOME/.config/neofetch/neofetchLogin.conf"
-
-# qutebrowser
-mkdir -p "$HOME/.config/qutebrowser"
-cp "$HOME/repos/configs/files/qutebrowser/config.py" "$HOME/.config/qutebrowser/config.py"
-
-# ranger
-mkdir -p "$HOME/.config/ranger"
-cp "$HOME/repos/configs/files/ranger/rc.conf" "$HOME/.config/ranger/rc.conf"
-cp "$HOME/repos/configs/files/ranger/rifle.conf" "$HOME/.config/ranger/rifle.conf"
-cp "$HOME/repos/configs/files/ranger/scope.sh" "$HOME/.config/ranger/scope.sh"
-
-# rcs
-[ -f "$HOME/.profile" ] || cp "$HOME/repos/configs/files/rcs/.profile" "$HOME/.profile"
-cp "$HOME/repos/configs/files/rcs/.bashrc" "$HOME/.bashrc"
-cp "$HOME/repos/configs/files/rcs/.bash_aliases" "$HOME/.bash_aliases"
-[ -f "$HOME/.bash_vars" ] || cp "$HOME/repos/configs/files/rcs/.bash_vars" "$HOME/.bash_vars"
-cp "$HOME/repos/configs/files/rcs/.inputrc" "$HOME/.inputrc"
-cp "$HOME/repos/configs/files/rcs/.tmux.conf" "$HOME/.tmux.conf"
-cp "$HOME/repos/configs/files/rcs/.Xresources" "$HOME/.Xresources"
-cp "$HOME/repos/configs/files/rcs/.xinitrci3" "$HOME/.xinitrci3"
-
-# vim
-mkdir -p "$HOME/.vim"
-cp "$HOME/repos/configs/files/vim/.vimrc" "$HOME/.vimrc"
-cp -r "$HOME/repos/configs/files/vim/ftplugin" "$HOME/.vim"
-cp -r "$HOME/repos/configs/files/vim/plugin" "$HOME/.vim"
-
-# sxiv
-mkdir -p "$HOME/.config/sxiv/exec"
-cp "$HOME/repos/configs/files/sxiv/config.h" "$HOME/repos/sxiv/config.h"
-cp "$HOME/repos/configs/files/sxiv/image-info" "$HOME/.config/sxiv/exec/image-info"
-cp "$HOME/repos/configs/files/sxiv/key-handler" "$HOME/.config/sxiv/exec/key-handler"
-
-# zathura
-mkdir -p "$HOME/.config/zathura"
-cp "$HOME/repos/configs/files/zathura/zathurarc" "$HOME/.config/zathura/zathurarc"
-
-# emojis
-cp "$HOME/repos/configs/files/emoji" "$HOME/.config/emoji"
diff --git a/stow/XserverDotfiles/dot-xinitrcbspwm b/stow/XserverDotfiles/dot-xinitrcbspwm
new file mode 100644
index 0000000..ac7f120
--- /dev/null
+++ b/stow/XserverDotfiles/dot-xinitrcbspwm
@@ -0,0 +1 @@
+exec bspwm
diff --git a/stow/XserverDotfiles/dot-xinitrcdwm b/stow/XserverDotfiles/dot-xinitrcdwm
new file mode 100644
index 0000000..e59268d
--- /dev/null
+++ b/stow/XserverDotfiles/dot-xinitrcdwm
@@ -0,0 +1,2 @@
+dwmblocks &
+xinit.sh; exec dwm
diff --git a/stow/XserverDotfiles/dot-xinitrcgnome b/stow/XserverDotfiles/dot-xinitrcgnome
new file mode 100644
index 0000000..74f16d6
--- /dev/null
+++ b/stow/XserverDotfiles/dot-xinitrcgnome
@@ -0,0 +1,3 @@
+export XDG_SESSION_TYPE=x11
+export GDK_BACKEND=x11
+xinit.sh; exec gnome-session
diff --git a/files/rcs/.xinitrci3 b/stow/XserverDotfiles/dot-xinitrci3
index ba0c6ef..ba0c6ef 100644
--- a/files/rcs/.xinitrci3
+++ b/stow/XserverDotfiles/dot-xinitrci3
diff --git a/stow/XserverDotfiles/dot-xinitrcpostintel b/stow/XserverDotfiles/dot-xinitrcpostintel
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/stow/XserverDotfiles/dot-xinitrcpostintel
diff --git a/stow/XserverDotfiles/dot-xinitrcpostnvidia b/stow/XserverDotfiles/dot-xinitrcpostnvidia
new file mode 100644
index 0000000..7412099
--- /dev/null
+++ b/stow/XserverDotfiles/dot-xinitrcpostnvidia
@@ -0,0 +1,2 @@
+sudo /usr/bin/prime-switch
+
diff --git a/stow/XserverDotfiles/dot-xinitrcprevintel b/stow/XserverDotfiles/dot-xinitrcprevintel
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/stow/XserverDotfiles/dot-xinitrcprevintel
diff --git a/files/rcs/.xinitrcxfce4 b/stow/XserverDotfiles/dot-xinitrcprevnvidia
index f1a7a82..18e2dfc 100644
--- a/files/rcs/.xinitrcxfce4
+++ b/stow/XserverDotfiles/dot-xinitrcprevnvidia
@@ -1,4 +1,4 @@
xrandr --setprovideroutputsource modesetting NVIDIA-0
xrandr --auto
xrandr --dpi 96
-exec xfce4-session
+/usr/bin/prime-offload
diff --git a/stow/XserverDotfiles/dot-xinitrcxfce4 b/stow/XserverDotfiles/dot-xinitrcxfce4
new file mode 100644
index 0000000..d6c8552
--- /dev/null
+++ b/stow/XserverDotfiles/dot-xinitrcxfce4
@@ -0,0 +1 @@
+exec xfce4-session
diff --git a/stow/bspwm/dot-config/bspwm/bspwmrc b/stow/bspwm/dot-config/bspwm/bspwmrc
new file mode 100755
index 0000000..5dd20e5
--- /dev/null
+++ b/stow/bspwm/dot-config/bspwm/bspwmrc
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+desktops="I II III IV V"
+
+if xrandr | grep -q 'HDMI-1-1 connected'; then
+ bspc monitor '^1' -d $desktops
+ bspc monitor '^2' -d $desktops
+else
+ bspc monitor -d $desktops
+fi
+
+bspc config border_width 4
+bspc config window_gap 10
+
+bspc config split_ratio 0.50
+bspc config borderless_monocle true
+bspc config gapless_monocle true
+
+#bspc rule -a Gimp desktop='^8' state=floating follow=on
+#bspc rule -a Chromium desktop='^2'
+#bspc rule -a mplayer2 state=floating
+#bspc rule -a mpv state=floating
+#bspc rule -a Kupfer.py focus=on
+bspc rule -a Screenkey manage=off
+
+pgrep -x sxhkd > /dev/null || sxhkd "$XDG_CONFIG_HOME/sxhkd/sxhkdrcbspc" >~/logs/sxhkd.log &
+
+xinit.sh
diff --git a/stow/cmus/dot-config/cmus/autosave b/stow/cmus/dot-config/cmus/autosave
new file mode 100644
index 0000000..86d1436
--- /dev/null
+++ b/stow/cmus/dot-config/cmus/autosave
@@ -0,0 +1,217 @@
+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 color_cmdline_attr=default
+set color_cmdline_bg=default
+set color_cmdline_fg=default
+set color_cur_sel_attr=default
+set color_error=lightred
+set color_info=lightcyan
+set color_separator=blue
+set color_statusline_attr=default
+set color_statusline_bg=gray
+set color_statusline_fg=black
+set color_titleline_attr=default
+set color_titleline_bg=blue
+set color_titleline_fg=white
+set color_trackwin_album_attr=bold
+set color_trackwin_album_bg=default
+set color_trackwin_album_fg=default
+set color_win_attr=default
+set color_win_bg=default
+set color_win_cur=lightgreen
+set color_win_cur_attr=default
+set color_win_cur_sel_attr=default
+set color_win_cur_sel_bg=blue
+set color_win_cur_sel_fg=lightgreen
+set color_win_dir=lightblue
+set color_win_fg=default
+set color_win_inactive_cur_sel_attr=default
+set color_win_inactive_cur_sel_bg=lightblue
+set color_win_inactive_cur_sel_fg=lightgreen
+set color_win_inactive_sel_attr=default
+set color_win_inactive_sel_bg=lightblue
+set color_win_inactive_sel_fg=black
+set color_win_sel_attr=default
+set color_win_sel_bg=blue
+set color_win_sel_fg=white
+set color_win_title_attr=default
+set color_win_title_bg=blue
+set color_win_title_fg=white
+set confirm_run=true
+set continue=true
+set continue_album=true
+set device=/dev/cdrom
+set display_artist_sort_name=false
+set dsp.alsa.device=
+set dsp.ao.buffer_size=16384
+set dsp.ao.device_interface=
+set dsp.ao.driver=
+set dsp.ao.wav_counter=1
+set dsp.ao.wav_dir=/home/taamas
+set dsp.jack.resampling_quality=2
+set dsp.jack.server_name=
+set dsp.oss.device=
+set follow=true
+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
+set icecast_default_charset=ISO-8859-1
+set id3_default_charset=ISO-8859-1
+set input.aac.priority=50
+set input.cdio.cddb_url=freedb.freedb.org:8880
+set input.cdio.priority=50
+set input.cue.priority=50
+set input.ffmpeg.priority=30
+set input.flac.priority=50
+set input.mad.priority=55
+set input.modplug.priority=50
+set input.mpc.priority=50
+set input.opus.priority=50
+set input.vorbis.priority=50
+set input.wav.priority=50
+set input.wavpack.priority=50
+set lib_add_filter=
+set lib_sort=albumartist date album discnumber tracknumber title filename play_count
+set mixer.alsa.channel=
+set mixer.alsa.device=
+set mixer.oss.channel=PCM
+set mixer.oss.device=
+set mixer.pulse.restore_volume=1
+set mouse=false
+set mpris=true
+set output_plugin=pulse
+set passwd=
+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=false
+set softvol_state=0 0
+set start_view=tree
+set status_display_program=
+set stop_after_queue=false
+set time_show_leading_zero=false
+set tree_width_max=0
+set tree_width_percent=33
+set wrap_search=true
+bind browser backspace browser-up
+bind browser i toggle show_hidden
+bind browser space win-activate
+bind browser u win-update
+bind common ! push shell
+bind common + vol +10%
+bind common , seek -1m
+bind common - vol -10%
+bind common . seek +1m
+bind common / search-start
+bind common 1 view tree
+bind common 2 view sorted
+bind common 3 view playlist
+bind common 4 view queue
+bind common 5 view browser
+bind common 6 view filters
+bind common 7 view settings
+bind common = vol +10%
+bind common ? search-b-start
+bind common C toggle continue
+bind common D win-remove
+bind common E win-add-Q
+bind common F push filter
+bind common G win-bottom
+bind common I echo {}
+bind common L push live-filter
+bind common M toggle play_library
+bind common N search-prev
+bind common P win-mv-before
+bind common U win-update-cache
+bind common [ vol +1% +0
+bind common ] vol +0 +1%
+bind common ^B win-page-up
+bind common ^C echo Type :quit<enter> to exit cmus.
+bind common ^D win-half-page-down
+bind common ^E win-scroll-down
+bind common ^F win-page-down
+bind common ^L refresh
+bind common ^R toggle repeat_current
+bind common ^U win-half-page-up
+bind common ^Y win-scroll-up
+bind common a win-add-l
+bind common b player-next
+bind common c player-pause
+bind common delete win-remove
+bind common down win-down
+bind common e win-add-q
+bind common end win-bottom
+bind common enter win-activate
+bind common f toggle follow
+bind common g win-top
+bind common h seek -5
+bind common home win-top
+bind common i win-sel-cur
+bind common j win-down
+bind common k win-up
+bind common l seek +5
+bind common left seek -5
+bind common m toggle aaa_mode
+bind common mlb_click_selected win-activate
+bind common mouse_scroll_down win-down
+bind common mouse_scroll_up win-up
+bind common n search-next
+bind common o toggle play_sorted
+bind common p win-mv-after
+bind common page_down win-page-down
+bind common page_up win-page-up
+bind common q quit -i
+bind common r toggle repeat
+bind common right seek +5
+bind common s toggle shuffle
+bind common space win-toggle
+bind common t toggle show_remaining_time
+bind common tab win-next
+bind common u update-cache
+bind common up win-up
+bind common v player-stop
+bind common x player-play
+bind common y win-add-p
+bind common z player-prev
+bind common { vol -1% -0
+bind common } vol -0 -1%
+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/stow/colorschemes/dot-config/colorschemes/customBlue b/stow/colorschemes/dot-config/colorschemes/customBlue
new file mode 100644
index 0000000..a96475f
--- /dev/null
+++ b/stow/colorschemes/dot-config/colorschemes/customBlue
@@ -0,0 +1,29 @@
+!Custom blue
+!*background: #181820
+*background: #232f40
+!*foreground: #ebdbb2
+*foreground: #ffffff
+! Black + DarkGrey
+*color0: #000000
+*color8: #181820
+! DarkRed + Red
+*color1: #ff3333
+*color9: #ff6666
+! DarkGreen + Green
+*color2: #66ff66
+*color10: #b2ff66
+! DarkYellow + Yellow
+*color3: #fabd2f
+*color11: #ffff66
+! DarkBlue + Blue
+*color4: #6666ff
+*color12: #66b2ff
+! DarkMagenta + Magenta
+*color5: #ff3366
+*color13: #ff66bb
+! DarkCyan + Cyan
+*color6: #33ddff
+*color14: #66ffff
+! LightGrey + White
+*color7: #ebdbb2
+*color15: #ffffff
diff --git a/stow/colorschemes/dot-config/colorschemes/gruvbox b/stow/colorschemes/dot-config/colorschemes/gruvbox
new file mode 100644
index 0000000..5c83041
--- /dev/null
+++ b/stow/colorschemes/dot-config/colorschemes/gruvbox
@@ -0,0 +1,31 @@
+!Gruvbox
+*background: #1d2021
+!*background: #32302f
+!*background: #282828
+!*foreground: #d5c4a1
+*foreground: #fbf1c7
+!*foreground: #ebdbb2
+! Black + DarkGrey
+*color0: #282828
+*color8: #928374
+! DarkRed + Red
+*color1: #cc241d
+*color9: #fb4934
+! DarkGreen + Green
+*color2: #98971a
+*color10: #b8bb26
+! DarkYellow + Yellow
+*color3: #d79921
+*color11: #fabd2f
+! DarkBlue + Blue
+*color4: #358588
+*color12: #83a598
+! DarkMagenta + Magenta
+*color5: #b16286
+*color13: #d3869b
+! DarkCyan + Cyan
+*color6: #689d6a
+*color14: #8ec07c
+! LightGrey + White
+*color7: #a89984
+*color15: #ebdbb2
diff --git a/stow/colorschemes/dot-config/colorschemes/nord b/stow/colorschemes/dot-config/colorschemes/nord
new file mode 100644
index 0000000..916a0ff
--- /dev/null
+++ b/stow/colorschemes/dot-config/colorschemes/nord
@@ -0,0 +1,47 @@
+! Copyright (c) 2016-present Arctic Ice Studio <development@arcticicestudio.com>
+! Copyright (c) 2016-present Sven Greb <code@svengreb.de>
+
+! Project: Nord XResources
+! Version: 0.1.0
+! Repository: https://github.com/arcticicestudio/nord-xresources
+! License: MIT
+
+#define nord0 #2E3440
+#define nord1 #3B4252
+#define nord2 #434C5E
+#define nord3 #4C566A
+#define nord4 #D8DEE9
+#define nord5 #E5E9F0
+#define nord6 #ECEFF4
+#define nord7 #8FBCBB
+#define nord8 #88C0D0
+#define nord9 #81A1C1
+#define nord10 #5E81AC
+#define nord11 #BF616A
+#define nord12 #D08770
+#define nord13 #EBCB8B
+#define nord14 #A3BE8C
+#define nord15 #B48EAD
+
+*.foreground: nord4
+*.background: nord0
+*.cursorColor: nord4
+*fading: 35
+*fadeColor: nord3
+
+*.color0: nord1
+*.color1: nord11
+*.color2: nord14
+*.color3: nord13
+*.color4: nord9
+*.color5: nord15
+*.color6: nord8
+*.color7: nord5
+*.color8: nord3
+*.color9: nord11
+*.color10: nord14
+*.color11: nord13
+*.color12: nord9
+*.color13: nord15
+*.color14: nord7
+*.color15: nord6
diff --git a/stow/dunst/dot-config/dunst/dunstifyIDs b/stow/dunst/dot-config/dunst/dunstifyIDs
new file mode 100644
index 0000000..2bccbf4
--- /dev/null
+++ b/stow/dunst/dot-config/dunst/dunstifyIDs
@@ -0,0 +1,12 @@
+vol 1001
+musicMode 1002
+brightness 1003
+cpu 1004
+memory 1005
+disk 1006
+batteryLow 1007
+moon 1008
+calcurse 1009
+bspwmDesktop 1010
+toggleKBLayouts 1011
+bspwmRunningStuff 1012
diff --git a/files/dunst/dunstrc b/stow/dunst/dot-config/dunst/dunstrc
index 689a2fb..80004aa 100644
--- a/files/dunst/dunstrc
+++ b/stow/dunst/dot-config/dunst/dunstrc
@@ -65,8 +65,11 @@
timeout = 5
[urgency_normal]
- background = "#285577"
- foreground = "#ffffff"
+ #background = "#285577" # default
+ #foreground = "#ffffff" # default
+ background = "#2e3440"
+ #foreground = "#81a1c1"
+ foreground = "#88c0d0"
timeout = 5
[urgency_critical]
diff --git a/files/emoji b/stow/emoji/dot-config/emoji
index 2441237..2441237 100644
--- a/files/emoji
+++ b/stow/emoji/dot-config/emoji
diff --git a/files/i3/config b/stow/i3/dot-config/i3/config
index c506e8b..00daed1 100644
--- a/files/i3/config
+++ b/stow/i3/dot-config/i3/config
@@ -172,13 +172,13 @@ bindsym $mod+Shift+Control+t move container to workspace $wst
# colors
#class #border #bg #text #indicator #child_border
-client.focused $black2 $black2 $white2 $blue $blue
+client.focused $blue2 $black2 $white2 $blue $blue
client.focused_inactive $black2 $black $white $black2 $black2
client.unfocused $black2 $black $blue2 $black $black
client.urgent $red $black $white $red $red
# for window config
-for_window [class="^.*"] border pixel 1
+for_window [class="^.*"] border pixel 4
# gaps config
gaps inner 14
@@ -262,7 +262,7 @@ bar {
separator_symbol "·"
strip_workspace_numbers yes
colors {
- background $black
+ background #000000
statusline $white2
separator $white2
focused_workspace $blue $black $blue
diff --git a/files/i3blocks/config b/stow/i3blocks/dot-config/i3blocks/config
index 17d9fee..17d9fee 100644
--- a/files/i3blocks/config
+++ b/stow/i3blocks/dot-config/i3blocks/config
diff --git a/files/mpv/input.conf b/stow/mpv/dot-config/mpv/input.conf
index 3acfb6c..3acfb6c 100644
--- a/files/mpv/input.conf
+++ b/stow/mpv/dot-config/mpv/input.conf
diff --git a/files/mpv/mpv.conf b/stow/mpv/dot-config/mpv/mpv.conf
index 9e5391f..9e5391f 100644
--- a/files/mpv/mpv.conf
+++ b/stow/mpv/dot-config/mpv/mpv.conf
diff --git a/stow/mpv/dot-config/mpv/scripts-opts/mpv_thumbnail_script.conf b/stow/mpv/dot-config/mpv/scripts-opts/mpv_thumbnail_script.conf
new file mode 100644
index 0000000..7380feb
--- /dev/null
+++ b/stow/mpv/dot-config/mpv/scripts-opts/mpv_thumbnail_script.conf
@@ -0,0 +1,70 @@
+# The thumbnail cache directory.
+# On Windows this defaults to %TEMP%\mpv_thumbs_cache,
+# and on other platforms to /tmp/mpv_thumbs_cache.
+# The directory will be created automatically, but must be writeable!
+# Use absolute paths, and take note that environment variables like %TEMP% are unsupported (despite the default)!
+cache_directory=/tmp/my_mpv_thumbnails
+# THIS IS NOT A WINDOWS PATH. COMMENT IT OUT OR ADJUST IT YOURSELF.
+
+# Whether to generate thumbnails automatically on video load, without a keypress
+# Defaults to yes
+autogenerate=yes
+
+# Only automatically thumbnail videos shorter than this (in seconds)
+# You will have to press T (or your own keybind) to enable the thumbnail previews
+# Set to 0 to disable the check, ie. thumbnail videos no matter how long they are
+# Defaults to 3600 (one hour)
+autogenerate_max_duration=3600
+
+# Use mpv to generate thumbnail even if ffmpeg is found in PATH
+# ffmpeg is slightly faster than mpv but lacks support for ordered chapters in MKVs,
+# which can break the resulting thumbnails. You have been warned.
+# Defaults to yes (don't use ffmpeg)
+prefer_mpv=yes
+
+# Explicitly disable subtitles on the mpv sub-calls
+# mpv can and will by default render subtitles into the thumbnails.
+# If this is not what you wish, set mpv_no_sub to yes
+# Defaults to no
+mpv_no_sub=no
+
+# Enable to disable the built-in keybind ("T") to add your own, see after the block
+disable_keybinds=no
+
+# The maximum dimensions of the thumbnails, in pixels
+# Defaults to 200 and 200
+thumbnail_width=200
+thumbnail_height=200
+
+# The thumbnail count target
+# (This will result in a thumbnail every ~10 seconds for a 25 minute video)
+thumbnail_count=150
+
+# The above target count will be adjusted by the minimum and
+# maximum time difference between thumbnails.
+# The thumbnail_count will be used to calculate a target separation,
+# and min/max_delta will be used to constrict it.
+
+# In other words, thumbnails will be:
+# - at least min_delta seconds apart (limiting the amount)
+# - at most max_delta seconds apart (raising the amount if needed)
+# Defaults to 5 and 90, values are seconds
+min_delta=5
+max_delta=90
+# 120 seconds aka 2 minutes will add more thumbnails only when the video is over 5 hours long!
+
+# Below are overrides for remote urls (you generally want less thumbnails, because it's slow!)
+# Thumbnailing network paths will be done with mpv (leveraging youtube-dl)
+
+# Allow thumbnailing network paths (naive check for "://")
+# Defaults to no
+thumbnail_network=yes
+# Override thumbnail count, min/max delta, as above
+remote_thumbnail_count=60
+remote_min_delta=15
+remote_max_delta=120
+
+# Try to grab the raw stream and disable ytdl for the mpv subcalls
+# Much faster than passing the url to ytdl again, but may cause problems with some sites
+# Defaults to yes
+remote_direct_stream=yes
diff --git a/stow/mpv/dot-config/mpv/scripts-opts/youtube-quality.conf b/stow/mpv/dot-config/mpv/scripts-opts/youtube-quality.conf
new file mode 100644
index 0000000..9c1a8c9
--- /dev/null
+++ b/stow/mpv/dot-config/mpv/scripts-opts/youtube-quality.conf
@@ -0,0 +1,41 @@
+# KEY BINDINGS
+
+# invoke or dismiss the quality menu
+toggle_menu_binding=ctrl+f
+# move the menu cursor up
+up_binding=UP
+# move the menu cursor down
+down_binding=DOWN
+# select menu entry
+select_binding=ENTER
+
+# formatting / cursors
+selected_and_active=▶ -
+selected_and_inactive=● -
+unselected_and_active=▷ -
+unselected_and_inactive=○ -
+
+# font size scales by window, if false requires larger font and padding sizes
+scale_playlist_by_window=no
+
+# playlist ass style overrides inside curly brackets, \keyvalue is one field, extra \ for escape in lua
+# example {\\fnUbuntu\\fs10\\b0\\bord1} equals: font=Ubuntu, size=10, bold=no, border=1
+# read http://docs.aegisub.org/3.2/ASS_Tags/ for reference of tags
+# undeclared tags will use default osd settings
+# these styles will be used for the whole playlist. More specific styling will need to be hacked in
+#
+# (a monospaced font is recommended but not required)
+style_ass_tags={\\fnmonospace}
+
+# paddings for top left corner
+text_padding_x=5
+text_padding_y=5
+
+# how many seconds until the quality menu times out
+menu_timeout=10
+
+#use youtube-dl to fetch a list of available formats (overrides quality_strings)
+fetch_formats=yes
+
+# list of ytdl-format strings to choose from
+quality_strings=[ {"4320p" : "bestvideo[height<=?4320p]+bestaudio/best"}, {"2160p" : "bestvideo[height<=?2160]+bestaudio/best"}, {"1440p" : "bestvideo[height<=?1440]+bestaudio/best"}, {"1080p" : "bestvideo[height<=?1080]+bestaudio/best"}, {"720p" : "bestvideo[height<=?720]+bestaudio/best"}, {"480p" : "bestvideo[height<=?480]+bestaudio/best"}, {"360p" : "bestvideo[height<=?360]+bestaudio/best"}, {"240p" : "bestvideo[height<=?240]+bestaudio/best"}, {"144p" : "bestvideo[height<=?144]+bestaudio/best"} ]
diff --git a/stow/mpv/dot-config/mpv/scripts/mpv-splice.lua b/stow/mpv/dot-config/mpv/scripts/mpv-splice.lua
new file mode 100644
index 0000000..b121dc4
--- /dev/null
+++ b/stow/mpv/dot-config/mpv/scripts/mpv-splice.lua
@@ -0,0 +1,328 @@
+-- -----------------------------------------------------------------------------
+--
+-- MPV Splice
+-- URL: https://github.com/pvpscript/mpv-video-splice
+--
+-- Requires: ffmpeg
+--
+-- Description:
+--
+-- This script provides the hability to create video slices by grabbing two
+-- timestamps, which generate a slice from timestamp A[i] to timestamp B[i],
+-- e.g.:
+-- -> Slice 1: 00:10:34.25 -> 00:15:00.00.
+-- -> Slice 2: 00:23:00.84 -> 00:24:10.00.
+-- ...
+-- -> Slice n: 01:44:22.47 -> 01:56:00.00.
+--
+-- Then, all the slices from 1 to n are joined together, creating a new
+-- video.
+--
+-- The output file will appear at the directory that the mpv command was ran,
+-- or in the environment variable set for it (see Environment variables below)
+--
+-- Note: This script prevents the mpv player from closing when the video ends,
+-- so that the slices don't get lost. Keep this in mind if there's the option
+-- 'keep-open=no' in the current config file.
+--
+-- Note: This script will also silence the terminal, so the script messages
+-- can be seen more clearly.
+--
+-- -----------------------------------------------------------------------------
+--
+--
+-- Usage:
+--
+-- In the video screen, press Alt + T to grab the first timestamp and then
+-- press Alt + T again to get the second timestamp. This process will generate
+-- a time range, which represents a video slice. Repeat this process to create
+-- more slices.
+--
+-- To see all the slices made, press Alt + P. All of the slices will appear
+-- in the terminal in order of creation, with their corresponding timestamps.
+-- Incomplete slices will show up as 'Slice N in progress', where N is the
+-- slice number.
+--
+-- To reset an incomplete slice, press Alt + R. If the first part of a slice
+-- was created at the wrong time, this will reset the current slice.
+--
+-- To delete a whole slice, start the slice deletion mode by pressing Alt + D.
+-- When in this mode, it's possible to press Alt + NUM, where NUM is any
+-- number between 0 inclusive and 9 inclusive. For each Alt + NUM pressed, a
+-- number will be concatenated to make the final number referring to the slice
+-- to be removed, then press Alt + D again to stop the slicing deletion mode
+-- and delete the slice corresponding to the formed number.
+--
+-- Example 1: Deleting slice number 3
+-- -> Alt + D # Start slice deletion mode
+-- -> Alt + 3 # Concatenate number 3
+-- -> Alt + D # Exit slice deletion mode
+--
+-- Example 2> Deleting slice number 76
+-- -> Alt + D # Start slice deletion mode
+-- -> Alt + 7 # Concatenate number 7
+-- -> Alt + 6 # Concatenate number 6
+-- -> Alt + D # Exit slice deletion mode
+--
+-- To fire up ffmpeg, which will slice up the video and concatenate the slices
+-- together, press Alt + C. It's important that there are at least one
+-- slice, otherwise no video will be created.
+--
+-- Note: No cut will be made unless the user presses Alt + C.
+-- Also, the original video file won't be affected by the cutting.
+--
+--
+-- -----------------------------------------------------------------------------
+--
+--
+-- Log level:
+--
+-- Everytime a timestamp is grabbed, a text will appear on the screen showing
+-- the selected time.
+-- When Alt + P is pressed, besides showing the slices in the terminal,
+-- it will also show on the screen the total number of cuts (or slices)
+-- that were made.
+-- When the actual cutting and joining process begins, a message will be shown
+-- on the screen and the terminal telling that it began. When the process ends,
+-- a message will appear on the screen and the terminal displaying the full path
+-- of the generated video. It will also appear a message in the terminal telling
+-- that the process ended.
+--
+-- Note: Every message that appears on the terminal has the log level of 'info'.
+--
+--
+-- -----------------------------------------------------------------------------
+--
+--
+-- Environment Variables:
+--
+-- This script uses environment variables to allow the user to
+-- set the temporary location of the video cuts and for setting the location for
+-- the resulting video.
+--
+-- To set the temporary directory, set the variable MPV_SPLICE_TEMP;
+-- e.g.: export MPV_SPLICE_TEMP="$HOME/temporary_location"
+--
+-- To set the video output directory, set the variable MPV_SPLICE_OUTPUT;
+-- e.g.: export MPV_SPLICE_OUTPUT="$HOME/output_location"
+--
+-- Make sure the directories set in the variables really exist, or else the
+-- script might fail.
+--
+-- -----------------------------------------------------------------------------
+
+--------------------------------------------------------------------------------
+-- Importing the mpv libraries
+
+local mp = require 'mp'
+local msg = require 'mp.msg'
+
+--------------------------------------------------------------------------------
+-- Default variables
+
+local default_tmp_location = "/tmp"
+local default_output_location = mp.get_property("working-directory")
+
+--------------------------------------------------------------------------------
+
+local concat_name = "concat.txt"
+
+local ffmpeg = "ffmpeg -hide_banner -loglevel warning"
+
+local tmp_location = os.getenv("MPV_SPLICE_TEMP")
+ and os.getenv("MPV_SPLICE_TEMP")
+ or default_tmp_location
+local output_location = os.getenv("MPV_SPLICE_OUTPUT")
+ and os.getenv("MPV_SPLICE_OUTPUT")
+ or default_output_location
+
+local times = {}
+local start_time = nil
+local remove_val = ""
+
+local exit_time = 0
+
+--------------------------------------------------------------------------------
+
+function notify(duration, ...)
+ local args = {...}
+ local text = ""
+
+ for i, v in ipairs(args) do
+ text = text .. tostring(v)
+ end
+
+ msg.info(text)
+ mp.command(string.format("show-text \"%s\" %d 1",
+ text, duration))
+end
+
+local function get_time()
+ local time_in_secs = mp.get_property_number('time-pos')
+
+ local hours = math.floor(time_in_secs / 3600)
+ local mins = math.floor((time_in_secs - hours * 3600) / 60)
+ local secs = time_in_secs - hours * 3600 - mins * 60
+
+ local fmt_time = string.format('%02d:%02d:%05.2f', hours, mins, secs)
+
+ return fmt_time
+end
+
+function put_time()
+ local time = get_time()
+ local message = ""
+
+ if not start_time then
+ start_time = time
+ message = "[START TIMESTAMP]"
+ else
+ --times[#times+1] = {
+ table.insert(times, {
+ t_start = start_time,
+ t_end = time
+ })
+ start_time = nil
+
+ message = "[END TIMESTAMP]"
+ end
+
+ notify(2000, message, ": ", time)
+end
+
+function show_times()
+ notify(2000, "Total cuts: ", #times)
+
+ for i, obj in ipairs(times) do
+ msg.info("Slice", i, ": ", obj.t_start, " -> ", obj.t_end)
+ end
+ if start_time then
+ notify(2000, "Slice ", #times+1, " in progress.")
+ end
+end
+
+function reset_current_slice()
+ if start_time then
+ notify(2000, "Slice ", #times+1, " reseted.")
+
+ start_time = nil
+ end
+end
+
+function delete_slice()
+ if remove_val == "" then
+ notify(2000, "Entered slice deletion mode.")
+
+ -- Add shortcut keys to the interval {0..9}.
+ for i=0,9,1 do
+ mp.add_key_binding("Alt+" .. i, "num_key_" .. i,
+ function()
+ remove_val = remove_val .. i
+ notify(1000, "Slice to remove: "
+ .. remove_val)
+ end
+ )
+ end
+ else
+ -- Remove previously added shortcut keys.
+ for i=0,9,1 do
+ mp.remove_key_binding("num_key_" .. i)
+ end
+
+ remove_num = tonumber(remove_val)
+ if #times >= remove_num and remove_num > 0 then
+ table.remove(times, remove_num)
+ notify(2000, "Removed slice ", remove_num)
+ end
+
+ remove_val = ""
+
+ msg.info("Exited slice deletion mode.")
+ end
+end
+
+function prevent_quit(name)
+ if start_time then
+ if os.time() - exit_time <= 2 then
+ mp.command(name)
+ else
+ exit_time = os.time()
+ end
+ notify(3000, "Slice has been marked. Press again to quit")
+ else
+ mp.command(name)
+ end
+end
+
+function process_video()
+ local alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+ local rnd_size = 10
+
+ local pieces = {}
+
+ -- Better seed randomization
+ math.randomseed(os.time())
+ math.random(); math.random(); math.random()
+
+ if times[#times] then
+ local tmp_dir = io.popen(string.format("mktemp -d -p %s",
+ tmp_location)):read("*l")
+ local input_file = mp.get_property("path")
+ local ext = string.gmatch(input_file, ".*%.(.*)$")()
+
+ local rnd_str = ""
+ for i=1,rnd_size,1 do
+ local rnd_index = math.floor(math.random() * #alphabet + 0.5)
+ rnd_str = rnd_str .. alphabet:sub(rnd_index, rnd_index)
+ end
+
+ local output_file = string.format("%s/%s_%s_cut.%s",
+ output_location,
+ mp.get_property("filename/no-ext"),
+ rnd_str, ext)
+
+ local cat_file_name = string.format("%s/%s", tmp_dir, "concat.txt")
+ local cat_file_ptr = io.open(cat_file_name, "w")
+
+ notify(2000, "Process started!")
+
+ for i, obj in ipairs(times) do
+ local path = string.format("%s/%s_%d.%s",
+ tmp_dir, rnd_str, i, ext)
+ cat_file_ptr:write(string.format("file '%s'\n", path))
+ os.execute(string.format("%s -ss %s -i \"%s\" -to %s " ..
+ "-c copy -copyts -avoid_negative_ts make_zero \"%s\"",
+ ffmpeg, obj.t_start, input_file, obj.t_end,
+ path))
+ end
+
+ cat_file_ptr:close()
+
+ cmd = string.format("%s -f concat -safe 0 -i \"%s\" " ..
+ "-c copy \"%s\"",
+ ffmpeg, cat_file_name, output_file)
+ os.execute(cmd)
+
+ notify(10000, "File saved as: ", output_file)
+ msg.info("Process ended!")
+
+ os.execute(string.format("rm -rf %s", tmp_dir))
+ msg.info("Temporary directory removed!")
+ end
+end
+
+mp.set_property("keep-open", "yes") -- Prevent mpv from exiting when the video ends
+mp.set_property("quiet", "yes") -- Silence terminal.
+
+mp.add_key_binding('q', "quit", function()
+ prevent_quit("quit")
+end)
+mp.add_key_binding('Shift+q', "quit-watch-later", function()
+ prevent_quit("quit-watch-later")
+end)
+
+mp.add_key_binding('Alt+t', "put_time", put_time)
+mp.add_key_binding('Alt+p', "show_times", show_times)
+mp.add_key_binding('Alt+c', "process_video", process_video)
+mp.add_key_binding('Alt+r', "reset_current_slice", reset_current_slice)
+mp.add_key_binding('Alt+d', "delete_slice", delete_slice)
diff --git a/stow/mpv/dot-config/mpv/scripts/mpv_thumbnail_script_client_osc.lua b/stow/mpv/dot-config/mpv/scripts/mpv_thumbnail_script_client_osc.lua
new file mode 100644
index 0000000..e822846
--- /dev/null
+++ b/stow/mpv/dot-config/mpv/scripts/mpv_thumbnail_script_client_osc.lua
@@ -0,0 +1,3886 @@
+--[[
+ Copyright (C) 2017 AMM
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]--
+--[[
+ mpv_thumbnail_script.lua 0.4.2 - commit a2de250 (branch master)
+ https://github.com/TheAMM/mpv_thumbnail_script
+ Built on 2018-02-07 20:36:55
+]]--
+local assdraw = require 'mp.assdraw'
+local msg = require 'mp.msg'
+local opt = require 'mp.options'
+local utils = require 'mp.utils'
+
+-- Determine platform --
+ON_WINDOWS = (package.config:sub(1,1) ~= '/')
+
+-- Some helper functions needed to parse the options --
+function isempty(v) return (v == false) or (v == nil) or (v == "") or (v == 0) or (type(v) == "table" and next(v) == nil) end
+
+function divmod (a, b)
+ return math.floor(a / b), a % b
+end
+
+-- Better modulo
+function bmod( i, N )
+ return (i % N + N) % N
+end
+
+function join_paths(...)
+ local sep = ON_WINDOWS and "\\" or "/"
+ local result = "";
+ for i, p in pairs({...}) do
+ if p ~= "" then
+ if is_absolute_path(p) then
+ result = p
+ else
+ result = (result ~= "") and (result:gsub("[\\"..sep.."]*$", "") .. sep .. p) or p
+ end
+ end
+ end
+ return result:gsub("[\\"..sep.."]*$", "")
+end
+
+-- /some/path/file.ext -> /some/path, file.ext
+function split_path( path )
+ local sep = ON_WINDOWS and "\\" or "/"
+ local first_index, last_index = path:find('^.*' .. sep)
+
+ if last_index == nil then
+ return "", path
+ else
+ local dir = path:sub(0, last_index-1)
+ local file = path:sub(last_index+1, -1)
+
+ return dir, file
+ end
+end
+
+function is_absolute_path( path )
+ local tmp, is_win = path:gsub("^[A-Z]:\\", "")
+ local tmp, is_unix = path:gsub("^/", "")
+ return (is_win > 0) or (is_unix > 0)
+end
+
+function Set(source)
+ local set = {}
+ for _, l in ipairs(source) do set[l] = true end
+ return set
+end
+
+---------------------------
+-- More helper functions --
+---------------------------
+
+-- Removes all keys from a table, without destroying the reference to it
+function clear_table(target)
+ for key, value in pairs(target) do
+ target[key] = nil
+ end
+end
+function shallow_copy(target)
+ local copy = {}
+ for k, v in pairs(target) do
+ copy[k] = v
+ end
+ return copy
+end
+
+-- Rounds to given decimals. eg. round_dec(3.145, 0) => 3
+function round_dec(num, idp)
+ local mult = 10^(idp or 0)
+ return math.floor(num * mult + 0.5) / mult
+end
+
+function file_exists(name)
+ local f = io.open(name, "rb")
+ if f ~= nil then
+ local ok, err, code = f:read(1)
+ io.close(f)
+ return code == nil
+ else
+ return false
+ end
+end
+
+function path_exists(name)
+ local f = io.open(name, "rb")
+ if f ~= nil then
+ io.close(f)
+ return true
+ else
+ return false
+ end
+end
+
+function create_directories(path)
+ local cmd
+ if ON_WINDOWS then
+ cmd = { args = {"cmd", "/c", "mkdir", path} }
+ else
+ cmd = { args = {"mkdir", "-p", path} }
+ end
+ utils.subprocess(cmd)
+end
+
+-- Find an executable in PATH or CWD with the given name
+function find_executable(name)
+ local delim = ON_WINDOWS and ";" or ":"
+
+ local pwd = os.getenv("PWD") or utils.getcwd()
+ local path = os.getenv("PATH")
+
+ local env_path = pwd .. delim .. path -- Check CWD first
+
+ local result, filename
+ for path_dir in env_path:gmatch("[^"..delim.."]+") do
+ filename = join_paths(path_dir, name)
+ if file_exists(filename) then
+ result = filename
+ break
+ end
+ end
+
+ return result
+end
+
+local ExecutableFinder = { path_cache = {} }
+-- Searches for an executable and caches the result if any
+function ExecutableFinder:get_executable_path( name, raw_name )
+ name = ON_WINDOWS and not raw_name and (name .. ".exe") or name
+
+ if self.path_cache[name] == nil then
+ self.path_cache[name] = find_executable(name) or false
+ end
+ return self.path_cache[name]
+end
+
+-- Format seconds to HH.MM.SS.sss
+function format_time(seconds, sep, decimals)
+ decimals = decimals == nil and 3 or decimals
+ sep = sep and sep or "."
+ local s = seconds
+ local h, s = divmod(s, 60*60)
+ local m, s = divmod(s, 60)
+
+ local second_format = string.format("%%0%d.%df", 2+(decimals > 0 and decimals+1 or 0), decimals)
+
+ return string.format("%02d"..sep.."%02d"..sep..second_format, h, m, s)
+end
+
+-- Format seconds to 1h 2m 3.4s
+function format_time_hms(seconds, sep, decimals, force_full)
+ decimals = decimals == nil and 1 or decimals
+ sep = sep ~= nil and sep or " "
+
+ local s = seconds
+ local h, s = divmod(s, 60*60)
+ local m, s = divmod(s, 60)
+
+ if force_full or h > 0 then
+ return string.format("%dh"..sep.."%dm"..sep.."%." .. tostring(decimals) .. "fs", h, m, s)
+ elseif m > 0 then
+ return string.format("%dm"..sep.."%." .. tostring(decimals) .. "fs", m, s)
+ else
+ return string.format("%." .. tostring(decimals) .. "fs", s)
+ end
+end
+
+-- Writes text on OSD and console
+function log_info(txt, timeout)
+ timeout = timeout or 1.5
+ msg.info(txt)
+ mp.osd_message(txt, timeout)
+end
+
+-- Join table items, ala ({"a", "b", "c"}, "=", "-", ", ") => "=a-, =b-, =c-"
+function join_table(source, before, after, sep)
+ before = before or ""
+ after = after or ""
+ sep = sep or ", "
+ local result = ""
+ for i, v in pairs(source) do
+ if not isempty(v) then
+ local part = before .. v .. after
+ if i == 1 then
+ result = part
+ else
+ result = result .. sep .. part
+ end
+ end
+ end
+ return result
+end
+
+function wrap(s, char)
+ char = char or "'"
+ return char .. s .. char
+end
+-- Wraps given string into 'string' and escapes any 's in it
+function escape_and_wrap(s, char, replacement)
+ char = char or "'"
+ replacement = replacement or "\\" .. char
+ return wrap(string.gsub(s, char, replacement), char)
+end
+-- Escapes single quotes in a string and wraps the input in single quotes
+function escape_single_bash(s)
+ return escape_and_wrap(s, "'", "'\\''")
+end
+
+-- Returns (a .. b) if b is not empty or nil
+function joined_or_nil(a, b)
+ return not isempty(b) and (a .. b) or nil
+end
+
+-- Put items from one table into another
+function extend_table(target, source)
+ for i, v in pairs(source) do
+ table.insert(target, v)
+ end
+end
+
+-- Creates a handle and filename for a temporary random file (in current directory)
+function create_temporary_file(base, mode, suffix)
+ local handle, filename
+ suffix = suffix or ""
+ while true do
+ filename = base .. tostring(math.random(1, 5000)) .. suffix
+ handle = io.open(filename, "r")
+ if not handle then
+ handle = io.open(filename, mode)
+ break
+ end
+ io.close(handle)
+ end
+ return handle, filename
+end
+
+
+function get_processor_count()
+ local proc_count
+
+ if ON_WINDOWS then
+ proc_count = tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
+ else
+ local cpuinfo_handle = io.open("/proc/cpuinfo")
+ if cpuinfo_handle ~= nil then
+ local cpuinfo_contents = cpuinfo_handle:read("*a")
+ local _, replace_count = cpuinfo_contents:gsub('processor', '')
+ proc_count = replace_count
+ end
+ end
+
+ if proc_count and proc_count > 0 then
+ return proc_count
+ else
+ return nil
+ end
+end
+
+function substitute_values(string, values)
+ local substitutor = function(match)
+ if match == "%" then
+ return "%"
+ else
+ -- nil is discarded by gsub
+ return values[match]
+ end
+ end
+
+ local substituted = string:gsub('%%(.)', substitutor)
+ return substituted
+end
+
+-- ASS HELPERS --
+function round_rect_top( ass, x0, y0, x1, y1, r )
+ local c = 0.551915024494 * r -- circle approximation
+ ass:move_to(x0 + r, y0)
+ ass:line_to(x1 - r, y0) -- top line
+ if r > 0 then
+ ass:bezier_curve(x1 - r + c, y0, x1, y0 + r - c, x1, y0 + r) -- top right corner
+ end
+ ass:line_to(x1, y1) -- right line
+ ass:line_to(x0, y1) -- bottom line
+ ass:line_to(x0, y0 + r) -- left line
+ if r > 0 then
+ ass:bezier_curve(x0, y0 + r - c, x0 + r - c, y0, x0 + r, y0) -- top left corner
+ end
+end
+
+function round_rect(ass, x0, y0, x1, y1, rtl, rtr, rbr, rbl)
+ local c = 0.551915024494
+ ass:move_to(x0 + rtl, y0)
+ ass:line_to(x1 - rtr, y0) -- top line
+ if rtr > 0 then
+ ass:bezier_curve(x1 - rtr + rtr*c, y0, x1, y0 + rtr - rtr*c, x1, y0 + rtr) -- top right corner
+ end
+ ass:line_to(x1, y1 - rbr) -- right line
+ if rbr > 0 then
+ ass:bezier_curve(x1, y1 - rbr + rbr*c, x1 - rbr + rbr*c, y1, x1 - rbr, y1) -- bottom right corner
+ end
+ ass:line_to(x0 + rbl, y1) -- bottom line
+ if rbl > 0 then
+ ass:bezier_curve(x0 + rbl - rbl*c, y1, x0, y1 - rbl + rbl*c, x0, y1 - rbl) -- bottom left corner
+ end
+ ass:line_to(x0, y0 + rtl) -- left line
+ if rtl > 0 then
+ ass:bezier_curve(x0, y0 + rtl - rtl*c, x0 + rtl - rtl*c, y0, x0 + rtl, y0) -- top left corner
+ end
+end
+-- $Revision: 1.5 $
+-- $Date: 2014-09-10 16:54:25 $
+
+-- This module was originally taken from http://cube3d.de/uploads/Main/sha1.txt.
+
+-------------------------------------------------------------------------------
+-- SHA-1 secure hash computation, and HMAC-SHA1 signature computation,
+-- in pure Lua (tested on Lua 5.1)
+-- License: MIT
+--
+-- Usage:
+-- local hashAsHex = sha1.hex(message) -- returns a hex string
+-- local hashAsData = sha1.bin(message) -- returns raw bytes
+--
+-- local hmacAsHex = sha1.hmacHex(key, message) -- hex string
+-- local hmacAsData = sha1.hmacBin(key, message) -- raw bytes
+--
+--
+-- Pass sha1.hex() a string, and it returns a hash as a 40-character hex string.
+-- For example, the call
+--
+-- local hash = sha1.hex("iNTERFACEWARE")
+--
+-- puts the 40-character string
+--
+-- "e76705ffb88a291a0d2f9710a5471936791b4819"
+--
+-- into the variable 'hash'
+--
+-- Pass sha1.hmacHex() a key and a message, and it returns the signature as a
+-- 40-byte hex string.
+--
+--
+-- The two "bin" versions do the same, but return the 20-byte string of raw
+-- data that the 40-byte hex strings represent.
+--
+-------------------------------------------------------------------------------
+--
+-- Description
+-- Due to the lack of bitwise operations in 5.1, this version uses numbers to
+-- represents the 32bit words that we combine with binary operations. The basic
+-- operations of byte based "xor", "or", "and" are all cached in a combination
+-- table (several 64k large tables are built on startup, which
+-- consumes some memory and time). The caching can be switched off through
+-- setting the local cfg_caching variable to false.
+-- For all binary operations, the 32 bit numbers are split into 8 bit values
+-- that are combined and then merged again.
+--
+-- Algorithm: http://www.itl.nist.gov/fipspubs/fip180-1.htm
+--
+-------------------------------------------------------------------------------
+
+local sha1 = (function()
+local sha1 = {}
+
+-- set this to false if you don't want to build several 64k sized tables when
+-- loading this file (takes a while but grants a boost of factor 13)
+local cfg_caching = false
+-- local storing of global functions (minor speedup)
+local floor,modf = math.floor,math.modf
+local char,format,rep = string.char,string.format,string.rep
+
+-- merge 4 bytes to an 32 bit word
+local function bytes_to_w32 (a,b,c,d) return a*0x1000000+b*0x10000+c*0x100+d end
+-- split a 32 bit word into four 8 bit numbers
+local function w32_to_bytes (i)
+ return floor(i/0x1000000)%0x100,floor(i/0x10000)%0x100,floor(i/0x100)%0x100,i%0x100
+end
+
+-- shift the bits of a 32 bit word. Don't use negative values for "bits"
+local function w32_rot (bits,a)
+ local b2 = 2^(32-bits)
+ local a,b = modf(a/b2)
+ return a+b*b2*(2^(bits))
+end
+
+-- caching function for functions that accept 2 arguments, both of values between
+-- 0 and 255. The function to be cached is passed, all values are calculated
+-- during loading and a function is returned that returns the cached values (only)
+local function cache2arg (fn)
+ if not cfg_caching then return fn end
+ local lut = {}
+ for i=0,0xffff do
+ local a,b = floor(i/0x100),i%0x100
+ lut[i] = fn(a,b)
+ end
+ return function (a,b)
+ return lut[a*0x100+b]
+ end
+end
+
+-- splits an 8-bit number into 8 bits, returning all 8 bits as booleans
+local function byte_to_bits (b)
+ local b = function (n)
+ local b = floor(b/n)
+ return b%2==1
+ end
+ return b(1),b(2),b(4),b(8),b(16),b(32),b(64),b(128)
+end
+
+-- builds an 8bit number from 8 booleans
+local function bits_to_byte (a,b,c,d,e,f,g,h)
+ local function n(b,x) return b and x or 0 end
+ return n(a,1)+n(b,2)+n(c,4)+n(d,8)+n(e,16)+n(f,32)+n(g,64)+n(h,128)
+end
+
+-- debug function for visualizing bits in a string
+local function bits_to_string (a,b,c,d,e,f,g,h)
+ local function x(b) return b and "1" or "0" end
+ return ("%s%s%s%s %s%s%s%s"):format(x(a),x(b),x(c),x(d),x(e),x(f),x(g),x(h))
+end
+
+-- debug function for converting a 8-bit number as bit string
+local function byte_to_bit_string (b)
+ return bits_to_string(byte_to_bits(b))
+end
+
+-- debug function for converting a 32 bit number as bit string
+local function w32_to_bit_string(a)
+ if type(a) == "string" then return a end
+ local aa,ab,ac,ad = w32_to_bytes(a)
+ local s = byte_to_bit_string
+ return ("%s %s %s %s"):format(s(aa):reverse(),s(ab):reverse(),s(ac):reverse(),s(ad):reverse()):reverse()
+end
+
+-- bitwise "and" function for 2 8bit number
+local band = cache2arg (function(a,b)
+ local A,B,C,D,E,F,G,H = byte_to_bits(b)
+ local a,b,c,d,e,f,g,h = byte_to_bits(a)
+ return bits_to_byte(
+ A and a, B and b, C and c, D and d,
+ E and e, F and f, G and g, H and h)
+ end)
+
+-- bitwise "or" function for 2 8bit numbers
+local bor = cache2arg(function(a,b)
+ local A,B,C,D,E,F,G,H = byte_to_bits(b)
+ local a,b,c,d,e,f,g,h = byte_to_bits(a)
+ return bits_to_byte(
+ A or a, B or b, C or c, D or d,
+ E or e, F or f, G or g, H or h)
+ end)
+
+-- bitwise "xor" function for 2 8bit numbers
+local bxor = cache2arg(function(a,b)
+ local A,B,C,D,E,F,G,H = byte_to_bits(b)
+ local a,b,c,d,e,f,g,h = byte_to_bits(a)
+ return bits_to_byte(
+ A ~= a, B ~= b, C ~= c, D ~= d,
+ E ~= e, F ~= f, G ~= g, H ~= h)
+ end)
+
+-- bitwise complement for one 8bit number
+local function bnot (x)
+ return 255-(x % 256)
+end
+
+-- creates a function to combine to 32bit numbers using an 8bit combination function
+local function w32_comb(fn)
+ return function (a,b)
+ local aa,ab,ac,ad = w32_to_bytes(a)
+ local ba,bb,bc,bd = w32_to_bytes(b)
+ return bytes_to_w32(fn(aa,ba),fn(ab,bb),fn(ac,bc),fn(ad,bd))
+ end
+end
+
+-- create functions for and, xor and or, all for 2 32bit numbers
+local w32_and = w32_comb(band)
+local w32_xor = w32_comb(bxor)
+local w32_or = w32_comb(bor)
+
+-- xor function that may receive a variable number of arguments
+local function w32_xor_n (a,...)
+ local aa,ab,ac,ad = w32_to_bytes(a)
+ for i=1,select('#',...) do
+ local ba,bb,bc,bd = w32_to_bytes(select(i,...))
+ aa,ab,ac,ad = bxor(aa,ba),bxor(ab,bb),bxor(ac,bc),bxor(ad,bd)
+ end
+ return bytes_to_w32(aa,ab,ac,ad)
+end
+
+-- combining 3 32bit numbers through binary "or" operation
+local function w32_or3 (a,b,c)
+ local aa,ab,ac,ad = w32_to_bytes(a)
+ local ba,bb,bc,bd = w32_to_bytes(b)
+ local ca,cb,cc,cd = w32_to_bytes(c)
+ return bytes_to_w32(
+ bor(aa,bor(ba,ca)), bor(ab,bor(bb,cb)), bor(ac,bor(bc,cc)), bor(ad,bor(bd,cd))
+ )
+end
+
+-- binary complement for 32bit numbers
+local function w32_not (a)
+ return 4294967295-(a % 4294967296)
+end
+
+-- adding 2 32bit numbers, cutting off the remainder on 33th bit
+local function w32_add (a,b) return (a+b) % 4294967296 end
+
+-- adding n 32bit numbers, cutting off the remainder (again)
+local function w32_add_n (a,...)
+ for i=1,select('#',...) do
+ a = (a+select(i,...)) % 4294967296
+ end
+ return a
+end
+-- converting the number to a hexadecimal string
+local function w32_to_hexstring (w) return format("%08x",w) end
+
+-- calculating the SHA1 for some text
+function sha1.hex(msg)
+ local H0,H1,H2,H3,H4 = 0x67452301,0xEFCDAB89,0x98BADCFE,0x10325476,0xC3D2E1F0
+ local msg_len_in_bits = #msg * 8
+
+ local first_append = char(0x80) -- append a '1' bit plus seven '0' bits
+
+ local non_zero_message_bytes = #msg +1 +8 -- the +1 is the appended bit 1, the +8 are for the final appended length
+ local current_mod = non_zero_message_bytes % 64
+ local second_append = current_mod>0 and rep(char(0), 64 - current_mod) or ""
+
+ -- now to append the length as a 64-bit number.
+ local B1, R1 = modf(msg_len_in_bits / 0x01000000)
+ local B2, R2 = modf( 0x01000000 * R1 / 0x00010000)
+ local B3, R3 = modf( 0x00010000 * R2 / 0x00000100)
+ local B4 = 0x00000100 * R3
+
+ local L64 = char( 0) .. char( 0) .. char( 0) .. char( 0) -- high 32 bits
+ .. char(B1) .. char(B2) .. char(B3) .. char(B4) -- low 32 bits
+
+ msg = msg .. first_append .. second_append .. L64
+
+ assert(#msg % 64 == 0)
+
+ local chunks = #msg / 64
+
+ local W = { }
+ local start, A, B, C, D, E, f, K, TEMP
+ local chunk = 0
+
+ while chunk < chunks do
+ --
+ -- break chunk up into W[0] through W[15]
+ --
+ start,chunk = chunk * 64 + 1,chunk + 1
+
+ for t = 0, 15 do
+ W[t] = bytes_to_w32(msg:byte(start, start + 3))
+ start = start + 4
+ end
+
+ --
+ -- build W[16] through W[79]
+ --
+ for t = 16, 79 do
+ -- For t = 16 to 79 let Wt = S1(Wt-3 XOR Wt-8 XOR Wt-14 XOR Wt-16).
+ W[t] = w32_rot(1, w32_xor_n(W[t-3], W[t-8], W[t-14], W[t-16]))
+ end
+
+ A,B,C,D,E = H0,H1,H2,H3,H4
+
+ for t = 0, 79 do
+ if t <= 19 then
+ -- (B AND C) OR ((NOT B) AND D)
+ f = w32_or(w32_and(B, C), w32_and(w32_not(B), D))
+ K = 0x5A827999
+ elseif t <= 39 then
+ -- B XOR C XOR D
+ f = w32_xor_n(B, C, D)
+ K = 0x6ED9EBA1
+ elseif t <= 59 then
+ -- (B AND C) OR (B AND D) OR (C AND D
+ f = w32_or3(w32_and(B, C), w32_and(B, D), w32_and(C, D))
+ K = 0x8F1BBCDC
+ else
+ -- B XOR C XOR D
+ f = w32_xor_n(B, C, D)
+ K = 0xCA62C1D6
+ end
+
+ -- TEMP = S5(A) + ft(B,C,D) + E + Wt + Kt;
+ A,B,C,D,E = w32_add_n(w32_rot(5, A), f, E, W[t], K),
+ A, w32_rot(30, B), C, D
+ end
+ -- Let H0 = H0 + A, H1 = H1 + B, H2 = H2 + C, H3 = H3 + D, H4 = H4 + E.
+ H0,H1,H2,H3,H4 = w32_add(H0, A),w32_add(H1, B),w32_add(H2, C),w32_add(H3, D),w32_add(H4, E)
+ end
+ local f = w32_to_hexstring
+ return f(H0) .. f(H1) .. f(H2) .. f(H3) .. f(H4)
+end
+
+local function hex_to_binary(hex)
+ return hex:gsub('..', function(hexval)
+ return string.char(tonumber(hexval, 16))
+ end)
+end
+
+function sha1.bin(msg)
+ return hex_to_binary(sha1.hex(msg))
+end
+
+local xor_with_0x5c = {}
+local xor_with_0x36 = {}
+-- building the lookuptables ahead of time (instead of littering the source code
+-- with precalculated values)
+for i=0,0xff do
+ xor_with_0x5c[char(i)] = char(bxor(i,0x5c))
+ xor_with_0x36[char(i)] = char(bxor(i,0x36))
+end
+
+local blocksize = 64 -- 512 bits
+
+function sha1.hmacHex(key, text)
+ assert(type(key) == 'string', "key passed to hmacHex should be a string")
+ assert(type(text) == 'string', "text passed to hmacHex should be a string")
+
+ if #key > blocksize then
+ key = sha1.bin(key)
+ end
+
+ local key_xord_with_0x36 = key:gsub('.', xor_with_0x36) .. string.rep(string.char(0x36), blocksize - #key)
+ local key_xord_with_0x5c = key:gsub('.', xor_with_0x5c) .. string.rep(string.char(0x5c), blocksize - #key)
+
+ return sha1.hex(key_xord_with_0x5c .. sha1.bin(key_xord_with_0x36 .. text))
+end
+
+function sha1.hmacBin(key, text)
+ return hex_to_binary(sha1.hmacHex(key, text))
+end
+
+return sha1
+end)()
+
+local SCRIPT_NAME = "mpv_thumbnail_script"
+
+local default_cache_base = ON_WINDOWS and os.getenv("TEMP") or "/tmp/"
+
+local thumbnailer_options = {
+ -- The thumbnail directory
+ cache_directory = join_paths(default_cache_base, "mpv_thumbs_cache"),
+
+ ------------------------
+ -- Generation options --
+ ------------------------
+
+ -- Automatically generate the thumbnails on video load, without a keypress
+ autogenerate = true,
+
+ -- Only automatically thumbnail videos shorter than this (seconds)
+ autogenerate_max_duration = 3600, -- 1 hour
+
+ -- SHA1-sum filenames over this length
+ -- It's nice to know what files the thumbnails are (hence directory names)
+ -- but long URLs may approach filesystem limits.
+ hash_filename_length = 128,
+
+ -- Use mpv to generate thumbnail even if ffmpeg is found in PATH
+ -- ffmpeg does not handle ordered chapters (MKVs which rely on other MKVs)!
+ -- mpv is a bit slower, but has better support overall (eg. subtitles in the previews)
+ prefer_mpv = true,
+
+ -- Explicitly disable subtitles on the mpv sub-calls
+ mpv_no_sub = false,
+ -- Add a "--no-config" to the mpv sub-call arguments
+ mpv_no_config = false,
+ -- Add a "--profile=<mpv_profile>" to the mpv sub-call arguments
+ -- Use "" to disable
+ mpv_profile = "",
+ -- Output debug logs to <thumbnail_path>.log, ala <cache_directory>/<video_filename>/000000.bgra.log
+ -- The logs are removed after successful encodes, unless you set mpv_keep_logs below
+ mpv_logs = true,
+ -- Keep all mpv logs, even the succesfull ones
+ mpv_keep_logs = false,
+
+ -- Disable the built-in keybind ("T") to add your own
+ disable_keybinds = false,
+
+ ---------------------
+ -- Display options --
+ ---------------------
+
+ -- Move the thumbnail up or down
+ -- For example:
+ -- topbar/bottombar: 24
+ -- rest: 0
+ vertical_offset = 24,
+
+ -- Adjust background padding
+ -- Examples:
+ -- topbar: 0, 10, 10, 10
+ -- bottombar: 10, 0, 10, 10
+ -- slimbox/box: 10, 10, 10, 10
+ pad_top = 10,
+ pad_bot = 0,
+ pad_left = 10,
+ pad_right = 10,
+
+ -- If true, pad values are screen-pixels. If false, video-pixels.
+ pad_in_screenspace = true,
+ -- Calculate pad into the offset
+ offset_by_pad = true,
+
+ -- Background color in BBGGRR
+ background_color = "000000",
+ -- Alpha: 0 - fully opaque, 255 - transparent
+ background_alpha = 80,
+
+ -- Keep thumbnail on the screen near left or right side
+ constrain_to_screen = true,
+
+ -- Do not display the thumbnailing progress
+ hide_progress = false,
+
+ -----------------------
+ -- Thumbnail options --
+ -----------------------
+
+ -- The maximum dimensions of the thumbnails (pixels)
+ thumbnail_width = 200,
+ thumbnail_height = 200,
+
+ -- The thumbnail count target
+ -- (This will result in a thumbnail every ~10 seconds for a 25 minute video)
+ thumbnail_count = 150,
+
+ -- The above target count will be adjusted by the minimum and
+ -- maximum time difference between thumbnails.
+ -- The thumbnail_count will be used to calculate a target separation,
+ -- and min/max_delta will be used to constrict it.
+
+ -- In other words, thumbnails will be:
+ -- at least min_delta seconds apart (limiting the amount)
+ -- at most max_delta seconds apart (raising the amount if needed)
+ min_delta = 5,
+ -- 120 seconds aka 2 minutes will add more thumbnails when the video is over 5 hours!
+ max_delta = 90,
+
+
+ -- Overrides for remote urls (you generally want less thumbnails!)
+ -- Thumbnailing network paths will be done with mpv
+
+ -- Allow thumbnailing network paths (naive check for "://")
+ thumbnail_network = false,
+ -- Override thumbnail count, min/max delta
+ remote_thumbnail_count = 60,
+ remote_min_delta = 15,
+ remote_max_delta = 120,
+
+ -- Try to grab the raw stream and disable ytdl for the mpv subcalls
+ -- Much faster than passing the url to ytdl again, but may cause problems with some sites
+ remote_direct_stream = true,
+}
+
+read_options(thumbnailer_options, SCRIPT_NAME)
+local Thumbnailer = {
+ cache_directory = thumbnailer_options.cache_directory,
+
+ state = {
+ ready = false,
+ available = false,
+ enabled = false,
+
+ thumbnail_template = nil,
+
+ thumbnail_delta = nil,
+ thumbnail_count = 0,
+
+ thumbnail_size = nil,
+
+ finished_thumbnails = 0,
+
+ -- List of thumbnail states (from 1 to thumbnail_count)
+ -- ready: 1
+ -- in progress: 0
+ -- not ready: -1
+ thumbnails = {},
+
+ worker_input_path = nil,
+ -- Extra options for the workers
+ worker_extra = {},
+ },
+ -- Set in register_client
+ worker_register_timeout = nil,
+ -- A timer used to wait for more workers in case we have none
+ worker_wait_timer = nil,
+ workers = {}
+}
+
+function Thumbnailer:clear_state()
+ clear_table(self.state)
+ self.state.ready = false
+ self.state.available = false
+ self.state.finished_thumbnails = 0
+ self.state.thumbnails = {}
+ self.state.worker_extra = {}
+end
+
+
+function Thumbnailer:on_file_loaded()
+ self:clear_state()
+end
+
+function Thumbnailer:on_thumb_ready(index)
+ self.state.thumbnails[index] = 1
+
+ -- Full recount instead of a naive increment (let's be safe!)
+ self.state.finished_thumbnails = 0
+ for i, v in pairs(self.state.thumbnails) do
+ if v > 0 then
+ self.state.finished_thumbnails = self.state.finished_thumbnails + 1
+ end
+ end
+end
+
+function Thumbnailer:on_thumb_progress(index)
+ self.state.thumbnails[index] = math.max(self.state.thumbnails[index], 0)
+end
+
+function Thumbnailer:on_start_file()
+ -- Clear state when a new file is being loaded
+ self:clear_state()
+end
+
+function Thumbnailer:on_video_change(params)
+ -- Gather a new state when we get proper video-dec-params and our state is empty
+ if params ~= nil then
+ if not self.state.ready then
+ self:update_state()
+ end
+ end
+end
+
+
+function Thumbnailer:update_state()
+ msg.debug("Gathering video/thumbnail state")
+
+ self.state.thumbnail_delta = self:get_delta()
+ self.state.thumbnail_count = self:get_thumbnail_count(self.state.thumbnail_delta)
+
+ -- Prefill individual thumbnail states
+ for i = 1, self.state.thumbnail_count do
+ self.state.thumbnails[i] = -1
+ end
+
+ self.state.thumbnail_template, self.state.thumbnail_directory = self:get_thumbnail_template()
+ self.state.thumbnail_size = self:get_thumbnail_size()
+
+ self.state.ready = true
+
+ local file_path = mp.get_property_native("path")
+ self.state.is_remote = file_path:find("://") ~= nil
+
+ self.state.available = false
+
+ -- Make sure the file has video (and not just albumart)
+ local track_list = mp.get_property_native("track-list")
+ local has_video = false
+ for i, track in pairs(track_list) do
+ if track.type == "video" and not track.external and not track.albumart then
+ has_video = true
+ break
+ end
+ end
+
+ if has_video and self.state.thumbnail_delta ~= nil and self.state.thumbnail_size ~= nil and self.state.thumbnail_count > 0 then
+ self.state.available = true
+ end
+
+ msg.debug("Thumbnailer.state:", utils.to_string(self.state))
+
+end
+
+
+function Thumbnailer:get_thumbnail_template()
+ local file_path = mp.get_property_native("path")
+ local is_remote = file_path:find("://") ~= nil
+
+ local filename = mp.get_property_native("filename/no-ext")
+ local filesize = mp.get_property_native("file-size", 0)
+
+ if is_remote then
+ filesize = 0
+ end
+
+ filename = filename:gsub('[^a-zA-Z0-9_.%-\' ]', '')
+ -- Hash overly long filenames (most likely URLs)
+ if #filename > thumbnailer_options.hash_filename_length then
+ filename = sha1.hex(filename)
+ end
+
+ local file_key = ("%s-%d"):format(filename, filesize)
+
+ local thumbnail_directory = join_paths(self.cache_directory, file_key)
+ local file_template = join_paths(thumbnail_directory, "%06d.bgra")
+ return file_template, thumbnail_directory
+end
+
+
+function Thumbnailer:get_thumbnail_size()
+ local video_dec_params = mp.get_property_native("video-dec-params")
+ local video_width = video_dec_params.dw
+ local video_height = video_dec_params.dh
+ if not (video_width and video_height) then
+ return nil
+ end
+
+ local w, h
+ if video_width > video_height then
+ w = thumbnailer_options.thumbnail_width
+ h = math.floor(video_height * (w / video_width))
+ else
+ h = thumbnailer_options.thumbnail_height
+ w = math.floor(video_width * (h / video_height))
+ end
+ return { w=w, h=h }
+end
+
+
+function Thumbnailer:get_delta()
+ local file_path = mp.get_property_native("path")
+ local file_duration = mp.get_property_native("duration")
+ local is_seekable = mp.get_property_native("seekable")
+
+ -- Naive url check
+ local is_remote = file_path:find("://") ~= nil
+
+ local remote_and_disallowed = is_remote
+ if is_remote and thumbnailer_options.thumbnail_network then
+ remote_and_disallowed = false
+ end
+
+ if remote_and_disallowed or not is_seekable or not file_duration then
+ -- Not a local path (or remote thumbnails allowed), not seekable or lacks duration
+ return nil
+ end
+
+ local thumbnail_count = thumbnailer_options.thumbnail_count
+ local min_delta = thumbnailer_options.min_delta
+ local max_delta = thumbnailer_options.max_delta
+
+ if is_remote then
+ thumbnail_count = thumbnailer_options.remote_thumbnail_count
+ min_delta = thumbnailer_options.remote_min_delta
+ max_delta = thumbnailer_options.remote_max_delta
+ end
+
+ local target_delta = (file_duration / thumbnail_count)
+ local delta = math.max(min_delta, math.min(max_delta, target_delta))
+
+ return delta
+end
+
+
+function Thumbnailer:get_thumbnail_count(delta)
+ if delta == nil then
+ return 0
+ end
+ local file_duration = mp.get_property_native("duration")
+
+ return math.ceil(file_duration / delta)
+end
+
+function Thumbnailer:get_closest(thumbnail_index)
+ -- Given a 1-based index, find the closest available thumbnail and return it's 1-based index
+
+ -- Check the direct thumbnail index first
+ if self.state.thumbnails[thumbnail_index] > 0 then
+ return thumbnail_index
+ end
+
+ local min_distance = self.state.thumbnail_count + 1
+ local closest = nil
+
+ -- Naive, inefficient, lazy. But functional.
+ for index, value in pairs(self.state.thumbnails) do
+ local distance = math.abs(index - thumbnail_index)
+ if distance < min_distance and value > 0 then
+ min_distance = distance
+ closest = index
+ end
+ end
+ return closest
+end
+
+function Thumbnailer:get_thumbnail_index(time_position)
+ -- Returns a 1-based thumbnail index for the given timestamp (between 1 and thumbnail_count, inclusive)
+ if self.state.thumbnail_delta and (self.state.thumbnail_count and self.state.thumbnail_count > 0) then
+ return math.min(math.floor(time_position / self.state.thumbnail_delta) + 1, self.state.thumbnail_count)
+ else
+ return nil
+ end
+end
+
+function Thumbnailer:get_thumbnail_path(time_position)
+ -- Given a timestamp, return:
+ -- the closest available thumbnail path (if any)
+ -- the 1-based thumbnail index calculated from the timestamp
+ -- the 1-based thumbnail index of the closest available (and used) thumbnail
+ -- OR nil if thumbnails are not available.
+
+ local thumbnail_index = self:get_thumbnail_index(time_position)
+ if not thumbnail_index then return nil end
+
+ local closest = self:get_closest(thumbnail_index)
+
+ if closest ~= nil then
+ return self.state.thumbnail_template:format(closest-1), thumbnail_index, closest
+ else
+ return nil, thumbnail_index, nil
+ end
+end
+
+function Thumbnailer:register_client()
+ self.worker_register_timeout = mp.get_time() + 2
+
+ mp.register_script_message("mpv_thumbnail_script-ready", function(index, path)
+ self:on_thumb_ready(tonumber(index), path)
+ end)
+ mp.register_script_message("mpv_thumbnail_script-progress", function(index, path)
+ self:on_thumb_progress(tonumber(index), path)
+ end)
+
+ mp.register_script_message("mpv_thumbnail_script-worker", function(worker_name)
+ if not self.workers[worker_name] then
+ msg.debug("Registered worker", worker_name)
+ self.workers[worker_name] = true
+ mp.commandv("script-message-to", worker_name, "mpv_thumbnail_script-slaved")
+ end
+ end)
+
+ -- Notify workers to generate thumbnails when video loads/changes
+ -- This will be executed after the on_video_change (because it's registered after it)
+ mp.observe_property("video-dec-params", "native", function()
+ local duration = mp.get_property_native("duration")
+ local max_duration = thumbnailer_options.autogenerate_max_duration
+
+ if self.state.available and thumbnailer_options.autogenerate then
+ -- Notify if autogenerate is on and video is not too long
+ if duration < max_duration or max_duration == 0 then
+ self:start_worker_jobs()
+ end
+ end
+ end)
+
+ local thumb_script_key = not thumbnailer_options.disable_keybinds and "T" or nil
+ mp.add_key_binding(thumb_script_key, "generate-thumbnails", function()
+ if self.state.available then
+ mp.osd_message("Started thumbnailer jobs")
+ self:start_worker_jobs()
+ else
+ mp.osd_message("Thumbnailing unavailabe")
+ end
+ end)
+end
+
+function Thumbnailer:_create_thumbnail_job_order()
+ -- Returns a list of 1-based thumbnail indices in a job order
+ local used_frames = {}
+ local work_frames = {}
+
+ -- Pick frames in increasing frequency.
+ -- This way we can do a quick few passes over the video and then fill in the gaps.
+ for x = 6, 0, -1 do
+ local nth = (2^x)
+
+ for thi = 1, self.state.thumbnail_count, nth do
+ if not used_frames[thi] then
+ table.insert(work_frames, thi)
+ used_frames[thi] = true
+ end
+ end
+ end
+ return work_frames
+end
+
+function Thumbnailer:prepare_source_path()
+ local file_path = mp.get_property_native("path")
+
+ if self.state.is_remote and thumbnailer_options.remote_direct_stream then
+ -- Use the direct stream (possibly) provided by ytdl
+ -- This skips ytdl on the sub-calls, making the thumbnailing faster
+ -- Works well on YouTube, rest not really tested
+ file_path = mp.get_property_native("stream-path")
+
+ -- edl:// urls can get LONG. In which case, save the path (URL)
+ -- to a temporary file and use that instead.
+ local playlist_filename = join_paths(self.state.thumbnail_directory, "playlist.txt")
+
+ if #file_path > 8000 then
+ -- Path is too long for a playlist - just pass the original URL to
+ -- workers and allow ytdl
+ self.state.worker_extra.enable_ytdl = true
+ file_path = mp.get_property_native("path")
+ msg.warn("Falling back to original URL and ytdl due to LONG source path. This will be slow.")
+
+ elseif #file_path > 1024 then
+ local playlist_file = io.open(playlist_filename, "wb")
+ if not playlist_file then
+ msg.error(("Tried to write a playlist to %s but couldn't!"):format(playlist_file))
+ return false
+ end
+
+ playlist_file:write(file_path .. "\n")
+ playlist_file:close()
+
+ file_path = "--playlist=" .. playlist_filename
+ msg.warn("Using playlist workaround due to long source path")
+ end
+ end
+
+ self.state.worker_input_path = file_path
+ return true
+end
+
+function Thumbnailer:start_worker_jobs()
+ -- Create directory for the thumbnails, if needed
+ local l, err = utils.readdir(self.state.thumbnail_directory)
+ if err then
+ msg.debug("Creating thumbnail directory", self.state.thumbnail_directory)
+ create_directories(self.state.thumbnail_directory)
+ end
+
+ -- Try to prepare the source path for workers, and bail if unable to do so
+ if not self:prepare_source_path() then
+ return
+ end
+
+ local worker_list = {}
+ for worker_name in pairs(self.workers) do table.insert(worker_list, worker_name) end
+
+ local worker_count = #worker_list
+
+ -- In case we have a worker timer created already, clear it
+ -- (For example, if the video-dec-params change in quick succession or the user pressed T, etc)
+ if self.worker_wait_timer then
+ self.worker_wait_timer:stop()
+ end
+
+ if worker_count == 0 then
+ local now = mp.get_time()
+ if mp.get_time() > self.worker_register_timeout then
+ -- Workers have had their time to register but we have none!
+ local err = "No thumbnail workers found. Make sure you are not missing a script!"
+ msg.error(err)
+ mp.osd_message(err, 3)
+
+ else
+ -- We may be too early. Delay the work start a bit to try again.
+ msg.warn("No workers found. Waiting a bit more for them.")
+ -- Wait at least half a second
+ local wait_time = math.max(self.worker_register_timeout - now, 0.5)
+ self.worker_wait_timer = mp.add_timeout(wait_time, function() self:start_worker_jobs() end)
+ end
+
+ else
+ -- We have at least one worker. This may not be all of them, but they have had
+ -- their time to register; we've done our best waiting for them.
+ self.state.enabled = true
+
+ msg.debug( ("Splitting %d thumbnails amongst %d worker(s)"):format(self.state.thumbnail_count, worker_count) )
+
+ local frame_job_order = self:_create_thumbnail_job_order()
+ local worker_jobs = {}
+ for i = 1, worker_count do worker_jobs[worker_list[i]] = {} end
+
+ -- Split frames amongst the workers
+ for i, thumbnail_index in ipairs(frame_job_order) do
+ local worker_id = worker_list[ ((i-1) % worker_count) + 1 ]
+ table.insert(worker_jobs[worker_id], thumbnail_index)
+ end
+
+ local state_json_string = utils.format_json(self.state)
+ msg.debug("Giving workers state:", state_json_string)
+
+ for worker_name, worker_frames in pairs(worker_jobs) do
+ if #worker_frames > 0 then
+ local frames_json_string = utils.format_json(worker_frames)
+ msg.debug("Assigning job to", worker_name, frames_json_string)
+ mp.commandv("script-message-to", worker_name, "mpv_thumbnail_script-job", state_json_string, frames_json_string)
+ end
+ end
+ end
+end
+
+mp.register_event("start-file", function() Thumbnailer:on_start_file() end)
+mp.observe_property("video-dec-params", "native", function(name, params) Thumbnailer:on_video_change(params) end)
+--[[
+This is mpv's original player/lua/osc.lua patched to display thumbnails
+
+Sections are denoted with -- mpv_thumbnail_script.lua --
+Current osc.lua version: 97816bbef0f97cfda7abdbe560707481d5f68ccd
+]]--
+
+local assdraw = require 'mp.assdraw'
+local msg = require 'mp.msg'
+local opt = require 'mp.options'
+local utils = require 'mp.utils'
+
+
+--
+-- Parameters
+--
+
+-- default user option values
+-- do not touch, change them in osc.conf
+local user_opts = {
+ showwindowed = true, -- show OSC when windowed?
+ showfullscreen = true, -- show OSC when fullscreen?
+ scalewindowed = 1, -- scaling of the controller when windowed
+ scalefullscreen = 1, -- scaling of the controller when fullscreen
+ scaleforcedwindow = 2, -- scaling when rendered on a forced window
+ vidscale = true, -- scale the controller with the video?
+ valign = 0.8, -- vertical alignment, -1 (top) to 1 (bottom)
+ halign = 0, -- horizontal alignment, -1 (left) to 1 (right)
+ barmargin = 0, -- vertical margin of top/bottombar
+ boxalpha = 80, -- alpha of the background box,
+ -- 0 (opaque) to 255 (fully transparent)
+ hidetimeout = 500, -- duration in ms until the OSC hides if no
+ -- mouse movement. enforced non-negative for the
+ -- user, but internally negative is "always-on".
+ fadeduration = 200, -- duration of fade out in ms, 0 = no fade
+ deadzonesize = 0.5, -- size of deadzone
+ minmousemove = 0, -- minimum amount of pixels the mouse has to
+ -- move between ticks to make the OSC show up
+ iamaprogrammer = false, -- use native mpv values and disable OSC
+ -- internal track list management (and some
+ -- functions that depend on it)
+ layout = "bottombar",
+ seekbarstyle = "bar", -- slider (diamond marker), knob (circle
+ -- marker with guide), or bar (fill)
+ title = "${media-title}", -- string compatible with property-expansion
+ -- to be shown as OSC title
+ tooltipborder = 1, -- border of tooltip in bottom/topbar
+ timetotal = false, -- display total time instead of remaining time?
+ timems = false, -- display timecodes with milliseconds?
+ seekranges = true, -- display seek ranges?
+ visibility = "auto", -- only used at init to set visibility_mode(...)
+ boxmaxchars = 80, -- title crop threshold for box layout
+}
+
+-- read_options may modify hidetimeout, so save the original default value in
+-- case the user set hidetimeout < 0 and we need the default instead.
+local hidetimeout_def = user_opts.hidetimeout
+-- read options from config and command-line
+opt.read_options(user_opts, "osc")
+if user_opts.hidetimeout < 0 then
+ user_opts.hidetimeout = hidetimeout_def
+ msg.warn("hidetimeout cannot be negative. Using " .. user_opts.hidetimeout)
+end
+
+
+-- mpv_thumbnail_script.lua --
+
+-- Patch in msg.trace
+if not msg.trace then
+ msg.trace = function(...) return mp.log("trace", ...) end
+end
+
+-- Patch in utils.format_bytes_humanized
+if not utils.format_bytes_humanized then
+ utils.format_bytes_humanized = function(b)
+ local d = {"Bytes", "KiB", "MiB", "GiB", "TiB", "PiB"}
+ local i = 1
+ while b >= 1024 do
+ b = b / 1024
+ i = i + 1
+ end
+ return string.format("%0.2f %s", b, d[i] and d[i] or "*1024^" .. (i-1))
+ end
+end
+
+Thumbnailer:register_client()
+
+function get_thumbnail_y_offset(thumb_size, msy)
+ local layout = user_opts.layout
+ local offset = 0
+
+ if layout == "bottombar" then
+ offset = 15 --+ margin
+ elseif layout == "topbar" then
+ offset = -(thumb_size.h * msy + 15)
+ elseif layout == "box" then
+ offset = 15
+ elseif layout == "slimbox" then
+ offset = 12
+ end
+
+ return offset / msy
+end
+
+
+local osc_thumb_state = {
+ visible = false,
+ overlay_id = 1,
+
+ last_path = nil,
+ last_x = nil,
+ last_y = nil,
+}
+
+function hide_thumbnail()
+ osc_thumb_state.visible = false
+ osc_thumb_state.last_path = nil
+ mp.command_native({ "overlay-remove", osc_thumb_state.overlay_id })
+end
+
+function display_thumbnail(pos, value, ass)
+ -- If thumbnails are not available, bail
+ if not (Thumbnailer.state.enabled and Thumbnailer.state.available) then
+ return
+ end
+
+ local duration = mp.get_property_number("duration", nil)
+ if not ((duration == nil) or (value == nil)) then
+ target_position = duration * (value / 100)
+
+ local msx, msy = get_virt_scale_factor()
+ local osd_w, osd_h = mp.get_osd_size()
+
+ local thumb_size = Thumbnailer.state.thumbnail_size
+ local thumb_path, thumb_index, closest_index = Thumbnailer:get_thumbnail_path(target_position)
+
+ local thumbs_ready = Thumbnailer.state.finished_thumbnails
+ local thumbs_total = Thumbnailer.state.thumbnail_count
+ local perc = math.floor((thumbs_ready / thumbs_total) * 100)
+
+ local display_progress = thumbs_ready ~= thumbs_total and not thumbnailer_options.hide_progress
+
+ local vertical_offset = thumbnailer_options.vertical_offset
+ local padding = thumbnailer_options.background_padding
+
+ local pad = {
+ l = thumbnailer_options.pad_left, r = thumbnailer_options.pad_right,
+ t = thumbnailer_options.pad_top, b = thumbnailer_options.pad_bot
+ }
+ if thumbnailer_options.pad_in_screenspace then
+ pad.l = pad.l * msx
+ pad.r = pad.r * msx
+ pad.t = pad.t * msy
+ pad.b = pad.b * msy
+ end
+
+ if thumbnailer_options.offset_by_pad then
+ vertical_offset = vertical_offset + (user_opts.layout == "topbar" and pad.t or pad.b)
+ end
+
+ local ass_w = thumb_size.w * msx
+ local ass_h = thumb_size.h * msy
+ local y_offset = get_thumbnail_y_offset(thumb_size, 1)
+
+ -- Constrain thumbnail display to window
+ -- (ie. don't let it go off-screen left/right)
+ if thumbnailer_options.constrain_to_screen and osd_w > (ass_w + pad.l + pad.r)/msx then
+ local padded_left = (pad.l + (ass_w / 2))
+ local padded_right = (pad.r + (ass_w / 2))
+ if pos.x - padded_left < 0 then
+ pos.x = padded_left
+ elseif pos.x + padded_right > osd_w*msx then
+ pos.x = osd_w*msx - padded_right
+ end
+ end
+
+ local text_h = 30 * msy
+ local bg_h = ass_h + (display_progress and text_h or 0)
+ local bg_left = pos.x - ass_w/2
+ local framegraph_h = 10 * msy
+
+ local bg_top = nil
+ local text_top = nil
+ local framegraph_top = nil
+
+ if user_opts.layout == "topbar" then
+ bg_top = pos.y - ( y_offset + thumb_size.h ) + vertical_offset
+ text_top = bg_top + ass_h + framegraph_h
+ framegraph_top = bg_top + ass_h
+ vertical_offset = -vertical_offset
+ else
+ bg_top = pos.y - y_offset - bg_h - vertical_offset
+ text_top = bg_top
+ framegraph_top = bg_top + 20 * msy
+ end
+
+ if display_progress then
+ if user_opts.layout == "topbar" then
+ pad.b = math.max(0, pad.b - 30)
+ else
+ pad.t = math.max(0, pad.t - 30)
+ end
+ end
+
+
+
+ -- Draw background
+ ass:new_event()
+ ass:pos(bg_left, bg_top)
+ ass:append(("{\\bord0\\1c&H%s&\\1a&H%X&}"):format(thumbnailer_options.background_color, thumbnailer_options.background_alpha))
+ ass:draw_start()
+ ass:rect_cw(-pad.l, -pad.t, ass_w+pad.r, bg_h+pad.b)
+ ass:draw_stop()
+
+ if display_progress then
+
+ ass:new_event()
+ ass:pos(pos.x, text_top)
+ ass:an(8)
+ -- Scale text to correct size
+ ass:append(("{\\fs20\\bord0\\fscx%f\\fscy%f}"):format(100*msx, 100*msy))
+ ass:append(("%d%% - %d/%d"):format(perc, thumbs_ready, thumbs_total))
+
+ -- Draw the generation progress
+ local block_w = thumb_size.w * (Thumbnailer.state.thumbnail_delta / duration) * msy
+ local block_max_x = thumb_size.w * msy
+
+ -- Draw finished thumbnail blocks (white)
+ ass:new_event()
+ ass:pos(bg_left, framegraph_top)
+ ass:append(("{\\bord0\\1c&HFFFFFF&\\1a&H%X&"):format(0))
+ ass:draw_start(2)
+ for i, v in pairs(Thumbnailer.state.thumbnails) do
+ if i ~= closest_index and v > 0 then
+ ass:rect_cw((i-1)*block_w, 0, math.min(block_max_x, i*block_w), framegraph_h)
+ end
+ end
+ ass:draw_stop()
+
+ -- Draw in-progress thumbnail blocks (grayish green)
+ ass:new_event()
+ ass:pos(bg_left, framegraph_top)
+ ass:append(("{\\bord0\\1c&H44AA44&\\1a&H%X&"):format(0))
+ ass:draw_start(2)
+ for i, v in pairs(Thumbnailer.state.thumbnails) do
+ if i ~= closest_index and v == 0 then
+ ass:rect_cw((i-1)*block_w, 0, math.min(block_max_x, i*block_w), framegraph_h)
+ end
+ end
+ ass:draw_stop()
+
+ if closest_index ~= nil then
+ ass:new_event()
+ ass:pos(bg_left, framegraph_top)
+ ass:append(("{\\bord0\\1c&H4444FF&\\1a&H%X&"):format(0))
+ ass:draw_start(2)
+ ass:rect_cw((closest_index-1)*block_w, 0, math.min(block_max_x, closest_index*block_w), framegraph_h)
+ ass:draw_stop()
+ end
+ end
+
+ if thumb_path then
+ local overlay_y_offset = get_thumbnail_y_offset(thumb_size, msy)
+
+ local thumb_x = math.floor(pos.x / msx - thumb_size.w/2)
+ local thumb_y = math.floor(pos.y / msy - thumb_size.h - overlay_y_offset - vertical_offset/msy)
+
+ osc_thumb_state.visible = true
+ if not (osc_thumb_state.last_path == thumb_path and osc_thumb_state.last_x == thumb_x and osc_thumb_state.last_y == thumb_y) then
+ local overlay_add_args = {
+ "overlay-add", osc_thumb_state.overlay_id,
+ thumb_x, thumb_y,
+ thumb_path,
+ 0,
+ "bgra",
+ thumb_size.w, thumb_size.h,
+ 4 * thumb_size.w
+ }
+ mp.command_native(overlay_add_args)
+
+ osc_thumb_state.last_path = thumb_path
+ osc_thumb_state.last_x = thumb_x
+ osc_thumb_state.last_y = thumb_y
+ end
+ end
+ end
+end
+
+-- // mpv_thumbnail_script.lua // --
+
+
+local osc_param = { -- calculated by osc_init()
+ playresy = 0, -- canvas size Y
+ playresx = 0, -- canvas size X
+ display_aspect = 1,
+ unscaled_y = 0,
+ areas = {},
+}
+
+local osc_styles = {
+ bigButtons = "{\\blur0\\bord0\\1c&HFFFFFF\\3c&HFFFFFF\\fs50\\fnmpv-osd-symbols}",
+ smallButtonsL = "{\\blur0\\bord0\\1c&HFFFFFF\\3c&HFFFFFF\\fs19\\fnmpv-osd-symbols}",
+ smallButtonsLlabel = "{\\fscx105\\fscy105\\fn" .. mp.get_property("options/osd-font") .. "}",
+ smallButtonsR = "{\\blur0\\bord0\\1c&HFFFFFF\\3c&HFFFFFF\\fs30\\fnmpv-osd-symbols}",
+ topButtons = "{\\blur0\\bord0\\1c&HFFFFFF\\3c&HFFFFFF\\fs12\\fnmpv-osd-symbols}",
+
+ elementDown = "{\\1c&H999999}",
+ timecodes = "{\\blur0\\bord0\\1c&HFFFFFF\\3c&HFFFFFF\\fs20}",
+ vidtitle = "{\\blur0\\bord0\\1c&HFFFFFF\\3c&HFFFFFF\\fs12\\q2}",
+ box = "{\\rDefault\\blur0\\bord1\\1c&H000000\\3c&HFFFFFF}",
+
+ topButtonsBar = "{\\blur0\\bord0\\1c&HFFFFFF\\3c&HFFFFFF\\fs18\\fnmpv-osd-symbols}",
+ smallButtonsBar = "{\\blur0\\bord0\\1c&HFFFFFF\\3c&HFFFFFF\\fs28\\fnmpv-osd-symbols}",
+ timecodesBar = "{\\blur0\\bord0\\1c&HFFFFFF\\3c&HFFFFFF\\fs27}",
+ timePosBar = "{\\blur0\\bord".. user_opts.tooltipborder .."\\1c&HFFFFFF\\3c&H000000\\fs30}",
+ vidtitleBar = "{\\blur0\\bord0\\1c&HFFFFFF\\3c&HFFFFFF\\fs18\\q2}",
+}
+
+-- internal states, do not touch
+local state = {
+ showtime, -- time of last invocation (last mouse move)
+ osc_visible = false,
+ anistart, -- time when the animation started
+ anitype, -- current type of animation
+ animation, -- current animation alpha
+ mouse_down_counter = 0, -- used for softrepeat
+ active_element = nil, -- nil = none, 0 = background, 1+ = see elements[]
+ active_event_source = nil, -- the "button" that issued the current event
+ rightTC_trem = not user_opts.timetotal, -- if the right timecode should display total or remaining time
+ tc_ms = user_opts.timems, -- Should the timecodes display their time with milliseconds
+ mp_screen_sizeX, mp_screen_sizeY, -- last screen-resolution, to detect resolution changes to issue reINITs
+ initREQ = false, -- is a re-init request pending?
+ last_mouseX, last_mouseY, -- last mouse position, to detect significant mouse movement
+ message_text,
+ message_timeout,
+ fullscreen = false,
+ timer = nil,
+ cache_idle = false,
+ idle = false,
+ enabled = true,
+ input_enabled = true,
+ showhide_enabled = false,
+}
+
+
+
+
+--
+-- Helperfunctions
+--
+
+-- scale factor for translating between real and virtual ASS coordinates
+function get_virt_scale_factor()
+ local w, h = mp.get_osd_size()
+ if w <= 0 or h <= 0 then
+ return 0, 0
+ end
+ return osc_param.playresx / w, osc_param.playresy / h
+end
+
+-- return mouse position in virtual ASS coordinates (playresx/y)
+function get_virt_mouse_pos()
+ local sx, sy = get_virt_scale_factor()
+ local x, y = mp.get_mouse_pos()
+ return x * sx, y * sy
+end
+
+function set_virt_mouse_area(x0, y0, x1, y1, name)
+ local sx, sy = get_virt_scale_factor()
+ mp.set_mouse_area(x0 / sx, y0 / sy, x1 / sx, y1 / sy, name)
+end
+
+function scale_value(x0, x1, y0, y1, val)
+ local m = (y1 - y0) / (x1 - x0)
+ local b = y0 - (m * x0)
+ return (m * val) + b
+end
+
+-- returns hitbox spanning coordinates (top left, bottom right corner)
+-- according to alignment
+function get_hitbox_coords(x, y, an, w, h)
+
+ local alignments = {
+ [1] = function () return x, y-h, x+w, y end,
+ [2] = function () return x-(w/2), y-h, x+(w/2), y end,
+ [3] = function () return x-w, y-h, x, y end,
+
+ [4] = function () return x, y-(h/2), x+w, y+(h/2) end,
+ [5] = function () return x-(w/2), y-(h/2), x+(w/2), y+(h/2) end,
+ [6] = function () return x-w, y-(h/2), x, y+(h/2) end,
+
+ [7] = function () return x, y, x+w, y+h end,
+ [8] = function () return x-(w/2), y, x+(w/2), y+h end,
+ [9] = function () return x-w, y, x, y+h end,
+ }
+
+ return alignments[an]()
+end
+
+function get_hitbox_coords_geo(geometry)
+ return get_hitbox_coords(geometry.x, geometry.y, geometry.an,
+ geometry.w, geometry.h)
+end
+
+function get_element_hitbox(element)
+ return element.hitbox.x1, element.hitbox.y1,
+ element.hitbox.x2, element.hitbox.y2
+end
+
+function mouse_hit(element)
+ return mouse_hit_coords(get_element_hitbox(element))
+end
+
+function mouse_hit_coords(bX1, bY1, bX2, bY2)
+ local mX, mY = get_virt_mouse_pos()
+ return (mX >= bX1 and mX <= bX2 and mY >= bY1 and mY <= bY2)
+end
+
+function limit_range(min, max, val)
+ if val > max then
+ val = max
+ elseif val < min then
+ val = min
+ end
+ return val
+end
+
+-- translate value into element coordinates
+function get_slider_ele_pos_for(element, val)
+
+ local ele_pos = scale_value(
+ element.slider.min.value, element.slider.max.value,
+ element.slider.min.ele_pos, element.slider.max.ele_pos,
+ val)
+
+ return limit_range(
+ element.slider.min.ele_pos, element.slider.max.ele_pos,
+ ele_pos)
+end
+
+-- translates global (mouse) coordinates to value
+function get_slider_value_at(element, glob_pos)
+
+ local val = scale_value(
+ element.slider.min.glob_pos, element.slider.max.glob_pos,
+ element.slider.min.value, element.slider.max.value,
+ glob_pos)
+
+ return limit_range(
+ element.slider.min.value, element.slider.max.value,
+ val)
+end
+
+-- get value at current mouse position
+function get_slider_value(element)
+ return get_slider_value_at(element, get_virt_mouse_pos())
+end
+
+function countone(val)
+ if not (user_opts.iamaprogrammer) then
+ val = val + 1
+ end
+ return val
+end
+
+-- align: -1 .. +1
+-- frame: size of the containing area
+-- obj: size of the object that should be positioned inside the area
+-- margin: min. distance from object to frame (as long as -1 <= align <= +1)
+function get_align(align, frame, obj, margin)
+ return (frame / 2) + (((frame / 2) - margin - (obj / 2)) * align)
+end
+
+-- multiplies two alpha values, formular can probably be improved
+function mult_alpha(alphaA, alphaB)
+ return 255 - (((1-(alphaA/255)) * (1-(alphaB/255))) * 255)
+end
+
+function add_area(name, x1, y1, x2, y2)
+ -- create area if needed
+ if (osc_param.areas[name] == nil) then
+ osc_param.areas[name] = {}
+ end
+ table.insert(osc_param.areas[name], {x1=x1, y1=y1, x2=x2, y2=y2})
+end
+
+
+--
+-- Tracklist Management
+--
+
+local nicetypes = {video = "Video", audio = "Audio", sub = "Subtitle"}
+
+-- updates the OSC internal playlists, should be run each time the track-layout changes
+function update_tracklist()
+ local tracktable = mp.get_property_native("track-list", {})
+
+ -- by osc_id
+ tracks_osc = {}
+ tracks_osc.video, tracks_osc.audio, tracks_osc.sub = {}, {}, {}
+ -- by mpv_id
+ tracks_mpv = {}
+ tracks_mpv.video, tracks_mpv.audio, tracks_mpv.sub = {}, {}, {}
+ for n = 1, #tracktable do
+ if not (tracktable[n].type == "unknown") then
+ local type = tracktable[n].type
+ local mpv_id = tonumber(tracktable[n].id)
+
+ -- by osc_id
+ table.insert(tracks_osc[type], tracktable[n])
+
+ -- by mpv_id
+ tracks_mpv[type][mpv_id] = tracktable[n]
+ tracks_mpv[type][mpv_id].osc_id = #tracks_osc[type]
+ end
+ end
+end
+
+-- return a nice list of tracks of the given type (video, audio, sub)
+function get_tracklist(type)
+ local msg = "Available " .. nicetypes[type] .. " Tracks: "
+ if #tracks_osc[type] == 0 then
+ msg = msg .. "none"
+ else
+ for n = 1, #tracks_osc[type] do
+ local track = tracks_osc[type][n]
+ local lang, title, selected = "unknown", "", "○"
+ if not(track.lang == nil) then lang = track.lang end
+ if not(track.title == nil) then title = track.title end
+ if (track.id == tonumber(mp.get_property(type))) then
+ selected = "●"
+ end
+ msg = msg.."\n"..selected.." "..n..": ["..lang.."] "..title
+ end
+ end
+ return msg
+end
+
+-- relatively change the track of given <type> by <next> tracks
+ --(+1 -> next, -1 -> previous)
+function set_track(type, next)
+ local current_track_mpv, current_track_osc
+ if (mp.get_property(type) == "no") then
+ current_track_osc = 0
+ else
+ current_track_mpv = tonumber(mp.get_property(type))
+ current_track_osc = tracks_mpv[type][current_track_mpv].osc_id
+ end
+ local new_track_osc = (current_track_osc + next) % (#tracks_osc[type] + 1)
+ local new_track_mpv
+ if new_track_osc == 0 then
+ new_track_mpv = "no"
+ else
+ new_track_mpv = tracks_osc[type][new_track_osc].id
+ end
+
+ mp.commandv("set", type, new_track_mpv)
+
+ if (new_track_osc == 0) then
+ show_message(nicetypes[type] .. " Track: none")
+ else
+ show_message(nicetypes[type] .. " Track: "
+ .. new_track_osc .. "/" .. #tracks_osc[type]
+ .. " [".. (tracks_osc[type][new_track_osc].lang or "unknown") .."] "
+ .. (tracks_osc[type][new_track_osc].title or ""))
+ end
+end
+
+-- get the currently selected track of <type>, OSC-style counted
+function get_track(type)
+ local track = mp.get_property(type)
+ if track ~= "no" and track ~= nil then
+ local tr = tracks_mpv[type][tonumber(track)]
+ if tr then
+ return tr.osc_id
+ end
+ end
+ return 0
+end
+
+
+--
+-- Element Management
+--
+
+local elements = {}
+
+function prepare_elements()
+
+ -- remove elements without layout or invisble
+ local elements2 = {}
+ for n, element in pairs(elements) do
+ if not (element.layout == nil) and (element.visible) then
+ table.insert(elements2, element)
+ end
+ end
+ elements = elements2
+
+ function elem_compare (a, b)
+ return a.layout.layer < b.layout.layer
+ end
+
+ table.sort(elements, elem_compare)
+
+
+ for _,element in pairs(elements) do
+
+ local elem_geo = element.layout.geometry
+
+ -- Calculate the hitbox
+ local bX1, bY1, bX2, bY2 = get_hitbox_coords_geo(elem_geo)
+ element.hitbox = {x1 = bX1, y1 = bY1, x2 = bX2, y2 = bY2}
+
+ local style_ass = assdraw.ass_new()
+
+ -- prepare static elements
+ style_ass:append("{}") -- hack to troll new_event into inserting a \n
+ style_ass:new_event()
+ style_ass:pos(elem_geo.x, elem_geo.y)
+ style_ass:an(elem_geo.an)
+ style_ass:append(element.layout.style)
+
+ element.style_ass = style_ass
+
+ local static_ass = assdraw.ass_new()
+
+
+ if (element.type == "box") then
+ --draw box
+ static_ass:draw_start()
+ static_ass:round_rect_cw(0, 0, elem_geo.w, elem_geo.h,
+ element.layout.box.radius)
+ static_ass:draw_stop()
+
+
+ elseif (element.type == "slider") then
+ --draw static slider parts
+
+ local slider_lo = element.layout.slider
+ -- offset between element outline and drag-area
+ local foV = slider_lo.border + slider_lo.gap
+
+ -- calculate positions of min and max points
+ if (slider_lo.stype == "slider") or
+ (slider_lo.stype == "knob") then
+ element.slider.min.ele_pos = elem_geo.h / 2
+ element.slider.max.ele_pos = elem_geo.w - (elem_geo.h / 2)
+
+ elseif (slider_lo.stype == "bar") then
+ element.slider.min.ele_pos =
+ slider_lo.border + slider_lo.gap
+ element.slider.max.ele_pos =
+ elem_geo.w - (slider_lo.border + slider_lo.gap)
+ end
+
+ element.slider.min.glob_pos =
+ element.hitbox.x1 + element.slider.min.ele_pos
+ element.slider.max.glob_pos =
+ element.hitbox.x1 + element.slider.max.ele_pos
+
+ -- -- --
+
+ static_ass:draw_start()
+
+ -- the box
+ static_ass:rect_cw(0, 0, elem_geo.w, elem_geo.h);
+
+ -- the "hole"
+ static_ass:rect_ccw(slider_lo.border, slider_lo.border,
+ elem_geo.w - slider_lo.border, elem_geo.h - slider_lo.border)
+
+ -- marker nibbles
+ if not (element.slider.markerF == nil) and (slider_lo.gap > 0) then
+ local markers = element.slider.markerF()
+ for _,marker in pairs(markers) do
+ if (marker > element.slider.min.value) and
+ (marker < element.slider.max.value) then
+
+ local s = get_slider_ele_pos_for(element, marker)
+
+ if (slider_lo.gap > 1) then -- draw triangles
+
+ local a = slider_lo.gap / 0.5 --0.866
+
+ --top
+ if (slider_lo.nibbles_top) then
+ static_ass:move_to(s - (a/2), slider_lo.border)
+ static_ass:line_to(s + (a/2), slider_lo.border)
+ static_ass:line_to(s, foV)
+ end
+
+ --bottom
+ if (slider_lo.nibbles_bottom) then
+ static_ass:move_to(s - (a/2),
+ elem_geo.h - slider_lo.border)
+ static_ass:line_to(s,
+ elem_geo.h - foV)
+ static_ass:line_to(s + (a/2),
+ elem_geo.h - slider_lo.border)
+ end
+
+ else -- draw 2x1px nibbles
+
+ --top
+ if (slider_lo.nibbles_top) then
+ static_ass:rect_cw(s - 1, slider_lo.border,
+ s + 1, slider_lo.border + slider_lo.gap);
+ end
+
+ --bottom
+ if (slider_lo.nibbles_bottom) then
+ static_ass:rect_cw(s - 1,
+ elem_geo.h -slider_lo.border -slider_lo.gap,
+ s + 1, elem_geo.h - slider_lo.border);
+ end
+ end
+ end
+ end
+ end
+ end
+
+ element.static_ass = static_ass
+
+
+ -- if the element is supposed to be disabled,
+ -- style it accordingly and kill the eventresponders
+ if not (element.enabled) then
+ element.layout.alpha[1] = 136
+ element.eventresponder = nil
+ end
+ end
+end
+
+
+--
+-- Element Rendering
+--
+
+function render_elements(master_ass)
+
+ for n=1, #elements do
+ local element = elements[n]
+
+ local style_ass = assdraw.ass_new()
+ style_ass:merge(element.style_ass)
+
+ --alpha
+ local ar = element.layout.alpha
+ if not (state.animation == nil) then
+ ar = {}
+ for ai, av in pairs(element.layout.alpha) do
+ ar[ai] = mult_alpha(av, state.animation)
+ end
+ end
+
+ style_ass:append(string.format("{\\1a&H%X&\\2a&H%X&\\3a&H%X&\\4a&H%X&}",
+ ar[1], ar[2], ar[3], ar[4]))
+
+ if element.eventresponder and (state.active_element == n) then
+
+ -- run render event functions
+ if not (element.eventresponder.render == nil) then
+ element.eventresponder.render(element)
+ end
+
+ if mouse_hit(element) then
+ -- mouse down styling
+ if (element.styledown) then
+ style_ass:append(osc_styles.elementDown)
+ end
+
+ if (element.softrepeat) and (state.mouse_down_counter >= 15
+ and state.mouse_down_counter % 5 == 0) then
+
+ element.eventresponder[state.active_event_source.."_down"](element)
+ end
+ state.mouse_down_counter = state.mouse_down_counter + 1
+ end
+
+ end
+
+ local elem_ass = assdraw.ass_new()
+
+ elem_ass:merge(style_ass)
+
+ if not (element.type == "button") then
+ elem_ass:merge(element.static_ass)
+ end
+
+ if (element.type == "slider") then
+
+ local slider_lo = element.layout.slider
+ local elem_geo = element.layout.geometry
+ local s_min = element.slider.min.value
+ local s_max = element.slider.max.value
+
+ -- draw pos marker
+ local pos = element.slider.posF()
+
+ if not (pos == nil) then
+
+ local foV = slider_lo.border + slider_lo.gap
+ local foH = 0
+ if (slider_lo.stype == "slider") or
+ (slider_lo.stype == "knob") then
+ foH = elem_geo.h / 2
+ elseif (slider_lo.stype == "bar") then
+ foH = slider_lo.border + slider_lo.gap
+ end
+
+ local xp = get_slider_ele_pos_for(element, pos)
+
+ -- the filling
+ local innerH = elem_geo.h - (2*foV)
+
+ if (slider_lo.stype == "bar") then
+ elem_ass:rect_cw(foH, foV, xp, elem_geo.h - foV)
+ elseif (slider_lo.stype == "slider") then
+ elem_ass:move_to(xp, foV)
+ elem_ass:line_to(xp+(innerH/2), (innerH/2)+foV)
+ elem_ass:line_to(xp, (innerH)+foV)
+ elem_ass:line_to(xp-(innerH/2), (innerH/2)+foV)
+ elseif (slider_lo.stype == "knob") then
+ elem_ass:rect_cw(xp, (9*innerH/20) + foV,
+ elem_geo.w - foH, (11*innerH/20) + foV)
+ elem_ass:rect_cw(foH, (3*innerH/8) + foV,
+ xp, (5*innerH/8) + foV)
+ elem_ass:round_rect_cw(xp - innerH/2, foV,
+ xp + innerH/2, foV + innerH, innerH/2.0)
+ end
+ end
+
+ -- seek ranges
+ local seekRanges = element.slider.seekRangesF()
+ if not (seekRanges == nil) then
+ for _,range in pairs(seekRanges) do
+ local pstart = get_slider_ele_pos_for(element, range["start"])
+ local pend = get_slider_ele_pos_for(element, range["end"])
+ elem_ass:rect_ccw(pstart, (elem_geo.h/2)-1, pend, (elem_geo.h/2) + 1)
+ end
+ end
+
+ elem_ass:draw_stop()
+
+ -- add tooltip
+ if not (element.slider.tooltipF == nil) then
+
+ if mouse_hit(element) then
+ local sliderpos = get_slider_value(element)
+ local tooltiplabel = element.slider.tooltipF(sliderpos)
+
+ local an = slider_lo.tooltip_an
+
+ local ty
+
+ if (an == 2) then
+ ty = element.hitbox.y1 - slider_lo.border
+ else
+ ty = element.hitbox.y1 + elem_geo.h/2
+ end
+
+ local tx = get_virt_mouse_pos()
+ if (slider_lo.adjust_tooltip) then
+ if (an == 2) then
+ if (sliderpos < (s_min + 3)) then
+ an = an - 1
+ elseif (sliderpos > (s_max - 3)) then
+ an = an + 1
+ end
+ elseif (sliderpos > (s_max-s_min)/2) then
+ an = an + 1
+ tx = tx - 5
+ else
+ an = an - 1
+ tx = tx + 10
+ end
+ end
+
+ -- tooltip label
+ elem_ass:new_event()
+ elem_ass:pos(tx, ty)
+ elem_ass:an(an)
+ elem_ass:append(slider_lo.tooltip_style)
+
+ --alpha
+ local ar = slider_lo.alpha
+ if not (state.animation == nil) then
+ ar = {}
+ for ai, av in pairs(slider_lo.alpha) do
+ ar[ai] = mult_alpha(av, state.animation)
+ end
+ end
+ elem_ass:append(string.format("{\\1a&H%X&\\2a&H%X&\\3a&H%X&\\4a&H%X&}",
+ ar[1], ar[2], ar[3], ar[4]))
+
+ elem_ass:append(tooltiplabel)
+
+ -- mpv_thumbnail_script.lua --
+ display_thumbnail({x=get_virt_mouse_pos(), y=ty, a=an}, sliderpos, elem_ass)
+ -- // mpv_thumbnail_script.lua // --
+
+ end
+ end
+
+ elseif (element.type == "button") then
+
+ local buttontext
+ if type(element.content) == "function" then
+ buttontext = element.content() -- function objects
+ elseif not (element.content == nil) then
+ buttontext = element.content -- text objects
+ end
+
+ local maxchars = element.layout.button.maxchars
+ if not (maxchars == nil) and (#buttontext > maxchars) then
+ local max_ratio = 1.25 -- up to 25% more chars while shrinking
+ local limit = math.max(0, math.floor(maxchars * max_ratio) - 3)
+ if (#buttontext > limit) then
+ while (#buttontext > limit) do
+ buttontext = buttontext:gsub(".[\128-\191]*$", "")
+ end
+ buttontext = buttontext .. "..."
+ end
+ local _, nchars2 = buttontext:gsub(".[\128-\191]*", "")
+ local stretch = (maxchars/#buttontext)*100
+ buttontext = string.format("{\\fscx%f}",
+ (maxchars/#buttontext)*100) .. buttontext
+ end
+
+ elem_ass:append(buttontext)
+ end
+
+ master_ass:merge(elem_ass)
+ end
+end
+
+--
+-- Message display
+--
+
+-- pos is 1 based
+function limited_list(prop, pos)
+ local proplist = mp.get_property_native(prop, {})
+ local count = #proplist
+ if count == 0 then
+ return count, proplist
+ end
+
+ local fs = tonumber(mp.get_property('options/osd-font-size'))
+ local max = math.ceil(osc_param.unscaled_y*0.75 / fs)
+ if max % 2 == 0 then
+ max = max - 1
+ end
+ local delta = math.ceil(max / 2) - 1
+ local begi = math.max(math.min(pos - delta, count - max + 1), 1)
+ local endi = math.min(begi + max - 1, count)
+
+ local reslist = {}
+ for i=begi, endi do
+ local item = proplist[i]
+ item.current = (i == pos) and true or nil
+ table.insert(reslist, item)
+ end
+ return count, reslist
+end
+
+function get_playlist()
+ local pos = mp.get_property_number('playlist-pos', 0) + 1
+ local count, limlist = limited_list('playlist', pos)
+ if count == 0 then
+ return 'Empty playlist.'
+ end
+
+ local message = string.format('Playlist [%d/%d]:\n', pos, count)
+ for i, v in ipairs(limlist) do
+ local title = v.title
+ local _, filename = utils.split_path(v.filename)
+ if title == nil then
+ title = filename
+ end
+ message = string.format('%s %s %s\n', message,
+ (v.current and '●' or '○'), title)
+ end
+ return message
+end
+
+function get_chapterlist()
+ local pos = mp.get_property_number('chapter', 0) + 1
+ local count, limlist = limited_list('chapter-list', pos)
+ if count == 0 then
+ return 'No chapters.'
+ end
+
+ local message = string.format('Chapters [%d/%d]:\n', pos, count)
+ for i, v in ipairs(limlist) do
+ local time = mp.format_time(v.time)
+ local title = v.title
+ if title == nil then
+ title = string.format('Chapter %02d', i)
+ end
+ message = string.format('%s[%s] %s %s\n', message, time,
+ (v.current and '●' or '○'), title)
+ end
+ return message
+end
+
+function show_message(text, duration)
+
+ --print("text: "..text.." duration: " .. duration)
+ if duration == nil then
+ duration = tonumber(mp.get_property("options/osd-duration")) / 1000
+ elseif not type(duration) == "number" then
+ print("duration: " .. duration)
+ end
+
+ -- cut the text short, otherwise the following functions
+ -- may slow down massively on huge input
+ text = string.sub(text, 0, 4000)
+
+ -- replace actual linebreaks with ASS linebreaks
+ text = string.gsub(text, "\n", "\\N")
+
+ state.message_text = text
+ state.message_timeout = mp.get_time() + duration
+end
+
+function render_message(ass)
+ if not(state.message_timeout == nil) and not(state.message_text == nil)
+ and state.message_timeout > mp.get_time() then
+ local _, lines = string.gsub(state.message_text, "\\N", "")
+
+ local fontsize = tonumber(mp.get_property("options/osd-font-size"))
+ local outline = tonumber(mp.get_property("options/osd-border-size"))
+ local maxlines = math.ceil(osc_param.unscaled_y*0.75 / fontsize)
+ local counterscale = osc_param.playresy / osc_param.unscaled_y
+
+ fontsize = fontsize * counterscale / math.max(0.65 + math.min(lines/maxlines, 1), 1)
+ outline = outline * counterscale / math.max(0.75 + math.min(lines/maxlines, 1)/2, 1)
+
+ local style = "{\\bord" .. outline .. "\\fs" .. fontsize .. "}"
+
+
+ ass:new_event()
+ ass:append(style .. state.message_text)
+ else
+ state.message_text = nil
+ state.message_timeout = nil
+ end
+end
+
+--
+-- Initialisation and Layout
+--
+
+function new_element(name, type)
+ elements[name] = {}
+ elements[name].type = type
+
+ -- add default stuff
+ elements[name].eventresponder = {}
+ elements[name].visible = true
+ elements[name].enabled = true
+ elements[name].softrepeat = false
+ elements[name].styledown = (type == "button")
+ elements[name].state = {}
+
+ if (type == "slider") then
+ elements[name].slider = {min = {value = 0}, max = {value = 100}}
+ end
+
+
+ return elements[name]
+end
+
+function add_layout(name)
+ if not (elements[name] == nil) then
+ -- new layout
+ elements[name].layout = {}
+
+ -- set layout defaults
+ elements[name].layout.layer = 50
+ elements[name].layout.alpha = {[1] = 0, [2] = 255, [3] = 255, [4] = 255}
+
+ if (elements[name].type == "button") then
+ elements[name].layout.button = {
+ maxchars = nil,
+ }
+ elseif (elements[name].type == "slider") then
+ -- slider defaults
+ elements[name].layout.slider = {
+ border = 1,
+ gap = 1,
+ nibbles_top = true,
+ nibbles_bottom = true,
+ stype = "slider",
+ adjust_tooltip = true,
+ tooltip_style = "",
+ tooltip_an = 2,
+ alpha = {[1] = 0, [2] = 255, [3] = 88, [4] = 255},
+ }
+ elseif (elements[name].type == "box") then
+ elements[name].layout.box = {radius = 0}
+ end
+
+ return elements[name].layout
+ else
+ msg.error("Can't add_layout to element \""..name.."\", doesn't exist.")
+ end
+end
+
+--
+-- Layouts
+--
+
+local layouts = {}
+
+-- Classic box layout
+layouts["box"] = function ()
+
+ local osc_geo = {
+ w = 550, -- width
+ h = 138, -- height
+ r = 10, -- corner-radius
+ p = 15, -- padding
+ }
+
+ -- make sure the OSC actually fits into the video
+ if (osc_param.playresx < (osc_geo.w + (2 * osc_geo.p))) then
+ osc_param.playresy = (osc_geo.w+(2*osc_geo.p))/osc_param.display_aspect
+ osc_param.playresx = osc_param.playresy * osc_param.display_aspect
+ end
+
+ -- position of the controller according to video aspect and valignment
+ local posX = math.floor(get_align(user_opts.halign, osc_param.playresx,
+ osc_geo.w, 0))
+ local posY = math.floor(get_align(user_opts.valign, osc_param.playresy,
+ osc_geo.h, 0))
+
+ -- position offset for contents aligned at the borders of the box
+ local pos_offsetX = (osc_geo.w - (2*osc_geo.p)) / 2
+ local pos_offsetY = (osc_geo.h - (2*osc_geo.p)) / 2
+
+ osc_param.areas = {} -- delete areas
+
+ -- area for active mouse input
+ add_area("input", get_hitbox_coords(posX, posY, 5, osc_geo.w, osc_geo.h))
+
+ -- area for show/hide
+ local sh_area_y0, sh_area_y1
+ if user_opts.valign > 0 then
+ -- deadzone above OSC
+ sh_area_y0 = get_align(-1 + (2*user_opts.deadzonesize),
+ posY - (osc_geo.h / 2), 0, 0)
+ sh_area_y1 = osc_param.playresy
+ else
+ -- deadzone below OSC
+ sh_area_y0 = 0
+ sh_area_y1 = (posY + (osc_geo.h / 2)) +
+ get_align(1 - (2*user_opts.deadzonesize),
+ osc_param.playresy - (posY + (osc_geo.h / 2)), 0, 0)
+ end
+ add_area("showhide", 0, sh_area_y0, osc_param.playresx, sh_area_y1)
+
+ -- fetch values
+ local osc_w, osc_h, osc_r, osc_p =
+ osc_geo.w, osc_geo.h, osc_geo.r, osc_geo.p
+
+ local lo
+
+ --
+ -- Background box
+ --
+
+ new_element("bgbox", "box")
+ lo = add_layout("bgbox")
+
+ lo.geometry = {x = posX, y = posY, an = 5, w = osc_w, h = osc_h}
+ lo.layer = 10
+ lo.style = osc_styles.box
+ lo.alpha[1] = user_opts.boxalpha
+ lo.alpha[3] = user_opts.boxalpha
+ lo.box.radius = osc_r
+
+ --
+ -- Title row
+ --
+
+ local titlerowY = posY - pos_offsetY - 10
+
+ lo = add_layout("title")
+ lo.geometry = {x = posX, y = titlerowY, an = 8, w = 496, h = 12}
+ lo.style = osc_styles.vidtitle
+ lo.button.maxchars = user_opts.boxmaxchars
+
+ lo = add_layout("pl_prev")
+ lo.geometry =
+ {x = (posX - pos_offsetX), y = titlerowY, an = 7, w = 12, h = 12}
+ lo.style = osc_styles.topButtons
+
+ lo = add_layout("pl_next")
+ lo.geometry =
+ {x = (posX + pos_offsetX), y = titlerowY, an = 9, w = 12, h = 12}
+ lo.style = osc_styles.topButtons
+
+ --
+ -- Big buttons
+ --
+
+ local bigbtnrowY = posY - pos_offsetY + 35
+ local bigbtndist = 60
+
+ lo = add_layout("playpause")
+ lo.geometry =
+ {x = posX, y = bigbtnrowY, an = 5, w = 40, h = 40}
+ lo.style = osc_styles.bigButtons
+
+ lo = add_layout("skipback")
+ lo.geometry =
+ {x = posX - bigbtndist, y = bigbtnrowY, an = 5, w = 40, h = 40}
+ lo.style = osc_styles.bigButtons
+
+ lo = add_layout("skipfrwd")
+ lo.geometry =
+ {x = posX + bigbtndist, y = bigbtnrowY, an = 5, w = 40, h = 40}
+ lo.style = osc_styles.bigButtons
+
+ lo = add_layout("ch_prev")
+ lo.geometry =
+ {x = posX - (bigbtndist * 2), y = bigbtnrowY, an = 5, w = 40, h = 40}
+ lo.style = osc_styles.bigButtons
+
+ lo = add_layout("ch_next")
+ lo.geometry =
+ {x = posX + (bigbtndist * 2), y = bigbtnrowY, an = 5, w = 40, h = 40}
+ lo.style = osc_styles.bigButtons
+
+ lo = add_layout("cy_audio")
+ lo.geometry =
+ {x = posX - pos_offsetX, y = bigbtnrowY, an = 1, w = 70, h = 18}
+ lo.style = osc_styles.smallButtonsL
+
+ lo = add_layout("cy_sub")
+ lo.geometry =
+ {x = posX - pos_offsetX, y = bigbtnrowY, an = 7, w = 70, h = 18}
+ lo.style = osc_styles.smallButtonsL
+
+ lo = add_layout("tog_fs")
+ lo.geometry =
+ {x = posX+pos_offsetX - 25, y = bigbtnrowY, an = 4, w = 25, h = 25}
+ lo.style = osc_styles.smallButtonsR
+
+ lo = add_layout("volume")
+ lo.geometry =
+ {x = posX+pos_offsetX - (25 * 2) - osc_geo.p,
+ y = bigbtnrowY, an = 4, w = 25, h = 25}
+ lo.style = osc_styles.smallButtonsR
+
+ --
+ -- Seekbar
+ --
+
+ lo = add_layout("seekbar")
+ lo.geometry =
+ {x = posX, y = posY+pos_offsetY-22, an = 2, w = pos_offsetX*2, h = 15}
+ lo.style = osc_styles.timecodes
+ lo.slider.tooltip_style = osc_styles.vidtitle
+ lo.slider.stype = user_opts["seekbarstyle"]
+ if lo.slider.stype == "knob" then
+ lo.slider.border = 0
+ end
+
+ --
+ -- Timecodes + Cache
+ --
+
+ local bottomrowY = posY + pos_offsetY - 5
+
+ lo = add_layout("tc_left")
+ lo.geometry =
+ {x = posX - pos_offsetX, y = bottomrowY, an = 4, w = 110, h = 18}
+ lo.style = osc_styles.timecodes
+
+ lo = add_layout("tc_right")
+ lo.geometry =
+ {x = posX + pos_offsetX, y = bottomrowY, an = 6, w = 110, h = 18}
+ lo.style = osc_styles.timecodes
+
+ lo = add_layout("cache")
+ lo.geometry =
+ {x = posX, y = bottomrowY, an = 5, w = 110, h = 18}
+ lo.style = osc_styles.timecodes
+
+end
+
+-- slim box layout
+layouts["slimbox"] = function ()
+
+ local osc_geo = {
+ w = 660, -- width
+ h = 70, -- height
+ r = 10, -- corner-radius
+ }
+
+ -- make sure the OSC actually fits into the video
+ if (osc_param.playresx < (osc_geo.w)) then
+ osc_param.playresy = (osc_geo.w)/osc_param.display_aspect
+ osc_param.playresx = osc_param.playresy * osc_param.display_aspect
+ end
+
+ -- position of the controller according to video aspect and valignment
+ local posX = math.floor(get_align(user_opts.halign, osc_param.playresx,
+ osc_geo.w, 0))
+ local posY = math.floor(get_align(user_opts.valign, osc_param.playresy,
+ osc_geo.h, 0))
+
+ osc_param.areas = {} -- delete areas
+
+ -- area for active mouse input
+ add_area("input", get_hitbox_coords(posX, posY, 5, osc_geo.w, osc_geo.h))
+
+ -- area for show/hide
+ local sh_area_y0, sh_area_y1
+ if user_opts.valign > 0 then
+ -- deadzone above OSC
+ sh_area_y0 = get_align(-1 + (2*user_opts.deadzonesize),
+ posY - (osc_geo.h / 2), 0, 0)
+ sh_area_y1 = osc_param.playresy
+ else
+ -- deadzone below OSC
+ sh_area_y0 = 0
+ sh_area_y1 = (posY + (osc_geo.h / 2)) +
+ get_align(1 - (2*user_opts.deadzonesize),
+ osc_param.playresy - (posY + (osc_geo.h / 2)), 0, 0)
+ end
+ add_area("showhide", 0, sh_area_y0, osc_param.playresx, sh_area_y1)
+
+ local lo
+
+ local tc_w, ele_h, inner_w = 100, 20, osc_geo.w - 100
+
+ -- styles
+ local styles = {
+ box = "{\\rDefault\\blur0\\bord1\\1c&H000000\\3c&HFFFFFF}",
+ timecodes = "{\\1c&HFFFFFF\\3c&H000000\\fs20\\bord2\\blur1}",
+ tooltip = "{\\1c&HFFFFFF\\3c&H000000\\fs12\\bord1\\blur0.5}",
+ }
+
+
+ new_element("bgbox", "box")
+ lo = add_layout("bgbox")
+
+ lo.geometry = {x = posX, y = posY - 1, an = 2, w = inner_w, h = ele_h}
+ lo.layer = 10
+ lo.style = osc_styles.box
+ lo.alpha[1] = user_opts.boxalpha
+ lo.alpha[3] = 0
+ if not (user_opts["seekbarstyle"] == "bar") then
+ lo.box.radius = osc_geo.r
+ end
+
+
+ lo = add_layout("seekbar")
+ lo.geometry =
+ {x = posX, y = posY - 1, an = 2, w = inner_w, h = ele_h}
+ lo.style = osc_styles.timecodes
+ lo.slider.border = 0
+ lo.slider.gap = 1.5
+ lo.slider.tooltip_style = styles.tooltip
+ lo.slider.stype = user_opts["seekbarstyle"]
+ lo.slider.adjust_tooltip = false
+
+ --
+ -- Timecodes
+ --
+
+ lo = add_layout("tc_left")
+ lo.geometry =
+ {x = posX - (inner_w/2) + osc_geo.r, y = posY + 1,
+ an = 7, w = tc_w, h = ele_h}
+ lo.style = styles.timecodes
+ lo.alpha[3] = user_opts.boxalpha
+
+ lo = add_layout("tc_right")
+ lo.geometry =
+ {x = posX + (inner_w/2) - osc_geo.r, y = posY + 1,
+ an = 9, w = tc_w, h = ele_h}
+ lo.style = styles.timecodes
+ lo.alpha[3] = user_opts.boxalpha
+
+ -- Cache
+
+ lo = add_layout("cache")
+ lo.geometry =
+ {x = posX, y = posY + 1,
+ an = 8, w = tc_w, h = ele_h}
+ lo.style = styles.timecodes
+ lo.alpha[3] = user_opts.boxalpha
+
+
+end
+
+layouts["bottombar"] = function()
+ local osc_geo = {
+ x = -2,
+ y = osc_param.playresy - 54 - user_opts.barmargin,
+ an = 7,
+ w = osc_param.playresx + 4,
+ h = 56,
+ }
+
+ local padX = 9
+ local padY = 3
+ local buttonW = 27
+ local tcW = (state.tc_ms) and 170 or 110
+ local tsW = 90
+ local minW = (buttonW + padX)*5 + (tcW + padX)*4 + (tsW + padX)*2
+
+ if ((osc_param.display_aspect > 0) and (osc_param.playresx < minW)) then
+ osc_param.playresy = minW / osc_param.display_aspect
+ osc_param.playresx = osc_param.playresy * osc_param.display_aspect
+ osc_geo.y = osc_param.playresy - 54 - user_opts.barmargin
+ osc_geo.w = osc_param.playresx + 4
+ end
+
+ local line1 = osc_geo.y + 9 + padY
+ local line2 = osc_geo.y + 36 + padY
+
+ osc_param.areas = {}
+
+ add_area("input", get_hitbox_coords(osc_geo.x, osc_geo.y, osc_geo.an,
+ osc_geo.w, osc_geo.h))
+
+ local sh_area_y0, sh_area_y1
+ sh_area_y0 = get_align(-1 + (2*user_opts.deadzonesize),
+ osc_geo.y - (osc_geo.h / 2), 0, 0)
+ sh_area_y1 = osc_param.playresy - user_opts.barmargin
+ add_area("showhide", 0, sh_area_y0, osc_param.playresx, sh_area_y1)
+
+ local lo, geo
+
+ -- Background bar
+ new_element("bgbox", "box")
+ lo = add_layout("bgbox")
+
+ lo.geometry = osc_geo
+ lo.layer = 10
+ lo.style = osc_styles.box
+ lo.alpha[1] = user_opts.boxalpha
+
+
+ -- Playlist prev/next
+ geo = { x = osc_geo.x + padX, y = line1,
+ an = 4, w = 18, h = 18 - padY }
+ lo = add_layout("pl_prev")
+ lo.geometry = geo
+ lo.style = osc_styles.topButtonsBar
+
+ geo = { x = geo.x + geo.w + padX, y = geo.y, an = geo.an, w = geo.w, h = geo.h }
+ lo = add_layout("pl_next")
+ lo.geometry = geo
+ lo.style = osc_styles.topButtonsBar
+
+ local t_l = geo.x + geo.w + padX
+
+ -- Cache
+ geo = { x = osc_geo.x + osc_geo.w - padX, y = geo.y,
+ an = 6, w = 150, h = geo.h }
+ lo = add_layout("cache")
+ lo.geometry = geo
+ lo.style = osc_styles.vidtitleBar
+
+ local t_r = geo.x - geo.w - padX*2
+
+ -- Title
+ geo = { x = t_l, y = geo.y, an = 4,
+ w = t_r - t_l, h = geo.h }
+ lo = add_layout("title")
+ lo.geometry = geo
+ lo.style = string.format("%s{\\clip(%f,%f,%f,%f)}",
+ osc_styles.vidtitleBar,
+ geo.x, geo.y-geo.h, geo.w, geo.y+geo.h)
+
+
+ -- Playback control buttons
+ geo = { x = osc_geo.x + padX, y = line2, an = 4,
+ w = buttonW, h = 36 - padY*2}
+ lo = add_layout("playpause")
+ lo.geometry = geo
+ lo.style = osc_styles.smallButtonsBar
+
+ geo = { x = geo.x + geo.w + padX, y = geo.y, an = geo.an, w = geo.w, h = geo.h }
+ lo = add_layout("ch_prev")
+ lo.geometry = geo
+ lo.style = osc_styles.smallButtonsBar
+
+ geo = { x = geo.x + geo.w + padX, y = geo.y, an = geo.an, w = geo.w, h = geo.h }
+ lo = add_layout("ch_next")
+ lo.geometry = geo
+ lo.style = osc_styles.smallButtonsBar
+
+ -- Left timecode
+ geo = { x = geo.x + geo.w + padX + tcW, y = geo.y, an = 6,
+ w = tcW, h = geo.h }
+ lo = add_layout("tc_left")
+ lo.geometry = geo
+ lo.style = osc_styles.timecodesBar
+
+ local sb_l = geo.x + padX
+
+ -- Fullscreen button
+ geo = { x = osc_geo.x + osc_geo.w - buttonW - padX, y = geo.y, an = 4,
+ w = buttonW, h = geo.h }
+ lo = add_layout("tog_fs")
+ lo.geometry = geo
+ lo.style = osc_styles.smallButtonsBar
+
+ -- Volume
+ geo = { x = geo.x - geo.w - padX, y = geo.y, an = geo.an, w = geo.w, h = geo.h }
+ lo = add_layout("volume")
+ lo.geometry = geo
+ lo.style = osc_styles.smallButtonsBar
+
+ -- Track selection buttons
+ geo = { x = geo.x - tsW - padX, y = geo.y, an = geo.an, w = tsW, h = geo.h }
+ lo = add_layout("cy_sub")
+ lo.geometry = geo
+ lo.style = osc_styles.smallButtonsBar
+
+ geo = { x = geo.x - geo.w - padX, y = geo.y, an = geo.an, w = geo.w, h = geo.h }
+ lo = add_layout("cy_audio")
+ lo.geometry = geo
+ lo.style = osc_styles.smallButtonsBar
+
+
+ -- Right timecode
+ geo = { x = geo.x - padX - tcW - 10, y = geo.y, an = geo.an,
+ w = tcW, h = geo.h }
+ lo = add_layout("tc_right")
+ lo.geometry = geo
+ lo.style = osc_styles.timecodesBar
+
+ local sb_r = geo.x - padX
+
+
+ -- Seekbar
+ geo = { x = sb_l, y = geo.y, an = geo.an,
+ w = math.max(0, sb_r - sb_l), h = geo.h }
+ new_element("bgbar1", "box")
+ lo = add_layout("bgbar1")
+
+ lo.geometry = geo
+ lo.layer = 15
+ lo.style = osc_styles.timecodesBar
+ lo.alpha[1] =
+ math.min(255, user_opts.boxalpha + (255 - user_opts.boxalpha)*0.8)
+
+ lo = add_layout("seekbar")
+ lo.geometry = geo
+ lo.style = osc_styles.timecodes
+ lo.slider.border = 0
+ lo.slider.gap = 2
+ lo.slider.tooltip_style = osc_styles.timePosBar
+ lo.slider.tooltip_an = 5
+ lo.slider.stype = user_opts["seekbarstyle"]
+end
+
+layouts["topbar"] = function()
+ local osc_geo = {
+ x = -2,
+ y = 54 + user_opts.barmargin,
+ an = 1,
+ w = osc_param.playresx + 4,
+ h = 56,
+ }
+
+ local padX = 9
+ local padY = 3
+ local buttonW = 27
+ local tcW = (state.tc_ms) and 170 or 110
+ local tsW = 90
+ local minW = (buttonW + padX)*5 + (tcW + padX)*4 + (tsW + padX)*2
+
+ if ((osc_param.display_aspect > 0) and (osc_param.playresx < minW)) then
+ osc_param.playresy = minW / osc_param.display_aspect
+ osc_param.playresx = osc_param.playresy * osc_param.display_aspect
+ osc_geo.y = 54 + user_opts.barmargin
+ osc_geo.w = osc_param.playresx + 4
+ end
+
+ local line1 = osc_geo.y - 36 - padY
+ local line2 = osc_geo.y - 9 - padY
+
+ osc_param.areas = {}
+
+ add_area("input", get_hitbox_coords(osc_geo.x, osc_geo.y, osc_geo.an,
+ osc_geo.w, osc_geo.h))
+
+ local sh_area_y0, sh_area_y1
+ sh_area_y0 = user_opts.barmargin
+ sh_area_y1 = (osc_geo.y + (osc_geo.h / 2)) +
+ get_align(1 - (2*user_opts.deadzonesize),
+ osc_param.playresy - (osc_geo.y + (osc_geo.h / 2)), 0, 0)
+ add_area("showhide", 0, sh_area_y0, osc_param.playresx, sh_area_y1)
+
+ local lo, geo
+
+ -- Background bar
+ new_element("bgbox", "box")
+ lo = add_layout("bgbox")
+
+ lo.geometry = osc_geo
+ lo.layer = 10
+ lo.style = osc_styles.box
+ lo.alpha[1] = user_opts.boxalpha
+
+
+ -- Playback control buttons
+ geo = { x = osc_geo.x + padX, y = line1, an = 4,
+ w = buttonW, h = 36 - padY*2 }
+ lo = add_layout("playpause")
+ lo.geometry = geo
+ lo.style = osc_styles.smallButtonsBar
+
+ geo = { x = geo.x + geo.w + padX, y = geo.y, an = geo.an, w = geo.w, h = geo.h }
+ lo = add_layout("ch_prev")
+ lo.geometry = geo
+ lo.style = osc_styles.smallButtonsBar
+
+ geo = { x = geo.x + geo.w + padX, y = geo.y, an = geo.an, w = geo.w, h = geo.h }
+ lo = add_layout("ch_next")
+ lo.geometry = geo
+ lo.style = osc_styles.smallButtonsBar
+
+
+ -- Left timecode
+ geo = { x = geo.x + geo.w + padX + tcW, y = geo.y, an = 6,
+ w = tcW, h = geo.h }
+ lo = add_layout("tc_left")
+ lo.geometry = geo
+ lo.style = osc_styles.timecodesBar
+
+ local sb_l = geo.x + padX
+
+ -- Fullscreen button
+ geo = { x = osc_geo.x + osc_geo.w - buttonW - padX, y = geo.y, an = 4,
+ w = buttonW, h = geo.h }
+ lo = add_layout("tog_fs")
+ lo.geometry = geo
+ lo.style = osc_styles.smallButtonsBar
+
+ -- Volume
+ geo = { x = geo.x - geo.w - padX, y = geo.y, an = geo.an, w = geo.w, h = geo.h }
+ lo = add_layout("volume")
+ lo.geometry = geo
+ lo.style = osc_styles.smallButtonsBar
+
+ -- Track selection buttons
+ geo = { x = geo.x - tsW - padX, y = geo.y, an = geo.an, w = tsW, h = geo.h }
+ lo = add_layout("cy_sub")
+ lo.geometry = geo
+ lo.style = osc_styles.smallButtonsBar
+
+ geo = { x = geo.x - geo.w - padX, y = geo.y, an = geo.an, w = geo.w, h = geo.h }
+ lo = add_layout("cy_audio")
+ lo.geometry = geo
+ lo.style = osc_styles.smallButtonsBar
+
+
+ -- Right timecode
+ geo = { x = geo.x - geo.w - padX - tcW - 10, y = geo.y, an = 4,
+ w = tcW, h = geo.h }
+ lo = add_layout("tc_right")
+ lo.geometry = geo
+ lo.style = osc_styles.timecodesBar
+
+ local sb_r = geo.x - padX
+
+
+ -- Seekbar
+ geo = { x = sb_l, y = user_opts.barmargin, an = 7,
+ w = math.max(0, sb_r - sb_l), h = geo.h }
+ new_element("bgbar1", "box")
+ lo = add_layout("bgbar1")
+
+ lo.geometry = geo
+ lo.layer = 15
+ lo.style = osc_styles.timecodesBar
+ lo.alpha[1] =
+ math.min(255, user_opts.boxalpha + (255 - user_opts.boxalpha)*0.8)
+
+ lo = add_layout("seekbar")
+ lo.geometry = geo
+ lo.style = osc_styles.timecodesBar
+ lo.slider.border = 0
+ lo.slider.gap = 2
+ lo.slider.tooltip_style = osc_styles.timePosBar
+ lo.slider.stype = user_opts["seekbarstyle"]
+ lo.slider.tooltip_an = 5
+
+
+ -- Playlist prev/next
+ geo = { x = osc_geo.x + padX, y = line2, an = 4, w = 18, h = 18 - padY }
+ lo = add_layout("pl_prev")
+ lo.geometry = geo
+ lo.style = osc_styles.topButtonsBar
+
+ geo = { x = geo.x + geo.w + padX, y = geo.y, an = geo.an, w = geo.w, h = geo.h }
+ lo = add_layout("pl_next")
+ lo.geometry = geo
+ lo.style = osc_styles.topButtonsBar
+
+ local t_l = geo.x + geo.w + padX
+
+ -- Cache
+ geo = { x = osc_geo.x + osc_geo.w - padX, y = geo.y,
+ an = 6, w = 150, h = geo.h }
+ lo = add_layout("cache")
+ lo.geometry = geo
+ lo.style = osc_styles.vidtitleBar
+
+ local t_r = geo.x - geo.w - padX*2
+
+ -- Title
+ geo = { x = t_l, y = geo.y, an = 4,
+ w = t_r - t_l, h = geo.h }
+ lo = add_layout("title")
+ lo.geometry = geo
+ lo.style = string.format("%s{\\clip(%f,%f,%f,%f)}",
+ osc_styles.vidtitleBar,
+ geo.x, geo.y-geo.h, geo.w, geo.y+geo.h)
+end
+
+-- Validate string type user options
+function validate_user_opts()
+ if layouts[user_opts.layout] == nil then
+ msg.warn("Invalid setting \""..user_opts.layout.."\" for layout")
+ user_opts.layout = "box"
+ end
+
+ if user_opts.seekbarstyle ~= "slider" and
+ user_opts.seekbarstyle ~= "bar" and
+ user_opts.seekbarstyle ~= "knob" then
+ msg.warn("Invalid setting \"" .. user_opts.seekbarstyle
+ .. "\" for seekbarstyle")
+ user_opts.seekbarstyle = "slider"
+ end
+end
+
+
+-- OSC INIT
+function osc_init()
+ msg.debug("osc_init")
+
+ -- set canvas resolution according to display aspect and scaling setting
+ local baseResY = 720
+ local display_w, display_h, display_aspect = mp.get_osd_size()
+ local scale = 1
+
+ if (mp.get_property("video") == "no") then -- dummy/forced window
+ scale = user_opts.scaleforcedwindow
+ elseif state.fullscreen then
+ scale = user_opts.scalefullscreen
+ else
+ scale = user_opts.scalewindowed
+ end
+
+ if user_opts.vidscale then
+ osc_param.unscaled_y = baseResY
+ else
+ osc_param.unscaled_y = display_h
+ end
+ osc_param.playresy = osc_param.unscaled_y / scale
+ if (display_aspect > 0) then
+ osc_param.display_aspect = display_aspect
+ end
+ osc_param.playresx = osc_param.playresy * osc_param.display_aspect
+
+
+
+
+
+ elements = {}
+
+ -- some often needed stuff
+ local pl_count = mp.get_property_number("playlist-count", 0)
+ local have_pl = (pl_count > 1)
+ local pl_pos = mp.get_property_number("playlist-pos", 0) + 1
+ local have_ch = (mp.get_property_number("chapters", 0) > 0)
+ local loop = mp.get_property("loop-playlist", "no")
+
+ local ne
+
+ -- title
+ ne = new_element("title", "button")
+
+ ne.content = function ()
+ local title = mp.command_native({"expand-text", user_opts.title})
+ -- escape ASS, and strip newlines and trailing slashes
+ title = title:gsub("\\n", " "):gsub("\\$", ""):gsub("{","\\{")
+ return not (title == "") and title or "mpv"
+ end
+
+ ne.eventresponder["mbtn_left_up"] = function ()
+ local title = mp.get_property_osd("media-title")
+ if (have_pl) then
+ title = string.format("[%d/%d] %s", countone(pl_pos - 1),
+ pl_count, title)
+ end
+ show_message(title)
+ end
+
+ ne.eventresponder["mbtn_right_up"] =
+ function () show_message(mp.get_property_osd("filename")) end
+
+ -- playlist buttons
+
+ -- prev
+ ne = new_element("pl_prev", "button")
+
+ ne.content = "\238\132\144"
+ ne.enabled = (pl_pos > 1) or (loop ~= "no")
+ ne.eventresponder["mbtn_left_up"] =
+ function ()
+ mp.commandv("playlist-prev", "weak")
+ show_message(get_playlist(), 3)
+ end
+ ne.eventresponder["shift+mbtn_left_up"] =
+ function () show_message(get_playlist(), 3) end
+ ne.eventresponder["mbtn_right_up"] =
+ function () show_message(get_playlist(), 3) end
+
+ --next
+ ne = new_element("pl_next", "button")
+
+ ne.content = "\238\132\129"
+ ne.enabled = (have_pl and (pl_pos < pl_count)) or (loop ~= "no")
+ ne.eventresponder["mbtn_left_up"] =
+ function ()
+ mp.commandv("playlist-next", "weak")
+ show_message(get_playlist(), 3)
+ end
+ ne.eventresponder["shift+mbtn_left_up"] =
+ function () show_message(get_playlist(), 3) end
+ ne.eventresponder["mbtn_right_up"] =
+ function () show_message(get_playlist(), 3) end
+
+
+ -- big buttons
+
+ --playpause
+ ne = new_element("playpause", "button")
+
+ ne.content = function ()
+ if mp.get_property("pause") == "yes" then
+ return ("\238\132\129")
+ else
+ return ("\238\128\130")
+ end
+ end
+ ne.eventresponder["mbtn_left_up"] =
+ function () mp.commandv("cycle", "pause") end
+
+ --skipback
+ ne = new_element("skipback", "button")
+
+ ne.softrepeat = true
+ ne.content = "\238\128\132"
+ ne.eventresponder["mbtn_left_down"] =
+ function () mp.commandv("seek", -5, "relative", "keyframes") end
+ ne.eventresponder["shift+mbtn_left_down"] =
+ function () mp.commandv("frame-back-step") end
+ ne.eventresponder["mbtn_right_down"] =
+ function () mp.commandv("seek", -30, "relative", "keyframes") end
+
+ --skipfrwd
+ ne = new_element("skipfrwd", "button")
+
+ ne.softrepeat = true
+ ne.content = "\238\128\133"
+ ne.eventresponder["mbtn_left_down"] =
+ function () mp.commandv("seek", 10, "relative", "keyframes") end
+ ne.eventresponder["shift+mbtn_left_down"] =
+ function () mp.commandv("frame-step") end
+ ne.eventresponder["mbtn_right_down"] =
+ function () mp.commandv("seek", 60, "relative", "keyframes") end
+
+ --ch_prev
+ ne = new_element("ch_prev", "button")
+
+ ne.enabled = have_ch
+ ne.content = "\238\132\132"
+ ne.eventresponder["mbtn_left_up"] =
+ function ()
+ mp.commandv("add", "chapter", -1)
+ show_message(get_chapterlist(), 3)
+ end
+ ne.eventresponder["shift+mbtn_left_up"] =
+ function () show_message(get_chapterlist(), 3) end
+ ne.eventresponder["mbtn_right_up"] =
+ function () show_message(get_chapterlist(), 3) end
+
+ --ch_next
+ ne = new_element("ch_next", "button")
+
+ ne.enabled = have_ch
+ ne.content = "\238\132\133"
+ ne.eventresponder["mbtn_left_up"] =
+ function ()
+ mp.commandv("add", "chapter", 1)
+ show_message(get_chapterlist(), 3)
+ end
+ ne.eventresponder["shift+mbtn_left_up"] =
+ function () show_message(get_chapterlist(), 3) end
+ ne.eventresponder["mbtn_right_up"] =
+ function () show_message(get_chapterlist(), 3) end
+
+ --
+ update_tracklist()
+
+ --cy_audio
+ ne = new_element("cy_audio", "button")
+
+ ne.enabled = (#tracks_osc.audio > 0)
+ ne.content = function ()
+ local aid = "–"
+ if not (get_track("audio") == 0) then
+ aid = get_track("audio")
+ end
+ return ("\238\132\134" .. osc_styles.smallButtonsLlabel
+ .. " " .. aid .. "/" .. #tracks_osc.audio)
+ end
+ ne.eventresponder["mbtn_left_up"] =
+ function () set_track("audio", 1) end
+ ne.eventresponder["mbtn_right_up"] =
+ function () set_track("audio", -1) end
+ ne.eventresponder["shift+mbtn_left_down"] =
+ function () show_message(get_tracklist("audio"), 2) end
+
+ --cy_sub
+ ne = new_element("cy_sub", "button")
+
+ ne.enabled = (#tracks_osc.sub > 0)
+ ne.content = function ()
+ local sid = "–"
+ if not (get_track("sub") == 0) then
+ sid = get_track("sub")
+ end
+ return ("\238\132\135" .. osc_styles.smallButtonsLlabel
+ .. " " .. sid .. "/" .. #tracks_osc.sub)
+ end
+ ne.eventresponder["mbtn_left_up"] =
+ function () set_track("sub", 1) end
+ ne.eventresponder["mbtn_right_up"] =
+ function () set_track("sub", -1) end
+ ne.eventresponder["shift+mbtn_left_down"] =
+ function () show_message(get_tracklist("sub"), 2) end
+
+ --tog_fs
+ ne = new_element("tog_fs", "button")
+ ne.content = function ()
+ if (state.fullscreen) then
+ return ("\238\132\137")
+ else
+ return ("\238\132\136")
+ end
+ end
+ ne.eventresponder["mbtn_left_up"] =
+ function () mp.commandv("cycle", "fullscreen") end
+
+
+ --seekbar
+ ne = new_element("seekbar", "slider")
+
+ ne.enabled = not (mp.get_property("percent-pos") == nil)
+ ne.slider.markerF = function ()
+ local duration = mp.get_property_number("duration", nil)
+ if not (duration == nil) then
+ local chapters = mp.get_property_native("chapter-list", {})
+ local markers = {}
+ for n = 1, #chapters do
+ markers[n] = (chapters[n].time / duration * 100)
+ end
+ return markers
+ else
+ return {}
+ end
+ end
+ ne.slider.posF =
+ function () return mp.get_property_number("percent-pos", nil) end
+ ne.slider.tooltipF = function (pos)
+ local duration = mp.get_property_number("duration", nil)
+ if not ((duration == nil) or (pos == nil)) then
+ possec = duration * (pos / 100)
+ return mp.format_time(possec)
+ else
+ return ""
+ end
+ end
+ ne.slider.seekRangesF = function()
+ if not (user_opts.seekranges) then
+ return nil
+ end
+ local cache_state = mp.get_property_native("demuxer-cache-state", nil)
+ if not cache_state then
+ return nil
+ end
+ local duration = mp.get_property_number("duration", nil)
+ if (duration == nil) or duration <= 0 then
+ return nil
+ end
+ local ranges = cache_state["seekable-ranges"]
+ for _, range in pairs(ranges) do
+ range["start"] = 100 * range["start"] / duration
+ range["end"] = 100 * range["end"] / duration
+ end
+ return ranges
+ end
+ ne.eventresponder["mouse_move"] = --keyframe seeking when mouse is dragged
+ function (element)
+ -- mouse move events may pile up during seeking and may still get
+ -- sent when the user is done seeking, so we need to throw away
+ -- identical seeks
+ local seekto = get_slider_value(element)
+ if (element.state.lastseek == nil) or
+ (not (element.state.lastseek == seekto)) then
+ mp.commandv("seek", seekto,
+ "absolute-percent", "keyframes")
+ element.state.lastseek = seekto
+ end
+
+ end
+ ne.eventresponder["mbtn_left_down"] = --exact seeks on single clicks
+ function (element) mp.commandv("seek", get_slider_value(element),
+ "absolute-percent", "exact") end
+ ne.eventresponder["reset"] =
+ function (element) element.state.lastseek = nil end
+
+
+ -- tc_left (current pos)
+ ne = new_element("tc_left", "button")
+
+ ne.content = function ()
+ if (state.tc_ms) then
+ return (mp.get_property_osd("playback-time/full"))
+ else
+ return (mp.get_property_osd("playback-time"))
+ end
+ end
+ ne.eventresponder["mbtn_left_up"] = function ()
+ state.tc_ms = not state.tc_ms
+ request_init()
+ end
+
+ -- tc_right (total/remaining time)
+ ne = new_element("tc_right", "button")
+
+ ne.visible = (mp.get_property_number("duration", 0) > 0)
+ ne.content = function ()
+ if (state.rightTC_trem) then
+ if state.tc_ms then
+ return ("-"..mp.get_property_osd("playtime-remaining/full"))
+ else
+ return ("-"..mp.get_property_osd("playtime-remaining"))
+ end
+ else
+ if state.tc_ms then
+ return (mp.get_property_osd("duration/full"))
+ else
+ return (mp.get_property_osd("duration"))
+ end
+ end
+ end
+ ne.eventresponder["mbtn_left_up"] =
+ function () state.rightTC_trem = not state.rightTC_trem end
+
+ -- cache
+ ne = new_element("cache", "button")
+
+ ne.content = function ()
+ local dmx_cache = mp.get_property_number("demuxer-cache-duration")
+ local cache_used = mp.get_property_number("cache-used")
+ local dmx_cache_state = mp.get_property_native("demuxer-cache-state", {})
+ local is_network = mp.get_property_native("demuxer-via-network")
+ local show_cache = cache_used and not dmx_cache_state["eof"]
+ if dmx_cache then
+ dmx_cache = string.format("%3.0fs", dmx_cache)
+ end
+ if dmx_cache_state["fw-bytes"] then
+ cache_used = (cache_used or 0)*1024 + dmx_cache_state["fw-bytes"]
+ end
+ if (is_network and dmx_cache) or show_cache then
+ -- Only show dmx-cache-duration by itself if it's a network file.
+ -- Cache can be forced even for local files, so always show that.
+ return string.format("Cache: %s%s%s",
+ (dmx_cache and dmx_cache or ""),
+ ((dmx_cache and show_cache) and " | " or ""),
+ (show_cache and
+ utils.format_bytes_humanized(cache_used) or ""))
+ else
+ return ""
+ end
+ end
+
+ -- volume
+ ne = new_element("volume", "button")
+
+ ne.content = function()
+ local volume = mp.get_property_number("volume", 0)
+ local mute = mp.get_property_native("mute")
+ local volicon = {"\238\132\139", "\238\132\140",
+ "\238\132\141", "\238\132\142"}
+ if volume == 0 or mute then
+ return "\238\132\138"
+ else
+ return volicon[math.min(4,math.ceil(volume / (100/3)))]
+ end
+ end
+ ne.eventresponder["mbtn_left_up"] =
+ function () mp.commandv("cycle", "mute") end
+
+ ne.eventresponder["wheel_up_press"] =
+ function () mp.commandv("osd-auto", "add", "volume", 5) end
+ ne.eventresponder["wheel_down_press"] =
+ function () mp.commandv("osd-auto", "add", "volume", -5) end
+
+
+ -- load layout
+ layouts[user_opts.layout]()
+
+ --do something with the elements
+ prepare_elements()
+
+end
+
+
+
+--
+-- Other important stuff
+--
+
+
+function show_osc()
+ -- show when disabled can happen (e.g. mouse_move) due to async/delayed unbinding
+ if not state.enabled then return end
+
+ msg.trace("show_osc")
+ --remember last time of invocation (mouse move)
+ state.showtime = mp.get_time()
+
+ osc_visible(true)
+
+ if (user_opts.fadeduration > 0) then
+ state.anitype = nil
+ end
+end
+
+function hide_osc()
+ msg.trace("hide_osc")
+ if not state.enabled then
+ -- typically hide happens at render() from tick(), but now tick() is
+ -- no-op and won't render again to remove the osc, so do that manually.
+ state.osc_visible = false
+ timer_stop()
+ render_wipe()
+ elseif (user_opts.fadeduration > 0) then
+ if not(state.osc_visible == false) then
+ state.anitype = "out"
+ control_timer()
+ end
+ else
+ osc_visible(false)
+ end
+end
+
+function osc_visible(visible)
+ state.osc_visible = visible
+ control_timer()
+end
+
+function pause_state(name, enabled)
+ state.paused = enabled
+ control_timer()
+end
+
+function cache_state(name, idle)
+ state.cache_idle = idle
+ control_timer()
+end
+
+function control_timer()
+ if (state.paused) and (state.osc_visible) and
+ ( not(state.cache_idle) or not (state.anitype == nil) ) then
+
+ timer_start()
+ else
+ timer_stop()
+ end
+end
+
+function timer_start()
+ if not (state.timer_active) then
+ msg.trace("timer start")
+
+ if (state.timer == nil) then
+ -- create new timer
+ state.timer = mp.add_periodic_timer(0.03, tick)
+ else
+ -- resume existing one
+ state.timer:resume()
+ end
+
+ state.timer_active = true
+ end
+end
+
+function timer_stop()
+ if (state.timer_active) then
+ msg.trace("timer stop")
+
+ if not (state.timer == nil) then
+ -- kill timer
+ state.timer:kill()
+ end
+
+ state.timer_active = false
+ end
+end
+
+
+
+function mouse_leave()
+ if user_opts.hidetimeout >= 0 then
+ hide_osc()
+ end
+ -- reset mouse position
+ state.last_mouseX, state.last_mouseY = nil, nil
+end
+
+function request_init()
+ state.initREQ = true
+end
+
+function render_wipe()
+ msg.trace("render_wipe()")
+ mp.set_osd_ass(0, 0, "{}")
+end
+
+function render()
+ msg.trace("rendering")
+ local current_screen_sizeX, current_screen_sizeY, aspect = mp.get_osd_size()
+ local mouseX, mouseY = get_virt_mouse_pos()
+ local now = mp.get_time()
+
+ -- check if display changed, if so request reinit
+ if not (state.mp_screen_sizeX == current_screen_sizeX
+ and state.mp_screen_sizeY == current_screen_sizeY) then
+
+ request_init()
+
+ state.mp_screen_sizeX = current_screen_sizeX
+ state.mp_screen_sizeY = current_screen_sizeY
+ end
+
+ -- init management
+ if state.initREQ then
+ osc_init()
+ state.initREQ = false
+
+ -- store initial mouse position
+ if (state.last_mouseX == nil or state.last_mouseY == nil)
+ and not (mouseX == nil or mouseY == nil) then
+
+ state.last_mouseX, state.last_mouseY = mouseX, mouseY
+ end
+ end
+
+
+ -- fade animation
+ if not(state.anitype == nil) then
+
+ if (state.anistart == nil) then
+ state.anistart = now
+ end
+
+ if (now < state.anistart + (user_opts.fadeduration/1000)) then
+
+ if (state.anitype == "in") then --fade in
+ osc_visible(true)
+ state.animation = scale_value(state.anistart,
+ (state.anistart + (user_opts.fadeduration/1000)),
+ 255, 0, now)
+ elseif (state.anitype == "out") then --fade out
+ state.animation = scale_value(state.anistart,
+ (state.anistart + (user_opts.fadeduration/1000)),
+ 0, 255, now)
+ end
+
+ else
+ if (state.anitype == "out") then
+ osc_visible(false)
+ end
+ state.anistart = nil
+ state.animation = nil
+ state.anitype = nil
+ end
+ else
+ state.anistart = nil
+ state.animation = nil
+ state.anitype = nil
+ end
+
+ --mouse show/hide area
+ for k,cords in pairs(osc_param.areas["showhide"]) do
+ set_virt_mouse_area(cords.x1, cords.y1, cords.x2, cords.y2, "showhide")
+ end
+ do_enable_keybindings()
+
+ --mouse input area
+ local mouse_over_osc = false
+
+ for _,cords in ipairs(osc_param.areas["input"]) do
+ if state.osc_visible then -- activate only when OSC is actually visible
+ set_virt_mouse_area(cords.x1, cords.y1, cords.x2, cords.y2, "input")
+ end
+ if state.osc_visible ~= state.input_enabled then
+ if state.osc_visible then
+ mp.enable_key_bindings("input")
+ else
+ mp.disable_key_bindings("input")
+ end
+ state.input_enabled = state.osc_visible
+ end
+
+ if (mouse_hit_coords(cords.x1, cords.y1, cords.x2, cords.y2)) then
+ mouse_over_osc = true
+ end
+ end
+
+ -- autohide
+ if not (state.showtime == nil) and (user_opts.hidetimeout >= 0)
+ and (state.showtime + (user_opts.hidetimeout/1000) < now)
+ and (state.active_element == nil) and not (mouse_over_osc) then
+
+ hide_osc()
+ end
+
+
+ -- actual rendering
+ local ass = assdraw.ass_new()
+
+ -- Messages
+ render_message(ass)
+
+ -- mpv_thumbnail_script.lua --
+ local thumb_was_visible = osc_thumb_state.visible
+ osc_thumb_state.visible = false
+ -- // mpv_thumbnail_script.lua // --
+
+ -- actual OSC
+ if state.osc_visible then
+ render_elements(ass)
+ end
+
+ -- mpv_thumbnail_script.lua --
+ if not osc_thumb_state.visible and thumb_was_visible then
+ hide_thumbnail()
+ end
+ -- // mpv_thumbnail_script.lua // --
+
+ -- submit
+ mp.set_osd_ass(osc_param.playresy * osc_param.display_aspect,
+ osc_param.playresy, ass.text)
+
+
+
+
+end
+
+--
+-- Eventhandling
+--
+
+local function element_has_action(element, action)
+ return element and element.eventresponder and
+ element.eventresponder[action]
+end
+
+function process_event(source, what)
+ local action = string.format("%s%s", source,
+ what and ("_" .. what) or "")
+
+ if what == "down" or what == "press" then
+
+ for n = 1, #elements do
+
+ if mouse_hit(elements[n]) and
+ elements[n].eventresponder and
+ (elements[n].eventresponder[source .. "_up"] or
+ elements[n].eventresponder[action]) then
+
+ if what == "down" then
+ state.active_element = n
+ state.active_event_source = source
+ end
+ -- fire the down or press event if the element has one
+ if element_has_action(elements[n], action) then
+ elements[n].eventresponder[action](elements[n])
+ end
+
+ end
+ end
+
+ elseif what == "up" then
+
+ if elements[state.active_element] then
+ local n = state.active_element
+
+ if n == 0 then
+ --click on background (does not work)
+ elseif element_has_action(elements[n], action) and
+ mouse_hit(elements[n]) then
+
+ elements[n].eventresponder[action](elements[n])
+ end
+
+ --reset active element
+ if element_has_action(elements[n], "reset") then
+ elements[n].eventresponder["reset"](elements[n])
+ end
+
+ end
+ state.active_element = nil
+ state.mouse_down_counter = 0
+
+ elseif source == "mouse_move" then
+
+ local mouseX, mouseY = get_virt_mouse_pos()
+ if (user_opts.minmousemove == 0) or
+ (not ((state.last_mouseX == nil) or (state.last_mouseY == nil)) and
+ ((math.abs(mouseX - state.last_mouseX) >= user_opts.minmousemove)
+ or (math.abs(mouseY - state.last_mouseY) >= user_opts.minmousemove)
+ )
+ ) then
+ show_osc()
+ end
+ state.last_mouseX, state.last_mouseY = mouseX, mouseY
+
+ local n = state.active_element
+ if element_has_action(elements[n], action) then
+ elements[n].eventresponder[action](elements[n])
+ end
+ tick()
+ end
+end
+
+-- called by mpv on every frame
+function tick()
+ if (not state.enabled) then return end
+
+ if (state.idle) then
+
+ -- render idle message
+ msg.trace("idle message")
+ local icon_x, icon_y = 320 - 26, 140
+
+ local ass = assdraw.ass_new()
+ ass:new_event()
+ ass:pos(icon_x, icon_y)
+ ass:append("{\\rDefault\\an7\\c&H430142&\\1a&H00&\\bord0\\shad0\\p6}m 1605 828 b 1605 1175 1324 1456 977 1456 631 1456 349 1175 349 828 349 482 631 200 977 200 1324 200 1605 482 1605 828{\\p0}")
+ ass:new_event()
+ ass:pos(icon_x, icon_y)
+ ass:append("{\\rDefault\\an7\\c&HDDDBDD&\\1a&H00&\\bord0\\shad0\\p6}m 1296 910 b 1296 1131 1117 1310 897 1310 676 1310 497 1131 497 910 497 689 676 511 897 511 1117 511 1296 689 1296 910{\\p0}")
+ ass:new_event()
+ ass:pos(icon_x, icon_y)
+ ass:append("{\\rDefault\\an7\\c&H691F69&\\1a&H00&\\bord0\\shad0\\p6}m 762 1113 l 762 708 b 881 776 1000 843 1119 911 1000 978 881 1046 762 1113{\\p0}")
+ ass:new_event()
+ ass:pos(icon_x, icon_y)
+ ass:append("{\\rDefault\\an7\\c&H682167&\\1a&H00&\\bord0\\shad0\\p6}m 925 42 b 463 42 87 418 87 880 87 1343 463 1718 925 1718 1388 1718 1763 1343 1763 880 1763 418 1388 42 925 42 m 925 42 m 977 200 b 1324 200 1605 482 1605 828 1605 1175 1324 1456 977 1456 631 1456 349 1175 349 828 349 482 631 200 977 200{\\p0}")
+ ass:new_event()
+ ass:pos(icon_x, icon_y)
+ ass:append("{\\rDefault\\an7\\c&H753074&\\1a&H00&\\bord0\\shad0\\p6}m 977 198 b 630 198 348 480 348 828 348 1176 630 1458 977 1458 1325 1458 1607 1176 1607 828 1607 480 1325 198 977 198 m 977 198 m 977 202 b 1323 202 1604 483 1604 828 1604 1174 1323 1454 977 1454 632 1454 351 1174 351 828 351 483 632 202 977 202{\\p0}")
+ ass:new_event()
+ ass:pos(icon_x, icon_y)
+ ass:append("{\\rDefault\\an7\\c&HE5E5E5&\\1a&H00&\\bord0\\shad0\\p6}m 895 10 b 401 10 0 410 0 905 0 1399 401 1800 895 1800 1390 1800 1790 1399 1790 905 1790 410 1390 10 895 10 m 895 10 m 925 42 b 1388 42 1763 418 1763 880 1763 1343 1388 1718 925 1718 463 1718 87 1343 87 880 87 418 463 42 925 42{\\p0}")
+ ass:new_event()
+ ass:pos(320, icon_y+65)
+ ass:an(8)
+ ass:append("Drop files or URLs to play here.")
+ mp.set_osd_ass(640, 360, ass.text)
+
+ if state.showhide_enabled then
+ mp.disable_key_bindings("showhide")
+ state.showhide_enabled = false
+ end
+
+
+ elseif (state.fullscreen and user_opts.showfullscreen)
+ or (not state.fullscreen and user_opts.showwindowed) then
+
+ -- render the OSC
+ render()
+ else
+ -- Flush OSD
+ mp.set_osd_ass(osc_param.playresy, osc_param.playresy, "")
+ end
+end
+
+function do_enable_keybindings()
+ if state.enabled then
+ if not state.showhide_enabled then
+ mp.enable_key_bindings("showhide", "allow-vo-dragging+allow-hide-cursor")
+ end
+ state.showhide_enabled = true
+ end
+end
+
+function enable_osc(enable)
+ state.enabled = enable
+ if enable then
+ do_enable_keybindings()
+ else
+ hide_osc() -- acts immediately when state.enabled == false
+ if state.showhide_enabled then
+ mp.disable_key_bindings("showhide")
+ end
+ state.showhide_enabled = false
+ end
+end
+
+-- mpv_thumbnail_script.lua --
+
+local builtin_osc_enabled = mp.get_property_native('osc')
+if builtin_osc_enabled then
+ local err = "You must disable the built-in OSC with osc=no in your configuration!"
+ mp.osd_message(err, 5)
+ msg.error(err)
+
+ -- This may break, but since we can, let's try to just disable the builtin OSC.
+ mp.set_property_native('osc', false)
+end
+
+-- // mpv_thumbnail_script.lua // --
+
+
+validate_user_opts()
+
+mp.register_event("start-file", request_init)
+mp.register_event("tracks-changed", request_init)
+mp.observe_property("playlist", nil, request_init)
+
+mp.register_script_message("osc-message", show_message)
+mp.register_script_message("osc-chapterlist", function(dur)
+ show_message(get_chapterlist(), dur)
+end)
+mp.register_script_message("osc-playlist", function(dur)
+ show_message(get_playlist(), dur)
+end)
+mp.register_script_message("osc-tracklist", function(dur)
+ local msg = {}
+ for k,v in pairs(nicetypes) do
+ table.insert(msg, get_tracklist(k))
+ end
+ show_message(table.concat(msg, '\n\n'), dur)
+end)
+
+mp.observe_property("fullscreen", "bool",
+ function(name, val)
+ state.fullscreen = val
+ request_init()
+ end
+)
+mp.observe_property("idle-active", "bool",
+ function(name, val)
+ state.idle = val
+ tick()
+ end
+)
+mp.observe_property("pause", "bool", pause_state)
+mp.observe_property("cache-idle", "bool", cache_state)
+mp.observe_property("vo-configured", "bool", function(name, val)
+ if val then
+ mp.register_event("tick", tick)
+ else
+ mp.unregister_event(tick)
+ end
+end)
+
+-- mouse show/hide bindings
+mp.set_key_bindings({
+ {"mouse_move", function(e) process_event("mouse_move", nil) end},
+ {"mouse_leave", mouse_leave},
+}, "showhide", "force")
+do_enable_keybindings()
+
+--mouse input bindings
+mp.set_key_bindings({
+ {"mbtn_left", function(e) process_event("mbtn_left", "up") end,
+ function(e) process_event("mbtn_left", "down") end},
+ {"shift+mbtn_left", function(e) process_event("shift+mbtn_left", "up") end,
+ function(e) process_event("shift+mbtn_left", "down") end},
+ {"mbtn_right", function(e) process_event("mbtn_right", "up") end,
+ function(e) process_event("mbtn_right", "down") end},
+ {"wheel_up", function(e) process_event("wheel_up", "press") end},
+ {"wheel_down", function(e) process_event("wheel_down", "press") end},
+ {"mbtn_left_dbl", "ignore"},
+ {"shift+mbtn_left_dbl", "ignore"},
+ {"mbtn_right_dbl", "ignore"},
+}, "input", "force")
+mp.enable_key_bindings("input")
+
+
+user_opts.hidetimeout_orig = user_opts.hidetimeout
+
+function always_on(val)
+ if val then
+ user_opts.hidetimeout = -1 -- disable autohide
+ if state.enabled then show_osc() end
+ else
+ user_opts.hidetimeout = user_opts.hidetimeout_orig
+ if state.enabled then hide_osc() end
+ end
+end
+
+-- mode can be auto/always/never/cycle
+-- the modes only affect internal variables and not stored on its own.
+function visibility_mode(mode, no_osd)
+ if mode == "cycle" then
+ if not state.enabled then
+ mode = "auto"
+ elseif user_opts.hidetimeout >= 0 then
+ mode = "always"
+ else
+ mode = "never"
+ end
+ end
+
+ if mode == "auto" then
+ always_on(false)
+ enable_osc(true)
+ elseif mode == "always" then
+ enable_osc(true)
+ always_on(true)
+ elseif mode == "never" then
+ enable_osc(false)
+ else
+ msg.warn("Ignoring unknown visibility mode '" .. mode .. "'")
+ return
+ end
+
+ if not no_osd and tonumber(mp.get_property("osd-level")) >= 1 then
+ mp.osd_message("OSC visibility: " .. mode)
+ end
+end
+
+visibility_mode(user_opts.visibility, true)
+mp.register_script_message("osc-visibility", visibility_mode)
+mp.add_key_binding(nil, "visibility", function() visibility_mode("cycle") end)
+
+set_virt_mouse_area(0, 0, 0, 0, "input")
diff --git a/stow/mpv/dot-config/mpv/scripts/mpv_thumbnail_script_server.lua b/stow/mpv/dot-config/mpv/scripts/mpv_thumbnail_script_server.lua
new file mode 100644
index 0000000..21a34d8
--- /dev/null
+++ b/stow/mpv/dot-config/mpv/scripts/mpv_thumbnail_script_server.lua
@@ -0,0 +1,736 @@
+--[[
+ Copyright (C) 2017 AMM
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]--
+--[[
+ mpv_thumbnail_script.lua 0.4.2 - commit a2de250 (branch master)
+ https://github.com/TheAMM/mpv_thumbnail_script
+ Built on 2018-02-07 20:36:54
+]]--
+local assdraw = require 'mp.assdraw'
+local msg = require 'mp.msg'
+local opt = require 'mp.options'
+local utils = require 'mp.utils'
+
+-- Determine platform --
+ON_WINDOWS = (package.config:sub(1,1) ~= '/')
+
+-- Some helper functions needed to parse the options --
+function isempty(v) return (v == false) or (v == nil) or (v == "") or (v == 0) or (type(v) == "table" and next(v) == nil) end
+
+function divmod (a, b)
+ return math.floor(a / b), a % b
+end
+
+-- Better modulo
+function bmod( i, N )
+ return (i % N + N) % N
+end
+
+function join_paths(...)
+ local sep = ON_WINDOWS and "\\" or "/"
+ local result = "";
+ for i, p in pairs({...}) do
+ if p ~= "" then
+ if is_absolute_path(p) then
+ result = p
+ else
+ result = (result ~= "") and (result:gsub("[\\"..sep.."]*$", "") .. sep .. p) or p
+ end
+ end
+ end
+ return result:gsub("[\\"..sep.."]*$", "")
+end
+
+-- /some/path/file.ext -> /some/path, file.ext
+function split_path( path )
+ local sep = ON_WINDOWS and "\\" or "/"
+ local first_index, last_index = path:find('^.*' .. sep)
+
+ if last_index == nil then
+ return "", path
+ else
+ local dir = path:sub(0, last_index-1)
+ local file = path:sub(last_index+1, -1)
+
+ return dir, file
+ end
+end
+
+function is_absolute_path( path )
+ local tmp, is_win = path:gsub("^[A-Z]:\\", "")
+ local tmp, is_unix = path:gsub("^/", "")
+ return (is_win > 0) or (is_unix > 0)
+end
+
+function Set(source)
+ local set = {}
+ for _, l in ipairs(source) do set[l] = true end
+ return set
+end
+
+---------------------------
+-- More helper functions --
+---------------------------
+
+-- Removes all keys from a table, without destroying the reference to it
+function clear_table(target)
+ for key, value in pairs(target) do
+ target[key] = nil
+ end
+end
+function shallow_copy(target)
+ local copy = {}
+ for k, v in pairs(target) do
+ copy[k] = v
+ end
+ return copy
+end
+
+-- Rounds to given decimals. eg. round_dec(3.145, 0) => 3
+function round_dec(num, idp)
+ local mult = 10^(idp or 0)
+ return math.floor(num * mult + 0.5) / mult
+end
+
+function file_exists(name)
+ local f = io.open(name, "rb")
+ if f ~= nil then
+ local ok, err, code = f:read(1)
+ io.close(f)
+ return code == nil
+ else
+ return false
+ end
+end
+
+function path_exists(name)
+ local f = io.open(name, "rb")
+ if f ~= nil then
+ io.close(f)
+ return true
+ else
+ return false
+ end
+end
+
+function create_directories(path)
+ local cmd
+ if ON_WINDOWS then
+ cmd = { args = {"cmd", "/c", "mkdir", path} }
+ else
+ cmd = { args = {"mkdir", "-p", path} }
+ end
+ utils.subprocess(cmd)
+end
+
+-- Find an executable in PATH or CWD with the given name
+function find_executable(name)
+ local delim = ON_WINDOWS and ";" or ":"
+
+ local pwd = os.getenv("PWD") or utils.getcwd()
+ local path = os.getenv("PATH")
+
+ local env_path = pwd .. delim .. path -- Check CWD first
+
+ local result, filename
+ for path_dir in env_path:gmatch("[^"..delim.."]+") do
+ filename = join_paths(path_dir, name)
+ if file_exists(filename) then
+ result = filename
+ break
+ end
+ end
+
+ return result
+end
+
+local ExecutableFinder = { path_cache = {} }
+-- Searches for an executable and caches the result if any
+function ExecutableFinder:get_executable_path( name, raw_name )
+ name = ON_WINDOWS and not raw_name and (name .. ".exe") or name
+
+ if self.path_cache[name] == nil then
+ self.path_cache[name] = find_executable(name) or false
+ end
+ return self.path_cache[name]
+end
+
+-- Format seconds to HH.MM.SS.sss
+function format_time(seconds, sep, decimals)
+ decimals = decimals == nil and 3 or decimals
+ sep = sep and sep or "."
+ local s = seconds
+ local h, s = divmod(s, 60*60)
+ local m, s = divmod(s, 60)
+
+ local second_format = string.format("%%0%d.%df", 2+(decimals > 0 and decimals+1 or 0), decimals)
+
+ return string.format("%02d"..sep.."%02d"..sep..second_format, h, m, s)
+end
+
+-- Format seconds to 1h 2m 3.4s
+function format_time_hms(seconds, sep, decimals, force_full)
+ decimals = decimals == nil and 1 or decimals
+ sep = sep ~= nil and sep or " "
+
+ local s = seconds
+ local h, s = divmod(s, 60*60)
+ local m, s = divmod(s, 60)
+
+ if force_full or h > 0 then
+ return string.format("%dh"..sep.."%dm"..sep.."%." .. tostring(decimals) .. "fs", h, m, s)
+ elseif m > 0 then
+ return string.format("%dm"..sep.."%." .. tostring(decimals) .. "fs", m, s)
+ else
+ return string.format("%." .. tostring(decimals) .. "fs", s)
+ end
+end
+
+-- Writes text on OSD and console
+function log_info(txt, timeout)
+ timeout = timeout or 1.5
+ msg.info(txt)
+ mp.osd_message(txt, timeout)
+end
+
+-- Join table items, ala ({"a", "b", "c"}, "=", "-", ", ") => "=a-, =b-, =c-"
+function join_table(source, before, after, sep)
+ before = before or ""
+ after = after or ""
+ sep = sep or ", "
+ local result = ""
+ for i, v in pairs(source) do
+ if not isempty(v) then
+ local part = before .. v .. after
+ if i == 1 then
+ result = part
+ else
+ result = result .. sep .. part
+ end
+ end
+ end
+ return result
+end
+
+function wrap(s, char)
+ char = char or "'"
+ return char .. s .. char
+end
+-- Wraps given string into 'string' and escapes any 's in it
+function escape_and_wrap(s, char, replacement)
+ char = char or "'"
+ replacement = replacement or "\\" .. char
+ return wrap(string.gsub(s, char, replacement), char)
+end
+-- Escapes single quotes in a string and wraps the input in single quotes
+function escape_single_bash(s)
+ return escape_and_wrap(s, "'", "'\\''")
+end
+
+-- Returns (a .. b) if b is not empty or nil
+function joined_or_nil(a, b)
+ return not isempty(b) and (a .. b) or nil
+end
+
+-- Put items from one table into another
+function extend_table(target, source)
+ for i, v in pairs(source) do
+ table.insert(target, v)
+ end
+end
+
+-- Creates a handle and filename for a temporary random file (in current directory)
+function create_temporary_file(base, mode, suffix)
+ local handle, filename
+ suffix = suffix or ""
+ while true do
+ filename = base .. tostring(math.random(1, 5000)) .. suffix
+ handle = io.open(filename, "r")
+ if not handle then
+ handle = io.open(filename, mode)
+ break
+ end
+ io.close(handle)
+ end
+ return handle, filename
+end
+
+
+function get_processor_count()
+ local proc_count
+
+ if ON_WINDOWS then
+ proc_count = tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
+ else
+ local cpuinfo_handle = io.open("/proc/cpuinfo")
+ if cpuinfo_handle ~= nil then
+ local cpuinfo_contents = cpuinfo_handle:read("*a")
+ local _, replace_count = cpuinfo_contents:gsub('processor', '')
+ proc_count = replace_count
+ end
+ end
+
+ if proc_count and proc_count > 0 then
+ return proc_count
+ else
+ return nil
+ end
+end
+
+function substitute_values(string, values)
+ local substitutor = function(match)
+ if match == "%" then
+ return "%"
+ else
+ -- nil is discarded by gsub
+ return values[match]
+ end
+ end
+
+ local substituted = string:gsub('%%(.)', substitutor)
+ return substituted
+end
+
+-- ASS HELPERS --
+function round_rect_top( ass, x0, y0, x1, y1, r )
+ local c = 0.551915024494 * r -- circle approximation
+ ass:move_to(x0 + r, y0)
+ ass:line_to(x1 - r, y0) -- top line
+ if r > 0 then
+ ass:bezier_curve(x1 - r + c, y0, x1, y0 + r - c, x1, y0 + r) -- top right corner
+ end
+ ass:line_to(x1, y1) -- right line
+ ass:line_to(x0, y1) -- bottom line
+ ass:line_to(x0, y0 + r) -- left line
+ if r > 0 then
+ ass:bezier_curve(x0, y0 + r - c, x0 + r - c, y0, x0 + r, y0) -- top left corner
+ end
+end
+
+function round_rect(ass, x0, y0, x1, y1, rtl, rtr, rbr, rbl)
+ local c = 0.551915024494
+ ass:move_to(x0 + rtl, y0)
+ ass:line_to(x1 - rtr, y0) -- top line
+ if rtr > 0 then
+ ass:bezier_curve(x1 - rtr + rtr*c, y0, x1, y0 + rtr - rtr*c, x1, y0 + rtr) -- top right corner
+ end
+ ass:line_to(x1, y1 - rbr) -- right line
+ if rbr > 0 then
+ ass:bezier_curve(x1, y1 - rbr + rbr*c, x1 - rbr + rbr*c, y1, x1 - rbr, y1) -- bottom right corner
+ end
+ ass:line_to(x0 + rbl, y1) -- bottom line
+ if rbl > 0 then
+ ass:bezier_curve(x0 + rbl - rbl*c, y1, x0, y1 - rbl + rbl*c, x0, y1 - rbl) -- bottom left corner
+ end
+ ass:line_to(x0, y0 + rtl) -- left line
+ if rtl > 0 then
+ ass:bezier_curve(x0, y0 + rtl - rtl*c, x0 + rtl - rtl*c, y0, x0 + rtl, y0) -- top left corner
+ end
+end
+local SCRIPT_NAME = "mpv_thumbnail_script"
+
+local default_cache_base = ON_WINDOWS and os.getenv("TEMP") or "/tmp/"
+
+local thumbnailer_options = {
+ -- The thumbnail directory
+ cache_directory = join_paths(default_cache_base, "mpv_thumbs_cache"),
+
+ ------------------------
+ -- Generation options --
+ ------------------------
+
+ -- Automatically generate the thumbnails on video load, without a keypress
+ autogenerate = true,
+
+ -- Only automatically thumbnail videos shorter than this (seconds)
+ autogenerate_max_duration = 3600, -- 1 hour
+
+ -- SHA1-sum filenames over this length
+ -- It's nice to know what files the thumbnails are (hence directory names)
+ -- but long URLs may approach filesystem limits.
+ hash_filename_length = 128,
+
+ -- Use mpv to generate thumbnail even if ffmpeg is found in PATH
+ -- ffmpeg does not handle ordered chapters (MKVs which rely on other MKVs)!
+ -- mpv is a bit slower, but has better support overall (eg. subtitles in the previews)
+ prefer_mpv = true,
+
+ -- Explicitly disable subtitles on the mpv sub-calls
+ mpv_no_sub = false,
+ -- Add a "--no-config" to the mpv sub-call arguments
+ mpv_no_config = false,
+ -- Add a "--profile=<mpv_profile>" to the mpv sub-call arguments
+ -- Use "" to disable
+ mpv_profile = "",
+ -- Output debug logs to <thumbnail_path>.log, ala <cache_directory>/<video_filename>/000000.bgra.log
+ -- The logs are removed after successful encodes, unless you set mpv_keep_logs below
+ mpv_logs = true,
+ -- Keep all mpv logs, even the succesfull ones
+ mpv_keep_logs = false,
+
+ -- Disable the built-in keybind ("T") to add your own
+ disable_keybinds = false,
+
+ ---------------------
+ -- Display options --
+ ---------------------
+
+ -- Move the thumbnail up or down
+ -- For example:
+ -- topbar/bottombar: 24
+ -- rest: 0
+ vertical_offset = 24,
+
+ -- Adjust background padding
+ -- Examples:
+ -- topbar: 0, 10, 10, 10
+ -- bottombar: 10, 0, 10, 10
+ -- slimbox/box: 10, 10, 10, 10
+ pad_top = 10,
+ pad_bot = 0,
+ pad_left = 10,
+ pad_right = 10,
+
+ -- If true, pad values are screen-pixels. If false, video-pixels.
+ pad_in_screenspace = true,
+ -- Calculate pad into the offset
+ offset_by_pad = true,
+
+ -- Background color in BBGGRR
+ background_color = "000000",
+ -- Alpha: 0 - fully opaque, 255 - transparent
+ background_alpha = 80,
+
+ -- Keep thumbnail on the screen near left or right side
+ constrain_to_screen = true,
+
+ -- Do not display the thumbnailing progress
+ hide_progress = false,
+
+ -----------------------
+ -- Thumbnail options --
+ -----------------------
+
+ -- The maximum dimensions of the thumbnails (pixels)
+ thumbnail_width = 200,
+ thumbnail_height = 200,
+
+ -- The thumbnail count target
+ -- (This will result in a thumbnail every ~10 seconds for a 25 minute video)
+ thumbnail_count = 150,
+
+ -- The above target count will be adjusted by the minimum and
+ -- maximum time difference between thumbnails.
+ -- The thumbnail_count will be used to calculate a target separation,
+ -- and min/max_delta will be used to constrict it.
+
+ -- In other words, thumbnails will be:
+ -- at least min_delta seconds apart (limiting the amount)
+ -- at most max_delta seconds apart (raising the amount if needed)
+ min_delta = 5,
+ -- 120 seconds aka 2 minutes will add more thumbnails when the video is over 5 hours!
+ max_delta = 90,
+
+
+ -- Overrides for remote urls (you generally want less thumbnails!)
+ -- Thumbnailing network paths will be done with mpv
+
+ -- Allow thumbnailing network paths (naive check for "://")
+ thumbnail_network = false,
+ -- Override thumbnail count, min/max delta
+ remote_thumbnail_count = 60,
+ remote_min_delta = 15,
+ remote_max_delta = 120,
+
+ -- Try to grab the raw stream and disable ytdl for the mpv subcalls
+ -- Much faster than passing the url to ytdl again, but may cause problems with some sites
+ remote_direct_stream = true,
+}
+
+read_options(thumbnailer_options, SCRIPT_NAME)
+function skip_nil(tbl)
+ local n = {}
+ for k, v in pairs(tbl) do
+ table.insert(n, v)
+ end
+ return n
+end
+
+function create_thumbnail_mpv(file_path, timestamp, size, output_path, options)
+ options = options or {}
+
+ local ytdl_disabled = not options.enable_ytdl and (mp.get_property_native("ytdl") == false
+ or thumbnailer_options.remote_direct_stream)
+
+ local header_fields_arg = nil
+ local header_fields = mp.get_property_native("http-header-fields")
+ if #header_fields > 0 then
+ -- We can't escape the headers, mpv won't parse "--http-header-fields='Name: value'" properly
+ header_fields_arg = "--http-header-fields=" .. table.concat(header_fields, ",")
+ end
+
+ local profile_arg = nil
+ if thumbnailer_options.mpv_profile ~= "" then
+ profile_arg = "--profile=" .. thumbnailer_options.mpv_profile
+ end
+
+ local log_arg = "--log-file=" .. output_path .. ".log"
+
+ local mpv_command = skip_nil({
+ "mpv",
+ -- Hide console output
+ "--msg-level=all=no",
+
+ -- Disable ytdl
+ (ytdl_disabled and "--no-ytdl" or nil),
+ -- Pass HTTP headers from current instance
+ header_fields_arg,
+ -- Pass User-Agent and Referer - should do no harm even with ytdl active
+ "--user-agent=" .. mp.get_property_native("user-agent"),
+ "--referrer=" .. mp.get_property_native("referrer"),
+ -- Disable hardware decoding
+ "--hwdec=no",
+
+ -- Insert --no-config, --profile=... and --log-file if enabled
+ (thumbnailer_options.mpv_no_config and "--no-config" or nil),
+ profile_arg,
+ (thumbnailer_options.mpv_logs and log_arg or nil),
+
+ file_path,
+
+ "--start=" .. tostring(timestamp),
+ "--frames=1",
+ "--hr-seek=yes",
+ "--no-audio",
+ -- Optionally disable subtitles
+ (thumbnailer_options.mpv_no_sub and "--no-sub" or nil),
+
+ ("--vf=scale=%d:%d"):format(size.w, size.h),
+ "--vf-add=format=bgra",
+ "--of=rawvideo",
+ "--ovc=rawvideo",
+ ("--o=%s"):format(output_path)
+ })
+ return utils.subprocess({args=mpv_command})
+end
+
+
+function create_thumbnail_ffmpeg(file_path, timestamp, size, output_path)
+ local ffmpeg_command = {
+ "ffmpeg",
+ "-loglevel", "quiet",
+ "-noaccurate_seek",
+ "-ss", format_time(timestamp, ":"),
+ "-i", file_path,
+
+ "-frames:v", "1",
+ "-an",
+
+ "-vf", ("scale=%d:%d"):format(size.w, size.h),
+ "-c:v", "rawvideo",
+ "-pix_fmt", "bgra",
+ "-f", "rawvideo",
+
+ "-y", output_path
+ }
+ return utils.subprocess({args=ffmpeg_command})
+end
+
+
+function check_output(ret, output_path, is_mpv)
+ local log_path = output_path .. ".log"
+ local success = true
+
+ if ret.killed_by_us then
+ return nil
+ else
+ if ret.error or ret.status ~= 0 then
+ msg.error("Thumbnailing command failed!")
+ msg.error("mpv process error:", ret.error)
+ msg.error("Process stdout:", ret.stdout)
+ if is_mpv then
+ msg.error("Debug log:", log_path)
+ end
+
+ success = false
+ end
+
+ if not file_exists(output_path) then
+ msg.error("Output file missing!", output_path)
+ success = false
+ end
+ end
+
+ if is_mpv and not thumbnailer_options.mpv_keep_logs then
+ -- Remove successful debug logs
+ if success and file_exists(log_path) then
+ os.remove(log_path)
+ end
+ end
+
+ return success
+end
+
+
+function do_worker_job(state_json_string, frames_json_string)
+ msg.debug("Handling given job")
+ local thumb_state, err = utils.parse_json(state_json_string)
+ if err then
+ msg.error("Failed to parse state JSON")
+ return
+ end
+
+ local thumbnail_indexes, err = utils.parse_json(frames_json_string)
+ if err then
+ msg.error("Failed to parse thumbnail frame indexes")
+ return
+ end
+
+ local thumbnail_func = create_thumbnail_mpv
+ if not thumbnailer_options.prefer_mpv then
+ if ExecutableFinder:get_executable_path("ffmpeg") then
+ thumbnail_func = create_thumbnail_ffmpeg
+ else
+ msg.warn("Could not find ffmpeg in PATH! Falling back on mpv.")
+ end
+ end
+
+ local file_duration = mp.get_property_native("duration")
+ local file_path = thumb_state.worker_input_path
+
+ if thumb_state.is_remote then
+ if (thumbnail_func == create_thumbnail_ffmpeg) then
+ msg.warn("Thumbnailing remote path, falling back on mpv.")
+ end
+ thumbnail_func = create_thumbnail_mpv
+ end
+
+ local generate_thumbnail_for_index = function(thumbnail_index)
+ -- Given a 1-based thumbnail index, generate a thumbnail for it based on the thumbnailer state
+ local thumb_idx = thumbnail_index - 1
+ msg.debug("Starting work on thumbnail", thumb_idx)
+
+ local thumbnail_path = thumb_state.thumbnail_template:format(thumb_idx)
+ -- Grab the "middle" of the thumbnail duration instead of the very start, and leave some margin in the end
+ local timestamp = math.min(file_duration - 0.25, (thumb_idx + 0.5) * thumb_state.thumbnail_delta)
+
+ mp.commandv("script-message", "mpv_thumbnail_script-progress", tostring(thumbnail_index))
+
+ -- The expected size (raw BGRA image)
+ local thumbnail_raw_size = (thumb_state.thumbnail_size.w * thumb_state.thumbnail_size.h * 4)
+
+ local need_thumbnail_generation = false
+
+ -- Check if the thumbnail already exists and is the correct size
+ local thumbnail_file = io.open(thumbnail_path, "rb")
+ if thumbnail_file == nil then
+ need_thumbnail_generation = true
+ else
+ local existing_thumbnail_filesize = thumbnail_file:seek("end")
+ if existing_thumbnail_filesize ~= thumbnail_raw_size then
+ -- Size doesn't match, so (re)generate
+ msg.warn("Thumbnail", thumb_idx, "did not match expected size, regenerating")
+ need_thumbnail_generation = true
+ end
+ thumbnail_file:close()
+ end
+
+ if need_thumbnail_generation then
+ local ret = thumbnail_func(file_path, timestamp, thumb_state.thumbnail_size, thumbnail_path, thumb_state.worker_extra)
+ local success = check_output(ret, thumbnail_path, thumbnail_func == create_thumbnail_mpv)
+
+ if success == nil then
+ -- Killed by us, changing files, ignore
+ msg.debug("Changing files, subprocess killed")
+ return true
+ elseif not success then
+ -- Real failure
+ mp.osd_message("Thumbnailing failed, check console for details", 3.5)
+ return true
+ end
+ else
+ msg.debug("Thumbnail", thumb_idx, "already done!")
+ end
+
+ -- Verify thumbnail size
+ -- Sometimes ffmpeg will output an empty file when seeking to a "bad" section (usually the end)
+ thumbnail_file = io.open(thumbnail_path, "rb")
+
+ -- Bail if we can't read the file (it should really exist by now, we checked this in check_output!)
+ if thumbnail_file == nil then
+ msg.error("Thumbnail suddenly disappeared!")
+ return true
+ end
+
+ -- Check the size of the generated file
+ local thumbnail_file_size = thumbnail_file:seek("end")
+ thumbnail_file:close()
+
+ -- Check if the file is big enough
+ local missing_bytes = math.max(0, thumbnail_raw_size - thumbnail_file_size)
+ if missing_bytes > 0 then
+ msg.warn(("Thumbnail missing %d bytes (expected %d, had %d), padding %s"):format(
+ missing_bytes, thumbnail_raw_size, thumbnail_file_size, thumbnail_path
+ ))
+ -- Pad the file if it's missing content (eg. ffmpeg seek to file end)
+ thumbnail_file = io.open(thumbnail_path, "ab")
+ thumbnail_file:write(string.rep(string.char(0), missing_bytes))
+ thumbnail_file:close()
+ end
+
+ msg.debug("Finished work on thumbnail", thumb_idx)
+ mp.commandv("script-message", "mpv_thumbnail_script-ready", tostring(thumbnail_index), thumbnail_path)
+ end
+
+ msg.debug(("Generating %d thumbnails @ %dx%d for %q"):format(
+ #thumbnail_indexes,
+ thumb_state.thumbnail_size.w,
+ thumb_state.thumbnail_size.h,
+ file_path))
+
+ for i, thumbnail_index in ipairs(thumbnail_indexes) do
+ local bail = generate_thumbnail_for_index(thumbnail_index)
+ if bail then return end
+ end
+
+end
+
+-- Set up listeners and keybinds
+
+-- Job listener
+mp.register_script_message("mpv_thumbnail_script-job", do_worker_job)
+
+
+-- Register this worker with the master script
+local register_timer = nil
+local register_timeout = mp.get_time() + 1.5
+
+local register_function = function()
+ if mp.get_time() > register_timeout and register_timer then
+ msg.error("Thumbnail worker registering timed out")
+ register_timer:stop()
+ else
+ msg.debug("Announcing self to master...")
+ mp.commandv("script-message", "mpv_thumbnail_script-worker", mp.get_script_name())
+ end
+end
+
+register_timer = mp.add_periodic_timer(0.1, register_function)
+
+mp.register_script_message("mpv_thumbnail_script-slaved", function()
+ msg.debug("Successfully registered with master")
+ register_timer:stop()
+end)
diff --git a/stow/mpv/dot-config/mpv/scripts/youtube-quality.lua b/stow/mpv/dot-config/mpv/scripts/youtube-quality.lua
new file mode 100644
index 0000000..b587f37
--- /dev/null
+++ b/stow/mpv/dot-config/mpv/scripts/youtube-quality.lua
@@ -0,0 +1,275 @@
+-- youtube-quality.lua
+--
+-- Change youtube video quality on the fly.
+--
+-- Diplays a menu that lets you switch to different ytdl-format settings while
+-- you're in the middle of a video (just like you were using the web player).
+--
+-- Bound to ctrl-f by default.
+
+local mp = require 'mp'
+local utils = require 'mp.utils'
+local msg = require 'mp.msg'
+local assdraw = require 'mp.assdraw'
+
+local opts = {
+ --key bindings
+ toggle_menu_binding = "ctrl+f",
+ up_binding = "UP",
+ down_binding = "DOWN",
+ select_binding = "ENTER",
+
+ --formatting / cursors
+ selected_and_active = "▶ - ",
+ selected_and_inactive = "● - ",
+ unselected_and_active = "▷ - ",
+ unselected_and_inactive = "○ - ",
+
+ --font size scales by window, if false requires larger font and padding sizes
+ scale_playlist_by_window=false,
+
+ --playlist ass style overrides inside curly brackets, \keyvalue is one field, extra \ for escape in lua
+ --example {\\fnUbuntu\\fs10\\b0\\bord1} equals: font=Ubuntu, size=10, bold=no, border=1
+ --read http://docs.aegisub.org/3.2/ASS_Tags/ for reference of tags
+ --undeclared tags will use default osd settings
+ --these styles will be used for the whole playlist. More specific styling will need to be hacked in
+ --
+ --(a monospaced font is recommended but not required)
+ style_ass_tags = "{\\fnmonospace}",
+
+ --paddings for top left corner
+ text_padding_x = 5,
+ text_padding_y = 5,
+
+ --other
+ menu_timeout = 10,
+
+ --use youtube-dl to fetch a list of available formats (overrides quality_strings)
+ fetch_formats = true,
+
+ --default menu entries
+ quality_strings=[[
+ [
+ {"4320p" : "bestvideo[height<=?4320p]+bestaudio/best"},
+ {"2160p" : "bestvideo[height<=?2160]+bestaudio/best"},
+ {"1440p" : "bestvideo[height<=?1440]+bestaudio/best"},
+ {"1080p" : "bestvideo[height<=?1080]+bestaudio/best"},
+ {"720p" : "bestvideo[height<=?720]+bestaudio/best"},
+ {"480p" : "bestvideo[height<=?480]+bestaudio/best"},
+ {"360p" : "bestvideo[height<=?360]+bestaudio/best"},
+ {"240p" : "bestvideo[height<=?240]+bestaudio/best"},
+ {"144p" : "bestvideo[height<=?144]+bestaudio/best"}
+ ]
+ ]],
+}
+(require 'mp.options').read_options(opts, "youtube-quality")
+opts.quality_strings = utils.parse_json(opts.quality_strings)
+
+local destroyer = nil
+
+
+function show_menu()
+ local selected = 1
+ local active = 0
+ local current_ytdl_format = mp.get_property("ytdl-format")
+ msg.verbose("current ytdl-format: "..current_ytdl_format)
+ local num_options = 0
+ local options = {}
+
+
+ if opts.fetch_formats then
+ options, num_options = download_formats()
+ end
+
+ if next(options) == nil then
+ for i,v in ipairs(opts.quality_strings) do
+ num_options = num_options + 1
+ for k,v2 in pairs(v) do
+ options[i] = {label = k, format=v2}
+ if v2 == current_ytdl_format then
+ active = i
+ selected = active
+ end
+ end
+ end
+ end
+
+ --set the cursor to the currently format
+ for i,v in ipairs(options) do
+ if v.format == current_ytdl_format then
+ active = i
+ selected = active
+ break
+ end
+ end
+
+ function selected_move(amt)
+ selected = selected + amt
+ if selected < 1 then selected = num_options
+ elseif selected > num_options then selected = 1 end
+ timeout:kill()
+ timeout:resume()
+ draw_menu()
+ end
+ function choose_prefix(i)
+ if i == selected and i == active then return opts.selected_and_active
+ elseif i == selected then return opts.selected_and_inactive end
+
+ if i ~= selected and i == active then return opts.unselected_and_active
+ elseif i ~= selected then return opts.unselected_and_inactive end
+ return "> " --shouldn't get here.
+ end
+
+ function draw_menu()
+ local ass = assdraw.ass_new()
+
+ ass:pos(opts.text_padding_x, opts.text_padding_y)
+ ass:append(opts.style_ass_tags)
+
+ for i,v in ipairs(options) do
+ ass:append(choose_prefix(i)..v.label.."\\N")
+ end
+
+ local w, h = mp.get_osd_size()
+ if opts.scale_playlist_by_window then w,h = 0, 0 end
+ mp.set_osd_ass(w, h, ass.text)
+ end
+
+ function destroy()
+ timeout:kill()
+ mp.set_osd_ass(0,0,"")
+ mp.remove_key_binding("move_up")
+ mp.remove_key_binding("move_down")
+ mp.remove_key_binding("select")
+ mp.remove_key_binding("escape")
+ destroyer = nil
+ end
+ timeout = mp.add_periodic_timer(opts.menu_timeout, destroy)
+ destroyer = destroy
+
+ mp.add_forced_key_binding(opts.up_binding, "move_up", function() selected_move(-1) end, {repeatable=true})
+ mp.add_forced_key_binding(opts.down_binding, "move_down", function() selected_move(1) end, {repeatable=true})
+ mp.add_forced_key_binding(opts.select_binding, "select", function()
+ destroy()
+ mp.set_property("ytdl-format", options[selected].format)
+ reload_resume()
+ end)
+ mp.add_forced_key_binding(opts.toggle_menu_binding, "escape", destroy)
+
+ draw_menu()
+ return
+end
+
+local ytdl = {
+ path = "youtube-dl",
+ searched = false,
+ blacklisted = {}
+}
+
+format_cache={}
+function download_formats()
+ local function exec(args)
+ local ret = utils.subprocess({args = args})
+ return ret.status, ret.stdout, ret
+ end
+
+ local function table_size(t)
+ s = 0
+ for i,v in ipairs(t) do
+ s = s+1
+ end
+ return s
+ end
+
+ local url = mp.get_property("path")
+
+ url = string.gsub(url, "ytdl://", "") -- Strip possible ytdl:// prefix.
+
+ -- don't fetch the format list if we already have it
+ if format_cache[url] ~= nil then
+ local res = format_cache[url]
+ return res, table_size(res)
+ end
+ mp.osd_message("fetching available formats with youtube-dl...", 60)
+
+ if not (ytdl.searched) then
+ local ytdl_mcd = mp.find_config_file("youtube-dl")
+ if not (ytdl_mcd == nil) then
+ msg.verbose("found youtube-dl at: " .. ytdl_mcd)
+ ytdl.path = ytdl_mcd
+ end
+ ytdl.searched = true
+ end
+
+ local command = {ytdl.path, "--no-warnings", "--no-playlist", "-J"}
+ table.insert(command, url)
+ local es, json, result = exec(command)
+
+ if (es < 0) or (json == nil) or (json == "") then
+ mp.osd_message("fetching formats failed...", 1)
+ msg.error("failed to get format list: " .. err)
+ return {}, 0
+ end
+
+ local json, err = utils.parse_json(json)
+
+ if (json == nil) then
+ mp.osd_message("fetching formats failed...", 1)
+ msg.error("failed to parse JSON data: " .. err)
+ return {}, 0
+ end
+
+ res = {}
+ msg.verbose("youtube-dl succeeded!")
+ for i,v in ipairs(json.formats) do
+ if v.vcodec ~= "none" then
+ local fps = v.fps and v.fps.."fps" or ""
+ local resolution = string.format("%sx%s", v.width, v.height)
+ local l = string.format("%-9s %-5s (%-4s / %s)", resolution, fps, v.ext, v.vcodec)
+ local f = string.format("%s+bestaudio/best", v.format_id)
+ table.insert(res, {label=l, format=f, width=v.width })
+ end
+ end
+
+ table.sort(res, function(a, b) return a.width > b.width end)
+
+ mp.osd_message("", 0)
+ format_cache[url] = res
+ return res, table_size(res)
+end
+
+
+-- register script message to show menu
+mp.register_script_message("toggle-quality-menu",
+function()
+ if destroyer ~= nil then
+ destroyer()
+ else
+ show_menu()
+ end
+end)
+
+-- keybind to launch menu
+mp.add_key_binding(opts.toggle_menu_binding, "quality-menu", show_menu)
+
+-- special thanks to reload.lua (https://github.com/4e6/mpv-reload/)
+function reload_resume()
+ local playlist_pos = mp.get_property_number("playlist-pos")
+ local reload_duration = mp.get_property_native("duration")
+ local time_pos = mp.get_property("time-pos")
+
+ mp.set_property_number("playlist-pos", playlist_pos)
+
+ -- Tries to determine live stream vs. pre-recordered VOD. VOD has non-zero
+ -- duration property. When reloading VOD, to keep the current time position
+ -- we should provide offset from the start. Stream doesn't have fixed start.
+ -- Decent choice would be to reload stream from it's current 'live' positon.
+ -- That's the reason we don't pass the offset when reloading streams.
+ if reload_duration and reload_duration > 0 then
+ local function seeker()
+ mp.commandv("seek", time_pos, "absolute")
+ mp.unregister_event(seeker)
+ end
+ mp.register_event("file-loaded", seeker)
+ end
+end
diff --git a/stow/neofetch/dot-config/neofetch/config.conf b/stow/neofetch/dot-config/neofetch/config.conf
new file mode 100644
index 0000000..41e77d4
--- /dev/null
+++ b/stow/neofetch/dot-config/neofetch/config.conf
@@ -0,0 +1,764 @@
+# See this wiki page for more info:
+# https://github.com/dylanaraps/neofetch/wiki/Customizing-Info
+print_info() {
+ info title
+ info underline
+
+ info "OS" distro
+ info "Host" model
+ info "Kernel" kernel
+ info "Uptime" uptime
+ info "Packages" packages
+ info "Shell" shell
+ info "Resolution" resolution
+ info "DE" de
+ info "WM" wm
+ info "WM Theme" wm_theme
+ info "Theme" theme
+ info "Icons" icons
+ info "Terminal" term
+ info "Terminal Font" term_font
+ info "CPU" cpu
+ info "GPU" gpu
+ info "Memory" memory
+
+ # info "GPU Driver" gpu_driver # Linux/macOS only
+ # info "CPU Usage" cpu_usage
+ # info "Disk" disk
+ # info "Battery" battery
+ # info "Font" font
+ # info "Song" song
+ # [[ "$player" ]] && prin "Music Player" "$player"
+ # info "Local IP" local_ip
+ # info "Public IP" public_ip
+ # info "Users" users
+ # info "Locale" locale # This only works on glibc systems.
+
+ info cols
+}
+
+
+# Kernel
+
+
+# Shorten the output of the kernel function.
+#
+# Default: 'on'
+# Values: 'on', 'off'
+# Flag: --kernel_shorthand
+# Supports: Everything except *BSDs (except PacBSD and PC-BSD)
+#
+# Example:
+# on: '4.8.9-1-ARCH'
+# off: 'Linux 4.8.9-1-ARCH'
+kernel_shorthand="on"
+
+
+# Distro
+
+
+# Shorten the output of the distro function
+#
+# Default: 'off'
+# Values: 'on', 'tiny', 'off'
+# Flag: --distro_shorthand
+# Supports: Everything except Windows and Haiku
+distro_shorthand="off"
+
+# Show/Hide OS Architecture.
+# Show 'x86_64', 'x86' and etc in 'Distro:' output.
+#
+# Default: 'on'
+# Values: 'on', 'off'
+# Flag: --os_arch
+#
+# Example:
+# on: 'Arch Linux x86_64'
+# off: 'Arch Linux'
+os_arch="on"
+
+
+# Uptime
+
+
+# Shorten the output of the uptime function
+#
+# Default: 'on'
+# Values: 'on', 'tiny', 'off'
+# Flag: --uptime_shorthand
+#
+# Example:
+# on: '2 days, 10 hours, 3 mins'
+# tiny: '2d 10h 3m'
+# off: '2 days, 10 hours, 3 minutes'
+uptime_shorthand="on"
+
+
+# Memory
+
+
+# Show memory pecentage in output.
+#
+# Default: 'off'
+# Values: 'on', 'off'
+# Flag: --memory_percent
+#
+# Example:
+# on: '1801MiB / 7881MiB (22%)'
+# off: '1801MiB / 7881MiB'
+memory_percent="off"
+
+
+# Packages
+
+
+# Show/Hide Package Manager names.
+#
+# Default: 'tiny'
+# Values: 'on', 'tiny' 'off'
+# Flag: --package_managers
+#
+# Example:
+# on: '998 (pacman), 8 (flatpak), 4 (snap)'
+# tiny: '908 (pacman, flatpak, snap)'
+# off: '908'
+package_managers="on"
+
+
+# Shell
+
+
+# Show the path to $SHELL
+#
+# Default: 'off'
+# Values: 'on', 'off'
+# Flag: --shell_path
+#
+# Example:
+# on: '/bin/bash'
+# off: 'bash'
+shell_path="off"
+
+# Show $SHELL version
+#
+# Default: 'on'
+# Values: 'on', 'off'
+# Flag: --shell_version
+#
+# Example:
+# on: 'bash 4.4.5'
+# off: 'bash'
+shell_version="on"
+
+
+# CPU
+
+
+# CPU speed type
+#
+# Default: 'bios_limit'
+# Values: 'scaling_cur_freq', 'scaling_min_freq', 'scaling_max_freq', 'bios_limit'.
+# Flag: --speed_type
+# Supports: Linux with 'cpufreq'
+# NOTE: Any file in '/sys/devices/system/cpu/cpu0/cpufreq' can be used as a value.
+speed_type="bios_limit"
+
+# CPU speed shorthand
+#
+# Default: 'off'
+# Values: 'on', 'off'.
+# Flag: --speed_shorthand
+# NOTE: This flag is not supported in systems with CPU speed less than 1 GHz
+#
+# Example:
+# on: 'i7-6500U (4) @ 3.1GHz'
+# off: 'i7-6500U (4) @ 3.100GHz'
+speed_shorthand="off"
+
+# Enable/Disable CPU brand in output.
+#
+# Default: 'on'
+# Values: 'on', 'off'
+# Flag: --cpu_brand
+#
+# Example:
+# on: 'Intel i7-6500U'
+# off: 'i7-6500U (4)'
+cpu_brand="on"
+
+# CPU Speed
+# Hide/Show CPU speed.
+#
+# Default: 'on'
+# Values: 'on', 'off'
+# Flag: --cpu_speed
+#
+# Example:
+# on: 'Intel i7-6500U (4) @ 3.1GHz'
+# off: 'Intel i7-6500U (4)'
+cpu_speed="on"
+
+# CPU Cores
+# Display CPU cores in output
+#
+# Default: 'logical'
+# Values: 'logical', 'physical', 'off'
+# Flag: --cpu_cores
+# Support: 'physical' doesn't work on BSD.
+#
+# Example:
+# logical: 'Intel i7-6500U (4) @ 3.1GHz' (All virtual cores)
+# physical: 'Intel i7-6500U (2) @ 3.1GHz' (All physical cores)
+# off: 'Intel i7-6500U @ 3.1GHz'
+cpu_cores="logical"
+
+# CPU Temperature
+# Hide/Show CPU temperature.
+# Note the temperature is added to the regular CPU function.
+#
+# Default: 'off'
+# Values: 'C', 'F', 'off'
+# Flag: --cpu_temp
+# Supports: Linux, BSD
+# NOTE: For FreeBSD and NetBSD-based systems, you'll need to enable
+# coretemp kernel module. This only supports newer Intel processors.
+#
+# Example:
+# C: 'Intel i7-6500U (4) @ 3.1GHz [27.2°C]'
+# F: 'Intel i7-6500U (4) @ 3.1GHz [82.0°F]'
+# off: 'Intel i7-6500U (4) @ 3.1GHz'
+cpu_temp="off"
+
+
+# GPU
+
+
+# Enable/Disable GPU Brand
+#
+# Default: 'on'
+# Values: 'on', 'off'
+# Flag: --gpu_brand
+#
+# Example:
+# on: 'AMD HD 7950'
+# off: 'HD 7950'
+gpu_brand="on"
+
+# Which GPU to display
+#
+# Default: 'all'
+# Values: 'all', 'dedicated', 'integrated'
+# Flag: --gpu_type
+# Supports: Linux
+#
+# Example:
+# all:
+# GPU1: AMD HD 7950
+# GPU2: Intel Integrated Graphics
+#
+# dedicated:
+# GPU1: AMD HD 7950
+#
+# integrated:
+# GPU1: Intel Integrated Graphics
+gpu_type="all"
+
+
+# Resolution
+
+
+# Display refresh rate next to each monitor
+# Default: 'off'
+# Values: 'on', 'off'
+# Flag: --refresh_rate
+# Supports: Doesn't work on Windows.
+#
+# Example:
+# on: '1920x1080 @ 60Hz'
+# off: '1920x1080'
+refresh_rate="off"
+
+
+# Gtk Theme / Icons / Font
+
+
+# Shorten output of GTK Theme / Icons / Font
+#
+# Default: 'off'
+# Values: 'on', 'off'
+# Flag: --gtk_shorthand
+#
+# Example:
+# on: 'Numix, Adwaita'
+# off: 'Numix [GTK2], Adwaita [GTK3]'
+gtk_shorthand="off"
+
+
+# Enable/Disable gtk2 Theme / Icons / Font
+#
+# Default: 'on'
+# Values: 'on', 'off'
+# Flag: --gtk2
+#
+# Example:
+# on: 'Numix [GTK2], Adwaita [GTK3]'
+# off: 'Adwaita [GTK3]'
+gtk2="on"
+
+# Enable/Disable gtk3 Theme / Icons / Font
+#
+# Default: 'on'
+# Values: 'on', 'off'
+# Flag: --gtk3
+#
+# Example:
+# on: 'Numix [GTK2], Adwaita [GTK3]'
+# off: 'Numix [GTK2]'
+gtk3="on"
+
+
+# IP Address
+
+
+# Website to ping for the public IP
+#
+# Default: 'http://ident.me'
+# Values: 'url'
+# Flag: --ip_host
+public_ip_host="http://ident.me"
+
+# Public IP timeout.
+#
+# Default: '2'
+# Values: 'int'
+# Flag: --ip_timeout
+public_ip_timeout=2
+
+
+# Disk
+
+
+# Which disks to display.
+# The values can be any /dev/sdXX, mount point or directory.
+# NOTE: By default we only show the disk info for '/'.
+#
+# Default: '/'
+# Values: '/', '/dev/sdXX', '/path/to/drive'.
+# Flag: --disk_show
+#
+# Example:
+# disk_show=('/' '/dev/sdb1'):
+# 'Disk (/): 74G / 118G (66%)'
+# 'Disk (/mnt/Videos): 823G / 893G (93%)'
+#
+# disk_show=('/'):
+# 'Disk (/): 74G / 118G (66%)'
+#
+disk_show=('/')
+
+# Disk subtitle.
+# What to append to the Disk subtitle.
+#
+# Default: 'mount'
+# Values: 'mount', 'name', 'dir'
+# Flag: --disk_subtitle
+#
+# Example:
+# name: 'Disk (/dev/sda1): 74G / 118G (66%)'
+# 'Disk (/dev/sdb2): 74G / 118G (66%)'
+#
+# mount: 'Disk (/): 74G / 118G (66%)'
+# 'Disk (/mnt/Local Disk): 74G / 118G (66%)'
+# 'Disk (/mnt/Videos): 74G / 118G (66%)'
+#
+# dir: 'Disk (/): 74G / 118G (66%)'
+# 'Disk (Local Disk): 74G / 118G (66%)'
+# 'Disk (Videos): 74G / 118G (66%)'
+disk_subtitle="mount"
+
+
+# Song
+
+
+# Manually specify a music player.
+#
+# Default: 'auto'
+# Values: 'auto', 'player-name'
+# Flag: --music_player
+#
+# Available values for 'player-name':
+#
+# amarok
+# audacious
+# banshee
+# bluemindo
+# clementine
+# cmus
+# deadbeef
+# deepin-music
+# dragon
+# elisa
+# exaile
+# gnome-music
+# gmusicbrowser
+# gogglesmm
+# guayadeque
+# iTunes
+# juk
+# lollypop
+# mocp
+# mopidy
+# mpd
+# netease-cloud-music
+# pogo
+# pragha
+# qmmp
+# quodlibet
+# rhythmbox
+# sayonara
+# smplayer
+# spotify
+# strawberry
+# tomahawk
+# vlc
+# xmms2d
+# xnoise
+# yarock
+music_player="auto"
+
+# Format to display song information.
+#
+# Default: '%artist% - %album% - %title%'
+# Values: '%artist%', '%album%', '%title%'
+# Flag: --song_format
+#
+# Example:
+# default: 'Song: Jet - Get Born - Sgt Major'
+song_format="%artist% - %album% - %title%"
+
+# Print the Artist, Album and Title on separate lines
+#
+# Default: 'off'
+# Values: 'on', 'off'
+# Flag: --song_shorthand
+#
+# Example:
+# on: 'Artist: The Fratellis'
+# 'Album: Costello Music'
+# 'Song: Chelsea Dagger'
+#
+# off: 'Song: The Fratellis - Costello Music - Chelsea Dagger'
+song_shorthand="off"
+
+# 'mpc' arguments (specify a host, password etc).
+#
+# Default: ''
+# Example: mpc_args=(-h HOST -P PASSWORD)
+mpc_args=()
+
+
+# Text Colors
+
+
+# Text Colors
+#
+# Default: 'distro'
+# Values: 'distro', 'num' 'num' 'num' 'num' 'num' 'num'
+# Flag: --colors
+#
+# Each number represents a different part of the text in
+# this order: 'title', '@', 'underline', 'subtitle', 'colon', 'info'
+#
+# Example:
+# colors=(distro) - Text is colored based on Distro colors.
+# colors=(4 6 1 8 8 6) - Text is colored in the order above.
+colors=(distro)
+
+
+# Text Options
+
+
+# Toggle bold text
+#
+# Default: 'on'
+# Values: 'on', 'off'
+# Flag: --bold
+bold="on"
+
+# Enable/Disable Underline
+#
+# Default: 'on'
+# Values: 'on', 'off'
+# Flag: --underline
+underline_enabled="on"
+
+# Underline character
+#
+# Default: '-'
+# Values: 'string'
+# Flag: --underline_char
+underline_char="-"
+
+
+# Info Separator
+# Replace the default separator with the specified string.
+#
+# Default: ':'
+# Flag: --separator
+#
+# Example:
+# separator="->": 'Shell-> bash'
+# separator=" =": 'WM = dwm'
+separator=":"
+
+
+# Color Blocks
+
+
+# Color block range
+# The range of colors to print.
+#
+# Default: '0', '15'
+# Values: 'num'
+# Flag: --block_range
+#
+# Example:
+#
+# Display colors 0-7 in the blocks. (8 colors)
+# neofetch --block_range 0 7
+#
+# Display colors 0-15 in the blocks. (16 colors)
+# neofetch --block_range 0 15
+block_range=(0 15)
+
+# Toggle color blocks
+#
+# Default: 'on'
+# Values: 'on', 'off'
+# Flag: --color_blocks
+color_blocks="on"
+
+# Color block width in spaces
+#
+# Default: '3'
+# Values: 'num'
+# Flag: --block_width
+block_width=3
+
+# Color block height in lines
+#
+# Default: '1'
+# Values: 'num'
+# Flag: --block_height
+block_height=1
+
+
+# Progress Bars
+
+
+# Bar characters
+#
+# Default: '-', '='
+# Values: 'string', 'string'
+# Flag: --bar_char
+#
+# Example:
+# neofetch --bar_char 'elapsed' 'total'
+# neofetch --bar_char '-' '='
+bar_char_elapsed="-"
+bar_char_total="="
+
+# Toggle Bar border
+#
+# Default: 'on'
+# Values: 'on', 'off'
+# Flag: --bar_border
+bar_border="on"
+
+# Progress bar length in spaces
+# Number of chars long to make the progress bars.
+#
+# Default: '15'
+# Values: 'num'
+# Flag: --bar_length
+bar_length=15
+
+# Progress bar colors
+# When set to distro, uses your distro's logo colors.
+#
+# Default: 'distro', 'distro'
+# Values: 'distro', 'num'
+# Flag: --bar_colors
+#
+# Example:
+# neofetch --bar_colors 3 4
+# neofetch --bar_colors distro 5
+bar_color_elapsed="distro"
+bar_color_total="distro"
+
+
+# Info display
+# Display a bar with the info.
+#
+# Default: 'off'
+# Values: 'bar', 'infobar', 'barinfo', 'off'
+# Flags: --cpu_display
+# --memory_display
+# --battery_display
+# --disk_display
+#
+# Example:
+# bar: '[---=======]'
+# infobar: 'info [---=======]'
+# barinfo: '[---=======] info'
+# off: 'info'
+cpu_display="off"
+memory_display="off"
+battery_display="off"
+disk_display="off"
+
+
+# Backend Settings
+
+
+# Image backend.
+#
+# Default: 'ascii'
+# Values: 'ascii', 'caca', 'chafa', 'jp2a', 'iterm2', 'off',
+# 'termpix', 'pixterm', 'tycat', 'w3m', 'kitty'
+# Flag: --backend
+image_backend="ascii"
+
+# Image Source
+#
+# Which image or ascii file to display.
+#
+# Default: 'auto'
+# Values: 'auto', 'ascii', 'wallpaper', '/path/to/img', '/path/to/ascii', '/path/to/dir/'
+# 'command output (neofetch --ascii "$(fortune | cowsay -W 30)")'
+# Flag: --source
+#
+# NOTE: 'auto' will pick the best image source for whatever image backend is used.
+# In ascii mode, distro ascii art will be used and in an image mode, your
+# wallpaper will be used.
+image_source="auto"
+
+
+# Ascii Options
+
+
+# Ascii distro
+# Which distro's ascii art to display.
+#
+# Default: 'auto'
+# Values: 'auto', 'distro_name'
+# Flag: --ascii_distro
+#
+# NOTE: Arch and Ubuntu have 'old' logo variants.
+# Change this to 'arch_old' or 'ubuntu_old' to use the old logos.
+# NOTE: Ubuntu has flavor variants.
+# Change this to 'Lubuntu', 'Xubuntu', 'Ubuntu-GNOME' or 'Ubuntu-Budgie' to use the flavors.
+# NOTE: Arch, Crux and Gentoo have a smaller logo variant.
+# Change this to 'arch_small', 'crux_small' or 'gentoo_small' to use the small logos.
+ascii_distro="auto"
+
+# Ascii Colors
+#
+# Default: 'distro'
+# Values: 'distro', 'num' 'num' 'num' 'num' 'num' 'num'
+# Flag: --ascii_colors
+#
+# Example:
+# ascii_colors=(distro) - Ascii is colored based on Distro colors.
+# ascii_colors=(4 6 1 8 8 6) - Ascii is colored using these colors.
+ascii_colors=(distro)
+
+# Bold ascii logo
+# Whether or not to bold the ascii logo.
+#
+# Default: 'on'
+# Values: 'on', 'off'
+# Flag: --ascii_bold
+ascii_bold="on"
+
+
+# Image Options
+
+
+# Image loop
+# Setting this to on will make neofetch redraw the image constantly until
+# Ctrl+C is pressed. This fixes display issues in some terminal emulators.
+#
+# Default: 'off'
+# Values: 'on', 'off'
+# Flag: --loop
+image_loop="off"
+
+# Thumbnail directory
+#
+# Default: '~/.cache/thumbnails/neofetch'
+# Values: 'dir'
+thumbnail_dir="${XDG_CACHE_HOME:-${HOME}/.cache}/thumbnails/neofetch"
+
+# Crop mode
+#
+# Default: 'normal'
+# Values: 'normal', 'fit', 'fill'
+# Flag: --crop_mode
+#
+# See this wiki page to learn about the fit and fill options.
+# https://github.com/dylanaraps/neofetch/wiki/What-is-Waifu-Crop%3F
+crop_mode="normal"
+
+# Crop offset
+# Note: Only affects 'normal' crop mode.
+#
+# Default: 'center'
+# Values: 'northwest', 'north', 'northeast', 'west', 'center'
+# 'east', 'southwest', 'south', 'southeast'
+# Flag: --crop_offset
+crop_offset="center"
+
+# Image size
+# The image is half the terminal width by default.
+#
+# Default: 'auto'
+# Values: 'auto', '00px', '00%', 'none'
+# Flags: --image_size
+# --size
+image_size="auto"
+
+# Gap between image and text
+#
+# Default: '3'
+# Values: 'num', '-num'
+# Flag: --gap
+gap=3
+
+# Image offsets
+# Only works with the w3m backend.
+#
+# Default: '0'
+# Values: 'px'
+# Flags: --xoffset
+# --yoffset
+yoffset=0
+xoffset=0
+
+# Image background color
+# Only works with the w3m backend.
+#
+# Default: ''
+# Values: 'color', 'blue'
+# Flag: --bg_color
+background_color=
+
+
+# Misc Options
+
+# Stdout mode
+# Turn off all colors and disables image backend (ASCII/Image).
+# Useful for piping into another command.
+# Default: 'off'
+# Values: 'on', 'off'
+stdout="off"
diff --git a/files/neofetch/neofetchLogin.conf b/stow/neofetch/dot-config/neofetch/neofetchLogin.conf
index 7750481..7750481 100644
--- a/files/neofetch/neofetchLogin.conf
+++ b/stow/neofetch/dot-config/neofetch/neofetchLogin.conf
diff --git a/stow/newsboat/dot-config/newsboat/config b/stow/newsboat/dot-config/newsboat/config
new file mode 100644
index 0000000..b789f5a
--- /dev/null
+++ b/stow/newsboat/dot-config/newsboat/config
@@ -0,0 +1,11 @@
+bind-key l open
+bind-key l show-urls article
+bind-key h quit
+bind-key j down
+bind-key k up
+bind-key J next-feed
+bind-key K prev-feed
+bind-key g home
+bind-key G end
+
+external-url-viewer urlview
diff --git a/stow/nvim/dot-config/nvim/init.vim b/stow/nvim/dot-config/nvim/init.vim
new file mode 100644
index 0000000..f182e5b
--- /dev/null
+++ b/stow/nvim/dot-config/nvim/init.vim
@@ -0,0 +1,3 @@
+set runtimepath^=~/.vim runtimepath+=~/.vim/after
+let &packpath = &runtimepath
+source ~/.vimrc
diff --git a/stow/pandoc/dot-config/pandoc/luascripts/diagram-generator.lua b/stow/pandoc/dot-config/pandoc/luascripts/diagram-generator.lua
new file mode 100644
index 0000000..4d69ba3
--- /dev/null
+++ b/stow/pandoc/dot-config/pandoc/luascripts/diagram-generator.lua
@@ -0,0 +1,299 @@
+--[[
+ This Lua filter is used to create images with or without captions from
+ code blocks. Currently PlantUML, GraphViz, Tikz, and Python can be
+ processed. For further details, see README.md.
+
+ Thanks to @floriandd2ba and @jgm for the initial implementation of
+ the PlantUML filter, which I used as a template. Thanks also @muxueqz
+ for the code to generate a GraphViz image.
+]]
+
+-- The PlantUML path. If set, uses the environment variable PLANTUML or the
+-- value "plantuml.jar" (local PlantUML version). In order to define a
+-- PlantUML version per pandoc document, use the meta data to define the key
+-- "plantumlPath".
+local plantumlPath = os.getenv("PLANTUML") or "plantuml.jar"
+
+-- The Inkscape path. In order to define an Inkscape version per pandoc
+-- document, use the meta data to define the key "inkscapePath".
+local inkscapePath = os.getenv("INKSCAPE") or "inkscape"
+
+-- The Python path. In order to define a Python version per pandoc document,
+-- use the meta data to define the key "pythonPath".
+local pythonPath = os.getenv("PYTHON")
+
+-- The Python environment's activate script. Can be set on a per document
+-- basis by using the meta data key "activatePythonPath".
+local pythonActivatePath = os.getenv("PYTHON_ACTIVATE")
+
+-- The Java path. In order to define a Java version per pandoc document,
+-- use the meta data to define the key "javaPath".
+local javaPath = os.getenv("JAVA_HOME")
+if javaPath then
+ javaPath = javaPath .. package.config:sub(1,1) .. "bin"
+ .. package.config:sub(1,1) .. "java"
+else
+ javaPath = "java"
+end
+
+-- The dot (Graphviz) path. In order to define a dot version per pandoc
+-- document, use the meta data to define the key "dotPath".
+local dotPath = os.getenv("DOT") or "dot"
+
+-- The pdflatex path. In order to define a pdflatex version per pandoc
+-- document, use the meta data to define the key "pdflatexPath".
+local pdflatexPath = os.getenv("PDFLATEX") or "pdflatex"
+
+-- The default format is SVG i.e. vector graphics:
+local filetype = "svg"
+local mimetype = "image/svg+xml"
+
+-- Check for output formats that potentially cannot use SVG
+-- vector graphics. In these cases, we use a different format
+-- such as PNG:
+if FORMAT == "docx" then
+ filetype = "png"
+ mimetype = "image/png"
+elseif FORMAT == "pptx" then
+ filetype = "png"
+ mimetype = "image/png"
+elseif FORMAT == "rtf" then
+ filetype = "png"
+ mimetype = "image/png"
+end
+
+-- Execute the meta data table to determine the paths. This function
+-- must be called first to get the desired path. If one of these
+-- meta options was set, it gets used instead of the corresponding
+-- environment variable:
+function Meta(meta)
+ plantumlPath = meta.plantumlPath or plantumlPath
+ inkscapePath = meta.inkscapePath or inkscapePath
+ pythonPath = meta.pythonPath or pythonPath
+ pythonActivatePath = meta.activatePythonPath or pythonActivatePath
+ javaPath = meta.javaPath or javaPath
+ dotPath = meta.dotPath or dotPath
+ pdflatexPath = meta.pdflatexPath or pdflatexPath
+end
+
+-- Call plantuml.jar with some parameters (cf. PlantUML help):
+local function plantuml(puml, filetype)
+ local final = pandoc.pipe(javaPath, {"-jar", plantumlPath, "-t" .. filetype, "-pipe", "-charset", "UTF8"}, puml)
+ return final
+end
+
+-- Call dot (GraphViz) in order to generate the image
+-- (thanks @muxueqz for this code):
+local function graphviz(code, filetype)
+ local final = pandoc.pipe(dotPath, {"-T" .. filetype}, code)
+ return final
+end
+
+-- Compile LaTeX with Tikz code to an image:
+local function tikz2image(src, filetype, additionalPackages)
+
+ -- Define file names:
+ local outfile = string.format("./tmp-latex/file.%s", filetype)
+ local tmp = "./tmp-latex/file"
+ local tmpDir = "./tmp-latex/"
+
+ -- Ensure, that the tmp directory exists:
+ os.execute("mkdir -p tmp-latex")
+
+ -- Build and write the LaTeX document:
+ local f = io.open(tmp .. ".tex", 'w')
+ f:write("\\documentclass{standalone}\n\\usepackage{tikz}\n")
+
+ -- Any additional package(s) are desired?
+ if additionalPackages then
+ f:write(additionalPackages)
+ end
+
+ f:write("\\begin{document}\n")
+ f:write(src)
+ f:write("\n\\end{document}\n")
+ f:close()
+
+ -- Execute the LaTeX compiler:
+ pandoc.pipe(pdflatexPath, {'-output-directory', tmpDir, tmp}, '')
+
+ -- Build the basic Inkscape command for the conversion:
+ local baseCommand = " --without-gui --file=" .. tmp .. ".pdf"
+ local knownFormat = false
+
+ if filetype == "png" then
+
+ -- Append the subcommands to convert into a PNG file:
+ baseCommand = baseCommand .. " --export-png="
+ .. tmp .. ".png --export-dpi=300"
+ knownFormat = true
+
+ elseif filetype == "svg" then
+
+ -- Append the subcommands to convert into a SVG file:
+ baseCommand = baseCommand .. " --export-plain-svg=" .. tmp .. ".svg"
+ knownFormat = true
+
+ end
+
+ -- Unfortunately, continuation is only possible, if we know the actual
+ -- format:
+ if not knownFormat then
+ error(string.format("Don't know how to convert pdf to %s.", filetype))
+ end
+
+ local imgData = nil
+
+ -- We know the desired format. Thus, execute Inkscape:
+ os.execute("\"" .. inkscapePath .. "\"" .. baseCommand)
+
+ -- Try to open the image:
+ local r = io.open(tmp .. "." .. filetype, 'rb')
+
+ -- Read the image, if available:
+ if r then
+ imgData = r:read("*all")
+ r:close()
+ end
+
+ -- Delete the image tmp file:
+ os.remove(outfile)
+
+ -- Remove the temporary files:
+ os.remove(tmp .. ".tex")
+ os.remove(tmp .. ".pdf")
+ os.remove(tmp .. ".log")
+ os.remove(tmp .. ".aux")
+
+ return imgData
+end
+
+-- Run Python to generate an image:
+local function py2image(code, filetype)
+
+ -- Define the temp files:
+ local outfile = string.format('%s.%s', os.tmpname(), filetype)
+ local pyfile = os.tmpname()
+
+ -- Replace the desired destination's file type in the Python code:
+ local extendedCode = string.gsub(code, "%$FORMAT%$", filetype)
+
+ -- Replace the desired destination's path in the Python code:
+ extendedCode = string.gsub(extendedCode, "%$DESTINATION%$", outfile)
+
+ -- Write the Python code:
+ local f = io.open(pyfile, 'w')
+ f:write(extendedCode)
+ f:close()
+
+ -- Execute Python in the desired environment:
+ local pycmd = pythonPath .. ' ' .. pyfile
+ local command = pythonActivatePath
+ and pythonActivatePath .. ' && ' .. pycmd
+ or pycmd
+ os.execute(command)
+
+ -- Try to open the written image:
+ local r = io.open(outfile, 'rb')
+ local imgData = nil
+
+ -- When the image exist, read it:
+ if r then
+ imgData = r:read("*all")
+ r:close()
+ else
+ io.stderr:write(string.format("File '%s' could not be opened", outfile))
+ error 'Could not create image from python code.'
+ end
+
+ -- Delete the tmp files:
+ os.remove(pyfile)
+ os.remove(outfile)
+
+ return imgData
+end
+
+-- Executes each document's code block to find matching code blocks:
+function CodeBlock(block)
+
+ -- Predefine a potential image:
+ local fname = nil
+
+ -- Using a table with all known generators i.e. converters:
+ local converters = {
+ plantuml = plantuml,
+ graphviz = graphviz,
+ tikz = tikz2image,
+ py2image = py2image,
+ }
+
+ -- Check if a converter exists for this block. If not, return the block
+ -- unchanged.
+ local img_converter = converters[block.classes[1]]
+ if not img_converter then
+ return nil
+ end
+
+ -- Call the correct converter which belongs to the used class:
+ local success, img = pcall(img_converter, block.text,
+ filetype, block.attributes["additionalPackages"] or nil)
+
+ -- Was ok?
+ if success and img then
+ -- Hash the figure name and content:
+ fname = pandoc.sha1(img) .. "." .. filetype
+
+ -- Store the data in the media bag:
+ pandoc.mediabag.insert(fname, mimetype, img)
+
+ else
+
+ -- an error occured; img contains the error message
+ io.stderr:write(tostring(img))
+ io.stderr:write('\n')
+ error 'Image conversion failed. Aborting.'
+
+ end
+
+ -- Case: This code block was an image e.g. PlantUML or dot/Graphviz, etc.:
+ if fname then
+
+ -- Define the default caption:
+ local caption = {}
+ local enableCaption = nil
+
+ -- If the user defines a caption, use it:
+ if block.attributes["caption"] then
+ caption = pandoc.read(block.attributes.caption).blocks[1].content
+
+ -- This is pandoc's current hack to enforce a caption:
+ enableCaption = "fig:"
+ end
+
+ -- Create a new image for the document's structure. Attach the user's
+ -- caption. Also use a hack (fig:) to enforce pandoc to create a
+ -- figure i.e. attach a caption to the image.
+ local imgObj = pandoc.Image(caption, fname, enableCaption)
+
+ -- Now, transfer the attribute "name" from the code block to the new
+ -- image block. It might gets used by the figure numbering lua filter.
+ -- If the figure numbering gets not used, this additional attribute
+ -- gets ignored as well.
+ if block.attributes["name"] then
+ imgObj.attributes["name"] = block.attributes["name"]
+ end
+
+ -- Finally, put the image inside an empty paragraph. By returning the
+ -- resulting paragraph object, the source code block gets replaced by
+ -- the image:
+ return pandoc.Para{ imgObj }
+ end
+end
+
+-- Normally, pandoc will run the function in the built-in order Inlines ->
+-- Blocks -> Meta -> Pandoc. We instead want Meta -> Blocks. Thus, we must
+-- define our custom order:
+return {
+ {Meta = Meta},
+ {CodeBlock = CodeBlock},
+}
diff --git a/stow/picom/dot-config/picom/picom.conf b/stow/picom/dot-config/picom/picom.conf
new file mode 100644
index 0000000..2f4dfc7
--- /dev/null
+++ b/stow/picom/dot-config/picom/picom.conf
@@ -0,0 +1,502 @@
+#################################
+# Animations #
+#################################
+# requires https://github.com/jonaburg/picom
+# (These are also the default values)
+transition-length = 200
+transition-pow-x = 0.5
+transition-pow-y = 0.5
+transition-pow-w = 0.5
+transition-pow-h = 0.5
+size-transition = true
+
+
+#################################
+# Corners #
+#################################
+# requires: https://github.com/sdhand/compton or https://github.com/jonaburg/picom
+corner-radius = 0;
+rounded-corners-exclude = [
+ #"window_type = 'normal'",
+ "class_g = 'awesome'",
+ #"class_g = 'URxvt'",
+ "class_g = 'i3bar'",
+ "class_g = 'dwm'",
+ "class_g = 'XTerm'",
+ "class_g = 'kitty'",
+ "class_g = 'Alacritty'",
+ "class_g = 'Polybar'",
+ "class_g = 'code-oss'",
+ #"class_g = 'TelegramDesktop'",
+ "class_g = 'firefox'",
+ "class_g = 'Thunderbird'"
+];
+round-borders = 0;
+round-borders-exclude = [
+ #"class_g = 'TelegramDesktop'",
+];
+
+#################################
+# Shadows #
+#################################
+
+
+# Enabled client-side shadows on windows. Note desktop windows
+# (windows with '_NET_WM_WINDOW_TYPE_DESKTOP') never get shadow,
+# unless explicitly requested using the wintypes option.
+#
+# shadow = false
+shadow = true;
+
+# The blur radius for shadows, in pixels. (defaults to 12)
+# shadow-radius = 12
+shadow-radius = 12;
+
+# The opacity of shadows. (0.0 - 1.0, defaults to 0.75)
+# shadow-opacity = .75
+
+# The left offset for shadows, in pixels. (defaults to -15)
+# shadow-offset-x = -15
+shadow-offset-x = -7;
+
+# The top offset for shadows, in pixels. (defaults to -15)
+# shadow-offset-y = -15
+shadow-offset-y = -7;
+
+# Avoid drawing shadows on dock/panel windows. This option is deprecated,
+# you should use the *wintypes* option in your config file instead.
+#
+# no-dock-shadow = false
+
+# Don't draw shadows on drag-and-drop windows. This option is deprecated,
+# you should use the *wintypes* option in your config file instead.
+#
+# no-dnd-shadow = false
+
+# Red color value of shadow (0.0 - 1.0, defaults to 0).
+# shadow-red = 0
+
+# Green color value of shadow (0.0 - 1.0, defaults to 0).
+# shadow-green = 0
+
+# Blue color value of shadow (0.0 - 1.0, defaults to 0).
+# shadow-blue = 0
+
+# Do not paint shadows on shaped windows. Note shaped windows
+# here means windows setting its shape through X Shape extension.
+# Those using ARGB background is beyond our control.
+# Deprecated, use
+# shadow-exclude = 'bounding_shaped'
+# or
+# shadow-exclude = 'bounding_shaped && !rounded_corners'
+# instead.
+#
+# shadow-ignore-shaped = ''
+
+# Specify a list of conditions of windows that should have no shadow.
+#
+# examples:
+# shadow-exclude = "n:e:Notification";
+#
+# shadow-exclude = []
+shadow-exclude = [
+ "name = 'Notification'",
+ "class_g = 'Conky'",
+ "class_g ?= 'Notify-osd'",
+ "class_g = 'Cairo-clock'",
+ "class_g = 'slop'",
+ "class_g = 'Polybar'",
+ "_GTK_FRAME_EXTENTS@:c"
+];
+
+# Specify a X geometry that describes the region in which shadow should not
+# be painted in, such as a dock window region. Use
+# shadow-exclude-reg = "x10+0+0"
+# for example, if the 10 pixels on the bottom of the screen should not have shadows painted on.
+#
+# shadow-exclude-reg = ""
+
+# Crop shadow of a window fully on a particular Xinerama screen to the screen.
+# xinerama-shadow-crop = false
+
+
+#################################
+# Fading #
+#################################
+
+
+# Fade windows in/out when opening/closing and when opacity changes,
+# unless no-fading-openclose is used.
+# fading = false
+fading = true;
+
+# Opacity change between steps while fading in. (0.01 - 1.0, defaults to 0.028)
+# fade-in-step = 0.028
+fade-in-step = 0.03;
+
+# Opacity change between steps while fading out. (0.01 - 1.0, defaults to 0.03)
+# fade-out-step = 0.03
+fade-out-step = 0.03;
+
+# The time between steps in fade step, in milliseconds. (> 0, defaults to 10)
+# fade-delta = 10
+
+# Specify a list of conditions of windows that should not be faded.
+# don't need this, we disable fading for all normal windows with wintypes: {}
+fade-exclude = [
+ "class_g = 'slop'" # maim
+]
+
+# Do not fade on window open/close.
+# no-fading-openclose = false
+
+# Do not fade destroyed ARGB windows with WM frame. Workaround of bugs in Openbox, Fluxbox, etc.
+# no-fading-destroyed-argb = false
+
+
+#################################
+# Transparency / Opacity #
+#################################
+
+# Opacity of inactive windows. (0.1 - 1.0, defaults to 1.0)
+# inactive-opacity = 1
+inactive-opacity = 0.8;
+
+# Opacity of window titlebars and borders. (0.1 - 1.0, disabled by default)
+# frame-opacity = 1.0
+frame-opacity = 0.7;
+
+# Default opacity for dropdown menus and popup menus. (0.0 - 1.0, defaults to 1.0)
+# menu-opacity = 1.0
+# menu-opacity is depreciated use dropdown-menu and popup-menu instead.
+
+#If using these 2 below change their values in line 510 & 511 aswell
+popup_menu = { opacity = 0.8; }
+dropdown_menu = { opacity = 0.8; }
+
+
+# Let inactive opacity set by -i override the '_NET_WM_OPACITY' values of windows.
+# inactive-opacity-override = true
+inactive-opacity-override = false;
+
+# Default opacity for active windows. (0.0 - 1.0, defaults to 1.0)
+active-opacity = 1.0;
+
+# Dim inactive windows. (0.0 - 1.0, defaults to 0.0)
+# inactive-dim = 0.0
+
+# Specify a list of conditions of windows that should always be considered focused.
+# focus-exclude = []
+focus-exclude = [
+ "class_g = 'Cairo-clock'",
+ "class_g = 'Bar'", # lemonbar
+ "class_g = 'slop'" # maim
+];
+
+# Use fixed inactive dim value, instead of adjusting according to window opacity.
+# inactive-dim-fixed = 1.0
+
+# Specify a list of opacity rules, in the format `PERCENT:PATTERN`,
+# like `50:name *= "Firefox"`. picom-trans is recommended over this.
+# Note we don't make any guarantee about possible conflicts with other
+# programs that set '_NET_WM_WINDOW_OPACITY' on frame or client windows.
+# example:
+# opacity-rule = [ "80:class_g = 'URxvt'" ];
+#
+# opacity-rule = []
+opacity-rule = [
+ "90:class_g = 'URxvt' && !focused",
+ "80:class_g = 'dmenu'"
+];
+
+
+#################################
+# Background-Blurring #
+#################################
+
+
+# Parameters for background blurring, see the *BLUR* section for more information.
+# blur-method =
+# blur-size = 12
+#
+# blur-deviation = false
+
+# Blur background of semi-transparent / ARGB windows.
+# Bad in performance, with driver-dependent behavior.
+# The name of the switch may change without prior notifications.
+#
+# blur-background = true;
+
+# Blur background of windows when the window frame is not opaque.
+# Implies:
+# blur-background
+# Bad in performance, with driver-dependent behavior. The name may change.
+#
+# blur-background-frame = false;
+
+
+# Use fixed blur strength rather than adjusting according to window opacity.
+# blur-background-fixed = false;
+
+
+# Specify the blur convolution kernel, with the following format:
+# example:
+# blur-kern = "5,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1";
+#
+# blur-kern = ''
+# blur-kern = "3x3box";
+
+blur: {
+ # requires: https://github.com/ibhagwan/picom
+ method = "kawase";
+ #method = "kernel";
+ strength = 3;
+ # deviation = 1.0;
+ # kernel = "11x11gaussian";
+ background = false;
+ background-frame = false;
+ background-fixed = false;
+ kern = "3x3box";
+}
+
+# Exclude conditions for background blur.
+blur-background-exclude = [
+ #"window_type = 'dock'",
+ #"window_type = 'desktop'",
+ #"class_g = 'URxvt'",
+ #
+ # prevents picom from blurring the background
+ # when taking selection screenshot with `main`
+ # https://github.com/naelstrof/maim/issues/130
+ "class_g = 'slop'",
+ "_GTK_FRAME_EXTENTS@:c"
+];
+
+
+#################################
+# General Settings #
+#################################
+
+# Daemonize process. Fork to background after initialization. Causes issues with certain (badly-written) drivers.
+# daemon = false
+
+# Specify the backend to use: `xrender`, `glx`, or `xr_glx_hybrid`.
+# `xrender` is the default one.
+#
+experimental-backends = true;
+backend = "glx";
+#backend = "xrender";
+
+
+# Enable/disable VSync.
+# vsync = false
+vsync = true
+
+# Enable remote control via D-Bus. See the *D-BUS API* section below for more details.
+# dbus = false
+
+# Try to detect WM windows (a non-override-redirect window with no
+# child that has 'WM_STATE') and mark them as active.
+#
+# mark-wmwin-focused = false
+mark-wmwin-focused = true;
+
+# Mark override-redirect windows that doesn't have a child window with 'WM_STATE' focused.
+# mark-ovredir-focused = false
+mark-ovredir-focused = true;
+
+# Try to detect windows with rounded corners and don't consider them
+# shaped windows. The accuracy is not very high, unfortunately.
+#
+# detect-rounded-corners = false
+detect-rounded-corners = true;
+
+# Detect '_NET_WM_OPACITY' on client windows, useful for window managers
+# not passing '_NET_WM_OPACITY' of client windows to frame windows.
+#
+# detect-client-opacity = false
+detect-client-opacity = true;
+
+# Specify refresh rate of the screen. If not specified or 0, picom will
+# try detecting this with X RandR extension.
+#
+# refresh-rate = 60
+refresh-rate = 0
+
+# Limit picom to repaint at most once every 1 / 'refresh_rate' second to
+# boost performance. This should not be used with
+# vsync drm/opengl/opengl-oml
+# as they essentially does sw-opti's job already,
+# unless you wish to specify a lower refresh rate than the actual value.
+#
+# sw-opti =
+
+# Use EWMH '_NET_ACTIVE_WINDOW' to determine currently focused window,
+# rather than listening to 'FocusIn'/'FocusOut' event. Might have more accuracy,
+# provided that the WM supports it.
+#
+# use-ewmh-active-win = false
+
+# Unredirect all windows if a full-screen opaque window is detected,
+# to maximize performance for full-screen windows. Known to cause flickering
+# when redirecting/unredirecting windows. paint-on-overlay may make the flickering less obvious.
+#
+# unredir-if-possible = false
+
+# Delay before unredirecting the window, in milliseconds. Defaults to 0.
+# unredir-if-possible-delay = 0
+
+# Conditions of windows that shouldn't be considered full-screen for unredirecting screen.
+# unredir-if-possible-exclude = []
+
+# Use 'WM_TRANSIENT_FOR' to group windows, and consider windows
+# in the same group focused at the same time.
+#
+# detect-transient = false
+detect-transient = true
+
+# Use 'WM_CLIENT_LEADER' to group windows, and consider windows in the same
+# group focused at the same time. 'WM_TRANSIENT_FOR' has higher priority if
+# detect-transient is enabled, too.
+#
+# detect-client-leader = false
+detect-client-leader = true
+
+# Resize damaged region by a specific number of pixels.
+# A positive value enlarges it while a negative one shrinks it.
+# If the value is positive, those additional pixels will not be actually painted
+# to screen, only used in blur calculation, and such. (Due to technical limitations,
+# with use-damage, those pixels will still be incorrectly painted to screen.)
+# Primarily used to fix the line corruption issues of blur,
+# in which case you should use the blur radius value here
+# (e.g. with a 3x3 kernel, you should use `--resize-damage 1`,
+# with a 5x5 one you use `--resize-damage 2`, and so on).
+# May or may not work with *--glx-no-stencil*. Shrinking doesn't function correctly.
+#
+# resize-damage = 1
+
+# Specify a list of conditions of windows that should be painted with inverted color.
+# Resource-hogging, and is not well tested.
+#
+# invert-color-include = []
+
+# GLX backend: Avoid using stencil buffer, useful if you don't have a stencil buffer.
+# Might cause incorrect opacity when rendering transparent content (but never
+# practically happened) and may not work with blur-background.
+# My tests show a 15% performance boost. Recommended.
+#
+# glx-no-stencil = false
+
+# GLX backend: Avoid rebinding pixmap on window damage.
+# Probably could improve performance on rapid window content changes,
+# but is known to break things on some drivers (LLVMpipe, xf86-video-intel, etc.).
+# Recommended if it works.
+#
+# glx-no-rebind-pixmap = false
+
+# Disable the use of damage information.
+# This cause the whole screen to be redrawn everytime, instead of the part of the screen
+# has actually changed. Potentially degrades the performance, but might fix some artifacts.
+# The opposing option is use-damage
+#
+# no-use-damage = false
+#use-damage = true (Causing Weird Black semi opaque rectangles when terminal is opened)
+#Changing use-damage to false fixes the problem
+use-damage = false
+
+# Use X Sync fence to sync clients' draw calls, to make sure all draw
+# calls are finished before picom starts drawing. Needed on nvidia-drivers
+# with GLX backend for some users.
+#
+# xrender-sync-fence = false
+
+# GLX backend: Use specified GLSL fragment shader for rendering window contents.
+# See `compton-default-fshader-win.glsl` and `compton-fake-transparency-fshader-win.glsl`
+# in the source tree for examples.
+#
+# glx-fshader-win = ''
+
+# Force all windows to be painted with blending. Useful if you
+# have a glx-fshader-win that could turn opaque pixels transparent.
+#
+# force-win-blend = false
+
+# Do not use EWMH to detect fullscreen windows.
+# Reverts to checking if a window is fullscreen based only on its size and coordinates.
+#
+# no-ewmh-fullscreen = false
+
+# Dimming bright windows so their brightness doesn't exceed this set value.
+# Brightness of a window is estimated by averaging all pixels in the window,
+# so this could comes with a performance hit.
+# Setting this to 1.0 disables this behaviour. Requires --use-damage to be disabled. (default: 1.0)
+#
+# max-brightness = 1.0
+
+# Make transparent windows clip other windows like non-transparent windows do,
+# instead of blending on top of them.
+#
+# transparent-clipping = false
+
+# Set the log level. Possible values are:
+# "trace", "debug", "info", "warn", "error"
+# in increasing level of importance. Case doesn't matter.
+# If using the "TRACE" log level, it's better to log into a file
+# using *--log-file*, since it can generate a huge stream of logs.
+#
+# log-level = "debug"
+log-level = "info";
+
+# Set the log file.
+# If *--log-file* is never specified, logs will be written to stderr.
+# Otherwise, logs will to written to the given file, though some of the early
+# logs might still be written to the stderr.
+# When setting this option from the config file, it is recommended to use an absolute path.
+#
+# log-file = '/path/to/your/log/file'
+
+# Show all X errors (for debugging)
+# show-all-xerrors = false
+
+# Write process ID to a file.
+# write-pid-path = '/path/to/your/log/file'
+
+# Window type settings
+#
+# 'WINDOW_TYPE' is one of the 15 window types defined in EWMH standard:
+# "unknown", "desktop", "dock", "toolbar", "menu", "utility",
+# "splash", "dialog", "normal", "dropdown_menu", "popup_menu",
+# "tooltip", "notification", "combo", and "dnd".
+#
+# Following per window-type options are available: ::
+#
+# fade, shadow:::
+# Controls window-type-specific shadow and fade settings.
+#
+# opacity:::
+# Controls default opacity of the window type.
+#
+# focus:::
+# Controls whether the window of this type is to be always considered focused.
+# (By default, all window types except "normal" and "dialog" has this on.)
+#
+# full-shadow:::
+# Controls whether shadow is drawn under the parts of the window that you
+# normally won't be able to see. Useful when the window has parts of it
+# transparent, and you want shadows in those areas.
+#
+# redir-ignore:::
+# Controls whether this type of windows should cause screen to become
+# redirected again after been unredirected. If you have unredir-if-possible
+# set, and doesn't want certain window to cause unnecessary screen redirection,
+# you can set this to `true`.
+#
+wintypes:
+{
+ normal = { fade = false; shadow = false; }
+ tooltip = { fade = true; shadow = true; opacity = 0.75; focus = true; full-shadow = false; };
+ dock = { shadow = false; }
+ dnd = { shadow = false; }
+ popup_menu = { opacity = 0.8; }
+ dropdown_menu = { opacity = 0.8; }
+};
diff --git a/stow/picom/dot-config/picom/picom.jonaburg.sample.conf b/stow/picom/dot-config/picom/picom.jonaburg.sample.conf
new file mode 100644
index 0000000..ee3fbbe
--- /dev/null
+++ b/stow/picom/dot-config/picom/picom.jonaburg.sample.conf
@@ -0,0 +1,512 @@
+#################################
+# Animations #
+#################################
+# requires https://github.com/jonaburg/picom
+# (These are also the default values)
+transition-length = 300
+transition-pow-x = 0.1
+transition-pow-y = 0.1
+transition-pow-w = 0.1
+transition-pow-h = 0.1
+size-transition = true
+
+
+#################################
+# Corners #
+#################################
+# requires: https://github.com/sdhand/compton or https://github.com/jonaburg/picom
+corner-radius = 10.0;
+rounded-corners-exclude = [
+ #"window_type = 'normal'",
+ "class_g = 'awesome'",
+ "class_g = 'URxvt'",
+ "class_g = 'XTerm'",
+ "class_g = 'kitty'",
+ "class_g = 'Alacritty'",
+ "class_g = 'Polybar'",
+ "class_g = 'code-oss'",
+ #"class_g = 'TelegramDesktop'",
+ "class_g = 'firefox'",
+ "class_g = 'Thunderbird'"
+];
+round-borders = 1;
+round-borders-exclude = [
+ #"class_g = 'TelegramDesktop'",
+];
+
+#################################
+# Shadows #
+#################################
+
+
+# Enabled client-side shadows on windows. Note desktop windows
+# (windows with '_NET_WM_WINDOW_TYPE_DESKTOP') never get shadow,
+# unless explicitly requested using the wintypes option.
+#
+# shadow = false
+shadow = false;
+
+# The blur radius for shadows, in pixels. (defaults to 12)
+# shadow-radius = 12
+shadow-radius = 7;
+
+# The opacity of shadows. (0.0 - 1.0, defaults to 0.75)
+# shadow-opacity = .75
+
+# The left offset for shadows, in pixels. (defaults to -15)
+# shadow-offset-x = -15
+shadow-offset-x = -7;
+
+# The top offset for shadows, in pixels. (defaults to -15)
+# shadow-offset-y = -15
+shadow-offset-y = -7;
+
+# Avoid drawing shadows on dock/panel windows. This option is deprecated,
+# you should use the *wintypes* option in your config file instead.
+#
+# no-dock-shadow = false
+
+# Don't draw shadows on drag-and-drop windows. This option is deprecated,
+# you should use the *wintypes* option in your config file instead.
+#
+# no-dnd-shadow = false
+
+# Red color value of shadow (0.0 - 1.0, defaults to 0).
+# shadow-red = 0
+
+# Green color value of shadow (0.0 - 1.0, defaults to 0).
+# shadow-green = 0
+
+# Blue color value of shadow (0.0 - 1.0, defaults to 0).
+# shadow-blue = 0
+
+# Do not paint shadows on shaped windows. Note shaped windows
+# here means windows setting its shape through X Shape extension.
+# Those using ARGB background is beyond our control.
+# Deprecated, use
+# shadow-exclude = 'bounding_shaped'
+# or
+# shadow-exclude = 'bounding_shaped && !rounded_corners'
+# instead.
+#
+# shadow-ignore-shaped = ''
+
+# Specify a list of conditions of windows that should have no shadow.
+#
+# examples:
+# shadow-exclude = "n:e:Notification";
+#
+# shadow-exclude = []
+shadow-exclude = [
+ "name = 'Notification'",
+ "class_g = 'Conky'",
+ "class_g ?= 'Notify-osd'",
+ "class_g = 'Cairo-clock'",
+ "class_g = 'slop'",
+ "class_g = 'Polybar'",
+ "_GTK_FRAME_EXTENTS@:c"
+];
+
+# Specify a X geometry that describes the region in which shadow should not
+# be painted in, such as a dock window region. Use
+# shadow-exclude-reg = "x10+0+0"
+# for example, if the 10 pixels on the bottom of the screen should not have shadows painted on.
+#
+# shadow-exclude-reg = ""
+
+# Crop shadow of a window fully on a particular Xinerama screen to the screen.
+# xinerama-shadow-crop = false
+
+
+#################################
+# Fading #
+#################################
+
+
+# Fade windows in/out when opening/closing and when opacity changes,
+# unless no-fading-openclose is used.
+# fading = false
+fading = true;
+
+# Opacity change between steps while fading in. (0.01 - 1.0, defaults to 0.028)
+# fade-in-step = 0.028
+fade-in-step = 0.03;
+
+# Opacity change between steps while fading out. (0.01 - 1.0, defaults to 0.03)
+# fade-out-step = 0.03
+fade-out-step = 0.03;
+
+# The time between steps in fade step, in milliseconds. (> 0, defaults to 10)
+# fade-delta = 10
+
+# Specify a list of conditions of windows that should not be faded.
+# don't need this, we disable fading for all normal windows with wintypes: {}
+fade-exclude = [
+ "class_g = 'slop'" # maim
+]
+
+# Do not fade on window open/close.
+# no-fading-openclose = false
+
+# Do not fade destroyed ARGB windows with WM frame. Workaround of bugs in Openbox, Fluxbox, etc.
+# no-fading-destroyed-argb = false
+
+
+#################################
+# Transparency / Opacity #
+#################################
+
+
+# Opacity of inactive windows. (0.1 - 1.0, defaults to 1.0)
+# inactive-opacity = 1
+inactive-opacity = 0.8;
+
+# Opacity of window titlebars and borders. (0.1 - 1.0, disabled by default)
+# frame-opacity = 1.0
+frame-opacity = 0.7;
+
+# Default opacity for dropdown menus and popup menus. (0.0 - 1.0, defaults to 1.0)
+# menu-opacity = 1.0
+# menu-opacity is depreciated use dropdown-menu and popup-menu instead.
+
+#If using these 2 below change their values in line 510 & 511 aswell
+popup_menu = { opacity = 0.8; }
+dropdown_menu = { opacity = 0.8; }
+
+
+# Let inactive opacity set by -i override the '_NET_WM_OPACITY' values of windows.
+# inactive-opacity-override = true
+inactive-opacity-override = false;
+
+# Default opacity for active windows. (0.0 - 1.0, defaults to 1.0)
+active-opacity = 1.0;
+
+# Dim inactive windows. (0.0 - 1.0, defaults to 0.0)
+# inactive-dim = 0.0
+
+# Specify a list of conditions of windows that should always be considered focused.
+# focus-exclude = []
+focus-exclude = [
+ "class_g = 'Cairo-clock'",
+ "class_g = 'Bar'", # lemonbar
+ "class_g = 'slop'" # maim
+];
+
+# Use fixed inactive dim value, instead of adjusting according to window opacity.
+# inactive-dim-fixed = 1.0
+
+# Specify a list of opacity rules, in the format `PERCENT:PATTERN`,
+# like `50:name *= "Firefox"`. picom-trans is recommended over this.
+# Note we don't make any guarantee about possible conflicts with other
+# programs that set '_NET_WM_WINDOW_OPACITY' on frame or client windows.
+# example:
+# opacity-rule = [ "80:class_g = 'URxvt'" ];
+#
+# opacity-rule = []
+opacity-rule = [
+ "80:class_g = 'Bar'", # lemonbar
+ "100:class_g = 'slop'", # maim
+ "100:class_g = 'XTerm'",
+ "100:class_g = 'URxvt'",
+ "100:class_g = 'kitty'",
+ "100:class_g = 'Alacritty'",
+ "80:class_g = 'Polybar'",
+ "100:class_g = 'code-oss'",
+ "100:class_g = 'Meld'",
+ "70:class_g = 'TelegramDesktop'",
+ "90:class_g = 'Joplin'",
+ "100:class_g = 'firefox'",
+ "100:class_g = 'Thunderbird'"
+];
+
+
+#################################
+# Background-Blurring #
+#################################
+
+
+# Parameters for background blurring, see the *BLUR* section for more information.
+# blur-method =
+# blur-size = 12
+#
+# blur-deviation = false
+
+# Blur background of semi-transparent / ARGB windows.
+# Bad in performance, with driver-dependent behavior.
+# The name of the switch may change without prior notifications.
+#
+# blur-background = true;
+
+# Blur background of windows when the window frame is not opaque.
+# Implies:
+# blur-background
+# Bad in performance, with driver-dependent behavior. The name may change.
+#
+# blur-background-frame = false;
+
+
+# Use fixed blur strength rather than adjusting according to window opacity.
+# blur-background-fixed = false;
+
+
+# Specify the blur convolution kernel, with the following format:
+# example:
+# blur-kern = "5,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1";
+#
+# blur-kern = ''
+# blur-kern = "3x3box";
+
+blur: {
+ # requires: https://github.com/ibhagwan/picom
+ method = "kawase";
+ #method = "kernel";
+ strength = 7;
+ # deviation = 1.0;
+ # kernel = "11x11gaussian";
+ background = false;
+ background-frame = false;
+ background-fixed = false;
+ kern = "3x3box";
+}
+
+# Exclude conditions for background blur.
+blur-background-exclude = [
+ #"window_type = 'dock'",
+ #"window_type = 'desktop'",
+ #"class_g = 'URxvt'",
+ #
+ # prevents picom from blurring the background
+ # when taking selection screenshot with `main`
+ # https://github.com/naelstrof/maim/issues/130
+ "class_g = 'slop'",
+ "_GTK_FRAME_EXTENTS@:c"
+];
+
+
+#################################
+# General Settings #
+#################################
+
+# Daemonize process. Fork to background after initialization. Causes issues with certain (badly-written) drivers.
+# daemon = false
+
+# Specify the backend to use: `xrender`, `glx`, or `xr_glx_hybrid`.
+# `xrender` is the default one.
+#
+experimental-backends = true;
+backend = "glx";
+#backend = "xrender";
+
+
+# Enable/disable VSync.
+# vsync = false
+vsync = true
+
+# Enable remote control via D-Bus. See the *D-BUS API* section below for more details.
+# dbus = false
+
+# Try to detect WM windows (a non-override-redirect window with no
+# child that has 'WM_STATE') and mark them as active.
+#
+# mark-wmwin-focused = false
+mark-wmwin-focused = true;
+
+# Mark override-redirect windows that doesn't have a child window with 'WM_STATE' focused.
+# mark-ovredir-focused = false
+mark-ovredir-focused = true;
+
+# Try to detect windows with rounded corners and don't consider them
+# shaped windows. The accuracy is not very high, unfortunately.
+#
+# detect-rounded-corners = false
+detect-rounded-corners = true;
+
+# Detect '_NET_WM_OPACITY' on client windows, useful for window managers
+# not passing '_NET_WM_OPACITY' of client windows to frame windows.
+#
+# detect-client-opacity = false
+detect-client-opacity = true;
+
+# Specify refresh rate of the screen. If not specified or 0, picom will
+# try detecting this with X RandR extension.
+#
+# refresh-rate = 60
+refresh-rate = 0
+
+# Limit picom to repaint at most once every 1 / 'refresh_rate' second to
+# boost performance. This should not be used with
+# vsync drm/opengl/opengl-oml
+# as they essentially does sw-opti's job already,
+# unless you wish to specify a lower refresh rate than the actual value.
+#
+# sw-opti =
+
+# Use EWMH '_NET_ACTIVE_WINDOW' to determine currently focused window,
+# rather than listening to 'FocusIn'/'FocusOut' event. Might have more accuracy,
+# provided that the WM supports it.
+#
+# use-ewmh-active-win = false
+
+# Unredirect all windows if a full-screen opaque window is detected,
+# to maximize performance for full-screen windows. Known to cause flickering
+# when redirecting/unredirecting windows. paint-on-overlay may make the flickering less obvious.
+#
+# unredir-if-possible = false
+
+# Delay before unredirecting the window, in milliseconds. Defaults to 0.
+# unredir-if-possible-delay = 0
+
+# Conditions of windows that shouldn't be considered full-screen for unredirecting screen.
+# unredir-if-possible-exclude = []
+
+# Use 'WM_TRANSIENT_FOR' to group windows, and consider windows
+# in the same group focused at the same time.
+#
+# detect-transient = false
+detect-transient = true
+
+# Use 'WM_CLIENT_LEADER' to group windows, and consider windows in the same
+# group focused at the same time. 'WM_TRANSIENT_FOR' has higher priority if
+# detect-transient is enabled, too.
+#
+# detect-client-leader = false
+detect-client-leader = true
+
+# Resize damaged region by a specific number of pixels.
+# A positive value enlarges it while a negative one shrinks it.
+# If the value is positive, those additional pixels will not be actually painted
+# to screen, only used in blur calculation, and such. (Due to technical limitations,
+# with use-damage, those pixels will still be incorrectly painted to screen.)
+# Primarily used to fix the line corruption issues of blur,
+# in which case you should use the blur radius value here
+# (e.g. with a 3x3 kernel, you should use `--resize-damage 1`,
+# with a 5x5 one you use `--resize-damage 2`, and so on).
+# May or may not work with *--glx-no-stencil*. Shrinking doesn't function correctly.
+#
+# resize-damage = 1
+
+# Specify a list of conditions of windows that should be painted with inverted color.
+# Resource-hogging, and is not well tested.
+#
+# invert-color-include = []
+
+# GLX backend: Avoid using stencil buffer, useful if you don't have a stencil buffer.
+# Might cause incorrect opacity when rendering transparent content (but never
+# practically happened) and may not work with blur-background.
+# My tests show a 15% performance boost. Recommended.
+#
+# glx-no-stencil = false
+
+# GLX backend: Avoid rebinding pixmap on window damage.
+# Probably could improve performance on rapid window content changes,
+# but is known to break things on some drivers (LLVMpipe, xf86-video-intel, etc.).
+# Recommended if it works.
+#
+# glx-no-rebind-pixmap = false
+
+# Disable the use of damage information.
+# This cause the whole screen to be redrawn everytime, instead of the part of the screen
+# has actually changed. Potentially degrades the performance, but might fix some artifacts.
+# The opposing option is use-damage
+#
+# no-use-damage = false
+#use-damage = true (Causing Weird Black semi opaque rectangles when terminal is opened)
+#Changing use-damage to false fixes the problem
+use-damage = false
+
+# Use X Sync fence to sync clients' draw calls, to make sure all draw
+# calls are finished before picom starts drawing. Needed on nvidia-drivers
+# with GLX backend for some users.
+#
+# xrender-sync-fence = false
+
+# GLX backend: Use specified GLSL fragment shader for rendering window contents.
+# See `compton-default-fshader-win.glsl` and `compton-fake-transparency-fshader-win.glsl`
+# in the source tree for examples.
+#
+# glx-fshader-win = ''
+
+# Force all windows to be painted with blending. Useful if you
+# have a glx-fshader-win that could turn opaque pixels transparent.
+#
+# force-win-blend = false
+
+# Do not use EWMH to detect fullscreen windows.
+# Reverts to checking if a window is fullscreen based only on its size and coordinates.
+#
+# no-ewmh-fullscreen = false
+
+# Dimming bright windows so their brightness doesn't exceed this set value.
+# Brightness of a window is estimated by averaging all pixels in the window,
+# so this could comes with a performance hit.
+# Setting this to 1.0 disables this behaviour. Requires --use-damage to be disabled. (default: 1.0)
+#
+# max-brightness = 1.0
+
+# Make transparent windows clip other windows like non-transparent windows do,
+# instead of blending on top of them.
+#
+# transparent-clipping = false
+
+# Set the log level. Possible values are:
+# "trace", "debug", "info", "warn", "error"
+# in increasing level of importance. Case doesn't matter.
+# If using the "TRACE" log level, it's better to log into a file
+# using *--log-file*, since it can generate a huge stream of logs.
+#
+# log-level = "debug"
+log-level = "info";
+
+# Set the log file.
+# If *--log-file* is never specified, logs will be written to stderr.
+# Otherwise, logs will to written to the given file, though some of the early
+# logs might still be written to the stderr.
+# When setting this option from the config file, it is recommended to use an absolute path.
+#
+# log-file = '/path/to/your/log/file'
+
+# Show all X errors (for debugging)
+# show-all-xerrors = false
+
+# Write process ID to a file.
+# write-pid-path = '/path/to/your/log/file'
+
+# Window type settings
+#
+# 'WINDOW_TYPE' is one of the 15 window types defined in EWMH standard:
+# "unknown", "desktop", "dock", "toolbar", "menu", "utility",
+# "splash", "dialog", "normal", "dropdown_menu", "popup_menu",
+# "tooltip", "notification", "combo", and "dnd".
+#
+# Following per window-type options are available: ::
+#
+# fade, shadow:::
+# Controls window-type-specific shadow and fade settings.
+#
+# opacity:::
+# Controls default opacity of the window type.
+#
+# focus:::
+# Controls whether the window of this type is to be always considered focused.
+# (By default, all window types except "normal" and "dialog" has this on.)
+#
+# full-shadow:::
+# Controls whether shadow is drawn under the parts of the window that you
+# normally won't be able to see. Useful when the window has parts of it
+# transparent, and you want shadows in those areas.
+#
+# redir-ignore:::
+# Controls whether this type of windows should cause screen to become
+# redirected again after been unredirected. If you have unredir-if-possible
+# set, and doesn't want certain window to cause unnecessary screen redirection,
+# you can set this to `true`.
+#
+wintypes:
+{
+ normal = { fade = false; shadow = false; }
+ tooltip = { fade = true; shadow = true; opacity = 0.75; focus = true; full-shadow = false; };
+ dock = { shadow = false; }
+ dnd = { shadow = false; }
+ popup_menu = { opacity = 0.8; }
+ dropdown_menu = { opacity = 0.8; }
+};
diff --git a/files/picom/picom.conf b/stow/picom/dot-config/picom/picom.upstream.conf
index 19c0d1b..19c0d1b 100644
--- a/files/picom/picom.conf
+++ b/stow/picom/dot-config/picom/picom.upstream.conf
diff --git a/files/qutebrowser/config.py b/stow/qutebrowser/dot-config/qutebrowser/config.py
index 2396e38..433a9cc 100644
--- a/files/qutebrowser/config.py
+++ b/stow/qutebrowser/dot-config/qutebrowser/config.py
@@ -1,3 +1,5 @@
+import dracula.draw
+
## Autogenerated config.py
## Documentation:
## qute://help/configuring.html
@@ -5,7 +7,7 @@
## This is here so configs done via the GUI are still loaded.
## Remove it to not load settings done via the GUI.
-# config.load_autoconfig()
+config.load_autoconfig()
## Aliases for commands. The keys of the given dictionary are the
## aliases, while the values are the commands they map to.
@@ -1356,27 +1358,38 @@ c.url.default_page = '/home/taamas/.mainpage/index.html'
## `:open google qutebrowser`.
## Type: Dict
c.url.searchengines = {
- 'DEFAULT': 'https://duckduckgo.com/?q={}', #duckduckgo
- 'ddg': 'https://duckduckgo.com/?q={}', #duckduckgo
- 'y': 'https://www.youtube.com/results?search_query={}', #youtube
- 'tw': 'https://twitter.com/{}', #twitter
- 'w': 'https://en.wikipedia.org/?search={}', #wikipedia
- 'we': 'https://es.wikipedia.org/?search={}', #wikipedia(ES)
- 'aw': 'https://wiki.archlinux.org/?search={}', #archWiki
- 'sf': 'https://scryfall.com/search?q={}', #scryfall
- 'mkm': 'https://www.cardmarket.com/en/Magic/MainPage/showSearchResult?searchFor={}', #mkm
- 'gi': 'https://game-icons.net/search.html?q={}', #game-icons
- 'bc': 'https://bandcamp.com/search?q={}', #bancamp
- 'mdd': 'https://www.megadede.com/search/{}', #megadede
- 'gh': 'https://www.github.com/search?q={}', #github
- 'ghu': 'https://github.com/search?q={}&type=Users', #github users
- 'ep': 'https://emojipedia.org/search/?q={}', #emojipedia
- 'rae': 'https://dle.rae.es/?w={}', #rae
- 'maps': 'https://www.google.com/maps/place/{}', #google maps
- 'im': 'https://imagemagick.org/script/search.php?q={}', # ImageMagick
- 'mc': 'https://minecraft.gamepedia.com/{}', # Minecraft wiki
- 'mb': 'https://musicbrainz.org/search?query={}&type=artist&method=indexed', # MusicBrainz (Artist)
- 'df': 'https://dwarffortresswiki.org/index.php/DF2014:{}' # Dwarf Fortress Wiki
+ 'DEFAULT': 'https://duckduckgo.com/?q={}', #duckduckgo
+ 'ddg': 'https://duckduckgo.com/?q={}', #duckduckgo
+ 's': 'https://search.disroot.org/search?q={}', #Disroot SearX
+ 'y': 'https://www.youtube.com/results?search_query={}', #youtube
+ 'tw': 'https://twitter.com/{}', #twitter
+ 'w': 'https://en.wikipedia.org/?search={}', #wikipedia
+ 'we': 'https://es.wikipedia.org/?search={}', #wikipedia(ES)
+ 'aw': 'https://wiki.archlinux.org/?search={}', #archWiki
+ 'sf': 'https://scryfall.com/search?q={}', #scryfall
+ 'mkm': 'https://www.cardmarket.com/en/Magic/Products/Search?searchString={}', #mkm
+ 'gi': 'https://game-icons.net/search.html?q={}', #game-icons
+ 'bc': 'https://bandcamp.com/search?q={}', #bancamp
+ 'mdd': 'https://www.megadede.com/search/{}', #megadede
+ 'gh': 'https://www.github.com/search?q={}', #github
+ 'ghu': 'https://github.com/search?q={}&type=Users', #github users
+ 'ep': 'https://emojipedia.org/search/?q={}', #emojipedia
+ 'rae': 'https://dle.rae.es/?w={}', #rae
+ 'maps': 'https://www.google.com/maps/place/{}', #google maps
+ 'im': 'https://imagemagick.org/script/search.php?q={}', # ImageMagick
+ 'mc': 'https://minecraft.gamepedia.com/{}', # Minecraft wiki
+ 'mb': 'https://musicbrainz.org/search?query={}&type=artist&method=indexed', # MusicBrainz (Artist)
+ 'df': 'https://dwarffortresswiki.org/index.php/DF2014:{}', # Dwarf Fortress Wiki
+ 'sl': 'https://senseis.xmp.net/?{}', # Sensei's Library
+ 'sls': 'https://senseis.xmp.net/?search={}&searchtype=title', # Sensei's Library search
+ 'bs': 'https://bsaber.com/?s={}', # Beast Saber
+ 'bp': 'https://bulbapedia.bulbagarden.net/wiki/{}', # Bulbapedia
+ 'bpm': 'https://bulbapedia.bulbagarden.net/wiki/{}#Learnset', # Bulbapedia (Learnset)
+ 'bpe': 'https://bulbapedia.bulbagarden.net/wiki/{}#Evolution', # Bulbapedia (Evolution)
+ 'sg': 'https://www.smogon.com/dex/ss/pokemon/{}', # Smogon
+ 'tex': 'https://www.ctan.org/search?phrase={}', # CTAN
+ 'keys': 'https://keys.openpgp.org/search?q={}', # openPGP keys
+ 'fl': 'https://fallenlondon.wiki/w/index.php?search={}' # Fallen London wiki
}
## Page(s) to open at the start.
@@ -1602,8 +1615,8 @@ config.bind(',i', 'spawn showURLimage.sh {url}')
config.bind(',fi', 'hint images spawn showURLimage.sh {hint-url}')
config.bind(',fI', 'hint all spawn showURLimage.sh {hint-url}')
# ripme bulk image downloading
-config.bind(',rm', 'spawn "ripme.sh {url}"')
-config.bind(',frm', 'hint links spawn "ripme.sh {hint-url}"')
+config.bind(',rm', 'spawn ripme.sh {url}')
+config.bind(',frm', 'hint links spawn ripme.sh {hint-url}')
## Bindings for caret mode
# config.bind('$', 'move-to-end-of-line', mode='caret')
@@ -1707,3 +1720,10 @@ config.bind(',frm', 'hint links spawn "ripme.sh {hint-url}"')
## Bindings for register mode
# config.bind('<Escape>', 'leave-mode', mode='register')
+
+dracula.draw.blood(c, {
+ 'spacing': {
+ 'vertical': 3,
+ 'horizontal': 8
+ }
+})
diff --git a/files/ranger/rc.conf b/stow/ranger/dot-config/ranger/rc.conf
index 4fe51db..4fe51db 100644
--- a/files/ranger/rc.conf
+++ b/stow/ranger/dot-config/ranger/rc.conf
diff --git a/files/ranger/rifle.conf b/stow/ranger/dot-config/ranger/rifle.conf
index 35a10a0..35a10a0 100644
--- a/files/ranger/rifle.conf
+++ b/stow/ranger/dot-config/ranger/rifle.conf
diff --git a/files/ranger/scope.sh b/stow/ranger/dot-config/ranger/scope.sh
index f403ed8..f403ed8 100755
--- a/files/ranger/scope.sh
+++ b/stow/ranger/dot-config/ranger/scope.sh
diff --git a/files/rcs/.Xresources b/stow/rcs/dot-Xresources
index dac62a7..4157806 100644
--- a/files/rcs/.Xresources
+++ b/stow/rcs/dot-Xresources
@@ -1,37 +1,9 @@
! To load into RESOURCE_MANAGER, run xrdb ~/.Xresources
! *background and foreground are read by sxiv
-!Gruvbox
-*background: #1d2021
-!*background: #32302f
-!*background: #282828
-!*foreground: #d5c4a1
-*foreground: #fbf1c7
-!*foreground: #ebdbb2
-! Black + DarkGrey
-*color0: #282828
-*color8: #928374
-! DarkRed + Red
-*color1: #cc241d
-*color9: #fb4934
-! DarkGreen + Green
-*color2: #98971a
-*color10: #b8bb26
-! DarkYellow + Yellow
-*color3: #d79921
-*color11: #fabd2f
-! DarkBlue + Blue
-*color4: #358588
-*color12: #83a598
-! DarkMagenta + Magenta
-*color5: #b16286
-*color13: #d3869b
-! DarkCyan + Cyan
-*color6: #689d6a
-*color14: #8ec07c
-! LightGrey + White
-*color7: #a89984
-*color15: #ebdbb2
+
+!#include "/home/taamas/.config/colorschemes/customBlue"
+#include "/home/taamas/.config/colorschemes/nord"
! Man pages
URxvt.colorIT: #ff0000
@@ -63,5 +35,6 @@ URxvt.perl-ext-common: default,font-size
URxvt.keysym.C-equal: font-size:reset
!Xcursor.size: 32
-Xcursor.theme: Bibata_Dodger_Blue
+Xcursor.theme: pTux-32
+!Xcursor.theme: Bibata_Dodger_Blue
!Xcursor.theme: Bibata_Turquoise
diff --git a/files/rcs/.bash_aliases b/stow/rcs/dot-bash_aliases
index 8c3fd6c..d229fc4 100644
--- a/files/rcs/.bash_aliases
+++ b/stow/rcs/dot-bash_aliases
@@ -11,6 +11,9 @@ alias grep='grep --color=auto'
alias fgrep='fgrep --color=auto'
alias egrep='egrep --color=auto'
alias nb='newsboat'
+alias z='cd ~/docs/zettelkasten && vim "$(./porTema.sh)"'
+alias zn='cd ~/docs/zettelkasten && vim "$(./nuevo.sh)"'
+alias o='rifle'
# config files
alias cb="vim ~/.bashrc"
@@ -18,6 +21,9 @@ alias cba="vim ~/.bash_aliases"
alias cx="vim ~/.Xresources"
alias ci3="vim ~/.config/i3/config"
alias ci3b="vim ~/.config/i3blocks/config"
+alias csx="vim ~/.config/sxhkd/sxhkdrc"
+alias cbs="vim ~/.config/bspwm/bspwmrc"
+alias csxbs="vim ~/.config/sxhkd/sxhkdrcbspc"
alias cdunst="vim ~/.config/dunst/dunstrc"
alias cv="vim ~/.vimrc"
alias cqb="vim ~/.config/qutebrowser/config.py"
@@ -25,8 +31,8 @@ alias cr="vim ~/.config/ranger/rc.conf"
alias crifle="vim ~/.config/ranger/rifle.conf"
alias ccmus="vim ~/.config/cmus/autosave"
alias cg="vim ~/.gitconfig"
-alias cnb="vim ~/.newsboat/config"
-alias cnbu="vim ~/.newsboat/urls"
+alias cnb="vim ~/.config/newsboat/config"
+alias cnbu="vim ~/.config/newsboat/urls"
alias ctmux="vim ~/.tmux.conf"
alias upgrade="sudo pacman -Syu"
diff --git a/files/rcs/.bash_vars b/stow/rcs/dot-bash_vars
index eca7a12..7dc9b53 100644
--- a/files/rcs/.bash_vars
+++ b/stow/rcs/dot-bash_vars
@@ -15,6 +15,8 @@ export VIEWER="sxiv"
export PLANTUML="$HOME/programs/plantUML/plantuml.jar"
+export LEDGER_FILE="$HOME/docs/ledger.dat"
+
export CONFIG_VOL_CHANGE="2"
export CONFIG_BIG_VOL_CHANGE="10"
@@ -35,6 +37,7 @@ export CONFIG_BATTERY="BAT1"
export CONFIG_SENSORS_TEMP_REGEX="^Package"
export CONFIG_SENSORS_TEMP_COL="4"
+export CONFIG_FOLDER_DISROOT_INBOX="$HOME/.mail/disroot/INBOX/new"
export CONFIG_FOLDER_GMAIL_INBOX="$HOME/.mail/gmail/[Gmail]/INBOX/new"
export CONFIG_FOLDER_UNIOVI_INBOX="$HOME/.mail/uniovi/INBOX/new"
@@ -48,6 +51,15 @@ export GREP_COLORS='ms=04;32;49:mc=04;32;49:sl=:cx=:fn=01;34;49:ln=34:bn=34:se=3
# Colors for gcc
export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
+# Colors for less
+export LESS_TERMCAP_mb=$'\E[01;31m'
+export LESS_TERMCAP_md=$(tput bold; tput setaf 14)
+export LESS_TERMCAP_me=$'\E[0m'
+export LESS_TERMCAP_so=$(tput setab 2; tput setaf 0)
+export LESS_TERMCAP_se=$'\E[0m'
+export LESS_TERMCAP_us=$(tput smul; tput setaf 3)
+export LESS_TERMCAP_ue=$'\E[0m'
+
#########
# $PATH #
#########
diff --git a/files/rcs/.bashrc b/stow/rcs/dot-bashrc
index a565c03..5c0c5bb 100644
--- a/files/rcs/.bashrc
+++ b/stow/rcs/dot-bashrc
@@ -64,4 +64,3 @@ PS1="${keys}[${wd}\u${keys}@${wd}\h${keys}]${wd}\w${umode}\$${def} "
# fzf goodies
. /usr/share/fzf/key-bindings.bash
. /usr/share/fzf/completion.bash
-
diff --git a/files/rcs/ttymaps.kmap b/stow/rcs/dot-config/ttymaps.kmap
index 51a7042..51a7042 100644
--- a/files/rcs/ttymaps.kmap
+++ b/stow/rcs/dot-config/ttymaps.kmap
diff --git a/files/rcs/.inputrc b/stow/rcs/dot-inputrc
index 0853a02..202ca16 100644
--- a/files/rcs/.inputrc
+++ b/stow/rcs/dot-inputrc
@@ -14,4 +14,3 @@ set show-all-if-ambiguous on
# Shows file type (directory or executable) as a character when showing autocomplete targets.
set visible-stats on
-
diff --git a/files/rcs/.profile b/stow/rcs/dot-profile
index 259dfdc..d461cce 100644
--- a/files/rcs/.profile
+++ b/stow/rcs/dot-profile
@@ -19,22 +19,28 @@ fi
sudo loadkeys es
sudo -n loadkeys "$XDG_CONFIG_HOME/ttymaps.kmap"
[[ $(tty) = "/dev/tty6" ]] && setfont alt-8x8
+
+xinitrc=""
+
if [ "$(tty)" = "/dev/tty1" ] && ! pgrep -x i3; then
+ export STATUS_BAR="polybar"
+ xinitrc="$HOME/.xinitrcbspwm"
+elif [ "$(tty)" = "/dev/tty2" ] && ! pgrep -x dwm; then
+ export STATUS_BAR="dwmblocks"
+ xinitrc="$HOME/.xinitrcdwm"
+elif [ "$(tty)" = "/dev/tty3" ] && ! pgrep -x bspwm; then
+ export STATUS_BAR="i3blocks"
+ xinitrc="$HOME/.xinitrci3"
+elif [ "$(tty)" = "/dev/tty4" ] && ! pgrep -x gnome-session; then
+ xinitrc="$HOME/.xinitrcgnome"
+elif tty | grep -q '/dev/tty.*' ; then
+ tmux new -s "$(tty | cut -d'/' -f3)"
+fi
+
+if [ -n "$xinitrc" ]; then
echo ""
- cat "$HOME/.xinitrci3" > "$HOME/.xinitrc"
+ cat "$HOME/.xinitrcprev" "$xinitrc" "$HOME/.xinitrcpost" > "$HOME/.xinitrc"
neofetch --config "$XDG_CONFIG_HOME/neofetch/neofetchLogin.conf"
#figlet "Starting i3-gaps..." | lolcat -a -d 1 -s 10 -p 1 -F 0.05
exec startx >"$CONFIG_FOLDER_LOGS/startx.log" 2>"$CONFIG_FOLDER_LOGS/startx.err"
-elif [ "$(tty)" = "/dev/tty3" ] && ! pgrep -x xfce4-session; then
- echo ""
- cat "$HOME/.xinitrcxfce4" > "$HOME/.xinitrc"
- neofetch --config "$XDG_CONFIG_HOME/neofetch/neofetchLogin.conf"
- exec startx
-elif [ "$(tty)" = "/dev/tty4" ] && ! pgrep -x gnome-session; then
- echo ""
- cat "$HOME/.xinitrcgnome" > "$HOME/.xinitrc"
- neofetch --config "$XDG_CONFIG_HOME/neofetch/neofetchLogin.conf"
- exec startx
-elif tty | grep -q '/dev/tty.*' ; then
- tmux new -s "$(tty | cut -d'/' -f3)"
fi
diff --git a/files/sxhkd/sxhkdrc b/stow/sxhkd/dot-config/sxhkd/sxhkdrc
index 12f6a7f..f726802 100644
--- a/files/sxhkd/sxhkdrc
+++ b/stow/sxhkd/dot-config/sxhkd/sxhkdrc
@@ -6,7 +6,9 @@
#
# Config file for the Simple X HotKey Daemon
-super + Return
+#super + Return
+# urxvt -title "floating" -e tmux
+super + control + Return
urxvt -title "floating" -e tmux
super + shift + Return
@@ -31,8 +33,8 @@ super + ctrl + shift + l
~/scripts/i3lock.sh
# touchpad
-super + t
- touchpad.sh
+# super + t
+# touchpad.sh
# From https://faq.i3wm.org/question/3747/enabling-multimedia-keys/?answer=3759#post-id-3759
# Pulse Audio controls
@@ -66,8 +68,11 @@ super + q
super + shift + f
firefox
+# super + shift + w
+# prevBG
+
super + w
- firefox https://web.whatsapp.com/
+ setRandomBG.sh
Print
screenshot.sh
@@ -84,25 +89,25 @@ super + shift + a
super + r
urxvt -title "floating" -e ranger
-super + c
+super + shift + c
~/scripts/dmenuTerm.sh
super + d
~/scripts/dmenuDic.sh
+super + shift + t
+ dmenutranslate
+
super + e
~/scripts/emoji.sh
-super + n
- ~/scripts/quicktext.sh
-
-super + m
+super + shift + m
~/scripts/dmenuMount.sh
-super + shift + m
+super + control + m
~/scripts/dmenuUmount.sh
-super + shift + c
+super + control + shift + c
notify-send "$(fortune)"
super + y
@@ -111,8 +116,14 @@ super + y
super + ctrl + f
screenRecord.sh
+super + ctrl + n
+ ~/scripts/quicktext.sh
+
super + ctrl + p
passmenu
super + z
docs.sh
+
+super + ctrl + space
+ toggleKBLayouts.sh
diff --git a/stow/sxhkd/dot-config/sxhkd/sxhkdrcbspc b/stow/sxhkd/dot-config/sxhkd/sxhkdrcbspc
new file mode 100644
index 0000000..192698d
--- /dev/null
+++ b/stow/sxhkd/dot-config/sxhkd/sxhkdrcbspc
@@ -0,0 +1,92 @@
+# _ _ _ _
+# _____ _| |__ | | ____| |_ __ ___| |__ ___ _ ____ ___ __ ___
+# / __\ \/ / '_ \| |/ / _` | '__/ __| '_ \/ __| '_ \ \ /\ / / '_ ` _ \
+# \__ \> <| | | | < (_| | | | (__| |_) \__ \ |_) \ V V /| | | | | |
+# |___/_/\_\_| |_|_|\_\__,_|_| \___|_.__/|___/ .__/ \_/\_/ |_| |_| |_|
+# |_|
+# Config file for the Simple X HotKey Daemon with bspwm
+
+# Select next or previous monitor
+super + {period,comma}
+ bspc monitor {next,prev} -f
+
+# Move node to next or previous monitor and keep it focused
+super + shift + {period,comma}
+ bspc node -m {next,prev} --follow
+
+# Select next or previous desktop on focused monitor
+super + {n,p}
+ bspc desktop {next,prev}.local -f;\
+ dunstify -r "$(dunstifyIDs.sh bspwmDesktop)" -t 500 \
+ "$(bspc query -D -d focused.local --names)"
+
+# Send node to next or previous desktop on focused monitor
+super + shift + {n,p}
+ bspc node -d {next,prev}.local
+
+# Select a specific desktop on focused monitor
+super + {0-9}
+ bspc desktop 'focused:^{0-9}' -f
+
+# Send node to specific desktop on focused monitor
+super + shift + {0-9}
+ bspc node -d 'focused:^{0-9}'
+
+# Directionally select node with same floating state
+super + {h,j,k,l}
+ direction={west,south,north,east}; \
+ if bspc wm -g | grep -q ':TF:'; then \
+ bspc node -f "$direction".floating; \
+ elif bspc wm -g | grep -q ':LM:'; then \
+ bspc node -f next.local.window.!floating; \
+ else \
+ bspc node -f "$direction".!floating; \
+ fi
+
+# Swap node with adjacent node
+super + shift + {h,j,k,l}
+ bspc node -s {west,south,north,east}
+
+# Resize node
+#super + alt + {h,j,k,l}
+# bspc node --resize {right,bottom,bottom,right} {-5,0,0,5} {0,5,-5,0}
+super + alt + h
+ bspc node --resize right -5 0
+super + alt + j
+ bspc node --resize bottom 0 5
+super + alt + k
+ bspc node --resize bottom 0 -5
+super + alt + l
+ bspc node --resize right 5 0
+
+# Select split direction
+super + {v,b}
+ bspc node --presel-dir ~{south,east}
+
+# Swap focus between tiled and floating nodes
+super + space
+ if bspc wm -g | grep -q ':TF:'; then \
+ bspc node -f last.local.leaf.!floating; \
+ else \
+ bspc node -f last.local.leaf.floating; \
+ fi
+
+# Switch focused node floating state
+super + shift + space
+ bspc node -t ~floating
+
+# Jump to next or previous global window
+super + ctrl + {h,l}
+ bspc node -f {next,prev}.window
+
+# Change layout (m for monocle)
+super + m
+ bspc desktop -l next
+
+# Close window on focused node
+super + shift + q
+ bspc node -c
+
+# Notify number of windows
+super + c
+ dunstify -r "$(dunstifyIDs.sh "bspwmRunningStuff")" -t 1000 "Running stuff" "bspwm nodes:\n$(bspc query -N any.leaf.window | wc -l)\ntmux sessions:\n$(tmux list-sessions | cut -d':' -f1)"
diff --git a/files/sxiv/image-info b/stow/sxiv/dot-config/sxiv/exec/image-info
index be503d8..be503d8 100755
--- a/files/sxiv/image-info
+++ b/stow/sxiv/dot-config/sxiv/exec/image-info
diff --git a/files/sxiv/key-handler b/stow/sxiv/dot-config/sxiv/exec/key-handler
index dc13548..ef81c79 100755
--- a/files/sxiv/key-handler
+++ b/stow/sxiv/dot-config/sxiv/exec/key-handler
@@ -32,16 +32,21 @@ case "$1" in
images=""
tmpFolder="/tmp/sxiv/gifs/"
mkdir -p "$tmpFolder"
+ res=$(echo "" | dmenu -p "Resolution of GIF?")
+ [ -n "$res" ] && res="-resize $res"
while read -r file
do
- convert "$file" -resize 512x999999\> "$tmpFolder$file"
+ if ! convert "$file" $res "$tmpFolder$file"; then
+ dunstify "Error converting files (Wrong resolution?)"
+ exit 1
+ fi
images="$images $tmpFolder$file"
done
delay=$(echo "" | dmenu -p "Delay of GIF?")
- [ "$delay" ] || exit 0
+ [ -n "$delay" ] || exit 0
name=$(echo "" | dmenu -p "Name of GIF?")
- [ "$name" ] || exit 0
- convert -delay "$delay" -loop 0 $images "$name.gif"
+ [ -n "$name" ] || exit 0
+ convert -delay "$delay" -loop 10 $images "$name.gif"
dunstify "Created $name.gif" ;;
"h")
diff --git a/files/rcs/.tmux.conf b/stow/tmux/dot-tmux.conf
index 62f9762..0f65fa6 100644
--- a/files/rcs/.tmux.conf
+++ b/stow/tmux/dot-tmux.conf
@@ -68,4 +68,3 @@ set-option -sa terminal-overrides ',rxvt-256color:RGB'
# tmux-url-select
bind u run tmux-url-select
-
diff --git a/stow/vim/dot-vim/autoload/plug.vim b/stow/vim/dot-vim/autoload/plug.vim
new file mode 100644
index 0000000..9c296ac
--- /dev/null
+++ b/stow/vim/dot-vim/autoload/plug.vim
@@ -0,0 +1,2788 @@
+" 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-default 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')
+if s:is_win && &shellslash
+ set noshellslash
+ let s:me = resolve(expand('<sfile>:p'))
+ set shellslash
+else
+ let s:me = resolve(expand('<sfile>:p'))
+endif
+let s:base_spec = { 'branch': '', '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! s:isabsolute(dir) abort
+ return a:dir =~# '^/' || (has('win32') && a:dir =~? '^\%(\\\|[A-Z]:\)')
+endfunction
+
+function! s:git_dir(dir) abort
+ let gitdir = s:trim(a:dir) . '/.git'
+ if isdirectory(gitdir)
+ return gitdir
+ endif
+ if !filereadable(gitdir)
+ return ''
+ endif
+ let gitdir = matchstr(get(readfile(gitdir), 0, ''), '^gitdir: \zs.*')
+ if len(gitdir) && !s:isabsolute(gitdir)
+ let gitdir = a:dir . '/' . gitdir
+ endif
+ return isdirectory(gitdir) ? gitdir : ''
+endfunction
+
+function! s:git_origin_url(dir) abort
+ let gitdir = s:git_dir(a:dir)
+ let config = gitdir . '/config'
+ if empty(gitdir) || !filereadable(config)
+ return ''
+ endif
+ return matchstr(join(readfile(config)), '\[remote "origin"\].\{-}url\s*=\s*\zs\S*\ze')
+endfunction
+
+function! s:git_revision(dir) abort
+ let gitdir = s:git_dir(a:dir)
+ let head = gitdir . '/HEAD'
+ if empty(gitdir) || !filereadable(head)
+ return ''
+ endif
+
+ let line = get(readfile(head), 0, '')
+ let ref = matchstr(line, '^ref: \zs.*')
+ if empty(ref)
+ return line
+ endif
+
+ if filereadable(gitdir . '/' . ref)
+ return get(readfile(gitdir . '/' . ref), 0, '')
+ endif
+
+ if filereadable(gitdir . '/packed-refs')
+ for line in readfile(gitdir . '/packed-refs')
+ if line =~# ' ' . ref
+ return matchstr(line, '^[0-9a-f]*')
+ endif
+ endfor
+ endif
+
+ return ''
+endfunction
+
+function! s:git_local_branch(dir) abort
+ let gitdir = s:git_dir(a:dir)
+ let head = gitdir . '/HEAD'
+ if empty(gitdir) || !filereadable(head)
+ return ''
+ endif
+ let branch = matchstr(get(readfile(head), 0, ''), '^ref: refs/heads/\zs.*')
+ return len(branch) ? branch : 'HEAD'
+endfunction
+
+function! s:git_origin_branch(spec)
+ if len(a:spec.branch)
+ return a:spec.branch
+ endif
+
+ " The file may not be present if this is a local repository
+ let gitdir = s:git_dir(a:spec.dir)
+ let origin_head = gitdir.'/refs/remotes/origin/HEAD'
+ if len(gitdir) && filereadable(origin_head)
+ return matchstr(get(readfile(origin_head), 0, ''),
+ \ '^ref: refs/remotes/origin/\zs.*')
+ endif
+
+ " The command may not return the name of a branch in detached HEAD state
+ let result = s:lines(s:system('git symbolic-ref --short HEAD', a:spec.dir))
+ return v:shell_error ? '' : result[-1]
+endfunction
+
+if s:is_win
+ function! s:plug_call(fn, ...)
+ let shellslash = &shellslash
+ try
+ set noshellslash
+ return call(a:fn, a:000)
+ finally
+ let &shellslash = shellslash
+ endtry
+ endfunction
+else
+ function! s:plug_call(fn, ...)
+ return call(a:fn, a:000)
+ endfunction
+endif
+
+function! s:plug_getcwd()
+ return s:plug_call('getcwd')
+endfunction
+
+function! s:plug_fnamemodify(fname, mods)
+ return s:plug_call('fnamemodify', a:fname, a:mods)
+endfunction
+
+function! s:plug_expand(fmt)
+ return s:plug_call('expand', a:fmt, 1)
+endfunction
+
+function! s:plug_tempname()
+ return s:plug_call('tempname')
+endfunction
+
+function! plug#begin(...)
+ if a:0 > 0
+ let s:plug_home_org = a:1
+ let home = s:path(s:plug_fnamemodify(s:plug_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 s:plug_fnamemodify(home, ':t') ==# 'plugin' && s:plug_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
+ if has('win32')
+ \ && &shellslash
+ \ && (&shell =~# 'cmd\(\.exe\)\?$' || &shell =~# 'powershell\(\.exe\)\?$')
+ return s:err('vim-plug does not support shell, ' . &shell . ', when shellslash is set.')
+ endif
+ if !has('nvim')
+ \ && (has('win32') || has('win32unix'))
+ \ && !has('multi_byte')
+ return s:err('Vim needs +multi_byte feature on Windows to run shell commands. Enable +iconv for best results.')
+ 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('plug#end() called without calling 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
+
+function! s:rtp(spec)
+ return s:path(a:spec.dir . get(a:spec, 'rtp', ''))
+endfunction
+
+if s:is_win
+ 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
+
+ " Copied from fzf
+ function! s:wrap_cmds(cmds)
+ let cmds = [
+ \ '@echo off',
+ \ 'setlocal enabledelayedexpansion']
+ \ + (type(a:cmds) == type([]) ? a:cmds : [a:cmds])
+ \ + ['endlocal']
+ if has('iconv')
+ if !exists('s:codepage')
+ let s:codepage = libcallnr('kernel32.dll', 'GetACP', 0)
+ endif
+ return map(cmds, printf('iconv(v:val."\r", "%s", "cp%d")', &encoding, s:codepage))
+ endif
+ return map(cmds, 'v:val."\r"')
+ endfunction
+
+ function! s:batchfile(cmd)
+ let batchfile = s:plug_tempname().'.bat'
+ call writefile(s:wrap_cmds(a:cmd), batchfile)
+ let cmd = plug#shellescape(batchfile, {'shell': &shell, 'script': 0})
+ if &shell =~# 'powershell\(\.exe\)\?$'
+ let cmd = '& ' . cmd
+ endif
+ return [batchfile, cmd]
+ endfunction
+else
+ 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', s:plug_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(repo . ' ' . v:exception)
+ endtry
+endfunction
+
+function! s:parse_options(arg)
+ let opts = copy(s:base_spec)
+ let type = type(a:arg)
+ let opt_errfmt = 'Invalid argument for "%s" option of :Plug (expected: %s)'
+ if type == s:TYPE.string
+ if empty(a:arg)
+ throw printf(opt_errfmt, 'tag', 'string')
+ endif
+ let opts.tag = a:arg
+ elseif type == s:TYPE.dict
+ for opt in ['branch', 'tag', 'commit', 'rtp', 'dir', 'as']
+ if has_key(a:arg, opt)
+ \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt]))
+ throw printf(opt_errfmt, opt, 'string')
+ endif
+ endfor
+ for opt in ['on', 'for']
+ if has_key(a:arg, opt)
+ \ && type(a:arg[opt]) != s:TYPE.list
+ \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt]))
+ throw printf(opt_errfmt, opt, 'string or list')
+ endif
+ endfor
+ if has_key(a:arg, 'do')
+ \ && type(a:arg.do) != s:TYPE.funcref
+ \ && (type(a:arg.do) != s:TYPE.string || empty(a:arg.do))
+ throw printf(opt_errfmt, 'do', 'string or funcref')
+ endif
+ call extend(opts, a:arg)
+ if has_key(opts, 'dir')
+ let opts.dir = s:dirpath(s:plug_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(s:plug_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 /^-\{1}\ /
+ 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 match plugH2 /^-\{2,}/
+ 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(s:plug_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=sh
+ endif
+ if a:swap
+ if &shell =~# 'powershell\(\.exe\)\?$' || &shell =~# 'pwsh$'
+ let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s'
+ elseif &shell =~# 'sh' || &shell =~# 'cmd\(\.exe\)\?$'
+ set shellredir=>%s\ 2>&1
+ endif
+ endif
+ return prev
+endfunction
+
+function! s:bang(cmd, ...)
+ let batchfile = ''
+ 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, cmd] = s:batchfile(cmd)
+ 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 && filereadable(batchfile)
+ 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
+ call s:load_plugin(spec)
+ 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:git_revision(a:spec.dir)
+ if !empty(output) && !s:hash_match(sha, s:lines(output)[0])
+ let credential_helper = s:git_version_requirement(2) ? '-c credential.helper= ' : ''
+ let output = s:system(
+ \ 'git '.credential_helper.'fetch --depth 999999 && git checkout '.plug#shellescape(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 = []
+ if get(g:, 'plug_shallow', 1)
+ call extend(s:clone_opt, ['--depth', '1'])
+ if s:git_version_requirement(1, 7, 10)
+ call add(s:clone_opt, '--no-single-branch')
+ endif
+ endif
+
+ if has('win32unix') || has('wsl')
+ call extend(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 '.plug#shellescape(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 '.plug#shellescape(tag).' -- 2>&1', spec.dir)
+ else
+ let branch = s:git_origin_branch(spec)
+ call s:log4(name, 'Merging origin/'.s:esc(branch))
+ let out = s:system('git checkout -q '.plug#shellescape(branch).' -- 2>&1'
+ \. (has_key(s:update.new, name) ? '' : ('&& git merge --ff-only '.plug#shellescape('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:rm_rf(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' || a:event == 'stderr') ?
+ \ 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': [''],
+ \ 'new': get(a:opts, 'new', 0) }
+ let s:jobs[a:name] = job
+
+ if s:nvim
+ if has_key(a:opts, 'dir')
+ let job.cwd = a:opts.dir
+ endif
+ let argv = a:cmd
+ call extend(job, {
+ \ 'on_stdout': function('s:nvim_cb'),
+ \ 'on_stderr': function('s:nvim_cb'),
+ \ 'on_exit': function('s:nvim_cb'),
+ \ })
+ let jid = s:plug_call('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 cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"script": 0})'))
+ if has_key(a:opts, 'dir')
+ let cmd = s:with_cd(cmd, a:opts.dir, 0)
+ endif
+ let argv = s:is_win ? ['cmd', '/s', '/c', '"'.cmd.'"'] : ['sh', '-c', cmd]
+ let jid = job_start(s:is_win ? join(argv, ' ') : argv, {
+ \ 'out_cb': function('s:job_cb', ['s:job_out_cb', job]),
+ \ 'err_cb': function('s:job_cb', ['s:job_out_cb', job]),
+ \ 'exit_cb': function('s:job_cb', ['s:job_exit_cb', job]),
+ \ 'err_mode': 'raw',
+ \ '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', has_key(a:opts, 'dir') ? [a:cmd, a:opts.dir] : [a: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()
+
+ 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)
+ let max = line('$')
+ for i in range(4, max > 4 ? max : 4)
+ if getline(i) =~# '^[-+x*] '.a:name.':'
+ for j in range(i + 1, max > 5 ? max : 5)
+ 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 cmd = s:git_version_requirement(2) ? ['git', '-c', 'credential.helper=', 'fetch'] : ['git', 'fetch']
+ if has_tag && !empty(globpath(spec.dir, '.git/shallow'))
+ call extend(cmd, ['--depth', '99999999'])
+ endif
+ if !empty(prog)
+ call add(cmd, prog)
+ endif
+ call s:spawn(name, cmd, { '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
+ let cmd = ['git', 'clone']
+ if !has_tag
+ call extend(cmd, s:clone_opt)
+ endif
+ if !empty(prog)
+ call add(cmd, prog)
+ endif
+ call s:spawn(name, extend(cmd, [spec.uri, 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 = ' '.join(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').join(' ')
+ 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, script)
+ let escaped = substitute('"'.a:arg.'"', '[&|<>()@^!"]', '^&', 'g')
+ return substitute(escaped, '%', (a:script ? '%' : '^') . '&', 'g')
+endfunction
+
+function! s:shellesc_ps1(arg)
+ return "'".substitute(escape(a:arg, '\"'), "'", "''", 'g')."'"
+endfunction
+
+function! s:shellesc_sh(arg)
+ return "'".substitute(a:arg, "'", "'\\\\''", 'g')."'"
+endfunction
+
+" Escape the shell argument based on the shell.
+" Vim and Neovim's shellescape() are insufficient.
+" 1. shellslash determines whether to use single/double quotes.
+" Double-quote escaping is fragile for cmd.exe.
+" 2. It does not work for powershell.
+" 3. It does not work for *sh shells if the command is executed
+" via cmd.exe (ie. cmd.exe /c sh -c command command_args)
+" 4. It does not support batchfile syntax.
+"
+" Accepts an optional dictionary with the following keys:
+" - shell: same as Vim/Neovim 'shell' option.
+" If unset, fallback to 'cmd.exe' on Windows or 'sh'.
+" - script: If truthy and shell is cmd.exe, escape for batchfile syntax.
+function! plug#shellescape(arg, ...)
+ if a:arg =~# '^[A-Za-z0-9_/:.-]\+$'
+ return a:arg
+ endif
+ let opts = a:0 > 0 && type(a:1) == s:TYPE.dict ? a:1 : {}
+ let shell = get(opts, 'shell', s:is_win ? 'cmd.exe' : 'sh')
+ let script = get(opts, 'script', 1)
+ if shell =~# 'cmd\(\.exe\)\?$'
+ return s:shellesc_cmd(a:arg, script)
+ elseif shell =~# 'powershell\(\.exe\)\?$' || shell =~# 'pwsh$'
+ return s:shellesc_ps1(a:arg)
+ endif
+ return s:shellesc_sh(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, ...)
+ let script = a:0 > 0 ? a:1 : 1
+ return printf('cd%s %s && %s', s:is_win ? ' /d' : '', plug#shellescape(a:dir, {'script': script}), a:cmd)
+endfunction
+
+function! s:system(cmd, ...)
+ let batchfile = ''
+ try
+ let [sh, shellcmdflag, shrd] = s:chsh(1)
+ if type(a:cmd) == s:TYPE.list
+ " Neovim's system() supports list argument to bypass the shell
+ " but it cannot set the working directory for the command.
+ " Assume that the command does not rely on the shell.
+ if has('nvim') && a:0 == 0
+ return system(a:cmd)
+ endif
+ let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"shell": &shell, "script": 0})'))
+ if &shell =~# 'powershell\(\.exe\)\?$'
+ let cmd = '& ' . cmd
+ endif
+ else
+ let cmd = a:cmd
+ endif
+ if a:0 > 0
+ let cmd = s:with_cd(cmd, a:1, type(a:cmd) != s:TYPE.list)
+ endif
+ if s:is_win && type(a:cmd) != s:TYPE.list
+ let [batchfile, cmd] = s:batchfile(cmd)
+ endif
+ return system(cmd)
+ finally
+ let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd]
+ if s:is_win && filereadable(batchfile)
+ 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:git_local_branch(a:spec.dir), s:git_origin_url(a:spec.dir)]
+ let remote = result[-1]
+ if empty(remote)
+ 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 sha = s:git_revision(a:spec.dir)
+ if empty(sha)
+ 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 current_branch = result[0]
+ " Check tag
+ let origin_branch = s:git_origin_branch(a:spec)
+ 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 origin_branch !=# current_branch
+ let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.',
+ \ current_branch, origin_branch)
+ endif
+ if empty(err)
+ let [ahead, behind] = split(s:lastline(s:system([
+ \ 'git', 'rev-list', '--count', '--left-right',
+ \ printf('HEAD...origin/%s', origin_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.', origin_branch, ahead, behind)
+ else
+ let err = printf("Ahead of origin/%s by %d commit(s).\n"
+ \ .'Cannot update until local changes are pushed.',
+ \ origin_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)
+ return s:system(s:is_win
+ \ ? 'rmdir /S /Q '.plug#shellescape(a:dir)
+ \ : ['rm', '-rf', 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(s:plug_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
+ let err_count = 0
+ 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
+ let err = s:rm_rf(line[2:])
+ setlocal modifiable
+ if empty(err)
+ call setline(l1, '~'.line[1:])
+ let s:clean_count += 1
+ else
+ delete _
+ call append(l1 - 1, s:format_message('x', line[1:], err))
+ let l2 += len(s:lines(err))
+ let err_count += 1
+ endif
+ let msg = printf('Removed %d directories.', s:clean_count)
+ if err_count > 0
+ let msg .= printf(' Failed to remove %d directories.', err_count)
+ endif
+ call setline(4, msg)
+ setlocal nomodifiable
+ endif
+ endif
+ let l1 += 1
+ endwhile
+endfunction
+
+function! s:upgrade()
+ echo 'Downloading the latest version of vim-plug'
+ redraw
+ let tmp = s:plug_tempname()
+ let new = tmp . '/plug.vim'
+
+ try
+ let out = s:system(['git', 'clone', '--depth', '1', s:plug_src, 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
+ let batchfile = ''
+ try
+ let [sh, shellcmdflag, shrd] = s:chsh(1)
+ let cmd = 'cd '.plug#shellescape(g:plugs[name].dir).' && git show --no-color --pretty=medium '.sha
+ if s:is_win
+ let [batchfile, cmd] = s:batchfile(cmd)
+ endif
+ execute 'silent %!' cmd
+ finally
+ let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd]
+ if s:is_win && filereadable(batchfile)
+ 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 branch = s:git_origin_branch(v)
+ if len(branch)
+ let range = origin ? '..origin/'.branch : 'HEAD@{1}..'
+ let cmd = ['git', 'log', '--graph', '--color=never']
+ if s:git_version_requirement(2, 10, 0)
+ call add(cmd, '--no-show-signature')
+ endif
+ call extend(cmd, ['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range])
+ if has_key(v, 'rtp')
+ call extend(cmd, ['--', 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
+ 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 '.plug#shellescape(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:git_revision(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 = s:plug_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/stow/vim/dot-vim/autoload/plug.vim.old b/stow/vim/dot-vim/autoload/plug.vim.old
new file mode 100644
index 0000000..c1657f2
--- /dev/null
+++ b/stow/vim/dot-vim/autoload/plug.vim.old
@@ -0,0 +1,2788 @@
+" 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-default 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')
+if s:is_win && &shellslash
+ set noshellslash
+ let s:me = resolve(expand('<sfile>:p'))
+ set shellslash
+else
+ let s:me = resolve(expand('<sfile>:p'))
+endif
+let s:base_spec = { 'branch': '', '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! s:isabsolute(dir) abort
+ return a:dir =~# '^/' || (has('win32') && a:dir =~? '^\%(\\\|[A-Z]:\)')
+endfunction
+
+function! s:git_dir(dir) abort
+ let gitdir = s:trim(a:dir) . '/.git'
+ if isdirectory(gitdir)
+ return gitdir
+ endif
+ if !filereadable(gitdir)
+ return ''
+ endif
+ let gitdir = matchstr(get(readfile(gitdir), 0, ''), '^gitdir: \zs.*')
+ if len(gitdir) && !s:isabsolute(gitdir)
+ let gitdir = a:dir . '/' . gitdir
+ endif
+ return isdirectory(gitdir) ? gitdir : ''
+endfunction
+
+function! s:git_origin_url(dir) abort
+ let gitdir = s:git_dir(a:dir)
+ let config = gitdir . '/config'
+ if empty(gitdir) || !filereadable(config)
+ return ''
+ endif
+ return matchstr(join(readfile(config)), '\[remote "origin"\].\{-}url\s*=\s*\zs\S*\ze')
+endfunction
+
+function! s:git_revision(dir) abort
+ let gitdir = s:git_dir(a:dir)
+ let head = gitdir . '/HEAD'
+ if empty(gitdir) || !filereadable(head)
+ return ''
+ endif
+
+ let line = get(readfile(head), 0, '')
+ let ref = matchstr(line, '^ref: \zs.*')
+ if empty(ref)
+ return line
+ endif
+
+ if filereadable(gitdir . '/' . ref)
+ return get(readfile(gitdir . '/' . ref), 0, '')
+ endif
+
+ if filereadable(gitdir . '/packed-refs')
+ for line in readfile(gitdir . '/packed-refs')
+ if line =~# ' ' . ref
+ return matchstr(line, '^[0-9a-f]*')
+ endif
+ endfor
+ endif
+
+ return ''
+endfunction
+
+function! s:git_local_branch(dir) abort
+ let gitdir = s:git_dir(a:dir)
+ let head = gitdir . '/HEAD'
+ if empty(gitdir) || !filereadable(head)
+ return ''
+ endif
+ let branch = matchstr(get(readfile(head), 0, ''), '^ref: refs/heads/\zs.*')
+ return len(branch) ? branch : 'HEAD'
+endfunction
+
+function! s:git_origin_branch(spec)
+ if len(a:spec.branch)
+ return a:spec.branch
+ endif
+
+ " The file may not be present if this is a local repository
+ let gitdir = s:git_dir(a:spec.dir)
+ let origin_head = gitdir.'/refs/remotes/origin/HEAD'
+ if len(gitdir) && filereadable(origin_head)
+ return matchstr(get(readfile(origin_head), 0, ''),
+ \ '^ref: refs/remotes/origin/\zs.*')
+ endif
+
+ " The command may not return the name of a branch in detached HEAD state
+ let result = s:lines(s:system('git symbolic-ref --short HEAD', a:spec.dir))
+ return v:shell_error ? '' : result[-1]
+endfunction
+
+if s:is_win
+ function! s:plug_call(fn, ...)
+ let shellslash = &shellslash
+ try
+ set noshellslash
+ return call(a:fn, a:000)
+ finally
+ let &shellslash = shellslash
+ endtry
+ endfunction
+else
+ function! s:plug_call(fn, ...)
+ return call(a:fn, a:000)
+ endfunction
+endif
+
+function! s:plug_getcwd()
+ return s:plug_call('getcwd')
+endfunction
+
+function! s:plug_fnamemodify(fname, mods)
+ return s:plug_call('fnamemodify', a:fname, a:mods)
+endfunction
+
+function! s:plug_expand(fmt)
+ return s:plug_call('expand', a:fmt, 1)
+endfunction
+
+function! s:plug_tempname()
+ return s:plug_call('tempname')
+endfunction
+
+function! plug#begin(...)
+ if a:0 > 0
+ let s:plug_home_org = a:1
+ let home = s:path(s:plug_fnamemodify(s:plug_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 s:plug_fnamemodify(home, ':t') ==# 'plugin' && s:plug_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
+ if has('win32')
+ \ && &shellslash
+ \ && (&shell =~# 'cmd\(\.exe\)\?$' || &shell =~# 'powershell\(\.exe\)\?$')
+ return s:err('vim-plug does not support shell, ' . &shell . ', when shellslash is set.')
+ endif
+ if !has('nvim')
+ \ && (has('win32') || has('win32unix'))
+ \ && !has('multi_byte')
+ return s:err('Vim needs +multi_byte feature on Windows to run shell commands. Enable +iconv for best results.')
+ 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('plug#end() called without calling 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
+
+function! s:rtp(spec)
+ return s:path(a:spec.dir . get(a:spec, 'rtp', ''))
+endfunction
+
+if s:is_win
+ 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
+
+ " Copied from fzf
+ function! s:wrap_cmds(cmds)
+ let cmds = [
+ \ '@echo off',
+ \ 'setlocal enabledelayedexpansion']
+ \ + (type(a:cmds) == type([]) ? a:cmds : [a:cmds])
+ \ + ['endlocal']
+ if has('iconv')
+ if !exists('s:codepage')
+ let s:codepage = libcallnr('kernel32.dll', 'GetACP', 0)
+ endif
+ return map(cmds, printf('iconv(v:val."\r", "%s", "cp%d")', &encoding, s:codepage))
+ endif
+ return map(cmds, 'v:val."\r"')
+ endfunction
+
+ function! s:batchfile(cmd)
+ let batchfile = s:plug_tempname().'.bat'
+ call writefile(s:wrap_cmds(a:cmd), batchfile)
+ let cmd = plug#shellescape(batchfile, {'shell': &shell, 'script': 0})
+ if &shell =~# 'powershell\(\.exe\)\?$'
+ let cmd = '& ' . cmd
+ endif
+ return [batchfile, cmd]
+ endfunction
+else
+ 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', s:plug_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(repo . ' ' . v:exception)
+ endtry
+endfunction
+
+function! s:parse_options(arg)
+ let opts = copy(s:base_spec)
+ let type = type(a:arg)
+ let opt_errfmt = 'Invalid argument for "%s" option of :Plug (expected: %s)'
+ if type == s:TYPE.string
+ if empty(a:arg)
+ throw printf(opt_errfmt, 'tag', 'string')
+ endif
+ let opts.tag = a:arg
+ elseif type == s:TYPE.dict
+ for opt in ['branch', 'tag', 'commit', 'rtp', 'dir', 'as']
+ if has_key(a:arg, opt)
+ \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt]))
+ throw printf(opt_errfmt, opt, 'string')
+ endif
+ endfor
+ for opt in ['on', 'for']
+ if has_key(a:arg, opt)
+ \ && type(a:arg[opt]) != s:TYPE.list
+ \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt]))
+ throw printf(opt_errfmt, opt, 'string or list')
+ endif
+ endfor
+ if has_key(a:arg, 'do')
+ \ && type(a:arg.do) != s:TYPE.funcref
+ \ && (type(a:arg.do) != s:TYPE.string || empty(a:arg.do))
+ throw printf(opt_errfmt, 'do', 'string or funcref')
+ endif
+ call extend(opts, a:arg)
+ if has_key(opts, 'dir')
+ let opts.dir = s:dirpath(s:plug_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(s:plug_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 /^-\{1}\ /
+ 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 match plugH2 /^-\{2,}/
+ 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(s:plug_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=sh
+ endif
+ if a:swap
+ if &shell =~# 'powershell\(\.exe\)\?$' || &shell =~# 'pwsh$'
+ let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s'
+ elseif &shell =~# 'sh' || &shell =~# 'cmd\(\.exe\)\?$'
+ set shellredir=>%s\ 2>&1
+ endif
+ endif
+ return prev
+endfunction
+
+function! s:bang(cmd, ...)
+ let batchfile = ''
+ 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, cmd] = s:batchfile(cmd)
+ 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 && filereadable(batchfile)
+ 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
+ call s:load_plugin(spec)
+ 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:git_revision(a:spec.dir)
+ if !empty(output) && !s:hash_match(sha, s:lines(output)[0])
+ let credential_helper = s:git_version_requirement(2) ? '-c credential.helper= ' : ''
+ let output = s:system(
+ \ 'git '.credential_helper.'fetch --depth 999999 && git checkout '.plug#shellescape(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 = []
+ if get(g:, 'plug_shallow', 1)
+ call extend(s:clone_opt, ['--depth', '1'])
+ if s:git_version_requirement(1, 7, 10)
+ call add(s:clone_opt, '--no-single-branch')
+ endif
+ endif
+
+ if has('win32unix') || has('wsl')
+ call extend(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 '.plug#shellescape(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 '.plug#shellescape(tag).' -- 2>&1', spec.dir)
+ else
+ let branch = s:git_origin_branch(spec)
+ call s:log4(name, 'Merging origin/'.s:esc(branch))
+ let out = s:system('git checkout -q '.plug#shellescape(branch).' -- 2>&1'
+ \. (has_key(s:update.new, name) ? '' : ('&& git merge --ff-only '.plug#shellescape('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:rm_rf(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' || a:event == 'stderr') ?
+ \ 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': [''],
+ \ 'new': get(a:opts, 'new', 0) }
+ let s:jobs[a:name] = job
+
+ if s:nvim
+ if has_key(a:opts, 'dir')
+ let job.cwd = a:opts.dir
+ endif
+ let argv = a:cmd
+ call extend(job, {
+ \ 'on_stdout': function('s:nvim_cb'),
+ \ 'on_stderr': function('s:nvim_cb'),
+ \ 'on_exit': function('s:nvim_cb'),
+ \ })
+ let jid = s:plug_call('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 cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"script": 0})'))
+ if has_key(a:opts, 'dir')
+ let cmd = s:with_cd(cmd, a:opts.dir, 0)
+ endif
+ let argv = s:is_win ? ['cmd', '/s', '/c', '"'.cmd.'"'] : ['sh', '-c', cmd]
+ let jid = job_start(s:is_win ? join(argv, ' ') : argv, {
+ \ 'out_cb': function('s:job_cb', ['s:job_out_cb', job]),
+ \ 'err_cb': function('s:job_cb', ['s:job_out_cb', job]),
+ \ 'exit_cb': function('s:job_cb', ['s:job_exit_cb', job]),
+ \ 'err_mode': 'raw',
+ \ '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', has_key(a:opts, 'dir') ? [a:cmd, a:opts.dir] : [a: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()
+
+ 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)
+ let max = line('$')
+ for i in range(4, max > 4 ? max : 4)
+ if getline(i) =~# '^[-+x*] '.a:name.':'
+ for j in range(i + 1, max > 5 ? max : 5)
+ 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 cmd = ['git', 'fetch']
+ if has_tag && !empty(globpath(spec.dir, '.git/shallow'))
+ call extend(cmd, ['--depth', '99999999'])
+ endif
+ if !empty(prog)
+ call add(cmd, prog)
+ endif
+ call s:spawn(name, cmd, { '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
+ let cmd = ['git', 'clone']
+ if !has_tag
+ call extend(cmd, s:clone_opt)
+ endif
+ if !empty(prog)
+ call add(cmd, prog)
+ endif
+ call s:spawn(name, extend(cmd, [spec.uri, 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 = ' '.join(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').join(' ')
+ 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, script)
+ let escaped = substitute('"'.a:arg.'"', '[&|<>()@^!"]', '^&', 'g')
+ return substitute(escaped, '%', (a:script ? '%' : '^') . '&', 'g')
+endfunction
+
+function! s:shellesc_ps1(arg)
+ return "'".substitute(escape(a:arg, '\"'), "'", "''", 'g')."'"
+endfunction
+
+function! s:shellesc_sh(arg)
+ return "'".substitute(a:arg, "'", "'\\\\''", 'g')."'"
+endfunction
+
+" Escape the shell argument based on the shell.
+" Vim and Neovim's shellescape() are insufficient.
+" 1. shellslash determines whether to use single/double quotes.
+" Double-quote escaping is fragile for cmd.exe.
+" 2. It does not work for powershell.
+" 3. It does not work for *sh shells if the command is executed
+" via cmd.exe (ie. cmd.exe /c sh -c command command_args)
+" 4. It does not support batchfile syntax.
+"
+" Accepts an optional dictionary with the following keys:
+" - shell: same as Vim/Neovim 'shell' option.
+" If unset, fallback to 'cmd.exe' on Windows or 'sh'.
+" - script: If truthy and shell is cmd.exe, escape for batchfile syntax.
+function! plug#shellescape(arg, ...)
+ if a:arg =~# '^[A-Za-z0-9_/:.-]\+$'
+ return a:arg
+ endif
+ let opts = a:0 > 0 && type(a:1) == s:TYPE.dict ? a:1 : {}
+ let shell = get(opts, 'shell', s:is_win ? 'cmd.exe' : 'sh')
+ let script = get(opts, 'script', 1)
+ if shell =~# 'cmd\(\.exe\)\?$'
+ return s:shellesc_cmd(a:arg, script)
+ elseif shell =~# 'powershell\(\.exe\)\?$' || shell =~# 'pwsh$'
+ return s:shellesc_ps1(a:arg)
+ endif
+ return s:shellesc_sh(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, ...)
+ let script = a:0 > 0 ? a:1 : 1
+ return printf('cd%s %s && %s', s:is_win ? ' /d' : '', plug#shellescape(a:dir, {'script': script}), a:cmd)
+endfunction
+
+function! s:system(cmd, ...)
+ let batchfile = ''
+ try
+ let [sh, shellcmdflag, shrd] = s:chsh(1)
+ if type(a:cmd) == s:TYPE.list
+ " Neovim's system() supports list argument to bypass the shell
+ " but it cannot set the working directory for the command.
+ " Assume that the command does not rely on the shell.
+ if has('nvim') && a:0 == 0
+ return system(a:cmd)
+ endif
+ let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"shell": &shell, "script": 0})'))
+ if &shell =~# 'powershell\(\.exe\)\?$'
+ let cmd = '& ' . cmd
+ endif
+ else
+ let cmd = a:cmd
+ endif
+ if a:0 > 0
+ let cmd = s:with_cd(cmd, a:1, type(a:cmd) != s:TYPE.list)
+ endif
+ if s:is_win && type(a:cmd) != s:TYPE.list
+ let [batchfile, cmd] = s:batchfile(cmd)
+ endif
+ return system(cmd)
+ finally
+ let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd]
+ if s:is_win && filereadable(batchfile)
+ 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:git_local_branch(a:spec.dir), s:git_origin_url(a:spec.dir)]
+ let remote = result[-1]
+ if empty(remote)
+ 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 sha = s:git_revision(a:spec.dir)
+ if empty(sha)
+ 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 current_branch = result[0]
+ " Check tag
+ let origin_branch = s:git_origin_branch(a:spec)
+ 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 origin_branch !=# current_branch
+ let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.',
+ \ current_branch, origin_branch)
+ endif
+ if empty(err)
+ let [ahead, behind] = split(s:lastline(s:system([
+ \ 'git', 'rev-list', '--count', '--left-right',
+ \ printf('HEAD...origin/%s', origin_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.', origin_branch, ahead, behind)
+ else
+ let err = printf("Ahead of origin/%s by %d commit(s).\n"
+ \ .'Cannot update until local changes are pushed.',
+ \ origin_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)
+ return s:system(s:is_win
+ \ ? 'rmdir /S /Q '.plug#shellescape(a:dir)
+ \ : ['rm', '-rf', 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(s:plug_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
+ let err_count = 0
+ 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
+ let err = s:rm_rf(line[2:])
+ setlocal modifiable
+ if empty(err)
+ call setline(l1, '~'.line[1:])
+ let s:clean_count += 1
+ else
+ delete _
+ call append(l1 - 1, s:format_message('x', line[1:], err))
+ let l2 += len(s:lines(err))
+ let err_count += 1
+ endif
+ let msg = printf('Removed %d directories.', s:clean_count)
+ if err_count > 0
+ let msg .= printf(' Failed to remove %d directories.', err_count)
+ endif
+ call setline(4, msg)
+ setlocal nomodifiable
+ endif
+ endif
+ let l1 += 1
+ endwhile
+endfunction
+
+function! s:upgrade()
+ echo 'Downloading the latest version of vim-plug'
+ redraw
+ let tmp = s:plug_tempname()
+ let new = tmp . '/plug.vim'
+
+ try
+ let out = s:system(['git', 'clone', '--depth', '1', s:plug_src, 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
+ let batchfile = ''
+ try
+ let [sh, shellcmdflag, shrd] = s:chsh(1)
+ let cmd = 'cd '.plug#shellescape(g:plugs[name].dir).' && git show --no-color --pretty=medium '.sha
+ if s:is_win
+ let [batchfile, cmd] = s:batchfile(cmd)
+ endif
+ execute 'silent %!' cmd
+ finally
+ let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd]
+ if s:is_win && filereadable(batchfile)
+ 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 branch = s:git_origin_branch(v)
+ if len(branch)
+ let range = origin ? '..origin/'.branch : 'HEAD@{1}..'
+ let cmd = ['git', 'log', '--graph', '--color=never']
+ if s:git_version_requirement(2, 10, 0)
+ call add(cmd, '--no-show-signature')
+ endif
+ call extend(cmd, ['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range])
+ if has_key(v, 'rtp')
+ call extend(cmd, ['--', 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
+ 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 '.plug#shellescape(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:git_revision(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 = s:plug_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/stow/vim/dot-vim/colors/burnttoast256.vim b/stow/vim/dot-vim/colors/burnttoast256.vim
new file mode 100644
index 0000000..b9abaac
--- /dev/null
+++ b/stow/vim/dot-vim/colors/burnttoast256.vim
@@ -0,0 +1,116 @@
+" Vim color file
+"
+" Name: burnttoast256.vim
+" Version: 1.4
+" Maintainer: Francois Labelle <quantum.omega@gmail.com>
+" Based on: xoria256.vim 1.1 by Dmitriy Y. Zotikov (xio) <xio@ungrund.org>
+"
+" Should work in recent 256 color terminals. 88-color terms like urxvt are
+" unsupported.
+"
+" Don't forget to install 'ncurses-term' and set TERM to xterm-256color or
+" similar value.
+"
+" Color numbers (0-255) see:
+" http://www.calmar.ws/vim/256-xterm-24bit-rgb-color-chart.html
+
+
+
+" Bla-bla ----------------------------------------------------------------------
+
+if &t_Co != 256 && ! has("gui_running")
+ echomsg ""
+ echomsg "err: please use GUI or a 256-color terminal (so that t_Co=256 can be set)"
+ echomsg ""
+ finish
+endif
+
+set background=dark
+
+hi clear
+
+if exists("syntax_on")
+ syntax reset
+endif
+
+let colors_name = "burnttoast256"
+
+
+" The real part ----------------------------------------------------------------
+
+"" General colors
+hi Normal ctermfg=252 guifg=#d0d0d0 ctermbg=16 guibg=#000000 cterm=none gui=none
+hi ColorColumn ctermbg=233 guibg=#121212
+hi CursorColumn ctermbg=238 guibg=#444444
+hi Cursor ctermbg=214 guibg=#ffaf00
+hi CursorLine cterm=none gui=none
+hi CursorLineNr ctermfg=7 guifg=#c0c0c0 ctermbg=233 guibg=#121212 cterm=bold gui=bold
+hi FoldColumn ctermfg=248 guifg=#a8a8a8 ctermbg=bg guibg=bg
+hi Folded ctermfg=255 guifg=#eeeeee ctermbg=60 guibg=#5f5f87
+hi IncSearch ctermfg=0 guifg=#000000 ctermbg=223 guibg=#ffd7af cterm=none gui=none
+hi LineNr ctermfg=8 guifg=#808080 ctermbg=233 guibg=#121212
+hi NonText ctermfg=237 guifg=#3a3a3a
+hi Pmenu ctermfg=15 guifg=#ffffff ctermbg=234 guibg=#1c1c1c
+hi PmenuSbar ctermbg=243 guibg=#767676
+hi PmenuSel ctermfg=146 guifg=#afafd7 ctermbg=232 guibg=#080808 cterm=bold gui=bold
+hi PmenuThumb ctermbg=252 guibg=#d0d0d0
+hi Search ctermfg=0 guifg=#000000 ctermbg=149 guibg=#afd75f
+hi SignColumn ctermfg=248 guifg=#a8a8a8
+hi SpecialKey ctermfg=237 guifg=#3a3a3a
+hi StatusLine ctermbg=239 guibg=#4e4e4e cterm=bold gui=bold
+hi StatusLineNC ctermbg=237 guibg=#3a3a3a cterm=none gui=none
+hi TabLine ctermfg=fg guifg=fg ctermbg=242 guibg=#6c6c6c cterm=underline gui=underline
+hi TabLineFill ctermfg=fg guifg=fg ctermbg=242 guibg=#6c6c6c cterm=underline gui=underline
+hi VertSplit ctermfg=237 guifg=#3a3a3a ctermbg=237 guibg=#3a3a3a cterm=none gui=none
+hi Visual ctermfg=24 guifg=#005f87 ctermbg=153 guibg=#afd7ff
+hi VIsualNOS ctermfg=24 guifg=#005f87 ctermbg=153 guibg=#afd7ff cterm=none gui=none
+hi WildMenu ctermfg=0 guifg=#000000 ctermbg=184 guibg=#d7d700 cterm=bold gui=bold
+
+"" Syntax highlighting
+hi Comment ctermfg=36 guifg=#00af87
+hi Constant ctermfg=222 guifg=#ffd787
+hi Error ctermfg=9 guifg=#ff0000 ctermbg=52 guibg=#5f0000 cterm=none gui=none
+hi ErrorMsg ctermfg=9 guifg=#ff0000 ctermbg=52 guibg=#5f0000
+hi Function ctermfg=134 guifg=#af5fd7
+hi Identifier ctermfg=134 guifg=#af5fd7 cterm=none gui=none
+hi Ignore ctermfg=238 guifg=#444444
+hi MatchParen ctermfg=188 guifg=#d7d7d7 ctermbg=68 guibg=#5f87d7 cterm=bold gui=bold
+hi Number ctermfg=180 guifg=#d7af87
+hi PreProc ctermfg=150 guifg=#afd787
+hi Special ctermfg=132 guifg=#af5f87
+hi Statement ctermfg=75 guifg=#5fafff cterm=none gui=none
+hi String ctermfg=217 guifg=#ffafaf
+hi Todo ctermfg=0 guifg=#000000 ctermbg=36 guibg=#00af87
+hi Type ctermfg=146 guifg=#afafd7 cterm=none gui=none
+hi Underlined ctermfg=39 guifg=#00afff cterm=underline gui=underline
+
+"" Python highlighting
+hi pythonFunctionCall ctermfg=210 guifg=#ff8787
+"hi pythonStrFormatting Special
+"hi pythonStrFormat Special
+"hi pythonStrTemplate Special
+
+"" Special
+""" .diff
+hi diffAdded ctermfg=150 guifg=#afd787
+hi diffRemoved ctermfg=174 guifg=#d78787
+""" vimdiff
+hi DiffAdd ctermbg=22 guibg=#005f00
+hi DiffDelete ctermfg=9 guifg=#ff0000 ctermbg=52 guibg=#5f0000 cterm=none gui=none
+hi DiffChange ctermbg=235 guibg=#262626 cterm=none gui=none
+hi DiffText ctermbg=17 guibg=#00005f cterm=none gui=none
+
+"" To highlight lines that are too long
+hi OverLength ctermbg=130 guibg=#af5f00
+
+"" Spelling and compiling errors
+hi SpellBad ctermbg=52 guibg=#5f0000
+
+"" Colors for unit testing.
+hi RedBar ctermbg=52 guibg=#5f0000
+hi GreenBar ctermbg=22 guibg=#005f00
+
+" For indentation
+hi IndentGuidesOdd ctermbg=235 guibg=#262626 ctermfg=237 guifg=#3a3a3a
+hi IndentGuidesEven ctermbg=233 guibg=#121212 ctermfg=237 guifg=#3a3a3a
+
diff --git a/stow/vim/dot-vim/colors/nord.vim b/stow/vim/dot-vim/colors/nord.vim
new file mode 100644
index 0000000..6b00afb
--- /dev/null
+++ b/stow/vim/dot-vim/colors/nord.vim
@@ -0,0 +1,764 @@
+" Copyright (C) 2016-present Arctic Ice Studio <development@arcticicestudio.com>
+" Copyright (C) 2016-present Sven Greb <development@svengreb.de>
+
+" Project: Nord Vim
+" Repository: https://github.com/arcticicestudio/nord-vim
+" License: MIT
+
+if version > 580
+ hi clear
+ if exists("syntax_on")
+ syntax reset
+ endif
+endif
+
+let g:colors_name = "nord"
+let s:nord_vim_version="0.15.0"
+set background=dark
+
+let s:nord0_gui = "#2E3440"
+let s:nord1_gui = "#3B4252"
+let s:nord2_gui = "#434C5E"
+let s:nord3_gui = "#4C566A"
+let s:nord3_gui_bright = "#616E88"
+let s:nord4_gui = "#D8DEE9"
+let s:nord5_gui = "#E5E9F0"
+let s:nord6_gui = "#ECEFF4"
+let s:nord7_gui = "#8FBCBB"
+let s:nord8_gui = "#88C0D0"
+let s:nord9_gui = "#81A1C1"
+let s:nord10_gui = "#5E81AC"
+let s:nord11_gui = "#BF616A"
+let s:nord12_gui = "#D08770"
+let s:nord13_gui = "#EBCB8B"
+let s:nord14_gui = "#A3BE8C"
+let s:nord15_gui = "#B48EAD"
+
+let s:nord1_term = "0"
+let s:nord3_term = "8"
+let s:nord5_term = "7"
+let s:nord6_term = "15"
+let s:nord7_term = "14"
+let s:nord8_term = "6"
+let s:nord9_term = "4"
+let s:nord10_term = "12"
+let s:nord11_term = "1"
+let s:nord12_term = "11"
+let s:nord13_term = "3"
+let s:nord14_term = "2"
+let s:nord15_term = "5"
+
+let s:nord3_gui_brightened = [
+ \ s:nord3_gui,
+ \ "#4e586d",
+ \ "#505b70",
+ \ "#525d73",
+ \ "#556076",
+ \ "#576279",
+ \ "#59647c",
+ \ "#5b677f",
+ \ "#5d6982",
+ \ "#5f6c85",
+ \ "#616e88",
+ \ "#63718b",
+ \ "#66738e",
+ \ "#687591",
+ \ "#6a7894",
+ \ "#6d7a96",
+ \ "#6f7d98",
+ \ "#72809a",
+ \ "#75829c",
+ \ "#78859e",
+ \ "#7b88a1",
+\ ]
+
+if !exists("g:nord_bold")
+ let g:nord_bold = 1
+endif
+
+let s:bold = "bold,"
+if g:nord_bold == 0
+ let s:bold = ""
+endif
+
+if !exists("g:nord_italic")
+ if has("gui_running") || $TERM_ITALICS == "true"
+ let g:nord_italic = 1
+ else
+ let g:nord_italic = 0
+ endif
+endif
+
+let s:italic = "italic,"
+if g:nord_italic == 0
+ let s:italic = ""
+endif
+
+let s:underline = "underline,"
+if ! get(g:, "nord_underline", 1)
+ let s:underline = "NONE,"
+endif
+
+let s:italicize_comments = ""
+if exists("g:nord_italic_comments")
+ if g:nord_italic_comments == 1
+ let s:italicize_comments = s:italic
+ endif
+endif
+
+if !exists('g:nord_uniform_status_lines')
+ let g:nord_uniform_status_lines = 0
+endif
+
+function! s:logWarning(msg)
+ echohl WarningMsg
+ echomsg 'nord: warning: ' . a:msg
+ echohl None
+endfunction
+
+if exists("g:nord_comment_brightness")
+ call s:logWarning('Variable g:nord_comment_brightness has been deprecated and will be removed in version 1.0.0!' .
+ \' The comment color brightness has been increased by 10% by default.' .
+ \' Please see https://github.com/arcticicestudio/nord-vim/issues/145 for more details.')
+ let g:nord_comment_brightness = 10
+endif
+
+if !exists("g:nord_uniform_diff_background")
+ let g:nord_uniform_diff_background = 0
+endif
+
+if !exists("g:nord_cursor_line_number_background")
+ let g:nord_cursor_line_number_background = 0
+endif
+
+if !exists("g:nord_bold_vertical_split_line")
+ let g:nord_bold_vertical_split_line = 0
+endif
+
+function! s:hi(group, guifg, guibg, ctermfg, ctermbg, attr, guisp)
+ if a:guifg != ""
+ exec "hi " . a:group . " guifg=" . a:guifg
+ endif
+ if a:guibg != ""
+ exec "hi " . a:group . " guibg=" . a:guibg
+ endif
+ if a:ctermfg != ""
+ exec "hi " . a:group . " ctermfg=" . a:ctermfg
+ endif
+ if a:ctermbg != ""
+ exec "hi " . a:group . " ctermbg=" . a:ctermbg
+ endif
+ if a:attr != ""
+ exec "hi " . a:group . " gui=" . a:attr . " cterm=" . substitute(a:attr, "undercurl", s:underline, "")
+ endif
+ if a:guisp != ""
+ exec "hi " . a:group . " guisp=" . a:guisp
+ endif
+endfunction
+
+"+---------------+
+"+ UI Components +
+"+---------------+
+"+--- Attributes ---+
+call s:hi("Bold", "", "", "", "", s:bold, "")
+call s:hi("Italic", "", "", "", "", s:italic, "")
+call s:hi("Underline", "", "", "", "", s:underline, "")
+
+"+--- Editor ---+
+call s:hi("ColorColumn", "", s:nord1_gui, "NONE", s:nord1_term, "", "")
+call s:hi("Cursor", s:nord0_gui, s:nord4_gui, "", "NONE", "", "")
+call s:hi("CursorLine", "", s:nord1_gui, "NONE", s:nord1_term, "NONE", "")
+call s:hi("Error", s:nord4_gui, s:nord11_gui, "", s:nord11_term, "", "")
+call s:hi("iCursor", s:nord0_gui, s:nord4_gui, "", "NONE", "", "")
+call s:hi("LineNr", s:nord3_gui, "NONE", s:nord3_term, "NONE", "", "")
+call s:hi("MatchParen", s:nord8_gui, s:nord3_gui, s:nord8_term, s:nord3_term, "", "")
+call s:hi("NonText", s:nord2_gui, "", s:nord3_term, "", "", "")
+call s:hi("Normal", s:nord4_gui, s:nord0_gui, "NONE", "NONE", "", "")
+call s:hi("PMenu", s:nord4_gui, s:nord2_gui, "NONE", s:nord1_term, "NONE", "")
+call s:hi("PmenuSbar", s:nord4_gui, s:nord2_gui, "NONE", s:nord1_term, "", "")
+call s:hi("PMenuSel", s:nord8_gui, s:nord3_gui, s:nord8_term, s:nord3_term, "", "")
+call s:hi("PmenuThumb", s:nord8_gui, s:nord3_gui, "NONE", s:nord3_term, "", "")
+call s:hi("SpecialKey", s:nord3_gui, "", s:nord3_term, "", "", "")
+call s:hi("SpellBad", s:nord11_gui, s:nord0_gui, s:nord11_term, "NONE", "undercurl", s:nord11_gui)
+call s:hi("SpellCap", s:nord13_gui, s:nord0_gui, s:nord13_term, "NONE", "undercurl", s:nord13_gui)
+call s:hi("SpellLocal", s:nord5_gui, s:nord0_gui, s:nord5_term, "NONE", "undercurl", s:nord5_gui)
+call s:hi("SpellRare", s:nord6_gui, s:nord0_gui, s:nord6_term, "NONE", "undercurl", s:nord6_gui)
+call s:hi("Visual", "", s:nord2_gui, "", s:nord1_term, "", "")
+call s:hi("VisualNOS", "", s:nord2_gui, "", s:nord1_term, "", "")
+"+- Neovim Support -+
+call s:hi("healthError", s:nord11_gui, s:nord1_gui, s:nord11_term, s:nord1_term, "", "")
+call s:hi("healthSuccess", s:nord14_gui, s:nord1_gui, s:nord14_term, s:nord1_term, "", "")
+call s:hi("healthWarning", s:nord13_gui, s:nord1_gui, s:nord13_term, s:nord1_term, "", "")
+call s:hi("TermCursorNC", "", s:nord1_gui, "", s:nord1_term, "", "")
+
+"+- Vim 8 Terminal Colors -+
+if has('terminal')
+ let g:terminal_ansi_colors = [s:nord1_gui, s:nord11_gui, s:nord14_gui, s:nord13_gui, s:nord9_gui, s:nord15_gui, s:nord8_gui, s:nord5_gui, s:nord3_gui, s:nord11_gui, s:nord14_gui, s:nord13_gui, s:nord9_gui, s:nord15_gui, s:nord7_gui, s:nord6_gui]
+endif
+
+"+- Neovim Terminal Colors -+
+if has('nvim')
+ let g:terminal_color_0 = s:nord1_gui
+ let g:terminal_color_1 = s:nord11_gui
+ let g:terminal_color_2 = s:nord14_gui
+ let g:terminal_color_3 = s:nord13_gui
+ let g:terminal_color_4 = s:nord9_gui
+ let g:terminal_color_5 = s:nord15_gui
+ let g:terminal_color_6 = s:nord8_gui
+ let g:terminal_color_7 = s:nord5_gui
+ let g:terminal_color_8 = s:nord3_gui
+ let g:terminal_color_9 = s:nord11_gui
+ let g:terminal_color_10 = s:nord14_gui
+ let g:terminal_color_11 = s:nord13_gui
+ let g:terminal_color_12 = s:nord9_gui
+ let g:terminal_color_13 = s:nord15_gui
+ let g:terminal_color_14 = s:nord7_gui
+ let g:terminal_color_15 = s:nord6_gui
+endif
+
+"+--- Gutter ---+
+call s:hi("CursorColumn", "", s:nord1_gui, "NONE", s:nord1_term, "", "")
+if g:nord_cursor_line_number_background == 0
+ call s:hi("CursorLineNr", s:nord4_gui, "", "NONE", "", "NONE", "")
+else
+ call s:hi("CursorLineNr", s:nord4_gui, s:nord1_gui, "NONE", s:nord1_term, "NONE", "")
+endif
+call s:hi("Folded", s:nord3_gui, s:nord1_gui, s:nord3_term, s:nord1_term, s:bold, "")
+call s:hi("FoldColumn", s:nord3_gui, s:nord0_gui, s:nord3_term, "NONE", "", "")
+call s:hi("SignColumn", s:nord1_gui, s:nord0_gui, s:nord1_term, "NONE", "", "")
+
+"+--- Navigation ---+
+call s:hi("Directory", s:nord8_gui, "", s:nord8_term, "NONE", "", "")
+
+"+--- Prompt/Status ---+
+call s:hi("EndOfBuffer", s:nord1_gui, "", s:nord1_term, "NONE", "", "")
+call s:hi("ErrorMsg", s:nord4_gui, s:nord11_gui, "NONE", s:nord11_term, "", "")
+call s:hi("ModeMsg", s:nord4_gui, "", "", "", "", "")
+call s:hi("MoreMsg", s:nord8_gui, "", s:nord8_term, "", "", "")
+call s:hi("Question", s:nord4_gui, "", "NONE", "", "", "")
+if g:nord_uniform_status_lines == 0
+ call s:hi("StatusLine", s:nord8_gui, s:nord3_gui, s:nord8_term, s:nord3_term, "NONE", "")
+ call s:hi("StatusLineNC", s:nord4_gui, s:nord1_gui, "NONE", s:nord1_term, "NONE", "")
+ call s:hi("StatusLineTerm", s:nord8_gui, s:nord3_gui, s:nord8_term, s:nord3_term, "NONE", "")
+ call s:hi("StatusLineTermNC", s:nord4_gui, s:nord1_gui, "NONE", s:nord1_term, "NONE", "")
+else
+ call s:hi("StatusLine", s:nord8_gui, s:nord3_gui, s:nord8_term, s:nord3_term, "NONE", "")
+ call s:hi("StatusLineNC", s:nord4_gui, s:nord3_gui, "NONE", s:nord3_term, "NONE", "")
+ call s:hi("StatusLineTerm", s:nord8_gui, s:nord3_gui, s:nord8_term, s:nord3_term, "NONE", "")
+ call s:hi("StatusLineTermNC", s:nord4_gui, s:nord3_gui, "NONE", s:nord3_term, "NONE", "")
+endif
+call s:hi("WarningMsg", s:nord0_gui, s:nord13_gui, s:nord1_term, s:nord13_term, "", "")
+call s:hi("WildMenu", s:nord8_gui, s:nord1_gui, s:nord8_term, s:nord1_term, "", "")
+
+"+--- Search ---+
+call s:hi("IncSearch", s:nord6_gui, s:nord10_gui, s:nord6_term, s:nord10_term, s:underline, "")
+call s:hi("Search", s:nord1_gui, s:nord8_gui, s:nord1_term, s:nord8_term, "NONE", "")
+
+"+--- Tabs ---+
+call s:hi("TabLine", s:nord4_gui, s:nord1_gui, "NONE", s:nord1_term, "NONE", "")
+call s:hi("TabLineFill", s:nord4_gui, s:nord1_gui, "NONE", s:nord1_term, "NONE", "")
+call s:hi("TabLineSel", s:nord8_gui, s:nord3_gui, s:nord8_term, s:nord3_term, "NONE", "")
+
+"+--- Window ---+
+call s:hi("Title", s:nord4_gui, "", "NONE", "", "NONE", "")
+
+if g:nord_bold_vertical_split_line == 0
+ call s:hi("VertSplit", s:nord2_gui, s:nord0_gui, s:nord3_term, "NONE", "NONE", "")
+else
+ call s:hi("VertSplit", s:nord2_gui, s:nord1_gui, s:nord3_term, s:nord1_term, "NONE", "")
+endif
+
+"+----------------------+
+"+ Language Base Groups +
+"+----------------------+
+call s:hi("Boolean", s:nord9_gui, "", s:nord9_term, "", "", "")
+call s:hi("Character", s:nord14_gui, "", s:nord14_term, "", "", "")
+call s:hi("Comment", s:nord3_gui_bright, "", s:nord3_term, "", s:italicize_comments, "")
+call s:hi("Conditional", s:nord9_gui, "", s:nord9_term, "", "", "")
+call s:hi("Constant", s:nord4_gui, "", "NONE", "", "", "")
+call s:hi("Define", s:nord9_gui, "", s:nord9_term, "", "", "")
+call s:hi("Delimiter", s:nord6_gui, "", s:nord6_term, "", "", "")
+call s:hi("Exception", s:nord9_gui, "", s:nord9_term, "", "", "")
+call s:hi("Float", s:nord15_gui, "", s:nord15_term, "", "", "")
+call s:hi("Function", s:nord8_gui, "", s:nord8_term, "", "", "")
+call s:hi("Identifier", s:nord4_gui, "", "NONE", "", "NONE", "")
+call s:hi("Include", s:nord9_gui, "", s:nord9_term, "", "", "")
+call s:hi("Keyword", s:nord9_gui, "", s:nord9_term, "", "", "")
+call s:hi("Label", s:nord9_gui, "", s:nord9_term, "", "", "")
+call s:hi("Number", s:nord15_gui, "", s:nord15_term, "", "", "")
+call s:hi("Operator", s:nord9_gui, "", s:nord9_term, "", "NONE", "")
+call s:hi("PreProc", s:nord9_gui, "", s:nord9_term, "", "NONE", "")
+call s:hi("Repeat", s:nord9_gui, "", s:nord9_term, "", "", "")
+call s:hi("Special", s:nord4_gui, "", "NONE", "", "", "")
+call s:hi("SpecialChar", s:nord13_gui, "", s:nord13_term, "", "", "")
+call s:hi("SpecialComment", s:nord8_gui, "", s:nord8_term, "", s:italicize_comments, "")
+call s:hi("Statement", s:nord9_gui, "", s:nord9_term, "", "", "")
+call s:hi("StorageClass", s:nord9_gui, "", s:nord9_term, "", "", "")
+call s:hi("String", s:nord14_gui, "", s:nord14_term, "", "", "")
+call s:hi("Structure", s:nord9_gui, "", s:nord9_term, "", "", "")
+call s:hi("Tag", s:nord4_gui, "", "", "", "", "")
+call s:hi("Todo", s:nord13_gui, "NONE", s:nord13_term, "NONE", "", "")
+call s:hi("Type", s:nord9_gui, "", s:nord9_term, "", "NONE", "")
+call s:hi("Typedef", s:nord9_gui, "", s:nord9_term, "", "", "")
+hi! link Macro Define
+hi! link PreCondit PreProc
+
+"+-----------+
+"+ Languages +
+"+-----------+
+call s:hi("asciidocAttributeEntry", s:nord10_gui, "", s:nord10_term, "", "", "")
+call s:hi("asciidocAttributeList", s:nord10_gui, "", s:nord10_term, "", "", "")
+call s:hi("asciidocAttributeRef", s:nord10_gui, "", s:nord10_term, "", "", "")
+call s:hi("asciidocHLabel", s:nord9_gui, "", s:nord9_term, "", "", "")
+call s:hi("asciidocListingBlock", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("asciidocMacroAttributes", s:nord8_gui, "", s:nord8_term, "", "", "")
+call s:hi("asciidocOneLineTitle", s:nord8_gui, "", s:nord8_term, "", "", "")
+call s:hi("asciidocPassthroughBlock", s:nord9_gui, "", s:nord9_term, "", "", "")
+call s:hi("asciidocQuotedMonospaced", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("asciidocTriplePlusPassthrough", s:nord7_gui, "", s:nord7_term, "", "", "")
+hi! link asciidocAdmonition Keyword
+hi! link asciidocAttributeRef markdownH1
+hi! link asciidocBackslash Keyword
+hi! link asciidocMacro Keyword
+hi! link asciidocQuotedBold Bold
+hi! link asciidocQuotedEmphasized Italic
+hi! link asciidocQuotedMonospaced2 asciidocQuotedMonospaced
+hi! link asciidocQuotedUnconstrainedBold asciidocQuotedBold
+hi! link asciidocQuotedUnconstrainedEmphasized asciidocQuotedEmphasized
+hi! link asciidocURL markdownLinkText
+
+call s:hi("awkCharClass", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("awkPatterns", s:nord9_gui, "", s:nord9_term, "", s:bold, "")
+hi! link awkArrayElement Identifier
+hi! link awkBoolLogic Keyword
+hi! link awkBrktRegExp SpecialChar
+hi! link awkComma Delimiter
+hi! link awkExpression Keyword
+hi! link awkFieldVars Identifier
+hi! link awkLineSkip Keyword
+hi! link awkOperator Operator
+hi! link awkRegExp SpecialChar
+hi! link awkSearch Keyword
+hi! link awkSemicolon Delimiter
+hi! link awkSpecialCharacter SpecialChar
+hi! link awkSpecialPrintf SpecialChar
+hi! link awkVariables Identifier
+
+call s:hi("cIncluded", s:nord7_gui, "", s:nord7_term, "", "", "")
+hi! link cOperator Operator
+hi! link cPreCondit PreCondit
+
+call s:hi("cmakeGeneratorExpression", s:nord10_gui, "", s:nord10_term, "", "", "")
+
+hi! link csPreCondit PreCondit
+hi! link csType Type
+hi! link csXmlTag SpecialComment
+
+call s:hi("cssAttributeSelector", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("cssDefinition", s:nord7_gui, "", s:nord7_term, "", "NONE", "")
+call s:hi("cssIdentifier", s:nord7_gui, "", s:nord7_term, "", s:underline, "")
+call s:hi("cssStringQ", s:nord7_gui, "", s:nord7_term, "", "", "")
+hi! link cssAttr Keyword
+hi! link cssBraces Delimiter
+hi! link cssClassName cssDefinition
+hi! link cssColor Number
+hi! link cssProp cssDefinition
+hi! link cssPseudoClass cssDefinition
+hi! link cssPseudoClassId cssPseudoClass
+hi! link cssVendor Keyword
+
+call s:hi("dosiniHeader", s:nord8_gui, "", s:nord8_term, "", "", "")
+hi! link dosiniLabel Type
+
+call s:hi("dtBooleanKey", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("dtExecKey", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("dtLocaleKey", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("dtNumericKey", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("dtTypeKey", s:nord7_gui, "", s:nord7_term, "", "", "")
+hi! link dtDelim Delimiter
+hi! link dtLocaleValue Keyword
+hi! link dtTypeValue Keyword
+
+if g:nord_uniform_diff_background == 0
+ call s:hi("DiffAdd", s:nord14_gui, s:nord0_gui, s:nord14_term, "NONE", "inverse", "")
+ call s:hi("DiffChange", s:nord13_gui, s:nord0_gui, s:nord13_term, "NONE", "inverse", "")
+ call s:hi("DiffDelete", s:nord11_gui, s:nord0_gui, s:nord11_term, "NONE", "inverse", "")
+ call s:hi("DiffText", s:nord9_gui, s:nord0_gui, s:nord9_term, "NONE", "inverse", "")
+else
+ call s:hi("DiffAdd", s:nord14_gui, s:nord1_gui, s:nord14_term, s:nord1_term, "", "")
+ call s:hi("DiffChange", s:nord13_gui, s:nord1_gui, s:nord13_term, s:nord1_term, "", "")
+ call s:hi("DiffDelete", s:nord11_gui, s:nord1_gui, s:nord11_term, s:nord1_term, "", "")
+ call s:hi("DiffText", s:nord9_gui, s:nord1_gui, s:nord9_term, s:nord1_term, "", "")
+endif
+" Legacy groups for official git.vim and diff.vim syntax
+hi! link diffAdded DiffAdd
+hi! link diffChanged DiffChange
+hi! link diffRemoved DiffDelete
+
+call s:hi("gitconfigVariable", s:nord7_gui, "", s:nord7_term, "", "", "")
+
+call s:hi("goBuiltins", s:nord7_gui, "", s:nord7_term, "", "", "")
+hi! link goConstants Keyword
+
+call s:hi("helpBar", s:nord3_gui, "", s:nord3_term, "", "", "")
+call s:hi("helpHyperTextJump", s:nord8_gui, "", s:nord8_term, "", s:underline, "")
+
+call s:hi("htmlArg", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("htmlLink", s:nord4_gui, "", "", "", "NONE", "NONE")
+hi! link htmlBold Bold
+hi! link htmlEndTag htmlTag
+hi! link htmlItalic Italic
+hi! link htmlH1 markdownH1
+hi! link htmlH2 markdownH1
+hi! link htmlH3 markdownH1
+hi! link htmlH4 markdownH1
+hi! link htmlH5 markdownH1
+hi! link htmlH6 markdownH1
+hi! link htmlSpecialChar SpecialChar
+hi! link htmlTag Keyword
+hi! link htmlTagN htmlTag
+
+call s:hi("javaDocTags", s:nord7_gui, "", s:nord7_term, "", "", "")
+hi! link javaCommentTitle Comment
+hi! link javaScriptBraces Delimiter
+hi! link javaScriptIdentifier Keyword
+hi! link javaScriptNumber Number
+
+call s:hi("jsonKeyword", s:nord7_gui, "", s:nord7_term, "", "", "")
+
+call s:hi("lessClass", s:nord7_gui, "", s:nord7_term, "", "", "")
+hi! link lessAmpersand Keyword
+hi! link lessCssAttribute Delimiter
+hi! link lessFunction Function
+hi! link cssSelectorOp Keyword
+
+hi! link lispAtomBarSymbol SpecialChar
+hi! link lispAtomList SpecialChar
+hi! link lispAtomMark Keyword
+hi! link lispBarSymbol SpecialChar
+hi! link lispFunc Function
+
+hi! link luaFunc Function
+
+call s:hi("markdownBlockquote", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("markdownCode", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("markdownCodeDelimiter", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("markdownFootnote", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("markdownId", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("markdownIdDeclaration", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("markdownH1", s:nord8_gui, "", s:nord8_term, "", "", "")
+call s:hi("markdownLinkText", s:nord8_gui, "", s:nord8_term, "", "", "")
+call s:hi("markdownUrl", s:nord4_gui, "", "NONE", "", "NONE", "")
+hi! link markdownBold Bold
+hi! link markdownBoldDelimiter Keyword
+hi! link markdownFootnoteDefinition markdownFootnote
+hi! link markdownH2 markdownH1
+hi! link markdownH3 markdownH1
+hi! link markdownH4 markdownH1
+hi! link markdownH5 markdownH1
+hi! link markdownH6 markdownH1
+hi! link markdownIdDelimiter Keyword
+hi! link markdownItalic Italic
+hi! link markdownItalicDelimiter Keyword
+hi! link markdownLinkDelimiter Keyword
+hi! link markdownLinkTextDelimiter Keyword
+hi! link markdownListMarker Keyword
+hi! link markdownRule Keyword
+hi! link markdownHeadingDelimiter Keyword
+
+call s:hi("perlPackageDecl", s:nord7_gui, "", s:nord7_term, "", "", "")
+
+call s:hi("phpClasses", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("phpDocTags", s:nord7_gui, "", s:nord7_term, "", "", "")
+hi! link phpDocCustomTags phpDocTags
+hi! link phpMemberSelector Keyword
+
+call s:hi("podCmdText", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("podVerbatimLine", s:nord4_gui, "", "NONE", "", "", "")
+hi! link podFormat Keyword
+
+hi! link pythonBuiltin Type
+hi! link pythonEscape SpecialChar
+
+call s:hi("rubyConstant", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("rubySymbol", s:nord6_gui, "", s:nord6_term, "", s:bold, "")
+hi! link rubyAttribute Identifier
+hi! link rubyBlockParameterList Operator
+hi! link rubyInterpolationDelimiter Keyword
+hi! link rubyKeywordAsMethod Function
+hi! link rubyLocalVariableOrMethod Function
+hi! link rubyPseudoVariable Keyword
+hi! link rubyRegexp SpecialChar
+
+call s:hi("rustAttribute", s:nord10_gui, "", s:nord10_term, "", "", "")
+call s:hi("rustEnum", s:nord7_gui, "", s:nord7_term, "", s:bold, "")
+call s:hi("rustMacro", s:nord8_gui, "", s:nord8_term, "", s:bold, "")
+call s:hi("rustModPath", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("rustPanic", s:nord9_gui, "", s:nord9_term, "", s:bold, "")
+call s:hi("rustTrait", s:nord7_gui, "", s:nord7_term, "", s:italic, "")
+hi! link rustCommentLineDoc Comment
+hi! link rustDerive rustAttribute
+hi! link rustEnumVariant rustEnum
+hi! link rustEscape SpecialChar
+hi! link rustQuestionMark Keyword
+
+call s:hi("sassClass", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("sassId", s:nord7_gui, "", s:nord7_term, "", s:underline, "")
+hi! link sassAmpersand Keyword
+hi! link sassClassChar Delimiter
+hi! link sassControl Keyword
+hi! link sassControlLine Keyword
+hi! link sassExtend Keyword
+hi! link sassFor Keyword
+hi! link sassFunctionDecl Keyword
+hi! link sassFunctionName Function
+hi! link sassidChar sassId
+hi! link sassInclude SpecialChar
+hi! link sassMixinName Function
+hi! link sassMixing SpecialChar
+hi! link sassReturn Keyword
+
+hi! link shCmdParenRegion Delimiter
+hi! link shCmdSubRegion Delimiter
+hi! link shDerefSimple Identifier
+hi! link shDerefVar Identifier
+
+hi! link sqlKeyword Keyword
+hi! link sqlSpecial Keyword
+
+call s:hi("vimAugroup", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("vimMapRhs", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("vimNotation", s:nord7_gui, "", s:nord7_term, "", "", "")
+hi! link vimFunc Function
+hi! link vimFunction Function
+hi! link vimUserFunc Function
+
+call s:hi("xmlAttrib", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("xmlCdataStart", s:nord3_gui_bright, "", s:nord3_term, "", s:bold, "")
+call s:hi("xmlNamespace", s:nord7_gui, "", s:nord7_term, "", "", "")
+hi! link xmlAttribPunct Delimiter
+hi! link xmlCdata Comment
+hi! link xmlCdataCdata xmlCdataStart
+hi! link xmlCdataEnd xmlCdataStart
+hi! link xmlEndTag xmlTagName
+hi! link xmlProcessingDelim Keyword
+hi! link xmlTagName Keyword
+
+call s:hi("yamlBlockMappingKey", s:nord7_gui, "", s:nord7_term, "", "", "")
+hi! link yamlBool Keyword
+hi! link yamlDocumentStart Keyword
+
+"+----------------+
+"+ Plugin Support +
+"+----------------+
+"+--- UI ---+
+" ALE
+" > w0rp/ale
+call s:hi("ALEWarningSign", s:nord13_gui, "", s:nord13_term, "", "", "")
+call s:hi("ALEErrorSign" , s:nord11_gui, "", s:nord11_term, "", "", "")
+call s:hi("ALEWarning" , s:nord13_gui, "", s:nord13_term, "", "undercurl", "")
+call s:hi("ALEError" , s:nord11_gui, "", s:nord11_term, "", "undercurl", "")
+
+" Coc
+" > neoclide/coc
+call s:hi("CocWarningSign", s:nord13_gui, "", s:nord13_term, "", "", "")
+call s:hi("CocErrorSign" , s:nord11_gui, "", s:nord11_term, "", "", "")
+call s:hi("CocInfoSign" , s:nord8_gui, "", s:nord8_term, "", "", "")
+call s:hi("CocHintSign" , s:nord10_gui, "", s:nord10_term, "", "", "")
+
+" Nvim LSP
+" > neovim/nvim-lsp
+call s:hi("LSPDiagnosticsWarning", s:nord13_gui, "", s:nord13_term, "", "", "")
+call s:hi("LSPDiagnosticsError" , s:nord11_gui, "", s:nord11_term, "", "", "")
+call s:hi("LSPDiagnosticsInformation" , s:nord8_gui, "", s:nord8_term, "", "", "")
+call s:hi("LSPDiagnosticsHint" , s:nord10_gui, "", s:nord10_term, "", "", "")
+
+" GitGutter
+" > airblade/vim-gitgutter
+call s:hi("GitGutterAdd", s:nord14_gui, "", s:nord14_term, "", "", "")
+call s:hi("GitGutterChange", s:nord13_gui, "", s:nord13_term, "", "", "")
+call s:hi("GitGutterChangeDelete", s:nord11_gui, "", s:nord11_term, "", "", "")
+call s:hi("GitGutterDelete", s:nord11_gui, "", s:nord11_term, "", "", "")
+
+" Signify
+" > mhinz/vim-signify
+call s:hi("SignifySignAdd", s:nord14_gui, "", s:nord14_term, "", "", "")
+call s:hi("SignifySignChange", s:nord13_gui, "", s:nord13_term, "", "", "")
+call s:hi("SignifySignChangeDelete", s:nord11_gui, "", s:nord11_term, "", "", "")
+call s:hi("SignifySignDelete", s:nord11_gui, "", s:nord11_term, "", "", "")
+
+" fugitive.vim
+" > tpope/vim-fugitive
+call s:hi("gitcommitDiscardedFile", s:nord11_gui, "", s:nord11_term, "", "", "")
+call s:hi("gitcommitUntrackedFile", s:nord11_gui, "", s:nord11_term, "", "", "")
+call s:hi("gitcommitSelectedFile", s:nord14_gui, "", s:nord14_term, "", "", "")
+
+" davidhalter/jedi-vim
+call s:hi("jediFunction", s:nord4_gui, s:nord3_gui, "", s:nord3_term, "", "")
+call s:hi("jediFat", s:nord8_gui, s:nord3_gui, s:nord8_term, s:nord3_term, s:underline.s:bold, "")
+
+" NERDTree
+" > scrooloose/nerdtree
+call s:hi("NERDTreeExecFile", s:nord7_gui, "", s:nord7_term, "", "", "")
+hi! link NERDTreeDirSlash Keyword
+hi! link NERDTreeHelp Comment
+
+" CtrlP
+" > ctrlpvim/ctrlp.vim
+hi! link CtrlPMatch Keyword
+hi! link CtrlPBufferHid Normal
+
+" vim-clap
+" > liuchengxu/vim-clap
+call s:hi("ClapDir", s:nord4_gui, "", "", "", "", "")
+call s:hi("ClapDisplay", s:nord4_gui, s:nord1_gui, "", s:nord1_term, "", "")
+call s:hi("ClapFile", s:nord4_gui, "", "", "NONE", "", "")
+call s:hi("ClapMatches", s:nord8_gui, "", s:nord8_term, "", "", "")
+call s:hi("ClapNoMatchesFound", s:nord13_gui, "", s:nord13_term, "", "", "")
+call s:hi("ClapSelected", s:nord7_gui, "", s:nord7_term, "", s:bold, "")
+call s:hi("ClapSelectedSign", s:nord9_gui, "", s:nord9_term, "", "", "")
+
+let s:clap_matches = [
+ \ [s:nord8_gui, s:nord8_term] ,
+ \ [s:nord9_gui, s:nord9_term] ,
+ \ [s:nord10_gui, s:nord10_term] ,
+ \ ]
+for s:nord_clap_match_i in range(1,12)
+ let clap_match_color = s:clap_matches[s:nord_clap_match_i % len(s:clap_matches) - 1]
+ call s:hi("ClapMatches" . s:nord_clap_match_i, clap_match_color[0], "", clap_match_color[1], "", "", "")
+ call s:hi("ClapFuzzyMatches" . s:nord_clap_match_i, clap_match_color[0], "", clap_match_color[1], "", "", "")
+endfor
+unlet s:nord_clap_match_i
+
+hi! link ClapCurrentSelection PmenuSel
+hi! link ClapCurrentSelectionSign ClapSelectedSign
+hi! link ClapInput Pmenu
+hi! link ClapPreview Pmenu
+hi! link ClapProviderAbout ClapDisplay
+hi! link ClapProviderColon Type
+hi! link ClapProviderId Type
+
+" vim-plug
+" > junegunn/vim-plug
+call s:hi("plugDeleted", s:nord11_gui, "", "", s:nord11_term, "", "")
+
+" vim-signature
+" > kshenoy/vim-signature
+call s:hi("SignatureMarkText", s:nord8_gui, "", s:nord8_term, "", "", "")
+
+" vim-startify
+" > mhinz/vim-startify
+call s:hi("StartifyFile", s:nord6_gui, "", s:nord6_term, "", "", "")
+call s:hi("StartifyFooter", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("StartifyHeader", s:nord8_gui, "", s:nord8_term, "", "", "")
+call s:hi("StartifyNumber", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("StartifyPath", s:nord8_gui, "", s:nord8_term, "", "", "")
+hi! link StartifyBracket Delimiter
+hi! link StartifySlash Normal
+hi! link StartifySpecial Comment
+
+"+--- Languages ---+
+" Haskell
+" > neovimhaskell/haskell-vim
+call s:hi("haskellPreProc", s:nord10_gui, "", s:nord10_term, "", "", "")
+call s:hi("haskellType", s:nord7_gui, "", s:nord7_term, "", "", "")
+hi! link haskellPragma haskellPreProc
+
+" JavaScript
+" > pangloss/vim-javascript
+call s:hi("jsGlobalNodeObjects", s:nord8_gui, "", s:nord8_term, "", s:italic, "")
+hi! link jsBrackets Delimiter
+hi! link jsFuncCall Function
+hi! link jsFuncParens Delimiter
+hi! link jsThis Keyword
+hi! link jsNoise Delimiter
+hi! link jsPrototype Keyword
+hi! link jsRegexpString SpecialChar
+
+" TypeScript
+" > HerringtonDarkholme/yats.vim
+call s:hi("typescriptBOMWindowMethod", s:nord8_gui, "", s:nord8_term, "", s:italic, "")
+call s:hi("typescriptClassName", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("typescriptDecorator", s:nord12_gui, "", s:nord12_term, "", "", "")
+call s:hi("typescriptInterfaceName", s:nord7_gui, "", s:nord7_term, "", s:bold, "")
+call s:hi("typescriptRegexpString", s:nord13_gui, "", s:nord13_term, "", "", "")
+" TypeScript JSX
+ call s:hi("tsxAttrib", s:nord7_gui, "", s:nord7_term, "", "", "")
+hi! link typescriptOperator Operator
+hi! link typescriptBinaryOp Operator
+hi! link typescriptAssign Operator
+hi! link typescriptMember Identifier
+hi! link typescriptDOMStorageMethod Identifier
+hi! link typescriptArrowFuncArg Identifier
+hi! link typescriptGlobal typescriptClassName
+hi! link typescriptBOMWindowProp Function
+hi! link typescriptArrowFuncDef Function
+hi! link typescriptAliasDeclaration Function
+hi! link typescriptPredefinedType Type
+hi! link typescriptTypeReference typescriptClassName
+hi! link typescriptTypeAnnotation Structure
+hi! link typescriptDocNamedParamType SpecialComment
+hi! link typescriptDocNotation Keyword
+hi! link typescriptDocTags Keyword
+hi! link typescriptImport Keyword
+hi! link typescriptExport Keyword
+hi! link typescriptTry Keyword
+hi! link typescriptVariable Keyword
+hi! link typescriptBraces Normal
+hi! link typescriptObjectLabel Normal
+hi! link typescriptCall Normal
+hi! link typescriptClassHeritage typescriptClassName
+hi! link typescriptFuncTypeArrow Structure
+hi! link typescriptMemberOptionality Structure
+hi! link typescriptNodeGlobal typescriptGlobal
+hi! link typescriptTypeBrackets Structure
+hi! link tsxEqual Operator
+hi! link tsxIntrinsicTagName htmlTag
+hi! link tsxTagName tsxIntrinsicTagName
+
+" Markdown
+" > plasticboy/vim-markdown
+call s:hi("mkdCode", s:nord7_gui, "", s:nord7_term, "", "", "")
+call s:hi("mkdFootnote", s:nord8_gui, "", s:nord8_term, "", "", "")
+call s:hi("mkdRule", s:nord10_gui, "", s:nord10_term, "", "", "")
+call s:hi("mkdLineBreak", s:nord9_gui, "", s:nord9_term, "", "", "")
+hi! link mkdBold Bold
+hi! link mkdItalic Italic
+hi! link mkdString Keyword
+hi! link mkdCodeStart mkdCode
+hi! link mkdCodeEnd mkdCode
+hi! link mkdBlockquote Comment
+hi! link mkdListItem Keyword
+hi! link mkdListItemLine Normal
+hi! link mkdFootnotes mkdFootnote
+hi! link mkdLink markdownLinkText
+hi! link mkdURL markdownUrl
+hi! link mkdInlineURL mkdURL
+hi! link mkdID Identifier
+hi! link mkdLinkDef mkdLink
+hi! link mkdLinkDefTarget mkdURL
+hi! link mkdLinkTitle mkdInlineURL
+hi! link mkdDelimiter Keyword
+
+" Vimwiki
+" > vimwiki/vimwiki
+if !exists("g:vimwiki_hl_headers") || g:vimwiki_hl_headers == 0
+ for s:i in range(1,6)
+ call s:hi("VimwikiHeader".s:i, s:nord8_gui, "", s:nord8_term, "", s:bold, "")
+ endfor
+else
+ let s:vimwiki_hcolor_guifg = [s:nord7_gui, s:nord8_gui, s:nord9_gui, s:nord10_gui, s:nord14_gui, s:nord15_gui]
+ let s:vimwiki_hcolor_ctermfg = [s:nord7_term, s:nord8_term, s:nord9_term, s:nord10_term, s:nord14_term, s:nord15_term]
+ for s:i in range(1,6)
+ call s:hi("VimwikiHeader".s:i, s:vimwiki_hcolor_guifg[s:i-1] , "", s:vimwiki_hcolor_ctermfg[s:i-1], "", s:bold, "")
+ endfor
+endif
+
+call s:hi("VimwikiLink", s:nord8_gui, "", s:nord8_term, "", s:underline, "")
+hi! link VimwikiHeaderChar markdownHeadingDelimiter
+hi! link VimwikiHR Keyword
+hi! link VimwikiList markdownListMarker
+
+" YAML
+" > stephpy/vim-yaml
+call s:hi("yamlKey", s:nord7_gui, "", s:nord7_term, "", "", "")
diff --git a/files/vim/ftplugin/antlr/antlr3.vim b/stow/vim/dot-vim/ftplugin/antlr/antlr3.vim
index 10e24e5..10e24e5 100644
--- a/files/vim/ftplugin/antlr/antlr3.vim
+++ b/stow/vim/dot-vim/ftplugin/antlr/antlr3.vim
diff --git a/files/vim/ftplugin/antlr/antlr4.vim b/stow/vim/dot-vim/ftplugin/antlr/antlr4.vim
index 456076c..456076c 100644
--- a/files/vim/ftplugin/antlr/antlr4.vim
+++ b/stow/vim/dot-vim/ftplugin/antlr/antlr4.vim
diff --git a/files/vim/ftplugin/asciidoc/asciidoc.vim b/stow/vim/dot-vim/ftplugin/asciidoc/asciidoc.vim
index b94b875..b94b875 100644
--- a/files/vim/ftplugin/asciidoc/asciidoc.vim
+++ b/stow/vim/dot-vim/ftplugin/asciidoc/asciidoc.vim
diff --git a/files/vim/ftplugin/cpp/cpp.vim b/stow/vim/dot-vim/ftplugin/cpp/cpp.vim
index e2072c1..e2072c1 100644
--- a/files/vim/ftplugin/cpp/cpp.vim
+++ b/stow/vim/dot-vim/ftplugin/cpp/cpp.vim
diff --git a/files/vim/ftplugin/cpp/snips/template.txt b/stow/vim/dot-vim/ftplugin/cpp/snips/template.txt
index 90ef863..90ef863 100644
--- a/files/vim/ftplugin/cpp/snips/template.txt
+++ b/stow/vim/dot-vim/ftplugin/cpp/snips/template.txt
diff --git a/files/vim/ftplugin/css/css.vim b/stow/vim/dot-vim/ftplugin/css/css.vim
index 695243c..695243c 100644
--- a/files/vim/ftplugin/css/css.vim
+++ b/stow/vim/dot-vim/ftplugin/css/css.vim
diff --git a/files/vim/ftplugin/html/html.vim b/stow/vim/dot-vim/ftplugin/html/html.vim
index 41890b4..41890b4 100644
--- a/files/vim/ftplugin/html/html.vim
+++ b/stow/vim/dot-vim/ftplugin/html/html.vim
diff --git a/files/vim/ftplugin/html/snips/template.txt b/stow/vim/dot-vim/ftplugin/html/snips/template.txt
index 38d9f26..38d9f26 100644
--- a/files/vim/ftplugin/html/snips/template.txt
+++ b/stow/vim/dot-vim/ftplugin/html/snips/template.txt
diff --git a/files/vim/ftplugin/java/java.vim b/stow/vim/dot-vim/ftplugin/java/java.vim
index 2595b32..2595b32 100644
--- a/files/vim/ftplugin/java/java.vim
+++ b/stow/vim/dot-vim/ftplugin/java/java.vim
diff --git a/files/vim/ftplugin/javascript/javascript.vim b/stow/vim/dot-vim/ftplugin/javascript/javascript.vim
index 4935410..4935410 100644
--- a/files/vim/ftplugin/javascript/javascript.vim
+++ b/stow/vim/dot-vim/ftplugin/javascript/javascript.vim
diff --git a/files/vim/ftplugin/plantuml/plantuml.vim b/stow/vim/dot-vim/ftplugin/plantuml/plantuml.vim
index e493e5b..5bab6a2 100644
--- a/files/vim/ftplugin/plantuml/plantuml.vim
+++ b/stow/vim/dot-vim/ftplugin/plantuml/plantuml.vim
@@ -15,7 +15,6 @@ let mapleader = " "
nnoremap <buffer> <leader>temp :-1r ~/.vim/ftplugin/plantuml/snips/template.txt<CR>Gdd?<+++><CR>cf>
nnoremap <buffer> <leader>C :sp ~/.vim/ftplugin/plantuml/plantuml.vim<CR>
-nnoremap <buffer> <leader>e :Errors<CR>
nnoremap <buffer> <leader>c :w<CR>:!compilePlantUML.sh "%"<CR><CR>
nnoremap <buffer> <leader>o :w<CR>:!compilePlantUML.sh -o "%"<CR><CR>
diff --git a/files/vim/ftplugin/plantuml/snips/template.txt b/stow/vim/dot-vim/ftplugin/plantuml/snips/template.txt
index f649539..f649539 100644
--- a/files/vim/ftplugin/plantuml/snips/template.txt
+++ b/stow/vim/dot-vim/ftplugin/plantuml/snips/template.txt
diff --git a/stow/vim/dot-vim/ftplugin/python/python.vim b/stow/vim/dot-vim/ftplugin/python/python.vim
new file mode 100644
index 0000000..358f902
--- /dev/null
+++ b/stow/vim/dot-vim/ftplugin/python/python.vim
@@ -0,0 +1,34 @@
+" ~/.vim/ftplugin/python/python.vim
+" Python-specific vim configuration
+
+" Tabs are expanded to 4 spaces
+setlocal tabstop=4 shiftwidth=4 expandtab
+" Show existing tabs (they can be deleted with :retab)
+setlocal list
+
+" Column marker: 90 characters
+call matchadd('ColorColumn', '\%91v', 100)
+setlocal textwidth=90
+
+let b:ale_python_pylint_options = '-d invalid-name'
+
+" Mappings
+
+let mapleader = " "
+
+nnoremap <buffer> <leader>C :sp ~/.vim/ftplugin/python/python.vim<CR>
+nnoremap <buffer> <leader>x :! ./%
+nnoremap <buffer> <leader>X :! ./%<CR>
+
+nnoremap <buffer> gd :YcmCompleter GoTo<CR>
+nnoremap <buffer> gr :YcmCompleter GoToReferences<CR>
+nnoremap <buffer> gR :YcmCompleter RefactorRename<Space>
+nnoremap <buffer> <leader>gd :YcmCompleter GetDoc<CR>
+nnoremap <buffer> <leader>gT :YcmCompleter GetType<CR>
+
+nnoremap <buffer> <leader>db :!tmux split-window -l '40\%' 'python -m̀ pdb %'
+
+let mapleader = ","
+
+inoremap <buffer> <leader>temp <Esc>:-1r ~/.vim/ftplugin/python/snips/template<CR>Gdd?<+++><CR>cf>
+inoremap <buffer> <leader>class <Esc>:-1r ~/.vim/ftplugin/python/snips/class<CR>Gdd?<+++><CR>cf>
diff --git a/stow/vim/dot-vim/ftplugin/python/snips/class b/stow/vim/dot-vim/ftplugin/python/snips/class
new file mode 100644
index 0000000..0febf0c
--- /dev/null
+++ b/stow/vim/dot-vim/ftplugin/python/snips/class
@@ -0,0 +1,3 @@
+class <+++>:
+ def __init__(<++>):
+ self.<++> = <++>
diff --git a/stow/vim/dot-vim/ftplugin/python/snips/template b/stow/vim/dot-vim/ftplugin/python/snips/template
new file mode 100644
index 0000000..d10fa4a
--- /dev/null
+++ b/stow/vim/dot-vim/ftplugin/python/snips/template
@@ -0,0 +1,5 @@
+#!/usr/bin/python
+
+"""<+++>"""
+
+<++>
diff --git a/stow/vim/dot-vim/ftplugin/sent/sent.vim b/stow/vim/dot-vim/ftplugin/sent/sent.vim
new file mode 100644
index 0000000..c9cc974
--- /dev/null
+++ b/stow/vim/dot-vim/ftplugin/sent/sent.vim
@@ -0,0 +1,19 @@
+" ~/.vim/ftplugin/sent/sent.vim
+" Vim configuration for writing sent presentations
+
+" Tabs hate: tabs are expanded to 4 spaces
+setlocal tabstop=4 shiftwidth=4 expandtab
+" Show existing tabs (they can be deleted with :retab)
+setlocal list
+
+call matchadd('ColorColumn', '\%41v', 100)
+setlocal textwidth=40
+
+setlocal spell
+
+let mapleader = " "
+
+nnoremap <buffer> <leader>C :sp ~/.vim/ftplugin/sent/sent.vim<CR>
+
+nnoremap <buffer> <leader>c :w<CR>:!killall sent <CR>:!sent % & <CR><CR>
+nnoremap <buffer> <leader>o :w<CR>:!sent % & <CR><CR>
diff --git a/files/vim/ftplugin/sh/sh.vim b/stow/vim/dot-vim/ftplugin/sh/sh.vim
index 3eb4da5..3eb4da5 100644
--- a/files/vim/ftplugin/sh/sh.vim
+++ b/stow/vim/dot-vim/ftplugin/sh/sh.vim
diff --git a/files/vim/ftplugin/sh/snippets/if.txt b/stow/vim/dot-vim/ftplugin/sh/snippets/if.txt
index 9f6bb88..9f6bb88 100644
--- a/files/vim/ftplugin/sh/snippets/if.txt
+++ b/stow/vim/dot-vim/ftplugin/sh/snippets/if.txt
diff --git a/files/vim/ftplugin/tex/snips/template.txt b/stow/vim/dot-vim/ftplugin/tex/snips/template.txt
index 1f4bd46..1f4bd46 100644
--- a/files/vim/ftplugin/tex/snips/template.txt
+++ b/stow/vim/dot-vim/ftplugin/tex/snips/template.txt
diff --git a/files/vim/ftplugin/tex/tex.vim b/stow/vim/dot-vim/ftplugin/tex/tex.vim
index e440561..87f3c88 100644
--- a/files/vim/ftplugin/tex/tex.vim
+++ b/stow/vim/dot-vim/ftplugin/tex/tex.vim
@@ -6,16 +6,23 @@
call matchadd('ColorColumn', '\%91v', 100)
setlocal textwidth=80
+let mapleader = " "
+
nnoremap <buffer> <leader>C :sp ~/.vim/ftplugin/tex/tex.vim<CR>
nnoremap <buffer> <leader>e :Errors<CR>
" proselint not enabled by default (it's slow)
"nnoremap <buffer> <leader>sp :SyntasticCheck proselint<CR>
-nnoremap <buffer> <leader>c :w<Enter>:! toPDF.sh %<CR>
-nnoremap <buffer> <leader>o :w<Enter>:! toPDF.sh -o %<CR><CR>
+nnoremap <buffer> <leader>c :w<Enter>:!toPDF.sh %<CR><CR>
+nnoremap <buffer> <leader>o :w<Enter>:!toPDF.sh -o %<CR><CR>
+nnoremap <buffer> <leader>O :w<Enter>:!toPDF.sh -o %<CR>
+nnoremap <buffer> <leader>m :w<Enter>:!make<CR><CR>
+nnoremap <buffer> <leader>M :w<Enter>:!make<CR>
nnoremap <buffer> <leader>temp :-1r ~/.vim/ftplugin/tex/snips/template.txt<CR>/<+++><CR>cf>
+let mapleader = ","
+
inoremap <buffer> <leader>ldocclass \documentclass{}<Esc>o<++><Esc>k$i
inoremap <buffer> <leader>lpckg \usepackage{}<Esc>o<++><Esc>k$i
inoremap <buffer> <leader>lbdoc \begin{document}<Esc>o<Enter><Enter><Enter>\end{document}<Esc>kkI
@@ -34,5 +41,5 @@ inoremap <buffer> <leader>lref ~\ref{}<++><Esc>F}i
inoremap <buffer> <leader>lenumerate \begin{enumerate}<Esc>o<Enter><Enter><Enter>\end{enumerate}<Esc>kkI\item<Enter><Esc>I<Tab><++><Esc>k<<i<Tab><Esc>$a<Space>
inoremap <buffer> <leader>litemize \begin{itemize}<Esc>o<Enter><Enter><Enter>\end{itemize}<Esc>kkI\item<Enter><Esc>I<Tab><++><Esc>k<<i<Tab><Esc>$a<Space>
inoremap <buffer> <leader>litem \item<Enter><++><Esc>k$a<Space>
-inoremap <buffer> <leader>limage \begin{figure}[H]<Enter>\begin{center}<Enter>\includegraphics[width=\textwidth]{}<Enter>\caption{<++>}<Enter>\end{center}<Enter>\end{figure}<Enter><++><Esc>4k$i
-inoremap <buffer> <leader>ltable \begin{table}[H]<Enter>\makebox[\linewidth]{\centering<Enter>\centering<Enter>\begin{tabular}{c<Space>cxxx}<Enter>\toprule<Enter><++><Space>&<Space><++><Space>\\<Enter>\midrule<Enter><++><Space>&<Space><++><Space>\\<Enter>\bottomrule<Enter>\end{tabular}<Enter>}<Enter>\end{table}<Enter><++><Esc>?xxx<Enter>cw
+inoremap <buffer> <leader>limage \begin{figure}[h]<Enter>\begin{center}<Enter>\includegraphics[width=\textwidth]{}<Enter>\caption{<++>}<Enter>\end{center}<Enter>\end{figure}<Enter><++><Esc>4k$i
+inoremap <buffer> <leader>ltable \begin{table}[h]<Enter>\makebox[\linewidth]{\centering<Enter>\centering<Enter>\begin{tabular}{c<Space>cxxx}<Enter>\toprule<Enter><++><Space>&<Space><++><Space>\\<Enter>\midrule<Enter><++><Space>&<Space><++><Space>\\<Enter>\bottomrule<Enter>\end{tabular}<Enter>}<Enter>\end{table}<Enter><++><Esc>?xxx<Enter>cw
diff --git a/files/vim/ftplugin/text/text.vim b/stow/vim/dot-vim/ftplugin/text/text.vim
index 406c1e1..c370a74 100644
--- a/files/vim/ftplugin/text/text.vim
+++ b/stow/vim/dot-vim/ftplugin/text/text.vim
@@ -8,6 +8,8 @@ setlocal tabstop=2 shiftwidth=2 expandtab
" Show existing tabs (they can be deleted with :retab)
setlocal list
+let mapleader = " "
+
call matchadd('ColorColumn', '\%101v', 100)
setlocal textwidth=100
diff --git a/files/vim/ftplugin/typescript/typescript.vim b/stow/vim/dot-vim/ftplugin/typescript/typescript.vim
index 7c91032..7c91032 100644
--- a/files/vim/ftplugin/typescript/typescript.vim
+++ b/stow/vim/dot-vim/ftplugin/typescript/typescript.vim
diff --git a/files/vim/ftplugin/vimwiki/snips/plantUml.txt b/stow/vim/dot-vim/ftplugin/vimwiki/snips/plantUml.txt
index 4a016eb..4a016eb 100644
--- a/files/vim/ftplugin/vimwiki/snips/plantUml.txt
+++ b/stow/vim/dot-vim/ftplugin/vimwiki/snips/plantUml.txt
diff --git a/files/vim/ftplugin/vimwiki/snips/template.txt b/stow/vim/dot-vim/ftplugin/vimwiki/snips/template.txt
index f6b16a8..f6b16a8 100644
--- a/files/vim/ftplugin/vimwiki/snips/template.txt
+++ b/stow/vim/dot-vim/ftplugin/vimwiki/snips/template.txt
diff --git a/files/vim/ftplugin/vimwiki/vimwiki.vim b/stow/vim/dot-vim/ftplugin/vimwiki/vimwiki.vim
index e8f7a18..c361600 100644
--- a/files/vim/ftplugin/vimwiki/vimwiki.vim
+++ b/stow/vim/dot-vim/ftplugin/vimwiki/vimwiki.vim
@@ -20,14 +20,20 @@ highlight Title ctermfg=blue
highlight TitleHash ctermfg=cyan
match TitleHash /^##*/
+setlocal conceallevel=1
+
+let mapleader = " "
+
nnoremap <buffer> <leader>C :sp ~/.vim/ftplugin/vimwiki/vimwiki.vim<CR>
nnoremap <buffer> <leader>e :Errors<CR>
nnoremap <buffer> <leader>tt :r ~/.vim/ftplugin/vimwiki/snips/template.txt<CR>
nnoremap <buffer> <leader>tp :r ~/.vim/ftplugin/vimwiki/snips/plantUml.txt<CR>/<+++><CR>cf>
-nnoremap <buffer> <leader>c :w<CR>:!toPDF.sh "%"<CR><CR>
+nnoremap <buffer> <leader>c :w<CR>:!toPDF.sh "%"<CR>
nnoremap <buffer> <leader>o :w<CR>:!toPDF.sh -o "%"<CR><CR>
+let mapleader = ","
+
inoremap <buffer> <leader>h <Esc><<A
inoremap <buffer> <leader>l <Esc>>>A
diff --git a/files/vim/plugin/SWTC.vim b/stow/vim/dot-vim/plugin/SWTC.vim
index 9459e92..9459e92 100644
--- a/files/vim/plugin/SWTC.vim
+++ b/stow/vim/dot-vim/plugin/SWTC.vim
diff --git a/files/vim/plugin/dragvisuals.vim b/stow/vim/dot-vim/plugin/dragvisuals.vim
index 12c4f5d..12c4f5d 100644
--- a/files/vim/plugin/dragvisuals.vim
+++ b/stow/vim/dot-vim/plugin/dragvisuals.vim
diff --git a/files/vim/plugin/rng.vim b/stow/vim/dot-vim/plugin/rng.vim
index 51c9991..51c9991 100644
--- a/files/vim/plugin/rng.vim
+++ b/stow/vim/dot-vim/plugin/rng.vim
diff --git a/files/vim/.vimrc b/stow/vim/dot-vimrc
index 514ec6a..9520648 100644
--- a/files/vim/.vimrc
+++ b/stow/vim/dot-vimrc
@@ -12,10 +12,12 @@ Plug 'PotatoesMaster/i3-vim-syntax'
Plug 'junegunn/goyo.vim'
Plug 'junegunn/fzf.vim'
Plug 'junegunn/limelight.vim'
+Plug 'arcticicestudio/nord-vim'
Plug 'tpope/vim-surround'
Plug 'tpope/vim-repeat'
Plug 'vimwiki/vimwiki'
Plug 'vim-airline/vim-airline'
+Plug 'psliwka/vim-smoothie'
"Plug 'vim-syntastic/syntastic'
Plug 'dense-analysis/ale'
"Plug 'Shougo/deoplete.nvim', {'do': ':UpdateRemotePlugins' }
@@ -24,7 +26,7 @@ Plug 'dense-analysis/ale'
Plug 'Valloric/YouCompleteMe'
"Plug 'OmniSharp/omnisharp-vim'
Plug 'dylanaraps/wal.vim'
-Plug 'ThePrimeagen/vim-be-good'
+"Plug 'ThePrimeagen/vim-be-good'
call plug#end()
" Basic settings
@@ -46,10 +48,11 @@ set nohlsearch
" Filetypes
filetype indent plugin on
-au BufRead,BufNewFile *.pu set filetype=plantuml
-au BufRead,BufNewFile *.puml set filetype=plantuml
+au BufRead,BufNewFile *.pu,*.puml,*.pumlc set filetype=plantuml
au BufRead,BufNewFile *.g set filetype=antlr3
au BufRead,BufNewFile *.g4 set filetype=antlr4
+au BufRead,BufNewFile *.sent set filetype=sent
+let g:tex_flavor = "latex"
" hlnext: Highlighting searches
nnoremap <silent> n n:call HLNext(0.1)<CR>
@@ -150,7 +153,7 @@ let g:airline_symbols.maxlinenr = '↩'
" filetype plugin on
let g:vimwiki_ext2syntax = {'.md': 'markdown', '.markdown': 'markdown', '.mdown': 'markdown'}
let g:vimwiki_list = [{'path': '~/docs/vimwiki/', 'syntax': 'markdown', 'ext': '.md'},
- \ {'path': '~/docs/notes/', 'syntax': 'markdown', 'ext': '.md'},
+ \ {'path': '~/docs/notas/', 'syntax': 'markdown', 'ext': '.md'},
\ {'path': '~/games/dungeonesYDragones/tarkba/vimwiki/', 'syntax': 'markdown', 'ext': '.md'},
\ {'path': '~/games/dungeonesYDragones/creaciones/vimwiki/', 'syntax': 'markdown', 'ext': '.md'}]
@@ -186,26 +189,29 @@ nnoremap <C-n> :tabnew<CR>
"nnoremap <S-Tab> gT
nnoremap <leader>vrc :80vsp ~/.vimrc<CR>
-nnoremap <leader>S :!tmux split-window -p 40<CR><CR>
+nnoremap <leader>S :!tmux split-window -l '40\%'<CR><CR>
nnoremap <leader>/ /grtfjx<CR>
-"
-" Color
+
+""" Color
+colorscheme nord
+
+" Syntax
syntax enable
syntax on
-" Line size marking
-highlight ColorColumn ctermbg=darkblue ctermfg=black
-"Redjail Bomb (thx Damian Conway!)
-"highlight ColorColumn ctermbg=red ctermfg=blue
-"exec 'set colorcolumn='.join(range(2,80,3), ',')
+" Line number color
+highlight LineNr ctermfg=12
+highlight LineNr cterm=NONE
+highlight CursorLineNr ctermfg=5
+highlight CursorLineNr cterm=underline
" Completion color
highlight Pmenu ctermbg=blue ctermfg=black
highlight PmenuSel ctermbg=darkblue ctermfg=white
" Panel border color
-highlight VertSplit ctermbg=black ctermfg=darkblue
+"highlight VertSplit ctermbg=black ctermfg=darkblue
" Symbol pairs match color
highlight MatchParen ctermbg=cyan ctermfg=black
@@ -217,6 +223,12 @@ highlight SpellBad ctermbg=red ctermfg=black
" Comments colors
highlight Comment ctermfg=green
+" Line size marking
+highlight ColorColumn ctermbg=darkblue ctermfg=black
+"Redjail Bomb (thx Damian Conway!)
+"highlight ColorColumn ctermbg=red ctermfg=blue
+"exec 'set colorcolumn='.join(range(2,80,3), ',')
+
"Clean trailing whitespaces on save
autocmd BufWritePre * %s/\s\+$//e
@@ -242,6 +254,19 @@ vnoremap <leader>y "+y
nnoremap <leader>d "+dd
vnoremap <leader>d "+d
+" For Goyo
+function! s:goyo_enter()
+ silent !tmux set status off
+endfunction
+
+function! s:goyo_leave()
+ silent !tmux set status on
+endfunction
+
+autocmd! User GoyoEnter nested call <SID>goyo_enter()
+autocmd! User GoyoLeave nested call <SID>goyo_leave()
+
+let g:goyo_linenr = 1
nnoremap <leader>G :Goyo \| set linebreak<CR>:e<CR>
" For LimeLight
diff --git a/stow/vis/dot-config/vis/colors/basic_colors b/stow/vis/dot-config/vis/colors/basic_colors
new file mode 100644
index 0000000..4be5fa4
--- /dev/null
+++ b/stow/vis/dot-config/vis/colors/basic_colors
@@ -0,0 +1,15 @@
+4
+12
+6
+14
+2
+10
+11
+3
+5
+1
+13
+9
+7
+15
+0
diff --git a/stow/vis/dot-config/vis/colors/rainbow b/stow/vis/dot-config/vis/colors/rainbow
new file mode 100644
index 0000000..13714c5
--- /dev/null
+++ b/stow/vis/dot-config/vis/colors/rainbow
@@ -0,0 +1,30 @@
+#1F80F3
+#08ADD8
+#06B3D4
+#01D7AF
+#01DBA9
+#0DF47E
+#26FE53
+#2CFF4B
+#4FF829
+#58F522
+#83DE0B
+#8FD506
+#ADBB01
+#BEAA01
+#D78C07
+#E27C0D
+#F65525
+#F94C2B
+#FE2A4D
+#FE2456
+#F20B81
+#D901AC
+#D501B2
+#B007D6
+#AA09DA
+#7C21F4
+#6D2DF9
+#5541FE
+#3E58FE
+#2A70F9
diff --git a/stow/vis/dot-config/vis/config b/stow/vis/dot-config/vis/config
new file mode 100644
index 0000000..a68497a
--- /dev/null
+++ b/stow/vis/dot-config/vis/config
@@ -0,0 +1,91 @@
+##Refresh rate of the visualizers. A really high refresh rate may cause screen tearing. Default is 20.
+#visualizer.fps=20
+
+##Sets the audio sources to use. Currently available ones are "mpd" and "alsa"Sets the audio sources to use.
+##Currently available ones are "mpd", "pulse" and "alsa". Defaults to "mpd".
+#audio.sources=pulse
+
+##vis tries to find the correct pulseaudio sink, however this will not work on all systems.
+##If pulse audio is not working with vis try switching the audio source. A list can be found by running the
+##command pacmd list-sinks | grep -e 'name:' -e 'index'
+#audio.pulse.source=0
+
+##Defaults to "/tmp/mpd.fifo"
+#mpd.fifo.path=/tmp/mpd.fifo
+
+##If set to false the visualizers will use mono mode instead of stereo. Some visualizers will
+##behave differently when mono is enabled. For example, spectrum show two sets of bars.
+#audio.stereo.enabled=false
+
+##Specifies how often the visualizer will change in seconds. 0 means do not rotate. Default is 0.
+#visualizer.rotation.secs=10
+
+##Configures the samples rate and the cutoff frequencies.
+#audio.sampling.frequency=44100
+#audio.low.cutoff.frequency=30
+#audio.high.cutoff.frequency=22050
+
+##Applies scaling factor to both lorenz and ellipse visualizers. This is useful when the system audio is set
+#to a low volume.
+#visualizer.scaling.multiplier=1.0
+
+##Configures the visualizers and the order they are in. Available visualizers are spectrum,lorenz,ellipse.
+##Defaults to spectrum,ellipse,lorenz
+#visualizers=spectrum,ellipse,lorenz
+
+
+##Configures what character the spectrum visualizer will use. Specifying a space (e.g " ") means the
+##background will be colored instead of the character. Defaults to " ".
+#visualizer.spectrum.character=#
+
+##Spectrum bar width. Defaults to 2.
+#visualizer.spectrum.bar.width=2
+
+##The amount of space between each bar in the spectrum visualizer. Defaults to 1. It's possible to set this to
+##zero to have no space between bars
+#visualizer.spectrum.bar.spacing=1
+
+##Available smoothing options are monstercat, sgs, none.
+#visualizer.spectrum.smoothing.mode=sgs
+
+##This configures the falloff effect on the spectrum visualizer. Available falloff options are fill,top,none.
+##Defaults to "fill"
+#visualizer.spectrum.falloff.mode=fill
+
+##Configures how fast the falloff character falls. This is an exponential falloff so values usually look
+##best 0.9+ and small changes in this value can have a large effect. Defaults to 0.95
+#visualizer.spectrum.falloff.weight=0.95
+
+##Margins in percent of total screen for spectrum visualizer. All margins default to 0
+#visualizer.spectrum.top.margin=0.30
+#visualizer.spectrum.bottom.margin=0.10
+#visualizer.spectrum.right.margin=0.10
+#visualizer.spectrum.left.margin=0.10
+
+##Reverses the direction of the spectrum so that high freqs are first and low freqs last. Defaults to false.
+#visualizer.spectrum.reversed=false
+
+##This configures the sgs smoothing effect on the spectrum visualizer. More points spreads out the smoothing
+##effect and increasing passes runs the smoother multiple times on reach run. Defaults are points=3 and passes=1
+#visualizer.sgs.smoothing.points=3
+#visualizer.sgs.smoothing.passes=1
+
+
+##Configures what character the ellipse visualizer will use. Specifying a space (e.g " ") means the
+##background will be colored instead of the character. Defaults to "█".
+#visualizer.ellipse.character=#
+
+##The radius of each color ring in the ellipse visualizer. Defaults to 2.
+#visualizer.ellipse.radius=2
+
+
+##Configures what character the lorenz visualizer will use. Specifying a space (e.g " ") means the
+##background will be colored instead of the character. Defaults to "█".
+#visualizer.lorenz.character=#
+
+
+##Turns off overriding the user's terminal colors. This is true by default.
+#colors.override.terminal=false
+
+##Specifies the color scheme. The color scheme must be in ~/.config/vis/colors/ directory. The default scheme is "colors".
+#colors.scheme=rainbow
diff --git a/files/zathura/zathurarc b/stow/zathura/dot-config/zathura/zathurarc
index b98c298..78e873b 100644
--- a/files/zathura/zathurarc
+++ b/stow/zathura/dot-config/zathura/zathurarc
@@ -8,6 +8,11 @@ set guioptions shv
# Outside the document
set default-bg "#000000"
+# Status bar
+set statusbar-bg "#1d2021"
+set statusbar-fg "#fbf1c7"
+set font "mononoki 10"
+
# Document light and dark colors
set recolor false # recolor at start
set recolor-keephue false
@@ -49,5 +54,5 @@ map I exec '/home/taamas/scripts/pdfs/pdfSxiv.sh "%"'
#map y feedkeys "<Mod4>d"
#map P change_mode presentation
-map x exec 'scp "%" taamas@93.156.110.0:~/pdfs'
-map X exec 'scp "%" taamas@93.156.110.0:/srv/http/files'
+#map x exec 'scp "%" taamas@93.156.110.0:~/pdfs'
+#map X exec 'scp "%" taamas@93.156.110.0:/srv/http/files'