diff options
| author | Alec Goncharow <Alec.Goncharow@gmail.com> | 2020-08-10 17:43:23 -0400 |
|---|---|---|
| committer | Alec Goncharow <Alec.Goncharow@gmail.com> | 2020-08-10 17:43:23 -0400 |
| commit | cfa23f93ef3432753e9e85cfeb3fd67cad5836c5 (patch) | |
| tree | f0124b37e9cd2f7eed2387cddb9889050d93db75 /.config | |
| parent | 2825fe0b91dc8f8abb318c83b85f893c35852e69 (diff) | |
2020
Diffstat (limited to '.config')
24 files changed, 3938 insertions, 94 deletions
diff --git a/.config/i3/config b/.config/i3/config index 6c11beb..036f1b8 100644 --- a/.config/i3/config +++ b/.config/i3/config @@ -13,7 +13,7 @@ set $mod Mod1 # Font for window titles. Will also be used by the bar unless a different font # is used in the bar {} block below. -font pango:DeJa Vu Sans Mono 8 +font pango:DeJa Vu Sans Mono 10 # This font is widely installed, provides lots of unicode glyphs, right-to-left # text rendering and scalability on retina/hidpi displays (thanks to pango). @@ -49,10 +49,10 @@ bindsym $mod+k focus up bindsym $mod+l focus right # alternatively, you can use the cursor keys: -bindsym $mod+Left focus left -bindsym $mod+Down focus down -bindsym $mod+Up focus up -bindsym $mod+Right focus right +#bindsym $mod+Left focus left +#bindsym $mod+Down focus down +#bindsym $mod+Up focus up +#bindsym $mod+Right focus right # move focused window bindsym $mod+Shift+h move left @@ -61,10 +61,10 @@ bindsym $mod+Shift+k move up bindsym $mod+Shift+l move right # alternatively, you can use the cursor keys: -bindsym $mod+Shift+Left move left -bindsym $mod+Shift+Down move down -bindsym $mod+Shift+Up move up -bindsym $mod+Shift+Right move right +#bindsym $mod+Shift+Left move left +#bindsym $mod+Shift+Down move down +#bindsym $mod+Shift+Up move up +#bindsym $mod+Shift+Right move right # split in horizontal orientation #bindsym $mod+Shift+v split h @@ -166,11 +166,17 @@ mode "resize" { bindsym $mod+r mode "resize" +mode "dota" { + bindsym $mod+o mode "default" +} + +bindsym $mod+o mode "dota" + # colors # class border bground text indicator child_border -client.focused #000000 #F1EBEB #FFFFFF #F1EBEB #76715e -client.focused_inactive #333333 #5F676A #FFFFFF #484E50 #48483e -client.unfocused #333333 #222222 #888888 #292D2E #272822 +client.focused #000000 #125906 #FFFFFF #125906 #06770E +client.focused_inactive #333333 #5F676A #FFFFFF #484E50 #5F676A +client.unfocused #333333 #222222 #888888 #292D2E #222222 client.urgent #2F343A #41DD18 #FFFFFF #39F923 #25FF03 client.placeholder #000000 #0C0C0C #FFFFFF #000000 #0C0C0C @@ -179,9 +185,9 @@ client.background #FFFFFF # Start i3bar to display a workspace bar (plus the system information i3status # finds out, if available) bar { - status_command i3blocks #i3status # | python ~/.config/i3status/wrapper.py + status_command i3blocks #i3status # | python ~/.config/i3status/wrapper.py position top - font pango:icons 9 + font pango:DejaVu Sans Mono 10 colors { background #292929 statusline #FFFFFF @@ -191,26 +197,26 @@ bar { active_workspace #333333 #222222 #FFFFFF inactive_workspace #666666 #292929 #888888 urgent_workspace #2F343A #00FFFF #292929 - binding_mode #00FFFF #292929 #FFFFFF + binding_mode #00FFFF #292929 #FFFFFF } } # alt tabbing for workspaces -bindsym Mod1+Tab workspace next_on_output -bindsym Mod1+Shift+Tab focus output left +bindsym Mod1+Tab workspace next +bindsym Mod1+Shift+Tab workspace prev # win + l opens i3lock -bindsym Mod4+l exec --no-startup-id "i3lock" +bindsym XF86PowerOff exec --no-startup-id "i3lock -i /home/alec/Downloads/notabug2.png" # fix mouse pointer speed -bindsym ctrl+Mod1+Tab exec --no-startup-id "xinput set-prop 'pointer:Logitech MX Master 2S' 'libinput Accel Speed' -0.75" +bindsym ctrl+Mod1+Tab exec --no-startup-id "xinput set-prop 'pointer:Logitech MX Master 2S' 'libinput Accel Speed' '-0.75' & /home/alec/.screenlayout/yes.sh & /home/alec/.nitrobg" # jump to urgent window bindsym $mod+x [urgent=latest] focus # brightness controls -bindsym XF86MonBrightnessDown exec --no-startup-id "/home/alec/.config/i3/dec_brightness.py && pkill -SIGRTMIN+12 i3blocks && pause 1" -bindsym XF86MonBrightnessUp exec --no-startup-id "xbacklight -inc 5 && pkill -SIGRTMIN+12 i3blocks" +bindsym F5 exec --no-startup-id "/home/alec/.config/i3/brightness.py -d 5 && pkill -SIGRTMIN+12 i3blocks && pause 1" +bindsym F6 exec --no-startup-id "/home/alec/.config/i3/brightness.py -i 5 && pkill -SIGRTMIN+12 i3blocks" # volume controls bindsym XF86AudioLowerVolume exec --no-startup-id "amixer set Master 5%- unmute && pkill -SIGRTMIN+10 i3blocks" @@ -221,17 +227,8 @@ bindsym XF86AudioMute exec --no-startup-id "amixer set Master toggle && pkill - bindsym --release Caps_Lock exec --no-startup-id pkill -SIGRTMIN+11 i3blocks bindsym --release Num_Lock exec --no-startup-id pkill -SIGRTMIN+11 i3blocks -# dota mode -bindsym ctrl+d mode "dota" - -mode "dota" { - bindsym ctrl+d mode "default" -} - -bindsym XF86Calculator exec "termite -e python" - -# for calendar -for_window [class="Yad"] floating enable +# home key combinations +#bindsym Mod1 + Left exec --no-startup-id "xdotool key Home" # gaps stuff for_window [class="^.*"] border pixel 0 diff --git a/.config/i3blocks/config b/.config/i3blocks/config index a8001dd..fff8dd6 100644 --- a/.config/i3blocks/config +++ b/.config/i3blocks/config @@ -24,22 +24,22 @@ # # The top properties below are applied to every block, but can be overridden. # Each block command defaults to the script name to avoid boilerplate. -command=/usr/lib/i3blocks/$BLOCK_NAME +command=~/.config/i3blocks/scripts/$BLOCK_NAME separator_block_width=15 markup=none - [arch-update] -command=python ~/.config/i3blocks/scripts/arch-update.py #run arch-update.py -h for options +command=~/.config/i3blocks/scripts/arch-update.py #run arch-update.py -h for options interval=3600 markup=pango +color=#006DA3 -[mediaplayer] -command=perl ~/.config/i3blocks/scripts/mediaplayer -instance=cmus +# This displays "ARTIST - SONG" if a music is playing. +# I rewrote this in rust because the old perl script was giving me issues +[i3blocks-cmus] interval=5 -signal=10 -label=♪ +#signal=10 + # Memory usage # @@ -67,18 +67,19 @@ interval=30 separator=false [disk-io] -label= -command=~/.config/i3blocks/scripts/disk-io +label= +command=~/.config/i3blocks/scripts/disk-io -w 2 +#command=$SCRIPT_DIR/disk-io -w 3 -M -P 0 interval=persist -#markup=pango - +markup=pango +instance=/sda/ # Network interface monitoring # # If the instance is not specified, use the interface used for default route. # The address can be forced to IPv4 or IPv6 with -4 or -6 switches. [iface] #instance=wlan0 -color=#00FF00 +color=#00ffff interval=10 separator=false @@ -101,6 +102,12 @@ interval=10 #min_width=CPU: 100.00% separator=false +[load_average] +interval=10 +separator=false +label=AVG + +# # Temperature # # Support multiple chips, though lm-sensors. @@ -109,29 +116,18 @@ separator=false [temperature] label=TEMP interval=10 -separator=false - -[load_average] -interval=10 -# Battery indicator -# -# The battery instance defaults to 0. -#[battery] -#label=BAT -#label=⚡ -#instance=1 -#interval=30 -#separator=false +[volume] +#label=VOL +label=♪ +instance=Master +#instance=PCM +interval=once +signal=10 -#[brightness] -#command=~/.config/i3blocks/brightness.py -#label=☀ -#interval=once -#signal=12 # Key indicators -#🎧 +# # Add the following bindings to i3 config file: # # bindsym --release Caps_Lock exec pkill -SIGRTMIN+11 i3blocks @@ -140,11 +136,34 @@ interval=10 instance=CAPS interval=once signal=11 - +markup=pango +# [keyindicator] instance=NUM interval=once signal=11 +markup=pango + +#[brightness] +#command=~/.config/i3blocks/scripts/brightness.py +#label= +#☀️ +#interval=once +#signal=12 + +# Battery indicator +# +# The battery instance defaults to 0. +#[battery] +#command=~/.config/i3blocks/scripts/batterybar +#label=BAT +#markup=pango +#label= BAT +#🔋 +#⚡ +#instance=1 +#interval=30 +#separator=false # Volume indicator # @@ -152,36 +171,15 @@ signal=11 # The second parameter overrides the mixer selection # See the script for details. -[volume] -#label=VOL -label=VOL -instance=Master -#instance=PCM -interval=once -signal=10 - # Date Time # -#[calendar] -#command=~/.config/i3blocks/scripts/calendar -#label= -#interval=3600 - [time] -command=~/.config/i3blocks/scripts/myCalendar -f '+%a %Y-%m-%d %H:%M:%S' label= -#command=date '+%a %Y-%m-%d %H:%M:%S' +command=~/.config/i3blocks/scripts/myCalendar -f '+%a %Y-%m-%d %H:%M:%S' interval=1 -# Generic media player support -# -# This displays "ARTIST - SONG" if a music is playing. -# Supported players are: spotify, vlc, audacious, xmms2, mplayer, and others. -#[mediaplayer] -#instance=spotify -#interval=5 -#signal=10 + # OpenVPN support # @@ -190,4 +188,3 @@ interval=1 #interval=20 - diff --git a/.config/i3blocks/i3blocks-cmus b/.config/i3blocks/i3blocks-cmus new file mode 160000 +Subproject c6969fcea8f09152ea4d1844f7ac11243618d24 diff --git a/.config/i3blocks/scripts/arch-update.py b/.config/i3blocks/scripts/arch-update.py index 7a7be68..7dc5d62 100755 --- a/.config/i3blocks/scripts/arch-update.py +++ b/.config/i3blocks/scripts/arch-update.py @@ -8,7 +8,6 @@ import subprocess from subprocess import check_output import argparse import re -import os def create_argparse(): @@ -104,6 +103,7 @@ if update_count > 0: info += ' [{0}]'.format(', '.join(matches)) print(message.format(args.updates_available_color, info)) + import os if 'BLOCK_BUTTON' in os.environ: button = os.environ['BLOCK_BUTTON'] if button is not '': @@ -112,6 +112,5 @@ if update_count > 0: subprocess.call("termite -e 'sudo pacman -Syu' --hold", shell=True) - elif not args.quiet: - print(message.format(args.base_color, '-Syu ')) + print(message.format(args.base_color, '-Syu ')) diff --git a/.config/i3blocks/scripts/bandwidth b/.config/i3blocks/scripts/bandwidth new file mode 100755 index 0000000..6a93c35 --- /dev/null +++ b/.config/i3blocks/scripts/bandwidth @@ -0,0 +1,113 @@ +#!/bin/bash +# Copyright (C) 2012 Stefan Breunig <stefan+measure-net-speed@mathphys.fsk.uni-heidelberg.de> +# Copyright (C) 2014 kaueraal +# Copyright (C) 2015 Thiago Perrotta <perrotta dot thiago at poli dot ufrj dot br> + +# 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/>. + +# Get custom IN and OUT labels if provided by command line arguments +while [[ $# -gt 1 ]]; do + key="$1" + case "$key" in + -i|--inlabel) + INLABEL="$2" + shift;; + -o|--outlabel) + OUTLABEL="$2" + shift;; + esac + shift +done + +[[ -z "$INLABEL" ]] && INLABEL="IN " +[[ -z "$OUTLABEL" ]] && OUTLABEL="OUT " + +# Use the provided interface, otherwise the device used for the default route. +if [[ -z $INTERFACE ]] && [[ -n $BLOCK_INSTANCE ]]; then + INTERFACE=$BLOCK_INSTANCE +elif [[ -z $INTERFACE ]]; then + INTERFACE=$(ip route | awk '/^default/ { print $5 ; exit }') +fi + +# Exit if there is no default route +[[ -z "$INTERFACE" ]] && exit + +# Issue #36 compliant. +if ! [ -e "/sys/class/net/${INTERFACE}/operstate" ] || \ + (! [ "$TREAT_UNKNOWN_AS_UP" = "1" ] && + ! [ "`cat /sys/class/net/${INTERFACE}/operstate`" = "up" ]) +then + echo "$INTERFACE down" + echo "$INTERFACE down" + echo "#FF0000" + exit 0 +fi + +# path to store the old results in +path="/dev/shm/$(basename $0)-${INTERFACE}" + +# grabbing data for each adapter. +read rx < "/sys/class/net/${INTERFACE}/statistics/rx_bytes" +read tx < "/sys/class/net/${INTERFACE}/statistics/tx_bytes" + +# get time +time=$(date +%s) + +# write current data if file does not exist. Do not exit, this will cause +# problems if this file is sourced instead of executed as another process. +if ! [[ -f "${path}" ]]; then + echo "${time} ${rx} ${tx}" > "${path}" + chmod 0666 "${path}" +fi + + +# read previous state and update data storage +read old < "${path}" +echo "${time} ${rx} ${tx}" > "${path}" + +# parse old data and calc time passed +old=(${old//;/ }) +time_diff=$(( $time - ${old[0]} )) + +# sanity check: has a positive amount of time passed +[[ "${time_diff}" -gt 0 ]] || exit + +# calc bytes transferred, and their rate in byte/s +rx_diff=$(( $rx - ${old[1]} )) +tx_diff=$(( $tx - ${old[2]} )) +rx_rate=$(( $rx_diff / $time_diff )) +tx_rate=$(( $tx_diff / $time_diff )) + +# shift by 10 bytes to get KiB/s. If the value is larger than +# 1024^2 = 1048576, then display MiB/s instead + +# incoming +echo -n "$INLABEL" +rx_kib=$(( $rx_rate >> 10 )) +if hash bc 2>/dev/null && [[ "$rx_rate" -gt 1048576 ]]; then + printf '%sM' "`echo "scale=1; $rx_kib / 1024" | bc`" +else + echo -n "${rx_kib}K" +fi + +echo -n " " + +# outgoing +echo -n "$OUTLABEL" +tx_kib=$(( $tx_rate >> 10 )) +if hash bc 2>/dev/null && [[ "$tx_rate" -gt 1048576 ]]; then + printf '%sM\n' "`echo "scale=1; $tx_kib / 1024" | bc`" +else + echo "${tx_kib}K" +fi diff --git a/.config/i3blocks/scripts/cpu_usage b/.config/i3blocks/scripts/cpu_usage new file mode 100755 index 0000000..44c1189 --- /dev/null +++ b/.config/i3blocks/scripts/cpu_usage @@ -0,0 +1,62 @@ +#!/usr/bin/perl +# +# Copyright 2014 Pierre Mavro <deimos@deimos.fr> +# Copyright 2014 Vivien Didelot <vivien@didelot.org> +# Copyright 2014 Andreas Guldstrand <andreas.guldstrand@gmail.com> +# +# Licensed under the terms of the GNU GPL v3, or any later version. + +use strict; +use warnings; +use utf8; +use Getopt::Long; + +# default values +my $t_warn = $ENV{T_WARN} // 50; +my $t_crit = $ENV{T_CRIT} // 80; +my $cpu_usage = -1; +my $decimals = $ENV{DECIMALS} // 2; +my $label = $ENV{LABEL} // ""; + +sub help { + print "Usage: cpu_usage [-w <warning>] [-c <critical>] [-d <decimals>]\n"; + print "-w <percent>: warning threshold to become yellow\n"; + print "-c <percent>: critical threshold to become red\n"; + print "-d <decimals>: Use <decimals> decimals for percentage (default is $decimals) \n"; + exit 0; +} + +GetOptions("help|h" => \&help, + "w=i" => \$t_warn, + "c=i" => \$t_crit, + "d=i" => \$decimals, +); + +# Get CPU usage +$ENV{LC_ALL}="en_US"; # if mpstat is not run under en_US locale, things may break, so make sure it is +open (MPSTAT, 'mpstat 1 1 |') or die; +while (<MPSTAT>) { + if (/^.*\s+(\d+\.\d+)[\s\x00]?$/) { + $cpu_usage = 100 - $1; # 100% - %idle + last; + } +} +close(MPSTAT); + +$cpu_usage eq -1 and die 'Can\'t find CPU information'; + +# Print short_text, full_text +print "${label}"; +printf "%.${decimals}f%%\n", $cpu_usage; +print "${label}"; +printf "%.${decimals}f%%\n", $cpu_usage; + +# Print color, if needed +if ($cpu_usage >= $t_crit) { + print "#FF0000\n"; + exit 33; +} elsif ($cpu_usage >= $t_warn) { + print "#FFFC00\n"; +} + +exit 0; diff --git a/.config/i3blocks/scripts/disk b/.config/i3blocks/scripts/disk new file mode 100755 index 0000000..c34240d --- /dev/null +++ b/.config/i3blocks/scripts/disk @@ -0,0 +1,48 @@ +#!/bin/sh +# Copyright (C) 2014 Julien Bonjean <julien@bonjean.info> + +# 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/>. + +DIR="${DIR:-$BLOCK_INSTANCE}" +DIR="${DIR:-$HOME}" +ALERT_LOW="${ALERT_LOW:-$1}" +ALERT_LOW="${ALERT_LOW:-10}" # color will turn red under this value (default: 10%) + +LOCAL_FLAG="-l" +if [ "$1" = "-n" ] || [ "$2" = "-n" ]; then + LOCAL_FLAG="" +fi + +df -h -P $LOCAL_FLAG "$DIR" | awk -v label="$LABEL" -v alert_low=$ALERT_LOW ' +/\/.*/ { + # full text + print label $4 + + # short text + print label $4 + + use=$5 + + # no need to continue parsing + exit 0 +} + +END { + gsub(/%$/,"",use) + if (100 - use < alert_low) { + # color + print "#FF0000" + } +} +' diff --git a/.config/i3blocks/scripts/disk-io b/.config/i3blocks/scripts/disk-io index 6e89bef..b6bebc2 100755 --- a/.config/i3blocks/scripts/disk-io +++ b/.config/i3blocks/scripts/disk-io @@ -5,7 +5,7 @@ # # i3blocks blocklet script to monitor disk io -label="${PREFIX:-""}" +label="${LABEL:-""}" dt="${DT:-5}" MB_only="${MB_ONLY:-0}" kB_only="${KB_ONLY:-0}" diff --git a/.config/i3blocks/scripts/i3blocks-cmus b/.config/i3blocks/scripts/i3blocks-cmus Binary files differnew file mode 100755 index 0000000..bd90f14 --- /dev/null +++ b/.config/i3blocks/scripts/i3blocks-cmus diff --git a/.config/i3blocks/scripts/i3blocks-contrib b/.config/i3blocks/scripts/i3blocks-contrib new file mode 160000 +Subproject 21708edc3d12c1f37285e3e9363f6541be72359 diff --git a/.config/i3blocks/scripts/iface b/.config/i3blocks/scripts/iface new file mode 100755 index 0000000..75baa94 --- /dev/null +++ b/.config/i3blocks/scripts/iface @@ -0,0 +1,70 @@ +#!/bin/bash +# Copyright (C) 2014 Julien Bonjean <julien@bonjean.info> +# Copyright (C) 2014 Alexander Keller <github@nycroth.com> + +# 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/>. + +#------------------------------------------------------------------------ + +# Use the provided interface, otherwise the device used for the default route. +IF="${IFACE:-$BLOCK_INSTANCE}" +IF="${IF:-$(ip route | awk '/^default/ { print $5 ; exit }')}" + +# Exit if there is no default route +[[ -z "$IF" ]] && exit + +#------------------------------------------------------------------------ + +# As per #36 -- It is transparent: e.g. if the machine has no battery or wireless +# connection (think desktop), the corresponding block should not be displayed. +[[ ! -d /sys/class/net/${IF} ]] && exit + +#------------------------------------------------------------------------ + +AF=${ADDRESS_FAMILY:-inet6?} +LABEL="${LABEL:-}" + +for flag in "$1" "$2"; do + case "$flag" in + -4) + AF=inet ;; + -6) + AF=inet6 ;; + -L) + if [[ "$IF" = "" ]]; then + LABEL="iface " + else + LABEL="$IF: " + fi ;; + esac +done + +if [[ "$IF" = "" ]] || [[ "$(cat /sys/class/net/$IF/operstate)" = 'down' ]]; then + echo "${LABEL}down" # full text + echo "${LABEL}down" # short text + echo \#FF0000 # color + exit +fi + +# if no interface is found, use the first device with a global scope +IPADDR=$(ip addr show $IF | perl -n -e "/$AF ([^ \/]+).* scope global/ && print \$1 and exit") + +case $BLOCK_BUTTON in + 3) echo -n "$IPADDR" | xclip -q -se c ;; +esac + +#------------------------------------------------------------------------ + +echo "$LABEL$IPADDR" # full text +echo "$LABEL$IPADDR" # short text diff --git a/.config/i3blocks/scripts/keyindicator b/.config/i3blocks/scripts/keyindicator new file mode 100755 index 0000000..50c3a3f --- /dev/null +++ b/.config/i3blocks/scripts/keyindicator @@ -0,0 +1,89 @@ +#!/usr/bin/env perl +# +# Copyright 2014 Marcelo Cerri <mhcerri at gmail dot com> +# +# 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/>. + +use strict; +use warnings; +use utf8; +use Getopt::Long; +use File::Basename; + +# Default values +my $indicator = $ENV{BLOCK_INSTANCE} || $ENV{KEY} || "CAPS"; +my $color_on = $ENV{COLOR_ON} || "#00FF00"; +my $color_off = $ENV{COLOR_OFF} || "#222222"; +my $bg_color_on = $ENV{BG_COLOR_ON}; +my $bg_color_off = $ENV{BG_COLOR_OFF}; +my $hide = $ENV{HIDE_WHEN_OFF} || 0; + +sub help { + my $program = basename($0); + printf "Usage: %s [-c <color on>] [-C <color off>] [-b <bg color on>] [-B <bg color off>] [--hide]\n", $program; + printf " -c <color on>: hex color to use when indicator is on\n"; + printf " -C <color off>: hex color to use when indicator is off\n"; + printf " -b <background color on>: hex color to use when indicator is on\n"; + printf " -B <background color off>: hex color to use when indicator is off\n"; + printf " --hide: don't output anything when indicator is off\n"; + printf "\n"; + printf "Note: environment variable \$BLOCK_INSTANCE should be one of:\n"; + printf " CAPS, NUM (default is CAPS).\n"; + exit 0; +} + +Getopt::Long::config qw(no_ignore_case); +GetOptions("help|h" => \&help, + "c=s" => \$color_on, + "C=s" => \$color_off, + "b=s" => \$bg_color_on, + "B=s" => \$bg_color_off, + "hide" => \$hide) or exit 1; + +# Key mapping +my %indicators = ( + CAPS => 0x00000001, + NUM => 0x00000002, +); + +# Retrieve key flags +my $mask = 0; +open(XSET, "xset -q |") or die; +while (<XSET>) { + if (/LED mask:\s*([0-9a-f]+)/) { + $mask = hex $1; + last; + } +} +close(XSET); + +# Determine if indicator is on or off +my $indicator_status = ($indicators{$indicator} || 0) & $mask; + +# Exit if --hide and indicator is off +if ($hide and !$indicator_status) { + exit 0 +} + +# Output +my $fg_color = $indicator_status ? $color_on : $color_off; +my $bg_color = $indicator_status ? $bg_color_on : $bg_color_off; + +if (defined $bg_color) { + printf "<span color='%s' bgcolor='%s'>%s</span>\n", $fg_color, $bg_color, $indicator; +} else { + printf "<span color='%s'>%s</span>\n", $fg_color, $indicator; +} + +exit 0 diff --git a/.config/i3blocks/scripts/load_average b/.config/i3blocks/scripts/load_average new file mode 100755 index 0000000..37a5c71 --- /dev/null +++ b/.config/i3blocks/scripts/load_average @@ -0,0 +1,34 @@ +#!/bin/sh +# Copyright (C) 2014 Julien Bonjean <julien@bonjean.info> + +# 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/>. + +load="$(cut -d ' ' -f1 /proc/loadavg)" +cpus="$(nproc)" + +# full text +echo "$load" + +# short text +echo "$load" + +# color if load is too high +awk -v cpus=$cpus -v cpuload=$load ' + BEGIN { + if (cpus <= cpuload) { + print "#FF0000"; + exit 33; + } + } +' diff --git a/.config/i3blocks/scripts/mediaplayer b/.config/i3blocks/scripts/mediaplayer Binary files differindex 718e335..dede5fd 100644 --- a/.config/i3blocks/scripts/mediaplayer +++ b/.config/i3blocks/scripts/mediaplayer diff --git a/.config/i3blocks/scripts/memory b/.config/i3blocks/scripts/memory new file mode 100755 index 0000000..90eb2c6 --- /dev/null +++ b/.config/i3blocks/scripts/memory @@ -0,0 +1,69 @@ +#!/bin/sh +# Copyright (C) 2014 Julien Bonjean <julien@bonjean.info> + +# 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/>. + +TYPE="${BLOCK_INSTANCE:-mem}" + +awk -v type=$TYPE ' +/^MemTotal:/ { + mem_total=$2 +} +/^MemFree:/ { + mem_free=$2 +} +/^Buffers:/ { + mem_free+=$2 +} +/^Cached:/ { + mem_free+=$2 +} +/^SwapTotal:/ { + swap_total=$2 +} +/^SwapFree:/ { + swap_free=$2 +} +END { + if (type == "swap") { + free=swap_free/1024/1024 + used=(swap_total-swap_free)/1024/1024 + total=swap_total/1024/1024 + } else { + free=mem_free/1024/1024 + used=(mem_total-mem_free)/1024/1024 + total=mem_total/1024/1024 + } + + pct=0 + if (total > 0) { + pct=used/total*100 + } + + # full text + printf("%.1fG/%.1fG (%.f%%)\n", used, total, pct) + + # short text + printf("%.f%%\n", pct) + + # color + if (pct > 90) { + print("#FF0000") + } else if (pct > 80) { + print("#FFAE00") + } else if (pct > 70) { + print("#FFF600") + } +} +' /proc/meminfo diff --git a/.config/i3blocks/scripts/myCalendar b/.config/i3blocks/scripts/myCalendar index 686fd00..d1557ba 100755 --- a/.config/i3blocks/scripts/myCalendar +++ b/.config/i3blocks/scripts/myCalendar @@ -1,4 +1,6 @@ #!/bin/bash +# this is a simple bash script / blocklet for i3blocks +# that will open a new termite instance with 'cal' when clicked datefmt='+%a %Y-%m-%d %H:%M:%S' diff --git a/.config/i3blocks/scripts/temperature b/.config/i3blocks/scripts/temperature new file mode 100755 index 0000000..2170f10 --- /dev/null +++ b/.config/i3blocks/scripts/temperature @@ -0,0 +1,69 @@ +#!/usr/bin/env perl +# Copyright 2014 Pierre Mavro <deimos@deimos.fr> +# Copyright 2014 Vivien Didelot <vivien@didelot.org> +# Copyright 2014 Andreas Guldstrand <andreas.guldstrand@gmail.com> +# Copyright 2014 Benjamin Chretien <chretien at lirmm dot fr> + +# 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/>. + +use strict; +use warnings; +use utf8; +use Getopt::Long; + +binmode(STDOUT, ":utf8"); + +# default values +my $t_warn = $ENV{T_WARN} || 70; +my $t_crit = $ENV{T_CRIT} || 90; +my $chip = $ENV{SENSOR_CHIP} || ""; +my $temperature = -9999; + +sub help { + print "Usage: temperature [-w <warning>] [-c <critical>] [--chip <chip>]\n"; + print "-w <percent>: warning threshold to become yellow\n"; + print "-c <percent>: critical threshold to become red\n"; + print "--chip <chip>: sensor chip\n"; + exit 0; +} + +GetOptions("help|h" => \&help, + "w=i" => \$t_warn, + "c=i" => \$t_crit, + "chip=s" => \$chip); + +# Get chip temperature +open (SENSORS, "sensors -u $chip |") or die; +while (<SENSORS>) { + if (/^\s+temp1_input:\s+[\+]*([\-]*\d+\.\d)/) { + $temperature = $1; + last; + } +} +close(SENSORS); + +$temperature eq -9999 and die 'Cannot find temperature'; + +# Print short_text, full_text +print "$temperature°C\n" x2; + +# Print color, if needed +if ($temperature >= $t_crit) { + print "#FF0000\n"; + exit 33; +} elsif ($temperature >= $t_warn) { + print "#FFFC00\n"; +} + +exit 0; diff --git a/.config/i3blocks/scripts/volume b/.config/i3blocks/scripts/volume new file mode 100755 index 0000000..6e0c4fe --- /dev/null +++ b/.config/i3blocks/scripts/volume @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +# Copyright (C) 2014 Julien Bonjean <julien@bonjean.info> +# Copyright (C) 2014 Alexander Keller <github@nycroth.com> + +# 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/>. + +#------------------------------------------------------------------------ + +# The second parameter overrides the mixer selection +# For PulseAudio users, eventually use "pulse" +# For Jack/Jack2 users, use "jackplug" +# For ALSA users, you may use "default" for your primary card +# or you may use hw:# where # is the number of the card desired +if [[ -z "$MIXER" ]] ; then + MIXER="default" + if command -v pulseaudio >/dev/null 2>&1 && pulseaudio --check ; then + # pulseaudio is running, but not all installations use "pulse" + if amixer -D pulse info >/dev/null 2>&1 ; then + MIXER="pulse" + fi + fi + [ -n "$(lsmod | grep jack)" ] && MIXER="jackplug" + MIXER="${2:-$MIXER}" +fi + +# The instance option sets the control to report and configure +# This defaults to the first control of your selected mixer +# For a list of the available, use `amixer -D $Your_Mixer scontrols` +if [[ -z "$SCONTROL" ]] ; then + SCONTROL="${BLOCK_INSTANCE:-$(amixer -D $MIXER scontrols | + sed -n "s/Simple mixer control '\([^']*\)',0/\1/p" | + head -n1 + )}" +fi + +# The first parameter sets the step to change the volume by (and units to display) +# This may be in in % or dB (eg. 5% or 3dB) +if [[ -z "$STEP" ]] ; then + STEP="${1:-5%}" +fi + +#------------------------------------------------------------------------ + +capability() { # Return "Capture" if the device is a capture device + amixer -D $MIXER get $SCONTROL | + sed -n "s/ Capabilities:.*cvolume.*/Capture/p" +} + +volume() { + amixer -D $MIXER get $SCONTROL $(capability) +} + +format() { + + perl_filter='if (/.*\[(\d+%)\] (\[(-?\d+.\d+dB)\] )?\[(on|off)\]/)' + perl_filter+='{CORE::say $4 eq "off" ? "MUTE" : "' + # If dB was selected, print that instead + perl_filter+=$([[ $STEP = *dB ]] && echo '$3' || echo '$1') + perl_filter+='"; exit}' + output=$(perl -ne "$perl_filter") + echo "$LABEL$output" +} + +#------------------------------------------------------------------------ + +case $BLOCK_BUTTON in + 3) amixer -q -D $MIXER sset $SCONTROL $(capability) toggle ;; # right click, mute/unmute + 4) amixer -q -D $MIXER sset $SCONTROL $(capability) ${STEP}+ unmute ;; # scroll up, increase + 5) amixer -q -D $MIXER sset $SCONTROL $(capability) ${STEP}- unmute ;; # scroll down, decrease +esac + +volume | format diff --git a/.config/i3blocks/scripts/wifi b/.config/i3blocks/scripts/wifi new file mode 100755 index 0000000..34b6348 --- /dev/null +++ b/.config/i3blocks/scripts/wifi @@ -0,0 +1,53 @@ +#!/bin/bash +# Copyright (C) 2014 Alexander Keller <github@nycroth.com> + +# 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/>. + +#------------------------------------------------------------------------ +if [[ -z "$INTERFACE" ]] ; then + INTERFACE="${BLOCK_INSTANCE:-wlan0}" +fi +#------------------------------------------------------------------------ + +# As per #36 -- It is transparent: e.g. if the machine has no battery or wireless +# connection (think desktop), the corresponding block should not be displayed. +[[ ! -d /sys/class/net/${INTERFACE}/wireless ]] && exit + +# If the wifi interface exists but no connection is active, "down" shall be displayed. +if [[ "$(cat /sys/class/net/$INTERFACE/operstate)" = 'down' ]]; then + echo "down" + echo "down" + echo "#FF0000" + exit +fi + +#------------------------------------------------------------------------ + +QUALITY=$(grep $INTERFACE /proc/net/wireless | awk '{ print int($3 * 100 / 70) }') + +#------------------------------------------------------------------------ + +echo $QUALITY% # full text +echo $QUALITY% # short text + +# color +if [[ $QUALITY -ge 80 ]]; then + echo "#00FF00" +elif [[ $QUALITY -ge 60 ]]; then + echo "#FFF600" +elif [[ $QUALITY -ge 40 ]]; then + echo "#FFAE00" +else + echo "#FF0000" +fi diff --git a/.config/nvim/.netrwhist b/.config/nvim/.netrwhist new file mode 100644 index 0000000..da7261b --- /dev/null +++ b/.config/nvim/.netrwhist @@ -0,0 +1,4 @@ +let g:netrw_dirhistmax =10 +let g:netrw_dirhistcnt =2 +let g:netrw_dirhist_2='/home/alec/code/rust/crafting-interpreters-rs/src' +let g:netrw_dirhist_1='/home/alec' diff --git a/.config/nvim/autoload/plug.vim b/.config/nvim/autoload/plug.vim new file mode 100644 index 0000000..7914bfe --- /dev/null +++ b/.config/nvim/autoload/plug.vim @@ -0,0 +1,2699 @@ +" vim-plug: Vim plugin manager +" ============================ +" +" Download plug.vim and put it in ~/.vim/autoload +" +" curl -fLo ~/.vim/autoload/plug.vim --create-dirs \ +" https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim +" +" Edit your .vimrc +" +" call plug#begin('~/.vim/plugged') +" +" " Make sure you use single quotes +" +" " Shorthand notation; fetches https://github.com/junegunn/vim-easy-align +" Plug 'junegunn/vim-easy-align' +" +" " Any valid git URL is allowed +" Plug 'https://github.com/junegunn/vim-github-dashboard.git' +" +" " Multiple Plug commands can be written in a single line using | separators +" Plug 'SirVer/ultisnips' | Plug 'honza/vim-snippets' +" +" " On-demand loading +" Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' } +" Plug 'tpope/vim-fireplace', { 'for': 'clojure' } +" +" " Using a non-master branch +" Plug 'rdnetto/YCM-Generator', { 'branch': 'stable' } +" +" " Using a tagged release; wildcard allowed (requires git 1.9.2 or above) +" Plug 'fatih/vim-go', { 'tag': '*' } +" +" " Plugin options +" Plug 'nsf/gocode', { 'tag': 'v.20150303', 'rtp': 'vim' } +" +" " Plugin outside ~/.vim/plugged with post-update hook +" Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' } +" +" " Unmanaged plugin (manually installed and updated) +" Plug '~/my-prototype-plugin' +" +" " Initialize plugin system +" call plug#end() +" +" Then reload .vimrc and :PlugInstall to install plugins. +" +" Plug options: +" +"| Option | Description | +"| ----------------------- | ------------------------------------------------ | +"| `branch`/`tag`/`commit` | Branch/tag/commit of the repository to use | +"| `rtp` | Subdirectory that contains Vim plugin | +"| `dir` | Custom directory for the plugin | +"| `as` | Use different name for the plugin | +"| `do` | Post-update hook (string or funcref) | +"| `on` | On-demand loading: Commands or `<Plug>`-mappings | +"| `for` | On-demand loading: File types | +"| `frozen` | Do not update unless explicitly specified | +" +" More information: https://github.com/junegunn/vim-plug +" +" +" Copyright (c) 2017 Junegunn Choi +" +" MIT License +" +" Permission is hereby granted, free of charge, to any person obtaining +" a copy of this software and associated documentation files (the +" "Software"), to deal in the Software without restriction, including +" without limitation the rights to use, copy, modify, merge, publish, +" distribute, sublicense, and/or sell copies of the Software, and to +" permit persons to whom the Software is furnished to do so, subject to +" the following conditions: +" +" The above copyright notice and this permission notice shall be +" included in all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +if exists('g:loaded_plug') + finish +endif +let g:loaded_plug = 1 + +let s:cpo_save = &cpo +set cpo&vim + +let s:plug_src = 'https://github.com/junegunn/vim-plug.git' +let s:plug_tab = get(s:, 'plug_tab', -1) +let s:plug_buf = get(s:, 'plug_buf', -1) +let s:mac_gui = has('gui_macvim') && has('gui_running') +let s:is_win = has('win32') +let s:nvim = has('nvim-0.2') || (has('nvim') && exists('*jobwait') && !s:is_win) +let s:vim8 = has('patch-8.0.0039') && exists('*job_start') +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': 'master', 'frozen': 0 } +let s:TYPE = { +\ 'string': type(''), +\ 'list': type([]), +\ 'dict': type({}), +\ 'funcref': type(function('call')) +\ } +let s:loaded = get(s:, 'loaded', {}) +let s:triggers = get(s:, 'triggers', {}) + +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 + call extend(opts, a:arg) + for opt in ['branch', 'tag', 'commit', 'rtp', 'dir', 'as'] + if has_key(opts, opt) + \ && (type(opts[opt]) != s:TYPE.string || empty(opts[opt])) + throw printf(opt_errfmt, opt, 'string') + endif + endfor + for opt in ['on', 'for'] + if has_key(opts, opt) + \ && type(opts[opt]) != s:TYPE.list + \ && (type(opts[opt]) != s:TYPE.string || empty(opts[opt])) + throw printf(opt_errfmt, opt, 'string or list') + endif + endfor + if has_key(opts, 'do') + \ && type(opts.do) != s:TYPE.funcref + \ && (type(opts.do) != s:TYPE.string || empty(opts.do)) + throw printf(opt_errfmt, 'do', 'string or funcref') + endif + 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 /^-/ + syn match plugPlus /^+/ + syn match plugStar /^*/ + syn match plugMessage /\(^- \)\@<=.*/ + syn match plugName /\(^- \)\@<=[^ ]*:/ + syn match plugSha /\%(: \)\@<=[0-9a-f]\{4,}$/ + syn match plugTag /(tag: [^)]\+)/ + syn match plugInstall /\(^+ \)\@<=[^:]*/ + syn match plugUpdate /\(^* \)\@<=[^:]*/ + syn match plugCommit /^ \X*[0-9a-f]\{7,9} .*/ contains=plugRelDate,plugEdge,plugTag + syn match plugEdge /^ \X\+$/ + syn match plugEdge /^ \X*/ contained nextgroup=plugSha + syn match plugSha /[0-9a-f]\{7,9}/ contained + syn match plugRelDate /([^)]*)$/ contained + syn match plugNotLoaded /(not loaded)$/ + syn match plugError /^x.*/ + syn region plugDeleted start=/^\~ .*/ end=/^\ze\S/ + syn match plugH2 /^.*:\n-\+$/ + syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean + hi def link plug1 Title + hi def link plug2 Repeat + hi def link plugH2 Type + hi def link plugX Exception + hi def link plugBracket Structure + hi def link plugNumber Number + + hi def link plugDash Special + hi def link plugPlus Constant + hi def link plugStar Boolean + + hi def link plugMessage Function + hi def link plugName Label + hi def link plugInstall Function + hi def link plugUpdate Type + + hi def link plugError Error + hi def link plugDeleted Ignore + hi def link plugRelDate Comment + hi def link plugEdge PreProc + hi def link plugSha Identifier + hi def link plugTag Constant + + hi def link plugNotLoaded Comment +endfunction + +function! s:lpad(str, len) + return a:str . repeat(' ', a:len - len(a:str)) +endfunction + +function! s:lines(msg) + return split(a:msg, "[\r\n]") +endfunction + +function! s:lastline(msg) + return get(s:lines(a:msg), -1, '') +endfunction + +function! s:new_window() + execute get(g:, 'plug_window', 'vertical topleft new') +endfunction + +function! s:plug_window_exists() + let buflist = tabpagebuflist(s:plug_tab) + return !empty(buflist) && index(buflist, s:plug_buf) >= 0 +endfunction + +function! s:switch_in() + if !s:plug_window_exists() + return 0 + endif + + if winbufnr(0) != s:plug_buf + let s:pos = [tabpagenr(), winnr(), winsaveview()] + execute 'normal!' s:plug_tab.'gt' + let winnr = bufwinnr(s:plug_buf) + execute winnr.'wincmd w' + call add(s:pos, winsaveview()) + else + let s:pos = [winsaveview()] + endif + + setlocal modifiable + return 1 +endfunction + +function! s:switch_out(...) + call winrestview(s:pos[-1]) + setlocal nomodifiable + if a:0 > 0 + execute a:1 + endif + + if len(s:pos) > 1 + execute 'normal!' s:pos[0].'gt' + execute s:pos[1] 'wincmd w' + call winrestview(s:pos[2]) + endif +endfunction + +function! s:finish_bindings() + nnoremap <silent> <buffer> R :call <SID>retry()<cr> + nnoremap <silent> <buffer> D :PlugDiff<cr> + nnoremap <silent> <buffer> S :PlugStatus<cr> + nnoremap <silent> <buffer> U :call <SID>status_update()<cr> + xnoremap <silent> <buffer> U :call <SID>status_update()<cr> + nnoremap <silent> <buffer> ]] :silent! call <SID>section('')<cr> + nnoremap <silent> <buffer> [[ :silent! call <SID>section('b')<cr> +endfunction + +function! s:prepare(...) + if empty(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:system(['git', 'rev-parse', 'HEAD'], a:spec.dir) + if !v:shell_error && !s:hash_match(sha, s:lines(output)[0]) + let output = s:system( + \ 'git fetch --depth 999999 && git checkout '.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 = get(spec, 'branch', 'master') + 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:lines(s:system('git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url', a:spec.dir)) + let remote = result[-1] + if v:shell_error + let err = join([remote, 'PlugClean required.'], "\n") + elseif !s:compare_git_uri(remote, a:spec.uri) + let err = join(['Invalid URI: '.remote, + \ 'Expected: '.a:spec.uri, + \ 'PlugClean required.'], "\n") + elseif a:check_branch && has_key(a:spec, 'commit') + let result = s:lines(s:system('git rev-parse HEAD 2>&1', a:spec.dir)) + let sha = result[-1] + if v:shell_error + let err = join(add(result, 'PlugClean required.'), "\n") + elseif !s:hash_match(sha, a:spec.commit) + let err = join([printf('Invalid HEAD (expected: %s, actual: %s)', + \ a:spec.commit[:6], sha[:6]), + \ 'PlugUpdate required.'], "\n") + endif + elseif a:check_branch + let branch = result[0] + " Check tag + if has_key(a:spec, 'tag') + let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir) + if a:spec.tag !=# tag && a:spec.tag !~ '\*' + let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.', + \ (empty(tag) ? 'N/A' : tag), a:spec.tag) + endif + " Check branch + elseif a:spec.branch !=# branch + let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.', + \ branch, a:spec.branch) + endif + if empty(err) + let [ahead, behind] = split(s:lastline(s:system([ + \ 'git', 'rev-list', '--count', '--left-right', + \ printf('HEAD...origin/%s', a:spec.branch) + \ ], a:spec.dir)), '\t') + if !v:shell_error && ahead + if behind + " Only mention PlugClean if diverged, otherwise it's likely to be + " pushable (and probably not that messed up). + let err = printf( + \ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n" + \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', a:spec.branch, ahead, behind) + else + let err = printf("Ahead of origin/%s by %d commit(s).\n" + \ .'Cannot update until local changes are pushed.', + \ a:spec.branch, ahead) + endif + endif + endif + endif + else + let err = 'Not found' + endif + return [err, err =~# 'PlugClean'] +endfunction + +function! s:rm_rf(dir) + if isdirectory(a:dir) + 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 range = origin ? '..origin/'.v.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 + 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:system_chomp(['git', 'rev-parse', '--short', 'HEAD'], g:plugs[name].dir) + if !empty(sha) + call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha)) + redraw + endif + endfor + + if a:0 > 0 + let fn = 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/.config/nvim/init.vim b/.config/nvim/init.vim new file mode 100644 index 0000000..415f6b6 --- /dev/null +++ b/.config/nvim/init.vim @@ -0,0 +1,226 @@ +set nocompatible " be iMproved, required +" auto-install vim-plug +if empty(glob('~/.config/nvim/autoload/plug.vim')) + silent !curl -fLo ~/.config/nvim/autoload/plug.vim --create-dirs \ + https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim + autocmd VimEnter * PlugInstall +endif +call plug#begin('~/.config/nvim/plugged') + +Plug 'neoclide/coc.nvim', {'branch': 'release'} +" rust lang +Plug 'rust-lang/rust.vim' +" syntastic +Plug 'vim-syntastic/syntastic' +"C++ Highlighting +Plug 'octol/vim-cpp-enhanced-highlight' +"GLSL +Plug 'tikhomirov/vim-glsl' +" latex +Plug 'lervag/vimtex' + +call plug#end() + +filetype plugin indent on " required +" To ignore plugin indent changes, instead use: +"filetype plugin on +" +" Brief help +" :PluginList - lists configured plugins +" :PluginInstall - installs plugins; append `!` to update or just :PluginUpdate +" :PluginSearch foo - searches for foo; append `!` to refresh local cache +" :PluginClean - confirms removal of unused plugins; append `!` to auto-approve removal +" +" see :h vundle for more details or wiki for FAQ +" Put your non-Plugin stuff after this line +" + +hi clear SignColumn + +function! HasPaste() + if &paste + return 'PASTE MODE ' + endif + return '' +endfunction + + +set clipboard+=unnamedplus +" " Copy to clipboard +vnoremap <leader>y "+y +nnoremap <leader>Y "+yg_ +nnoremap <leader>y "+y +nnoremap <leader>yy "+yy + +" " Paste from clipboard +nnoremap <leader>p "+p +nnoremap <leader>P "+P +vnoremap <leader>p "+p +vnoremap <leader>P "+P + +"python with virtualenv support +py << EOF +import os +import sys +if 'VIRTUAL_ENV' in os.environ: + project_base_dir = os.environ['VIRTUAL_ENV'] + activate_this = os.path.join(project_base_dir, 'bin/activate_this.py') + execfile(activate_this, dict(__file__=activate_this)) +EOF + + + + +" use <tab> for trigger completion and navigate to the next complete item +function! s:check_back_space() abort + let col = col('.') - 1 + return !col || getline('.')[col - 1] =~ '\s' +endfunction + +inoremap <silent><expr> <Tab> + \ pumvisible() ? "\<C-n>" : + \ <SID>check_back_space() ? "\<Tab>" : + \ coc#refresh() + +set laststatus=2 +"set statusline=\ %{HasPaste()}%F%m%r%h\ %w\ \ CWD:\ %r%{getcwd()}%h\ \ \ Line:\ %l\ \ Column:\ %c + +map <C-h> :set hlsearch!<CR> + +"nnoremap <C-Left> :tabprevious<CR> +"nnoremap <C-Right> :tabnext<CR> + +nnoremap <C-l> :ls<CR>:b<space> + +fun! TrimWhitespace() + let l:save = winsaveview() + keeppatterns %s/\s\+$//e + call winrestview(l:save) +endfun + +noremap <C-w> :call TrimWhitespace()<CR> + +set hlsearch +set incsearch + +set mouse=a + +" regex by default +set magic +set smarttab + +let g:enable_bold_font = 1 + +" Statusline {{{2 + +" Statusline definition {{{3 +set statusline= " clear +set statusline+=%1*\ " Color - +set statusline+=%t " Tail of the filename +set statusline+=%2* " Color +set statusline+=%h " Help file flag +set statusline+=%r " Read only flag +set statusline+=%3* " Color +set statusline+=%m " Modified flag +set statusline+=%= " Left/right separator +set statusline+=%4*\ " Color - +" set statusline+=%{g:pluginUpdateStatus} " Up to date plugins +set statusline+=\ \ " -- +set statusline+=%{&filetype} " Filetype +set statusline+=\ \ " -- +set statusline+=%{&fenc} " File encoding +set statusline+=[%{&ff}] " File format +set statusline+=[ " Indent settings: begin +set statusline+=%{&expandtab?\"sp\":\"tab\"}\ " Indent settings +set statusline+=%{&shiftwidth} " Indent settings +set statusline+=] " Indent settings: end +set statusline+=\ %5*\ \ \ " - Color --- +set statusline+=%2c " Cursor column +set statusline+=\ \ " -- +set statusline+=%2v " Cursor column (virtual) +set statusline+=\ \ " -- +set statusline+=(%l\ /\ %L) " Cursor line/total lines +" set statusline+=\ \ " -- +" set statusline+=%P " Percent through file + +" Statusline colors {{{3 +hi User1 ctermfg=250 ctermbg=234 +hi User2 ctermfg=008 ctermbg=234 +hi User3 ctermfg=143 ctermbg=234 +hi User4 ctermfg=237 ctermbg=233 +hi User5 ctermfg=238 ctermbg=233 + + + + +"------------Start Python PEP 8 stuff---------------- +" Number of spaces that a pre-existing tab is equal to. +au BufRead,BufNewFile *.html,*py,*pyw,*.c,*.h,*.js,*.tex,*.glsl,*.java,*.ts,*.tsx,*.c,*.cpp,*.h,*.hpp set tabstop=4 + +"spaces for indents +au BufRead,BufNewFile *.html,*.py,*.pyw,*.js,*.tex,*.glsl,*.java,*.ts,*.tsx,*.c,*.cpp,*.h,*.hpp set shiftwidth=4 +au BufRead,BufNewFile *.html,*.py,*.pyw,*.js,*.tex,*.glsl,*.java,*.ts,*.tsx,*.c,*.cpp,*.h,*.hpp set expandtab +au BufRead,BufNewFile *.html,*.py,*.js,*.tex,*.glsl,*.java,*.ts,*.tsx,*.c,*.cpp,*.h,*.hpp set softtabstop=4 + +" Use the below highlight group when displaying bad whitespace is desired. +highlight BadWhitespace ctermbg=red guibg=red + +" Display tabs at the beginning of a line in Python mode as bad. +au BufRead,BufNewFile *.py,*.pyw match BadWhitespace /^\t\+/ +" Make trailing whitespace be flagged as bad. +au BufRead,BufNewFile *.py,*.pyw,*.c,*.h match BadWhitespace /\s\+$/ + +" Wrap text after a certain number of characters +au BufRead,BufNewFile *.py,*.pyw, set textwidth=100 + +" Use UNIX (\n) line endings. +au BufNewFile *.py,*.pyw,*.c,*.h set fileformat=unix + +" Set the default file encoding to UTF-8: +set encoding=utf-8 + +" Number of spaces that a pre-existing tab is equal to. +au BufRead,BufNewFile *.css set tabstop=2 + +"spaces for indents +au BufRead,BufNewFile *.css set shiftwidth=2 +au BufRead,BufNewFile *.css set expandtab +au BufRead,BufNewFile *.css set softtabstop=2 + +au BufRead,BufNewFile *.tex set syntax=context +set ai "auto indent +set si "smart indent + +set nu + +highlight LineNr ctermfg=grey + +let python_highlight_all=1 +syntax on +highlight Pmenu ctermfg=cyan ctermbg=black + +let g:ycm_python_binary_path = 'python' +let g:ycm_server_python_interpreter = '/usr/bin/python' +let g:ycm_autoclose_preview_window_after_completion=1 +let g:ycm_collect_identifiers_from_comments_and_strings=1 +let g:ycm_global_ycm_extra_conf = '~/.vim/.ycm_extra_conf.py' +set backspace=indent,eol,start + +" Rust +let g:rustfmt_autosave = 1 +"let g:ycm_rust_src_path = '~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/' +"let g:racer_experimental_completer = 1 +"let g:ycm_racerd_binary_path = '~/.cargo/bin/racer' +nnoremap <C-]> :YcmCompleter GoTo<CR> + +" Syntastic +set statusline+=%#warningmsg# +set statusline+=%{SyntasticStatuslineFlag()} +set statusline+=%* + +nnoremap <C-e> :SyntasticReset<CR> +let g:syntastic_always_populate_loc_list = 1 +let g:syntastic_auto_loc_list = 1 +let g:syntastic_check_on_open = 1 +let g:syntastic_check_on_wq = 0 + diff --git a/.config/picom.conf b/.config/picom.conf new file mode 100644 index 0000000..86c688b --- /dev/null +++ b/.config/picom.conf @@ -0,0 +1,230 @@ +# Thank you code_nomad: http://9m.no/ꪯ鵞
+
+#################################
+#
+# Backend
+#
+#################################
+
+# Backend to use: "xrender" or "glx".
+# GLX backend is typically much faster but depends on a sane driver.
+backend = "glx";
+
+#################################
+#
+# GLX backend
+#
+#################################
+
+glx-no-stencil = true;
+
+# GLX backend: Copy unmodified regions from front buffer instead of redrawing them all.
+# My tests with nvidia-drivers show a 10% decrease in performance when the whole screen is modified,
+# but a 20% increase when only 1/4 is.
+# My tests on nouveau show terrible slowdown.
+# Useful with --glx-swap-method, as well.
+glx-copy-from-front = false;
+
+# GLX backend: Use MESA_copy_sub_buffer to do partial screen update.
+# My tests on nouveau shows a 200% performance boost when only 1/4 of the screen is updated.
+# May break VSync and is not available on some drivers.
+# Overrides --glx-copy-from-front.
+# glx-use-copysubbuffermesa = true;
+
+# 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).
+# Recommended if it works.
+# glx-no-rebind-pixmap = true;
+
+
+# GLX backend: GLX buffer swap method we assume.
+# Could be undefined (0), copy (1), exchange (2), 3-6, or buffer-age (-1).
+# undefined is the slowest and the safest, and the default value.
+# copy is fastest, but may fail on some drivers,
+# 2-6 are gradually slower but safer (6 is still faster than 0).
+# Usually, double buffer means 2, triple buffer means 3.
+# buffer-age means auto-detect using GLX_EXT_buffer_age, supported by some drivers.
+# Useless with --glx-use-copysubbuffermesa.
+# Partially breaks --resize-damage.
+# Defaults to undefined.
+glx-swap-method = "undefined";
+
+#################################
+#
+# Shadows
+#
+#################################
+
+# Enabled client-side shadows on windows.
+shadow = true;
+# Don't draw shadows on DND windows.
+no-dnd-shadow = true;
+# Avoid drawing shadows on dock/panel windows.
+no-dock-shadow = true;
+# Zero the part of the shadow's mask behind the window. Fix some weirdness with ARGB windows.
+clear-shadow = true;
+# The blur radius for shadows. (default 12)
+shadow-radius = 5;
+# The left offset for shadows. (default -15)
+shadow-offset-x = -5;
+# The top offset for shadows. (default -15)
+shadow-offset-y = -5;
+# The translucency for shadows. (default .75)
+shadow-opacity = 0.5;
+
+# Set if you want different colour shadows
+# shadow-red = 0.0;
+# shadow-green = 0.0;
+# shadow-blue = 0.0;
+
+# The shadow exclude options are helpful if you have shadows enabled. Due to the way compton draws its shadows, certain applications will have visual glitches
+# (most applications are fine, only apps that do weird things with xshapes or argb are affected).
+# This list includes all the affected apps I found in my testing. The "! name~=''" part excludes shadows on any "Unknown" windows, this prevents a visual glitch with the XFWM alt tab switcher.
+shadow-exclude = [
+ "! name~=''",
+ "name = 'Notification'",
+ "name = 'Plank'",
+ "name = 'Docky'",
+ "name = 'Kupfer'",
+ "name = 'xfce4-notifyd'",
+ "name *= 'VLC'",
+ "name *= 'compton'",
+ "name *= 'Chromium'",
+ "name *= 'Chrome'",
+ "class_g = 'Conky'",
+ "class_g = 'Kupfer'",
+ "class_g = 'Synapse'",
+ "class_g ?= 'Notify-osd'",
+ "class_g ?= 'Cairo-dock'",
+ "class_g ?= 'Xfce4-notifyd'",
+ "class_g ?= 'Xfce4-power-manager'",
+ "_GTK_FRAME_EXTENTS@:c"
+];
+# Avoid drawing shadow on all shaped windows (see also: --detect-rounded-corners)
+shadow-ignore-shaped = false;
+
+#################################
+#
+# Opacity
+#
+#################################
+
+menu-opacity = 1;
+inactive-opacity = 1;
+active-opacity = 1;
+frame-opacity = 1;
+inactive-opacity-override = false;
+
+# Dim inactive windows. (0.0 - 1.0)
+inactive-dim = 0.2;
+# Do not let dimness adjust based on window opacity.
+# inactive-dim-fixed = true;
+# Blur background of transparent windows. Bad performance with X Render backend. GLX backend is preferred.
+# blur-background = true;
+# Blur background of opaque windows with transparent frames as well.
+# blur-background-frame = true;
+# Do not let blur radius adjust based on window opacity.
+blur-background-fixed = false;
+blur-background-exclude = [
+ "window_type = 'dock'",
+ "window_type = 'desktop'"
+];
+
+#################################
+#
+# Fading
+#
+#################################
+
+# Fade windows during opacity changes.
+fading = true;
+# The time between steps in a fade in milliseconds. (default 10).
+fade-delta = 4;
+# Opacity change between steps while fading in. (default 0.028).
+fade-in-step = 0.03;
+# Opacity change between steps while fading out. (default 0.03).
+fade-out-step = 0.03;
+# Fade windows in/out when opening/closing
+# no-fading-openclose = true;
+
+# Specify a list of conditions of windows that should not be faded.
+fade-exclude = [ ];
+
+#################################
+#
+# Other
+#
+#################################
+
+# Try to detect WM windows and mark them as active.
+mark-wmwin-focused = true;
+# Mark all non-WM but override-redirect windows active (e.g. menus).
+mark-ovredir-focused = true;
+# Use EWMH _NET_WM_ACTIVE_WINDOW to determine which window is focused instead of using FocusIn/Out events.
+# Usually more reliable but depends on a EWMH-compliant WM.
+use-ewmh-active-win = true;
+# Detect rounded corners and treat them as rectangular when --shadow-ignore-shaped is on.
+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.
+# This prevents opacity being ignored for some apps.
+# For example without this enabled my xfce4-notifyd is 100% opacity no matter what.
+detect-client-opacity = true;
+
+# Specify refresh rate of the screen.
+# If not specified or 0, compton will try detecting this with X RandR extension.
+refresh-rate = 0;
+
+# Set VSync method. VSync methods currently available:
+# none: No VSync
+# drm: VSync with DRM_IOCTL_WAIT_VBLANK. May only work on some drivers.
+# opengl: Try to VSync with SGI_video_sync OpenGL extension. Only work on some drivers.
+# opengl-oml: Try to VSync with OML_sync_control OpenGL extension. Only work on some drivers.
+# opengl-swc: Try to VSync with SGI_swap_control OpenGL extension. Only work on some drivers. Works only with GLX backend. Known to be most effective on many drivers. Does not actually control paint timing, only buffer swap is affected, so it doesn’t have the effect of --sw-opti unlike other methods. Experimental.
+# opengl-mswc: Try to VSync with MESA_swap_control OpenGL extension. Basically the same as opengl-swc above, except the extension we use.
+# (Note some VSync methods may not be enabled at compile time.)
+vsync = "opengl-swc";
+
+# Enable DBE painting mode, intended to use with VSync to (hopefully) eliminate tearing.
+# Reported to have no effect, though.
+dbe = false;
+
+# Limit compton 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 = true;
+
+# Unredirect all windows if a full-screen opaque window is detected, to maximize performance for full-screen windows, like games.
+# Known to cause flickering when redirecting/unredirecting windows.
+# paint-on-overlay may make the flickering less obvious.
+unredir-if-possible = true;
+
+# Specify a list of conditions of windows that should always be considered focused.
+focus-exclude = [ ];
+
+# Use WM_TRANSIENT_FOR to group windows, and consider windows in the same group focused at the same time.
+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 = true;
+
+#################################
+#
+# Window type settings
+#
+#################################
+
+wintypes:
+{
+ tooltip =
+ {
+ # fade: Fade the particular type of windows.
+ fade = true;
+ # shadow: Give those windows shadow
+ shadow = false;
+ # opacity: Default opacity for the type of windows.
+ opacity = 0.85;
+ # focus: Whether to always consider windows of this type focused.
+ focus = true;
+ };
+};
diff --git a/.config/termite/config b/.config/termite/config index cf18d3b..a05d048 100644 --- a/.config/termite/config +++ b/.config/termite/config @@ -3,7 +3,7 @@ #audible_bell = false clickable_url = true #dynamic_title = true -font = DejaVu Sans Mono 10 +font = DejaVu Sans Mono 12 #fullscreen = true #geometry = 640x480 #icon_name = terminal |
