From be71e93eebd7354750bdf4ef6975e436d631bb55 Mon Sep 17 00:00:00 2001 From: dctrud Date: Fri, 17 Nov 2017 12:52:10 -0600 Subject: [PATCH] Squashed down --- .gitignore | 14 + .gitmodules | 3 + LICENSE-MIT | 22 + README.md | 8 + bin/dotfiles | 329 +++ bin/eachdir | 91 + bin/git-jump | 182 ++ bin/gpr | 280 +++ bin/isip | 26 + bin/manh | 43 + bin/manp | 35 + bin/org_convert | 4 + bin/pid | 23 + bin/rename | 310 +++ bin/resample-dpi | 38 + bin/ssid | 26 + conf/manh/styles.css | 37 + copy/.gitconfig | 20 + copy/.muttrc | 205 ++ copy/.ssh/config | 7 + init/10_osx_xcode.sh | 9 + init/20_osx_homebrew.sh | 39 + init/30_osx_homebrew_casks.sh | 59 + init/30_osx_homebrew_recipes.sh | 41 + init/50_vim.sh | 8 + link/.Xresources | 26 + link/.bash_profile | 4 + link/.bashrc | 30 + link/.dircolors | 475 ++++ link/.gitignore_global | 65 + link/.minttyrc | 24 + link/.screenrc | 15 + link/.spacemacs | 537 ++++ link/.vim/autoload/plug.vim | 2504 +++++++++++++++++++ link/.vim/autoload/plug.vim.old | 2019 +++++++++++++++ link/.vim/colors/monokai.vim | 109 + link/.vim/nerdtree_plugin/expanderrific.vim | 46 + link/.vim/plugin/bclose.vim | 75 + link/.vimrc | 233 ++ manual/Package Control.sublime-settings | 36 + manual/Preferences.sublime-settings | 27 + source/00_dotfiles.sh | 2 + source/10_user_bin.sh | 3 + source/45_linuxbrew.sh | 17 + source/50_anaconda.sh | 15 + source/50_dircolors.sh | 4 + source/50_editor.sh | 5 + source/50_file.sh | 42 + source/50_history.sh | 18 + source/50_misc.sh | 22 + source/50_osx.sh | 16 + source/50_prompt.sh | 2 + source/50_pyenv.sh | 15 + source/50_rbenv.sh | 16 + source/99_biohpc_only.sh | 54 + vendor/rename/README.txt | 6 + vendor/rename/rename | 310 +++ 57 files changed, 8631 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 LICENSE-MIT create mode 100644 README.md create mode 100755 bin/dotfiles create mode 100755 bin/eachdir create mode 100755 bin/git-jump create mode 100755 bin/gpr create mode 100755 bin/isip create mode 100755 bin/manh create mode 100755 bin/manp create mode 100755 bin/org_convert create mode 100755 bin/pid create mode 100755 bin/rename create mode 100755 bin/resample-dpi create mode 100755 bin/ssid create mode 100644 conf/manh/styles.css create mode 100644 copy/.gitconfig create mode 100644 copy/.muttrc create mode 100644 copy/.ssh/config create mode 100644 init/10_osx_xcode.sh create mode 100644 init/20_osx_homebrew.sh create mode 100644 init/30_osx_homebrew_casks.sh create mode 100644 init/30_osx_homebrew_recipes.sh create mode 100644 init/50_vim.sh create mode 100644 link/.Xresources create mode 100755 link/.bash_profile create mode 100644 link/.bashrc create mode 100644 link/.dircolors create mode 100644 link/.gitignore_global create mode 100644 link/.minttyrc create mode 100644 link/.screenrc create mode 100644 link/.spacemacs create mode 100644 link/.vim/autoload/plug.vim create mode 100644 link/.vim/autoload/plug.vim.old create mode 100644 link/.vim/colors/monokai.vim create mode 100644 link/.vim/nerdtree_plugin/expanderrific.vim create mode 100644 link/.vim/plugin/bclose.vim create mode 100644 link/.vimrc create mode 100644 manual/Package Control.sublime-settings create mode 100644 manual/Preferences.sublime-settings create mode 100644 source/00_dotfiles.sh create mode 100644 source/10_user_bin.sh create mode 100644 source/45_linuxbrew.sh create mode 100644 source/50_anaconda.sh create mode 100644 source/50_dircolors.sh create mode 100644 source/50_editor.sh create mode 100644 source/50_file.sh create mode 100644 source/50_history.sh create mode 100644 source/50_misc.sh create mode 100644 source/50_osx.sh create mode 100644 source/50_prompt.sh create mode 100644 source/50_pyenv.sh create mode 100644 source/50_rbenv.sh create mode 100644 source/99_biohpc_only.sh create mode 100644 vendor/rename/README.txt create mode 100755 vendor/rename/rename diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dc470f1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# Lots of junk goes in here. +/caches/ +/backups/ + +# Top secret stuff! +/link/.ssh/authorized_keys +/link/.ssh/id_* +/link/.ssh/known_hosts + +# Don't commit Vim plugins, vim-plug will install them. +/link/.vim/plugged + +# Ignore any file with LOCAL in the name (good for per-machine vars/functions) +*LOCAL* diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..6d96bbe --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "vendor/bash-git-prompt"] + path = vendor/bash-git-prompt + url = https://github.com/magicmonty/bash-git-prompt.git diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..1056fb5 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,22 @@ +Copyright (c) 2014 "Cowboy" Ben Alman + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..fba146e --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# Dave's dotfiles + +A customized version of https://github.com/cowboy/dotfiles + +## Original License +Copyright (c) 2014 "Cowboy" Ben Alman +Licensed under the MIT license. + diff --git a/bin/dotfiles b/bin/dotfiles new file mode 100755 index 0000000..7c269c7 --- /dev/null +++ b/bin/dotfiles @@ -0,0 +1,329 @@ +#!/usr/bin/env bash +[[ "$1" == "source" ]] || \ + +echo 'Dotfiles - "Cowboy" Ben Alman - http://benalman.com/' + +if [[ "$1" == "-h" || "$1" == "--help" ]]; then cat < /dev/null)" =~ Ubuntu ]] || return 1 +} +function get_os() { + for os in osx ubuntu; do + is_$os; [[ $? == ${1:-0} ]] && echo $os + done +} + +# Remove an entry from $PATH +# Based on http://stackoverflow.com/a/2108540/142339 +function path_remove() { + local arg path + path=":$PATH:" + for arg in "$@"; do path="${path//:$arg:/:}"; done + path="${path%:}" + path="${path#:}" + echo "$path" +} + +# Display a fancy multi-select menu. +# Inspired by http://serverfault.com/a/298312 +function prompt_menu() { + local exitcode prompt choices nums i n + exitcode=0 + if [[ "$2" ]]; then + _prompt_menu_draws "$1" + read -t $2 -n 1 -sp "To edit this list, press any key within $2 seconds. " + exitcode=$? + echo "" + fi 1>&2 + if [[ "$exitcode" == 0 ]]; then + prompt="Toggle options (Separate options with spaces, ENTER when done): " + while _prompt_menu_draws "$1" 1 && read -rp "$prompt" nums && [[ "$nums" ]]; do + _prompt_menu_adds $nums + done + fi 1>&2 + _prompt_menu_adds +} + +function _prompt_menu_iter() { + local i sel state + local fn=$1; shift + for i in "${!menu_options[@]}"; do + state=0 + for sel in "${menu_selects[@]}"; do + [[ "$sel" == "${menu_options[i]}" ]] && state=1 && break + done + $fn $state $i "$@" + done +} + +function _prompt_menu_draws() { + e_header "$1" + _prompt_menu_iter _prompt_menu_draw "$2" +} + +function _prompt_menu_draw() { + local modes=(error success) + if [[ "$3" ]]; then + e_${modes[$1]} "$(printf "%2d) %s\n" $(($2+1)) "${menu_options[$2]}")" + else + e_${modes[$1]} "${menu_options[$2]}" + fi +} + +function _prompt_menu_adds() { + _prompt_menu_result=() + _prompt_menu_iter _prompt_menu_add "$@" + menu_selects=("${_prompt_menu_result[@]}") +} + +function _prompt_menu_add() { + local state i n keep match + state=$1; shift + i=$1; shift + for n in "$@"; do + if [[ $n =~ ^[0-9]+$ ]] && (( n-1 == i )); then + match=1; [[ "$state" == 0 ]] && keep=1 + fi + done + [[ ! "$match" && "$state" == 1 || "$keep" ]] || return + _prompt_menu_result=("${_prompt_menu_result[@]}" "${menu_options[i]}") +} + +# Given strings containing space-delimited words A and B, "setdiff A B" will +# return all words in A that do not exist in B. Arrays in bash are insane +# (and not in a good way). +# From http://stackoverflow.com/a/1617303/142339 +function setdiff() { + local debug skip a b + if [[ "$1" == 1 ]]; then debug=1; shift; fi + if [[ "$1" ]]; then + local setdiffA setdiffB setdiffC + setdiffA=($1); setdiffB=($2) + fi + setdiffC=() + for a in "${setdiffA[@]}"; do + skip= + for b in "${setdiffB[@]}"; do + [[ "$a" == "$b" ]] && skip=1 && break + done + [[ "$skip" ]] || setdiffC=("${setdiffC[@]}" "$a") + done + [[ "$debug" ]] && for a in setdiffA setdiffB setdiffC; do + echo "$a ($(eval echo "\${#$a[*]}")) $(eval echo "\${$a[*]}")" 1>&2 + done + [[ "$1" ]] && echo "${setdiffC[@]}" +} + +# If this file was being sourced, exit now. +[[ "$1" == "source" ]] && return + + +########################################### +# INTERNAL DOTFILES "INIT" VARS / FUNCTIONS +########################################### + +# Initialize. +init_file=$DOTFILES/caches/init/selected +function init_files() { + local i f dirname oses os opt remove + dirname="$(dirname "$1")" + f=("$@") + menu_options=(); menu_selects=() + for i in "${!f[@]}"; do menu_options[i]="$(basename "${f[i]}")"; done + if [[ -e "$init_file" ]]; then + # Read cache file if possible + IFS=$'\n' read -d '' -r -a menu_selects < "$init_file" + else + # Otherwise default to all scripts not specifically for other OSes + oses=($(get_os 1)) + for opt in "${menu_options[@]}"; do + remove= + for os in "${oses[@]}"; do + [[ "$opt" =~ (^|[^a-z])$os($|[^a-z]) ]] && remove=1 && break + done + [[ "$remove" ]] || menu_selects=("${menu_selects[@]}" "$opt") + done + fi + prompt_menu "Run the following init scripts?" $prompt_delay + # Write out cache file for future reading. + rm "$init_file" 2>/dev/null + for i in "${!menu_selects[@]}"; do + echo "${menu_selects[i]}" >> "$init_file" + echo "$dirname/${menu_selects[i]}" + done +} +function init_do() { + e_header "Sourcing $(basename "$2")" + source "$2" +} + +# Copy files. +function copy_header() { e_header "Copying files into home directory"; } +function copy_test() { + if [[ -e "$2" && ! "$(cmp "$1" "$2" 2> /dev/null)" ]]; then + echo "same file" + elif [[ "$1" -ot "$2" ]]; then + echo "destination file newer" + fi +} +function copy_do() { + e_success "Copying ~/$1." + cp "$2" ~/ +} + +# Link files. +function link_header() { e_header "Linking files into home directory"; } +function link_test() { + [[ "$1" -ef "$2" ]] && echo "same file" +} +function link_do() { + e_success "Linking ~/$1." + ln -sf ${2#$HOME/} ~/ +} + +# Copy, link, init, etc. +function do_stuff() { + local base dest skip + local files=($DOTFILES/$1/*) + [[ $(declare -f "$1_files") ]] && files=($($1_files "${files[@]}")) + # No files? abort. + if (( ${#files[@]} == 0 )); then return; fi + # Run _header function only if declared. + [[ $(declare -f "$1_header") ]] && "$1_header" + # Iterate over files. + for file in "${files[@]}"; do + base="$(basename $file)" + dest="$HOME/$base" + # Run _test function only if declared. + if [[ $(declare -f "$1_test") ]]; then + # If _test function returns a string, skip file and print that message. + skip="$("$1_test" "$file" "$dest")" + if [[ "$skip" ]]; then + e_error "Skipping ~/$base, $skip." + continue + fi + # Destination file already exists in ~/. Back it up! + if [[ -e "$dest" ]]; then + e_arrow "Backing up ~/$base." + # Set backup flag, so a nice message can be shown at the end. + backup=1 + # Create backup dir if it doesn't already exist. + [[ -e "$backup_dir" ]] || mkdir -p "$backup_dir" + # Backup file / link / whatever. + mv "$dest" "$backup_dir" + fi + fi + # Do stuff. + "$1_do" "$base" "$file" + done +} + +# Enough with the functions, let's do stuff. + +export prompt_delay=5 + +# Ensure that we can actually, like, compile anything. +if [[ ! "$(type -P gcc)" ]] && is_osx; then + e_error "XCode or the Command Line Tools for XCode must be installed first." + exit 1 +fi + +# If Git is not installed, install it (Ubuntu only, since Git comes standard +# with recent XCode or CLT) +if [[ ! "$(type -P git)" ]] && is_ubuntu; then + e_header "Installing Git" + sudo apt-get -qq install git-core +fi + +# If Git isn't installed by now, something exploded. We gots to quit! +if [[ ! "$(type -P git)" ]]; then + e_error "Git should be installed. It isn't. Aborting." + exit 1 +fi + +# Initialize. +if [[ ! -d $DOTFILES ]]; then + # $DOTFILES directory doesn't exist? Clone it! + new_dotfiles_install=1 + prompt_delay=15 + e_header "Downloading dotfiles" + git clone --recursive git://github.com/${github_user:-cowboy}/dotfiles.git $DOTFILES + cd $DOTFILES +elif [[ "$1" != "restart" ]]; then + # Make sure we have the latest files. + e_header "Updating dotfiles" + cd $DOTFILES + prev_head="$(git rev-parse HEAD)" + git pull + git submodule update --init --recursive --quiet + if [[ "$(git rev-parse HEAD)" != "$prev_head" ]]; then + e_header "Changes detected, restarting script" + exec "$0" "restart" + fi +fi + +# Add binaries into the path +[[ -d $DOTFILES/bin ]] && PATH=$DOTFILES/bin:$PATH +export PATH + +# Tweak file globbing. +shopt -s dotglob +shopt -s nullglob + +# Create caches dir and init subdir, if they don't already exist. +mkdir -p "$DOTFILES/caches/init" + +# If backups are needed, this is where they'll go. +backup_dir="$DOTFILES/backups/$(date "+%Y_%m_%d-%H_%M_%S")/" +backup= + +# Execute code for each file in these subdirectories. +do_stuff "copy" +do_stuff "link" +do_stuff "init" + +# Alert if backups were made. +if [[ "$backup" ]]; then + echo -e "\nBackups were moved to ~/${backup_dir#$HOME/}" +fi + +# All done! +e_header "All done!" diff --git a/bin/eachdir b/bin/eachdir new file mode 100755 index 0000000..4528a24 --- /dev/null +++ b/bin/eachdir @@ -0,0 +1,91 @@ +#!/usr/bin/env bash + +function _eachdir() { + +if [[ "$1" == "-h" || "$1" == "--help" ]]; then cat <&1 )" + if [[ "$output" ]]; then + # If the command had output, display a header and that output. + echo -e "${h1}${d}${h2}\n$output\n" + else + # Otherwise push it onto an array for later display. + nops=("${nops[@]}" "$d") + fi +done + +# List any dirs that had no output. +if [[ ${#nops[@]} > 0 ]]; then + echo "${h1}no output from${h2}" + for d in "${nops[@]}"; do echo "$d"; done +fi + +} + +# By putting the above code inside a function, if this file is sourced (which +# is required for external aliases/functions to be used as commands), vars +# can be local and return can be used to exit. +_eachdir "$@" diff --git a/bin/git-jump b/bin/git-jump new file mode 100755 index 0000000..3dbc6b7 --- /dev/null +++ b/bin/git-jump @@ -0,0 +1,182 @@ +#!/usr/bin/env bash + +function help() { +cat </dev/null + # If the commit was successful, tag it (overwriting any previous tag) + if [[ $? == 0 ]]; then + status="*" + git tag -f "git-jump-$head_sha" >/dev/null + fi + echo "Previous HEAD was $head_sha$status, $(git_commit_subject $head_sha)" +} + +# Restore previously-saved changes +function restore() { + local status="" + # Save current changes before restoring + save + # Attempt to restore saved changes for specified commit + git checkout "git-jump-$1" 2>/dev/null + if [[ $? == 0 ]]; then + # If the restore was successful, figure out exactly what was saved, check + # out the original commit, then restore the saved changes on top of it + status="*" + local patch="$(git format-patch HEAD^ --stdout)" + git checkout HEAD^ 2>/dev/null + echo "$patch" | git apply - + else + # Otherwise, just restore the original commit + git checkout "$1" 2>/dev/null + fi + echo "HEAD is now $1$status, $(git_commit_subject $1)" +} + +# Clean (permanently) current changes and remove the current saved tag +function clean() { + local head_sha=$(git_head_sha) + git tag -d "git-jump-$head_sha" &>/dev/null + if [[ $? == 0 ]]; then + echo "Removed stored data for commit $head_sha." + fi + local repo_root="$(git_repo_toplevel)" + git reset HEAD "$repo_root" >/dev/null + git clean -f -d -q -- "$repo_root" >/dev/null + git checkout -- "$repo_root" >/dev/null + echo "Unstaged changes and untracked files removed." +} + +# Remove (permanently) all saved tags +function clean_all_tags() { + git for-each-ref refs/tags --format='%(refname:short)' | \ + while read tag; do + if [[ "$tag" =~ ^git-jump- ]]; then + git tag -d "$tag" + fi + done +} + +# Jump to next commit +function next() { + local next_sha=$(git_next_sha) + if [[ "$next_sha" == "$(git_head_sha)" ]]; then + # Abort if no more commits + echo "Already at last commit in $git_branch. Congratulations!" + else + # Checkout branch by name if at its HEAD + if [[ "$next_sha" == "$(git_branch_sha)" ]]; then + next_sha="$git_branch" + fi + echo "Jumping ahead to next commit." + restore $next_sha + fi +} + +# Jump to previous commit +function prev() { + local prev_sha=$(git_prev_sha) + if [[ "$prev_sha" == "$(git_head_sha)" ]]; then + # Abort if no more commits + echo "Already at first commit in $git_branch." + else + echo "Jumping back to previous commit." + restore $prev_sha + fi +} + +# Show help if requested +if [[ "$1" == "--help" || "$1" == "-h" ]]; then + help + exit +fi + +# Check if branch is valid +git rev-parse "$git_branch" &>/dev/null +if [[ $? != 0 ]]; then + echo "Error: Branch \"$git_branch\" does not appear to be valid." + echo "Try $(basename "$0") --help for more information." + exit 1 +fi + +# Handle CLI arguments +if [[ "$1" == "next" ]]; then + next +elif [[ "$1" == "prev" ]]; then + prev +elif [[ "$1" == "clean" ]]; then + clean +elif [[ "$1" == "cleanall" ]]; then + clean_all_tags + clean +else + usage + exit 1 +fi diff --git a/bin/gpr b/bin/gpr new file mode 100755 index 0000000..9fef290 --- /dev/null +++ b/bin/gpr @@ -0,0 +1,280 @@ +#!/usr/bin/env bash + +function help() { + pr=123 + local_ref=foo + script="$(basename "$0") $pr" + cat </dev/null + git config --global --add gpr.token $token + echo "Authorization successful, token saved." + exit + else + echo "Error authorizing with GitHub, please try again." + exit 5 + fi +fi + +pr="$1"; shift +script="$(basename "$0") $pr" +branch="pr$pr" + +repo="$(git remote show -n origin | perl -ne '/Fetch URL: .*github\.com[:\/](.*\/.*)\.git/ && print $1')" + +# Let's fetch some JSON. +token="$(git config --get gpr.token)" +json="$(curl -fsSL "https://api.github.com/repos/$repo/pulls/$pr?access_token=$token" 2>/dev/null)" +if [[ $? != 0 || ! "$json" ]]; then + echo "Error fetching GitHub API data for $repo PR $pr!" + echo "If you're trying to access a private repo and haven't yet done so, please run" + echo "the \"$(basename "$0") auth\" command to generate a GitHub auth token." + exit 2 +fi + +# Let's parse some JSON. +remote_url="$(node -pe "($json).head.repo.git_url")" +remote_ref="$(node -pe "($json).head.ref")" +local_url="$(node -pe "($json).base.repo.git_url")" +local_ref="$(node -pe "($json).base.ref")" +num_commits="$(node -pe "($json).commits")" + +# Let's get the project's .git folder. +git_dir="$(git rev-parse --show-toplevel)/.git" + +function del_branch() { + if [[ "$(git branch | grep " $1\$")" ]]; then + git checkout "$local_ref" 2>/dev/null + git branch -D "$1" 2>/dev/null + fi +} + +# Use the specified step, otherwise attempt to auto-detect it. +if [[ "$1" ]]; then + step=$1 +elif [[ "$(git branch | grep " $branch-squash\$")" ]]; then + # STEP 3 should never auto-execute twice. + if [[ "$(git branch --contains "$(git rev-parse $branch-squash)" | grep " $local_ref\$")" ]]; then + echo "Error merging branch \"$branch-squash\" into \"$local_ref\" branch! (already done)" + echo + echo "Redo the last step with: $script 3" + exit 4 + fi + step=3 +elif [[ "$(git branch | grep " $branch\$")" ]]; then + step=2 +else + step=1 +fi + +# Let's do some stuff. +if [[ $step == 1 ]]; then + header 1 + + # Clean up any prior work on this PR. + del_branch "$branch" + del_branch "$branch-squash" + + # Fetch remote, create a branch, etc. + if [[ "$remote_url" == "$local_url" ]]; then + git fetch origin "$remote_ref" + else + git fetch "$remote_url" "$remote_ref" + fi + git checkout -b "$branch" FETCH_HEAD + + # Save ref to last PR author commit for later use + git tag --force "_${branch}_author_head" FETCH_HEAD + + # Rebase! + git rebase "$local_ref" + if [[ $? != 0 ]]; then + echo "Error while attempting rebase!" + exit 3 + fi + + echo + echo "Changed files in HEAD~$num_commits:" + git --no-pager diff --name-only HEAD~"$num_commits" + + echo + echo "-=[ Next Steps ]=============================================================-" + echo "$(help_step2 1) with: $script" + echo " Or redo the current step with: $script 1" + +elif [[ $step == 2 ]]; then + header 2 + + # Clean up any prior squashes for this PR. + del_branch "$branch-squash" + + # Create branch and squash merge all commits. + git checkout -b "$branch-squash" "$local_ref" + git merge --squash "$branch" + + # Append useful information to commit message. + squash_msg_file="$git_dir/SQUASH_MSG" + echo -e "\nCloses gh-$pr." >> "$squash_msg_file" + + # Retrieve author name and email from stored commit, and commit. + author="$(git log "_${branch}_author_head" -n1 --format="%an <%ae>")" + git commit --author="$author" + + echo + echo "-=[ Next Steps ]=============================================================-" + echo "$(help_step3 1) with: $script" + echo " Or redo the current step with: $script 2" + +elif [[ $step == 3 ]]; then + header 3 + + # Actually merge squashed commits into branch. + git checkout "$local_ref" + git merge "$branch-squash" + + echo + echo "-=[ Next Steps ]=============================================================-" + echo "$(help_step4 1) with: $script 4" + echo " Or redo the current step with: $script 3" + +elif [[ $step == 4 ]]; then + header 4 + + del_branch "$branch" + del_branch "$branch-squash" + git tag -d "_${branch}_author_head" 2>/dev/null + + echo + echo "All done." +fi diff --git a/bin/isip b/bin/isip new file mode 100755 index 0000000..692cd44 --- /dev/null +++ b/bin/isip @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +if [[ "$1" == "-h" || "$1" == "--help" ]]; then cat </dev/null && cat > "$file" < + + + +$(man "$@" 2>/dev/null | man2html -bare -nodepage) + + +EOF + +# Open HTML (if it does exist). +[[ -e "$file" ]] && open "$file" diff --git a/bin/manp b/bin/manp new file mode 100755 index 0000000..8377578 --- /dev/null +++ b/bin/manp @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +if [[ "$1" == "-h" || "$1" == "--help" ]]; then cat </dev/null 2>&1 + +# Open PDF (if it does exist). +[[ -e "$file" ]] && open "$file" diff --git a/bin/org_convert b/bin/org_convert new file mode 100755 index 0000000..0311300 --- /dev/null +++ b/bin/org_convert @@ -0,0 +1,4 @@ +#!/bin/sh +# Export all .org to GFM and dokuwiki via pandoc +find ~/Org/ -iname "*.org" -type f -exec sh -c 'pandoc "${0}" -t "markdown_github" --atx-headers -o "${0%.org}.md"' {} \; +find ~/Org/ -iname "*.org" -type f -exec sh -c 'pandoc "${0}" -t "dokuwiki" -o "${0%.org}.dokuwiki"' {} \; diff --git a/bin/pid b/bin/pid new file mode 100755 index 0000000..4af4155 --- /dev/null +++ b/bin/pid @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +if [[ "$1" == "-h" || "$1" == "--help" ]]; then cat < +S> + +F +S> + +F +S> +S> +S> +S> +S> +S> +S> +S> +S> +S> +S> + +=head1 DESCRIPTION + +C renames the filenames supplied according to the rules specified. If a given filename is not modified, it will not be renamed. If no filenames are given on the command line, filenames will be read via standard input. + +For example, to rename all files matching C<*.bak> to strip the extension, you might say + + rename 's/\.bak$//' *.bak + +If are confident that none of the filenames has C<.bak> anywhere else than at the end, you can also use the much easier typed + + rename -s .bak '' *.bak + +You can always do multiple changes in one ago: + + rename -s .tgz .tar.gz -s .tbz2 .tar.bz2 *.tar.* + +Note however that expressive options are order sensitive. The following would probably surprise you: + + rename -s foo bar -s bar baz * + +Since operations are cumulative, this would end up substituting (some of) the F matches in filenames with F! So pay attention to order. You may want to request a verbose dry run with C<-nv> for the first stab at a complex rename operation. + + rename -nv -s bar baz -s foo bar * + +You can combine the various expressive options to suit your needs. F.ex files from Microsoft(tm) Windows systems often have blanks and (sometimes nothing but) capital letters in their names. Let's say you have a bunch of such files to clean up, and you also want to move them to subdirectories based on extension. The following command should help, provided all directories already exist: + + rename -cz -e '$_ = "$1/$_" if /(\..*)\z/' * + +Again you need to pay attention to order sensitivity for expressive options. If you placed the C<-c> after the C<-e> in the above example, files with F<.zip> and F<.ZIP> extensions would be (attempted to be) moved to different directories because the directory name prefix would be added before the filenames were normalized. Once again, use verbose dry run requested using C<-nv> to get an idea of what exactly a complex rename operation is going to do. + +=head1 ARGUMENTS + +=over 4 + +=item B<-h>, B<--help> + +See a synopsis. + +=item B<--man> + +Browse the manpage. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<-0>, B<--null> + +When reading file names from C, split on NUL bytes instead of newlines. This is useful in combination with GNU find's C<-print0> option, GNU grep's C<-Z> option, and GNU sort's C<-z> option, to name just a few. B + +=item B<-c>, B<--lower-case> + +Converts file names to all lower case. + +=item B<-C>, B<--upper-case> + +Converts file names to all upper case. + +=item B<-e>, B<--expr> + +The C argument to this option should be a Perl expression that assumes the filename in the C<$_> variable and modifies it for the filenames to be renamed. When no other C<-c>, C<-C>, C<-e>, C<-s>, or C<-z> options are given, you can omit the C<-e> from infront of the code. + +=item B<-g>, B<--glob> + +Glob filename arguments. This is useful if you're using a braindead shell such as F which won't expand wildcards on behalf of the user. + +=item B<-f>, B<--force> + +Rename even when a file with the destination name already exists. + +=item B<-i>, B<--interactive> + +Ask the user to confirm every action before it is taken. + +=item B<-k>, B<--backwards>, B<--reverse-order> + +Process the list of files in reverse order, last file first. This prevents conflicts when renaming files to names which are currently taken but would be freed later during the process of renaming. + +=item B<-l>, B<--symlink> + +Create symlinks from the new names to the existing ones, instead of renaming the files. B.> + +=item B<-L>, B<--hardlink> + +Create hard links from the new names to the existing ones, instead of renaming the files. B.> + +=item B<-n>, B<--dry-run>, B<--just-print> + +Show how the files would be renamed, but don't actually do anything. + +=item B<-s>, B<--subst>, B<--simple> + +Perform a simple textual substitution of C to C. The C and C parameters must immediately follow the argument. + +Quoting issues aside, this is equivalent to supplying a C<-e 's/\Qfrom/to/'>. + +=item B<-v>, B<--verbose> + +Print additional information about the operations (not) executed. + +=item B<-z>, B<--sanitize> + +Replaces consecutive blanks, shell meta characters, and control characters in filenames with underscores. + +=back + +=head1 SEE ALSO + +mv(1), perl(1), find(1), grep(1), sort(1) + +=head1 BUGS + +None currently known. + +=head1 AUTHORS + +Aristotle Pagaltzis + +Idea, inspiration and original code from Larry Wall and Robin Barker. + +=head1 COPYRIGHT + +This script is free software; you can redistribute it and/or modify it under the same terms as Perl itself. + +=cut + +use Pod::Usage; +use Getopt::Long 2.24, qw(:config bundling no_ignore_case no_auto_abbrev); + +use constant ERROR => do { bless \(my $l = 0), 'LOGLEVEL' }; +use constant INFO => do { bless \(my $l = 1), 'LOGLEVEL' }; +use constant DEBUG => do { bless \(my $l = 2), 'LOGLEVEL' }; +use constant VERB_FOR => { + link => { + inf => 'link', + pastp => 'linked', + exec => sub { link shift, shift or die }, + }, + symlink => { + inf => 'symlink', + pastp => 'symlinked', + exec => sub { symlink shift, shift or die }, + }, + rename => { + inf => 'rename', + pastp => 'renamed', + exec => sub { rename shift, shift or die }, + }, +}; + +sub argv_to_subst_expr { + my $modifier = shift || ''; + pod2usage( -verbose => 1 ) if @ARGV < 2; + my ($from, $to) = map quotemeta, splice @ARGV, 0, 2; + # the ugly \${\""} construct is necessary because unknown backslash escapes are + # not treated the same in pattern- vs doublequote-quoting context; only the + # latter lets us do the right thing with problematic input like + # ']{ool(haracter$' or maybe '>>' + sprintf 's/\Q${\"%s"}/%s/%s', $from, $to, $modifier; +} + +my @EXPR; + +GetOptions( + 'h|help' => sub { pod2usage( -verbose => 1 ) }, + 'man' => sub { pod2usage( -verbose => 2 ) }, + '0|null' => \my $opt_null, + 'c|lower-case' => sub { push @EXPR, 's/([[:upper:]]+)/\L$1/g' }, + 'C|upper-case' => sub { push @EXPR, 's/([[:lower:]]+)/\U$1/g' }, + 'e|expr=s' => \@EXPR, + 'f|force' => \my $opt_force, + 'g|glob' => \my $opt_glob, + 'i|interactive' => \my $opt_interactive, + 'k|backwards|reverse-order' => \my $opt_backwards, + 'l|symlink' => \my $opt_symlink, + 'L|hardlink' => \my $opt_hardlink, + 'n|just-print|dry-run' => \my $opt_dryrun, + 'p|mkpath|make-dirs' => \my $opt_mkpath, + 'v|verbose+' => \(my $opt_verbose = 0), + 'z|sanitize' => sub { push @EXPR, 's/[!"\$&()=?`*\';<>|_[:cntrl:][:blank:]]+/_/g' }, + 's|subst|simple' => sub { push @EXPR, argv_to_subst_expr }, + 'S|subst-global' => sub { push @EXPR, argv_to_subst_expr('g') }, +) or pod2usage( -verbose => 1 ); + +die "TODO" if $opt_mkpath; + +if(not @EXPR) { + pod2usage( -verbose => 1 ) if not @ARGV or -e $ARGV[0]; + push @EXPR, shift; +} + +pod2usage( -verbose => 1 ) + if ($opt_hardlink and $opt_symlink) + or ($opt_null and @ARGV); + +++$opt_verbose if $opt_dryrun; + +BEGIN { + *CORE::GLOBAL::warn = sub { + if(ref $_[0] eq 'LOGLEVEL') { + my $msglevel = ${(shift)}; + print "@_\n" if $opt_verbose >= $msglevel; + return; + } + warn @_; + }; +} + +my $code = do { + my $cat = "sub { ".join('; ', @EXPR)." }"; + warn DEBUG, "Using expression: $cat"; + + my $evaled = eval $cat; + die $@ if $@; + die "Evaluation to subref failed. Check expression using -vn\n" + unless 'CODE' eq ref $evaled; + + $evaled; +}; + +my $verb = VERB_FOR->{ + $opt_hardlink ? 'link' : + $opt_symlink ? 'symlink' : + do { 'rename' } +}; + +if (!@ARGV) { + warn INFO, "Reading filenames from STDIN"; + @ARGV = do { + if($opt_null) { + warn INFO, "Splitting on NUL bytes"; + local $/ = "\0"; + } + ; + }; + chomp @ARGV; +} + +@ARGV = map glob, @ARGV if $opt_glob; + +@ARGV = reverse @ARGV if $opt_backwards; + +for (@ARGV) { + my $old = $_; + + $code->(); + + if($old eq $_) { + warn DEBUG, "'$old' unchanged"; + next; + } + + if(!$opt_force and -e) { + warn ERROR, "'$old' not $verb->{pastp}: '$_' already exists"; + next; + } + + if($opt_dryrun) { + warn INFO, "'$old' would be $verb->{pastp} to '$_'"; + next; + } + + if($opt_interactive) { + print "\u$verb->{inf} '$old' to '$_'? [n] "; + if( !~ /^y(?:es)?$/i) { + warn DEBUG, "Skipping '$old'."; + next; + } + } + + eval { $verb->{exec}($old, $_) }; + + if($@) { + warn ERROR, "Can't $verb->{inf} '$old' to '$_': $!"; + next; + } + + warn INFO, "'$old' $verb->{pastp} to '$_'"; +} diff --git a/bin/resample-dpi b/bin/resample-dpi new file mode 100755 index 0000000..e7736a5 --- /dev/null +++ b/bin/resample-dpi @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +if [[ ! "$1" || "$1" == "-h" || "$1" == "--help" ]]; then cat </dev/null 2>&1 +done diff --git a/bin/ssid b/bin/ssid new file mode 100755 index 0000000..776d70b --- /dev/null +++ b/bin/ssid @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +if [[ "$1" == "-h" || "$1" == "--help" ]]; then cat < hr, +body > address { + display: none; +} diff --git a/copy/.gitconfig b/copy/.gitconfig new file mode 100644 index 0000000..4b08baa --- /dev/null +++ b/copy/.gitconfig @@ -0,0 +1,20 @@ +[user] + name = David Trudgian + email = EMAIL +[core] + excludesfile = ~/.gitignore_global +[color] + ui = auto +[color "branch"] + current = yellow reverse + local = yellow + remote = green +[color "diff"] + meta = yellow bold + frag = magenta bold + old = red bold + new = green bold +[color "status"] + added = yellow + changed = green + untracked = cyan diff --git a/copy/.muttrc b/copy/.muttrc new file mode 100644 index 0000000..bd79219 --- /dev/null +++ b/copy/.muttrc @@ -0,0 +1,205 @@ +# Server Options +# -------------- +set spoolfile="~/Maildir/INBOX" +set folder="~/Maildir" +set smtp_url="" +set from="" +set realname="" + +# Paths +# ----- +#set header_cache=~/.mutt/cache/headers +set message_cachedir=~/.mutt/cache/bodies + +# Mailboxes +# --------- +set record="=Sent Items" +set postponed="=Drafts" +set trash="=Trash" +mailboxes =INBOX =INBOX.Tickets =INBOX.Lists + +# Signatures +# ---------- +set sig_dashes +set signature="~/.signature" + +# EMACS +# ----- +set editor = emacsclient + +# Status Bar +# ---------- +set status_chars=" *%A" +set status_format="───[ Folder: %f ]───[%r%m messages%?n? (%n new)?%?d? (%d to delete)?%?t? (%t tagged)? ]───%>─%?p?( %p postponed )?───" + +# Message Index +# ------------- +set sort='threads' +set sort_aux = reverse-last-date-received +set uncollapse_jump +set date_format="%e %b %Y %H:%M" +set index_format="%3C|%Z|%d| %-20.20F (%-4.4c) %s" + +# Message Viewing +# --------------- +set pipe_decode +set thorough_search +auto_view text/html +alternative_order text/plain text/enriched text/html +ignore * +unignore from: to: cc: date: subject: +unhdr_order * +hdr_order from: to: cc: date: subject: + +# Composing +# --------- +set edit_headers +set fast_reply +set askcc +set fcc_attach +unset mime_forward +set forward_format="Fwd: %s" +set forward_decode +set attribution="On %d, %n wrote:" +set reply_to +set reverse_name +set include +set forward_quote + +set pager_index_lines=10 +set pager_context=3 +set pager_stop +set menu_scroll +set tilde +unset markers + +set quote_regexp = "^( {0,4}[>|:#%]| {0,4}[a-z0-9]+[>|]+)+" + +macro pager \Cu "|urlview" "call urlview to open links" + +# Don't ask for everything +# ------------------------ +set delete +unset confirmappend +set quit + +# Sidebar Patch +# ------------- +set sidebar_visible = yes +set sidebar_width = 20 +bind index,pager CP sidebar-next +bind index,pager CN sidebar-prev +bind index,pager CO sidebar-open +set sidebar_short_path = yes +set sidebar_delim_chars = '/.' +set sidebar_folder_indent = yes +set sidebar_indent_string = ' ' +set sidebar_divider_char = '|' +set mail_check_stats +set sidebar_format = '%B%?F? [%F]?%* %?N?%N/?%S' + +# Notifications for new mail +set new_mail_command="notify-send 'New Email in %f' '%n new messages, %u unread.' &" + +# Compose a new email (not a reply) to the sender +bind index,pager @ compose-to-sender + +# for background in 16 color terminal, valid background colors include: +# base03, bg, black, any of the non brights + +# basic colors --------------------------------------------------------- +color normal brightyellow default +color error red default +color tilde black default +color message cyan default +color markers red white +color attachment white default +color search brightmagenta default +#color status J_black J_status +color status brightyellow black +color indicator brightblack yellow +color tree yellow default # arrow in threads + +# basic monocolor screen +mono bold bold +mono underline underline +mono indicator reverse +mono error bold + +# index ---------------------------------------------------------------- +color index red default "~A" # all messages +color index brightred default "~E" # expired messages +color index blue default "~N" # new messages +color index blue default "~O" # old messages +color index brightmagenta default "~Q" # messages that have been replied to +color index brightgreen default "~R" # read messages +color index blue default "~U" # unread messages +color index blue default "~U~$" # unread, unreferenced messages +color index brightyellow default "~v" # messages part of a collapsed thread +color index brightyellow default "~P" # messages from me +color index cyan default "~p!~F" # messages to me +color index cyan default "~N~p!~F" # new messages to me +color index cyan default "~U~p!~F" # unread messages to me +color index brightgreen default "~R~p!~F" # messages to me +color index red default "~F" # flagged messages +color index red default "~F~p" # flagged messages to me +color index red default "~N~F" # new flagged messages +color index red default "~N~F~p" # new flagged messages to me +color index red default "~U~F~p" # new flagged messages to me +color index black red "~D" # deleted messages +color index brightcyan default "~v~(!~N)" # collapsed thread with no unread +color index yellow default "~v~(~N)" # collapsed thread with some unread +color index green default "~N~v~(~N)" # collapsed thread with unread parent +# statusbg used to indicated flagged when foreground color shows other status +# for collapsed thread +color index red black "~v~(~F)!~N" # collapsed thread with flagged, no unread +color index yellow black "~v~(~F~N)" # collapsed thread with some unread & flagged +color index green black "~N~v~(~F~N)" # collapsed thread with unread parent & flagged +color index green black "~N~v~(~F)" # collapsed thread with unread parent, no unread inside, but some flagged +color index cyan black "~v~(~p)" # collapsed thread with unread parent, no unread inside, some to me directly +color index yellow red "~v~(~D)" # thread with deleted (doesn't differentiate between all or partial) + +# message headers ------------------------------------------------------ +#color header brightgreen default "^" +color hdrdefault brightgreen default +color header brightyellow default "^(From)" +color header blue default "^(Subject)" + +# body ----------------------------------------------------------------- +color quoted blue default +color quoted1 cyan default +color quoted2 yellow default +color quoted3 red default +color quoted4 brightred default + +color signature brightgreen default +color bold black default +color underline black default +color normal default default +# +color body brightcyan default "[;:][-o][)/(|]" # emoticons +color body brightcyan default "[;:][)(|]" # emoticons +color body brightcyan default "[*]?((N)?ACK|CU|LOL|SCNR|BRB|BTW|CWYL|\ + |FWIW|vbg|GD&R|HTH|HTHBE|IMHO|IMNSHO|\ + |IRL|RTFM|ROTFL|ROFL|YMMV)[*]?" +color body brightcyan default "[ ][*][^*]*[*][ ]?" # more emoticon? +color body brightcyan default "[ ]?[*][^*]*[*][ ]" # more emoticon? + +## pgp + +color body red default "(BAD signature)" +color body cyan default "(Good signature)" +color body brightblack default "^gpg: Good signature .*" +color body brightyellow default "^gpg: " +color body brightyellow red "^gpg: BAD signature from.*" +mono body bold "^gpg: Good signature" +mono body bold "^gpg: BAD signature from.*" + +# yes, an insance URL regex +color body red default "([a-z][a-z0-9+-]*://(((([a-z0-9_.!~*'();:&=+$,-]|%[0-9a-f][0-9a-f])*@)?((([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?|[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)(:[0-9]+)?)|([a-z0-9_.!~*'()$,;:@&=+-]|%[0-9a-f][0-9a-f])+)(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?(#([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?|(www|ftp)\\.(([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?(:[0-9]+)?(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?(#([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?)[^].,:;!)? \t\r\n<>\"]" +# and a heavy handed email regex +color body red default "((@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\]),)*@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\]):)?[0-9a-z_.+%$-]+@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\])" + +# Border lines. +color body blue default "( *[-+=#*~_]){6,}" + diff --git a/copy/.ssh/config b/copy/.ssh/config new file mode 100644 index 0000000..3336f6b --- /dev/null +++ b/copy/.ssh/config @@ -0,0 +1,7 @@ +Host * + +# Forward ssh agent to the remote machine. +ForwardAgent yes + +# Automatically add all common hosts to the host file as they are connected to. +StrictHostKeyChecking no diff --git a/init/10_osx_xcode.sh b/init/10_osx_xcode.sh new file mode 100644 index 0000000..0be6ca7 --- /dev/null +++ b/init/10_osx_xcode.sh @@ -0,0 +1,9 @@ +# OSX-only stuff. Abort if not OSX. +is_osx || return 1 + +# Some tools look for XCode, even though they don't need it. +# https://github.com/joyent/node/issues/3681 +# https://github.com/mxcl/homebrew/issues/10245 +if [[ ! -d "$('xcode-select' -print-path 2>/dev/null)" ]]; then + sudo xcode-select -switch /usr/bin +fi diff --git a/init/20_osx_homebrew.sh b/init/20_osx_homebrew.sh new file mode 100644 index 0000000..a65657a --- /dev/null +++ b/init/20_osx_homebrew.sh @@ -0,0 +1,39 @@ +# OSX-only stuff. Abort if not OSX. +is_osx || return 1 + +# Install Homebrew. +if [[ ! "$(type -P brew)" ]]; then + e_header "Installing Homebrew" + true | ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" +fi + +# Exit if, for some reason, Homebrew is not installed. +[[ ! "$(type -P brew)" ]] && e_error "Homebrew failed to install." && return 1 + +e_header "Updating Homebrew" +brew doctor +brew update + +# Functions used in subsequent init scripts. + +# Tap Homebrew kegs. +function brew_tap_kegs() { + kegs=($(setdiff "${kegs[*]}" "$(brew tap)")) + if (( ${#kegs[@]} > 0 )); then + e_header "Tapping Homebrew kegs: ${kegs[*]}" + for keg in "${kegs[@]}"; do + brew tap $keg + done + fi +} + +# Install Homebrew recipes. +function brew_install_recipes() { + recipes=($(setdiff "${recipes[*]}" "$(brew list)")) + if (( ${#recipes[@]} > 0 )); then + e_header "Installing Homebrew recipes: ${recipes[*]}" + for recipe in "${recipes[@]}"; do + brew install $recipe + done + fi +} diff --git a/init/30_osx_homebrew_casks.sh b/init/30_osx_homebrew_casks.sh new file mode 100644 index 0000000..e905138 --- /dev/null +++ b/init/30_osx_homebrew_casks.sh @@ -0,0 +1,59 @@ +# OSX-only stuff. Abort if not OSX. +is_osx || return 1 + +# Exit if Homebrew is not installed. +[[ ! "$(type -P brew)" ]] && e_error "Brew casks need Homebrew to install." && return 1 + +# Ensure the cask keg and recipe are installed. +kegs=(caskroom/cask) +brew_tap_kegs +recipes=(brew-cask) +brew_install_recipes + +# Exit if, for some reason, cask is not installed. +[[ ! "$(brew ls --versions brew-cask)" ]] && e_error "Brew-cask failed to install." && return 1 + +# Hack to show the first-run brew-cask password prompt immediately. +brew cask info this-is-somewhat-annoying 2>/dev/null + +# Homebrew casks +casks=( + # Applications + adobe-reader + boot2docker + firefox + google-chrome + iterm2 + macvim + remote-desktop-connection + spotify + vagrant + virtualbox +) + +# Install Homebrew casks. +casks=($(setdiff "${casks[*]}" "$(brew cask list 2>/dev/null)")) +if (( ${#casks[@]} > 0 )); then + e_header "Installing Homebrew casks: ${casks[*]}" + for cask in "${casks[@]}"; do + brew cask install $cask + done + brew cask cleanup +fi + +# Work around colorPicker symlink issue. +# https://github.com/caskroom/homebrew-cask/issues/7004 +cps=() +for f in ~/Library/ColorPickers/*.colorPicker; do + [[ -L "$f" ]] && cps=("${cps[@]}" "$f") +done + +if (( ${#cps[@]} > 0 )); then + e_header "Fixing colorPicker symlinks" + for f in "${cps[@]}"; do + target="$(readlink "$f")" + e_arrow "$(basename "$f")" + rm "$f" + cp -R "$target" ~/Library/ColorPickers/ + done +fi diff --git a/init/30_osx_homebrew_recipes.sh b/init/30_osx_homebrew_recipes.sh new file mode 100644 index 0000000..71c2f79 --- /dev/null +++ b/init/30_osx_homebrew_recipes.sh @@ -0,0 +1,41 @@ +# OSX-only stuff. Abort if not OSX. +is_osx || return 1 + +# Exit if Homebrew is not installed. +[[ ! "$(type -P brew)" ]] && e_error "Brew recipes need Homebrew to install." && return 1 + +# Homebrew recipes +recipes=( + ansible + bash + git + htop-osx + man2html + mercurial + ssh-copy-id +) + +brew_install_recipes + +# Misc cleanup! + +# This is where brew stores its binary symlinks +local binroot="$(brew --config | awk '/HOMEBREW_PREFIX/ {print $2}')"/bin + +# htop +if [[ "$(type -P $binroot/htop)" ]] && [[ "$(stat -L -f "%Su:%Sg" "$binroot/htop")" != "root:wheel" || ! "$(($(stat -L -f "%DMp" "$binroot/htop") & 4))" ]]; then + e_header "Updating htop permissions" + sudo chown root:wheel "$binroot/htop" + sudo chmod u+s "$binroot/htop" +fi + +# bash +if [[ "$(type -P $binroot/bash)" && "$(cat /etc/shells | grep -q "$binroot/bash")" ]]; then + e_header "Adding $binroot/bash to the list of acceptable shells" + echo "$binroot/bash" | sudo tee -a /etc/shells >/dev/null +fi +if [[ "$(dscl . -read ~ UserShell | awk '{print $2}')" != "$binroot/bash" ]]; then + e_header "Making $binroot/bash your default shell" + sudo chsh -s "$binroot/bash" "$USER" >/dev/null 2>&1 + e_arrow "Please exit and restart all your shells." +fi diff --git a/init/50_vim.sh b/init/50_vim.sh new file mode 100644 index 0000000..6e8575b --- /dev/null +++ b/init/50_vim.sh @@ -0,0 +1,8 @@ +# Backups, swaps and undos are stored here. +mkdir -p $DOTFILES/caches/vim + +# Download Vim plugins. +if [[ "$(type -P vim)" ]]; then + vim +PlugUpgrade +PlugUpdate +qall +fi + diff --git a/link/.Xresources b/link/.Xresources new file mode 100644 index 0000000..55c268d --- /dev/null +++ b/link/.Xresources @@ -0,0 +1,26 @@ +!Fonts +Xterm*faceName: Source Code Pro,Source Code Pro Semibold +XTerm*faceSize: 14 + +UXterm*faceName: Source Code Pro, Source Code Pro Semibold +UXterm*faceSize: 14 + +!Colors +*foreground: #000000 +*background: #ffffff +*color0: #2E3436 +*color1: #CC0000 +*color2: #4E9A06 +*color3: #C4A000 +*color4: #3465A4 +*color5: #75507B +*color6: #06989A +*color7: #D3D7CF +*color8: #555753 +*color9: #EF2929 +*color10: #8AE234 +*color11: #FCE94F +*color12: #729FCF +*color13: #AD7FA8 +*color14: #34E2E2 +*color15: #EEEEEC diff --git a/link/.bash_profile b/link/.bash_profile new file mode 100755 index 0000000..4e413b4 --- /dev/null +++ b/link/.bash_profile @@ -0,0 +1,4 @@ +if [ -f ~/.bashrc ]; then + source ~/.bashrc +fi + diff --git a/link/.bashrc b/link/.bashrc new file mode 100644 index 0000000..6473808 --- /dev/null +++ b/link/.bashrc @@ -0,0 +1,30 @@ +if [ -z "$PS1" ]; then + return +fi + + +# Where the magic happens. +export DOTFILES=~/.dotfiles + +# Add binaries into the path +PATH=$DOTFILES/bin:$PATH +export PATH + +# Source all files in "source" +function src() { + local file + if [[ "$1" ]]; then + source "$DOTFILES/source/$1.sh" + else + for file in $DOTFILES/source/*; do + source "$file" + done + fi +} + +# Run dotfiles script, then source. +function dotfiles() { + $DOTFILES/bin/dotfiles "$@" && src +} + +src diff --git a/link/.dircolors b/link/.dircolors new file mode 100644 index 0000000..72e82db --- /dev/null +++ b/link/.dircolors @@ -0,0 +1,475 @@ +# Exact Solarized Dark color theme for the color GNU ls utility. +# Designed for dircolors (GNU coreutils) 5.97 +# +# This simple theme was simultaneously designed for these terminal color schemes: +# - Solarized dark (best) +# - Solarized light +# - default dark +# - default light +# with a slight optimization for Solarized Dark. +# +# How the colors were selected: +# - Terminal emulators often have an option typically enabled by default that makes +# bold a different color. It is important to leave this option enabled so that +# you can access the entire 16-color Solarized palette, and not just 8 colors. +# - We favor universality over a greater number of colors. So we limit the number +# of colors so that this theme will work out of the box in all terminals, +# Solarized or not, dark or light. +# - We choose to have the following category of files: +# NORMAL & FILE, DIR, LINK, EXEC and +# editable text including source, unimportant text, binary docs & multimedia source +# files, viewable multimedia, archived/compressed, and unimportant non-text +# - For uniqueness, we stay away from the Solarized foreground colors are -- either +# base00 (brightyellow) or base0 (brightblue). However, they can be used if +# you know what the bg/fg colors of your terminal are, in order to optimize the display. +# - 3 different options are provided: universal, solarized dark, and solarized light. +# The only difference between the universal scheme and one that's optimized for +# dark/light is the color of "unimportant" files, which should blend more with the +# background +# - We note that blue is the hardest color to see on dark bg and yellow is the hardest +# color to see on light bg (with blue being particularly bad). So we choose yellow +# for multimedia files which are usually accessed in a GUI folder browser anyway. +# And blue is kept for custom use of this scheme's user. +# - See table below to see the assignments. + + +# Installation instructions: +# This file goes in the /etc directory, and must be world readable. +# You can copy this file to .dir_colors in your $HOME directory to override +# the system defaults. + +# COLOR needs one of these arguments: 'tty' colorizes output to ttys, but not +# pipes. 'all' adds color characters to all output. 'none' shuts colorization +# off. +COLOR tty + +# Below, there should be one TERM entry for each termtype that is colorizable +TERM ansi +TERM color_xterm +TERM color-xterm +TERM con132x25 +TERM con132x30 +TERM con132x43 +TERM con132x60 +TERM con80x25 +TERM con80x28 +TERM con80x30 +TERM con80x43 +TERM con80x50 +TERM con80x60 +TERM cons25 +TERM console +TERM cygwin +TERM dtterm +TERM dvtm +TERM dvtm-256color +TERM Eterm +TERM eterm-color +TERM fbterm +TERM gnome +TERM gnome-256color +TERM jfbterm +TERM konsole +TERM konsole-256color +TERM kterm +TERM linux +TERM linux-c +TERM mach-color +TERM mlterm +TERM nxterm +TERM putty +TERM putty-256color +TERM rxvt +TERM rxvt-256color +TERM rxvt-cygwin +TERM rxvt-cygwin-native +TERM rxvt-unicode +TERM rxvt-unicode256 +TERM rxvt-unicode-256color +TERM screen +TERM screen-16color +TERM screen-16color-bce +TERM screen-16color-s +TERM screen-16color-bce-s +TERM screen-256color +TERM screen-256color-bce +TERM screen-256color-s +TERM screen-256color-bce-s +TERM screen-256color-italic +TERM screen-bce +TERM screen-w +TERM screen.linux +TERM screen.xterm-256color +TERM screen.xterm-new +TERM st +TERM st-meta +TERM st-256color +TERM st-meta-256color +TERM tmux +TERM tmux-256color +TERM vt100 +TERM xterm +TERM xterm-new +TERM xterm-16color +TERM xterm-256color +TERM xterm-256color-italic +TERM xterm-88color +TERM xterm-color +TERM xterm-debian +TERM xterm-termite + +# EIGHTBIT, followed by '1' for on, '0' for off. (8-bit output) +EIGHTBIT 1 + +############################################################################# +# Below are the color init strings for the basic file types. A color init +# string consists of one or more of the following numeric codes: +# +# Attribute codes: +# 00=none 01=bold 04=underscore 05=blink 07=reverse 08=concealed +# Text color codes: +# 30=black 31=red 32=green 33=yellow 34=blue 35=magenta 36=cyan 37=white +# Background color codes: +# 40=black 41=red 42=green 43=yellow 44=blue 45=magenta 46=cyan 47=white +# +# NOTES: +# - See http://www.oreilly.com/catalog/wdnut/excerpt/color_names.html +# - Color combinations +# ANSI Color code Solarized Notes Universal SolDark SolLight +# ~~~~~~~~~~~~~~~ ~~~~~~~~~ ~~~~~ ~~~~~~~~~ ~~~~~~~ ~~~~~~~~ +# 00 none NORMAL, FILE +# 30 black base02 +# 01;30 bright black base03 bg of SolDark +# 31 red red docs & mm src +# 01;31 bright red orange EXEC +# 32 green green editable text +# 01;32 bright green base01 unimportant text +# 33 yellow yellow unclear in light bg multimedia +# 01;33 bright yellow base00 fg of SolLight unimportant non-text +# 34 blue blue unclear in dark bg user customized +# 01;34 bright blue base0 fg in SolDark unimportant text +# 35 magenta magenta LINK +# 01;35 bright magenta violet archive/compressed +# 36 cyan cyan DIR +# 01;36 bright cyan base1 unimportant non-text +# 37 white base2 +# 01;37 bright white base3 bg in SolLight +# 05;37;41 unclear in Putty dark + + +### By file type + +# global default +NORMAL 00 +# normal file +FILE 00 +# directory +DIR 34 +# 777 directory +OTHER_WRITABLE 34;40 +# symbolic link +LINK 35 + +# pipe, socket, block device, character device (blue bg) +FIFO 30;44 +SOCK 35;44 +DOOR 35;44 # Solaris 2.5 and later +BLK 33;44 +CHR 37;44 + + +############################################################################# +### By file attributes + +# Orphaned symlinks (blinking white on red) +# Blink may or may not work (works on iTerm dark or light, and Putty dark) +ORPHAN 05;37;41 +# ... and the files that orphaned symlinks point to (blinking white on red) +MISSING 05;37;41 + +# files with execute permission +EXEC 01;31 # Unix +.cmd 01;31 # Win +.exe 01;31 # Win +.com 01;31 # Win +.bat 01;31 # Win +.reg 01;31 # Win +.app 01;31 # OSX + +############################################################################# +### By extension + +# List any file extensions like '.gz' or '.tar' that you would like ls +# to colorize below. Put the extension, a space, and the color init string. +# (and any comments you want to add after a '#') + +### Text formats + +# Text that we can edit with a regular editor +.txt 32 +.org 32 +.md 32 +.mkd 32 + +# Source text +.h 32 +.hpp 32 +.c 32 +.C 32 +.cc 32 +.cpp 32 +.cxx 32 +.objc 32 +.cl 32 +.sh 32 +.bash 32 +.csh 32 +.zsh 32 +.el 32 +.vim 32 +.java 32 +.pl 32 +.pm 32 +.py 32 +.rb 32 +.hs 32 +.php 32 +.htm 32 +.html 32 +.shtml 32 +.erb 32 +.haml 32 +.xml 32 +.rdf 32 +.css 32 +.sass 32 +.scss 32 +.less 32 +.js 32 +.coffee 32 +.man 32 +.0 32 +.1 32 +.2 32 +.3 32 +.4 32 +.5 32 +.6 32 +.7 32 +.8 32 +.9 32 +.l 32 +.n 32 +.p 32 +.pod 32 +.tex 32 +.go 32 +.sql 32 +.csv 32 + +### Multimedia formats + +# Image +.bmp 33 +.cgm 33 +.dl 33 +.dvi 33 +.emf 33 +.eps 33 +.gif 33 +.jpeg 33 +.jpg 33 +.JPG 33 +.mng 33 +.pbm 33 +.pcx 33 +.pdf 33 +.pgm 33 +.png 33 +.PNG 33 +.ppm 33 +.pps 33 +.ppsx 33 +.ps 33 +.svg 33 +.svgz 33 +.tga 33 +.tif 33 +.tiff 33 +.xbm 33 +.xcf 33 +.xpm 33 +.xwd 33 +.xwd 33 +.yuv 33 + +# Audio +.aac 33 +.au 33 +.flac 33 +.m4a 33 +.mid 33 +.midi 33 +.mka 33 +.mp3 33 +.mpa 33 +.mpeg 33 +.mpg 33 +.ogg 33 +.opus 33 +.ra 33 +.wav 33 + +# Video +.anx 33 +.asf 33 +.avi 33 +.axv 33 +.flc 33 +.fli 33 +.flv 33 +.gl 33 +.m2v 33 +.m4v 33 +.mkv 33 +.mov 33 +.MOV 33 +.mp4 33 +.mp4v 33 +.mpeg 33 +.mpg 33 +.nuv 33 +.ogm 33 +.ogv 33 +.ogx 33 +.qt 33 +.rm 33 +.rmvb 33 +.swf 33 +.vob 33 +.webm 33 +.wmv 33 + +### Misc + +# Binary document formats and multimedia source +.doc 31 +.docx 31 +.rtf 31 +.odt 31 +.dot 31 +.dotx 31 +.ott 31 +.xls 31 +.xlsx 31 +.ods 31 +.ots 31 +.ppt 31 +.pptx 31 +.odp 31 +.otp 31 +.fla 31 +.psd 31 + +# Archives, compressed +.7z 1;35 +.apk 1;35 +.arj 1;35 +.bin 1;35 +.bz 1;35 +.bz2 1;35 +.cab 1;35 # Win +.deb 1;35 +.dmg 1;35 # OSX +.gem 1;35 +.gz 1;35 +.iso 1;35 +.jar 1;35 +.msi 1;35 # Win +.rar 1;35 +.rpm 1;35 +.tar 1;35 +.tbz 1;35 +.tbz2 1;35 +.tgz 1;35 +.tx 1;35 +.war 1;35 +.xpi 1;35 +.xz 1;35 +.z 1;35 +.Z 1;35 +.zip 1;35 + +# For testing +.ANSI-30-black 30 +.ANSI-01;30-brblack 01;30 +.ANSI-31-red 31 +.ANSI-01;31-brred 01;31 +.ANSI-32-green 32 +.ANSI-01;32-brgreen 01;32 +.ANSI-33-yellow 33 +.ANSI-01;33-bryellow 01;33 +.ANSI-34-blue 34 +.ANSI-01;34-brblue 01;34 +.ANSI-35-magenta 35 +.ANSI-01;35-brmagenta 01;35 +.ANSI-36-cyan 36 +.ANSI-01;36-brcyan 01;36 +.ANSI-37-white 37 +.ANSI-01;37-brwhite 01;37 + +############################################################################# +# Your customizations + +# Unimportant text files +# For universal scheme, use brightgreen 01;32 +# For optimal on light bg (but too prominent on dark bg), use white 01;34 +.log 01;32 +*~ 01;32 +*# 01;32 +#.log 01;34 +#*~ 01;34 +#*# 01;34 + +# Unimportant non-text files +# For universal scheme, use brightcyan 01;36 +# For optimal on dark bg (but too prominent on light bg), change to 01;33 +#.bak 01;36 +#.BAK 01;36 +#.old 01;36 +#.OLD 01;36 +#.org_archive 01;36 +#.off 01;36 +#.OFF 01;36 +#.dist 01;36 +#.DIST 01;36 +#.orig 01;36 +#.ORIG 01;36 +#.swp 01;36 +#.swo 01;36 +#*,v 01;36 +.bak 01;33 +.BAK 01;33 +.old 01;33 +.OLD 01;33 +.org_archive 01;33 +.off 01;33 +.OFF 01;33 +.dist 01;33 +.DIST 01;33 +.orig 01;33 +.ORIG 01;33 +.swp 01;33 +.swo 01;33 +*,v 01;33 + +# The brightmagenta (Solarized: purple) color is free for you to use for your +# custom file type +.gpg 34 +.gpg 34 +.pgp 34 +.asc 34 +.3des 34 +.aes 34 +.enc 34 +.sqlite 34 diff --git a/link/.gitignore_global b/link/.gitignore_global new file mode 100644 index 0000000..2fe63ab --- /dev/null +++ b/link/.gitignore_global @@ -0,0 +1,65 @@ +# Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.so + +# Packages # +############ +# It's better to unpack these files and commit the raw source because +# git has its own built in compression methods. +*.7z +*.jar +*.rar +*.zip +*.gz +*.bzip +*.xz +*.lzma + +#packing-only formats +*.iso +*.tar + +#package management formats +*.dmg +*.xpi +*.gem +*.egg +*.deb +*.rpm + +# Logs and databases # +###################### +*.log +*.sql +*.sqlite + +# OS generated files # +###################### +.DS_Store + +# Thumbnails +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes + +# Windows image file caches +Thumbs.db + +# Folder config file +Desktop.ini + +# NPM +.npmignore + +# Vim +.swp +.swo +.*.swp +.*.swo diff --git a/link/.minttyrc b/link/.minttyrc new file mode 100644 index 0000000..8a8cf94 --- /dev/null +++ b/link/.minttyrc @@ -0,0 +1,24 @@ +BoldAsFont=no +Font=Consolas +FontHeight=12 + +ForegroundColour=248,248,242 +BackgroundColour=39,40,34 +CursorColour=253,157,79 +Black=39,40,34 +BoldBlack=117,113,94 +Red=249,38,114 +BoldRed=204,6,78 +Green=166,226,46 +BoldGreen=122,172,24 +Yellow=244,191,117 +BoldYellow=240,169,69 +Blue=102,217,239 +BoldBlue=33,199,233 +Magenta=174,129,255 +BoldMagenta=126,51,255 +Cyan=161,239,228 +BoldCyan=95,227,210 +White=248,248,242 +BoldWhite=249,248,245 + diff --git a/link/.screenrc b/link/.screenrc new file mode 100644 index 0000000..a723066 --- /dev/null +++ b/link/.screenrc @@ -0,0 +1,15 @@ +# the following two lines give a two-line status, with the current window highlighted +hardstatus alwayslastline +hardstatus string '%{= kG}[%{G}%H%? %1`%?%{g}][%= %{= kw}%-w%{+b yk} %n*%t%?(%u)%? %{-}%+w %=%{g}][%{B}%m/%d %{W}%C%A%{g}]' + +# huge scrollback buffer +defscrollback 5000 + +# 256 colors +attrcolor b ".I" +termcapinfo xterm 'Co#256:AB=\E[48;5;%dm:AF=\E[38;5;%dm' +defbce on + +# default windows +screen -t Local 0 bash + diff --git a/link/.spacemacs b/link/.spacemacs new file mode 100644 index 0000000..3ec3ec5 --- /dev/null +++ b/link/.spacemacs @@ -0,0 +1,537 @@ +;; -*- mode: emacs-lisp -*- +;; This file is loaded by Spacemacs at startup. +;; It must be stored in your home directory. + +(defun dotspacemacs/layers () + "Configuration Layers declaration. +You should not put any user code in this function besides modifying the variable +values." + (setq-default + ;; Base distribution to use. This is a layer contained in the directory + ;; `+distribution'. For now available distributions are `spacemacs-base' + ;; or `spacemacs'. (default 'spacemacs) + dotspacemacs-distribution 'spacemacs + ;; List of additional paths where to look for configuration layers. + ;; Paths must have a trailing slash (i.e. `~/.mycontribs/') + dotspacemacs-configuration-layer-path '() + ;; List of configuration layers to load. If it is the symbol `all' instead + ;; of a list then all discovered layers will be installed. + dotspacemacs-configuration-layers + '( + ;; ---------------------------------------------------------------- + ;; Example of useful layers you may want to use right away. + ;; Uncomment some layer names and press (Vim style) or + ;; (Emacs style) to install them. + ;; ---------------------------------------------------------------- + (auto-completion :variables + auto-completion-return-key-behavior nil + auto-completion-tab-key-behavior 'complete + ) + c-c++ + docker + django + emacs-lisp + (git :variables + git-gutter-use-fringe t) + html + (ibuffer :variables ibuffer-group-buffers-by 'projects) + java + javascript + markdown + org + puppet + python + (shell :variables + shell-default-shell 'ansi-term + shell-default-height 30 + shell-default-position 'bottom + ) + shell-scripts + spell-checking + syntax-checking + vagrant + version-control + windows-scripts + yaml + ;; private layers + tabbar + ) + ;; List of additional packages that will be installed without being + ;; wrapped in a layer. If you need some configuration for these + ;; packages, then consider creating a layer. You can also put the + ;; configuration in `dotspacemacs/user-config'. + dotspacemacs-additional-packages '( + groovy-mode + all-the-icons + dokuwiki-mode + ) + ;; A list of packages and/or extensions that will not be install and loaded. + dotspacemacs-excluded-packages '( + ) + ;; If non-nil spacemacs will delete any orphan packages, i.e. packages that + ;; are declared in a layer which is not a member of + ;; the list `dotspacemacs-configuration-layers'. (default t) + dotspacemacs-delete-orphan-packages t)) + +(defun dotspacemacs/init () + "Initialization function. +This function is called at the very startup of Spacemacs initialization +before layers configuration. +You should not put any user code in there besides modifying the variable +values." + ;; This setq-default sexp is an exhaustive list of all the supported + ;; spacemacs settings. + (setq-default + ;; If non nil ELPA repositories are contacted via HTTPS whenever it's + ;; possible. Set it to nil if you have no way to use HTTPS in your + ;; environment, otherwise it is strongly recommended to let it set to t. + ;; This variable has no effect if Emacs is launched with the parameter + ;; `--insecure' which forces the value of this variable to nil. + ;; (default t) + dotspacemacs-elpa-https t + ;; Maximum allowed time in seconds to contact an ELPA repository. + dotspacemacs-elpa-timeout 5 + ;; If non nil then spacemacs will check for updates at startup + ;; when the current branch is not `develop'. (default t) + dotspacemacs-check-for-update t + ;; One of `vim', `emacs' or `hybrid'. Evil is always enabled but if the + ;; variable is `emacs' then the `holy-mode' is enabled at startup. `hybrid' + ;; uses emacs key bindings for vim's insert mode, but otherwise leaves evil + ;; unchanged. (default 'vim) + dotspacemacs-editing-style 'vim + ;; If non nil output loading progress in `*Messages*' buffer. (default nil) + dotspacemacs-verbose-loading nil + ;; Specify the startup banner. Default value is `official', it displays + ;; the official spacemacs logo. An integer value is the index of text + ;; banner, `random' chooses a random text banner in `core/banners' + ;; directory. A string value must be a path to an image format supported + ;; by your Emacs build. + ;; If the value is nil then no banner is displayed. (default 'official) + dotspacemacs-startup-banner 'official + ;; List of items to show in the startup buffer. If nil it is disabled. + ;; Possible values are: `recents' `bookmarks' `projects'. + ;; (default '(recents projects)) + dotspacemacs-startup-lists '(recents projects) + ;; Number of recent files to show in the startup buffer. Ignored if + ;; `dotspacemacs-startup-lists' doesn't include `recents'. (default 5) + dotspacemacs-startup-recent-list-size 5 + ;; Default major mode of the scratch buffer (default `text-mode') + dotspacemacs-scratch-mode 'org-mode + ;; List of themes, the first of the list is loaded when spacemacs starts. + ;; Press T n to cycle to the next theme in the list (works great + ;; with 2 themes variants, one dark and one light) + dotspacemacs-themes '(monokai) + ;; If non nil the cursor color matches the state color in GUI Emacs. + dotspacemacs-colorize-cursor-according-to-state t + ;; Default font. `powerline-scale' allows to quickly tweak the mode-line + ;; size to make separators look not too crappy. + dotspacemacs-default-font '("Roboto Mono for Powerline" + :size 16 + :weight normal + :width normal + :powerline-scale 1.1) + ;; The leader key + dotspacemacs-leader-key "SPC" + ;; The leader key accessible in `emacs state' and `insert state' + ;; (default "M-m") + dotspacemacs-emacs-leader-key "M-m" + ;; Major mode leader key is a shortcut key which is the equivalent of + ;; pressing ` m`. Set it to `nil` to disable it. (default ",") + dotspacemacs-major-mode-leader-key "," + ;; Major mode leader key accessible in `emacs state' and `insert state'. + ;; (default "C-M-m) + dotspacemacs-major-mode-emacs-leader-key "C-M-m" + ;; These variables control whether separate commands are bound in the GUI to + ;; the key pairs C-i, TAB and C-m, RET. + ;; Setting it to a non-nil value, allows for separate commands under + ;; and TAB or and RET. + ;; In the terminal, these pairs are generally indistinguishable, so this only + ;; works in the GUI. (default nil) + dotspacemacs-distinguish-gui-tab nil + ;; (Not implemented) dotspacemacs-distinguish-gui-ret nil + ;; The command key used for Evil commands (ex-commands) and + ;; Emacs commands (M-x). + ;; By default the command key is `:' so ex-commands are executed like in Vim + ;; with `:' and Emacs commands are executed with ` :'. + dotspacemacs-command-key ":" + ;; If non nil `Y' is remapped to `y$'. (default t) + dotspacemacs-remap-Y-to-y$ t + ;; Name of the default layout (default "Default") + dotspacemacs-default-layout-name "Default" + ;; If non nil the default layout name is displayed in the mode-line. + ;; (default nil) + dotspacemacs-display-default-layout nil + ;; If non nil then the last auto saved layouts are resume automatically upon + ;; start. (default nil) + dotspacemacs-auto-resume-layouts nil + ;; Location where to auto-save files. Possible values are `original' to + ;; auto-save the file in-place, `cache' to auto-save the file to another + ;; file stored in the cache directory and `nil' to disable auto-saving. + ;; (default 'cache) + dotspacemacs-auto-save-file-location 'cache + ;; Maximum number of rollback slots to keep in the cache. (default 5) + dotspacemacs-max-rollback-slots 5 + ;; If non nil then `ido' replaces `helm' for some commands. For now only + ;; `find-files' (SPC f f), `find-spacemacs-file' (SPC f e s), and + ;; `find-contrib-file' (SPC f e c) are replaced. (default nil) + dotspacemacs-use-ido nil + ;; If non nil, `helm' will try to minimize the space it uses. (default nil) + dotspacemacs-helm-resize nil + ;; if non nil, the helm header is hidden when there is only one source. + ;; (default nil) + dotspacemacs-helm-no-header nil + ;; define the position to display `helm', options are `bottom', `top', + ;; `left', or `right'. (default 'bottom) + dotspacemacs-helm-position 'bottom + ;; If non nil the paste micro-state is enabled. When enabled pressing `p` + ;; several times cycle between the kill ring content. (default nil) + dotspacemacs-enable-paste-micro-state nil + ;; Which-key delay in seconds. The which-key buffer is the popup listing + ;; the commands bound to the current keystroke sequence. (default 0.4) + dotspacemacs-which-key-delay 0.4 + ;; Which-key frame position. Possible values are `right', `bottom' and + ;; `right-then-bottom'. right-then-bottom tries to display the frame to the + ;; right; if there is insufficient space it displays it at the bottom. + ;; (default 'bottom) + dotspacemacs-which-key-position 'bottom + ;; If non nil a progress bar is displayed when spacemacs is loading. This + ;; may increase the boot time on some systems and emacs builds, set it to + ;; nil to boost the loading time. (default t) + dotspacemacs-loading-progress-bar t + ;; If non nil the frame is fullscreen when Emacs starts up. (default nil) + ;; (Emacs 24.4+ only) + dotspacemacs-fullscreen-at-startup nil + ;; If non nil `spacemacs/toggle-fullscreen' will not use native fullscreen. + ;; Use to disable fullscreen animations in OSX. (default nil) + dotspacemacs-fullscreen-use-non-native nil + ;; If non nil the frame is maximized when Emacs starts up. + ;; Takes effect only if `dotspacemacs-fullscreen-at-startup' is nil. + ;; (default nil) (Emacs 24.4+ only) + dotspacemacs-maximized-at-startup nil + ;; A value from the range (0..100), in increasing opacity, which describes + ;; the transparency level of a frame when it's active or selected. + ;; Transparency can be toggled through `toggle-transparency'. (default 90) + dotspacemacs-active-transparency 90 + ;; A value from the range (0..100), in increasing opacity, which describes + ;; the transparency level of a frame when it's inactive or deselected. + ;; Transparency can be toggled through `toggle-transparency'. (default 90) + dotspacemacs-inactive-transparency 90 + ;; If non nil unicode symbols are displayed in the mode line. (default t) + dotspacemacs-mode-line-unicode-symbols t + ;; If non nil smooth scrolling (native-scrolling) is enabled. Smooth + ;; scrolling overrides the default behavior of Emacs which recenters the + ;; point when it reaches the top or bottom of the screen. (default t) + dotspacemacs-smooth-scrolling t + ;; If non nil line numbers are turned on in all `prog-mode' and `text-mode' + ;; derivatives. If set to `relative', also turns on relative line numbers. + ;; (default nil) + dotspacemacs-line-numbers t + ;; If non-nil smartparens-strict-mode will be enabled in programming modes. + ;; (default nil) + dotspacemacs-smartparens-strict-mode nil + ;; Select a scope to highlight delimiters. Possible values are `any', + ;; `current', `all' or `nil'. Default is `all' (highlight any scope and + ;; emphasis the current one). (default 'all) + dotspacemacs-highlight-delimiters 'all + ;; If non nil advises quit functions to keep server open when quitting. + ;; (default nil) + dotspacemacs-persistent-server nil + ;; List of search tool executable names. Spacemacs uses the first installed + ;; tool of the list. Supported tools are `ag', `pt', `ack' and `grep'. + ;; (default '("ag" "pt" "ack" "grep")) + dotspacemacs-search-tools '("ag" "pt" "ack" "grep") + ;; The default package repository used if no explicit repository has been + ;; specified with an installed package. + ;; Not used for now. (default nil) + dotspacemacs-default-package-repository nil + ;; Delete whitespace while saving buffer. Possible values are `all' + ;; to aggressively delete empty line and long sequences of whitespace, + ;; `trailing' to delete only the whitespace at end of lines, `changed'to + ;; delete only whitespace for changed lines or `nil' to disable cleanup. + ;; (default nil) + dotspacemacs-whitespace-cleanup nil + )) + +(defun dotspacemacs/user-init () + "Initialization function for user code. +It is called immediately after `dotspacemacs/init', before layer configuration +executes. + This function is mostly useful for variables that need to be set +before packages are loaded. If you are unsure, you should try in setting them in +`dotspacemacs/user-config' first." + ) + +(defun dotspacemacs/user-config () + "Configuration function for user code. +This function is called at the very end of Spacemacs initialization after +layers configuration. +This is the place where most of your configurations should be done. Unless it is +explicitly specified that a variable should be set before a package is loaded, +you should place your code here." + + ;; Open new files (dropped onto emacs) in a new frame/window + (setq ns-pop-up-frames t) + + ;; When drag/dropping files onto emacs, don't insert into the buffer, + ;; but open the file + (global-set-key [ns-drag-file] 'ns-find-file) + + ;; Org mode + (setq org-startup-indented t) + + ;; Use jk as escape + (setq-default evil-escape-key-sequence "jk") + + ;; search in directory on RET by default (instead of opening dired) + (require 'helm) + (defun helm-find-files-navigate-forward (orig-fun &rest args) + (if (file-directory-p (helm-get-selection)) + (apply orig-fun args) + (helm-maybe-exit-minibuffer))) + (advice-add 'helm-execute-persistent-action :around #'helm-find-files-navigate-forward) + (define-key helm-find-files-map (kbd "") 'helm-execute-persistent-action) + + ;; Neotree smart open, and no hidden files showing + (setq neo-smart-open t) + (setq-default neo-show-hidden-files nil) + ;; Neotree nice icons and smaller text + (setq neo-theme 'icons) + + ;; EMACS for mutt editing + (add-to-list 'auto-mode-alist '("/mutt" . mail-mode)) + + ;; emacsclient in a new frame + (add-hook 'server-switch-hook + (lambda nil + (let ((server-buf (current-buffer))) + (bury-buffer) + (switch-to-buffer-other-frame server-buf)))) + + ;; close the emacsclient frame when done + (add-hook 'server-done-hook 'delete-frame) + (add-hook 'server-done-hook (lambda nil (kill-buffer nil))) + + + ;; Follow symlinks to git repo files by default + (setq vc-follow-symlinks t) + + ;; Org and Deft settings + + ;; Note are under "~/Org" and look in subdirs too + (setq deft-directory "~/Org") + (setq deft-recursive t) + (setq deft-extensions '("org" "md")) + (setq deft-text-mode 'org-mode) + (setq deft-use-filename-as-title t) + (global-set-key [f8] 'deft) + + ;; Use same location for Org agenda + (setq org-agenda-directory "~/Org/") + (setq org-agenda-files (directory-files (expand-file-name org-agenda-directory) t + "^[^\.][^#][[:alnum:]]+\.org$")) + + ;; Exports don't have TOC or numbering by default + (setq org-export-with-toc nil) + (setq org-export-with-creator nil) + (setq org-export-with-section-numbers nil) + + ;; Show everything by default on open + (setq org-startup-folded "showall") + + ;; Full width styling for titles + (setq org-fontify-whole-heading-line t) + + ;open agenda in current window + (setq org-agenda-window-setup (quote current-window)) + ;;warn me of any deadlines in next 7 days + (setq org-deadline-warning-days 7) + ;;show me tasks scheduled or due in next fortnight + (setq org-agenda-span (quote fortnight)) + ;;don't show tasks as scheduled if they are already shown as a deadline + (setq org-agenda-skip-scheduled-if-deadline-is-shown t) + ;;don't give awarning colour to tasks with impending deadlines + ;;if they are scheduled to be done + (setq org-agenda-skip-deadline-prewarning-if-scheduled (quote pre-scheduled)) + ;;don't show tasks that are scheduled or have deadlines in the + ;;normal todo list + (setq org-agenda-todo-ignore-deadlines (quote all)) + (setq org-agenda-todo-ignore-scheduled (quote all)) + ;;sort tasks in order of when they are due and then by priority + (setq org-agenda-sorting-strategy + (quote + ((agenda deadline-up priority-down) + (todo priority-down category-keep) + (tags priority-down category-keep) + (search category-keep)))) + + ;; Org capture setup + (setq org-default-notes-file "~/Org/refile.org") + + ;; Capture templates for: TODO tasks, Notes, appointments, phone calls, meetings, and org-protocol + (setq org-capture-templates + (quote (("t" "todo" entry (file "~/Org/refile.org") + "* TODO %?\n%U\n%a\n") + ("r" "respond" entry (file "~/Org/refile.org") + "* NEXT Respond to %:from on %:subject\nSCHEDULED: %t\n%U\n%a\n") + ("n" "note" entry (file "~/Org/refile.org") + "* %? :NOTE:\n%U\n%a\n") + ("j" "Journal" entry (file+datetree "~/Org/journal.org") + "* %?\n%U\n") + ("w" "org-protocol" entry (file "~/Org/refile.org") + "* TODO Review %c\n%U\n" :immediate-finish t) + ("m" "Meeting" entry (file "~/Org/refile.org") + "* MEETING with %? :MEETING:\n%U") + ("p" "Phone call" entry (file "~/Org/refile.org") + "* PHONE %? :PHONE:\n%U") + ("h" "Habit" entry (file "~/Org/refile.org") + "* NEXT %?\n%U\n%a\nSCHEDULED: %(format-time-string \"%<<%Y-%m-%d %a .+1d/3d>>\")\n:PROPERTIES:\n:STYLE: habit\n:REPEAT_TO_STATE: NEXT\n:END:\n")))) + + (setq org-refile-targets '((nil :maxlevel . 9) + (org-agenda-files :maxlevel . 9))) + (setq org-outline-path-complete-in-steps nil) ; Refile in a single go + (setq org-refile-use-outline-path t) ; Show full paths for refiling + + ;; mobileorg settings + (setq org-directory "~/Org") + (setq org-mobile-inbox-for-pull "~/Org/refile.org") + (setq org-mobile-directory "~/MobileOrg") + (setq org-mobile-files '("~/Org")) + + ;; Fix powerline colors on mac + (setq ns-use-srgb-colorspace nil) + + ;; org-mode fontified headings + (setq org-fontify-whole-heading-line t) + + ;; org-mode override themes and don't scale headings too much + (custom-set-faces + '(org-level-1 ((t (:inherit outline-1 :height 1.2)))) + '(org-level-2 ((t (:inherit outline-2 :height 1.1)))) + '(org-level-3 ((t (:inherit outline-3 :height 1.0)))) + '(org-level-4 ((t (:inherit outline-4 :height 1.0)))) + '(org-level-5 ((t (:inherit outline-5 :height 1.0)))) + ) + + ;; markdown mode override themes and don't scale headings too much + (custom-set-faces + '(markdown-header-face-1 ((t (:inherit markdown-header-face :height 1.2)))) + '(markdown-header-face-2 ((t (:inherit markdown-header-face :height 1.1)))) + '(markdown-header-face-3 ((t (:inherit markdown-header-face :height 1.0)))) + '(markdown-header-face-4 ((t (:inherit markdown-header-face :height 1.0)))) + '(markdown-header-face-5 ((t (:inherit markdown-header-face :height 1.0)))) + '(markdown-header-face-6 ((t (:inherit markdown-header-face :height 1.0)))) + ) + + ;; fill-mode in org-mode and markdown-mode, and mail-mode, and dokuwiki-mode + (add-hook 'org-mode-hook 'turn-on-auto-fill) + (add-hook 'org-mode-hook + (lambda() (set-fill-column 80))) + + (add-hook 'markdown-mode-hook 'turn-on-auto-fill) + (add-hook 'markdown-mode-hook + (lambda() (set-fill-column 80)) + ) + + (add-hook 'mail-mode-hook 'turn-on-auto-fill) + (add-hook 'mail-mode-hook + (lambda() (set-fill-column 80)) + ) + (add-hook 'dokuwiki-mode-hook 'turn-on-auto-fill) + (add-hook 'dokuwiki-mode-hook + (lambda() (set-fill-column 80)) + ) + + (defun fill-buffer () + (interactive) + (save-excursion + (save-restriction + (widen) + (fill-region (point-min) (point-max))))) + + ;; OSX style cut paste everywhere + (define-key global-map [?\s-x] 'kill-region) + (define-key global-map [?\s-c] 'kill-ring-save) + (define-key global-map [?\s-v] 'yank) + (define-key global-map [?\s-z] 'undo) + + ;; groovy-mode for nextflow + (add-to-list 'auto-mode-alist '("\\.nf\\'" . groovy-mode)) + +) + +;; Do not write anything past this comment. This is where Emacs will +;; auto-generate custom variable definitions. +(custom-set-variables + ;; custom-set-variables was added by Custom. + ;; If you edit it by hand, you could mess it up, so be careful. + ;; Your init file should contain only one such instance. + ;; If there is more than one, they won't work right. + '(compilation-message-face (quote default)) + '(custom-safe-hemes + (quote + ("889a93331bc657c0f05a04b8665b78b3c94a12ca76771342cee27d6605abcd0e" "70403e220d6d7100bae7775b3334eddeb340ba9c37f4b39c189c2c29d458543b" "c1390663960169cd92f58aad44ba3253227d8f715c026438303c09b9fb66cdfb" "357d5abe6f693f2875bb3113f5c031b7031f21717e8078f90d9d9bc3a14bcbd8" "3632cf223c62cb7da121be0ed641a2243f7ec0130178722554e613c9ab3131de" "bffa9739ce0752a37d9b1eee78fc00ba159748f50dc328af4be661484848e476" "c7a9a68bd07e38620a5508fef62ec079d274475c8f92d75ed0c33c45fbe306bc" default))) + '(evil-want-Y-yank-to-eol t) + '(fci-rule-color "#3C3D37" t) + '(highlight-changes-colors (quote ("#FD5FF0" "#AE81FF"))) + '(highlight-tail-colors + (quote + (("#3C3D37" . 0) + ("#679A01" . 20) + ("#4BBEAE" . 30) + ("#1DB4D0" . 50) + ("#9A8F21" . 60) + ("#A75B00" . 70) + ("#F309DF" . 85) + ("#3C3D37" . 100)))) + '(magit-diff-use-overlays nil) + '(org-agenda-files (quote ("~/Org/TODO.org"))) + '(package-selected-packages + (quote + (company-emacs-eclim eclim tabbar dokuwiki-mode all-the-icons memoize font-lock+ solarized-theme madhat2r-theme color-theme-sanityinc-solarized pandoc-mode ox-pandoc ht org-category-capture groovy-mode vagrant-tramp vagrant ibuffer-projectile mu4e-maildirs-extension mu4e-alert winum unfill fuzzy sanityinc-tomoorow-day-theme sanityinc-tomorrow-dayi-theme zonokai-theme zenburn-theme zen-and-art-theme yapfify xterm-color uuidgen underwater-theme ujelly-theme twilight-theme twilight-bright-theme twilight-anti-bright-theme tronesque-theme toxi-theme tao-theme tangotango-theme tango-plus-theme tango-2-theme sunny-day-theme sublime-themes subatomic256-theme subatomic-theme spacegray-theme soothe-theme soft-stone-theme soft-morning-theme soft-charcoal-theme smyx-theme shell-pop seti-theme rvm ruby-tools ruby-test-mode rubocop rspec-mode robe reverse-theme rbenv rake railscasts-theme py-isort purple-haze-theme pug-mode professional-theme powershell planet-theme phoenix-dark-pink-theme phoenix-dark-mono-theme pastels-on-dark-theme organic-green-theme org-projectile org-download org-caldav org omtose-phellack-theme oldlace-theme occidental-theme obsidian-theme noctilux-theme niflheim-theme naquadah-theme mwim mustang-theme multi-term monokai-theme monochrome-theme molokai-theme moe-theme minitest minimal-theme material-theme majapahit-theme lush-theme livid-mode skewer-mode simple-httpd live-py-mode link-hint light-soap-theme jbeans-theme jazz-theme ir-black-theme insert-shebang inkpot-theme hide-comnt heroku-theme hemisu-theme hc-zenburn-theme gruvbox-theme gruber-darker-theme grandshell-theme gotham-theme git-link gandalf-theme flyspell-correct-helm flyspell-correct flatui-theme flatland-theme firebelly-theme farmhouse-theme eyebrowse evil-visual-mark-mode evil-unimpaired evil-ediff goto-chg undo-tree espresso-theme eshell-z eshell-prompt-extras esh-help dumb-jump dracula-theme docker tablist docker-tramp django-theme disaster diminish deft darktooth-theme autothemer darkokai-theme darkmine-theme darkburn-theme dakrone-theme cyberpunk-theme company-shell company-c-headers column-enforce-mode color-theme-sanityinc-tomorrow cmake-mode clues-theme clang-format chruby cherry-blossom-theme busybee-theme bundler inf-ruby bubbleberry-theme birds-of-paradise-plus-theme badwolf-theme apropospriate-theme anti-zenburn-theme ample-zen-theme ample-theme alect-themes afternoon-theme yaml-mode puppet-mode pony-mode fish-mode dockerfile-mode web-mode web-beautify toc-org tagedit smeargle slim-mode scss-mode sass-mode pyvenv pytest pyenv-mode py-yapf pip-requirements orgit org-repo-todo org-present org-pomodoro alert log4e gntp org-plus-contrib org-bullets mmm-mode markdown-toc markdown-mode magit-gitflow less-css-mode json-mode json-snatcher json-reformat js2-refactor multiple-cursors js2-mode js-doc jade-mode hy-mode htmlize helm-pydoc helm-gitignore request helm-flyspell helm-css-scss helm-company helm-c-yasnippet haml-mode gnuplot gitignore-mode gitconfig-mode gitattributes-mode git-timemachine git-messenger git-gutter-fringe+ git-gutter-fringe fringe-helper git-gutter+ git-gutter gh-md flycheck-pos-tip flycheck evil-magit magit magit-popup git-commit with-editor emmet-mode diff-hl cython-mode company-web web-completion-data company-tern dash-functional tern company-statistics company-quickhelp pos-tip company-anaconda company coffee-mode auto-yasnippet yasnippet auto-dictionary anaconda-mode pythonic f ac-ispell auto-complete ws-butler window-numbering volatile-highlights vi-tilde-fringe spaceline s powerline smooth-scrolling restart-emacs rainbow-delimiters popwin persp-mode pcre2el paradox hydra spinner page-break-lines open-junk-file neotree move-text macrostep lorem-ipsum linum-relative leuven-theme info+ indent-guide ido-vertical-mode hungry-delete hl-todo highlight-parentheses highlight-numbers parent-mode highlight-indentation help-fns+ helm-themes helm-swoop helm-projectile helm-mode-manager helm-make projectile pkg-info epl helm-flx helm-descbinds helm-ag google-translate golden-ratio flx-ido flx fill-column-indicator fancy-battery expand-region exec-path-from-shell evil-visualstar evil-tutor evil-surround evil-search-highlight-persist evil-numbers evil-nerd-commenter evil-mc evil-matchit evil-lisp-state smartparens evil-indent-plus evil-iedit-state iedit evil-exchange evil-escape evil-args evil-anzu anzu eval-sexp-fu highlight elisp-slime-nav define-word clean-aindent-mode buffer-move bracketed-paste auto-highlight-symbol auto-compile packed dash aggressive-indent adaptive-wrap ace-window ace-link ace-jump-helm-line helm avy helm-core popup async quelpa package-build use-package which-key bind-key bind-map evil spacemacs-theme))) + '(pos-tip-background-color "#A6E22E") + '(pos-tip-foreground-color "#272822") + '(tabbar-separator (quote (0.5))) + '(tramp-copy-size-limit 10000000) + '(tramp-inline-compress-start-size 1000000) + '(tramp-persistency-file-name "/home2/dtrudgian/.emacs.d/.cache/tramp") + '(vc-annotate-background nil) + '(vc-annotate-color-map + (quote + ((20 . "#F92672") + (40 . "#CF4F1F") + (60 . "#C26C0F") + (80 . "#E6DB74") + (100 . "#AB8C00") + (120 . "#A18F00") + (140 . "#989200") + (160 . "#8E9500") + (180 . "#A6E22E") + (200 . "#729A1E") + (220 . "#609C3C") + (240 . "#4E9D5B") + (260 . "#3C9F79") + (280 . "#A1EFE4") + (300 . "#299BA6") + (320 . "#2896B5") + (340 . "#2790C3") + (360 . "#66D9EF")))) + '(vc-annotate-very-old-color nil) + '(weechat-color-list + (unspecified "#272822" "#3C3D37" "#F70057" "#F92672" "#86C30D" "#A6E22E" "#BEB244" "#E6DB74" "#40CAE4" "#66D9EF" "#FB35EA" "#FD5FF0" "#74DBCD" "#A1EFE4" "#F8F8F2" "#F8F8F0"))) +(custom-set-faces + ;; custom-set-faces was added by Custom. + ;; If you edit it by hand, you could mess it up, so be careful. + ;; Your init file should contain only one such instance. + ;; If there is more than one, they won't work right. + '(default ((((class color) (min-colors 257)) (:foreground "#F8F8F2" :background "#272822")) (((class color) (min-colors 89)) (:foreground "#F5F5F5" :background "#1B1E1C")))) + '(markdown-header-face-1 ((t (:inherit markdown-header-face :height 1.2)))) + '(markdown-header-face-2 ((t (:inherit markdown-header-face :height 1.1)))) + '(markdown-header-face-3 ((t (:inherit markdown-header-face :height 1.0)))) + '(markdown-header-face-4 ((t (:inherit markdown-header-face :height 1.0)))) + '(markdown-header-face-5 ((t (:inherit markdown-header-face :height 1.0)))) + '(markdown-header-face-6 ((t (:inherit markdown-header-face :height 1.0)))) + '(org-level-1 ((t (:inherit outline-1 :height 1.2)))) + '(org-level-2 ((t (:inherit outline-2 :height 1.1)))) + '(org-level-3 ((t (:inherit outline-3 :height 1.0)))) + '(org-level-4 ((t (:inherit outline-4 :height 1.0)))) + '(org-level-5 ((t (:inherit outline-5 :height 1.0))))) diff --git a/link/.vim/autoload/plug.vim b/link/.vim/autoload/plug.vim new file mode 100644 index 0000000..143c377 --- /dev/null +++ b/link/.vim/autoload/plug.vim @@ -0,0 +1,2504 @@ +" 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 ``-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') || has('win64') +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 } +let s:TYPE = { +\ 'string': type(''), +\ 'list': type([]), +\ 'dict': type({}), +\ 'funcref': type(function('call')) +\ } +let s:loaded = get(s:, 'loaded', {}) +let s:triggers = get(s:, 'triggers', {}) + +function! plug#begin(...) + if a:0 > 0 + let s:plug_home_org = a:1 + let home = s:path(fnamemodify(expand(a:1), ':p')) + elseif exists('g:plug_home') + let home = s:path(g:plug_home) + elseif !empty(&rtp) + let home = s:path(split(&rtp, ',')[0]) . '/plugged' + else + return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.') + endif + if fnamemodify(home, ':t') ==# 'plugin' && fnamemodify(home, ':h') ==# s:first_rtp + return s:err('Invalid plug home. '.home.' is a standard Vim runtime path and is not allowed.') + endif + + let g:plug_home = home + let g:plugs = {} + let g:plugs_order = [] + let s:triggers = {} + + call s:define_commands() + return 1 +endfunction + +function! s:define_commands() + command! -nargs=+ -bar Plug call plug#() + if !executable('git') + return s:err('`git` executable not found. Most commands will not be available. To suppress this message, prepend `silent!` to `call plug#begin(...)`.') + endif + command! -nargs=* -bar -bang -complete=customlist,s:names PlugInstall call s:install(0, []) + command! -nargs=* -bar -bang -complete=customlist,s:names PlugUpdate call s:update(0, []) + command! -nargs=0 -bar -bang PlugClean call s:clean(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(0, ) +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! plug#end() + if !exists('g:plugs') + return s:err('Call plug#begin() first') + endif + + if exists('#PlugLOD') + augroup PlugLOD + autocmd! + augroup END + augroup! PlugLOD + endif + let lod = { 'ft': {}, 'map': {}, 'cmd': {} } + + if exists('g:did_load_filetypes') + filetype off + endif + for name in g:plugs_order + if !has_key(g:plugs, name) + continue + endif + let plug = g:plugs[name] + if get(s:loaded, name, 0) || !has_key(plug, 'on') && !has_key(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 =~? '^.\+' + 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 ``.') + 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, "", , , , %s)', + \ cmd, string(cmd), string(names)) + endfor + + for [map, names] in items(lod.map) + for [mode, map_prefix, key_prefix] in + \ [['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) + endfor + endfor + + for [ft, names] in items(lod.ft) + augroup PlugLOD + execute printf('autocmd FileType %s call lod_ft(%s, %s)', + \ ft, string(ft), string(names)) + augroup END + endfor + + call s:reorg_rtp() + filetype plugin indent on + if has('vim_starting') + if has('syntax') && !exists('g:syntax_on') + syntax enable + end + else + call s:reload_plugins() + endif +endfunction + +function! s:loaded_names() + return filter(copy(g:plugs_order), 'get(s:loaded, v:val, 0)') +endfunction + +function! s:load_plugin(spec) + call s:source(s:rtp(a:spec), 'plugin/**/*.vim', 'after/plugin/**/*.vim') +endfunction + +function! s:reload_plugins() + for name in s:loaded_names() + call s:load_plugin(g:plugs[name]) + endfor +endfunction + +function! s:trim(str) + return substitute(a:str, '[\/]\+$', '', '') +endfunction + +function! s:version_requirement(val, min) + for idx in range(0, len(a:min) - 1) + let v = get(a:val, idx, 0) + if v < a:min[idx] | return 0 + elseif v > a:min[idx] | return 1 + endif + endfor + return 1 +endfunction + +function! s:git_version_requirement(...) + if !exists('s:git_version') + let s:git_version = map(split(split(s:system('git --version'))[2], '\.'), 'str2nr(v:val)') + endif + return s:version_requirement(s:git_version, a:000) +endfunction + +function! s:progress_opt(base) + return a:base && !s:is_win && + \ s:git_version_requirement(1, 7, 1) ? '--progress' : '' +endfunction + +if s:is_win + function! s:rtp(spec) + return s:path(a:spec.dir . get(a:spec, 'rtp', '')) + endfunction + + function! s:path(path) + return s:trim(substitute(a:path, '/', '\', 'g')) + endfunction + + function! s:dirpath(path) + return s:path(a:path) . '\' + endfunction + + function! s:is_local_plug(repo) + return a:repo =~? '^[a-z]:\|^[%~]' + endfunction +else + function! s:rtp(spec) + return s:dirpath(a:spec.dir . get(a:spec, 'rtp', '')) + endfunction + + function! s:path(path) + return s:trim(a:path) + endfunction + + function! s:dirpath(path) + return substitute(a:path, '[/\\]*$', '/', '') + endfunction + + function! s:is_local_plug(repo) + return a:repo[0] =~ '[/$~]' + endfunction +endif + +function! s:err(msg) + echohl ErrorMsg + echom '[vim-plug] '.a:msg + echohl None +endfunction + +function! s:warn(cmd, msg) + echohl WarningMsg + execute a:cmd 'a:msg' + echohl None +endfunction + +function! s:esc(path) + return escape(a:path, ' ') +endfunction + +function! s:escrtp(path) + return escape(a:path, ' ,') +endfunction + +function! s:remove_rtp() + for name in s:loaded_names() + let rtp = s:rtp(g:plugs[name]) + execute 'set rtp-='.s:escrtp(rtp) + let after = globpath(rtp, 'after') + if isdirectory(after) + execute 'set rtp-='.s:escrtp(after) + endif + endfor +endfunction + +function! s:reorg_rtp() + if !empty(s:first_rtp) + execute 'set rtp-='.s:first_rtp + execute 'set rtp-='.s:last_rtp + endif + + " &rtp is modified from outside + if exists('s:prtp') && s:prtp !=# &rtp + call s:remove_rtp() + unlet! s:middle + endif + + let s:middle = get(s:, 'middle', &rtp) + let rtps = map(s:loaded_names(), 's:rtp(g:plugs[v:val])') + let afters = filter(map(copy(rtps), 'globpath(v:val, "after")'), '!empty(v:val)') + let rtp = join(map(rtps, 'escape(v:val, ",")'), ',') + \ . ','.s:middle.',' + \ . join(map(afters, 'escape(v:val, ",")'), ',') + let &rtp = substitute(substitute(rtp, ',,*', ',', 'g'), '^,\|,$', '', 'g') + let s:prtp = &rtp + + if !empty(s:first_rtp) + execute 'set rtp^='.s:first_rtp + execute 'set rtp+='.s:last_rtp + endif +endfunction + +function! s:doautocmd(...) + if exists('#'.join(a:000, '#')) + execute 'doautocmd' ((v:version > 703 || has('patch442')) ? '' : '') 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'] + 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 = "\" . prefix + endif + let prefix .= v:operator + endif + call feedkeys(prefix, 'n') + endif + call feedkeys(substitute(a:map, '^', "\", '') . extra) +endfunction + +function! plug#(repo, ...) + if a:0 > 1 + return s:err('Invalid number of arguments (1..2)') + endif + + try + let repo = s:trim(a:repo) + let opts = a:0 == 1 ? s:parse_options(a:1) : s:base_spec + let name = get(opts, 'as', fnamemodify(repo, ':t:s?\.git$??')) + let spec = extend(s:infer_properties(name, repo), opts) + if !has_key(g:plugs, name) + call add(g:plugs_order, name) + endif + let g:plugs[name] = spec + let s:loaded[name] = get(s:loaded, name, 0) + catch + return s:err(v:exception) + endtry +endfunction + +function! s:parse_options(arg) + let opts = copy(s:base_spec) + let type = type(a:arg) + if type == s:TYPE.string + let opts.tag = a:arg + elseif type == s:TYPE.dict + call extend(opts, a:arg) + if has_key(opts, 'dir') + let opts.dir = s:dirpath(expand(opts.dir)) + endif + else + throw 'Invalid argument type (expected: string or dictionary)' + endif + return opts +endfunction + +function! s:infer_properties(name, repo) + let repo = a:repo + if s:is_local_plug(repo) + return { 'dir': s:dirpath(expand(repo)) } + else + if repo =~ ':' + let uri = repo + else + if repo !~ '/' + throw printf('Invalid argument: %s (implicit `vim-scripts'' expansion is deprecated)', repo) + endif + let fmt = get(g:, 'plug_url_format', 'https://git::@github.com/%s.git') + let uri = printf(fmt, repo) + endif + return { 'dir': s:dirpath(g:plug_home.'/'.a:name), 'uri': uri } + endif +endfunction + +function! s:install(force, names) + call s:update_impl(0, a:force, a:names) +endfunction + +function! s:update(force, names) + call s:update_impl(1, a:force, a:names) +endfunction + +function! plug#helptags() + if !exists('g:plugs') + return s:err('plug#begin was not called') + endif + for spec in values(g:plugs) + let docd = join([s:rtp(spec), 'doc'], '/') + if isdirectory(docd) + silent! execute 'helptags' s:esc(docd) + endif + endfor + return 1 +endfunction + +function! s:syntax() + syntax clear + syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber + syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX + syn match plugNumber /[0-9]\+[0-9.]*/ contained + syn match plugBracket /[[\]]/ contained + syn match plugX /x/ contained + syn match plugDash /^-/ + syn match plugPlus /^+/ + syn match plugStar /^*/ + syn match plugMessage /\(^- \)\@<=.*/ + syn match plugName /\(^- \)\@<=[^ ]*:/ + syn match plugSha /\%(: \)\@<=[0-9a-f]\{4,}$/ + syn match plugTag /(tag: [^)]\+)/ + syn match plugInstall /\(^+ \)\@<=[^:]*/ + syn match plugUpdate /\(^* \)\@<=[^:]*/ + syn match plugCommit /^ \X*[0-9a-f]\{7,9} .*/ contains=plugRelDate,plugEdge,plugTag + syn match plugEdge /^ \X\+$/ + syn match plugEdge /^ \X*/ contained nextgroup=plugSha + syn match plugSha /[0-9a-f]\{7,9}/ contained + syn match plugRelDate /([^)]*)$/ contained + syn match plugNotLoaded /(not loaded)$/ + syn match plugError /^x.*/ + syn region plugDeleted start=/^\~ .*/ end=/^\ze\S/ + syn match plugH2 /^.*:\n-\+$/ + syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean + hi def link plug1 Title + hi def link plug2 Repeat + hi def link plugH2 Type + hi def link plugX Exception + hi def link plugBracket Structure + hi def link plugNumber Number + + hi def link plugDash Special + hi def link plugPlus Constant + hi def link plugStar Boolean + + hi def link plugMessage Function + hi def link plugName Label + hi def link plugInstall Function + hi def link plugUpdate Type + + hi def link plugError Error + hi def link plugDeleted Ignore + hi def link plugRelDate Comment + hi def link plugEdge PreProc + hi def link plugSha Identifier + hi def link plugTag Constant + + hi def link plugNotLoaded Comment +endfunction + +function! s:lpad(str, len) + return a:str . repeat(' ', a:len - len(a:str)) +endfunction + +function! s:lines(msg) + return split(a:msg, "[\r\n]") +endfunction + +function! s:lastline(msg) + return get(s:lines(a:msg), -1, '') +endfunction + +function! s:new_window() + execute get(g:, 'plug_window', 'vertical topleft new') +endfunction + +function! s:plug_window_exists() + let buflist = tabpagebuflist(s:plug_tab) + return !empty(buflist) && index(buflist, s:plug_buf) >= 0 +endfunction + +function! s:switch_in() + if !s:plug_window_exists() + return 0 + endif + + if winbufnr(0) != s:plug_buf + let s:pos = [tabpagenr(), winnr(), winsaveview()] + execute 'normal!' s:plug_tab.'gt' + let winnr = bufwinnr(s:plug_buf) + execute winnr.'wincmd w' + call add(s:pos, winsaveview()) + else + let s:pos = [winsaveview()] + endif + + setlocal modifiable + return 1 +endfunction + +function! s:switch_out(...) + call winrestview(s:pos[-1]) + setlocal nomodifiable + if a:0 > 0 + execute a:1 + endif + + if len(s:pos) > 1 + execute 'normal!' s:pos[0].'gt' + execute s:pos[1] 'wincmd w' + call winrestview(s:pos[2]) + endif +endfunction + +function! s:finish_bindings() + nnoremap R :call retry() + nnoremap D :PlugDiff + nnoremap S :PlugStatus + nnoremap U :call status_update() + xnoremap U :call status_update() + nnoremap ]] :silent! call section('') + nnoremap [[ :silent! call section('b') +endfunction + +function! s:prepare(...) + if empty(getcwd()) + throw 'Invalid current working directory. Cannot proceed.' + endif + + for evar in ['$GIT_DIR', '$GIT_WORK_TREE'] + if exists(evar) + throw evar.' detected. Cannot proceed.' + endif + endfor + + call s:job_abort() + if s:switch_in() + if b:plug_preview == 1 + pc + endif + enew + else + call s:new_window() + endif + + nnoremap q :if b:plug_preview==1pcendifbd + 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 ['', 'L', 'o', 'X', 'd', 'dd'] + execute 'silent! unmap ' k + endfor + setlocal buftype=nofile bufhidden=wipe nobuflisted nolist noswapfile nowrap cursorline modifiable nospell + setf vim-plug + if exists('g:syntax_on') + call s:syntax() + endif +endfunction + +function! s:assign_name() + " Assign buffer name + let prefix = '[Plugins]' + let name = prefix + let idx = 2 + while bufexists(name) + let name = printf('%s (%s)', prefix, idx) + let idx = idx + 1 + endwhile + silent! execute 'f' fnameescape(name) +endfunction + +function! s:chsh(swap) + let prev = [&shell, &shellcmdflag, &shellredir] + if s:is_win + set shell=cmd.exe shellcmdflag=/c shellredir=>%s\ 2>&1 + elseif a:swap + set shell=sh shellredir=>%s\ 2>&1 + endif + return prev +endfunction + +function! s:bang(cmd, ...) + try + let [sh, shellcmdflag, shrd] = s:chsh(a:0) + " FIXME: Escaping is incomplete. We could use shellescape with eval, + " but it won't work on Windows. + let cmd = a:0 ? s:with_cd(a:cmd, a:1) : a:cmd + if s:is_win + let batchfile = tempname().'.bat' + call writefile(['@echo off', cmd], batchfile) + let cmd = batchfile + 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 + call delete(batchfile) + endif + endtry + return v:shell_error ? 'Exit status: ' . v:shell_error : '' +endfunction + +function! s:regress_bar() + let bar = substitute(getline(2)[1:-2], '.*\zs=', 'x', '') + call s:progress_bar(2, bar, len(bar)) +endfunction + +function! s:is_updated(dir) + return !empty(s:system_chomp('git log --pretty=format:"%h" "HEAD...HEAD@{1}"', a:dir)) +endfunction + +function! s:do(pull, force, todo) + for [name, spec] in items(a:todo) + if !isdirectory(spec.dir) + continue + endif + let installed = has_key(s:update.new, name) + let updated = installed ? 0 : + \ (a:pull && index(s:update.errors, name) < 0 && s:is_updated(spec.dir)) + if a:force || installed || updated + execute 'cd' s:esc(spec.dir) + call append(3, '- Post-update hook for '. name .' ... ') + let error = '' + let type = type(spec.do) + if type == s:TYPE.string + if spec.do[0] == ':' + if !get(s:loaded, name, 0) + let s:loaded[name] = 1 + call s:reorg_rtp() + endif + call s:load_plugin(spec) + try + execute spec.do[1:] + catch + let error = v:exception + endtry + if !s:plug_window_exists() + cd - + throw 'Warning: vim-plug was terminated by the post-update hook of '.name + endif + else + let error = s:bang(spec.do) + endif + elseif type == s:TYPE.funcref + try + let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged') + call spec.do({ 'name': name, 'status': status, 'force': a:force }) + catch + let error = v:exception + endtry + else + let error = 'Invalid hook type' + endif + call s:switch_in() + call setline(4, empty(error) ? (getline(4) . 'OK') + \ : ('x' . getline(4)[1:] . error)) + if !empty(error) + call add(s:update.errors, name) + call s:regress_bar() + endif + cd - + endif + endfor +endfunction + +function! s:hash_match(a, b) + return stridx(a:a, a:b) == 0 || stridx(a:b, a:a) == 0 +endfunction + +function! s:checkout(spec) + let sha = a:spec.commit + let output = s:system('git rev-parse HEAD', a:spec.dir) + if !v:shell_error && !s:hash_match(sha, s:lines(output)[0]) + let output = s:system( + \ 'git fetch --depth 999999 && git checkout '.s:esc(sha).' --', a:spec.dir) + endif + return output +endfunction + +function! s:finish(pull) + let new_frozen = len(filter(keys(s:update.new), 'g:plugs[v:val].frozen')) + if new_frozen + let s = new_frozen > 1 ? 's' : '' + call append(3, printf('- Installed %d frozen plugin%s', new_frozen, s)) + endif + call append(3, '- Finishing ... ') | 4 + redraw + call plug#helptags() + call plug#end() + call setline(4, getline(4) . 'Done!') + redraw + let msgs = [] + if !empty(s:update.errors) + call add(msgs, "Press 'R' to retry.") + endif + if a:pull && len(s:update.new) < len(filter(getline(5, '$'), + \ "v:val =~ '^- ' && stridx(v:val, 'Already up-to-date') < 0")) + call add(msgs, "Press 'D' to see the updated changes.") + endif + echo join(msgs, ' ') + call s:finish_bindings() +endfunction + +function! s:retry() + if empty(s:update.errors) + return + endif + echo + call s:update_impl(s:update.pull, s:update.force, + \ extend(copy(s:update.errors), [s:update.threads])) +endfunction + +function! s:is_managed(name) + return has_key(g:plugs[a:name], 'uri') +endfunction + +function! s:names(...) + return sort(filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)')) +endfunction + +function! s:check_ruby() + silent! ruby require 'thread'; VIM::command("let g:plug_ruby = '#{RUBY_VERSION}'") + if !exists('g:plug_ruby') + redraw! + return s:warn('echom', 'Warning: Ruby interface is broken') + endif + let ruby_version = split(g:plug_ruby, '\.') + unlet g:plug_ruby + return s:version_requirement(ruby_version, [1, 8, 7]) +endfunction + +function! s:update_impl(pull, force, args) abort + let sync = index(a:args, '--sync') >= 0 || has('vim_starting') + let args = filter(copy(a:args), 'v:val != "--sync"') + let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ? + \ remove(args, -1) : get(g:, 'plug_threads', 16) + + let managed = filter(copy(g:plugs), 's:is_managed(v:key)') + let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') : + \ filter(managed, 'index(args, v:key) >= 0') + + if empty(todo) + return s:warn('echo', 'No plugin to '. (a:pull ? 'update' : 'install')) + endif + + if !s:is_win && s:git_version_requirement(2, 3) + let s:git_terminal_prompt = exists('$GIT_TERMINAL_PROMPT') ? $GIT_TERMINAL_PROMPT : '' + let $GIT_TERMINAL_PROMPT = 0 + for plug in values(todo) + let plug.uri = substitute(plug.uri, + \ '^https://git::@github\.com', 'https://github.com', '') + endfor + endif + + if !isdirectory(g:plug_home) + try + call mkdir(g:plug_home, 'p') + catch + return s:err(printf('Invalid plug directory: %s. '. + \ 'Try to call plug#begin with a valid directory', g:plug_home)) + endtry + endif + + if has('nvim') && !exists('*jobwait') && threads > 1 + call s:warn('echom', '[vim-plug] Update Neovim for parallel installer') + endif + + let use_job = s:nvim || s:vim8 + let python = (has('python') || has('python3')) && !use_job + let ruby = has('ruby') && !use_job && (v:version >= 703 || v:version == 702 && has('patch374')) && !(s:is_win && has('gui_running')) && threads > 1 && s:check_ruby() + + let s:update = { + \ 'start': reltime(), + \ 'all': todo, + \ 'todo': copy(todo), + \ 'errors': [], + \ 'pull': a:pull, + \ 'force': a:force, + \ 'new': {}, + \ 'threads': (python || ruby || use_job) ? min([len(todo), threads]) : 1, + \ 'bar': '', + \ 'fin': 0 + \ } + + call s:prepare(1) + call append(0, ['', '']) + normal! 2G + silent! redraw + + let s:clone_opt = get(g:, 'plug_shallow', 1) ? + \ '--depth 1' . (s:git_version_requirement(1, 7, 10) ? ' --no-single-branch' : '') : '' + + if has('win32unix') + let s:clone_opt .= ' -c core.eol=lf -c core.autocrlf=input' + endif + + " Python version requirement (>= 2.7) + if python && !has('python3') && !ruby && !use_job && s:update.threads > 1 + redir => pyv + silent python import platform; print platform.python_version() + redir END + let python = s:version_requirement( + \ map(split(split(pyv)[0], '\.'), 'str2nr(v:val)'), [2, 6]) + endif + + if (python || ruby) && s:update.threads > 1 + try + let imd = &imd + if s:mac_gui + set noimd + endif + if ruby + call s:update_ruby() + else + call s:update_python() + endif + catch + let lines = getline(4, '$') + let printed = {} + silent! 4,$d _ + for line in lines + let name = s:extract_name(line, '.', '') + if empty(name) || !has_key(printed, name) + call append('$', line) + if !empty(name) + let printed[name] = 1 + if line[0] == 'x' && index(s:update.errors, name) < 0 + call add(s:update.errors, name) + end + endif + endif + endfor + finally + let &imd = imd + call s:update_finish() + endtry + else + call s:update_vim() + while use_job && sync + sleep 100m + if s:update.fin + break + endif + endwhile + endif +endfunction + +function! s:log4(name, msg) + call setline(4, printf('- %s (%s)', a:msg, a:name)) + redraw +endfunction + +function! s:update_finish() + if exists('s:git_terminal_prompt') + let $GIT_TERMINAL_PROMPT = s:git_terminal_prompt + endif + if s:switch_in() + call append(3, '- Updating ...') | 4 + for [name, spec] in items(filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && (s:update.force || s:update.pull || has_key(s:update.new, v:key))')) + let [pos, _] = s:logpos(name) + if !pos + continue + endif + if has_key(spec, 'commit') + call s:log4(name, 'Checking out '.spec.commit) + let out = s:checkout(spec) + elseif has_key(spec, 'tag') + let tag = spec.tag + if tag =~ '\*' + let tags = s:lines(s:system('git tag --list '.s:shellesc(tag).' --sort -version:refname 2>&1', spec.dir)) + if !v:shell_error && !empty(tags) + let tag = tags[0] + call s:log4(name, printf('Latest tag for %s -> %s', spec.tag, tag)) + call append(3, '') + endif + endif + call s:log4(name, 'Checking out '.tag) + let out = s:system('git checkout -q '.s:esc(tag).' -- 2>&1', spec.dir) + else + let branch = s:esc(get(spec, 'branch', 'master')) + call s:log4(name, 'Merging origin/'.branch) + let out = s:system('git checkout -q '.branch.' -- 2>&1' + \. (has_key(s:update.new, name) ? '' : ('&& git merge --ff-only origin/'.branch.' 2>&1')), spec.dir) + endif + if !v:shell_error && filereadable(spec.dir.'/.gitmodules') && + \ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir)) + call s:log4(name, 'Updating submodules. This may take a while.') + let out .= s:bang('git submodule update --init --recursive 2>&1', spec.dir) + endif + let msg = s:format_message(v:shell_error ? 'x': '-', name, out) + if v:shell_error + call add(s:update.errors, name) + call s:regress_bar() + silent execute pos 'd _' + call append(4, msg) | 4 + elseif !empty(out) + call setline(pos, msg[0]) + endif + redraw + endfor + silent 4 d _ + try + call s:do(s:update.pull, s:update.force, filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && has_key(v:val, "do")')) + catch + call s:warn('echom', v:exception) + call s:warn('echo', '') + return + endtry + call s:finish(s:update.pull) + call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(s:update.start)))[0] . ' sec.') + call s:switch_out('normal! gg') + endif +endfunction + +function! s:job_abort() + if (!s:nvim && !s:vim8) || !exists('s:jobs') + return + endif + + for [name, j] in items(s:jobs) + if s:nvim + silent! call jobstop(j.jobid) + elseif s:vim8 + silent! call job_stop(j.jobid) + endif + if j.new + call s:system('rm -rf ' . s:shellesc(g:plugs[name].dir)) + endif + endfor + let s:jobs = {} +endfunction + +function! s:last_non_empty_line(lines) + let len = len(a:lines) + for idx in range(len) + let line = a:lines[len-idx-1] + if !empty(line) + return line + endif + endfor + return '' +endfunction + +function! s:job_out_cb(self, data) abort + let self = a:self + let data = remove(self.lines, -1) . a:data + let lines = map(split(data, "\n", 1), 'split(v:val, "\r", 1)[-1]') + call extend(self.lines, lines) + " To reduce the number of buffer updates + let self.tick = get(self, 'tick', -1) + 1 + if !self.running || self.tick % len(s:jobs) == 0 + let bullet = self.running ? (self.new ? '+' : '*') : (self.error ? 'x' : '-') + let result = self.error ? join(self.lines, "\n") : s:last_non_empty_line(self.lines) + call s:log(bullet, self.name, result) + endif +endfunction + +function! s:job_exit_cb(self, data) abort + let a:self.running = 0 + let a:self.error = a:data != 0 + call s:reap(a:self.name) + call s:tick() +endfunction + +function! s:job_cb(fn, job, ch, data) + if !s:plug_window_exists() " plug window closed + return s:job_abort() + endif + call call(a:fn, [a:job, a:data]) +endfunction + +function! s:nvim_cb(job_id, data, event) dict abort + return a:event == 'stdout' ? + \ s:job_cb('s:job_out_cb', self, 0, join(a:data, "\n")) : + \ s:job_cb('s:job_exit_cb', self, 0, a:data) +endfunction + +function! s:spawn(name, cmd, opts) + let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [''], + \ 'batchfile': (s:is_win && (s:nvim || s:vim8)) ? tempname().'.bat' : '', + \ 'new': get(a:opts, 'new', 0) } + let s:jobs[a:name] = job + let cmd = has_key(a:opts, 'dir') ? s:with_cd(a:cmd, a:opts.dir) : a:cmd + if !empty(job.batchfile) + call writefile(['@echo off', cmd], job.batchfile) + let cmd = job.batchfile + endif + let argv = add(s:is_win ? ['cmd', '/c'] : ['sh', '-c'], cmd) + + if s:nvim + call extend(job, { + \ 'on_stdout': function('s:nvim_cb'), + \ 'on_exit': function('s:nvim_cb'), + \ }) + let jid = jobstart(argv, job) + if jid > 0 + let job.jobid = jid + else + let job.running = 0 + let job.error = 1 + let job.lines = [jid < 0 ? argv[0].' is not executable' : + \ 'Invalid arguments (or job table is full)'] + endif + elseif s:vim8 + let jid = job_start(s:is_win ? join(argv, ' ') : argv, { + \ 'out_cb': function('s:job_cb', ['s:job_out_cb', job]), + \ 'exit_cb': function('s:job_cb', ['s:job_exit_cb', job]), + \ 'out_mode': 'raw' + \}) + if job_status(jid) == 'run' + let job.jobid = jid + else + let job.running = 0 + let job.error = 1 + let job.lines = ['Failed to start job'] + endif + else + let job.lines = s:lines(call('s:system', [cmd])) + let job.error = v:shell_error != 0 + let job.running = 0 + endif +endfunction + +function! s:reap(name) + let job = s:jobs[a:name] + if job.error + call add(s:update.errors, a:name) + elseif get(job, 'new', 0) + let s:update.new[a:name] = 1 + endif + let s:update.bar .= job.error ? 'x' : '=' + + let bullet = job.error ? 'x' : '-' + let result = job.error ? join(job.lines, "\n") : s:last_non_empty_line(job.lines) + call s:log(bullet, a:name, empty(result) ? 'OK' : result) + call s:bar() + + if has_key(job, 'batchfile') && !empty(job.batchfile) + call delete(job.batchfile) + endif + call remove(s:jobs, a:name) +endfunction + +function! s:bar() + if s:switch_in() + let total = len(s:update.all) + call setline(1, (s:update.pull ? 'Updating' : 'Installing'). + \ ' plugins ('.len(s:update.bar).'/'.total.')') + call s:progress_bar(2, s:update.bar, total) + call s:switch_out() + endif +endfunction + +function! s:logpos(name) + for i in range(4, line('$')) + if getline(i) =~# '^[-+x*] '.a:name.':' + for j in range(i + 1, line('$')) + if getline(j) !~ '^ ' + return [i, j - 1] + endif + endfor + return [i, i] + endif + endfor + return [0, 0] +endfunction + +function! s:log(bullet, name, lines) + if s:switch_in() + let [b, e] = s:logpos(a:name) + if b > 0 + silent execute printf('%d,%d d _', b, e) + if b > winheight('.') + let b = 4 + endif + else + let b = 4 + endif + " FIXME For some reason, nomodifiable is set after :d in vim8 + setlocal modifiable + call append(b - 1, s:format_message(a:bullet, a:name, a:lines)) + call s:switch_out() + endif +endfunction + +function! s:update_vim() + let s:jobs = {} + + call s:bar() + call s:tick() +endfunction + +function! s:tick() + let pull = s:update.pull + let prog = s:progress_opt(s:nvim || s:vim8) +while 1 " Without TCO, Vim stack is bound to explode + if empty(s:update.todo) + if empty(s:jobs) && !s:update.fin + call s:update_finish() + let s:update.fin = 1 + endif + return + endif + + let name = keys(s:update.todo)[0] + let spec = remove(s:update.todo, name) + let new = !isdirectory(spec.dir) + + call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...') + redraw + + let has_tag = has_key(spec, 'tag') + if !new + let [error, _] = s:git_validate(spec, 0) + if empty(error) + if pull + let fetch_opt = (has_tag && !empty(globpath(spec.dir, '.git/shallow'))) ? '--depth 99999999' : '' + call s:spawn(name, printf('git fetch %s %s 2>&1', fetch_opt, prog), { 'dir': spec.dir }) + else + let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 } + endif + else + let s:jobs[name] = { 'running': 0, 'lines': s:lines(error), 'error': 1 } + endif + else + call s:spawn(name, + \ printf('git clone %s %s %s %s 2>&1', + \ has_tag ? '' : s:clone_opt, + \ prog, + \ s:shellesc(spec.uri), + \ s:shellesc(s:trim(spec.dir))), { 'new': 1 }) + endif + + if !s:jobs[name].running + call s:reap(name) + endif + if len(s:jobs) >= s:update.threads + break + endif +endwhile +endfunction + +function! s:update_python() +let py_exe = has('python') ? 'python' : 'python3' +execute py_exe "<< EOF" +import datetime +import functools +import os +try: + import queue +except ImportError: + import Queue as queue +import random +import re +import shutil +import signal +import subprocess +import tempfile +import threading as thr +import time +import traceback +import vim + +G_NVIM = vim.eval("has('nvim')") == '1' +G_PULL = vim.eval('s:update.pull') == '1' +G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1 +G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)')) +G_CLONE_OPT = vim.eval('s:clone_opt') +G_PROGRESS = vim.eval('s:progress_opt(1)') +G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads')) +G_STOP = thr.Event() +G_IS_WIN = vim.eval('s:is_win') == '1' + +class PlugError(Exception): + def __init__(self, msg): + self.msg = msg +class CmdTimedOut(PlugError): + pass +class CmdFailed(PlugError): + pass +class InvalidURI(PlugError): + pass +class Action(object): + INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-'] + +class Buffer(object): + def __init__(self, lock, num_plugs, is_pull): + self.bar = '' + self.event = 'Updating' if is_pull else 'Installing' + self.lock = lock + self.maxy = int(vim.eval('winheight(".")')) + self.num_plugs = num_plugs + + def __where(self, name): + """ Find first line with name in current buffer. Return line num. """ + found, lnum = False, 0 + matcher = re.compile('^[-+x*] {0}:'.format(name)) + for line in vim.current.buffer: + if matcher.search(line) is not None: + found = True + break + lnum += 1 + + if not found: + lnum = -1 + return lnum + + def header(self): + curbuf = vim.current.buffer + curbuf[0] = self.event + ' plugins ({0}/{1})'.format(len(self.bar), self.num_plugs) + + num_spaces = self.num_plugs - len(self.bar) + curbuf[1] = '[{0}{1}]'.format(self.bar, num_spaces * ' ') + + with self.lock: + vim.command('normal! 2G') + vim.command('redraw') + + def write(self, action, name, lines): + first, rest = lines[0], lines[1:] + msg = ['{0} {1}{2}{3}'.format(action, name, ': ' if first else '', first)] + msg.extend([' ' + line for line in rest]) + + try: + if action == Action.ERROR: + self.bar += 'x' + vim.command("call add(s:update.errors, '{0}')".format(name)) + elif action == Action.DONE: + self.bar += '=' + + curbuf = vim.current.buffer + lnum = self.__where(name) + if lnum != -1: # Found matching line num + del curbuf[lnum] + if lnum > self.maxy and action in set([Action.INSTALL, Action.UPDATE]): + lnum = 3 + else: + lnum = 3 + curbuf.append(msg, lnum) + + self.header() + except vim.error: + pass + +class Command(object): + CD = 'cd /d' if G_IS_WIN else 'cd' + + def __init__(self, cmd, cmd_dir=None, timeout=60, cb=None, clean=None): + self.cmd = cmd + if cmd_dir: + self.cmd = '{0} {1} && {2}'.format(Command.CD, cmd_dir, self.cmd) + self.timeout = timeout + self.callback = cb if cb else (lambda msg: None) + self.clean = clean if clean else (lambda: None) + self.proc = None + + @property + def alive(self): + """ Returns true only if command still running. """ + return self.proc and self.proc.poll() is None + + def execute(self, ntries=3): + """ Execute the command with ntries if CmdTimedOut. + Returns the output of the command if no Exception. + """ + attempt, finished, limit = 0, False, self.timeout + + while not finished: + try: + attempt += 1 + result = self.try_command() + finished = True + return result + except CmdTimedOut: + if attempt != ntries: + self.notify_retry() + self.timeout += limit + else: + raise + + def notify_retry(self): + """ Retry required for command, notify user. """ + for count in range(3, 0, -1): + if G_STOP.is_set(): + raise KeyboardInterrupt + msg = 'Timeout. Will retry in {0} second{1} ...'.format( + count, 's' if count != 1 else '') + self.callback([msg]) + time.sleep(1) + self.callback(['Retrying ...']) + + def try_command(self): + """ Execute a cmd & poll for callback. Returns list of output. + Raises CmdFailed -> return code for Popen isn't 0 + Raises CmdTimedOut -> command exceeded timeout without new output + """ + first_line = True + + try: + tfile = tempfile.NamedTemporaryFile(mode='w+b') + preexec_fn = not G_IS_WIN and os.setsid or None + self.proc = subprocess.Popen(self.cmd, stdout=tfile, + stderr=subprocess.STDOUT, + stdin=subprocess.PIPE, shell=True, + preexec_fn=preexec_fn) + thrd = thr.Thread(target=(lambda proc: proc.wait()), args=(self.proc,)) + thrd.start() + + thread_not_started = True + while thread_not_started: + try: + thrd.join(0.1) + thread_not_started = False + except RuntimeError: + pass + + while self.alive: + if G_STOP.is_set(): + raise KeyboardInterrupt + + if first_line or random.random() < G_LOG_PROB: + first_line = False + line = '' if G_IS_WIN else nonblock_read(tfile.name) + if line: + self.callback([line]) + + time_diff = time.time() - os.path.getmtime(tfile.name) + if time_diff > self.timeout: + raise CmdTimedOut(['Timeout!']) + + thrd.join(0.5) + + tfile.seek(0) + result = [line.decode('utf-8', 'replace').rstrip() for line in tfile] + + if self.proc.returncode != 0: + raise CmdFailed([''] + result) + + return result + except: + self.terminate() + raise + + def terminate(self): + """ Terminate process and cleanup. """ + if self.alive: + if G_IS_WIN: + os.kill(self.proc.pid, signal.SIGINT) + else: + os.killpg(self.proc.pid, signal.SIGTERM) + self.clean() + +class Plugin(object): + def __init__(self, name, args, buf_q, lock): + self.name = name + self.args = args + self.buf_q = buf_q + self.lock = lock + self.tag = args.get('tag', 0) + + def manage(self): + try: + if os.path.exists(self.args['dir']): + self.update() + else: + self.install() + with self.lock: + thread_vim_command("let s:update.new['{0}'] = 1".format(self.name)) + except PlugError as exc: + self.write(Action.ERROR, self.name, exc.msg) + except KeyboardInterrupt: + G_STOP.set() + self.write(Action.ERROR, self.name, ['Interrupted!']) + except: + # Any exception except those above print stack trace + msg = 'Trace:\n{0}'.format(traceback.format_exc().rstrip()) + self.write(Action.ERROR, self.name, msg.split('\n')) + raise + + def install(self): + target = self.args['dir'] + if target[-1] == '\\': + target = target[0:-1] + + def clean(target): + def _clean(): + try: + shutil.rmtree(target) + except OSError: + pass + return _clean + + self.write(Action.INSTALL, self.name, ['Installing ...']) + callback = functools.partial(self.write, Action.INSTALL, self.name) + cmd = 'git clone {0} {1} {2} {3} 2>&1'.format( + '' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'], + esc(target)) + com = Command(cmd, None, G_TIMEOUT, callback, clean(target)) + result = com.execute(G_RETRIES) + self.write(Action.DONE, self.name, result[-1:]) + + def repo_uri(self): + cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url' + command = Command(cmd, self.args['dir'], G_TIMEOUT,) + result = command.execute(G_RETRIES) + return result[-1] + + def update(self): + actual_uri = self.repo_uri() + expect_uri = self.args['uri'] + regex = re.compile(r'^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$') + ma = regex.match(actual_uri) + mb = regex.match(expect_uri) + if ma is None or mb is None or ma.groups() != mb.groups(): + msg = ['', + 'Invalid URI: {0}'.format(actual_uri), + 'Expected {0}'.format(expect_uri), + 'PlugClean required.'] + raise InvalidURI(msg) + + if G_PULL: + self.write(Action.UPDATE, self.name, ['Updating ...']) + callback = functools.partial(self.write, Action.UPDATE, self.name) + fetch_opt = '--depth 99999999' if self.tag and os.path.isfile(os.path.join(self.args['dir'], '.git/shallow')) else '' + cmd = 'git fetch {0} {1} 2>&1'.format(fetch_opt, G_PROGRESS) + com = Command(cmd, self.args['dir'], G_TIMEOUT, callback) + result = com.execute(G_RETRIES) + self.write(Action.DONE, self.name, result[-1:]) + else: + self.write(Action.DONE, self.name, ['Already installed']) + + def write(self, action, name, msg): + self.buf_q.put((action, name, msg)) + +class PlugThread(thr.Thread): + def __init__(self, tname, args): + super(PlugThread, self).__init__() + self.tname = tname + self.args = args + + def run(self): + thr.current_thread().name = self.tname + buf_q, work_q, lock = self.args + + try: + while not G_STOP.is_set(): + name, args = work_q.get_nowait() + plug = Plugin(name, args, buf_q, lock) + plug.manage() + work_q.task_done() + except queue.Empty: + pass + +class RefreshThread(thr.Thread): + def __init__(self, lock): + super(RefreshThread, self).__init__() + self.lock = lock + self.running = True + + def run(self): + while self.running: + with self.lock: + thread_vim_command('noautocmd normal! a') + time.sleep(0.33) + + def stop(self): + self.running = False + +if G_NVIM: + def thread_vim_command(cmd): + vim.session.threadsafe_call(lambda: vim.command(cmd)) +else: + def thread_vim_command(cmd): + vim.command(cmd) + +def esc(name): + return '"' + name.replace('"', '\"') + '"' + +def nonblock_read(fname): + """ Read a file with nonblock flag. Return the last line. """ + fread = os.open(fname, os.O_RDONLY | os.O_NONBLOCK) + buf = os.read(fread, 100000).decode('utf-8', 'replace') + os.close(fread) + + line = buf.rstrip('\r\n') + left = max(line.rfind('\r'), line.rfind('\n')) + if left != -1: + left += 1 + line = line[left:] + + return line + +def main(): + thr.current_thread().name = 'main' + nthreads = int(vim.eval('s:update.threads')) + plugs = vim.eval('s:update.todo') + mac_gui = vim.eval('s:mac_gui') == '1' + + lock = thr.Lock() + buf = Buffer(lock, len(plugs), G_PULL) + buf_q, work_q = queue.Queue(), queue.Queue() + for work in plugs.items(): + work_q.put(work) + + start_cnt = thr.active_count() + for num in range(nthreads): + tname = 'PlugT-{0:02}'.format(num) + thread = PlugThread(tname, (buf_q, work_q, lock)) + thread.start() + if mac_gui: + rthread = RefreshThread(lock) + rthread.start() + + while not buf_q.empty() or thr.active_count() != start_cnt: + try: + action, name, msg = buf_q.get(True, 0.25) + buf.write(action, name, ['OK'] if not msg else msg) + buf_q.task_done() + except queue.Empty: + pass + except KeyboardInterrupt: + G_STOP.set() + + if mac_gui: + rthread.stop() + rthread.join() + +main() +EOF +endfunction + +function! s:update_ruby() + ruby << EOF + module PlugStream + SEP = ["\r", "\n", nil] + def get_line + buffer = '' + loop do + char = readchar rescue return + if SEP.include? char.chr + buffer << $/ + break + else + buffer << char + end + end + buffer + end + end unless defined?(PlugStream) + + def esc arg + %["#{arg.gsub('"', '\"')}"] + end + + def killall pid + pids = [pid] + if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM + pids.each { |pid| Process.kill 'INT', pid.to_i rescue nil } + else + unless `which pgrep 2> /dev/null`.empty? + children = pids + until children.empty? + children = children.map { |pid| + `pgrep -P #{pid}`.lines.map { |l| l.chomp } + }.flatten + pids += children + end + end + pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil } + end + end + + def compare_git_uri a, b + regex = %r{^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$} + regex.match(a).to_a.drop(1) == regex.match(b).to_a.drop(1) + end + + require 'thread' + require 'fileutils' + require 'timeout' + running = true + iswin = VIM::evaluate('s:is_win').to_i == 1 + pull = VIM::evaluate('s:update.pull').to_i == 1 + base = VIM::evaluate('g:plug_home') + all = VIM::evaluate('s:update.todo') + limit = VIM::evaluate('get(g:, "plug_timeout", 60)') + tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1 + nthr = VIM::evaluate('s:update.threads').to_i + maxy = VIM::evaluate('winheight(".")').to_i + vim7 = VIM::evaluate('v:version').to_i <= 703 && RUBY_PLATFORM =~ /darwin/ + cd = iswin ? 'cd /d' : 'cd' + tot = VIM::evaluate('len(s:update.todo)') || 0 + bar = '' + skip = 'Already installed' + mtx = Mutex.new + take1 = proc { mtx.synchronize { running && all.shift } } + logh = proc { + cnt = bar.length + $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})" + $curbuf[2] = '[' + bar.ljust(tot) + ']' + VIM::command('normal! 2G') + VIM::command('redraw') + } + where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } } + log = proc { |name, result, type| + mtx.synchronize do + ing = ![true, false].include?(type) + bar += type ? '=' : 'x' unless ing + b = case type + when :install then '+' when :update then '*' + when true, nil then '-' else + VIM::command("call add(s:update.errors, '#{name}')") + 'x' + end + result = + if type || type.nil? + ["#{b} #{name}: #{result.lines.to_a.last || 'OK'}"] + elsif result =~ /^Interrupted|^Timeout/ + ["#{b} #{name}: #{result}"] + else + ["#{b} #{name}"] + result.lines.map { |l| " " << l } + end + if lnum = where.call(name) + $curbuf.delete lnum + lnum = 4 if ing && lnum > maxy + end + result.each_with_index do |line, offset| + $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp) + end + logh.call + end + } + bt = proc { |cmd, name, type, cleanup| + tried = timeout = 0 + begin + tried += 1 + timeout += limit + fd = nil + data = '' + if iswin + Timeout::timeout(timeout) do + tmp = VIM::evaluate('tempname()') + system("(#{cmd}) > #{tmp}") + data = File.read(tmp).chomp + File.unlink tmp rescue nil + end + else + fd = IO.popen(cmd).extend(PlugStream) + first_line = true + log_prob = 1.0 / nthr + while line = Timeout::timeout(timeout) { fd.get_line } + data << line + log.call name, line.chomp, type if name && (first_line || rand < log_prob) + first_line = false + end + fd.close + end + [$? == 0, data.chomp] + rescue Timeout::Error, Interrupt => e + if fd && !fd.closed? + killall fd.pid + fd.close + end + cleanup.call if cleanup + if e.is_a?(Timeout::Error) && tried < tries + 3.downto(1) do |countdown| + s = countdown > 1 ? 's' : '' + log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type + sleep 1 + end + log.call name, 'Retrying ...', type + retry + end + [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"] + end + } + main = Thread.current + threads = [] + watcher = Thread.new { + if vim7 + while VIM::evaluate('getchar(1)') + sleep 0.1 + end + else + require 'io/console' # >= Ruby 1.9 + nil until IO.console.getch == 3.chr + end + mtx.synchronize do + running = false + threads.each { |t| t.raise Interrupt } unless vim7 + end + threads.each { |t| t.join rescue nil } + main.kill + } + refresh = Thread.new { + while true + mtx.synchronize do + break unless running + VIM::command('noautocmd normal! a') + end + sleep 0.2 + end + } if VIM::evaluate('s:mac_gui') == 1 + + clone_opt = VIM::evaluate('s:clone_opt') + progress = VIM::evaluate('s:progress_opt(1)') + nthr.times do + mtx.synchronize do + threads << Thread.new { + while pair = take1.call + name = pair.first + dir, uri, tag = pair.last.values_at *%w[dir uri tag] + exists = File.directory? dir + ok, result = + if exists + chdir = "#{cd} #{iswin ? dir : esc(dir)}" + ret, data = bt.call "#{chdir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url", nil, nil, nil + current_uri = data.lines.to_a.last + if !ret + if data =~ /^Interrupted|^Timeout/ + [false, data] + else + [false, [data.chomp, "PlugClean required."].join($/)] + end + elsif !compare_git_uri(current_uri, uri) + [false, ["Invalid URI: #{current_uri}", + "Expected: #{uri}", + "PlugClean required."].join($/)] + else + if pull + log.call name, 'Updating ...', :update + fetch_opt = (tag && File.exist?(File.join(dir, '.git/shallow'))) ? '--depth 99999999' : '' + bt.call "#{chdir} && git fetch #{fetch_opt} #{progress} 2>&1", name, :update, nil + else + [true, skip] + end + end + else + d = esc dir.sub(%r{[\\/]+$}, '') + log.call name, 'Installing ...', :install + bt.call "git clone #{clone_opt unless tag} #{progress} #{uri} #{d} 2>&1", name, :install, proc { + FileUtils.rm_rf dir + } + end + mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok + log.call name, result, ok + end + } if running + end + end + threads.each { |t| t.join rescue nil } + logh.call + refresh.kill if refresh + watcher.kill +EOF +endfunction + +function! s:shellesc_cmd(arg) + let escaped = substitute(a:arg, '[&|<>()@^]', '^&', 'g') + let escaped = substitute(escaped, '%', '%%', 'g') + let escaped = substitute(escaped, '"', '\\^&', 'g') + let escaped = substitute(escaped, '\(\\\+\)\(\\^\)', '\1\1\2', 'g') + return '^"'.substitute(escaped, '\(\\\+\)$', '\1\1', '').'^"' +endfunction + +function! s:shellesc(arg) + if &shell =~# 'cmd.exe$' + return s:shellesc_cmd(a:arg) + endif + return shellescape(a:arg) +endfunction + +function! s:glob_dir(path) + return map(filter(s:glob(a:path, '**'), 'isdirectory(v:val)'), 's:dirpath(v:val)') +endfunction + +function! s:progress_bar(line, bar, total) + call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']') +endfunction + +function! s:compare_git_uri(a, b) + " See `git help clone' + " https:// [user@] github.com[:port] / junegunn/vim-plug [.git] + " [git@] github.com[:port] : junegunn/vim-plug [.git] + " file:// / junegunn/vim-plug [/] + " / junegunn/vim-plug [/] + let pat = '^\%(\w\+://\)\='.'\%([^@/]*@\)\='.'\([^:/]*\%(:[0-9]*\)\=\)'.'[:/]'.'\(.\{-}\)'.'\%(\.git\)\=/\?$' + let ma = matchlist(a:a, pat) + let mb = matchlist(a:b, pat) + return ma[1:2] ==# mb[1:2] +endfunction + +function! s:format_message(bullet, name, message) + if a:bullet != 'x' + return [printf('%s %s: %s', a:bullet, a:name, s:lastline(a:message))] + else + let lines = map(s:lines(a:message), '" ".v:val') + return extend([printf('x %s:', a:name)], lines) + endif +endfunction + +function! s:with_cd(cmd, dir) + return printf('cd%s %s && %s', s:is_win ? ' /d' : '', s:shellesc(a:dir), a:cmd) +endfunction + +function! s:system(cmd, ...) + try + let [sh, shellcmdflag, shrd] = s:chsh(1) + let cmd = a:0 > 0 ? s:with_cd(a:cmd, a:1) : a:cmd + if s:is_win + let batchfile = tempname().'.bat' + call writefile(['@echo off', cmd], batchfile) + let cmd = batchfile + endif + return system(s:is_win ? '('.cmd.')' : cmd) + finally + let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] + if s:is_win + call delete(batchfile) + endif + endtry +endfunction + +function! s:system_chomp(...) + let ret = call('s:system', a:000) + return v:shell_error ? '' : substitute(ret, '\n$', '', '') +endfunction + +function! s:git_validate(spec, check_branch) + let err = '' + if isdirectory(a:spec.dir) + let result = s:lines(s:system('git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url', a:spec.dir)) + let remote = result[-1] + if v:shell_error + let err = join([remote, 'PlugClean required.'], "\n") + elseif !s:compare_git_uri(remote, a:spec.uri) + let err = join(['Invalid URI: '.remote, + \ 'Expected: '.a:spec.uri, + \ 'PlugClean required.'], "\n") + elseif a:check_branch && has_key(a:spec, 'commit') + let result = s:lines(s:system('git rev-parse HEAD 2>&1', a:spec.dir)) + let sha = result[-1] + if v:shell_error + let err = join(add(result, 'PlugClean required.'), "\n") + elseif !s:hash_match(sha, a:spec.commit) + let err = join([printf('Invalid HEAD (expected: %s, actual: %s)', + \ a:spec.commit[:6], sha[:6]), + \ 'PlugUpdate required.'], "\n") + endif + elseif a:check_branch + let branch = result[0] + " Check tag + if has_key(a:spec, 'tag') + let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir) + if a:spec.tag !=# tag && a:spec.tag !~ '\*' + let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.', + \ (empty(tag) ? 'N/A' : tag), a:spec.tag) + endif + " Check branch + elseif a:spec.branch !=# branch + let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.', + \ branch, a:spec.branch) + endif + if empty(err) + let [ahead, behind] = split(s:lastline(s:system(printf( + \ 'git rev-list --count --left-right HEAD...origin/%s', + \ a:spec.branch), a:spec.dir)), '\t') + if !v:shell_error && ahead + if behind + " Only mention PlugClean if diverged, otherwise it's likely to be + " pushable (and probably not that messed up). + let err = printf( + \ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n" + \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', a:spec.branch, ahead, behind) + else + let err = printf("Ahead of origin/%s by %d commit(s).\n" + \ .'Cannot update until local changes are pushed.', + \ a:spec.branch, ahead) + endif + endif + endif + endif + else + let err = 'Not found' + endif + return [err, err =~# 'PlugClean'] +endfunction + +function! s:rm_rf(dir) + if isdirectory(a:dir) + call s:system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . s:shellesc(a:dir)) + endif +endfunction + +function! s:clean(force) + call s:prepare() + call append(0, 'Searching for invalid plugins in '.g:plug_home) + call append(1, '') + + " List of valid directories + let dirs = [] + let errs = {} + let [cnt, total] = [0, len(g:plugs)] + for [name, spec] in items(g:plugs) + if !s:is_managed(name) + call add(dirs, spec.dir) + else + let [err, clean] = s:git_validate(spec, 1) + if clean + let errs[spec.dir] = s:lines(err)[0] + else + call add(dirs, spec.dir) + endif + endif + let cnt += 1 + call s:progress_bar(2, repeat('=', cnt), total) + normal! 2G + redraw + endfor + + let allowed = {} + for dir in dirs + let allowed[s:dirpath(fnamemodify(dir, ':h:h'))] = 1 + let allowed[dir] = 1 + for child in s:glob_dir(dir) + let allowed[child] = 1 + endfor + endfor + + let todo = [] + let found = sort(s:glob_dir(g:plug_home)) + while !empty(found) + let f = remove(found, 0) + if !has_key(allowed, f) && isdirectory(f) + call add(todo, f) + call append(line('$'), '- ' . f) + if has_key(errs, f) + call append(line('$'), ' ' . errs[f]) + endif + let found = filter(found, 'stridx(v:val, f) != 0') + end + endwhile + + 4 + redraw + if empty(todo) + call append(line('$'), 'Already clean.') + else + let s:clean_count = 0 + call append(3, ['Directories to delete:', '']) + redraw! + if a:force || s:ask_no_interrupt('Delete all directories?') + call s:delete([6, line('$')], 1) + else + call setline(4, 'Cancelled.') + nnoremap d :set opfunc=delete_opg@ + nmap dd d_ + xnoremap d :call delete_op(visualmode(), 1) + echo 'Delete the lines (d{motion}) to delete the corresponding directories' + endif + endif + 4 + setlocal nomodifiable +endfunction + +function! s:delete_op(type, ...) + call s:delete(a:0 ? [line("'<"), line("'>")] : [line("'["), line("']")], 0) +endfunction + +function! s:delete(range, force) + let [l1, l2] = a:range + let force = a:force + while l1 <= l2 + let line = getline(l1) + if line =~ '^- ' && isdirectory(line[2:]) + execute l1 + redraw! + let answer = force ? 1 : s:ask('Delete '.line[2:].'?', 1) + let force = force || answer > 1 + if answer + call s:rm_rf(line[2:]) + setlocal modifiable + call setline(l1, '~'.line[1:]) + let s:clean_count += 1 + call setline(4, printf('Removed %d directories.', s:clean_count)) + setlocal nomodifiable + endif + endif + let l1 += 1 + endwhile +endfunction + +function! s:upgrade() + echo 'Downloading the latest version of vim-plug' + redraw + let tmp = tempname() + let new = tmp . '/plug.vim' + + try + let out = s:system(printf('git clone --depth 1 %s %s', s: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 L :call status_load(line('.')) + xnoremap L :call status_load(line('.')) + end +endfunction + +function! s:extract_name(str, prefix, suffix) + return matchstr(a:str, '^'.a:prefix.' \zs[^:]\+\ze:.*'.a:suffix.'$') +endfunction + +function! s:status_load(lnum) + let line = getline(a:lnum) + let name = s:extract_name(line, '-', '(not loaded)') + if !empty(name) + call plug#load(name) + setlocal modifiable + call setline(a:lnum, substitute(line, ' (not loaded)$', '', '')) + setlocal nomodifiable + endif +endfunction + +function! s:status_update() range + let lines = getline(a:firstline, a:lastline) + let names = filter(map(lines, 's:extract_name(v:val, "[x-]", "")'), '!empty(v:val)') + if !empty(names) + echo + execute 'PlugUpdate' join(names) + endif +endfunction + +function! s:is_preview_window_open() + silent! wincmd P + if &previewwindow + wincmd p + return 1 + endif +endfunction + +function! s:find_name(lnum) + for lnum in reverse(range(1, a:lnum)) + let line = getline(lnum) + if empty(line) + return '' + endif + let name = s:extract_name(line, '-', '') + if !empty(name) + return name + endif + endfor + return '' +endfunction + +function! s:preview_commit() + if b:plug_preview < 0 + let b:plug_preview = !s:is_preview_window_open() + endif + + let sha = matchstr(getline('.'), '^ \X*\zs[0-9a-f]\{7,9}') + if empty(sha) + return + endif + + let name = s:find_name(line('.')) + if empty(name) || !has_key(g:plugs, name) || !isdirectory(g:plugs[name].dir) + return + endif + + if exists('g:plug_pwindow') && !s:is_preview_window_open() + execute g:plug_pwindow + execute 'e' sha + else + execute 'pedit' sha + wincmd P + endif + setlocal previewwindow filetype=git buftype=nofile nobuflisted modifiable + try + let [sh, shellcmdflag, shrd] = s:chsh(1) + let cmd = 'cd '.s:shellesc(g:plugs[name].dir).' && git show --no-color --pretty=medium '.sha + if s:is_win + let batchfile = tempname().'.bat' + call writefile(['@echo off', cmd], batchfile) + let cmd = batchfile + endif + execute 'silent %!' cmd + finally + let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] + if s:is_win + call delete(batchfile) + endif + endtry + setlocal nomodifiable + nnoremap q :q + 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 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 + 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! call preview_commit() + nnoremap o :silent! call preview_commit() + endif + if cnts[0] + nnoremap X :call revert() + echo "Press 'X' on each block to revert the update" + endif + normal! gg + setlocal nomodifiable +endfunction + +function! s:revert() + if search('^Pending updates', 'bnW') + return + endif + + let name = s:find_name(line('.')) + if empty(name) || !has_key(g:plugs, name) || + \ input(printf('Revert the update of %s? (y/N) ', name)) !~? '^y' + return + endif + + call s:system('git reset --hard HEAD@{1} && git checkout '.s:esc(g:plugs[name].branch).' --', g:plugs[name].dir) + setlocal modifiable + normal! "_dap + setlocal nomodifiable + echo 'Reverted' +endfunction + +function! s:snapshot(force, ...) abort + call s:prepare() + setf vim + call append(0, ['" Generated by vim-plug', + \ '" '.strftime("%c"), + \ '" :source this file in vim to restore the snapshot', + \ '" or execute: vim -S snapshot.vim', + \ '', '', 'PlugUpdate!']) + 1 + let anchor = line('$') - 3 + let names = sort(keys(filter(copy(g:plugs), + \'has_key(v:val, "uri") && !has_key(v:val, "commit") && isdirectory(v:val.dir)'))) + for name in reverse(names) + let sha = s:system_chomp('git rev-parse --short HEAD', g:plugs[name].dir) + if !empty(sha) + call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha)) + redraw + endif + endfor + + if a:0 > 0 + let fn = expand(a:1) + if filereadable(fn) && !(a:force || s:ask(a:1.' already exists. Overwrite?')) + return + endif + call writefile(getline(1, '$'), fn) + echo 'Saved as '.a:1 + silent execute 'e' s:esc(fn) + setf vim + endif +endfunction + +function! s:split_rtp() + return split(&rtp, '\\\@:p')) +let s:base_spec = { 'branch': 'master', 'frozen': 0 } +let s:TYPE = { +\ 'string': type(''), +\ 'list': type([]), +\ 'dict': type({}), +\ 'funcref': type(function('call')) +\ } +let s:loaded = get(s:, 'loaded', {}) +let s:triggers = get(s:, 'triggers', {}) + +function! plug#begin(...) + if a:0 > 0 + let s:plug_home_org = a:1 + let home = s:path(fnamemodify(expand(a:1), ':p')) + elseif exists('g:plug_home') + let home = s:path(g:plug_home) + elseif !empty(&rtp) + let home = s:path(split(&rtp, ',')[0]) . '/plugged' + else + return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.') + endif + + 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 s:add() + if !executable('git') + return s:err('`git` executable not found. vim-plug requires git.') + endif + command! -nargs=* -bar -bang -complete=customlist,s:names PlugInstall call s:install('' == '!', []) + command! -nargs=* -bar -bang -complete=customlist,s:names PlugUpdate call s:update('' == '!', []) + command! -nargs=0 -bar -bang PlugClean call s:clean('' == '!') + 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 PlugSnapshot call s:snapshot() +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:source(from, ...) + for pattern in a:000 + for vim in s:lines(globpath(a:from, pattern)) + execute 'source' s:esc(vim) + endfor + endfor +endfunction + +function! s:assoc(dict, key, val) + let a:dict[a:key] = add(get(a:dict, a:key, []), a:val) +endfunction + +function! plug#end() + if !exists('g:plugs') + return s:err('Call plug#begin() first') + endif + + if exists('#PlugLOD') + augroup PlugLOD + autocmd! + augroup END + augroup! PlugLOD + endif + let lod = { 'ft': {}, 'map': {}, 'cmd': {} } + + filetype off + for name in g:plugs_order + let plug = g:plugs[name] + if get(s:loaded, name, 0) || !has_key(plug, 'on') && !has_key(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 =~? '^.\+' + 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]' + if exists(':'.cmd) != 2 + call s:assoc(lod.cmd, cmd, name) + endif + call add(s:triggers[name].cmd, cmd) + endif + endfor + endif + + if has_key(plug, 'for') + let types = s:to_a(plug.for) + if !empty(types) + call s:source(s:rtp(plug), 'ftdetect/**/*.vim', 'after/ftdetect/**/*.vim') + 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 %s call s:lod_cmd(%s, "", , , , %s)', + \ cmd, string(cmd), string(names)) + endfor + + for [map, names] in items(lod.map) + for [mode, map_prefix, key_prefix] in + \ [['i', '', ''], ['n', '', ''], ['v', '', 'gv'], ['o', '', '']] + execute printf( + \ '%snoremap %s %s:call lod_map(%s, %s, "%s")', + \ mode, map, map_prefix, string(map), string(names), key_prefix) + endfor + endfor + + for [ft, names] in items(lod.ft) + augroup PlugLOD + execute printf('autocmd FileType %s call lod_ft(%s, %s)', + \ ft, string(ft), string(names)) + augroup END + endfor + + call s:reorg_rtp() + filetype plugin indent on + if has('vim_starting') + syntax enable + else + call s:reload() + endif +endfunction + +function! s:loaded_names() + return filter(copy(g:plugs_order), 'get(s:loaded, v:val, 0)') +endfunction + +function! s:reload() + for name in s:loaded_names() + call s:source(s:rtp(g:plugs[name]), 'plugin/**/*.vim', 'after/plugin/**/*.vim') + 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(...) + let s:git_version = get(s:, 'git_version', + \ map(split(split(s:system('git --version'))[-1], '\.'), 'str2nr(v:val)')) + return s:version_requirement(s:git_version, a:000) +endfunction + +function! s:progress_opt(base) + return a:base && !s:is_win && + \ s:git_version_requirement(1, 7, 1) ? '--progress' : '' +endfunction + +if s:is_win + function! s:rtp(spec) + return s:path(a:spec.dir . get(a:spec, 'rtp', '')) + endfunction + + function! s:path(path) + return s:trim(substitute(a:path, '/', '\', 'g')) + endfunction + + function! s:dirpath(path) + return s:path(a:path) . '\' + endfunction + + function! s:is_local_plug(repo) + return a:repo =~? '^[a-z]:\|^[%~]' + endfunction +else + function! s:rtp(spec) + return s:dirpath(a:spec.dir . get(a:spec, 'rtp', '')) + endfunction + + function! s:path(path) + return s:trim(a:path) + endfunction + + function! s:dirpath(path) + return substitute(a:path, '[/\\]*$', '/', '') + endfunction + + function! s:is_local_plug(repo) + return a:repo[0] =~ '[/$~]' + endfunction +endif + +function! s:err(msg) + echohl ErrorMsg + echom a:msg + echohl None + return 0 +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")'), 'isdirectory(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! 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 unknowns = filter(copy(a:000), '!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 + for name in a:000 + call s:lod([name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) + endfor + if exists('#BufRead') + doautocmd BufRead + endif + return 1 +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 exists('#User#'.name) + execute 'doautocmd User' name + endif + endfor +endfunction + +function! s:lod_ft(pat, names) + call s:lod(a:names, ['plugin', 'after/plugin', 'syntax', 'after/syntax']) + execute 'autocmd! PlugLOD FileType' a:pat + if exists('#filetypeplugin#FileType') + doautocmd filetypeplugin FileType + endif + if exists('#filetypeindent#FileType') + doautocmd filetypeindent FileType + endif +endfunction + +function! s:lod_cmd(cmd, bang, l1, l2, args, names) + call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) + 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, prefix) + call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) + let extra = '' + while 1 + let c = getchar(0) + if c == 0 + break + endif + let extra .= nr2char(c) + endwhile + call feedkeys(a:prefix . substitute(a:map, '^', "\", '') . extra) +endfunction + +function! s:add(repo, ...) + if a:0 > 1 + return s:err('Invalid number of arguments (1..2)') + endif + + try + let repo = s:trim(a:repo) + let name = fnamemodify(repo, ':t:s?\.git$??') + let spec = extend(s:infer_properties(name, repo), + \ a:0 == 1 ? s:parse_options(a:1) : s:base_spec) + if !has_key(g:plugs, name) + call add(g:plugs_order, name) + endif + let g:plugs[name] = spec + let s:loaded[name] = get(s:loaded, name, 0) + catch + return s:err(v:exception) + endtry +endfunction + +function! s:parse_options(arg) + let opts = copy(s:base_spec) + let type = type(a:arg) + if type == s:TYPE.string + let opts.tag = a:arg + elseif type == s:TYPE.dict + call extend(opts, a:arg) + if has_key(opts, 'dir') + let opts.dir = s:dirpath(expand(opts.dir)) + endif + else + throw 'Invalid argument type (expected: string or dictionary)' + endif + return opts +endfunction + +function! s:infer_properties(name, repo) + let repo = a:repo + if s:is_local_plug(repo) + return { 'dir': s:dirpath(expand(repo)) } + else + if repo =~ ':' + let uri = repo + else + if repo !~ '/' + let repo = 'vim-scripts/'. repo + endif + let fmt = get(g:, 'plug_url_format', 'https://git::@github.com/%s.git') + let uri = printf(fmt, repo) + endif + let dir = s:dirpath( fnamemodify(join([g:plug_home, a:name], '/'), ':p') ) + return { 'dir': dir, '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([spec.dir, '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 plugInstall /\(^+ \)\@<=[^:]*/ + syn match plugUpdate /\(^* \)\@<=[^:]*/ + syn match plugCommit /^ [0-9a-z]\{7} .*/ contains=plugRelDate,plugSha + syn match plugSha /\(^ \)\@<=[0-9a-z]\{7}/ contained + syn match plugRelDate /([^)]*)$/ contained + syn match plugNotLoaded /(not loaded)$/ + syn match plugError /^x.*/ + syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean + hi def link plug1 Title + hi def link plug2 Repeat + 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 plugRelDate Comment + hi def link plugSha Identifier + + 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:prepare() + call s:job_abort() + if s:switch_in() + silent %d _ + else + call s:new_window() + nnoremap q :if b:plug_preview==1pcendifechoq + nnoremap R :silent! call retry() + nnoremap D :PlugDiff + nnoremap S :PlugStatus + nnoremap U :call status_update() + xnoremap U :call status_update() + nnoremap ]] :silent! call section('') + nnoremap [[ :silent! call section('b') + let b:plug_preview = -1 + let s:plug_tab = tabpagenr() + let s:plug_buf = winbufnr(0) + call s:assign_name() + endif + silent! unmap + silent! unmap L + silent! unmap o + silent! unmap X + setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap cursorline modifiable + setf vim-plug + call s:syntax() +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: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 && !empty(s:system_chomp('git log --pretty=format:"%h" "HEAD...HEAD@{1}"', spec.dir))) + if a:force || installed || updated + execute 'cd' s:esc(spec.dir) + call append(3, '- Post-update hook for '. name .' ... ') + let type = type(spec.do) + if type == s:TYPE.string + try + " FIXME: Escaping is incomplete. We could use shellescape with eval, + " but it won't work on Windows. + let g:_plug_do = '!'.escape(spec.do, '#!%') + execute "normal! :execute g:_plug_do\\" + finally + let result = v:shell_error ? ('Exit status: '.v:shell_error) : 'Done!' + unlet g:_plug_do + endtry + elseif type == s:TYPE.funcref + try + let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged') + call spec.do({ 'name': name, 'status': status, 'force': a:force }) + let result = 'Done!' + catch + let result = 'Error: ' . v:exception + endtry + else + let result = 'Error: Invalid type!' + endif + call setline(4, getline(4) . result) + cd - + endif + endfor +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 ... ') + 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 =~ '^- ' && stridx(v:val, 'Already up-to-date') < 0")) + call add(msgs, "Press 'D' to see the updated changes.") + endif + echo join(msgs, ' ') +endfunction + +function! s:retry() + if empty(s:update.errors) + return + endif + 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:update_impl(pull, force, args) abort + let args = copy(a:args) + let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ? + \ remove(args, -1) : get(g:, 'plug_threads', s:is_win ? 1 : 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) + echohl WarningMsg + echo 'No plugin to '. (a:pull ? 'update' : 'install') . '.' + echohl None + return + 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 + echohl WarningMsg + echomsg 'vim-plug: update Neovim for parallel installer' + echohl None + endif + + let python = (has('python') || has('python3')) && !s:is_win && !has('win32unix') + \ && (!s:nvim || has('vim_starting')) + let ruby = has('ruby') && !s:nvim && (v:version >= 703 || v:version == 702 && has('patch374')) + + let s:update = { + \ 'start': reltime(), + \ 'all': todo, + \ 'todo': copy(todo), + \ 'errors': [], + \ 'pull': a:pull, + \ 'force': a:force, + \ 'new': {}, + \ 'threads': (python || ruby || s:nvim) ? min([len(todo), threads]) : 1, + \ 'bar': '', + \ 'fin': 0 + \ } + + call s:prepare() + call append(0, ['', '']) + normal! 2G + silent! redraw + + let s:clone_opt = get(g:, 'plug_shallow', 1) ? + \ '--depth 1' . (s:git_version_requirement(1, 7, 10) ? ' --no-single-branch' : '') : '' + + " Python version requirement (>= 2.7) + if python && !has('python3') && !ruby && !s:nvim && 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() + endif +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 s:do(s:update.pull, s:update.force, filter(copy(s:update.all), 'has_key(v:val, "do")')) + 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 || !exists('s:jobs') + return + endif + + for [name, j] in items(s:jobs) + silent! call jobstop(j.jobid) + if j.new + call s:system('rm -rf ' . s:shellesc(g:plugs[name].dir)) + endif + endfor + let s:jobs = {} +endfunction + +" When a:event == 'stdout', data = list of strings +" When a:event == 'exit', data = returncode +function! s:job_handler(job_id, data, event) abort + if !s:plug_window_exists() " plug window closed + return s:job_abort() + endif + + if a:event == 'stdout' + let self.result .= substitute(s:to_s(a:data), '[\r\n]', '', 'g') . "\n" + " To reduce the number of buffer updates + let self.tick = get(self, 'tick', -1) + 1 + if self.tick % len(s:jobs) == 0 + call s:log(self.new ? '+' : '*', self.name, self.result) + endif + elseif a:event == 'exit' + let self.running = 0 + if a:data != 0 + let self.error = 1 + endif + call s:reap(self.name) + call s:tick() + endif +endfunction + +function! s:spawn(name, cmd, opts) + let job = { 'name': a:name, 'running': 1, 'error': 0, 'result': '', + \ 'new': get(a:opts, 'new', 0), + \ 'on_stdout': function('s:job_handler'), + \ 'on_exit' : function('s:job_handler'), + \ } + let s:jobs[a:name] = job + + if s:nvim + let argv = [ 'sh', '-c', + \ (has_key(a:opts, 'dir') ? s:with_cd(a:cmd, a:opts.dir) : a:cmd) ] + let jid = jobstart(argv, job) + if jid > 0 + let job.jobid = jid + else + let job.running = 0 + let job.error = 1 + let job.result = jid < 0 ? 'sh is not executable' : + \ 'Invalid arguments (or job table is full)' + endif + else + let params = has_key(a:opts, 'dir') ? [a:cmd, a:opts.dir] : [a:cmd] + let job.result = call('s:system', params) + 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' : '=' + + call s:log(job.error ? 'x' : '-', a:name, job.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) + for i in range(1, line('$')) + if getline(i) =~# '^[-+x*] '.a:name.':' + return i + endif + endfor + return 0 +endfunction + +function! s:log(bullet, name, lines) + if s:switch_in() + let pos = s:logpos(a:name) + if pos > 0 + execute pos 'd _' + if pos > winheight('.') + let pos = 4 + endif + else + let pos = 4 + endif + call append(pos - 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) +while 1 " Without TCO, Vim stack is bound to explode + if empty(s:update.todo) + if empty(s:jobs) && !s:update.fin + let s:update.fin = 1 + call s:update_finish() + endif + return + endif + + let name = keys(s:update.todo)[0] + let spec = remove(s:update.todo, name) + let new = !isdirectory(spec.dir) + + call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...') + redraw + + let has_tag = has_key(spec, 'tag') + let checkout = s:shellesc(has_tag ? spec.tag : spec.branch) + let merge = s:shellesc(has_tag ? spec.tag : 'origin/'.spec.branch) + + if !new + let [valid, msg] = s:git_valid(spec, 0) + if valid + 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 && git checkout -q %s 2>&1 && git merge --ff-only %s 2>&1 && git submodule update --init --recursive 2>&1)', + \ fetch_opt, prog, checkout, merge), { 'dir': spec.dir }) + else + let s:jobs[name] = { 'running': 0, 'result': 'Already installed', 'error': 0 } + endif + else + let s:jobs[name] = { 'running': 0, 'result': msg, 'error': 1 } + endif + else + call s:spawn(name, + \ printf('git clone %s %s --recursive %s -b %s %s 2>&1', + \ has_tag ? '' : s:clone_opt, + \ prog, + \ s:shellesc(spec.uri), + \ checkout, + \ s:shellesc(s:trim(spec.dir))), { 'new': 1 }) + endif + + if !s:jobs[name].running + call s:reap(name) + endif + if len(s:jobs) >= s:update.threads + break + endif +endwhile +endfunction + +function! s:update_python() +let py_exe = has('python3') ? 'python3' : 'python' +execute py_exe "<< EOF" +""" Due to use of signals this function is POSIX only. """ +import datetime +import functools +import os +try: + import queue +except ImportError: + import Queue as queue +import random +import re +import shutil +import signal +import subprocess +import tempfile +import threading as thr +import time +import traceback +import vim + +G_NVIM = vim.eval("has('nvim')") == '1' +G_PULL = vim.eval('s:update.pull') == '1' +G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1 +G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)')) +G_CLONE_OPT = vim.eval('s:clone_opt') +G_PROGRESS = vim.eval('s:progress_opt(1)') +G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads')) +G_STOP = thr.Event() +G_THREADS = {} + +class BaseExc(Exception): + def __init__(self, msg): + self._msg = msg + @property + def msg(self): + return self._msg +class CmdTimedOut(BaseExc): + pass +class CmdFailed(BaseExc): + pass +class InvalidURI(BaseExc): + pass +class Action(object): + INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-'] + +class Buffer(object): + def __init__(self, lock, num_plugs, is_pull, is_win): + self.bar = '' + self.event = 'Updating' if is_pull else 'Installing' + self.is_win = is_win + 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') + if not self.is_win: + 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)] + padded_rest = [' ' + line for line in rest] + msg.extend(padded_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): + def __init__(self, cmd, cmd_dir=None, timeout=60, ntries=3, cb=None, clean=None): + self.cmd = cmd + self.cmd_dir = cmd_dir + self.timeout = timeout + self.ntries = ntries + self.callback = cb if cb else (lambda msg: None) + self.clean = clean + + def attempt_cmd(self): + """ Tries to run the command, returns result if no exceptions. """ + attempt = 0 + finished = False + limit = self.timeout + + while not finished: + try: + attempt += 1 + result = self.timeout_cmd() + finished = True + except CmdTimedOut: + if attempt != self.ntries: + 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.timeout += limit + self.callback(['Retrying ...']) + else: + raise + + return result + + def timeout_cmd(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 + """ + proc = None + first_line = True + try: + tfile = tempfile.NamedTemporaryFile(mode='w+b', delete=False) + proc = subprocess.Popen(self.cmd, cwd=self.cmd_dir, stdout=tfile, + stderr=subprocess.STDOUT, shell=True, preexec_fn=os.setsid) + while proc.poll() is None: + # Yield this thread + time.sleep(0.2) + + if G_STOP.is_set(): + raise KeyboardInterrupt + + if first_line or random.random() < G_LOG_PROB: + first_line = False + line = 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!']) + + tfile.seek(0) + result = [line.decode('utf-8', 'replace').rstrip() for line in tfile] + + if proc.returncode != 0: + msg = [''] + msg.extend(result) + raise CmdFailed(msg) + except: + if proc and proc.poll() is None: + os.killpg(proc.pid, signal.SIGTERM) + if self.clean: + self.clean() + raise + finally: + os.remove(tfile.name) + + return result + +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 + tag = args.get('tag', 0) + self.checkout = esc(tag if tag else args['branch']) + self.merge = esc(tag if tag else 'origin/' + args['branch']) + self.tag = tag + + 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 (CmdTimedOut, CmdFailed, InvalidURI) 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'] + + 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} --recursive {2} -b {3} {4} 2>&1'.format( + '' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'], self.checkout, esc(target)) + com = Command(cmd, None, G_TIMEOUT, G_RETRIES, callback, clean(target)) + result = com.attempt_cmd() + self.write(Action.DONE, self.name, result[-1:]) + + def update(self): + match = re.compile(r'git::?@') + actual_uri = re.sub(match, '', self.repo_uri()) + expect_uri = re.sub(match, '', self.args['uri']) + if actual_uri != expect_uri: + 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 '' + cmds = ['git fetch {0} {1}'.format(fetch_opt, G_PROGRESS), + 'git checkout -q {0}'.format(self.checkout), + 'git merge --ff-only {0}'.format(self.merge), + 'git submodule update --init --recursive'] + cmd = ' 2>&1 && '.join(cmds) + com = Command(cmd, self.args['dir'], G_TIMEOUT, G_RETRIES, callback) + result = com.attempt_cmd() + self.write(Action.DONE, self.name, result[-1:]) + else: + self.write(Action.DONE, self.name, ['Already installed']) + + def repo_uri(self): + cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url' + command = Command(cmd, self.args['dir'], G_TIMEOUT, G_RETRIES) + result = command.attempt_cmd() + return result[-1] + + 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 + finally: + global G_THREADS + with lock: + del G_THREADS[thr.current_thread().name] + +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.2) + + 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' + is_win = vim.eval('s:is_win') == '1' + + lock = thr.Lock() + buf = Buffer(lock, len(plugs), G_PULL, is_win) + buf_q, work_q = queue.Queue(), queue.Queue() + for work in plugs.items(): + work_q.put(work) + + global G_THREADS + for num in range(nthreads): + tname = 'PlugT-{0:02}'.format(num) + thread = PlugThread(tname, (buf_q, work_q, lock)) + thread.start() + G_THREADS[tname] = thread + if mac_gui: + rthread = RefreshThread(lock) + rthread.start() + + while not buf_q.empty() or len(G_THREADS) != 0: + try: + action, name, msg = buf_q.get(True, 0.25) + buf.write(action, name, 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] + 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 + + 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 + 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') unless iswin + } + 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}"] + 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 { + while VIM::evaluate('getchar(1)') + sleep 0.1 + end + mtx.synchronize do + running = false + threads.each { |t| t.raise Interrupt } + end + threads.each { |t| t.join rescue nil } + main.kill + } + refresh = Thread.new { + while true + mtx.synchronize do + break unless running + VIM::command('noautocmd normal! a') + end + sleep 0.2 + end + } if VIM::evaluate('s:mac_gui') == 1 + + clone_opt = VIM::evaluate('s:clone_opt') + progress = VIM::evaluate('s:progress_opt(1)') + nthr.times do + mtx.synchronize do + threads << Thread.new { + while pair = take1.call + name = pair.first + dir, uri, branch, tag = pair.last.values_at *%w[dir uri branch tag] + checkout = esc(tag ? tag : branch) + merge = esc(tag ? tag : "origin/#{branch}") + subm = "git submodule update --init --recursive 2>&1" + exists = File.directory? dir + ok, result = + if exists + dir = iswin ? dir : esc(dir) + ret, data = bt.call "#{cd} #{dir} && git rev-parse --abbrev-ref HEAD 2>&1 && 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 current_uri.sub(/git::?@/, '') != uri.sub(/git::?@/, '') + [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 "#{cd} #{dir} && git fetch #{fetch_opt} #{progress} 2>&1 && git checkout -q #{checkout} 2>&1 && git merge --ff-only #{merge} 2>&1 && #{subm}", 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} --recursive #{uri} -b #{checkout} #{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(arg) + return '"'.escape(a:arg, '"').'"' +endfunction + +function! s:glob_dir(path) + return map(filter(s:lines(globpath(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) + let a = substitute(a:a, 'git:\{1,2}@', '', '') + let b = substitute(a:b, 'git:\{1,2}@', '', '') + return a ==# b +endfunction + +function! s:format_message(bullet, name, message) + if a:bullet != 'x' + return [printf('%s %s: %s', a:bullet, a:name, s:lastline(a:message))] + else + let lines = map(s:lines(a:message), '" ".v:val') + return extend([printf('x %s:', a:name)], lines) + endif +endfunction + +function! s:with_cd(cmd, dir) + return printf('cd%s %s && %s', s:is_win ? ' /d' : '', s:shellesc(a:dir), a:cmd) +endfunction + +function! s:system(cmd, ...) + try + let [sh, shrd] = [&shell, &shellredir] + if !s:is_win + set shell=sh shellredir=>%s\ 2>&1 + endif + let cmd = a:0 > 0 ? s:with_cd(a:cmd, a:1) : a:cmd + return system(s:is_win ? '('.cmd.')' : cmd) + finally + let [&shell, &shellredir] = [sh, shrd] + endtry +endfunction + +function! s:system_chomp(...) + let ret = call('s:system', a:000) + return v:shell_error ? '' : substitute(ret, '\n$', '', '') +endfunction + +function! s:git_valid(spec, check_branch) + let ret = 1 + let msg = 'OK' + if isdirectory(a:spec.dir) + let result = s:lines(s:system('git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url', a:spec.dir)) + let remote = result[-1] + if v:shell_error + let msg = join([remote, 'PlugClean required.'], "\n") + let ret = 0 + elseif !s:compare_git_uri(remote, a:spec.uri) + let msg = join(['Invalid URI: '.remote, + \ 'Expected: '.a:spec.uri, + \ 'PlugClean required.'], "\n") + let ret = 0 + 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 + let msg = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.', + \ (empty(tag) ? 'N/A' : tag), a:spec.tag) + let ret = 0 + endif + " Check branch + elseif a:spec.branch !=# branch + let msg = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.', + \ branch, a:spec.branch) + let ret = 0 + endif + endif + else + let msg = 'Not found' + let ret = 0 + endif + return [ret, msg] +endfunction + +function! s:rm_rf(dir) + if isdirectory(a:dir) + call s:system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . s:shellesc(a:dir)) + endif +endfunction + +function! s:clean(force) + call s:prepare() + call append(0, 'Searching for unused plugins in '.g:plug_home) + call append(1, '') + + " List of valid directories + let dirs = [] + let [cnt, total] = [0, len(g:plugs)] + for [name, spec] in items(g:plugs) + if !s:is_managed(name) || s:git_valid(spec, 0)[0] + call add(dirs, spec.dir) + endif + let cnt += 1 + call s:progress_bar(2, repeat('=', cnt), total) + normal! 2G + redraw + endfor + + let allowed = {} + for dir in dirs + let allowed[s:dirpath(fnamemodify(dir, ':h:h'))] = 1 + let allowed[dir] = 1 + for child in s:glob_dir(dir) + let allowed[child] = 1 + endfor + endfor + + let todo = [] + let found = sort(s:glob_dir(g:plug_home)) + while !empty(found) + let f = remove(found, 0) + if !has_key(allowed, f) && isdirectory(f) + call add(todo, f) + call append(line('$'), '- ' . f) + let found = filter(found, 'stridx(v:val, f) != 0') + end + endwhile + + normal! G + redraw + if empty(todo) + call append(line('$'), 'Already clean.') + else + call inputsave() + let yes = a:force || (input('Proceed? (y/N) ') =~? '^y') + call inputrestore() + if yes + for dir in todo + call s:rm_rf(dir) + endfor + call append(line('$'), 'Removed.') + else + call append(line('$'), 'Cancelled.') + endif + endif + normal! G +endfunction + +function! s:upgrade() + echo 'Downloading the latest version of vim-plug' + redraw + let tmp = tempname() + let new = tmp . '/plug.vim' + + try + let out = s:system(printf('git clone --depth 1 %s %s', s: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) + if has_key(spec, 'uri') + if isdirectory(spec.dir) + let [valid, msg] = s:git_valid(spec, 1) + else + let [valid, msg] = [0, 'Not found. Try PlugInstall.'] + endif + else + if isdirectory(spec.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 valid && 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 L :call status_load(line('.')) + xnoremap L :call status_load(line('.')) + 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 + return 0 +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('.'), '\(^ \)\@<=[0-9a-z]\{7}') + 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 + + execute 'pedit' sha + wincmd P + setlocal filetype=git buftype=nofile nobuflisted modifiable + execute 'silent read !cd' s:shellesc(g:plugs[name].dir) '&& git show --pretty=medium' sha + normal! gg"_dd + setlocal nomodifiable + nnoremap q :q + wincmd p +endfunction + +function! s:section(flags) + call search('\(^[x-] \)\@<=[^:]\+:', a:flags) +endfunction + +function! s:diff() + call s:prepare() + call append(0, 'Collecting updated changes ...') + normal! gg + redraw + + let cnt = 0 + for [k, v] in items(g:plugs) + if !isdirectory(v.dir) || !s:is_managed(k) + continue + endif + + let diff = s:system_chomp('git log --pretty=format:"%h %s (%cr)" "HEAD...HEAD@{1}"', v.dir) + if !empty(diff) + call append(1, '') + call append(2, '- '.k.':') + call append(3, map(s:lines(diff), '" ". v:val')) + let cnt += 1 + normal! gg + redraw + endif + endfor + + call setline(1, cnt == 0 ? 'No updates.' : 'Last update:') + nnoremap :silent! call preview_commit() + nnoremap o :silent! call preview_commit() + nnoremap X :call revert() + normal! gg + setlocal nomodifiable + if cnt > 0 + echo "Press 'X' on each block to revert the update" + endif +endfunction + +function! s:revert() + let name = s:find_name(line('.')) + if empty(name) || !has_key(g:plugs, name) || + \ input(printf('Revert the update of %s? (y/N) ', name)) !~? '^y' + return + endif + + call s:system('git reset --hard HEAD@{1} && git checkout '.s:esc(g:plugs[name].branch), g:plugs[name].dir) + setlocal modifiable + normal! "_dap + setlocal nomodifiable + echo 'Reverted.' +endfunction + +function! s:snapshot(...) abort + let home = get(s:, 'plug_home_org', g:plug_home) + let [type, var, header] = s:is_win ? + \ ['dosbatch', '%PLUG_HOME%', + \ ['@echo off', ':: Generated by vim-plug', ':: '.strftime("%c"), '', + \ ':: Make sure to PlugUpdate first', '', 'set PLUG_HOME='.home]] : + \ ['sh', '$PLUG_HOME', + \ ['#!/bin/sh', '# Generated by vim-plug', '# '.strftime("%c"), '', + \ 'vim +PlugUpdate +qa', '', 'PLUG_HOME='.s:esc(home)]] + + call s:prepare() + execute 'setf' type + call append(0, header) + call append('$', '') + 1 + redraw + + let dirs = sort(map(values(filter(copy(g:plugs), + \'has_key(v:val, "uri") && isdirectory(v:val.dir)')), 'v:val.dir')) + let anchor = line('$') - 1 + for dir in reverse(dirs) + let sha = s:system_chomp('git rev-parse --short HEAD', dir) + if !empty(sha) + call append(anchor, printf('cd %s && git reset --hard %s', + \ substitute(dir, '^\V'.escape(g:plug_home, '\'), var, ''), sha)) + redraw + endif + endfor + + if a:0 > 0 + let fn = expand(a:1) + let fne = s:esc(fn) + call writefile(getline(1, '$'), fn) + if !s:is_win | call s:system('chmod +x ' . fne) | endif + echo 'Saved to '.a:1 + silent execute 'e' fne + endif +endfunction + +function! s:split_rtp() + return split(&rtp, '\\\@'] +let expanderrific_collapse = ['h', ''] + +for key in expanderrific_expand + call NERDTreeAddKeyMap({'key': key, 'scope': 'DirNode', 'callback': 'NERDTreeExpanderrificExpand'}) +endfor + +for key in expanderrific_collapse + call NERDTreeAddKeyMap({'key': key, 'scope': 'Node', 'callback': 'NERDTreeExpanderrificCollapse'}) +endfor + +function! NERDTreeExpanderrificExpand(dir) + let opts = {'reuse': 1} + if g:NERDTreeCascadeOpenSingleChildDir == 0 + call a:dir.open(opts) + else + call a:dir.openAlong(opts) + endif + call b:NERDTree.render() +endfunction + +function! NERDTreeExpanderrificCollapse(node) + let node = a:node + if !a:node.path.isDirectory || !a:node.isOpen + let node = a:node.parent + endif + if node != b:NERDTreeRoot + call node.close() + call b:NERDTree.render() + call node.putCursorHere(0, 0) + endif +endfunction + diff --git a/link/.vim/plugin/bclose.vim b/link/.vim/plugin/bclose.vim new file mode 100644 index 0000000..0c8e1ce --- /dev/null +++ b/link/.vim/plugin/bclose.vim @@ -0,0 +1,75 @@ +" Modified to use :BD or :BD! as the command, and to prevent 'Press ENTER' +" prompt when run the first time (sometimes) - Cowboy + +" Delete buffer while keeping window layout (don't close buffer's windows). +" Version 2008-11-18 from http://vim.wikia.com/wiki/VimTip165 +if v:version < 700 || exists('loaded_bclose') || &cp + finish +endif +let loaded_bclose = 1 +if !exists('bclose_multiple') + let bclose_multiple = 1 +endif + +" Display an error message. +function! s:Warn(msg) + echohl ErrorMsg + echomsg a:msg + echohl NONE +endfunction + +" Command ':Bclose' executes ':bd' to delete buffer in current window. +" The window will show the alternate buffer (Ctrl-^) if it exists, +" or the previous buffer (:bp), or a blank buffer if no previous. +" Command ':Bclose!' is the same, but executes ':bd!' (discard changes). +" An optional argument can specify which buffer to close (name or number). +function! s:Bclose(bang, buffer) + if empty(a:buffer) + let btarget = bufnr('%') + elseif a:buffer =~ '^\d\+$' + let btarget = bufnr(str2nr(a:buffer)) + else + let btarget = bufnr(a:buffer) + endif + if btarget < 0 + call s:Warn('No matching buffer for '.a:buffer) + return + endif + if empty(a:bang) && getbufvar(btarget, '&modified') + call s:Warn('No write since last change for buffer '.btarget.' (use :BD!)') + return + endif + " Numbers of windows that view target buffer which we will delete. + let wnums = filter(range(1, winnr('$')), 'winbufnr(v:val) == btarget') + if !g:bclose_multiple && len(wnums) > 1 + call s:Warn('Buffer is in multiple windows (use ":let bclose_multiple=1")') + return + endif + let wcurrent = winnr() + for w in wnums + execute w.'wincmd w' + let prevbuf = bufnr('#') + if prevbuf > 0 && buflisted(prevbuf) && prevbuf != w + buffer # + else + silent bprevious + endif + if btarget == bufnr('%') + " Numbers of listed buffers which are not the target to be deleted. + let blisted = filter(range(1, bufnr('$')), 'buflisted(v:val) && v:val != btarget') + " Listed, not target, and not displayed. + let bhidden = filter(copy(blisted), 'bufwinnr(v:val) < 0') + " Take the first buffer, if any (could be more intelligent). + let bjump = (bhidden + blisted + [-1])[0] + if bjump > 0 + execute 'buffer '.bjump + else + execute 'enew'.a:bang + endif + endif + endfor + execute 'bdelete'.a:bang.' '.btarget + execute wcurrent.'wincmd w' +endfunction +command! -bang -complete=buffer -nargs=? BD call s:Bclose('', '') +" nnoremap bd :Bclose diff --git a/link/.vimrc b/link/.vimrc new file mode 100644 index 0000000..bd29a9f --- /dev/null +++ b/link/.vimrc @@ -0,0 +1,233 @@ +" Change mapleader +let mapleader="," + +" Now ; works just like : but with 866% less keypresses! +nnoremap ; : + +" Move more naturally up/down when wrapping is enabled. +nnoremap j gj +nnoremap k gk + +" Local dirs +set backupdir=$DOTFILES/caches/vim +set directory=$DOTFILES/caches/vim +let g:netrw_home = expand('$DOTFILES/caches/vim') + +" Visual settings +set cursorline " Highlight current line +set number " Enable line numbers. +set showtabline=2 " Always show tab bar. +set title " Show the filename in the window titlebar. +set noshowmode " Don't show the current mode (airline.vim takes care of us) +set laststatus=2 " Always show status line + +" Make it obvious where 80 characters is +set textwidth=80 +" Don't always wrap at 80 +set formatoptions-=t + + +" Scrolling +set scrolloff=3 " Start scrolling three lines before horizontal border of window. +set sidescrolloff=3 " Start scrolling three columns before vertical border of window. + +" Indentation +" Defaults +set autoindent " Copy indent from last line when starting new line. +set shiftwidth=4 " The # of spaces for indenting. +set smarttab " At start of line, inserts shiftwidth spaces, deletes shiftwidth spaces. +set softtabstop=4 " Tab key results in 4 spaces +set tabstop=4 " Tabs indent only 4 spaces +set expandtab " Expand tabs to spaces + +" Python PEP8 +au BufNewFile,BufRead *.py + \ set tabstop=4 | + \ set softtabstop=4 | + \ set shiftwidth=4 | + \ set textwidth=79 | + \ set expandtab | + \ set autoindent | + \ set fileformat=unix + +" Web +au BufNewFile,BufRead *.js, *.html, *.css + \ set tabstop=2 | + \ set softtabstop=2 | + \ set shiftwidth=2 + +" Reformatting +set nojoinspaces " Only insert single space after a '.', '?' and '!' with a join command. + +" Toggle show tabs and trailing spaces (,c) +set listchars=tab:▸\ ,trail:·,eol:¬,nbsp:_,extends:>,precedes:< +"set listchars=tab:>\ ,trail:.,eol:$,nbsp:_,extends:>,precedes:< +"set fillchars=fold:- +nnoremap v :call ToggleInvisibles() + +" Extra whitespace +augroup highlight_extra_whitespace + autocmd! + autocmd BufWinEnter * :2match ExtraWhitespaceMatch /\s\+$/ + autocmd InsertEnter * :2match ExtraWhitespaceMatch /\s\+\%#\@ss :call StripExtraWhiteSpace() + +" Search / replace +set gdefault " By default add g flag to search/replace. Add g to toggle. +set hlsearch " Highlight searches +set incsearch " Highlight dynamically as pattern is typed. +set ignorecase " Ignore case of searches. +set smartcase " Ignore 'ignorecase' if search pattern contains uppercase characters. + +" Clear last search +map / :nohlsearch + +" Ignore things +set wildignore+=*.jpg,*.jpeg,*.gif,*.png,*.gif,*.psd,*.o,*.obj,*.min.js +set wildignore+=*/bower_components/*,*/node_modules/* +set wildignore+=*/vendor/*,*/.git/*,*/.hg/*,*/.svn/*,*/log/*,*/tmp/* + +" Vim commands +set hidden " When a buffer is brought to foreground, remember undo history and marks. +set report=0 " Show all changes. +set mouse=a " Enable mouse in all modes. +set shortmess+=I " Hide intro menu. + +" Splits +set splitbelow " New split goes below +set splitright " New split goes right + +" Ctrl-J/K/L/H select split +nnoremap j +nnoremap k +nnoremap l +nnoremap h + +" Buffer navigation +nnoremap b :CtrlPBuffer " List other buffers +map :b# " Switch between the last two files +map gb :bnext " Next buffer +map gB :bprev " Prev buffer + +" Jump to buffer number 1-9 with , or 1-99 with gb +let c = 1 +while c <= 99 + if c < 10 + execute "nnoremap " . c . " :" . c . "b" + endif + execute "nnoremap " . c . "gb :" . c . "b" + let c += 1 +endwhile + +" Fix page up and down +map +map +imap +imap + +" Use Q for formatting the current paragraph (or selection) +" vmap Q gq +" nmap Q gqap + +" When editing a file, always jump to the last known cursor position. Don't do +" it for commit messages, when the position is invalid, or when inside an event +" handler (happens when dropping a file on gvim). +augroup vimrcEx + autocmd! + autocmd BufReadPost * + \ if &ft != 'gitcommit' && line("'\"") > 0 && line("'\"") <= line("$") | + \ exe "normal g`\"" | + \ endif +augroup END + +" F12: Source .vimrc & .gvimrc files +nmap :call SourceConfigs() + +if !exists("*SourceConfigs") + function! SourceConfigs() + let files = ".vimrc" + source $MYVIMRC + if has("gui_running") + let files .= ", .gvimrc" + source $MYGVIMRC + endif + echom "Sourced " . files + endfunction +endif + +"" FILE TYPES +augroup file_types + autocmd! + + " vim + autocmd BufRead .vimrc,*.vim set keywordprg=:help + +augroup END + +" PLUGINS + +" Airline +let g:airline#extensions#tabline#enabled = 1 +let g:airline#extensions#tabline#buffer_nr_format = '%s ' +let g:airline#extensions#tabline#buffer_nr_show = 1 +let g:airline_theme='papercolor' +let g:airline_powerline_fonts = 1 + +" CtrlP.vim +map p +map r :CtrlPMRUFiles +"let g:ctrlp_match_window_bottom = 0 " Show at top of window + +" https://github.com/junegunn/vim-plug +" Reload .vimrc and :PlugInstall to install plugins. +let g:plug_threads = 1 +call plug#begin('~/.vim/plugged') +Plug 'vim-airline/vim-airline' +Plug 'vim-airline/vim-airline-themes' +Plug 'tpope/vim-sensible' +Plug 'ctrlpvim/ctrlp.vim' +Plug 'plasticboy/vim-markdown' +Plug 'vim-scripts/indentpython.vim' +Plug 'scrooloose/syntastic' +Plug 'jistr/vim-nerdtree-tabs' +Plug 'airblade/vim-gitgutter' +Plug 'tpope/vim-commentary' +call plug#end() + +" MacVim Font +set gfn=Monaco:h14 + +" Color scheme +set background=dark +colorscheme monokai + +" Better Python Highlighting +let python_highlight_all=1 +syntax enable + +" Markdown do not fold +let g:vim_markdown_folding_disabled = 1 + diff --git a/manual/Package Control.sublime-settings b/manual/Package Control.sublime-settings new file mode 100644 index 0000000..2a0833e --- /dev/null +++ b/manual/Package Control.sublime-settings @@ -0,0 +1,36 @@ +{ + "bootstrapped": true, + "in_process_packages": + [ + ], + "installed_packages": + [ + "A File Icon", + "ApacheConf.tmLanguage", + "AutoPEP8", + "BracketHighlighter", + "Color Highlighter", + "DocBlockr", + "Dockerfile Syntax Highlighting", + "Emmet", + "GitGutter", + "Markdown Preview", + "MarkdownEditing", + "Package Control", + "Puppet", + "Python Fix Imports", + "SideBarEnhancements", + "SublimeCodeIntel", + "SublimeLinter", + "SublimeLinter-contrib-eslint", + "SublimeLinter-contrib-markdownlint", + "SublimeLinter-csslint", + "SublimeLinter-flake8", + "SublimeLinter-pyyaml", + "SublimeLinter-shellcheck", + "SublimeREPL", + "Sync Settings", + "Theme - Soda", + "Vagrant" + ] +} diff --git a/manual/Preferences.sublime-settings b/manual/Preferences.sublime-settings new file mode 100644 index 0000000..5c9e9fc --- /dev/null +++ b/manual/Preferences.sublime-settings @@ -0,0 +1,27 @@ +{ + "color_scheme": "Packages/User/SublimeLinter/Monokai Bright (SL).tmTheme", + "font_face": "Roboto Mono for Powerline", + "font_options": "subpixel_antialiase", + "font_size": 11, + "highlight_line": true, + "highlight_modified_tabs": true, + "ignored_packages": + [ + "Markdown", + "Vintage" + ], + "indent_guide_options": + [ + "draw_normal", + "draw_active" + ], + "rulers": + [ + 80, + 100 + ], + "tab_size": 4, + "theme": "Soda Light.sublime-theme", + "translate_tabs_to_spaces": true, + "trim_trailing_white_space_on_save": true +} diff --git a/source/00_dotfiles.sh b/source/00_dotfiles.sh new file mode 100644 index 0000000..a6eb67b --- /dev/null +++ b/source/00_dotfiles.sh @@ -0,0 +1,2 @@ +# Passing the "source" arg tells it to only define functions, then quit. +source $DOTFILES/bin/dotfiles "source" diff --git a/source/10_user_bin.sh b/source/10_user_bin.sh new file mode 100644 index 0000000..9dd3353 --- /dev/null +++ b/source/10_user_bin.sh @@ -0,0 +1,3 @@ +export PATH="$HOME/bin:$HOME/.local/bin:$PATH:$HOME/.npm-packages/bin" + + diff --git a/source/45_linuxbrew.sh b/source/45_linuxbrew.sh new file mode 100644 index 0000000..b95651f --- /dev/null +++ b/source/45_linuxbrew.sh @@ -0,0 +1,17 @@ +if [ -d $HOME/.linuxbrew ]; then + + green='\033[0;32m' + NC='\033[0m' # No Color + echo -e "${green}[dotfiles] linuxbrew available to activate - abrew${NC}" + + abrew () + { + + export PATH="$HOME/.linuxbrew/bin:$HOME/.linuxbrew/sbin:$PATH" + export MANPATH="$HOME/.linuxbrew/share/man:$MANPATH" + export INFOPATH="$HOME/.linuxbrew/share/info:$INFOPATH" + + } + +fi + diff --git a/source/50_anaconda.sh b/source/50_anaconda.sh new file mode 100644 index 0000000..790ee94 --- /dev/null +++ b/source/50_anaconda.sh @@ -0,0 +1,15 @@ + +# anaconda + +if [ -d "$HOME/anaconda/bin" ]; then + + green='\033[0;32m' + NC='\033[0m' # No Color + echo -e "${green}[dotfiles] anaconda available to activate - aconda{NC}" + + + export PATH="$HOME/anaconda/bin:$PATH" + +fi + + diff --git a/source/50_dircolors.sh b/source/50_dircolors.sh new file mode 100644 index 0000000..20dbd40 --- /dev/null +++ b/source/50_dircolors.sh @@ -0,0 +1,4 @@ +if hash dircolors 2>/dev/null; then + eval `dircolors $HOME/.dircolors` +fi + diff --git a/source/50_editor.sh b/source/50_editor.sh new file mode 100644 index 0000000..1a6ebbc --- /dev/null +++ b/source/50_editor.sh @@ -0,0 +1,5 @@ +# Editing + +export EDITOR='vim' +export VISUAL="$EDITOR" +alias q="$EDITOR" diff --git a/source/50_file.sh b/source/50_file.sh new file mode 100644 index 0000000..fe4237e --- /dev/null +++ b/source/50_file.sh @@ -0,0 +1,42 @@ +# Files will be created with these permissions: +# files 644 -rw-r--r-- (666 minus 022) +# dirs 755 drwxr-xr-x (777 minus 022) +umask 022 + +# Always use color output for `ls` +if is_osx; then + alias ls="command ls -G" +else + alias ls="command ls --color" + export LS_COLORS='no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.avi=01;35:*.fli=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.ogg=01;35:*.mp3=01;35:*.wav=01;35:' +fi + +# Directory listing +if [[ "$(type -P tree)" ]]; then + alias ll='tree --dirsfirst -aLpughDFiC 1' + alias lsd='ll -d' +else + alias ll='ls -al' + alias lsd='CLICOLOR_FORCE=1 ll | grep --color=never "^d"' +fi + +# Easier navigation: .., ..., - +alias ..='cd ..' +alias ...='cd ../..' +alias -- -='cd -' + +# File size +alias fs="stat -f '%z bytes'" +alias df="df -h" + +# Recursively delete `.DS_Store` files +alias dsstore="find . -name '*.DS_Store' -type f -ls -delete" + +# Aliasing eachdir like this allows you to use aliases/functions as commands. +alias eachdir=". eachdir" + +# Create a new directory and enter it +function md() { + mkdir -p "$@" && cd "$@" +} + diff --git a/source/50_history.sh b/source/50_history.sh new file mode 100644 index 0000000..f59a290 --- /dev/null +++ b/source/50_history.sh @@ -0,0 +1,18 @@ +# History settings + +# Allow use to re-edit a faild history substitution. +shopt -s histreedit +# History expansions will be verified before execution. +shopt -s histverify + +# Entries beginning with space aren't added into history, and duplicate +# entries will be erased (leaving the most recent entry). +export HISTCONTROL="ignorespace:erasedups" +# Give history timestamps. +export HISTTIMEFORMAT="[%F %T] " +# Lots o' history. +export HISTSIZE=10000 +export HISTFILESIZE=10000 + +# Easily re-execute the last history command. +alias r="fc -s" diff --git a/source/50_misc.sh b/source/50_misc.sh new file mode 100644 index 0000000..b5bd480 --- /dev/null +++ b/source/50_misc.sh @@ -0,0 +1,22 @@ +# Case-insensitive globbing (used in pathname expansion) +shopt -s nocaseglob + +# Check the window size after each command and, if necessary, +# update the values of LINES and COLUMNS. +shopt -s checkwinsize + +alias grep='grep --color=auto' + +# Prevent less from clearing the screen while still showing colors. +export LESS=-XR + +# Set the terminal's title bar. +function titlebar() { + echo -n $'\e]0;'"$*"$'\a' +} + +# SSH auto-completion based on entries in known_hosts. +if [[ -e ~/.ssh/known_hosts ]]; then + complete -o default -W "$(cat ~/.ssh/known_hosts | sed 's/[, ].*//' | sort | uniq | grep -v '[0-9]')" ssh scp sftp +fi + diff --git a/source/50_osx.sh b/source/50_osx.sh new file mode 100644 index 0000000..26f3067 --- /dev/null +++ b/source/50_osx.sh @@ -0,0 +1,16 @@ +# OSX-only stuff. Abort if not OSX. +is_osx || return 1 + +# APPLE, Y U PUT /usr/bin B4 /usr/local/bin?! +PATH="/usr/local/bin:/usr/local/sbin:$(path_remove /usr/local/bin)" +export PATH + +# Trim new lines and copy to clipboard +alias c="tr -d '\n' | pbcopy" + +# Make 'less' more. +[[ "$(type -P lesspipe.sh)" ]] && eval "$(lesspipe.sh)" + +# Start ScreenSaver. This will lock the screen if locking is enabled. +alias ss="open /System/Library/Frameworks/ScreenSaver.framework/Versions/A/Resources/ScreenSaverEngine.app" + diff --git a/source/50_prompt.sh b/source/50_prompt.sh new file mode 100644 index 0000000..4364618 --- /dev/null +++ b/source/50_prompt.sh @@ -0,0 +1,2 @@ +export PS1="\[\e[33m\]\u\[\e[35m\]@\[\e[31m\]\h\[\e[m\]:\[\e[34m\]\w\[\e[m\]\n\[\e[36m\]\@\[\e[m\] $ " + diff --git a/source/50_pyenv.sh b/source/50_pyenv.sh new file mode 100644 index 0000000..12ea6dc --- /dev/null +++ b/source/50_pyenv.sh @@ -0,0 +1,15 @@ +#pyenv + +if [ -d "$HOME/.pyenv" ]; then + + green='\033[0;32m' + NC='\033[0m' # No Color + echo -e "${green}[dotfiles] pyenv available to activate - pyenv${NC}" + + pyenv () + { + export PATH="$HOME/.pyenv/bin:$PATH" + if which pyenv > /dev/null; then eval "$(pyenv init -)"; fi + } + +fi diff --git a/source/50_rbenv.sh b/source/50_rbenv.sh new file mode 100644 index 0000000..23161c5 --- /dev/null +++ b/source/50_rbenv.sh @@ -0,0 +1,16 @@ +# rbenv + +if [ -d "$HOME/.rbenv" ]; then + + green='\033[0;32m' + NC='\033[0m' # No Color + echo -e "${green}[dotfiles] rbenv available to activate - arbenv${NC}" + + arbenv () + { + export PATH="$HOME/.rbenv/bin:$PATH" + if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi + } +fi + + diff --git a/source/99_biohpc_only.sh b/source/99_biohpc_only.sh new file mode 100644 index 0000000..d091adb --- /dev/null +++ b/source/99_biohpc_only.sh @@ -0,0 +1,54 @@ +# Things that should only and always run on UTSW biohpc systems +# Looks for a file named '.biohpchost' in user home directory +# rather than checking hostnames which are complex across the +# different biohpc systems. + +if [ -f $HOME/.biohpchost ]; then + + green='\033[0;32m' + NC='\033[0m' # No Color + + echo -e "${green}[dotfiles] BioHPC host - $BASH_SOURCE${NC}" + + # BioHPC Global bashrc + if [ -f /etc/bashrc ]; then + . /etc/bashrc + fi + + # UTSW Proxies + export http_proxy="http://proxy.swmed.edu:3128" + export https_proxy="http://proxy.swmed.edu:3128" + export all_proxy="http://proxy.swmed.edu:3128" + + # Modules + module load shared slurm git/v2.5.3 python/2.7.x-anaconda + + # Default python environment + source activate dct_default + + # Convenience Aliases + export WORK="/work/biohpcadmin/dtrudgian" + export WORK_SHARED="/work/biohpcadmin/shared" + export PROJECT="/project/biohpcadmin/dtrudgian" + export PROJECT_SHARED="/project/biohpcadmin/shared" + export ARCHIVE="/archive/biohpcadmin/dtrudgian" + export ARCHIVE_SHARED="/archive/biohpcadmin/shared" + + alias cdp="cd $PROJECT" + alias cdps="cd $PROJECT_SHARED" + alias cdw="cd $WORK" + alias cdws="cd $WORK_SHARED" + alias cda="cd $ARCHIVE" + alias cdas="cd $ARCHIVE_SHARED" + + alias aconda="module load python/2.7.x-anaconda" + alias dconda="module rm python/2.7.x-anaconda" + + alias screenws="screen -c ~/.screenrc-ws" + + if [ "$COLORTERM" = "gnome-terminal" ]; then + export TERM="xterm-256color" + fi + +fi + diff --git a/vendor/rename/README.txt b/vendor/rename/README.txt new file mode 100644 index 0000000..1eb3fc5 --- /dev/null +++ b/vendor/rename/README.txt @@ -0,0 +1,6 @@ +rename 0.1.3 +Wednesday, Nov 4, 2009, 00:35 +Aristotle Pagaltzis +http://plasmasturm.org/code/rename/ + +Note: downloaded directly because there's no repo to include. diff --git a/vendor/rename/rename b/vendor/rename/rename new file mode 100755 index 0000000..eb115b6 --- /dev/null +++ b/vendor/rename/rename @@ -0,0 +1,310 @@ +#!/usr/bin/perl +use strict; +use warnings; + +=head1 NAME + +rename - renames multiple files + +=head1 SYNOPSIS + +F +S> + +F +S> + +F +S> +S> +S> +S> +S> +S> +S> +S> +S> +S> +S> + +=head1 DESCRIPTION + +C renames the filenames supplied according to the rules specified. If a given filename is not modified, it will not be renamed. If no filenames are given on the command line, filenames will be read via standard input. + +For example, to rename all files matching C<*.bak> to strip the extension, you might say + + rename 's/\.bak$//' *.bak + +If are confident that none of the filenames has C<.bak> anywhere else than at the end, you can also use the much easier typed + + rename -s .bak '' *.bak + +You can always do multiple changes in one ago: + + rename -s .tgz .tar.gz -s .tbz2 .tar.bz2 *.tar.* + +Note however that expressive options are order sensitive. The following would probably surprise you: + + rename -s foo bar -s bar baz * + +Since operations are cumulative, this would end up substituting (some of) the F matches in filenames with F! So pay attention to order. You may want to request a verbose dry run with C<-nv> for the first stab at a complex rename operation. + + rename -nv -s bar baz -s foo bar * + +You can combine the various expressive options to suit your needs. F.ex files from Microsoft(tm) Windows systems often have blanks and (sometimes nothing but) capital letters in their names. Let's say you have a bunch of such files to clean up, and you also want to move them to subdirectories based on extension. The following command should help, provided all directories already exist: + + rename -cz -e '$_ = "$1/$_" if /(\..*)\z/' * + +Again you need to pay attention to order sensitivity for expressive options. If you placed the C<-c> after the C<-e> in the above example, files with F<.zip> and F<.ZIP> extensions would be (attempted to be) moved to different directories because the directory name prefix would be added before the filenames were normalized. Once again, use verbose dry run requested using C<-nv> to get an idea of what exactly a complex rename operation is going to do. + +=head1 ARGUMENTS + +=over 4 + +=item B<-h>, B<--help> + +See a synopsis. + +=item B<--man> + +Browse the manpage. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<-0>, B<--null> + +When reading file names from C, split on NUL bytes instead of newlines. This is useful in combination with GNU find's C<-print0> option, GNU grep's C<-Z> option, and GNU sort's C<-z> option, to name just a few. B + +=item B<-c>, B<--lower-case> + +Converts file names to all lower case. + +=item B<-C>, B<--upper-case> + +Converts file names to all upper case. + +=item B<-e>, B<--expr> + +The C argument to this option should be a Perl expression that assumes the filename in the C<$_> variable and modifies it for the filenames to be renamed. When no other C<-c>, C<-C>, C<-e>, C<-s>, or C<-z> options are given, you can omit the C<-e> from infront of the code. + +=item B<-g>, B<--glob> + +Glob filename arguments. This is useful if you're using a braindead shell such as F which won't expand wildcards on behalf of the user. + +=item B<-f>, B<--force> + +Rename even when a file with the destination name already exists. + +=item B<-i>, B<--interactive> + +Ask the user to confirm every action before it is taken. + +=item B<-k>, B<--backwards>, B<--reverse-order> + +Process the list of files in reverse order, last file first. This prevents conflicts when renaming files to names which are currently taken but would be freed later during the process of renaming. + +=item B<-l>, B<--symlink> + +Create symlinks from the new names to the existing ones, instead of renaming the files. B.> + +=item B<-L>, B<--hardlink> + +Create hard links from the new names to the existing ones, instead of renaming the files. B.> + +=item B<-n>, B<--dry-run>, B<--just-print> + +Show how the files would be renamed, but don't actually do anything. + +=item B<-s>, B<--subst>, B<--simple> + +Perform a simple textual substitution of C to C. The C and C parameters must immediately follow the argument. + +Quoting issues aside, this is equivalent to supplying a C<-e 's/\Qfrom/to/'>. + +=item B<-v>, B<--verbose> + +Print additional information about the operations (not) executed. + +=item B<-z>, B<--sanitize> + +Replaces consecutive blanks, shell meta characters, and control characters in filenames with underscores. + +=back + +=head1 SEE ALSO + +mv(1), perl(1), find(1), grep(1), sort(1) + +=head1 BUGS + +None currently known. + +=head1 AUTHORS + +Aristotle Pagaltzis + +Idea, inspiration and original code from Larry Wall and Robin Barker. + +=head1 COPYRIGHT + +This script is free software; you can redistribute it and/or modify it under the same terms as Perl itself. + +=cut + +use Pod::Usage; +use Getopt::Long 2.24, qw(:config bundling no_ignore_case no_auto_abbrev); + +use constant ERROR => do { bless \(my $l = 0), 'LOGLEVEL' }; +use constant INFO => do { bless \(my $l = 1), 'LOGLEVEL' }; +use constant DEBUG => do { bless \(my $l = 2), 'LOGLEVEL' }; +use constant VERB_FOR => { + link => { + inf => 'link', + pastp => 'linked', + exec => sub { link shift, shift or die }, + }, + symlink => { + inf => 'symlink', + pastp => 'symlinked', + exec => sub { symlink shift, shift or die }, + }, + rename => { + inf => 'rename', + pastp => 'renamed', + exec => sub { rename shift, shift or die }, + }, +}; + +sub argv_to_subst_expr { + my $modifier = shift || ''; + pod2usage( -verbose => 1 ) if @ARGV < 2; + my ($from, $to) = map quotemeta, splice @ARGV, 0, 2; + # the ugly \${\""} construct is necessary because unknown backslash escapes are + # not treated the same in pattern- vs doublequote-quoting context; only the + # latter lets us do the right thing with problematic input like + # ']{ool(haracter$' or maybe '>>' + sprintf 's/\Q${\"%s"}/%s/%s', $from, $to, $modifier; +} + +my @EXPR; + +GetOptions( + 'h|help' => sub { pod2usage( -verbose => 1 ) }, + 'man' => sub { pod2usage( -verbose => 2 ) }, + '0|null' => \my $opt_null, + 'c|lower-case' => sub { push @EXPR, 's/([[:upper:]]+)/\L$1/g' }, + 'C|upper-case' => sub { push @EXPR, 's/([[:lower:]]+)/\U$1/g' }, + 'e|expr=s' => \@EXPR, + 'f|force' => \my $opt_force, + 'g|glob' => \my $opt_glob, + 'i|interactive' => \my $opt_interactive, + 'k|backwards|reverse-order' => \my $opt_backwards, + 'l|symlink' => \my $opt_symlink, + 'L|hardlink' => \my $opt_hardlink, + 'n|just-print|dry-run' => \my $opt_dryrun, + 'p|mkpath|make-dirs' => \my $opt_mkpath, + 'v|verbose+' => \(my $opt_verbose = 0), + 'z|sanitize' => sub { push @EXPR, 's/[!"\$&()=?`*\';<>|_[:cntrl:][:blank:]]+/_/g' }, + 's|subst|simple' => sub { push @EXPR, argv_to_subst_expr }, + 'S|subst-global' => sub { push @EXPR, argv_to_subst_expr('g') }, +) or pod2usage( -verbose => 1 ); + +die "TODO" if $opt_mkpath; + +if(not @EXPR) { + pod2usage( -verbose => 1 ) if not @ARGV or -e $ARGV[0]; + push @EXPR, shift; +} + +pod2usage( -verbose => 1 ) + if ($opt_hardlink and $opt_symlink) + or ($opt_null and @ARGV); + +++$opt_verbose if $opt_dryrun; + +BEGIN { + *CORE::GLOBAL::warn = sub { + if(ref $_[0] eq 'LOGLEVEL') { + my $msglevel = ${(shift)}; + print "@_\n" if $opt_verbose >= $msglevel; + return; + } + warn @_; + }; +} + +my $code = do { + my $cat = "sub { ".join('; ', @EXPR)." }"; + warn DEBUG, "Using expression: $cat"; + + my $evaled = eval $cat; + die $@ if $@; + die "Evaluation to subref failed. Check expression using -vn\n" + unless 'CODE' eq ref $evaled; + + $evaled; +}; + +my $verb = VERB_FOR->{ + $opt_hardlink ? 'link' : + $opt_symlink ? 'symlink' : + do { 'rename' } +}; + +if (!@ARGV) { + warn INFO, "Reading filenames from STDIN"; + @ARGV = do { + if($opt_null) { + warn INFO, "Splitting on NUL bytes"; + local $/ = "\0"; + } + ; + }; + chomp @ARGV; +} + +@ARGV = map glob, @ARGV if $opt_glob; + +@ARGV = reverse @ARGV if $opt_backwards; + +for (@ARGV) { + my $old = $_; + + $code->(); + + if($old eq $_) { + warn DEBUG, "'$old' unchanged"; + next; + } + + if(!$opt_force and -e) { + warn ERROR, "'$old' not $verb->{pastp}: '$_' already exists"; + next; + } + + if($opt_dryrun) { + warn INFO, "'$old' would be $verb->{pastp} to '$_'"; + next; + } + + if($opt_interactive) { + print "\u$verb->{inf} '$old' to '$_'? [n] "; + if( !~ /^y(?:es)?$/i) { + warn DEBUG, "Skipping '$old'."; + next; + } + } + + eval { $verb->{exec}($old, $_) }; + + if($@) { + warn ERROR, "Can't $verb->{inf} '$old' to '$_': $!"; + next; + } + + warn INFO, "'$old' $verb->{pastp} to '$_'"; +}