diff --git a/.gitignore b/.gitignore index 56a18f5..5be71c7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,10 @@ .AppleDouble .netrwhist vim/.vim/plugged +vim/.vim/autoload zsh/.zsh/cache +<<<<<<< HEAD .DS_Store +======= +gnupg +>>>>>>> origin/main diff --git a/bash/#.bashrc# b/bash/#.bashrc# new file mode 100644 index 0000000..703d61a --- /dev/null +++ b/bash/#.bashrc# @@ -0,0 +1,288 @@ +### Andrew's .bashrc + + + +# _ _ +# ___ _ ____ _(_)_ __ ___ _ __ _ __ ___ ___ _ __ | |_ +# / _ \ '_ \ \ / / | '__/ _ \| '_ \| '_ ` _ \ / _ \ '_ \| __| +# | __/ | | \ V /| | | | (_) | | | | | | | | | __/ | | | |_ +# \___|_| |_|\_/ |_|_| \___/|_| |_|_| |_| |_|\___|_| |_|\__| + + +# Set some defaults. +export EDITOR="emacsclient -t" +export VISUAL="emacsclient -c -a emacs" +export ALTERNATE_EDITOR="" +export PATH=".:~/bin:~/.local/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin" +export LSCOLORS="ExGxBxDxCxEgEdxbxgxcxd" + +if [[-d + + + +# checks (stolen from zshuery) +if [[ $(uname) = 'Linux' ]]; then + IS_LINUX=1 +fi + +if [[ $(uname) = 'Darwin' ]]; then + IS_MAC=1 +fi + +if [[ -x `which brew >/dev/null 2>&1` ]]; then + HAS_BREW=1 +fi + +if [[ -x `which apt-get >/dev/null 2>&1` ]]; then + sdfHAS_APT=1 +fi + + + +# _ +# _ __ _ __ ___ _ __ ___ _ __ | |_ +# | '_ \| '__/ _ \| '_ ` _ \| '_ \| __| +# | |_) | | | (_) | | | | | | |_) | |_ +# | .__/|_| \___/|_| |_| |_| .__/ \__| +# |_| |_| + + +# define useful aliases for color codes +sh_norm="\[\033[0m\]" +sh_black="\[\033[0;30m\]" +sh_dark_gray="\[\033[1;30m\]" +sh_blue="\[\033[0;34m\]" +sh_light_blue="\[\033[1;34m\]" +sh_green="\[\033[0;32m\]" +sh_light_green="\[\033[1;32m\]" +sh_cyan="\[\033[0;36m\]" +sh_light_cyan="\[\033[1;36m\]" +sh_red="\[\033[0;31m\]" +sh_light_red="\[\033[1;31m\]" +sh_purple="\[\033[0;35m\]" +sh_light_purple="\[\033[1;35m\]" +sh_brown="\[\033[0;33m\]" +sh_yellow="\[\033[1;33m\]" +sh_light_gray="\[\033[0;37m\]" +sh_white="\[\033[1;37m\]" + +function git_prompt { + PCHANGED="" + PAHEAD="" + PBEHIND="" + local FULL_BRANCH="$(git symbolic-ref -q HEAD 2>/dev/null || git name-rev --name-only --no-undefined --always HEAD 2>/dev/null)" + PBRANCH="$(echo $FULL_BRANCH | sed 's/refs\/heads\///')" + if [ -n $PBRANCH ]; then + echo -n "${PBRANCH} " + # Are there unstaged changes in the working directory + local CHANGED=$(git status --porcelain --ignore-submodule -unormal 2>/dev/null | wc -l) + if [ $CHANGED -gt 0 ]; then + PCHANGED="◊" + fi + # Are we behind the origin? + local NUM_BEHIND="$(git log --oneline ..@{u} 2>/dev/null | wc -l | tr -d ' ' )" + if [ "$NUM_BEHIND" -gt 0 ]; then + PBEHIND="↓" + fi + # Are we ahead of the origin? + local NUM_AHEAD="$(git log --oneline @{u}.. 2>/dev/null | wc -l | tr -d ' ')" + if [ "$NUM_AHEAD" -gt 0 ]; then + PAHEAD="↑" + fi + + if [[ -n $PCHANGED || -n $PAHEAD || -n $PBEHIND ]]; then + echo -n "${PCHANGED}${PBEHIND}${PAHEAD}" + fi + fi +} + + + +# Make a pretty prompt. +export PROMPT_COMMAND='history -a; if [ $? -ne 0 ];then ERROR_FLAG=1;else ERROR_FLAG=;fi;' +export PS1=${HOSTCOLOUR}${sh_light_gray}'\h'${sh_green}':\w'${sh_light_gray}' $(git_prompt)'${sh_light_gray}'${ERROR_FLAG:+'${sh_light_red}'}\$ '${sh_norm} + +# New files and folders should not be world readable +umask 0027 + +# Borrow features from sensible.bash + +# Trim long paths in prompt +PROMPT_DIRTRIM=2 + +# Update window size after every command +shopt -s checkwinsize + + + + + +# _ _ _ +# | |__ (_)___| |_ ___ _ __ _ _ +# | '_ \| / __| __/ _ \| '__| | | | +# | | | | \__ \ || (_) | | | |_| | +# |_| |_|_|___/\__\___/|_| \__, | +# |___/ + +# Append history don't overwrite +shopt -s histappend + +# Save multi-line commands as one line +shopt -s cmdhist + +# Avoid duplicate entries +export HISTCONTROL="erasedups:ignoreboth" + +# Use ISO8601 time format for history file. +export HISTTIMEFORMAT='%F %T ' + +# Big history file +HISTSIZE=500000 +HISTFILESIZE=100000 + + + + +# _ _ +# __ _| (_) __ _ ___ ___ ___ +# / _` | | |/ _` / __|/ _ \/ __| +# | (_| | | | (_| \__ \ __/\__ \ +# \__,_|_|_|\__,_|___/\___||___/ + +# jump down the tree. +alias ..="cd .." + +# empty the screen (or use C-l) +alias c="clear" + +# why wouldn't you want human readable sizes? +alias df="df -h" + +# get outta here! +alias e="exit" + +# grep shortie +alias g="grep --color=auto -i " + +# shorter ls +alias l="ls -G --color" + +# list files with details +alias ll="ls -lh --color" + +# list all files. +alias la="ls -alh --color" + +# List directories. +alias ld="ls --color -Gd */" + +# add some color to grep. +alias grep="grep --color=auto" + +# make the whole directory tree if required and let us know about it. +alias mkdir="mkdir -p -v " + +# don't ping forever. +#alias ping="ping -c5" + +# refresh the shell customizations without opening a new one. +alias refresh="source ~/.bashrc && hash -r" + +# A python calculator +alias pc='python -ic "from __future__ import division; from math import *"' + +# Start nano with line numbering no wrapping and autoindenting +alias nano='nano -ciw ' + +# shortcut for tmux command +alias tm="tmux new-session -A -s main" + +# emacsclient +alias ec="emacsclient -n -c -a emacs" + + + + + +# __ _ _ +# / _|_ _ _ __ ___| |_(_) ___ _ __ ___ +# | |_| | | | '_ \ / __| __| |/ _ \| '_ \/ __| +# | _| |_| | | | | (__| |_| | (_) | | | \__ \ +# |_| \__,_|_| |_|\___|\__|_|\___/|_| |_|___/ + +# General purpose extract command. +extract () { + if [ -f $1 ] ; then + case $1 in + *.tar.bz2) tar xjf $1 ;; + *.tar.gz) tar xzf $1 ;; + *.tar.xz) tar xJf $1 ;; + *.bz2) bunzip2 $1 ;; + *.rar) rar x $1 ;; + *.gz) gunzip $1 ;; + *.tar) tar xf $1 ;; + *.tbz2) tar xjf $1 ;; + *.tgz) tar xzf $1 ;; + *.zip) unzip $1 ;; + *.Z) uncompress $1 ;; + *.7z) 7za e $1 ;; + *.xz) xz -dv $1 ;; + *) echo "'$1' cannot be extracted via extract()" ;; + esac + else + echo "'$1' is not a valid file" + fi +} + + +# General purpose backup command. +bu () { tar czf ~/.backup/`basename $1`-`date +%Y%m%d%H%M`.tgz $1; } + +# create an archive of a list of files, compress the archive, and encrypt the compressed archive +function archive-and-encrypt { + echo -n "Enter desired output filename: " + read filename + tar cJ $1 | \ + gpg -q -r andrew@amdavidson.com -s -e -o "$filename.tar.xz.gpg" +} + + +# run mosh and tmux to a specific host +function mt { + mosh $1 -- tmux new-session -A -s main +} + + + + +# _ _ +# (_)_ __ ___ _ __ ___ _ __| |_ ___ +# | | '_ ` _ \| '_ \ / _ \| '__| __/ __| +# | | | | | | | |_) | (_) | | | |_\__ \ +# |_|_| |_| |_| .__/ \___/|_| \__|___/ +# |_| + +# import fzf if possible + +# Debian +if [ -d /usr/share/doc/fzf/examples ]; then + source /usr/share/doc/fzf/examples/key-bindings.bash + + if [ -f /usr/share/doc/fzf/examples/completion.bash ]; then + source /usr/share/doc/fzf/examples/completion.bash + fi +fi + +# MacOS +if [ -d /opt/homebrew/opt/fzf/shell ]; then + source /opt/homebrew/opt/fzf/shell/key-bindings.bash + source /opt/homebrew/opt/fzf/shell/completion.bash +fi + + + +# Import local extensions if needed. +if [ -f $HOME/.bashrc_local ]; then + source $HOME/.bashrc_local +fi + diff --git a/bash/.bashrc b/bash/.bashrc index 182b965..0d7f1de 100644 --- a/bash/.bashrc +++ b/bash/.bashrc @@ -11,10 +11,15 @@ # Set some defaults. export EDITOR="nvim" -export PATH=".:~/bin:~/.local/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin" +export VISUAL="nvim" +export ALTERNATE_EDITOR="" +export PATH=".:~/bin:~/.local/bin:/usr/games:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin" export LSCOLORS="ExGxBxDxCxEgEdxbxgxcxd" - +# include doom in path if it exists +if [ -d ~/.config/emacs/bin ]; then + export PATH="$HOME/.emacs.d/bin:$PATH" +fi # checks (stolen from zshuery) if [[ $(uname) = 'Linux' ]]; then @@ -33,11 +38,6 @@ if [[ -x `which apt-get >/dev/null 2>&1` ]]; then HAS_APT=1 fi -if [[ -x `which yum >/dev/null 2>&1` ]]; then - HAS_YUM=1 -fi - - # _ # _ __ _ __ ___ _ __ ___ _ __ | |_ @@ -100,7 +100,7 @@ function git_prompt { # Make a pretty prompt. export PROMPT_COMMAND='history -a; if [ $? -ne 0 ];then ERROR_FLAG=1;else ERROR_FLAG=;fi;' -export PS1=${HOSTCOLOUR}${sh_light_gray}'\t | \h'${sh_green}':\w'${sh_light_gray}' $(git_prompt)'${sh_light_gray}'${ERROR_FLAG:+'${sh_light_red}'}\$ '${sh_norm} +export PS1=${HOSTCOLOUR}${sh_light_gray}'\h'${sh_green}':\w'${sh_light_gray}' $(git_prompt)'${sh_light_gray}'${ERROR_FLAG:+'${sh_light_red}'}\$ '${sh_norm} # New files and folders should not be world readable umask 0027 @@ -194,16 +194,18 @@ alias pc='python -ic "from __future__ import division; from math import *"' # Start nano with line numbering no wrapping and autoindenting alias nano='nano -ciw ' -# shortcut for tmux command -alias tm="tmux new-session -A -s main" - -# always use nvim -alias vim='nvim' -alias vi='nvim' -alias nv='nvim' +# emacsclient +alias ec="emacsclient -n -c -a emacs" +if [[ HAS_GNOME ]]; then + alias nosleep="gnome-session-inhibit --inhibit-only" +fi +# magic packets to wake up hosts +alias wake_richie="wakeonlan ec:b1:d7:44:91:40" +alias wake_ari="wakeonlan 3c:97:0e:97:32:84 6c:88:14:36:92:b0" +alias wake_uzi="wakeonlan f4:d4:88:7d:6a:e9 34:99:71:d8:a4:48" @@ -250,9 +252,15 @@ function archive-and-encrypt { } +# shortcut for tmux command +function tm { + tmux new-session -A -s main -x $(tput cols) -y $(tput lines) +} + + # run mosh and tmux to a specific host function mt { - mosh $1 -- tmux new-session -A -s main + mosh $1 -- tm } @@ -276,6 +284,15 @@ if [ -d /usr/share/doc/fzf/examples ]; then fi fi +# Arch +if [ -d /usr/share/fzf ]; then + source /usr/share/fzf/key-bindings.bash + + if [ -f /usr/share/fzf/completion.bash ]; then + source /usr/share/fzf/completion.bash + fi +fi + # MacOS if [ -d /opt/homebrew/opt/fzf/shell ]; then source /opt/homebrew/opt/fzf/shell/key-bindings.bash diff --git a/emacs/.doom.d/config.el b/emacs/.doom.d/config.el index cb6b3ef..f327834 100644 --- a/emacs/.doom.d/config.el +++ b/emacs/.doom.d/config.el @@ -41,6 +41,7 @@ ;; If you use `org' and don't want your org files in the default location below, ;; change `org-directory'. It must be set before org loads! (setq org-directory "~/Nextcloud/org/") +(setq org-agenda-files '("~/Nextcloud/org/todo")) ;; Whenever you reconfigure a package, make sure to wrap your config in an diff --git a/emacs/.doom.d/init.el b/emacs/.doom.d/init.el index cf04949..783d25e 100644 --- a/emacs/.doom.d/init.el +++ b/emacs/.doom.d/init.el @@ -31,16 +31,16 @@ ;;deft ; notational velocity for Emacs doom ; what makes DOOM look the way it does doom-dashboard ; a nifty splash screen for Emacs - ;;doom-quit ; DOOM quit-message prompts when you quit Emacs + doom-quit ; DOOM quit-message prompts when you quit Emacs ;;(emoji +unicode) ; 🙂 hl-todo ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW ;;hydra ;;indent-guides ; highlighted indent columns ;;ligatures ; ligatures and symbols to make your code pretty again - ;;minimap ; show a map of the code on the side + minimap ; show a map of the code on the side modeline ; snazzy, Atom-inspired modeline, plus API ;;nav-flash ; blink cursor line after big motions - ;;neotree ; a project drawer, like NERDTree for vim + neotree ; a project drawer, like NERDTree for vim ophints ; highlight the region an operation acts on (popup +defaults) ; tame sudden yet inevitable temporary windows ;;tabs ; a tab bar for Emacs diff --git a/git/.gitconfig b/git/.gitconfig index 6810dbd..fe1cd38 100644 --- a/git/.gitconfig +++ b/git/.gitconfig @@ -1,7 +1,7 @@ [user] name = Andrew Davidson - email = andrew@andr3w.net signingkey = /Users/amd/.ssh/id_rsa.pub + email = andrew@amd.im [color] ui = auto diff --git a/mutt/.mailcap b/mutt/.mailcap deleted file mode 100644 index 49eb343..0000000 --- a/mutt/.mailcap +++ /dev/null @@ -1,6 +0,0 @@ -text/html; firefox %s && sleep 5; test=test -n "$DISPLAY"; -text/html; links -html-numbered-links 1 -dump %s; nametemplate=%s.html; copiousoutput -#text/html; w3m -I %{charset} -T text/html; copiousoutput; -text/plain; vim %s; - - diff --git a/mutt/.muttrc b/mutt/.muttrc deleted file mode 100644 index 0227ffd..0000000 --- a/mutt/.muttrc +++ /dev/null @@ -1,172 +0,0 @@ -# Paths --------------------- -set folder = ~/.maildir -set alias_file = ~/.mutt/alias -set header_cache = ~/.mutt/cache/headers -set message_cachedir = ~/.mutt/cache/bodies -set certificate_file = ~/.mutt/certificates -set mailcap_path = ~/.mailcap -set tmpdir = ~/.mutt/temp -set signature = ~/.sig - -# Options ------------------- -set wait_key = no -set mbox_type = Maildir -set timeout = 3 -set mail_check = 0 -unset move -set delete -unset confirmappend -set quit -unset mark_old -set pipe_decode -set thorough_search -set charset = utf-8 -set send_charset = utf-8 - -# Sidebar ------------------- -set sidebar_visible = yes -set sidebar_width = 20 -set sidebar_short_path = yes -set sidebar_format = "%B%?F? [%F]?%* %?N?%N/?%S" -color sidebar_new color221 color233 - -# Header Options ------------ -ignore * -unignore from: reply-to: to: cc: bcc: date: subject: x-Spam-score: list-id: -unhdr_order * -hdr_order from: reply-to: to: cc: bcc: date: subject: x-Spam-score: list-id: - -# Setup IMAP ------------------- -set my_pass=`~/bin/pw.sh IMAP_PASSWORD` -set my_user=`~/bin/pw.sh IMAP_USERNAME` -set folder = "imaps://$my_user:$my_pass@imap.fastmail.com:993" -set spoolfile= "+INBOX" -set record = +Sent -set postponed = +Drafts -set trash = +Trash -set imap_check_subscribed = yes -bind index G imap-fetch-mail - -# Setup SMTP ------------------- -set smtp_url=smtp://$my_user:$my_pass@smtp.fastmail.com:587 -set ssl_force_tls=yes -#set ssl_starttls=yes - - -# Mailboxes ----------------- -# source ~/.mutt/mailboxes - -# Index --------------------- -set date_format = "%m/%d" -set index_format = "[%Z] %D %-20.20F %s" -set sort = threads -set sort_aux = reverse-last-date-received -set uncollapse_jump -set sort_re -set reply_regexp = "^(([Rr][Ee]?(\[[0-9]+\])?: *)?(\[[^]]+\] *)?)*" - -bind index R group-reply -bind index sync-mailbox -bind index collapse-thread - -macro index / "unset wait_keymutt-notmuch-py~/.cache/mutt_results" \ - "search mail (using notmuch)" - -macro index \Cr "T~UN." "mark all messages as read" - -macro index,pager o "$HOME/bin/fastmail.sh" "run offlineimap to sync fastmail" - -macro index,pager C "?" "copy a message to a mailbox" -macro index,pager s "?" "move a message to a mailbox" - -bind index D purge-message - -bind index,pager sidebar-next -bind index,pager sidebar-prev -bind index,pager sidebar-open - - -# Pager View ----------------- -set pager_index_lines = 10 # number of index lines to show -set pager_context = 3 # number of context lines to show -set pager_stop # don't go to next message automatically -set menu_scroll # scroll in menus -set tilde # show tildes like in vim -unset markers # no ugly plus signs - -set quote_regexp = "^( {0,4}[>|:#%]| {0,4}[a-z0-9]+[>|]+)+" -alternative_order text/plain text/enriched text/html - - -# Pager Bindings ------------- -bind pager k previous-line -bind pager j next-line -bind pager R group-reply - -# View attachments properly. -bind attach view-mailcap -auto_view text/html - -macro pager \Cu "|urlview" "call urlview to open links" - -# Setup Identity -set realname="Andrew Davidson" -set from="andrew@amdavidson.com" - - - -# Setup VIM for editing headers -set edit_headers -set editor="nvim +':set textwidth=0' +':set wrapmargin=0' +':set wrap' +':set linebreak' +':set nolist' +/^$ ++1" - -# Contacts shortcuts -set query_command = "khard email --parsable --search-in-source-files '%s'" -bind editor complete-query -bind editor ^T complete -macro index,pager A "khard add-email" "add the sender email address to khard" - -# Some neat stuff. -set fcc_attach=yes # Forward attachments. -unset reply_self # Don't include myself when replying to all -set smart_wrap # wrap text smartly and don't clip words. -set forward_format='Fwd: %s' # make the forwarding subject line look more like other clients. -set forward_decode -set reply_to -set reverse_name -set include -set forward_quote - -# Crypto stuff -set pgp_decode_command="gpg --pinentry-mode loopback %?p?--passphrase-fd 0? --no-verbose --batch --output - %f" -set pgp_verify_command="gpg --pinentry-mode loopback --no-verbose --batch --output - --verify %s %f" -set pgp_decrypt_command="gpg --pinentry-mode loopback --passphrase-fd 0 --no-verbose --batch --output - %f" -set pgp_sign_command="gpg --pinentry-mode loopback --no-verbose --batch --output - --passphrase-fd 0 --armor --detach-sign --textmode %?a?-u %a? %f" -set pgp_clearsign_command="gpg --pinentry-mode loopback --no-verbose --batch --output - --passphrase-fd 0 --armor --textmode --clearsign %?a?-u %a? %f" -set pgp_encrypt_only_command="/usr/lib/neomutt/pgpewrap gpg --batch --quiet --no-verbose --output - --encrypt --textmode --armor --always-trust --encrypt-to 0x49CF25C6 -- -r %r -- %f" -set pgp_encrypt_sign_command="/usr/lib/neomutt/pgpewrap gpg --passphrase-fd 0 --batch --quiet --no-verbose --textmode --output - --encrypt --sign %?a?-u %a? --armor --always-trust --encrypt-to 0x49CF25C6 -- -r %r -- %f" -set pgp_import_command="gpg --no-verbose --import -v %f" -set pgp_export_command="gpg --no-verbose --export --armor %r" -set pgp_verify_key_command="gpg --no-verbose --batch --fingerprint --check-sigs %r" -set pgp_list_pubring_command="gpg --no-verbose --batch --with-colons --list-keys %r" -set pgp_list_secring_command="gpg --no-verbose --batch --with-colons --list-secret-keys %r" - -# Sign emails as me. -set pgp_sign_as=0x49CF25C6 - -# Automatically sign emails. -#set crypt_autosign - -# Reply to signed emails with a signed email. -set crypt_replysign - -# Encrypt and sign replies to signed emails. -# set crypt_replyencrypt=yes - -# Encrypt and sign replies to encrypted emails. -set crypt_replysignencrypted=yes - -# Time out of GPG after xx seconds. -set pgp_timeout=28800 - -# Automatically verify signatures. -set crypt_verify_sig=yes diff --git a/neomutt/.config/neomutt/neomuttrc b/neomutt/.config/neomutt/neomuttrc new file mode 100644 index 0000000..ffd3764 --- /dev/null +++ b/neomutt/.config/neomutt/neomuttrc @@ -0,0 +1,86 @@ +### Me +set from = "andrew@amd.im" +set realname = "Andrew Davidson" + + +### Connectivity +set imap_user = "andrew@bigfootstoybox.com" +set imap_pass = "`pass show email/bigfootstoybox.com/andrew`" +set folder = "imaps://mail.amd.im" +set smtp_url = "smtps://$imap_user:$imap_pass@mail.amd.im:465" +set ssl_starttls = no +set ssl_force_tls = yes +set imap_check_subscribed # automatically check subscribed folders +mailboxes +INBOX + + +### Mailbox Locations +set spoolfile = "+INBOX" +set record = "=Sent" +set postponed = "=Drafts" +set trash = "=Trash" + + +### Local Folders +set header_cache = "~/.cache/mutt/headers" +set message_cachedir = "~/.cache/mutt/messages" + + +### Local Settings +set mail_check = 60 # check for mail every XX seconds +set imap_keepalive = 900 # does what it says on the tin, in seconds +set edit_headers # include headers when editing a message +set fast_reply # dont ask too many questions when replying +set pager_stop # stop scrolling pager at end of message +set pager_context # add 3 lines of context to message pager +set forward_quote # include message when forwarding +set forward_decode # decode message when forwarding +set reply_to # follow Reply to: header +set reverse_name # reply as the recipient address +set include # include message in replies as default +set text_flowed=yes # correct indentation +unset sig_dashes # no dashes before signature +set charset = "utf-8" # prefer utf-8 +set send_charset = "utf-8:iso-8859-1:us-ascii" # send in utf-8 +set editor = "nvim" # we know we want to edit in nvim +unset confirmappend # don't double check when moving messages +set reverse_alias # if an alias is set for a contact, use that +set pager_index_lines = 10 # Shows 10 lines of index when pager is active + + +### Message threading +set sort_re # changes method by which threads are sorted +set sort = reverse-threads # sort by threads +set sort_aux = last-date-received # secondarily sory by most recent message received in thread + + +### Sidebar +set sidebar_visible # show the sidebar +set sidebar_short_path # simplify paths in sidebar +unset sidebar_folder_indent # indent mailboxes in sidebar +set sidebar_format = "%B %* [%?N?%N/?%S]" # what should the sidebar display +set sidebar_sort_method = path +set mail_check_stats # check mailbox statistics for sidebar view + + +### Colorscheme +source "./nord.theme" + + +### Keybindings +bind index j next-entry +bind index k previous-entry +bind pager j next-line +bind pager k previous-line + +bind attach,index,pager \CD next-page +bind attach,index,pager \CU previous-page +bind pager g top +bind pager G bottom +bind attach,index g first-entry +bind attach,index G last-entry + +bind index,pager \CJ sidebar-next +bind index,pager \CK sidebar-prev +bind index,pager \CE sidebar-open +bind index,pager B sidebar-toggle-visible diff --git a/neomutt/.config/neomutt/nord.theme b/neomutt/.config/neomutt/nord.theme new file mode 100644 index 0000000..9df3c0a --- /dev/null +++ b/neomutt/.config/neomutt/nord.theme @@ -0,0 +1,28 @@ +color normal default default # default colours +color index brightblue default ~N # new messages +color index red default ~F # flagged messages +color index blue default ~T # tagged messages +color index brightred default ~D # deleted messages +color body brightgreen default (https?|ftp)://[\-\.+,/%~_:?&=\#a-zA-Z0-9]+ # links +color body brightgreen default [\-\.+_a-zA-Z0-9]+@[\-\.a-zA-Z0-9]+ # email-addresses +color attachment magenta default # attachments +color signature brightwhite default # sigs +color search brightred black # highlight results + +color indicator black cyan # currently highlighted message +color error red default # error messages +color status white brightblack # status line +color tree white default # thread tree arrows +color tilde cyan default # blank line padding + +color hdrdefault brightblue default # default headers +color header cyan default "^From:" +color header cyan default "^Subject:" + +color quoted cyan default # quote colours +color quoted1 brightcyan default +color quoted2 blue default +color quoted3 green default +color quoted4 yellow default +color quoted5 red default + diff --git a/tmux/.tmux.conf b/tmux/.tmux.conf index 9abbb50..63e2c46 100644 --- a/tmux/.tmux.conf +++ b/tmux/.tmux.conf @@ -1,6 +1,6 @@ # More logical (to me) split keys. -bind - split-window -p 38 -v -bind | split-window -p 38 -h +bind - split-window -v -l 38% +bind | split-window -h -l 38% set -g @plugin 'tmux-plugins/tpm' set -g @plugin 'tmux-plugins/tmux-sensible' diff --git a/tmux/.tmux/plugins/.gitignore b/tmux/.tmux/plugins/.gitignore new file mode 100644 index 0000000..72e8ffc --- /dev/null +++ b/tmux/.tmux/plugins/.gitignore @@ -0,0 +1 @@ +* diff --git a/vim/.vim/autoload/plug.vim b/vim/.vim/autoload/plug.vim index 143c377..5c910ca 100644 --- a/vim/.vim/autoload/plug.vim +++ b/vim/.vim/autoload/plug.vim @@ -1,67 +1,36 @@ " vim-plug: Vim plugin manager " ============================ " -" Download plug.vim and put it in ~/.vim/autoload +" 1. Download plug.vim and put it in 'autoload' directory " +" # Vim " curl -fLo ~/.vim/autoload/plug.vim --create-dirs \ " https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim " -" Edit your .vimrc +" # Neovim +" sh -c 'curl -fLo "${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/autoload/plug.vim --create-dirs \ +" https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim' " -" call plug#begin('~/.vim/plugged') +" 2. Add a vim-plug section to your ~/.vimrc (or ~/.config/nvim/init.vim for Neovim) " -" " Make sure you use single quotes +" call plug#begin() " -" " Shorthand notation; fetches https://github.com/junegunn/vim-easy-align -" Plug 'junegunn/vim-easy-align' +" " List your plugins here +" Plug 'tpope/vim-sensible' " -" " 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. +" 3. Reload the file or restart Vim, then you can, " -" Plug options: +" :PlugInstall to install plugins +" :PlugUpdate to update plugins +" :PlugDiff to review the changes from the last update +" :PlugClean to remove plugins no longer in the list " -"| 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 ``-mappings | -"| `for` | On-demand loading: File types | -"| `frozen` | Do not update unless explicitly specified | -" -" More information: https://github.com/junegunn/vim-plug +" For more information, see https://github.com/junegunn/vim-plug " " -" Copyright (c) 2017 Junegunn Choi +" Copyright (c) 2024 Junegunn Choi " " MIT License " @@ -96,11 +65,17 @@ 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') || has('win64') +let s:is_win = has('win32') let s:nvim = has('nvim-0.2') || (has('nvim') && exists('*jobwait') && !s:is_win) let s:vim8 = has('patch-8.0.0039') && exists('*job_start') -let s:me = resolve(expand(':p')) -let s:base_spec = { 'branch': 'master', 'frozen': 0 } +if s:is_win && &shellslash + set noshellslash + let s:me = resolve(expand(':p')) + set shellslash +else + let s:me = resolve(expand(':p')) +endif +let s:base_spec = { 'branch': '', 'frozen': 0 } let s:TYPE = { \ 'string': type(''), \ 'list': type([]), @@ -110,18 +85,139 @@ let s:TYPE = { let s:loaded = get(s:, 'loaded', {}) let s:triggers = get(s:, 'triggers', {}) +function! s:is_powershell(shell) + return a:shell =~# 'powershell\(\.exe\)\?$' || a:shell =~# 'pwsh\(\.exe\)\?$' +endfunction + +function! s:isabsolute(dir) abort + return a:dir =~# '^/' || (has('win32') && a:dir =~? '^\%(\\\|[A-Z]:\)') +endfunction + +function! s:git_dir(dir) abort + let gitdir = s:trim(a:dir) . '/.git' + if isdirectory(gitdir) + return gitdir + endif + if !filereadable(gitdir) + return '' + endif + let gitdir = matchstr(get(readfile(gitdir), 0, ''), '^gitdir: \zs.*') + if len(gitdir) && !s:isabsolute(gitdir) + let gitdir = a:dir . '/' . gitdir + endif + return isdirectory(gitdir) ? gitdir : '' +endfunction + +function! s:git_origin_url(dir) abort + let gitdir = s:git_dir(a:dir) + let config = gitdir . '/config' + if empty(gitdir) || !filereadable(config) + return '' + endif + return matchstr(join(readfile(config)), '\[remote "origin"\].\{-}url\s*=\s*\zs\S*\ze') +endfunction + +function! s:git_revision(dir) abort + let gitdir = s:git_dir(a:dir) + let head = gitdir . '/HEAD' + if empty(gitdir) || !filereadable(head) + return '' + endif + + let line = get(readfile(head), 0, '') + let ref = matchstr(line, '^ref: \zs.*') + if empty(ref) + return line + endif + + if filereadable(gitdir . '/' . ref) + return get(readfile(gitdir . '/' . ref), 0, '') + endif + + if filereadable(gitdir . '/packed-refs') + for line in readfile(gitdir . '/packed-refs') + if line =~# ' ' . ref + return matchstr(line, '^[0-9a-f]*') + endif + endfor + endif + + return '' +endfunction + +function! s:git_local_branch(dir) abort + let gitdir = s:git_dir(a:dir) + let head = gitdir . '/HEAD' + if empty(gitdir) || !filereadable(head) + return '' + endif + let branch = matchstr(get(readfile(head), 0, ''), '^ref: refs/heads/\zs.*') + return len(branch) ? branch : 'HEAD' +endfunction + +function! s:git_origin_branch(spec) + if len(a:spec.branch) + return a:spec.branch + endif + + " The file may not be present if this is a local repository + let gitdir = s:git_dir(a:spec.dir) + let origin_head = gitdir.'/refs/remotes/origin/HEAD' + if len(gitdir) && filereadable(origin_head) + return matchstr(get(readfile(origin_head), 0, ''), + \ '^ref: refs/remotes/origin/\zs.*') + endif + + " The command may not return the name of a branch in detached HEAD state + let result = s:lines(s:system('git symbolic-ref --short HEAD', a:spec.dir)) + return v:shell_error ? '' : result[-1] +endfunction + +if s:is_win + function! s:plug_call(fn, ...) + let shellslash = &shellslash + try + set noshellslash + return call(a:fn, a:000) + finally + let &shellslash = shellslash + endtry + endfunction +else + function! s:plug_call(fn, ...) + return call(a:fn, a:000) + endfunction +endif + +function! s:plug_getcwd() + return s:plug_call('getcwd') +endfunction + +function! s:plug_fnamemodify(fname, mods) + return s:plug_call('fnamemodify', a:fname, a:mods) +endfunction + +function! s:plug_expand(fmt) + return s:plug_call('expand', a:fmt, 1) +endfunction + +function! s:plug_tempname() + return s:plug_call('tempname') +endfunction + function! plug#begin(...) if a:0 > 0 - let s:plug_home_org = a:1 - let home = s:path(fnamemodify(expand(a:1), ':p')) + 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 has('nvim') + let home = stdpath('data') . '/plugged' elseif !empty(&rtp) let home = s:path(split(&rtp, ',')[0]) . '/plugged' else return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.') endif - if fnamemodify(home, ':t') ==# 'plugin' && fnamemodify(home, ':h') ==# s:first_rtp + 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 @@ -139,6 +235,16 @@ function! s:define_commands() 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\)\?$' || s:is_powershell(&shell)) + 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(0, []) command! -nargs=* -bar -bang -complete=customlist,s:names PlugUpdate call s:update(0, []) command! -nargs=0 -bar -bang PlugClean call s:clean(0) @@ -193,9 +299,17 @@ function! s:ask_no_interrupt(...) endtry endfunction +function! s:lazy(plug, opt) + return has_key(a:plug, a:opt) && + \ (empty(s:to_a(a:plug[a:opt])) || + \ !isdirectory(a:plug.dir) || + \ len(s:glob(s:rtp(a:plug), 'plugin')) || + \ len(s:glob(s:rtp(a:plug), 'after/plugin'))) +endfunction + function! plug#end() if !exists('g:plugs') - return s:err('Call plug#begin() first') + return s:err('plug#end() called without calling plug#begin() first') endif if exists('#PlugLOD') @@ -206,7 +320,7 @@ function! plug#end() endif let lod = { 'ft': {}, 'map': {}, 'cmd': {} } - if exists('g:did_load_filetypes') + if get(g:, 'did_load_filetypes', 0) filetype off endif for name in g:plugs_order @@ -214,7 +328,7 @@ function! plug#end() continue endif let plug = g:plugs[name] - if get(s:loaded, name, 0) || !has_key(plug, 'on') && !has_key(plug, 'for') + if get(s:loaded, name, 0) || !s:lazy(plug, 'on') && !s:lazy(plug, 'for') let s:loaded[name] = 1 continue endif @@ -245,6 +359,9 @@ function! plug#end() if !empty(types) augroup filetypedetect call s:source(s:rtp(plug), 'ftdetect/**/*.vim', 'after/ftdetect/**/*.vim') + if has('nvim-0.5.0') + call s:source(s:rtp(plug), 'ftdetect/**/*.lua', 'after/ftdetect/**/*.lua') + endif augroup END endif for type in types @@ -261,7 +378,7 @@ function! plug#end() for [map, names] in items(lod.map) for [mode, map_prefix, key_prefix] in - \ [['i', '', ''], ['n', '', ''], ['v', '', 'gv'], ['o', '', '']] + \ [['i', '', ''], ['n', '', ''], ['v', '', 'gv'], ['o', '', '']] execute printf( \ '%snoremap %s %s:call lod_map(%s, %s, %s, "%s")', \ mode, map, map_prefix, string(map), string(names), mode != 'i', key_prefix) @@ -292,6 +409,9 @@ endfunction function! s:load_plugin(spec) call s:source(s:rtp(a:spec), 'plugin/**/*.vim', 'after/plugin/**/*.vim') + if has('nvim-0.5.0') + call s:source(s:rtp(a:spec), 'plugin/**/*.lua', 'after/plugin/**/*.lua') + endif endfunction function! s:reload_plugins() @@ -316,7 +436,7 @@ 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)') + 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 @@ -326,11 +446,11 @@ function! s:progress_opt(base) \ s:git_version_requirement(1, 7, 1) ? '--progress' : '' endfunction -if s:is_win - function! s:rtp(spec) - return s:path(a:spec.dir . get(a:spec, 'rtp', '')) - endfunction +function! s: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 @@ -342,11 +462,33 @@ if s:is_win function! s:is_local_plug(repo) return a:repo =~? '^[a-z]:\|^[%~]' endfunction -else - function! s:rtp(spec) - return s:dirpath(a:spec.dir . get(a:spec, 'rtp', '')) + + " 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 s:is_powershell(&shell) + let cmd = '& ' . cmd + endif + return [batchfile, cmd] + endfunction +else function! s:path(path) return s:trim(a:path) endfunction @@ -426,8 +568,8 @@ endfunction function! s:dobufread(names) for name in a:names - let path = s:rtp(g:plugs[name]).'/**' - for dir in ['ftdetect', 'ftplugin'] + 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 @@ -487,6 +629,9 @@ function! s:lod(names, types, ...) let rtp = s:rtp(g:plugs[name]) for dir in a:types call s:source(rtp, dir.'/**/*.vim') + if has('nvim-0.5.0') " see neovim#14686 + call s:source(rtp, dir.'/**/*.lua') + endif endfor if a:0 if !s:source(rtp, a:1) && !empty(s:glob(rtp, a:2)) @@ -546,7 +691,7 @@ function! plug#(repo, ...) try let repo = s:trim(a:repo) let opts = a:0 == 1 ? s:parse_options(a:1) : s:base_spec - let name = get(opts, 'as', fnamemodify(repo, ':t:s?\.git$??')) + let 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) @@ -554,19 +699,41 @@ function! plug#(repo, ...) let g:plugs[name] = spec let s:loaded[name] = get(s:loaded, name, 0) catch - return s:err(v:exception) + return s:err(repo . ' ' . v:exception) endtry endfunction function! s:parse_options(arg) let opts = copy(s:base_spec) let type = type(a:arg) + let opt_errfmt = 'Invalid argument for "%s" option of :Plug (expected: %s)' if type == s:TYPE.string + if empty(a:arg) + throw printf(opt_errfmt, 'tag', 'string') + endif let opts.tag = a:arg elseif type == s:TYPE.dict + for opt in ['branch', 'tag', 'commit', 'rtp', 'dir', 'as'] + if has_key(a:arg, opt) + \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt])) + throw printf(opt_errfmt, opt, 'string') + endif + endfor + for opt in ['on', 'for'] + if has_key(a:arg, opt) + \ && type(a:arg[opt]) != s:TYPE.list + \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt])) + throw printf(opt_errfmt, opt, 'string or list') + endif + endfor + if has_key(a:arg, 'do') + \ && type(a:arg.do) != s:TYPE.funcref + \ && (type(a:arg.do) != s:TYPE.string || empty(a:arg.do)) + throw printf(opt_errfmt, 'do', 'string or funcref') + endif call extend(opts, a:arg) if has_key(opts, 'dir') - let opts.dir = s:dirpath(expand(opts.dir)) + let opts.dir = s:dirpath(s:plug_expand(opts.dir)) endif else throw 'Invalid argument type (expected: string or dictionary)' @@ -577,7 +744,7 @@ endfunction function! s:infer_properties(name, repo) let repo = a:repo if s:is_local_plug(repo) - return { 'dir': s:dirpath(expand(repo)) } + return { 'dir': s:dirpath(s:plug_expand(repo)) } else if repo =~ ':' let uri = repo @@ -616,11 +783,12 @@ 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 + syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX,plugAbort syn match plugNumber /[0-9]\+[0-9.]*/ contained syn match plugBracket /[[\]]/ contained syn match plugX /x/ contained - syn match plugDash /^-/ + syn match plugAbort /\~/ contained + syn match plugDash /^-\{1}\ / syn match plugPlus /^+/ syn match plugStar /^*/ syn match plugMessage /\(^- \)\@<=.*/ @@ -638,11 +806,13 @@ function! s:syntax() syn match plugError /^x.*/ syn region plugDeleted start=/^\~ .*/ end=/^\ze\S/ syn match plugH2 /^.*:\n-\+$/ + syn match plugH2 /^-\{2,}/ syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean hi def link plug1 Title hi def link plug2 Repeat hi def link plugH2 Type hi def link plugX Exception + hi def link plugAbort Ignore hi def link plugBracket Structure hi def link plugNumber Number @@ -678,7 +848,7 @@ function! s:lastline(msg) endfunction function! s:new_window() - execute get(g:, 'plug_window', 'vertical topleft new') + execute get(g:, 'plug_window', '-tabnew') endfunction function! s:plug_window_exists() @@ -730,7 +900,7 @@ function! s:finish_bindings() endfunction function! s:prepare(...) - if empty(getcwd()) + if empty(s:plug_getcwd()) throw 'Invalid current working directory. Cannot proceed.' endif @@ -740,7 +910,7 @@ function! s:prepare(...) endif endfor - call s:job_abort() + call s:job_abort(0) if s:switch_in() if b:plug_preview == 1 pc @@ -750,7 +920,7 @@ function! s:prepare(...) call s:new_window() endif - nnoremap q :if b:plug_preview==1pcendifbd + nnoremap q :call close_pane() if a:0 == 0 call s:finish_bindings() endif @@ -763,12 +933,26 @@ function! s:prepare(...) execute 'silent! unmap ' 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:close_pane() + if b:plug_preview == 1 + pc + let b:plug_preview = -1 + elseif exists('s:jobs') && !empty(s:jobs) + call s:job_abort(1) + else + bd + endif +endfunction + function! s:assign_name() " Assign buffer name let prefix = '[Plugins]' @@ -783,31 +967,35 @@ endfunction function! s:chsh(swap) let prev = [&shell, &shellcmdflag, &shellredir] - if s:is_win - set shell=cmd.exe shellcmdflag=/c shellredir=>%s\ 2>&1 - elseif a:swap - set shell=sh shellredir=>%s\ 2>&1 + if !s:is_win + set shell=sh + endif + if a:swap + if s:is_powershell(&shell) + 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 = tempname().'.bat' - call writefile(['@echo off', cmd], batchfile) - let cmd = batchfile + 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\\" finally unlet g:_plug_bang let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] - if s:is_win + if s:is_win && filereadable(batchfile) call delete(batchfile) endif endtry @@ -820,10 +1008,15 @@ function! s:regress_bar() endfunction function! s:is_updated(dir) - return !empty(s:system_chomp('git log --pretty=format:"%h" "HEAD...HEAD@{1}"', a:dir)) + return !empty(s:system_chomp(['git', 'log', '--pretty=format:%h', 'HEAD...HEAD@{1}'], a:dir)) endfunction function! s:do(pull, force, todo) + if has('nvim') + " Reset &rtp to invalidate Neovim cache of loaded Lua modules + " See https://github.com/junegunn/vim-plug/pull/1157#issuecomment-1809226110 + let &rtp = &rtp + endif for [name, spec] in items(a:todo) if !isdirectory(spec.dir) continue @@ -857,6 +1050,7 @@ function! s:do(pull, force, todo) 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 @@ -883,12 +1077,15 @@ 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:git_revision(a:spec.dir) + let error = 0 + if !empty(output) && !s:hash_match(sha, s:lines(output)[0]) + let credential_helper = s:git_version_requirement(2) ? '-c credential.helper= ' : '' let output = s:system( - \ 'git fetch --depth 999999 && git checkout '.s:esc(sha).' --', a:spec.dir) + \ 'git '.credential_helper.'fetch --depth 999999 && git checkout '.plug#shellescape(sha).' --', a:spec.dir) + let error = v:shell_error endif - return output + return [output, error] endfunction function! s:finish(pull) @@ -908,7 +1105,7 @@ function! s:finish(pull) call add(msgs, "Press 'R' to retry.") endif if a:pull && len(s:update.new) < len(filter(getline(5, '$'), - \ "v:val =~ '^- ' && stridx(v:val, 'Already up-to-date') < 0")) + \ "v:val =~ '^- ' && v:val !~# 'Already up.to.date'")) call add(msgs, "Press 'D' to see the updated changes.") endif echo join(msgs, ' ') @@ -949,7 +1146,7 @@ function! s:update_impl(pull, force, args) abort 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 managed = filter(deepcopy(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') @@ -1001,13 +1198,21 @@ function! s:update_impl(pull, force, args) abort normal! 2G silent! redraw - let s:clone_opt = get(g:, 'plug_shallow', 1) ? - \ '--depth 1' . (s:git_version_requirement(1, 7, 10) ? ' --no-single-branch' : '') : '' - - if has('win32unix') - let s:clone_opt .= ' -c core.eol=lf -c core.autocrlf=input' + " Set remote name, overriding a possible user git config's clone.defaultRemoteName + let s:clone_opt = ['--origin', 'origin'] + 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 @@ -1075,13 +1280,15 @@ function! s:update_finish() if !pos continue endif + let out = '' + let error = 0 if has_key(spec, 'commit') call s:log4(name, 'Checking out '.spec.commit) - let out = s:checkout(spec) + let [out, error] = s:checkout(spec) elseif has_key(spec, 'tag') let tag = spec.tag if tag =~ '\*' - let tags = s:lines(s:system('git tag --list '.s:shellesc(tag).' --sort -version:refname 2>&1', spec.dir)) + 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)) @@ -1089,20 +1296,17 @@ function! s:update_finish() endif endif call s:log4(name, 'Checking out '.tag) - let out = s:system('git checkout -q '.s:esc(tag).' -- 2>&1', spec.dir) - else - let branch = s:esc(get(spec, 'branch', 'master')) - call s:log4(name, 'Merging origin/'.branch) - let out = s:system('git checkout -q '.branch.' -- 2>&1' - \. (has_key(s:update.new, name) ? '' : ('&& git merge --ff-only origin/'.branch.' 2>&1')), spec.dir) + let out = s:system('git checkout -q '.plug#shellescape(tag).' -- 2>&1', spec.dir) + let error = v:shell_error endif - if !v:shell_error && filereadable(spec.dir.'/.gitmodules') && + if !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 2>&1', spec.dir) + let out .= s:bang('git submodule update --init --recursive'.s:submodule_opt.' 2>&1', spec.dir) + let error = v:shell_error endif let msg = s:format_message(v:shell_error ? 'x': '-', name, out) - if v:shell_error + if error call add(s:update.errors, name) call s:regress_bar() silent execute pos 'd _' @@ -1126,7 +1330,12 @@ function! s:update_finish() endif endfunction -function! s:job_abort() +function! s:mark_aborted(name, message) + let attrs = { 'running': 0, 'error': 1, 'abort': 1, 'lines': [a:message] } + let s:jobs[a:name] = extend(get(s:jobs, a:name, {}), attrs) +endfunction + +function! s:job_abort(cancel) if (!s:nvim && !s:vim8) || !exists('s:jobs') return endif @@ -1138,10 +1347,20 @@ function! s:job_abort() silent! call job_stop(j.jobid) endif if j.new - call s:system('rm -rf ' . s:shellesc(g:plugs[name].dir)) + call s:rm_rf(g:plugs[name].dir) + endif + if a:cancel + call s:mark_aborted(name, 'Aborted') endif endfor - let s:jobs = {} + + if a:cancel + for todo in values(s:update.todo) + let todo.abort = 1 + endfor + else + let s:jobs = {} + endif endfunction function! s:last_non_empty_line(lines) @@ -1155,6 +1374,16 @@ function! s:last_non_empty_line(lines) return '' endfunction +function! s:bullet_for(job, ...) + if a:job.running + return a:job.new ? '+' : '*' + endif + if get(a:job, 'abort', 0) + return '~' + endif + return a:job.error ? 'x' : get(a:000, 0, '-') +endfunction + function! s:job_out_cb(self, data) abort let self = a:self let data = remove(self.lines, -1) . a:data @@ -1163,9 +1392,10 @@ function! s:job_out_cb(self, data) abort " 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) + if len(result) + call s:log(s:bullet_for(self), self.name, result) + endif endif endfunction @@ -1178,35 +1408,34 @@ endfunction function! s:job_cb(fn, job, ch, data) if !s:plug_window_exists() " plug window closed - return s:job_abort() + return s:job_abort(0) endif call call(a:fn, [a:job, a:data]) endfunction function! s:nvim_cb(job_id, data, event) dict abort - return a:event == 'stdout' ? + 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': [''], - \ 'batchfile': (s:is_win && (s:nvim || s:vim8)) ? tempname().'.bat' : '', - \ 'new': get(a:opts, 'new', 0) } +function! s:spawn(name, spec, queue, opts) + let job = { 'name': a:name, 'spec': a:spec, 'running': 1, 'error': 0, 'lines': [''], + \ 'new': get(a:opts, 'new', 0), 'queue': copy(a:queue) } + let Item = remove(job.queue, 0) + let argv = type(Item) == s:TYPE.funcref ? call(Item, [a:spec]) : Item let s:jobs[a:name] = job - let cmd = has_key(a:opts, 'dir') ? s:with_cd(a:cmd, a:opts.dir) : a:cmd - if !empty(job.batchfile) - call writefile(['@echo off', cmd], job.batchfile) - let cmd = job.batchfile - endif - let argv = add(s:is_win ? ['cmd', '/c'] : ['sh', '-c'], cmd) if s:nvim + if has_key(a:opts, 'dir') + let job.cwd = a:opts.dir + endif call extend(job, { \ 'on_stdout': function('s:nvim_cb'), + \ 'on_stderr': function('s:nvim_cb'), \ 'on_exit': function('s:nvim_cb'), \ }) - let jid = jobstart(argv, job) + let jid = s:plug_call('jobstart', argv, job) if jid > 0 let job.jobid = jid else @@ -1216,9 +1445,16 @@ function! s:spawn(name, cmd, opts) \ 'Invalid arguments (or job table is full)'] endif elseif s:vim8 + let cmd = join(map(copy(argv), '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' @@ -1229,30 +1465,33 @@ function! s:spawn(name, cmd, opts) let job.lines = ['Failed to start job'] endif else - let job.lines = s:lines(call('s:system', [cmd])) + let job.lines = s:lines(call('s:system', has_key(a:opts, 'dir') ? [argv, a:opts.dir] : [argv])) let job.error = v:shell_error != 0 let job.running = 0 endif endfunction function! s:reap(name) - let job = s:jobs[a:name] + let job = remove(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 more = len(get(job, 'queue', [])) let result = job.error ? join(job.lines, "\n") : s:last_non_empty_line(job.lines) - call s:log(bullet, a:name, empty(result) ? 'OK' : result) - call s:bar() - - if has_key(job, 'batchfile') && !empty(job.batchfile) - call delete(job.batchfile) + if len(result) + call s:log(s:bullet_for(job), a:name, result) + endif + + if !job.error && more + let job.spec.queue = job.queue + let s:update.todo[a:name] = job.spec + else + let s:update.bar .= s:bullet_for(job, '=') + call s:bar() endif - call remove(s:jobs, a:name) endfunction function! s:bar() @@ -1266,9 +1505,10 @@ function! s:bar() endfunction function! s:logpos(name) - for i in range(4, line('$')) + let max = line('$') + for i in range(4, max > 4 ? max : 4) if getline(i) =~# '^[-+x*] '.a:name.':' - for j in range(i + 1, line('$')) + for j in range(i + 1, max > 5 ? max : 5) if getline(j) !~ '^ ' return [i, j - 1] endif @@ -1304,6 +1544,16 @@ function! s:update_vim() call s:tick() endfunction +function! s:checkout_command(spec) + let a:spec.branch = s:git_origin_branch(a:spec) + return ['git', 'checkout', '-q', a:spec.branch, '--'] +endfunction + +function! s:merge_command(spec) + let a:spec.branch = s:git_origin_branch(a:spec) + return ['git', 'merge', '--ff-only', 'origin/'.a:spec.branch] +endfunction + function! s:tick() let pull = s:update.pull let prog = s:progress_opt(s:nvim || s:vim8) @@ -1318,18 +1568,39 @@ while 1 " Without TCO, Vim stack is bound to explode let name = keys(s:update.todo)[0] let spec = remove(s:update.todo, name) - let new = !isdirectory(spec.dir) + if get(spec, 'abort', 0) + call s:mark_aborted(name, 'Skipped') + call s:reap(name) + continue + endif - call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...') - redraw + let queue = get(spec, 'queue', []) + let new = empty(globpath(spec.dir, '.git', 1)) + + if empty(queue) + call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...') + redraw + endif let has_tag = has_key(spec, 'tag') - if !new + if len(queue) + call s:spawn(name, spec, queue, { 'dir': spec.dir }) + elseif !new let [error, _] = s:git_validate(spec, 0) if empty(error) if pull - let fetch_opt = (has_tag && !empty(globpath(spec.dir, '.git/shallow'))) ? '--depth 99999999' : '' - call s:spawn(name, printf('git fetch %s %s 2>&1', fetch_opt, prog), { 'dir': spec.dir }) + let cmd = s:git_version_requirement(2) ? ['git', '-c', 'credential.helper=', 'fetch'] : ['git', 'fetch'] + if has_tag && !empty(globpath(spec.dir, '.git/shallow')) + call extend(cmd, ['--depth', '99999999']) + endif + if !empty(prog) + call add(cmd, prog) + endif + let queue = [cmd, split('git remote set-head origin -a')] + if !has_tag && !has_key(spec, 'commit') + call extend(queue, [function('s:checkout_command'), function('s:merge_command')]) + endif + call s:spawn(name, spec, queue, { 'dir': spec.dir }) else let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 } endif @@ -1337,12 +1608,14 @@ while 1 " Without TCO, Vim stack is bound to explode let s:jobs[name] = { 'running': 0, 'lines': s:lines(error), 'error': 1 } endif else - call s:spawn(name, - \ printf('git clone %s %s %s %s 2>&1', - \ has_tag ? '' : s:clone_opt, - \ prog, - \ s:shellesc(spec.uri), - \ s:shellesc(s:trim(spec.dir))), { 'new': 1 }) + 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, spec, [extend(cmd, [spec.uri, s:trim(spec.dir)]), function('s:checkout_command'), function('s:merge_command')], { 'new': 1 }) endif if !s:jobs[name].running @@ -1379,7 +1652,7 @@ G_NVIM = vim.eval("has('nvim')") == '1' G_PULL = vim.eval('s:update.pull') == '1' G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1 G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)')) -G_CLONE_OPT = vim.eval('s:clone_opt') +G_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() @@ -1916,7 +2189,7 @@ function! s:update_ruby() end } if VIM::evaluate('s:mac_gui') == 1 - clone_opt = VIM::evaluate('s:clone_opt') + clone_opt = VIM::evaluate('s:clone_opt').join(' ') progress = VIM::evaluate('s:progress_opt(1)') nthr.times do mtx.synchronize do @@ -1969,19 +2242,45 @@ function! s:update_ruby() EOF endfunction -function! s:shellesc_cmd(arg) - let escaped = substitute(a:arg, '[&|<>()@^]', '^&', 'g') - let escaped = substitute(escaped, '%', '%%', 'g') - let escaped = substitute(escaped, '"', '\\^&', 'g') - let escaped = substitute(escaped, '\(\\\+\)\(\\^\)', '\1\1\2', 'g') - return '^"'.substitute(escaped, '\(\\\+\)$', '\1\1', '').'^"' +function! s:shellesc_cmd(arg, script) + let escaped = substitute('"'.a:arg.'"', '[&|<>()@^!"]', '^&', 'g') + return substitute(escaped, '%', (a:script ? '%' : '^') . '&', 'g') endfunction -function! s:shellesc(arg) - if &shell =~# 'cmd.exe$' - return s:shellesc_cmd(a:arg) +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 - return shellescape(a:arg) + 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 s:is_powershell(shell) + return s:shellesc_ps1(a:arg) + endif + return s:shellesc_sh(a:arg) endfunction function! s:glob_dir(path) @@ -2013,23 +2312,42 @@ function! s:format_message(bullet, name, message) endif endfunction -function! s:with_cd(cmd, dir) - return printf('cd%s %s && %s', s:is_win ? ' /d' : '', s:shellesc(a:dir), a:cmd) +function! s:with_cd(cmd, dir, ...) + let script = a:0 > 0 ? a:1 : 1 + let pwsh = s:is_powershell(&shell) + let cd = s:is_win && !pwsh ? 'cd /d' : 'cd' + let sep = pwsh ? ';' : '&&' + return printf('%s %s %s %s', cd, plug#shellescape(a:dir, {'script': script, 'shell': &shell}), sep, a:cmd) endfunction function! s:system(cmd, ...) + let batchfile = '' try let [sh, shellcmdflag, shrd] = s:chsh(1) - let cmd = a:0 > 0 ? s:with_cd(a:cmd, a:1) : a:cmd - if s:is_win - let batchfile = tempname().'.bat' - call writefile(['@echo off', cmd], batchfile) - let cmd = batchfile + 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 s:is_powershell(&shell) + let cmd = '& ' . cmd + endif + else + let cmd = a:cmd endif - return system(s:is_win ? '('.cmd.')' : cmd) + 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 + if s:is_win && filereadable(batchfile) call delete(batchfile) endif endtry @@ -2043,18 +2361,17 @@ 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 result = [s:git_local_branch(a:spec.dir), s:git_origin_url(a:spec.dir)] let remote = result[-1] - if v:shell_error + if empty(remote) let err = join([remote, 'PlugClean required.'], "\n") elseif !s:compare_git_uri(remote, a:spec.uri) let err = join(['Invalid URI: '.remote, \ 'Expected: '.a:spec.uri, \ 'PlugClean required.'], "\n") elseif a:check_branch && has_key(a:spec, 'commit') - let result = s:lines(s:system('git rev-parse HEAD 2>&1', a:spec.dir)) - let sha = result[-1] - if v:shell_error + let sha = s:git_revision(a:spec.dir) + if empty(sha) let err = join(add(result, 'PlugClean required.'), "\n") elseif !s:hash_match(sha, a:spec.commit) let err = join([printf('Invalid HEAD (expected: %s, actual: %s)', @@ -2062,8 +2379,9 @@ function! s:git_validate(spec, check_branch) \ 'PlugUpdate required.'], "\n") endif elseif a:check_branch - let branch = result[0] + let current_branch = result[0] " Check tag + let origin_branch = s:git_origin_branch(a:spec) if has_key(a:spec, 'tag') let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir) if a:spec.tag !=# tag && a:spec.tag !~ '\*' @@ -2071,25 +2389,29 @@ function! s:git_validate(spec, check_branch) \ (empty(tag) ? 'N/A' : tag), a:spec.tag) endif " Check branch - elseif a:spec.branch !=# branch + elseif origin_branch !=# current_branch let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.', - \ branch, a:spec.branch) + \ current_branch, origin_branch) endif if empty(err) - let [ahead, behind] = split(s:lastline(s:system(printf( - \ 'git rev-list --count --left-right HEAD...origin/%s', - \ a:spec.branch), a:spec.dir)), '\t') - if !v:shell_error && ahead - if behind + let ahead_behind = split(s:lastline(s:system([ + \ 'git', 'rev-list', '--count', '--left-right', + \ printf('HEAD...origin/%s', origin_branch) + \ ], a:spec.dir)), '\t') + if v:shell_error || len(ahead_behind) != 2 + let err = "Failed to compare with the origin. The default branch might have changed.\nPlugClean required." + else + let [ahead, behind] = ahead_behind + if ahead && 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 + \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', origin_branch, ahead, behind) + elseif ahead let err = printf("Ahead of origin/%s by %d commit(s).\n" \ .'Cannot update until local changes are pushed.', - \ a:spec.branch, ahead) + \ origin_branch, ahead) endif endif endif @@ -2102,7 +2424,9 @@ endfunction function! s:rm_rf(dir) if isdirectory(a:dir) - call s:system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . s:shellesc(a:dir)) + return s:system(s:is_win + \ ? 'rmdir /S /Q '.plug#shellescape(a:dir) + \ : ['rm', '-rf', a:dir]) endif endfunction @@ -2116,7 +2440,7 @@ function! s:clean(force) let errs = {} let [cnt, total] = [0, len(g:plugs)] for [name, spec] in items(g:plugs) - if !s:is_managed(name) + if !s:is_managed(name) || get(spec, 'frozen', 0) call add(dirs, spec.dir) else let [err, clean] = s:git_validate(spec, 1) @@ -2134,7 +2458,7 @@ function! s:clean(force) let allowed = {} for dir in dirs - let allowed[s:dirpath(fnamemodify(dir, ':h:h'))] = 1 + 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 @@ -2184,6 +2508,7 @@ 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:]) @@ -2192,11 +2517,22 @@ function! s:delete(range, force) let answer = force ? 1 : s:ask('Delete '.line[2:].'?', 1) let force = force || answer > 1 if answer - call s:rm_rf(line[2:]) + let err = s:rm_rf(line[2:]) setlocal modifiable - call setline(l1, '~'.line[1:]) - let s:clean_count += 1 - call setline(4, printf('Removed %d directories.', s:clean_count)) + 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 @@ -2207,11 +2543,11 @@ endfunction function! s:upgrade() echo 'Downloading the latest version of vim-plug' redraw - let tmp = tempname() + let tmp = s:plug_tempname() let new = tmp . '/plug.vim' try - let out = s:system(printf('git clone --depth 1 %s %s', s:plug_src, tmp)) + 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 @@ -2336,34 +2672,41 @@ function! s:preview_commit() let sha = matchstr(getline('.'), '^ \X*\zs[0-9a-f]\{7,9}') if empty(sha) - return + let name = matchstr(getline('.'), '^- \zs[^:]*\ze:$') + if empty(name) + return + endif + let title = 'HEAD@{1}..' + let command = 'git diff --no-color HEAD@{1}' + else + let title = sha + let command = 'git show --no-color --pretty=medium '.sha + let name = s:find_name(line('.')) 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 + if !s:is_preview_window_open() + execute get(g:, 'plug_pwindow', 'vertical rightbelow new') + execute 'e' title else - execute 'pedit' sha + execute 'pedit' title wincmd P endif - setlocal previewwindow filetype=git buftype=nofile nobuflisted modifiable + setlocal previewwindow filetype=git buftype=nofile bufhidden=wipe nobuflisted modifiable + let batchfile = '' try let [sh, shellcmdflag, shrd] = s:chsh(1) - let cmd = 'cd '.s:shellesc(g:plugs[name].dir).' && git show --no-color --pretty=medium '.sha + let cmd = 'cd '.plug#shellescape(g:plugs[name].dir).' && '.command if s:is_win - let batchfile = tempname().'.bat' - call writefile(['@echo off', cmd], batchfile) - let cmd = batchfile + let [batchfile, cmd] = s:batchfile(cmd) endif execute 'silent %!' cmd finally let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] - if s:is_win + if s:is_win && filereadable(batchfile) call delete(batchfile) endif endtry @@ -2406,12 +2749,23 @@ function! s:diff() 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 diff = s:system_chomp('git log --graph --color=never '.join(map(['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range], 's:shellesc(v:val)')), 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 + let branch = s:git_origin_branch(v) + if len(branch) + let range = origin ? '..origin/'.branch : 'HEAD@{1}..' + let cmd = ['git', 'log', '--graph', '--color=never'] + if s:git_version_requirement(2, 10, 0) + call add(cmd, '--no-show-signature') + endif + call extend(cmd, ['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range]) + if has_key(v, 'rtp') + call extend(cmd, ['--', v.rtp]) + endif + let diff = s:system_chomp(cmd, v.dir) + if !empty(diff) + let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : '' + call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)'))) + let cnts[origin] += 1 + endif endif let bar .= '=' call s:progress_bar(2, bar, len(total)) @@ -2426,8 +2780,13 @@ function! s:diff() \ . (cnts[1] ? printf(' %d plugin(s) have pending updates.', cnts[1]) : '')) if cnts[0] || cnts[1] - nnoremap :silent! call preview_commit() - nnoremap o :silent! call preview_commit() + nnoremap (plug-preview) :silent! call preview_commit() + if empty(maparg("\", 'n')) + nmap (plug-preview) + endif + if empty(maparg('o', 'n')) + nmap o (plug-preview) + endif endif if cnts[0] nnoremap X :call revert() @@ -2448,7 +2807,7 @@ function! s:revert() return endif - call s:system('git reset --hard HEAD@{1} && git checkout '.s:esc(g:plugs[name].branch).' --', g:plugs[name].dir) + 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 @@ -2466,9 +2825,9 @@ function! s:snapshot(force, ...) abort 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)'))) + \'has_key(v:val, "uri") && isdirectory(v:val.dir)'))) for name in reverse(names) - let sha = s:system_chomp('git rev-parse --short HEAD', g:plugs[name].dir) + let sha = has_key(g:plugs[name], 'commit') ? g:plugs[name].commit : s:git_revision(g:plugs[name].dir) if !empty(sha) call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha)) redraw @@ -2476,7 +2835,7 @@ function! s:snapshot(force, ...) abort endfor if a:0 > 0 - let fn = expand(a:1) + let fn = s:plug_expand(a:1) if filereadable(fn) && !(a:force || s:ask(a:1.' already exists. Overwrite?')) return endif diff --git a/vim/.vimrc b/vim/.vimrc index 23f1e37..b5c728f 100644 --- a/vim/.vimrc +++ b/vim/.vimrc @@ -1,18 +1,19 @@ " ==================== Plugins ==================== call plug#begin() -" Airline -Plug 'bling/vim-airline' - " Git integration Plug 'tpope/vim-fugitive' Plug 'airblade/vim-gitgutter' -" Gruvbox theme -Plug 'morhetz/gruvbox' +" Nord Theme +Plug 'nordtheme/vim' + +" Airline +Plug 'bling/vim-airline' +let g:airline_powerline_fonts = 1 " Commenter -Plug 'scrooloose/nerdcommenter' +" Plug 'scrooloose/nerdcommenter' " Sayonara Plug 'mhinz/vim-sayonara', { 'on': 'Sayonara' } @@ -20,8 +21,6 @@ Plug 'mhinz/vim-sayonara', { 'on': 'Sayonara' } " Go support for Vim "Plug 'fatih/vim-go', { 'do': ':GoInstallBinaries' } -" Python Stuff -" Plug 'davidhalter/jedi-vim' if has('nvim') Plug 'Shougo/deoplete.nvim', { 'do': ':UpdateRemotePlugins' } else @@ -30,28 +29,35 @@ else Plug 'roxma/vim-hug-neovim-rpc' endif let g:deoplete#enable_at_startup = 1 + " Python source for deoplete Plug 'zchee/deoplete-jedi', { 'for': 'python' } " Vim source for deoplete Plug 'Shougo/neco-vim', { 'for': 'vim' } -"{{ Python-related plugins +" Python-related plugins " Python completion, goto definition etc. -Plug 'davidhalter/jedi-vim', { 'for': 'python' } +" Plug 'davidhalter/jedi-vim', { 'for': 'python' } " Python syntax highlighting and more -Plug 'numirias/semshi', { 'do': ':UpdateRemotePlugins' } +" Plug 'numirias/semshi', { 'do': ':UpdateRemotePlugins' } " Python auto formatting -Plug 'psf/black', { 'branch': 'stable' } -autocmd BufWritePre *.py execute ':Black' +" Plug 'psf/black', { 'branch': 'stable' } +" autocmd BufWritePre *.py execute ':Black' " Smart Line Numbers Plug 'myusuf3/numbers.vim' Plug 'tpope/vim-commentary' +" Ale +Plug 'dense-analysis/ale' + +" OpenSCAD Support +Plug 'salkin-mada/openscad.nvim' + call plug#end() " ==================== Vim Settings ==================== @@ -139,7 +145,7 @@ endif " has("autocmd") "let g:dracula_colorterm = 0 "silent! colorscheme dracula set background=dark -silent! colorscheme gruvbox +silent! colorscheme nord " ==================== Code Folding ==================== @@ -186,6 +192,9 @@ map q: :q " Allow saving of files as sudo when I forgot to start vim using sudo. cmap w!! w !sudo tee > /dev/null % +" Disable F1 key +nmap + " Go shortcuts just for .go files autocmd FileType go nmap r (go-run) autocmd FileType go nmap t (go-test)