diff options
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' |